Commit | Line | Data |
---|---|---|
50a964ce DM |
1 | " reader module |
2 | ||
3 | let Reader = {} | |
4 | ||
5 | function NewReader(tokens) | |
6 | let r = copy(g:Reader) | |
7 | let r.tokens = a:tokens | |
8 | let r.pos = 0 | |
9 | return r | |
10 | endfunction | |
11 | ||
12 | function Reader.peek() dict | |
13 | return self.tokens[self.pos] | |
14 | endfunction | |
15 | ||
16 | function Reader.nexttoken() dict | |
17 | let self.pos = self.pos + 1 | |
18 | return self.tokens[self.pos - 1] | |
19 | endfunction | |
20 | ||
21 | function Tokenize(str) | |
22 | let tokenize_pat = "[[:blank:]\\n,]*" . | |
23 | \ "\\(" . | |
24 | \ "\\~@\\|" . | |
25 | \ "[\\[\\]{}()'`~^@]\\|" . | |
26 | \ "\"\\%(\\\\.\\|[^\\\\\"]\\)*\"\\|" . | |
27 | \ ";[^\\n]*\\|" . | |
28 | \ "[^[:blank:]\\n\\[\\]{}('\"`,;)]*" . | |
29 | \ "\\)" | |
30 | let tokens = [] | |
31 | let pos = 0 | |
32 | while 1 | |
33 | let mat = matchlist(a:str, tokenize_pat, pos) | |
34 | if len(mat) == 0 || mat[0] == "" | |
35 | break | |
36 | endif | |
37 | if mat[1] != "" && mat[1][0] != ";" | |
38 | call add(tokens, mat[1]) | |
39 | endif | |
40 | let pos = matchend(a:str, tokenize_pat, pos) | |
41 | endwhile | |
42 | return tokens | |
43 | endfunction | |
44 | ||
45 | function ParseString(token) | |
46 | let str = a:token[1:-2] | |
47 | let str = substitute(str, '\\"', '"', "g") | |
48 | let str = substitute(str, '\\n', "\n", "g") | |
8d78bc26 | 49 | let str = substitute(str, '\\\\', "\\", "g") |
50a964ce DM |
50 | return str |
51 | endfunction | |
52 | ||
53 | function ReadAtom(rdr) | |
54 | let token = a:rdr.nexttoken() | |
55 | if token =~ "^-\\?[0-9]\\+$" | |
56 | return IntegerNew(str2nr(token)) | |
57 | elseif token =~ "^-\\?[0-9][0-9.]*$" | |
58 | return FloatNew(str2float(token)) | |
59 | elseif token =~ "^\".*\"$" | |
60 | return StringNew(ParseString(token)) | |
61 | elseif token =~ "^:" | |
62 | return KeywordNew(token[1:-1]) | |
63 | elseif token == "nil" | |
64 | return g:MalNil | |
65 | elseif token == "true" | |
66 | return TrueNew() | |
67 | elseif token == "false" | |
68 | return FalseNew() | |
69 | else | |
70 | return SymbolNew(token) | |
71 | endif | |
72 | endfunction | |
73 | ||
74 | function ReadTokensList(rdr, start, last) | |
75 | let elements = [] | |
76 | let token = a:rdr.nexttoken() | |
77 | if token != a:start | |
78 | throw "expected '" . a:start . "'" | |
79 | endif | |
80 | let token = a:rdr.peek() | |
81 | while token != a:last | |
82 | if token == "" | |
83 | throw "expected '" . a:last . "', got EOF" | |
84 | endif | |
85 | call add(elements, ReadForm(a:rdr)) | |
86 | let token = a:rdr.peek() | |
87 | endwhile | |
88 | call a:rdr.nexttoken() | |
89 | return elements | |
90 | endfunction | |
91 | ||
92 | function ReadList(rdr) | |
93 | let elements = ReadTokensList(a:rdr, "(", ")") | |
94 | return ListNew(elements) | |
95 | endfunction | |
96 | ||
97 | function ReadVector(rdr) | |
98 | let elements = ReadTokensList(a:rdr, "[", "]") | |
99 | return VectorNew(elements) | |
100 | endfunction | |
101 | ||
102 | function ReadHash(rdr) | |
103 | let elements = ReadTokensList(a:rdr, "{", "}") | |
104 | return HashBuild(elements) | |
105 | endfunction | |
106 | ||
107 | function ReadForm(rdr) | |
108 | let token = a:rdr.peek() | |
109 | if token == ";" | |
110 | return "" | |
111 | elseif token == "'" | |
112 | call a:rdr.nexttoken() | |
113 | return ListNew([SymbolNew("quote"), ReadForm(a:rdr)]) | |
114 | elseif token == "`" | |
115 | call a:rdr.nexttoken() | |
116 | return ListNew([SymbolNew("quasiquote"), ReadForm(a:rdr)]) | |
117 | elseif token == "~" | |
118 | call a:rdr.nexttoken() | |
119 | return ListNew([SymbolNew("unquote"), ReadForm(a:rdr)]) | |
120 | elseif token == "~@" | |
121 | call a:rdr.nexttoken() | |
122 | return ListNew([SymbolNew("splice-unquote"), ReadForm(a:rdr)]) | |
123 | elseif token == "^" | |
124 | call a:rdr.nexttoken() | |
125 | let meta = ReadForm(a:rdr) | |
126 | return ListNew([SymbolNew("with-meta"), ReadForm(a:rdr), meta]) | |
127 | elseif token == "@" | |
128 | call a:rdr.nexttoken() | |
129 | return ListNew([SymbolNew("deref"), ReadForm(a:rdr)]) | |
130 | elseif token == "(" | |
131 | return ReadList(a:rdr)") | |
132 | elseif token == ")" | |
133 | throw "unexpected ')'" | |
134 | elseif token == "[" | |
135 | return ReadVector(a:rdr) | |
136 | elseif token == "]" | |
137 | throw "unexpected ']'" | |
138 | elseif token == "{" | |
139 | return ReadHash(a:rdr) | |
140 | elseif token == "}" | |
141 | throw "unexpected '}'" | |
142 | else | |
143 | return ReadAtom(a:rdr) | |
144 | endif | |
145 | endfunction | |
146 | ||
147 | function ReadStr(str) | |
148 | let tokens = Tokenize(a:str) | |
149 | if empty(tokens) | |
150 | return "" | |
151 | endif | |
152 | return ReadForm(NewReader(tokens)) | |
153 | endfunction |