diff --git a/src/common/closh/zero/reader.cljc b/src/common/closh/zero/reader.cljc index e1941d1e..a8a16533 100644 --- a/src/common/closh/zero/reader.cljc +++ b/src/common/closh/zero/reader.cljc @@ -2,9 +2,11 @@ (:refer-clojure :exclude [read read-string]) (:require [clojure.tools.reader.reader-types :as r] [clojure.tools.reader.edn :as edn] - #?(:cljs [closh.zero.cljs-reader :as reader]))) + #?(:cljs [cljs.tools.reader]) + #?(:cljs [cljs.tools.reader.impl.utils :refer [ws-rx]])) + #?(:cljs (:import goog.string.StringBuffer))) -(set! *warn-on-reflection* true) +#?(:clj (set! *warn-on-reflection* true)) #?(:clj (defmacro require-reader [] @@ -13,12 +15,21 @@ '(do (require 'clojure.tools.reader) (def read-clojure clojure.tools.reader/read))))) -#?(:clj (require-reader)) +#?(:clj (require-reader) + :cljs + (defn- ^:no-doc read-clojure + "This is a verbatim copy of `cljs.tools.reader/read`. We need a copy otherwise re-binding ends up in infinite loop." + {:arglists '([] [reader] [opts reader] [reader eof-error? eof-value])} + ([reader] (read-clojure reader true nil)) + ([{eof :eof :as opts :or {eof :eofthrow}} reader] (cljs.tools.reader/read* reader (= eof :eofthrow) eof nil opts (to-array []))) + ([reader eof-error? sentinel] (cljs.tools.reader/read* reader eof-error? sentinel nil {} (to-array []))))) (defn whitespace?-custom "Customizes `clojure.tools.reader.impl.utils/whitespace?` so that read-token splits token only on whitespace and does not split on comma." [ch] - (and ch (Character/isWhitespace ^Character ch))) + (and (some? ch) + #?(:clj (Character/isWhitespace ^Character ch) + :cljs (.test ws-rx ch)))) (defn macro-terminating? "Customizes `clojure.tools.reader/macro-terminating?` so that read-token is more permissive. For example it does not stop on curly braces but reads them in so they can be used for brace expansion." @@ -37,7 +48,7 @@ (defn ^String read-token* "Read in a single logical token from the reader" [rdr] - (loop [sb (StringBuilder.) ch (r/read-char rdr)] + (loop [sb #?(:clj (StringBuilder.) :cljs (StringBuffer.)) ch (r/read-char rdr)] (if (or (whitespace?-custom ch) (macro-terminating? ch) (nil? ch)) @@ -50,10 +61,16 @@ (if (= \" (r/peek-char rdr)) (edn/read rdr) (let [token (read-token* rdr)] - (try - (Integer/parseInt token) - (catch Exception _ - (symbol token)))))) + #?(:clj + (try + (Integer/parseInt token) + (catch Exception _ + (symbol token))) + :cljs + (let [number (js/Number token)] + (if (js/isNaN number) + (symbol token) + number)))))) (defn read* [opts reader] (loop [coll (transient [])] @@ -96,8 +113,8 @@ (recur (conj! coll token))))))))) (defn read - ([] - (read *in*)) + #?(:clj ([] + (read *in*))) ([stream] (read stream true nil)) ([stream eof-error? eof-value] diff --git a/src/lumo/closh/zero/cljs_reader.cljs b/src/lumo/closh/zero/cljs_reader.cljs deleted file mode 100644 index 435e9a5f..00000000 --- a/src/lumo/closh/zero/cljs_reader.cljs +++ /dev/null @@ -1,99 +0,0 @@ -(ns closh.zero.cljs-reader - (:require [cljs.tools.reader.reader-types :refer [string-push-back-reader unread read-char log-source]] - [cljs.tools.reader :refer [READ_FINISHED macros]] - [cljs.tools.reader.impl.errors :as err] - [cljs.tools.reader.impl.utils :refer [ws-rx]] - [goog.array :as garray])) - -(def ^:no-doc read-internal-orig cljs.tools.reader/read*-internal) - -(defn- ^:no-doc ^boolean macro-terminating? - "Customizes `cljs.tools.reader/macro-terminating?` so that read-token is more permissive. For example it does not stop on curly braces but reads them in so they can be used for brace expansion." - [ch] - (case ch - ; (\" \; \@ \^ \` \~ \( \) \[ \] \{ \} \\) true - (\" \; \( \) \[ \] \\) true - false)) - -(defn- ^:no-doc ^boolean whitespace? - "Customizes `cljs.tools.reader.impl.utils/whitespace?` so that read-token splits token only on whitespace and does not split on comma." - [ch] - (when-not (nil? ch) - (.test ws-rx ch))) - -(defn- ^:no-doc read-token - "Reads a non-whitespace token. If it is a valid number it coerces it to number. Otherwise returns it as a symbol." - [reader ch] - (let [token (with-redefs [cljs.tools.reader/macro-terminating? macro-terminating? - cljs.tools.reader.impl.utils/whitespace? whitespace?] - (apply cljs.tools.reader/read-token reader :symbol ch)) - number (js/Number token)] - (if (js/isNaN number) - (symbol token) - number))) - -(defn- ^:no-doc read-internal-custom - "Customizes `cljs.tools.reader/read*-internal` with our reader enhancements." - [^not-native reader ^boolean eof-error? sentinel return-on opts pending-forms] - (with-redefs [cljs.tools.reader/read*-internal read-internal-orig] - (loop [] - (let [ret (log-source reader - (if-not ^boolean (garray/isEmpty pending-forms) - (let [form (aget pending-forms 0)] - (garray/removeAt pending-forms 0) - form) - (let [ch (read-char reader)] - (if-let [skip (when (= ch \\) - (when-let [ch (read-char reader)] - (if (= ch \newline) - reader - (do (unread reader ch) - nil))))] - skip - (cond - (= ch \newline) \newline - (whitespace? ch) reader - (nil? ch) (if eof-error? (err/throw-eof-error reader nil) sentinel) - (identical? ch return-on) READ_FINISHED - ; (number-literal? reader ch) (read-number reader ch) - (= \~ ch) (read-token reader ch) - :else (if-let [f (macros ch)] - (f reader ch opts pending-forms) - (read-token reader ch)))))))] - (if (identical? ret reader) - (recur) - ret))))) - -(defn- ^:no-doc read-orig - "This is a verbatim copy `cljs.tools.reader/read`. We need a copy otherwise re-binding ends up in infinite loop." - {:arglists '([] [reader] [opts reader] [reader eof-error? eof-value])} - ([reader] (read-orig reader true nil)) - ([{eof :eof :as opts :or {eof :eofthrow}} reader] (cljs.tools.reader/read* reader (= eof :eofthrow) eof nil opts (to-array []))) - ([reader eof-error? sentinel] (cljs.tools.reader/read* reader eof-error? sentinel nil {} (to-array [])))) - -(defn read - "Replacement for a `cljs.tools.reader/read` which allows reading the command mode. It tries to read all input on the line and returns a list of forms." - ([reader] - (read {} reader)) - ([opts reader] - (with-redefs [cljs.tools.reader/read*-internal read-internal-custom] - (loop [coll (transient [])] - (let [ch (read-char reader)] - (cond - (nil? ch) - (if-let [result (seq (persistent! coll))] - result - (read-orig opts reader)) - - (and (not= ch \newline) (whitespace? ch)) (recur coll) - - :else (do - (unread reader ch) - (let [token (read-orig opts reader)] - (if (or (= token \;) - (= token \newline) - (and (:eof opts) (= token (:eof opts)))) - (if-let [result (seq (persistent! coll))] - result - (recur (transient []))) - (recur (conj! coll token)))))))))))