mirror of
https://github.com/kanaka/mal.git
synced 2024-10-27 14:52:16 +03:00
8a19f60386
- Reorder README to have implementation list after "learning tool" bullet. - This also moves tests/ and libs/ into impls. It would be preferrable to have these directories at the top level. However, this causes difficulties with the wasm implementations which need pre-open directories and have trouble with paths starting with "../../". So in lieu of that, symlink those directories to the top-level. - Move the run_argv_test.sh script into the tests directory for general hygiene.
97 lines
3.2 KiB
Hy
97 lines
3.2 KiB
Hy
(import [hy.models [HyInteger :as Int HyKeyword :as Keyword
|
|
HyString :as Str HySymbol :as Sym]]
|
|
[re])
|
|
|
|
(defclass Blank [Exception])
|
|
|
|
(defclass Reader []
|
|
(defn --init-- [self tokens &optional [position 0]]
|
|
(setv self.tokens tokens self.position position))
|
|
(defn next [self]
|
|
(setv self.position (+ 1 self.position))
|
|
(get self.tokens (- self.position 1)))
|
|
(defn peek [self]
|
|
(if (> (len self.tokens) self.position)
|
|
(get self.tokens self.position)
|
|
None)))
|
|
|
|
(def tok-re (.compile re "[\\s,]*(~@|[\\[\\]{}()'`~^@]|\"(?:[\\\\].|[^\\\\\"])*\"?|;.*|[^\\s\\[\\]{}()'\"`@,;]+)"))
|
|
(def int-re (.compile re "-?[0-9]+$"))
|
|
(def str-re (.compile re "^\"(?:[\\\\].|[^\\\\\"])*\"$"))
|
|
(def str-bad-re (.compile re "^\".*$"))
|
|
|
|
(defn tokenize [str]
|
|
(list-comp
|
|
t
|
|
(t (.findall re tok-re str))
|
|
(!= (get t 0) ";")))
|
|
|
|
(defn unescape [s]
|
|
(-> s (.replace "\\\\" "\u029e")
|
|
(.replace "\\\"" "\"")
|
|
(.replace "\\n" "\n")
|
|
(.replace "\u029e" "\\")))
|
|
|
|
(defn read-atom [rdr]
|
|
(setv token (.next rdr))
|
|
(if
|
|
(.match re int-re token) (int token)
|
|
(.match re str-re token) (Str (unescape (cut token 1 -1)))
|
|
(.match re str-bad-re token) (raise (Exception "expected '\"', got EOF"))
|
|
(= ":" (get token 0)) (Keyword token)
|
|
(= "nil" token) None
|
|
(= "true" token) True
|
|
(= "false" token) False
|
|
True (Sym token)))
|
|
|
|
(defn read-seq [rdr &optional [start "("] [end ")"]]
|
|
(setv ast (list)
|
|
token (.next rdr))
|
|
(if (!= token start)
|
|
(raise (Exception (+ "expected '" start "'")))
|
|
(do
|
|
(setv token (.peek rdr))
|
|
(while (!= token end)
|
|
(if (not token) (raise (Exception (+ "expected '" end
|
|
", got EOF"))))
|
|
(.append ast (read-form rdr))
|
|
(setv token (.peek rdr)))
|
|
(.next rdr)
|
|
ast)))
|
|
|
|
(defn read-form [rdr]
|
|
(setv token (.peek rdr))
|
|
(if
|
|
(= ";" (get token 0)) (.next rdr)
|
|
|
|
(= "'" token) (do (.next rdr)
|
|
(tuple [(Sym "quote") (read-form rdr)]))
|
|
(= "`" token) (do (.next rdr)
|
|
(tuple [(Sym "quasiquote") (read-form rdr)]))
|
|
(= "~" token) (do (.next rdr)
|
|
(tuple [(Sym "unquote") (read-form rdr)]))
|
|
(= "~@" token) (do (.next rdr)
|
|
(tuple [(Sym "splice-unquote")
|
|
(read-form rdr)]))
|
|
(= "^" token) (do (.next rdr)
|
|
(setv meta (read-form rdr))
|
|
(tuple [(Sym "with-meta") (read-form rdr) meta]))
|
|
(= "@" token) (do (.next rdr)
|
|
(tuple [(Sym "deref") (read-form rdr)]))
|
|
|
|
(= ")" token) (raise (Exception "unexpected ')'"))
|
|
(= "(" token) (tuple (read-seq rdr "(" ")"))
|
|
|
|
(= "]" token) (raise (Exception "unexpected ')'"))
|
|
(= "[" token) (read-seq rdr "[" "]")
|
|
|
|
(= "}" token) (raise (Exception "unexpected '}'"))
|
|
(= "{" token) (dict (partition (read-seq rdr "{" "}") 2))
|
|
|
|
True (read-atom rdr)))
|
|
|
|
(defn read-str [str]
|
|
(setv tokens (tokenize str))
|
|
(if (= 0 (len tokens)) (raise (Blank "blank line")))
|
|
(read-form (Reader tokens)))
|