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