aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore3
-rw-r--r--Cargo.lock11
-rw-r--r--Cargo.toml1
-rw-r--r--default.nix6
-rw-r--r--nix/sources.json38
-rw-r--r--nix/sources.nix134
-rw-r--r--shell.nix10
-rw-r--r--src/error.rs71
-rw-r--r--src/error/mod.rs29
-rw-r--r--src/format.rs (renamed from src/format/mod.rs)0
-rw-r--r--src/lex.rs (renamed from src/lex/mod.rs)9
-rw-r--r--src/main.rs6
-rw-r--r--src/parse.rs (renamed from src/parse/mod.rs)0
-rw-r--r--src/readline.rs (renamed from src/readline/mod.rs)2
14 files changed, 284 insertions, 36 deletions
diff --git a/.gitignore b/.gitignore
index 6c22375..fd72c00 100644
--- a/.gitignore
+++ b/.gitignore
@@ -8,3 +8,6 @@
8# ignore history used by rustyline 8# ignore history used by rustyline
9history.txt 9history.txt
10./.idea 10./.idea
11
12.envrc
13result
diff --git a/Cargo.lock b/Cargo.lock
index 8b0baa1..de51705 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -158,6 +158,7 @@ dependencies = [
158 "radix_fmt", 158 "radix_fmt",
159 "regex", 159 "regex",
160 "rustyline", 160 "rustyline",
161 "term_size",
161] 162]
162 163
163[[package]] 164[[package]]
@@ -374,6 +375,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
374checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" 375checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
375 376
376[[package]] 377[[package]]
378name = "term_size"
379version = "0.3.2"
380source = "registry+https://github.com/rust-lang/crates.io-index"
381checksum = "1e4129646ca0ed8f45d09b929036bafad5377103edd06e50bf574b353d2b08d9"
382dependencies = [
383 "libc",
384 "winapi",
385]
386
387[[package]]
377name = "textwrap" 388name = "textwrap"
378version = "0.11.0" 389version = "0.11.0"
379source = "registry+https://github.com/rust-lang/crates.io-index" 390source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/Cargo.toml b/Cargo.toml
index ae37294..4527862 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -19,3 +19,4 @@ lazy_static = "1.3.0"
19directories = "2.0.1" 19directories = "2.0.1"
20regex = "1.1.7" 20regex = "1.1.7"
21num = "0.2" 21num = "0.2"
22term_size = "0.3"
diff --git a/default.nix b/default.nix
new file mode 100644
index 0000000..a755ba1
--- /dev/null
+++ b/default.nix
@@ -0,0 +1,6 @@
1let
2 pkgs = import <nixpkgs> {};
3 sources = import ./nix/sources.nix;
4 naersk = pkgs.callPackage sources.naersk {};
5in
6 naersk.buildPackage ./.
diff --git a/nix/sources.json b/nix/sources.json
new file mode 100644
index 0000000..17be642
--- /dev/null
+++ b/nix/sources.json
@@ -0,0 +1,38 @@
1{
2 "naersk": {
3 "branch": "master",
4 "description": "Build rust crates in Nix. No configuration, no code generation, no IFD. Sandbox friendly.",
5 "homepage": "",
6 "owner": "nmattia",
7 "repo": "naersk",
8 "rev": "22b96210b2433228d42bce460f3befbdcfde7520",
9 "sha256": "0qa8jnw71qk1i1fgpydx1nv17cnvlydb9jhd6f7gvhglagm19b4v",
10 "type": "tarball",
11 "url": "https://github.com/nmattia/naersk/archive/22b96210b2433228d42bce460f3befbdcfde7520.tar.gz",
12 "url_template": "https://github.com/<owner>/<repo>/archive/<rev>.tar.gz"
13 },
14 "niv": {
15 "branch": "master",
16 "description": "Easy dependency management for Nix projects",
17 "homepage": "https://github.com/nmattia/niv",
18 "owner": "nmattia",
19 "repo": "niv",
20 "rev": "9d35b9e4837ab88517210b1701127612c260eccf",
21 "sha256": "0q50xhnm8g2yfyakrh0nly4swyygxpi0a8cb9gp65wcakcgvzvdh",
22 "type": "tarball",
23 "url": "https://github.com/nmattia/niv/archive/9d35b9e4837ab88517210b1701127612c260eccf.tar.gz",
24 "url_template": "https://github.com/<owner>/<repo>/archive/<rev>.tar.gz"
25 },
26 "nixpkgs": {
27 "branch": "nixos-19.09",
28 "description": "DEPRECATED! This is an obsolete, read-only mirror of the NixOS/nixpkgs repository.",
29 "homepage": "https://github.com/NixOS/nixpkgs",
30 "owner": "NixOS",
31 "repo": "nixpkgs-channels",
32 "rev": "75f4ba05c63be3f147bcc2f7bd4ba1f029cedcb1",
33 "sha256": "157c64220lf825ll4c0cxsdwg7cxqdx4z559fdp7kpz0g6p8fhhr",
34 "type": "tarball",
35 "url": "https://github.com/NixOS/nixpkgs-channels/archive/75f4ba05c63be3f147bcc2f7bd4ba1f029cedcb1.tar.gz",
36 "url_template": "https://github.com/<owner>/<repo>/archive/<rev>.tar.gz"
37 }
38}
diff --git a/nix/sources.nix b/nix/sources.nix
new file mode 100644
index 0000000..8a725cb
--- /dev/null
+++ b/nix/sources.nix
@@ -0,0 +1,134 @@
1# This file has been generated by Niv.
2
3let
4
5 #
6 # The fetchers. fetch_<type> fetches specs of type <type>.
7 #
8
9 fetch_file = pkgs: spec:
10 if spec.builtin or true then
11 builtins_fetchurl { inherit (spec) url sha256; }
12 else
13 pkgs.fetchurl { inherit (spec) url sha256; };
14
15 fetch_tarball = pkgs: spec:
16 if spec.builtin or true then
17 builtins_fetchTarball { inherit (spec) url sha256; }
18 else
19 pkgs.fetchzip { inherit (spec) url sha256; };
20
21 fetch_git = spec:
22 builtins.fetchGit { url = spec.repo; inherit (spec) rev ref; };
23
24 fetch_builtin-tarball = spec:
25 builtins.trace
26 ''
27 WARNING:
28 The niv type "builtin-tarball" will soon be deprecated. You should
29 instead use `builtin = true`.
30
31 $ niv modify <package> -a type=tarball -a builtin=true
32 ''
33 builtins_fetchTarball { inherit (spec) url sha256; };
34
35 fetch_builtin-url = spec:
36 builtins.trace
37 ''
38 WARNING:
39 The niv type "builtin-url" will soon be deprecated. You should
40 instead use `builtin = true`.
41
42 $ niv modify <package> -a type=file -a builtin=true
43 ''
44 (builtins_fetchurl { inherit (spec) url sha256; });
45
46 #
47 # Various helpers
48 #
49
50 # The set of packages used when specs are fetched using non-builtins.
51 mkPkgs = sources:
52 let
53 sourcesNixpkgs =
54 import (builtins_fetchTarball { inherit (sources.nixpkgs) url sha256; }) {};
55 hasNixpkgsPath = builtins.any (x: x.prefix == "nixpkgs") builtins.nixPath;
56 hasThisAsNixpkgsPath = <nixpkgs> == ./.;
57 in
58 if builtins.hasAttr "nixpkgs" sources
59 then sourcesNixpkgs
60 else if hasNixpkgsPath && ! hasThisAsNixpkgsPath then
61 import <nixpkgs> {}
62 else
63 abort
64 ''
65 Please specify either <nixpkgs> (through -I or NIX_PATH=nixpkgs=...) or
66 add a package called "nixpkgs" to your sources.json.
67 '';
68
69 # The actual fetching function.
70 fetch = pkgs: name: spec:
71
72 if ! builtins.hasAttr "type" spec then
73 abort "ERROR: niv spec ${name} does not have a 'type' attribute"
74 else if spec.type == "file" then fetch_file pkgs spec
75 else if spec.type == "tarball" then fetch_tarball pkgs spec
76 else if spec.type == "git" then fetch_git spec
77 else if spec.type == "builtin-tarball" then fetch_builtin-tarball spec
78 else if spec.type == "builtin-url" then fetch_builtin-url spec
79 else
80 abort "ERROR: niv spec ${name} has unknown type ${builtins.toJSON spec.type}";
81
82 # Ports of functions for older nix versions
83
84 # a Nix version of mapAttrs if the built-in doesn't exist
85 mapAttrs = builtins.mapAttrs or (
86 f: set: with builtins;
87 listToAttrs (map (attr: { name = attr; value = f attr set.${attr}; }) (attrNames set))
88 );
89
90 # fetchTarball version that is compatible between all the versions of Nix
91 builtins_fetchTarball = { url, sha256 }@attrs:
92 let
93 inherit (builtins) lessThan nixVersion fetchTarball;
94 in
95 if lessThan nixVersion "1.12" then
96 fetchTarball { inherit url; }
97 else
98 fetchTarball attrs;
99
100 # fetchurl version that is compatible between all the versions of Nix
101 builtins_fetchurl = { url, sha256 }@attrs:
102 let
103 inherit (builtins) lessThan nixVersion fetchurl;
104 in
105 if lessThan nixVersion "1.12" then
106 fetchurl { inherit url; }
107 else
108 fetchurl attrs;
109
110 # Create the final "sources" from the config
111 mkSources = config:
112 mapAttrs (
113 name: spec:
114 if builtins.hasAttr "outPath" spec
115 then abort
116 "The values in sources.json should not have an 'outPath' attribute"
117 else
118 spec // { outPath = fetch config.pkgs name spec; }
119 ) config.sources;
120
121 # The "config" used by the fetchers
122 mkConfig =
123 { sourcesFile ? ./sources.json
124 , sources ? builtins.fromJSON (builtins.readFile sourcesFile)
125 , pkgs ? mkPkgs sources
126 }: rec {
127 # The sources, i.e. the attribute set of spec name to spec
128 inherit sources;
129
130 # The "pkgs" (evaluated nixpkgs) to use for e.g. non-builtin fetchers
131 inherit pkgs;
132 };
133in
134mkSources (mkConfig {}) // { __functor = _: settings: mkSources (mkConfig settings); }
diff --git a/shell.nix b/shell.nix
new file mode 100644
index 0000000..c0e1bb0
--- /dev/null
+++ b/shell.nix
@@ -0,0 +1,10 @@
1{ pkgs ? import <nixpkgs> {} }:
2
3pkgs.mkShell {
4 buildInputs = with pkgs; [
5 cargo
6 rustc
7 rustfmt
8 pkg-config
9 ];
10}
diff --git a/src/error.rs b/src/error.rs
new file mode 100644
index 0000000..e05d4e3
--- /dev/null
+++ b/src/error.rs
@@ -0,0 +1,71 @@
1/* Copyright (C) 2019 Akshay Oppiliappan <[email protected]>
2 * Refer to LICENCE for more information.
3 * */
4
5use std::iter::ExactSizeIterator;
6
7use crate::lex;
8
9#[derive(Debug)]
10pub enum CalcError {
11 Math(Math),
12 Syntax(String),
13 Parser(String),
14 Help,
15}
16
17#[derive(Debug)]
18pub enum Math {
19 DivideByZero,
20 OutOfBounds,
21 UnknownBase,
22}
23
24pub fn handler(e: CalcError) -> String {
25 match e {
26 CalcError::Math(math_err) => match math_err {
27 Math::DivideByZero => "Math Error: Divide by zero error!".to_string(),
28 Math::OutOfBounds => "Domain Error: Out of bounds!".to_string(),
29 Math::UnknownBase => "Base too large! Accepted ranges: 0 - 36".to_string(),
30 },
31 CalcError::Syntax(details) => format!("Syntax Error: {}", details),
32 CalcError::Parser(details) => format!("Parser Error: {}", details),
33 CalcError::Help => {
34 // calculate max width but ideally this should be calculated once
35 let mut max_width = 79; // capped at 79
36 if let Some((w, _)) = term_size::dimensions() {
37 if w < max_width {
38 max_width = w;
39 }
40 }
41 let operators: Vec<_> = lex::OPERATORS.keys().map(|c| c.to_string()).collect();
42 format!(
43 "Constants\n{}\nFunctions\n{}\nOperators\n{}\n",
44 blocks(max_width, lex::CONSTANTS.keys().cloned()),
45 blocks(max_width, lex::FUNCTIONS.keys().cloned()),
46 operators.join(" ")
47 )
48 }
49 }
50}
51
52/// Convert iterator into strings of chunks of 8 right padded with space.
53fn blocks(
54 max_width: usize,
55 mut iter: impl Iterator<Item = &'static str> + ExactSizeIterator,
56) -> String {
57 // multiply by eight since we are formatting it into chunks of 8
58 let items_per_line = max_width / 8;
59 let full_bytes = (iter.len() - iter.len() % items_per_line) * 8;
60 let part_bytes = iter.len() % items_per_line * 8; // leftovers
61 let n_newlines = iter.len() / items_per_line + if part_bytes > 0 { 1 } else { 0 };
62 let mut s = String::with_capacity(full_bytes + part_bytes + n_newlines);
63 for _ in 0..n_newlines {
64 for item in iter.by_ref().take(items_per_line) {
65 s.extend(format!("{:>8}", item).chars());
66 }
67 s.push('\n');
68 }
69 debug_assert_eq!(s.capacity(), s.len()); // check capacity calculation
70 s
71}
diff --git a/src/error/mod.rs b/src/error/mod.rs
deleted file mode 100644
index ec2b555..0000000
--- a/src/error/mod.rs
+++ /dev/null
@@ -1,29 +0,0 @@
1/* Copyright (C) 2019 Akshay Oppiliappan <[email protected]>
2 * Refer to LICENCE for more information.
3 * */
4
5#[derive(Debug)]
6pub enum CalcError {
7 Math(Math),
8 Syntax(String),
9 Parser(String),
10}
11
12#[derive(Debug)]
13pub enum Math {
14 DivideByZero,
15 OutOfBounds,
16 UnknownBase,
17}
18
19pub fn handler(e: CalcError) -> String {
20 match e {
21 CalcError::Math(math_err) => match math_err {
22 Math::DivideByZero => "Math Error: Divide by zero error!".to_string(),
23 Math::OutOfBounds => "Domain Error: Out of bounds!".to_string(),
24 Math::UnknownBase => "Base too large! Accepted ranges: 0 - 36".to_string(),
25 },
26 CalcError::Syntax(details) => format!("Syntax Error: {}", details),
27 CalcError::Parser(details) => format!("Parser Error: {}", details),
28 }
29}
diff --git a/src/format/mod.rs b/src/format.rs
index 45673d7..45673d7 100644
--- a/src/format/mod.rs
+++ b/src/format.rs
diff --git a/src/lex/mod.rs b/src/lex.rs
index 3222c12..76f61a0 100644
--- a/src/lex/mod.rs
+++ b/src/lex.rs
@@ -5,9 +5,8 @@
5use lazy_static::lazy_static; 5use lazy_static::lazy_static;
6use std::collections::HashMap; 6use std::collections::HashMap;
7 7
8use crate::CONFIGURATION;
9
10use crate::error::{CalcError, Math}; 8use crate::error::{CalcError, Math};
9use crate::CONFIGURATION;
11 10
12#[derive(Debug, Copy, Clone, PartialEq)] 11#[derive(Debug, Copy, Clone, PartialEq)]
13pub struct Operator { 12pub struct Operator {
@@ -70,14 +69,14 @@ pub enum Token {
70} 69}
71 70
72lazy_static! { 71lazy_static! {
73 static ref CONSTANTS: HashMap<&'static str, Token> = { 72 pub static ref CONSTANTS: HashMap<&'static str, Token> = {
74 let mut m = HashMap::new(); 73 let mut m = HashMap::new();
75 m.insert("e", Token::Num(std::f64::consts::E)); 74 m.insert("e", Token::Num(std::f64::consts::E));
76 m.insert("pi", Token::Num(std::f64::consts::PI)); 75 m.insert("pi", Token::Num(std::f64::consts::PI));
77 m 76 m
78 }; 77 };
79 78
80 static ref FUNCTIONS: HashMap<&'static str, Token> = { 79 pub static ref FUNCTIONS: HashMap<&'static str, Token> = {
81 let mut m = HashMap::new(); 80 let mut m = HashMap::new();
82 m.insert("sin", Function::token_from_fn("sin".into(), |x| is_radian_mode(x, CONFIGURATION.radian_mode).sin())); 81 m.insert("sin", Function::token_from_fn("sin".into(), |x| is_radian_mode(x, CONFIGURATION.radian_mode).sin()));
83 m.insert("cos", Function::token_from_fn("cos".into(), |x| is_radian_mode(x, CONFIGURATION.radian_mode).cos())); 82 m.insert("cos", Function::token_from_fn("cos".into(), |x| is_radian_mode(x, CONFIGURATION.radian_mode).cos()));
@@ -106,7 +105,7 @@ lazy_static! {
106 m 105 m
107 }; 106 };
108 107
109 static ref OPERATORS: HashMap<char, Token> = { 108 pub static ref OPERATORS: HashMap<char, Token> = {
110 let mut m = HashMap::new(); 109 let mut m = HashMap::new();
111 m.insert('+', Operator::token_from_op('+', |x, y| x + y, 2, true)); 110 m.insert('+', Operator::token_from_op('+', |x, y| x + y, 2, true));
112 m.insert('-', Operator::token_from_op('-', |x, y| x - y, 2, true)); 111 m.insert('-', Operator::token_from_op('-', |x, y| x - y, 2, true));
diff --git a/src/main.rs b/src/main.rs
index 5b7fdb4..75c33bb 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -193,8 +193,10 @@ fn parse_arguments() -> Configuration {
193} 193}
194 194
195pub fn eval_math_expression(input: &str, prev_ans: Option<f64>) -> Result<f64, CalcError> { 195pub fn eval_math_expression(input: &str, prev_ans: Option<f64>) -> Result<f64, CalcError> {
196 let input = input.trim(); 196 let input = input.trim().replace(" ", "");
197 let input = input.replace(" ", ""); 197 if input == "help" {
198 return Err(CalcError::Help);
199 }
198 if input.is_empty() { 200 if input.is_empty() {
199 return Ok(0.); 201 return Ok(0.);
200 } 202 }
diff --git a/src/parse/mod.rs b/src/parse.rs
index aa8bd4b..aa8bd4b 100644
--- a/src/parse/mod.rs
+++ b/src/parse.rs
diff --git a/src/readline/mod.rs b/src/readline.rs
index ea195ee..d689f95 100644
--- a/src/readline/mod.rs
+++ b/src/readline.rs
@@ -12,6 +12,7 @@ use directories::ProjectDirs;
12 12
13use regex::Regex; 13use regex::Regex;
14 14
15use crate::error::CalcError;
15use crate::eval_math_expression; 16use crate::eval_math_expression;
16 17
17pub struct RLHelper { 18pub struct RLHelper {
@@ -67,6 +68,7 @@ impl Highlighter for LineHighlighter {
67 } 68 }
68 Owned(coloured) 69 Owned(coloured)
69 } 70 }
71 Err(CalcError::Help) => Owned(line.replace("help", "\x1b[36mhelp\x1b[0m")),
70 Err(_) => Owned(format!("\x1b[31m{}\x1b[0m", line)), 72 Err(_) => Owned(format!("\x1b[31m{}\x1b[0m", line)),
71 } 73 }
72 } 74 }