mirror of
https://github.com/kanaka/mal.git
synced 2024-11-11 00:52:44 +03:00
166 lines
3.9 KiB
VimL
166 lines
3.9 KiB
VimL
" reader module
|
|
|
|
let Reader = {}
|
|
|
|
function NewReader(tokens)
|
|
let r = copy(g:Reader)
|
|
let r.tokens = a:tokens
|
|
let r.pos = 0
|
|
return r
|
|
endfunction
|
|
|
|
function Reader.peek() dict
|
|
return self.tokens[self.pos]
|
|
endfunction
|
|
|
|
function Reader.nexttoken() dict
|
|
let self.pos = self.pos + 1
|
|
return self.tokens[self.pos - 1]
|
|
endfunction
|
|
|
|
function Tokenize(str)
|
|
let tokenize_pat = "[[:blank:]\\n,]*" .
|
|
\ "\\(" .
|
|
\ "\\~@\\|" .
|
|
\ "[\\[\\]{}()'`~^@]\\|" .
|
|
\ "\"\\%(\\\\.\\|[^\\\\\"]\\)*\"\\|" .
|
|
\ "\"\\%(\\\\.\\|[^\\\\\"]\\)*\\|" .
|
|
\ ";[^\\n]*\\|" .
|
|
\ "[^[:blank:]\\n\\[\\]{}('\"`,;)]*" .
|
|
\ "\\)"
|
|
let tokens = []
|
|
let pos = 0
|
|
while 1
|
|
let mat = matchlist(a:str, tokenize_pat, pos)
|
|
if len(mat) == 0 || mat[0] == ""
|
|
break
|
|
endif
|
|
if mat[1] != "" && mat[1][0] != ";"
|
|
call add(tokens, mat[1])
|
|
endif
|
|
let pos = matchend(a:str, tokenize_pat, pos)
|
|
endwhile
|
|
return tokens
|
|
endfunction
|
|
|
|
function UnescapeChar(seq)
|
|
if a:seq == '\"'
|
|
return '"'
|
|
elseif a:seq == '\n'
|
|
return "\n"
|
|
elseif a:seq == '\\'
|
|
return '\'
|
|
else
|
|
return a:seq
|
|
endif
|
|
endfunction
|
|
|
|
function ParseString(token)
|
|
return substitute(a:token[1:-2], '\\.', '\=UnescapeChar(submatch(0))', "g")
|
|
endfunction
|
|
|
|
function ReadAtom(rdr)
|
|
let token = a:rdr.nexttoken()
|
|
if token =~ "^-\\?[0-9]\\+$"
|
|
return IntegerNew(str2nr(token))
|
|
elseif token =~ "^-\\?[0-9][0-9.]*$"
|
|
return FloatNew(str2float(token))
|
|
elseif token =~ "^\".*\"$"
|
|
return StringNew(ParseString(token))
|
|
elseif token =~ "^\".*$"
|
|
throw "expected '\"', got EOF"
|
|
elseif token =~ "^:"
|
|
return KeywordNew(token[1:-1])
|
|
elseif token == "nil"
|
|
return g:MalNil
|
|
elseif token == "true"
|
|
return TrueNew()
|
|
elseif token == "false"
|
|
return FalseNew()
|
|
else
|
|
return SymbolNew(token)
|
|
endif
|
|
endfunction
|
|
|
|
function ReadTokensList(rdr, start, last)
|
|
let elements = []
|
|
let token = a:rdr.nexttoken()
|
|
if token != a:start
|
|
throw "expected '" . a:start . "'"
|
|
endif
|
|
let token = a:rdr.peek()
|
|
while token != a:last
|
|
call add(elements, ReadForm(a:rdr))
|
|
try
|
|
let token = a:rdr.peek()
|
|
catch
|
|
throw "expected '" . a:last . "', got EOF"
|
|
endtry
|
|
endwhile
|
|
call a:rdr.nexttoken()
|
|
return elements
|
|
endfunction
|
|
|
|
function ReadList(rdr)
|
|
let elements = ReadTokensList(a:rdr, "(", ")")
|
|
return ListNew(elements)
|
|
endfunction
|
|
|
|
function ReadVector(rdr)
|
|
let elements = ReadTokensList(a:rdr, "[", "]")
|
|
return VectorNew(elements)
|
|
endfunction
|
|
|
|
function ReadHash(rdr)
|
|
let elements = ReadTokensList(a:rdr, "{", "}")
|
|
return HashBuild(elements)
|
|
endfunction
|
|
|
|
function ReadForm(rdr)
|
|
let token = a:rdr.peek()
|
|
if token == ";"
|
|
return ""
|
|
elseif token == "'"
|
|
call a:rdr.nexttoken()
|
|
return ListNew([SymbolNew("quote"), ReadForm(a:rdr)])
|
|
elseif token == "`"
|
|
call a:rdr.nexttoken()
|
|
return ListNew([SymbolNew("quasiquote"), ReadForm(a:rdr)])
|
|
elseif token == "~"
|
|
call a:rdr.nexttoken()
|
|
return ListNew([SymbolNew("unquote"), ReadForm(a:rdr)])
|
|
elseif token == "~@"
|
|
call a:rdr.nexttoken()
|
|
return ListNew([SymbolNew("splice-unquote"), ReadForm(a:rdr)])
|
|
elseif token == "^"
|
|
call a:rdr.nexttoken()
|
|
let meta = ReadForm(a:rdr)
|
|
return ListNew([SymbolNew("with-meta"), ReadForm(a:rdr), meta])
|
|
elseif token == "@"
|
|
call a:rdr.nexttoken()
|
|
return ListNew([SymbolNew("deref"), ReadForm(a:rdr)])
|
|
elseif token == "("
|
|
return ReadList(a:rdr)")
|
|
elseif token == ")"
|
|
throw "unexpected ')'"
|
|
elseif token == "["
|
|
return ReadVector(a:rdr)
|
|
elseif token == "]"
|
|
throw "unexpected ']'"
|
|
elseif token == "{"
|
|
return ReadHash(a:rdr)
|
|
elseif token == "}"
|
|
throw "unexpected '}'"
|
|
else
|
|
return ReadAtom(a:rdr)
|
|
endif
|
|
endfunction
|
|
|
|
function ReadStr(str)
|
|
let tokens = Tokenize(a:str)
|
|
if empty(tokens)
|
|
return ""
|
|
endif
|
|
return ReadForm(NewReader(tokens))
|
|
endfunction
|