diff --git a/docs/index.html b/docs/index.html new file mode 100644 index 0000000..3f0bde8 --- /dev/null +++ b/docs/index.html @@ -0,0 +1,31 @@ + + + + + ANTONIVS ORNARE | SIMON RIVVLVS HOC FECIT | MMXXIV + + + + + + +

ANTONIVS ORNARE | SIMON RIVVLVS HOC FECIT | MMXXIV

+
+
+ + +
+
+
+ + + + + \ No newline at end of file diff --git a/docs/js/antonine.js b/docs/js/antonine.js new file mode 100644 index 0000000..9146fa0 --- /dev/null +++ b/docs/js/antonine.js @@ -0,0 +1,70 @@ +goog.addDependency("base.js", ['goog'], []); +goog.addDependency("debug/error.js", ['goog.debug.Error'], []); +goog.addDependency("dom/nodetype.js", ['goog.dom.NodeType'], []); +goog.addDependency("asserts/asserts.js", ['goog.asserts'], ['goog.debug.Error', 'goog.dom.NodeType']); +goog.addDependency("dom/htmlelement.js", ['goog.dom.HtmlElement'], []); +goog.addDependency("dom/tagname.js", ['goog.dom.TagName'], ['goog.dom.HtmlElement']); +goog.addDependency("dom/element.js", ['goog.dom.element'], ['goog.dom.NodeType', 'goog.dom.TagName']); +goog.addDependency("asserts/dom.js", ['goog.asserts.dom'], ['goog.dom.TagName', 'goog.asserts', 'goog.dom.element']); +goog.addDependency("dom/asserts.js", ['goog.dom.asserts'], ['goog.asserts']); +goog.addDependency("functions/functions.js", ['goog.functions'], []); +goog.addDependency("string/typedstring.js", ['goog.string.TypedString'], []); +goog.addDependency("string/const.js", ['goog.string.Const'], ['goog.asserts', 'goog.string.TypedString']); +goog.addDependency("html/trustedtypes.js", ['goog.html.trustedtypes'], []); +goog.addDependency("html/safescript.js", ['goog.html.SafeScript'], ['goog.string.Const', 'goog.string.TypedString', 'goog.html.trustedtypes', 'goog.asserts']); +goog.addDependency("fs/url.js", ['goog.fs.url'], []); +goog.addDependency("fs/blob.js", ['goog.fs.blob'], []); +goog.addDependency("html/trustedresourceurl.js", ['goog.html.TrustedResourceUrl'], ['goog.asserts', 'goog.fs.blob', 'goog.fs.url', 'goog.html.SafeScript', 'goog.html.trustedtypes', 'goog.string.Const', 'goog.string.TypedString']); +goog.addDependency("string/internal.js", ['goog.string.internal'], []); +goog.addDependency("html/safeurl.js", ['goog.html.SafeUrl'], ['goog.asserts', 'goog.fs.url', 'goog.html.TrustedResourceUrl', 'goog.string.Const', 'goog.string.TypedString', 'goog.string.internal']); +goog.addDependency("html/safestyle.js", ['goog.html.SafeStyle'], ['goog.string.Const', 'goog.html.SafeUrl', 'goog.string.TypedString', 'goog.asserts', 'goog.string.internal']); +goog.addDependency("object/object.js", ['goog.object'], []); +goog.addDependency("html/safestylesheet.js", ['goog.html.SafeStyleSheet'], ['goog.string.Const', 'goog.html.SafeStyle', 'goog.string.TypedString', 'goog.object', 'goog.asserts', 'goog.string.internal']); +goog.addDependency("flags/flags.js", ['goog.flags'], []); +goog.addDependency("labs/useragent/useragent.js", ['goog.labs.userAgent'], ['goog.flags']); +goog.addDependency("labs/useragent/util.js", ['goog.labs.userAgent.util'], ['goog.string.internal', 'goog.labs.userAgent']); +goog.addDependency("labs/useragent/highentropy/highentropyvalue.js", ['goog.labs.userAgent.highEntropy.highEntropyValue'], ['goog.labs.userAgent.util', 'goog.string.internal']); +goog.addDependency("labs/useragent/highentropy/highentropydata.js", ['goog.labs.userAgent.highEntropy.highEntropyData'], ['goog.labs.userAgent.highEntropy.highEntropyValue']); +goog.addDependency("labs/useragent/browser.js", ['goog.labs.userAgent.browser'], ['goog.labs.userAgent.util', 'goog.labs.userAgent.highEntropy.highEntropyValue', 'goog.asserts', 'goog.string.internal', 'goog.labs.userAgent.highEntropy.highEntropyData', 'goog.labs.userAgent']); +goog.addDependency("array/array.js", ['goog.array'], ['goog.asserts']); +goog.addDependency("dom/tags.js", ['goog.dom.tags'], ['goog.object']); +goog.addDependency("html/safehtml.js", ['goog.html.SafeHtml'], ['goog.string.Const', 'goog.html.SafeScript', 'goog.html.SafeStyle', 'goog.html.SafeStyleSheet', 'goog.html.SafeUrl', 'goog.dom.TagName', 'goog.html.TrustedResourceUrl', 'goog.string.TypedString', 'goog.asserts', 'goog.labs.userAgent.browser', 'goog.array', 'goog.object', 'goog.string.internal', 'goog.dom.tags', 'goog.html.trustedtypes']); +goog.addDependency("html/uncheckedconversions.js", ['goog.html.uncheckedconversions'], ['goog.asserts', 'goog.html.SafeHtml', 'goog.html.SafeScript', 'goog.html.SafeStyle', 'goog.html.SafeStyleSheet', 'goog.html.SafeUrl', 'goog.html.TrustedResourceUrl', 'goog.string.Const', 'goog.string.internal']); +goog.addDependency("dom/safe.js", ['goog.dom.safe', 'goog.dom.safe.InsertAdjacentHtmlPosition'], ['goog.asserts', 'goog.asserts.dom', 'goog.dom.asserts', 'goog.functions', 'goog.html.SafeHtml', 'goog.html.SafeScript', 'goog.html.SafeStyle', 'goog.html.SafeUrl', 'goog.html.TrustedResourceUrl', 'goog.html.uncheckedconversions', 'goog.string.Const', 'goog.string.internal']); +goog.addDependency("string/string.js", ['goog.string', 'goog.string.Unicode'], ['goog.dom.safe', 'goog.html.uncheckedconversions', 'goog.string.Const', 'goog.string.internal']); +goog.addDependency("collections/maps.js", ['goog.collections.maps'], []); +goog.addDependency("structs/structs.js", ['goog.structs'], ['goog.array', 'goog.object']); +goog.addDependency("uri/utils.js", ['goog.uri.utils', 'goog.uri.utils.ComponentIndex', 'goog.uri.utils.QueryArray', 'goog.uri.utils.QueryValue', 'goog.uri.utils.StandardQueryParam'], ['goog.asserts', 'goog.string']); +goog.addDependency("uri/uri.js", ['goog.Uri', 'goog.Uri.QueryData'], ['goog.array', 'goog.asserts', 'goog.collections.maps', 'goog.string', 'goog.structs', 'goog.uri.utils', 'goog.uri.utils.ComponentIndex', 'goog.uri.utils.StandardQueryParam']); +goog.addDependency("reflect/reflect.js", ['goog.reflect'], []); +goog.addDependency("math/integer.js", ['goog.math.Integer'], ['goog.reflect']); +goog.addDependency("string/stringbuffer.js", ['goog.string.StringBuffer'], []); +goog.addDependency("math/long.js", ['goog.math.Long'], ['goog.asserts', 'goog.reflect']); +goog.addDependency("../cljs/core.js", ['cljs.core'], ['goog.string', 'goog.Uri', 'goog.object', 'goog.math.Integer', 'goog.string.StringBuffer', 'goog.array', 'goog.math.Long']); +goog.addDependency("../process/env.js", ['process.env'], ['cljs.core']); +goog.addDependency("../cljs/math.js", ['cljs.math'], ['cljs.core']); +goog.addDependency("../instaparse/util.js", ['instaparse.util'], ['cljs.core']); +goog.addDependency("../instaparse/auto_flatten_seq.js", ['instaparse.auto_flatten_seq'], ['cljs.core']); +goog.addDependency("../instaparse/reduction.js", ['instaparse.reduction'], ['cljs.core', 'instaparse.util', 'instaparse.auto_flatten_seq']); +goog.addDependency("../instaparse/combinators_source.js", ['instaparse.combinators_source'], ['instaparse.reduction', 'cljs.core', 'instaparse.util']); +goog.addDependency("../clojure/string.js", ['clojure.string'], ['goog.string', 'cljs.core', 'goog.string.StringBuffer']); +goog.addDependency("../instaparse/print.js", ['instaparse.print'], ['cljs.core', 'clojure.string']); +goog.addDependency("i18n/uchar.js", ['goog.i18n.uChar'], []); +goog.addDependency("../instaparse/failure.js", ['instaparse.failure'], ['instaparse.print', 'cljs.core']); +goog.addDependency("../instaparse/gll.js", ['instaparse.gll'], ['instaparse.combinators_source', 'instaparse.print', 'instaparse.reduction', 'cljs.core', 'goog.i18n.uChar', 'instaparse.util', 'instaparse.auto_flatten_seq', 'instaparse.failure']); +goog.addDependency("../instaparse/transform.js", ['instaparse.transform'], ['cljs.core', 'instaparse.util', 'instaparse.gll']); +goog.addDependency("../instaparse/line_col.js", ['instaparse.line_col'], ['cljs.core', 'instaparse.transform', 'instaparse.util']); +goog.addDependency("../cljs/tools/reader/impl/utils.js", ['cljs.tools.reader.impl.utils'], ['goog.string', 'cljs.core', 'clojure.string']); +goog.addDependency("../cljs/tools/reader/reader_types.js", ['cljs.tools.reader.reader_types'], ['goog.string', 'cljs.core', 'goog.string.StringBuffer', 'cljs.tools.reader.impl.utils']); +goog.addDependency("../cljs/tools/reader/impl/inspect.js", ['cljs.tools.reader.impl.inspect'], ['cljs.core']); +goog.addDependency("../cljs/tools/reader/impl/errors.js", ['cljs.tools.reader.impl.errors'], ['cljs.core', 'cljs.tools.reader.reader_types', 'cljs.tools.reader.impl.inspect', 'clojure.string']); +goog.addDependency("../cljs/tools/reader/impl/commons.js", ['cljs.tools.reader.impl.commons'], ['cljs.tools.reader.impl.errors', 'cljs.core', 'cljs.tools.reader.reader_types', 'cljs.tools.reader.impl.utils']); +goog.addDependency("../cljs/tools/reader.js", ['cljs.tools.reader'], ['cljs.tools.reader.impl.commons', 'goog.string', 'cljs.tools.reader.impl.errors', 'cljs.core', 'cljs.tools.reader.reader_types', 'goog.string.StringBuffer', 'cljs.tools.reader.impl.utils', 'goog.array']); +goog.addDependency("../instaparse/cfg.js", ['instaparse.cfg'], ['instaparse.combinators_source', 'cljs.tools.reader', 'instaparse.reduction', 'cljs.core', 'cljs.tools.reader.reader_types', 'instaparse.util', 'instaparse.gll', 'clojure.string']); +goog.addDependency("string/stringformat.js", ['goog.string.format'], ['goog.string']); +goog.addDependency("../clojure/walk.js", ['clojure.walk'], ['cljs.core']); +goog.addDependency("../instaparse/abnf.js", ['instaparse.abnf'], ['instaparse.combinators_source', 'instaparse.reduction', 'cljs.core', 'instaparse.transform', 'instaparse.cfg', 'instaparse.util', 'goog.string.format', 'instaparse.gll', 'clojure.walk']); +goog.addDependency("../instaparse/viz.js", ['instaparse.viz'], ['cljs.core']); +goog.addDependency("../instaparse/repeat.js", ['instaparse.repeat'], ['instaparse.combinators_source', 'instaparse.reduction', 'cljs.core', 'instaparse.auto_flatten_seq', 'instaparse.failure', 'instaparse.gll', 'instaparse.viz']); +goog.addDependency("../instaparse/core.js", ['instaparse.core'], ['instaparse.combinators_source', 'instaparse.line_col', 'instaparse.print', 'instaparse.reduction', 'cljs.core', 'instaparse.transform', 'instaparse.cfg', 'instaparse.util', 'instaparse.abnf', 'instaparse.failure', 'instaparse.gll', 'instaparse.viz', 'clojure.walk', 'instaparse.repeat']); +goog.addDependency("../antonine/calculator.js", ['antonine.calculator'], ['goog.string', 'cljs.math', 'instaparse.core', 'cljs.core']); diff --git a/docs/js/grammar.bnf b/docs/js/grammar.bnf new file mode 100644 index 0000000..d9ce30c --- /dev/null +++ b/docs/js/grammar.bnf @@ -0,0 +1,8 @@ +EXPRESSION := NUMBER | NUMBER OPERATOR EXPRESSION + := #'[IVXLCDM]+' + := ADD | SUBTRACT | MULTIPLY | DIVIDE +ADD := <'+'> +SUBTRACT := <'-'> +MULTIPLY := <'x'> | <'*'> +DIVIDE := <'/'> +SPACE := #'(?U)\s+' \ No newline at end of file diff --git a/project.clj b/project.clj index f258f07..b2e5cf2 100644 --- a/project.clj +++ b/project.clj @@ -1,15 +1,19 @@ -(defproject antonine "0.2.0" +(defproject antonine "0.2.1-SNAPSHOT" :aot :all + :cljsbuild {:builds [{:source-paths ["src"] + :compiler {:output-to "docs/js/antonine.js"}}]} + :dependencies [[instaparse "1.5.0"] + [org.clojure/clojure "1.11.3"] + [org.clojure/clojurescript "1.11.132"] + [org.clojure/tools.cli "1.1.230"] + [org.jline/jline "3.23.0"]] :description "A calculator which uses Roman numerals." - :url "http://example.com/FIXME" :license {:name "GNU General Public License" :url "http://www.gnu.org/licenses/gpl-2.0.html"} :main antonine.core - :dependencies [[instaparse "1.5.0"] - [org.clojure/clojure "1.11.3"] - [org.clojure/tools.cli "1.1.230"] - [org.jline/jline "3.23.0"]] - :plugins [[lein-cljsbuild "1.1.8"]] + :plugins [[com.bhauman/figwheel-main "0.2.18"] + [com.bhauman/rebel-readline-cljs "0.1.4"] + [lein-cljsbuild "1.1.8"]] :profiles {:jar {:aot :all} :uberjar {:aot :all}} - ) + :url "http://example.com/FIXME") diff --git a/src/antonine/calculator.cljc b/src/antonine/calculator.cljc index e224100..f2a05ac 100644 --- a/src/antonine/calculator.cljc +++ b/src/antonine/calculator.cljc @@ -1,7 +1,10 @@ (ns antonine.calculator - (:require [clojure.math :refer [floor]] - [instaparse.core :refer [get-failure]]) - (:import [java.lang NumberFormatException])) + (:require #?(:clj [clojure.java.io :refer [resource]]) + #?(:clj [clojure.core :refer [format]] + :cljs [goog.string :refer [format]]) + [clojure.math :refer [floor]] + [instaparse.core :refer [get-failure parser]]) + #?(:clj (:import [java.lang NumberFormatException]))) (defn incordec "In reading roman numerals, a lower value character preceding a higher value may imply a decrement @@ -20,9 +23,15 @@ (= c \C) (incordec accumulator 500 100) (= c \D) (+ 500 accumulator) (= c \M) (+ 1000 accumulator) - :else (throw - (NumberFormatException. - (format "Did not recognise the character '%c'", c))))) + :else (let [m (format "Did not recognise the character '%c' as valid in a Roman number", c)] + #?(:clj + (throw + (NumberFormatException. m)) + :cljs + (throw {:type :number-format-exception + :message m + :character c + :cause :reader}))))) (defn read-roman "Read this `input`, interpreting it as a Roman numeral, and return the @@ -87,27 +96,32 @@ [arg1 arg2] (apply r-op (list / arg1 arg2))) -(defn calculate - [parse-tree] - (let [failure-text (:text (get-failure parse-tree))] - (if (= :EXPRESSION (first parse-tree)) - (case (count parse-tree) - 2 (read-roman (second parse-tree)) - 4 (let [lhs (read-roman (second parse-tree)) - op (case (first (nth parse-tree 2)) - :ADD + - :MULTIPLY * - :SUBTRACT - - :DIVIDE /) - rhs (calculate (nth parse-tree 3))] - (int (floor (apply op (list lhs rhs))))) - ;;else - (throw - (ex-info - (format "Unexpected parse tree '%s'" failure-text) - {:problem parse-tree}))) - (throw - (ex-info - (format "Unexpected expression: '%s'" failure-text) - {:problem parse-tree}))))) + +(def ^:export grammar (parser #?(:clj (resource "grammar.bnf") + :cljs (js/require "grammar.bnf")))) + +(defn calculate + [input] + (let [parse-tree (if (string? input) (grammar input) input) + failure-text (:text (get-failure parse-tree))] + (if (= :EXPRESSION (first parse-tree)) + (case (count parse-tree) + 2 (read-roman (second parse-tree)) + 4 (let [lhs (read-roman (second parse-tree)) + op (case (first (nth parse-tree 2)) + :ADD + + :MULTIPLY * + :SUBTRACT - + :DIVIDE /) + rhs (calculate (nth parse-tree 3))] + (int (floor (apply op (list lhs rhs))))) + ;;else + (throw + (ex-info + (format "Unexpected parse tree '%s'" failure-text) + {:problem parse-tree}))) + (throw + (ex-info + (format "Unexpected expression: '%s'" failure-text) + {:problem parse-tree}))))) diff --git a/src/antonine/core.clj b/src/antonine/core.clj index 94a99aa..9161de7 100644 --- a/src/antonine/core.clj +++ b/src/antonine/core.clj @@ -3,7 +3,7 @@ [antonine.char-reader :refer [read-chars]] [clojure.java.io :refer [resource]] [clojure.pprint :refer [pprint]] - [clojure.string :as s :refer [trim upper-case]] + [clojure.string :as s] [clojure.tools.cli :refer [parse-opts]] [instaparse.core :refer [parser]]) (:gen-class)) @@ -11,7 +11,7 @@ (defn- romanise [arg] (s/replace (s/replace - (upper-case arg) + (s/upper-case arg) "J" "I") "U" "V")) @@ -41,7 +41,7 @@ (try (loop [] (flush) (try - (if-let [input (trim (upper-case (read-chars prompt)))] + (if-let [input (s/trim (s/upper-case (read-chars prompt)))] (if (or (empty? input) (= input stop-word)) (throw (ex-info @@ -91,4 +91,4 @@ (if (empty? arguments) (repl options) - (println (write-roman (calculate (grammar (trim (s/join " " arguments))))))))) \ No newline at end of file + (println (write-roman (calculate (grammar (s/trim (s/join " " arguments))))))))) \ No newline at end of file