DISABLE FDs (REMOVE ME).
[jackhill/mal.git] / vimscript / reader.vim
CommitLineData
50a964ce
DM
1" reader module
2
3let Reader = {}
4
5function NewReader(tokens)
6 let r = copy(g:Reader)
7 let r.tokens = a:tokens
8 let r.pos = 0
9 return r
10endfunction
11
12function Reader.peek() dict
13 return self.tokens[self.pos]
14endfunction
15
16function Reader.nexttoken() dict
17 let self.pos = self.pos + 1
18 return self.tokens[self.pos - 1]
19endfunction
20
21function 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
44endfunction
45
e73fcefe
DM
46function 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
56endfunction
57
50a964ce 58function ParseString(token)
e73fcefe 59 return substitute(a:token[1:-2], '\\.', '\=UnescapeChar(submatch(0))', "g")
50a964ce
DM
60endfunction
61
62function 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
83endfunction
84
85function 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
102endfunction
103
104function ReadList(rdr)
105 let elements = ReadTokensList(a:rdr, "(", ")")
106 return ListNew(elements)
107endfunction
108
109function ReadVector(rdr)
110 let elements = ReadTokensList(a:rdr, "[", "]")
111 return VectorNew(elements)
112endfunction
113
114function ReadHash(rdr)
115 let elements = ReadTokensList(a:rdr, "{", "}")
116 return HashBuild(elements)
117endfunction
118
119function 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
157endfunction
158
159function ReadStr(str)
160 let tokens = Tokenize(a:str)
161 if empty(tokens)
162 return ""
163 endif
164 return ReadForm(NewReader(tokens))
165endfunction