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 | \ "\"\\%(\\\\.\\|[^\\\\\"]\\)*\"\\|" . | |
5f80c83f | 27 | \ "\"\\%(\\\\.\\|[^\\\\\"]\\)*\\|" . |
50a964ce DM |
28 | \ ";[^\\n]*\\|" . |
29 | \ "[^[:blank:]\\n\\[\\]{}('\"`,;)]*" . | |
30 | \ "\\)" | |
31 | let tokens = [] | |
32 | let pos = 0 | |
33 | while 1 | |
34 | let mat = matchlist(a:str, tokenize_pat, pos) | |
35 | if len(mat) == 0 || mat[0] == "" | |
36 | break | |
37 | endif | |
38 | if mat[1] != "" && mat[1][0] != ";" | |
39 | call add(tokens, mat[1]) | |
40 | endif | |
41 | let pos = matchend(a:str, tokenize_pat, pos) | |
42 | endwhile | |
43 | return tokens | |
44 | endfunction | |
45 | ||
e73fcefe DM |
46 | function UnescapeChar(seq) |
47 | if a:seq == '\"' | |
48 | return '"' | |
49 | elseif a:seq == '\n' | |
50 | return "\n" | |
51 | elseif a:seq == '\\' | |
52 | return '\' | |
53 | else | |
54 | return a:seq | |
55 | endif | |
56 | endfunction | |
57 | ||
50a964ce | 58 | function ParseString(token) |
e73fcefe | 59 | return substitute(a:token[1:-2], '\\.', '\=UnescapeChar(submatch(0))', "g") |
50a964ce DM |
60 | endfunction |
61 | ||
62 | function ReadAtom(rdr) | |
63 | let token = a:rdr.nexttoken() | |
64 | if token =~ "^-\\?[0-9]\\+$" | |
65 | return IntegerNew(str2nr(token)) | |
66 | elseif token =~ "^-\\?[0-9][0-9.]*$" | |
67 | return FloatNew(str2float(token)) | |
0b66c996 | 68 | elseif token =~ "^\"\\%(\\\\.\\|[^\\\\\"]\\)*\"$" |
50a964ce | 69 | return StringNew(ParseString(token)) |
5f80c83f JM |
70 | elseif token =~ "^\".*$" |
71 | throw "expected '\"', got EOF" | |
50a964ce DM |
72 | elseif token =~ "^:" |
73 | return KeywordNew(token[1:-1]) | |
74 | elseif token == "nil" | |
75 | return g:MalNil | |
76 | elseif token == "true" | |
77 | return TrueNew() | |
78 | elseif token == "false" | |
79 | return FalseNew() | |
80 | else | |
81 | return SymbolNew(token) | |
82 | endif | |
83 | endfunction | |
84 | ||
85 | function ReadTokensList(rdr, start, last) | |
86 | let elements = [] | |
87 | let token = a:rdr.nexttoken() | |
88 | if token != a:start | |
89 | throw "expected '" . a:start . "'" | |
90 | endif | |
91 | let token = a:rdr.peek() | |
92 | while token != a:last | |
50a964ce | 93 | call add(elements, ReadForm(a:rdr)) |
5f80c83f JM |
94 | try |
95 | let token = a:rdr.peek() | |
96 | catch | |
97 | throw "expected '" . a:last . "', got EOF" | |
98 | endtry | |
50a964ce DM |
99 | endwhile |
100 | call a:rdr.nexttoken() | |
101 | return elements | |
102 | endfunction | |
103 | ||
104 | function ReadList(rdr) | |
105 | let elements = ReadTokensList(a:rdr, "(", ")") | |
106 | return ListNew(elements) | |
107 | endfunction | |
108 | ||
109 | function ReadVector(rdr) | |
110 | let elements = ReadTokensList(a:rdr, "[", "]") | |
111 | return VectorNew(elements) | |
112 | endfunction | |
113 | ||
114 | function ReadHash(rdr) | |
115 | let elements = ReadTokensList(a:rdr, "{", "}") | |
116 | return HashBuild(elements) | |
117 | endfunction | |
118 | ||
119 | function ReadForm(rdr) | |
120 | let token = a:rdr.peek() | |
121 | if token == ";" | |
122 | return "" | |
123 | elseif token == "'" | |
124 | call a:rdr.nexttoken() | |
125 | return ListNew([SymbolNew("quote"), ReadForm(a:rdr)]) | |
126 | elseif token == "`" | |
127 | call a:rdr.nexttoken() | |
128 | return ListNew([SymbolNew("quasiquote"), ReadForm(a:rdr)]) | |
129 | elseif token == "~" | |
130 | call a:rdr.nexttoken() | |
131 | return ListNew([SymbolNew("unquote"), ReadForm(a:rdr)]) | |
132 | elseif token == "~@" | |
133 | call a:rdr.nexttoken() | |
134 | return ListNew([SymbolNew("splice-unquote"), ReadForm(a:rdr)]) | |
135 | elseif token == "^" | |
136 | call a:rdr.nexttoken() | |
137 | let meta = ReadForm(a:rdr) | |
138 | return ListNew([SymbolNew("with-meta"), ReadForm(a:rdr), meta]) | |
139 | elseif token == "@" | |
140 | call a:rdr.nexttoken() | |
141 | return ListNew([SymbolNew("deref"), ReadForm(a:rdr)]) | |
142 | elseif token == "(" | |
143 | return ReadList(a:rdr)") | |
144 | elseif token == ")" | |
145 | throw "unexpected ')'" | |
146 | elseif token == "[" | |
147 | return ReadVector(a:rdr) | |
148 | elseif token == "]" | |
149 | throw "unexpected ']'" | |
150 | elseif token == "{" | |
151 | return ReadHash(a:rdr) | |
152 | elseif token == "}" | |
153 | throw "unexpected '}'" | |
154 | else | |
155 | return ReadAtom(a:rdr) | |
156 | endif | |
157 | endfunction | |
158 | ||
159 | function ReadStr(str) | |
160 | let tokens = Tokenize(a:str) | |
161 | if empty(tokens) | |
162 | return "" | |
163 | endif | |
164 | return ReadForm(NewReader(tokens)) | |
165 | endfunction |