Merge branch 'master' into dart
[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 \ "\"\\%(\\\\.\\|[^\\\\\"]\\)*\"\\|" .
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
43endfunction
44
45function 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
51endfunction
52
53function 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
72endfunction
73
74function 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
90endfunction
91
92function ReadList(rdr)
93 let elements = ReadTokensList(a:rdr, "(", ")")
94 return ListNew(elements)
95endfunction
96
97function ReadVector(rdr)
98 let elements = ReadTokensList(a:rdr, "[", "]")
99 return VectorNew(elements)
100endfunction
101
102function ReadHash(rdr)
103 let elements = ReadTokensList(a:rdr, "{", "}")
104 return HashBuild(elements)
105endfunction
106
107function 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
145endfunction
146
147function ReadStr(str)
148 let tokens = Tokenize(a:str)
149 if empty(tokens)
150 return ""
151 endif
152 return ReadForm(NewReader(tokens))
153endfunction