浏览代码

fix: calc parses negative numbers

Sebastian Bensusan 4 年之前
父节点
当前提交
4c10750fce
共有 3 个文件被更改,包括 38 次插入22 次删除
  1. 22 20
      src/main/frontend/extensions/calc.cljc
  2. 3 1
      src/main/grammar/calc.bnf
  3. 13 1
      src/test/frontend/extensions/calc_test.cljc

+ 22 - 20
src/main/frontend/extensions/calc.cljc

@@ -27,37 +27,39 @@
 
 (defn eval* [env ast]
   (insta/transform
-   {:number     (comp edn/read-string #(str/replace % "," ""))
-    :scientific edn/read-string
-    :expr       identity
-    :add        +
-    :sub        -
-    :mul        *
-    :div        /
-    :pow        (fn [a b]
+   {:number        (comp edn/read-string #(str/replace % "," ""))
+    :negnumber     (comp edn/read-string #(str/replace % "," ""))
+    :scientific    edn/read-string
+    :negscientific edn/read-string
+    :expr          identity
+    :add           +
+    :sub           -
+    :mul           *
+    :div           /
+    :pow           (fn [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)))
-    :ln         (fn [a]
+    :ln            (fn [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)))
-    :cos        (fn [a]
+    :cos           (fn [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)))
-    :atan       (fn [a]
+    :atan          (fn [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)))
-    :acos       (fn [a]
+    :acos          (fn [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)
                   val)
-    :toassign   str/trim
-    :variable   (fn [var]
-                  (let [var (str/trim var)]
+    :toassign      str/trim
+    :variable      (fn [var]
+                     (let [var (str/trim var)]
                     (or (get @env var)
                         (throw
                          (ex-info (util/format "Can't find variable %s" var)

+ 3 - 1
src/main/grammar/calc.bnf

@@ -17,8 +17,10 @@ tan = <#'\s*'> <'tan('> expr <')'> <#'\s*'>
 atan = <#'\s*'> <'atan('> expr <')'> <#'\s*'>
 acos = <#'\s*'> <'acos('> 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*'
+negnumber = #'\s*-\d+(,\d+)*(\.\d*)?\s*'
 number = #'\s*\d+(,\d+)*(\.\d*)?\s*'
 variable = #'\s*[a-zA-Z]+(\_+[a-zA-Z]+)*\s*'
 toassign = #'\s*[a-zA-Z]+(\_+[a-zA-Z]+)*\s*'

+ 13 - 1
src/test/frontend/extensions/calc_test.cljc

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