runtest: set INPUTRC to /dev/null
[jackhill/mal.git] / nim / reader.nim
CommitLineData
b94acce6 1import nre, optional_t, strutils, types
2
2800f318 3let
b94acce6 4 tokenRE = re"""[\s,]*(~@|[\[\]{}()'`~^@]|"(?:\\.|[^\\"])*"|;.*|[^\s\[\]{}('"`,;)]*)"""
5 intRE = re"-?[0-9]+$"
6
3603af96 7type
8 Blank* = object of Exception
9
10 Reader = object
11 tokens: seq[string]
12 position: int
b94acce6 13
14proc next(r: var Reader): string =
15 if r.position >= r.tokens.len:
16 result = nil
17 else:
18 result = r.tokens[r.position]
19 inc r.position
20
21proc peek(r: Reader): string =
22 if r.position >= r.tokens.len: nil
23 else: r.tokens[r.position]
24
25proc tokenize(str: string): seq[string] =
26 result = @[]
27 for match in str.findIter(tokenRE):
3603af96 28 if match.captures[0][0] != ';':
29 result.add match.captures[0]
b94acce6 30
31proc read_form(r: var Reader): MalType
32
33proc read_seq(r: var Reader, fr, to: string): seq[MalType] =
34 result = @[]
35 var t = r.next
36 if t != fr: raise newException(ValueError, "expected '" & fr & "'")
37
38 t = r.peek
39 while t != to:
40 if t == nil: raise newException(ValueError, "expected '" & to & "', got EOF")
41 result.add r.read_form
42 t = r.peek
43 discard r.next
44
45proc read_list(r: var Reader): MalType =
46 result = list r.read_seq("(", ")")
47
48proc read_vector(r: var Reader): MalType =
49 result = vector r.read_seq("[", "]")
50
51proc read_hash_map(r: var Reader): MalType =
52 result = hash_map r.read_seq("{", "}")
53
54proc read_atom(r: var Reader): MalType =
55 let t = r.next
56 if t.match(intRE): number t.parseInt
819bd786 57 elif t[0] == '"': str t[1 .. <t.high].replace("\\\"", "\"")
58 elif t[0] == ':': keyword t[1 .. t.high]
59 elif t == "nil": nilObj
60 elif t == "true": trueObj
61 elif t == "false": falseObj
b94acce6 62 else: symbol t
63
64proc read_form(r: var Reader): MalType =
65 if r.peek[0] == ';':
66 discard r.next
67 return nilObj
68 case r.peek
69 of "'":
70 discard r.next
71 result = list(symbol "quote", r.read_form)
72 of "`":
73 discard r.next
74 result = list(symbol "quasiquote", r.read_form)
75 of "~":
76 discard r.next
77 result = list(symbol "unquote", r.read_form)
78 of "~@":
79 discard r.next
80 result = list(symbol "splice-unquote", r.read_form)
81 of "^":
82 discard r.next
83 let meta = r.read_form
84 result = list(symbol "with-meta", r.read_form, meta)
85 of "@":
86 discard r.next
87 result = list(symbol "deref", r.read_form)
88
89 # list
90 of "(": result = r.read_list
91 of ")": raise newException(ValueError, "unexpected ')'")
92
93 # vector
94 of "[": result = r.read_vector
95 of "]": raise newException(ValueError, "unexpected ']'")
96
97 # hash-map
98 of "{": result = r.read_hash_map
99 of "}": raise newException(ValueError, "unexpected '}'")
100
101 # atom
102 else: result = r.read_atom
103
104proc read_str*(str: string): MalType =
105 var r = Reader(tokens: str.tokenize)
3603af96 106 if r.tokens.len == 0:
107 raise newException(Blank, "Blank line")
b94acce6 108 r.read_form