aboutsummaryrefslogtreecommitdiff
path: root/nim/reader.nim
diff options
context:
space:
mode:
authorJoel Martin <github@martintribe.org>2015-03-03 22:46:09 -0600
committerJoel Martin <github@martintribe.org>2015-03-03 22:46:09 -0600
commit2b0fa48326927683fa19f399b3edb8aae3dbcd36 (patch)
tree4f0d2b3b6b9e397f968f977cc38ec9a5b8b200f1 /nim/reader.nim
parent58ba5af4704c4539ee7d3814dfeff8672577f86a (diff)
parenta2cd0a3adae2ccf2566122bcd90230d905ab59dc (diff)
downloadmal-2b0fa48326927683fa19f399b3edb8aae3dbcd36.tar.gz
mal-2b0fa48326927683fa19f399b3edb8aae3dbcd36.zip
Merge pull request #20 from def-/master
Add Nim
Diffstat (limited to 'nim/reader.nim')
-rw-r--r--nim/reader.nim108
1 files changed, 108 insertions, 0 deletions
diff --git a/nim/reader.nim b/nim/reader.nim
new file mode 100644
index 0000000..856a6a6
--- /dev/null
+++ b/nim/reader.nim
@@ -0,0 +1,108 @@
+import nre, optional_t, strutils, types
+
+var
+ tokenRE = re"""[\s,]*(~@|[\[\]{}()'`~^@]|"(?:\\.|[^\\"])*"|;.*|[^\s\[\]{}('"`,;)]*)"""
+ intRE = re"-?[0-9]+$"
+
+type
+ Blank* = object of Exception
+
+ Reader = object
+ tokens: seq[string]
+ position: int
+
+proc next(r: var Reader): string =
+ if r.position >= r.tokens.len:
+ result = nil
+ else:
+ result = r.tokens[r.position]
+ inc r.position
+
+proc peek(r: Reader): string =
+ if r.position >= r.tokens.len: nil
+ else: r.tokens[r.position]
+
+proc tokenize(str: string): seq[string] =
+ result = @[]
+ for match in str.findIter(tokenRE):
+ if match.captures[0][0] != ';':
+ result.add match.captures[0]
+
+proc read_form(r: var Reader): MalType
+
+proc read_seq(r: var Reader, fr, to: string): seq[MalType] =
+ result = @[]
+ var t = r.next
+ if t != fr: raise newException(ValueError, "expected '" & fr & "'")
+
+ t = r.peek
+ while t != to:
+ if t == nil: raise newException(ValueError, "expected '" & to & "', got EOF")
+ result.add r.read_form
+ t = r.peek
+ discard r.next
+
+proc read_list(r: var Reader): MalType =
+ result = list r.read_seq("(", ")")
+
+proc read_vector(r: var Reader): MalType =
+ result = vector r.read_seq("[", "]")
+
+proc read_hash_map(r: var Reader): MalType =
+ result = hash_map r.read_seq("{", "}")
+
+proc read_atom(r: var Reader): MalType =
+ let t = r.next
+ if t.match(intRE): number t.parseInt
+ elif t[0] == '"': str t[1 .. <t.high].replace("\\\"", "\"")
+ elif t[0] == ':': keyword t[1 .. t.high]
+ elif t == "nil": nilObj
+ elif t == "true": trueObj
+ elif t == "false": falseObj
+ else: symbol t
+
+proc read_form(r: var Reader): MalType =
+ if r.peek[0] == ';':
+ discard r.next
+ return nilObj
+ case r.peek
+ of "'":
+ discard r.next
+ result = list(symbol "quote", r.read_form)
+ of "`":
+ discard r.next
+ result = list(symbol "quasiquote", r.read_form)
+ of "~":
+ discard r.next
+ result = list(symbol "unquote", r.read_form)
+ of "~@":
+ discard r.next
+ result = list(symbol "splice-unquote", r.read_form)
+ of "^":
+ discard r.next
+ let meta = r.read_form
+ result = list(symbol "with-meta", r.read_form, meta)
+ of "@":
+ discard r.next
+ result = list(symbol "deref", r.read_form)
+
+ # list
+ of "(": result = r.read_list
+ of ")": raise newException(ValueError, "unexpected ')'")
+
+ # vector
+ of "[": result = r.read_vector
+ of "]": raise newException(ValueError, "unexpected ']'")
+
+ # hash-map
+ of "{": result = r.read_hash_map
+ of "}": raise newException(ValueError, "unexpected '}'")
+
+ # atom
+ else: result = r.read_atom
+
+proc read_str*(str: string): MalType =
+ var r = Reader(tokens: str.tokenize)
+ if r.tokens.len == 0:
+ raise newException(Blank, "Blank line")
+ r.read_form