fix: calc parses negative numbers

pull/2455/head
Sebastian Bensusan 2021-07-17 22:48:23 -07:00 committed by Tienson Qin
parent d05c39b534
commit 4c10750fce
3 changed files with 38 additions and 22 deletions

View File

@ -27,37 +27,39 @@
(defn eval* [env ast] (defn eval* [env ast]
(insta/transform (insta/transform
{:number (comp edn/read-string #(str/replace % "," "")) {:number (comp edn/read-string #(str/replace % "," ""))
:scientific edn/read-string :negnumber (comp edn/read-string #(str/replace % "," ""))
:expr identity :scientific edn/read-string
:add + :negscientific edn/read-string
:sub - :expr identity
:mul * :add +
:div / :sub -
:pow (fn [a b] :mul *
:div /
:pow (fn [a b]
#?(:clj (java.lang.Math/pow a b) :cljs (js/Math.pow a b))) #?(:clj (java.lang.Math/pow a b) :cljs (js/Math.pow a b)))
:log (fn [a] :log (fn [a]
#?(:clj (java.lang.Math/log10 a) :cljs (js/Math.log10 a))) #?(:clj (java.lang.Math/log10 a) :cljs (js/Math.log10 a)))
:ln (fn [a] :ln (fn [a]
#?(:clj (java.lang.Math/log a) :cljs (js/Math.log a))) #?(:clj (java.lang.Math/log a) :cljs (js/Math.log a)))
:sin (fn [a] :sin (fn [a]
#?(:clj (java.lang.Math/sin a) :cljs (js/Math.sin a))) #?(:clj (java.lang.Math/sin a) :cljs (js/Math.sin a)))
:cos (fn [a] :cos (fn [a]
#?(:clj (java.lang.Math/cos a) :cljs (js/Math.cos a))) #?(:clj (java.lang.Math/cos a) :cljs (js/Math.cos a)))
:tan (fn [a] :tan (fn [a]
#?(:clj (java.lang.Math/tan a) :cljs (js/Math.tan a))) #?(:clj (java.lang.Math/tan a) :cljs (js/Math.tan a)))
:atan (fn [a] :atan (fn [a]
#?(:clj (java.lang.Math/atan a) :cljs (js/Math.atan a))) #?(:clj (java.lang.Math/atan a) :cljs (js/Math.atan a)))
:asin (fn [a] :asin (fn [a]
#?(:clj (java.lang.Math/asin a) :cljs (js/Math.asin a))) #?(:clj (java.lang.Math/asin a) :cljs (js/Math.asin a)))
:acos (fn [a] :acos (fn [a]
#?(:clj (java.lang.Math/acos a) :cljs (js/Math.acos a))) #?(:clj (java.lang.Math/acos a) :cljs (js/Math.acos a)))
:assignment (fn [var val] :assignment (fn [var val]
(swap! env assoc var val) (swap! env assoc var val)
val) val)
:toassign str/trim :toassign str/trim
:variable (fn [var] :variable (fn [var]
(let [var (str/trim var)] (let [var (str/trim var)]
(or (get @env var) (or (get @env var)
(throw (throw
(ex-info (util/format "Can't find variable %s" var) (ex-info (util/format "Can't find variable %s" var)

View File

@ -17,8 +17,10 @@ tan = <#'\s*'> <'tan('> expr <')'> <#'\s*'>
atan = <#'\s*'> <'atan('> expr <')'> <#'\s*'> atan = <#'\s*'> <'atan('> expr <')'> <#'\s*'>
acos = <#'\s*'> <'acos('> expr <')'> <#'\s*'> acos = <#'\s*'> <'acos('> expr <')'> <#'\s*'>
asin = <#'\s*'> <'asin('> expr <')'> <#'\s*'> asin = <#'\s*'> <'asin('> expr <')'> <#'\s*'>
<term> = scientific | number | variable | <#'\s*'> <'('> expr <')'> <#'\s*'> <term> = negscientific | scientific | negnumber | number | variable | <#'\s*'> <'('> expr <')'> <#'\s*'>
negscientific = #'\s*-[0-9]+\.?[0-9]*(e|E)-?[0-9]+()\s*'
scientific = #'\s*[0-9]+\.?[0-9]*(e|E)-?[0-9]+()\s*' scientific = #'\s*[0-9]+\.?[0-9]*(e|E)-?[0-9]+()\s*'
negnumber = #'\s*-\d+(,\d+)*(\.\d*)?\s*'
number = #'\s*\d+(,\d+)*(\.\d*)?\s*' number = #'\s*\d+(,\d+)*(\.\d*)?\s*'
variable = #'\s*[a-zA-Z]+(\_+[a-zA-Z]+)*\s*' variable = #'\s*[a-zA-Z]+(\_+[a-zA-Z]+)*\s*'
toassign = #'\s*[a-zA-Z]+(\_+[a-zA-Z]+)*\s*' toassign = #'\s*[a-zA-Z]+(\_+[a-zA-Z]+)*\s*'

View File

@ -22,7 +22,12 @@
98123 "9,8,123" 98123 "9,8,123"
1123.0 " 112,3.0 " 1123.0 " 112,3.0 "
22.1124131 "2,2.1124131" 22.1124131 "2,2.1124131"
100.01231 " 1,00.01231 "))) 100.01231 " 1,00.01231 "))
(testing "even when they are negative"
(are [value expr] (= value (run expr))
-98123 "-98123"
-1123.0 " -112,3.0 "
-22.1124131 "-2,2.1124131")))
(testing "basic operations work" (testing "basic operations work"
(are [value expr] (= value (run expr)) (are [value expr] (= value (run expr))
1 "1 + 0" 1 "1 + 0"
@ -32,11 +37,15 @@
1 "(2-1 ) " 1 "(2-1 ) "
211 "100 + 111" 211 "100 + 111"
2111 "1,000 + 11,11" 2111 "1,000 + 11,11"
-111 "1,000 + -11,11"
0 "1 + 2 + 3 + 4 + 5 -1-2-3-4-5" 0 "1 + 2 + 3 + 4 + 5 -1-2-3-4-5"
1 "1 * 1" 1 "1 * 1"
-1 "1 * -1"
2 "1*2" 2 "1*2"
-2 "-1*2"
9 " 3 *3" 9 " 3 *3"
1 " 2 * 3 / 3 / 2" 1 " 2 * 3 / 3 / 2"
-1 " 2 * 3 / 3 / -2"
#?(:clj 1/2 #?(:clj 1/2
:cljs 0.5) " 1 / 2" :cljs 0.5) " 1 / 2"
0.5 " 1/ 2.0")) 0.5 " 1/ 2.0"))
@ -62,6 +71,7 @@
1.0e1 "1.0e01" 1.0e1 "1.0e01"
1.23e-10 "123.0e-12" 1.23e-10 "123.0e-12"
12.3 "123.0e-1" 12.3 "123.0e-1"
-12.3 "-123.0e-1"
12.3 "123.0E-1" 12.3 "123.0E-1"
2.0 "1e0 + 1e0")) 2.0 "1e0 + 1e0"))
(testing "scientific functions" (testing "scientific functions"
@ -80,9 +90,11 @@
(calc/eval env (calc/parse expr)) (calc/eval env (calc/parse expr))
(= final-env @env)) (= final-env @env))
{"a" 1} "a = 1" {"a" 1} "a = 1"
{"a" -1} "a = -1"
{"variable" 1} "variable = 1 + 0 * 2" {"variable" 1} "variable = 1 + 0 * 2"
{"x" 1} "x= 2 * 1 - 1 " {"x" 1} "x= 2 * 1 - 1 "
{"y" 4} "y =8 / 4 + 2 * 1 - 25 * 0 / 1" {"y" 4} "y =8 / 4 + 2 * 1 - 25 * 0 / 1"
{"y" 4} "y =8 / 4 + 2 * 1 - 25 * 0 / 1"
{"zzz" 14.0} "zzz=3 *2 ^ 2 + 1 * 2" {"zzz" 14.0} "zzz=3 *2 ^ 2 + 1 * 2"
{"foo" 74.0} "foo = (((3*2) ^ 2 + 1) * 2)")) {"foo" 74.0} "foo = (((3*2) ^ 2 + 1) * 2)"))
(testing "variables can have underscores" (testing "variables can have underscores"