1
1
mirror of https://github.com/kanaka/mal.git synced 2024-09-11 13:55:55 +03:00

Vimscript implementation

This commit is contained in:
Dov Murik 2015-10-20 15:02:00 -04:00
parent 406761e769
commit 50a964ce14
25 changed files with 2792 additions and 2 deletions

View File

@ -18,7 +18,7 @@ mal_TEST_OPTS = --start-timeout 60 --test-timeout 120
IMPLS = awk bash c clojure coffee cpp crystal cs erlang elixir es6 factor forth fsharp go groovy \
haskell java julia js lua make mal ocaml matlab miniMAL nim \
perl php ps python r racket rpython ruby rust scala swift vb guile
perl php ps python r racket rpython ruby rust scala swift vb vimscript guile
step0 = step0_repl
step1 = step1_read_print
@ -102,6 +102,7 @@ rust_STEP_TO_PROG = rust/target/release/$($(1))
scala_STEP_TO_PROG = scala/$($(1)).scala
swift_STEP_TO_PROG = swift/$($(1))
vb_STEP_TO_PROG = vb/$($(1)).exe
vimscript_STEP_TO_PROG = vimscript/$($(1)).vim
guile_STEP_TO_PROG = guile/$($(1)).scm
# Needed some argument munging
@ -150,10 +151,15 @@ rust_RUNSTEP = ../$(2) $(3)
scala_RUNSTEP = sbt 'run-main $($(1))$(if $(3), $(3),)'
swift_RUNSTEP = ../$(2) $(3)
vb_RUNSTEP = mono ../$(2) --raw $(3)
vimscript_RUNSTEP = ./run_vimscript.sh ../$(2) $(3)
# needs TERM=dumb to work with readline
guile_RUNSTEP = guile --no-auto-compile -L ../guile ../$(2) $(3)
vimscript_TEST_OPTS = --test-timeout 30
ifeq ($(MAL_IMPL),vimscript)
mal_TEST_OPTS = --start-timeout 60 --test-timeout 180
endif
# Derived lists
STEPS = $(sort $(filter step%,$(.VARIABLES)))

View File

@ -6,7 +6,7 @@
Mal is a Clojure inspired Lisp interpreter.
Mal is implemented in 40 different languages:
Mal is implemented in 41 different languages:
* GNU awk
* Bash shell
@ -47,6 +47,7 @@ Mal is implemented in 40 different languages:
* Rust
* Scala
* Swift
* Vimscript
* Visual Basic.NET
@ -519,6 +520,18 @@ make
./stepX_YYY
```
### Vimscript
*The Vimscript implementation was created by [Dov Murik](https://github.com/dubek)*
The Vimscript implementation of mal requires Vim to run. It has been tested
with Vim 7.4.
```
cd vimscript
./run_vimscript.sh ./stepX_YYY.vim
```
### Visual Basic.NET ###
The VB.NET implementation of mal has been tested on Linux using the Mono

2
vimscript/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
/*.o
/*.so

24
vimscript/Dockerfile Normal file
View File

@ -0,0 +1,24 @@
FROM ubuntu:vivid
MAINTAINER Joel Martin <github@martintribe.org>
##########################################################
# General requirements for testing or common across many
# implementations
##########################################################
RUN apt-get -y update
# Required for running tests
RUN apt-get -y install make python
# Some typical implementation and test requirements
RUN apt-get -y install curl libreadline-dev libedit-dev
RUN mkdir -p /mal
WORKDIR /mal
##########################################################
# Specific implementation requirements
##########################################################
RUN apt-get -y install gcc vim

21
vimscript/Makefile Normal file
View File

@ -0,0 +1,21 @@
SOURCES_BASE = readline.vim types.vim reader.vim printer.vim
SOURCES_LISP = env.vim core.vim stepA_mal.vim
SOURCES = $(SOURCES_BASE) $(SOURCES_LISP)
all: libvimreadline.so
libvimreadline.so: vimreadline.o
$(CC) -g -shared -o $@ $< -lreadline
vimreadline.o: vimreadline.c
$(CC) -g -fPIC -c $< -o $@
clean:
rm -f vimreadline.o libvimreadline.so
stats: $(SOURCES)
@wc $^
stats-lisp: $(SOURCES_LISP)
@wc $^
.PHONY: stats stats-lisp clean

397
vimscript/core.vim Normal file
View File

@ -0,0 +1,397 @@
" core module
function MalEqualQ(args)
return BoolNew(EqualQ(a:args[0], a:args[1]))
endfunction
function MalLt(args)
return BoolNew(ObjValue(a:args[0]) < ObjValue(a:args[1]))
endfunction
function MalLte(args)
return BoolNew(ObjValue(a:args[0]) <= ObjValue(a:args[1]))
endfunction
function MalGt(args)
return BoolNew(ObjValue(a:args[0]) > ObjValue(a:args[1]))
endfunction
function MalGte(args)
return BoolNew(ObjValue(a:args[0]) >= ObjValue(a:args[1]))
endfunction
function MalAdd(args)
return IntegerNew(ObjValue(a:args[0]) + ObjValue(a:args[1]))
endfunction
function MalSub(args)
return IntegerNew(ObjValue(a:args[0]) - ObjValue(a:args[1]))
endfunction
function MalMul(args)
return IntegerNew(ObjValue(a:args[0]) * ObjValue(a:args[1]))
endfunction
function MalDiv(args)
return IntegerNew(ObjValue(a:args[0]) / ObjValue(a:args[1]))
endfunction
function MalList(args)
return ListNew(a:args)
endfunction
function MalListQ(args)
return BoolNew(ListQ(a:args[0]))
endfunction
function MalVector(args)
return VectorNew(a:args)
endfunction
function MalVectorQ(args)
return BoolNew(VectorQ(a:args[0]))
endfunction
function MalSequentialQ(args)
return BoolNew(SequentialQ(a:args[0]))
endfunction
function MalHashMap(args)
return HashBuild(a:args)
endfunction
function MalMapQ(args)
return BoolNew(HashQ(a:args[0]))
endfunction
function MalEmptyQ(args)
return BoolNew(EmptyQ(a:args[0]))
endfunction
function MalCount(args)
return IntegerNew(ListCount(a:args[0]))
endfunction
function MalAssoc(args)
let hash = copy(ObjValue(a:args[0]))
let new_elements = HashBuild(a:args[1:])
call extend(hash, ObjValue(new_elements))
return HashNew(hash)
endfunction
function MalDissoc(args)
let hash = copy(ObjValue(a:args[0]))
for keyobj in a:args[1:]
let key = HashMakeKey(keyobj)
if has_key(hash, key)
call remove(hash, key)
endif
endfor
return HashNew(hash)
endfunction
function MalGet(args)
if !HashQ(a:args[0])
return g:MalNil
endif
let hash = ObjValue(a:args[0])
let key = HashMakeKey(a:args[1])
return get(hash, key, g:MalNil)
endfunction
function MalContainsQ(args)
if !HashQ(a:args[0])
return FalseNew()
endif
let hash = ObjValue(a:args[0])
let key = HashMakeKey(a:args[1])
return BoolNew(has_key(hash, key))
endfunction
function MalKeys(args)
let listobjs = []
for keyname in keys(ObjValue(a:args[0]))
let keyobj = HashParseKey(keyname)
call add(listobjs, keyobj)
endfor
return ListNew(listobjs)
endfunction
function MalVals(args)
return ListNew(values(ObjValue(a:args[0])))
endfunction
function MalPrStr(args)
return StringNew(join(map(copy(a:args), 'PrStr(v:val, 1)'), " "))
endfunction
function MalStr(args)
return StringNew(join(map(copy(a:args), 'PrStr(v:val, 0)'), ""))
endfunction
function MalPrn(args)
call PrintLn(join(map(copy(a:args), 'PrStr(v:val, 1)'), " "))
return g:MalNil
endfunction
function MalPrintLn(args)
call PrintLn(join(map(copy(a:args), 'PrStr(v:val, 0)'), " "))
return g:MalNil
endfunction
function MalReadString(args)
return ReadStr(ObjValue(a:args[0]))
endfunction
function MalReadLine(args)
let [eof, line] = Readline(ObjValue(a:args[0]))
return eof ? g:MalNil : StringNew(line)
endfunction
function MalSlurp(args)
let filename = ObjValue(a:args[0])
let lines = readfile(filename, "b")
return StringNew(join(lines, "\n"))
endfunction
function MalCons(args)
let items = copy(ObjValue(a:args[1]))
call insert(items, a:args[0])
return ListNew(items)
endfunction
function MalConcat(args)
let res = []
for list in a:args
let res = res + ObjValue(list)
endfor
return ListNew(res)
endfunction
function MalFirst(args)
return ListFirst(a:args[0])
endfunction
function MalNth(args)
return ListNth(a:args[0], ObjValue(a:args[1]))
endfunction
function MalRest(args)
return ListRest(a:args[0])
endfunction
function MalApply(args)
let funcobj = a:args[0]
let rest = a:args[1:]
if len(rest) == 0
let funcargs = []
elseif len(rest) == 1
let funcargs = ObjValue(rest[-1])
else
let funcargs = rest[:-2] + ObjValue(rest[-1])
endif
if NativeFunctionQ(funcobj)
return NativeFuncInvoke(funcobj, ListNew(funcargs))
elseif FunctionQ(funcobj)
return FuncInvoke(funcobj, ListNew(funcargs))
else
throw "Not a function"
endif
endfunction
function MalMap(args)
let funcobj = a:args[0]
let res = []
for item in ObjValue(a:args[1])
unlet! mappeditem
if NativeFunctionQ(funcobj)
let mappeditem = NativeFuncInvoke(funcobj, ListNew([item]))
elseif FunctionQ(funcobj)
let mappeditem = FuncInvoke(funcobj, ListNew([item]))
else
throw "Not a function"
endif
call add(res, mappeditem)
endfor
return ListNew(res)
endfunction
function MalThrow(args)
unlet! g:MalExceptionObj
let g:MalExceptionObj = a:args[0]
throw "__MalException__"
endfunction
function MalNilQ(args)
return BoolNew(NilQ(a:args[0]))
endfunction
function MalTrueQ(args)
return BoolNew(TrueQ(a:args[0]))
endfunction
function MalFalseQ(args)
return BoolNew(FalseQ(a:args[0]))
endfunction
function MalSymbol(args)
return SymbolNew(ObjValue(a:args[0]))
endfunction
function MalSymbolQ(args)
return BoolNew(SymbolQ(a:args[0]))
endfunction
function MalKeyword(args)
return KeywordNew(ObjValue(a:args[0]))
endfunction
function MalKeywordQ(args)
return BoolNew(KeywordQ(a:args[0]))
endfunction
function ConjList(list, elements)
let newlist = a:list
for e in a:elements
let newlist = MalCons([e, newlist])
endfor
return newlist
endfunction
function ConjVector(vector, elements)
let items = copy(ObjValue(a:vector))
for e in a:elements
call add(items, e)
endfor
return VectorNew(items)
endfunction
function MalConj(args)
if ListQ(a:args[0])
return ConjList(a:args[0], a:args[1:])
elseif VectorQ(a:args[0])
return ConjVector(a:args[0], a:args[1:])
endif
endfunction
function MalMeta(args)
return ObjMeta(a:args[0])
endfunction
function MalWithMeta(args)
let obj = a:args[0]
return ObjNewWithMeta(ObjType(obj), copy(ObjValue(obj)), a:args[1])
endfunction
function MalAtom(args)
return AtomNew(a:args[0])
endfunction
function MalAtomQ(args)
return AtomQ(a:args[0])
endfunction
function MalDeref(args)
return ObjValue(a:args[0])
endfunction
function MalResetBang(args)
return ObjSetValue(a:args[0], a:args[1])
endfunction
function MalSwapBang(args)
let atomval = ObjValue(a:args[0])
let funcobj = a:args[1]
let args = a:args[2:]
let res = MalApply([funcobj, ListNew([atomval] + args)])
return ObjSetValue(a:args[0], res)
endfunction
function VimToMal(e)
if type(a:e) == type(0)
return IntegerNew(a:e)
elseif type(a:e) == type(0.0)
return FloatNew(a:e)
elseif type(a:e) == type("")
return StringNew(a:e)
elseif type(a:e) == type([])
let res = []
for v in a:e
call add(res, VimToMal(v))
endfor
return ListNew(res)
elseif type(a:e) == type({})
let res = {}
for [k,v] in items(a:e)
let keystring = HashMakeKey(StringNew(k))
let res[keystring] = VimToMal(v)
endfor
return HashNew(res)
else
return g:MalNil
endif
endfunction
function MalVimStar(args)
let vimexpr = ObjValue(a:args[0])
let vimres = eval(vimexpr)
return VimToMal(vimres)
endfunction
let CoreNs = {
\ "=": NewNativeFn("MalEqualQ"),
\ "<": NewNativeFn("MalLt"),
\ "<=": NewNativeFn("MalLte"),
\ ">": NewNativeFn("MalGt"),
\ ">=": NewNativeFn("MalGte"),
\ "+": NewNativeFn("MalAdd"),
\ "-": NewNativeFn("MalSub"),
\ "*": NewNativeFn("MalMul"),
\ "/": NewNativeFn("MalDiv"),
\ "nil?": NewNativeFn("MalNilQ"),
\ "true?": NewNativeFn("MalTrueQ"),
\ "false?": NewNativeFn("MalFalseQ"),
\ "symbol": NewNativeFn("MalSymbol"),
\ "symbol?": NewNativeFn("MalSymbolQ"),
\ "keyword": NewNativeFn("MalKeyword"),
\ "keyword?": NewNativeFn("MalKeywordQ"),
\ "list": NewNativeFn("MalList"),
\ "list?": NewNativeFn("MalListQ"),
\ "vector": NewNativeFn("MalVector"),
\ "vector?": NewNativeFn("MalVectorQ"),
\ "sequential?": NewNativeFn("MalSequentialQ"),
\ "hash-map": NewNativeFn("MalHashMap"),
\ "map?": NewNativeFn("MalMapQ"),
\ "empty?": NewNativeFn("MalEmptyQ"),
\ "count": NewNativeFn("MalCount"),
\ "assoc": NewNativeFn("MalAssoc"),
\ "dissoc": NewNativeFn("MalDissoc"),
\ "get": NewNativeFn("MalGet"),
\ "contains?": NewNativeFn("MalContainsQ"),
\ "keys": NewNativeFn("MalKeys"),
\ "vals": NewNativeFn("MalVals"),
\ "pr-str": NewNativeFn("MalPrStr"),
\ "str": NewNativeFn("MalStr"),
\ "prn": NewNativeFn("MalPrn"),
\ "println": NewNativeFn("MalPrintLn"),
\ "read-string": NewNativeFn("MalReadString"),
\ "readline": NewNativeFn("MalReadLine"),
\ "slurp": NewNativeFn("MalSlurp"),
\ "cons": NewNativeFn("MalCons"),
\ "concat": NewNativeFn("MalConcat"),
\ "first": NewNativeFn("MalFirst"),
\ "nth": NewNativeFn("MalNth"),
\ "rest": NewNativeFn("MalRest"),
\ "apply": NewNativeFn("MalApply"),
\ "map": NewNativeFn("MalMap"),
\ "throw": NewNativeFn("MalThrow"),
\ "conj": NewNativeFn("MalConj"),
\ "meta": NewNativeFn("MalMeta"),
\ "with-meta": NewNativeFn("MalWithMeta"),
\ "atom": NewNativeFn("MalAtom"),
\ "atom?": NewNativeFn("MalAtomQ"),
\ "deref": NewNativeFn("MalDeref"),
\ "reset!": NewNativeFn("MalResetBang"),
\ "swap!": NewNativeFn("MalSwapBang"),
\ "vim*": NewNativeFn("MalVimStar")
\ }

62
vimscript/env.vim Normal file
View File

@ -0,0 +1,62 @@
" env module
let Env = {}
function NewEnv(outer)
let e = copy(g:Env)
let e.data = {}
let e.outer = a:outer
return e
endfunction
function NewEnvWithBinds(outer, binds, exprs)
let env = NewEnv(a:outer)
let i = 0
while i < ListCount(a:binds)
let varname = ObjValue(ListNth(a:binds, i))
if varname == "&"
" TODO
let restvarname = ObjValue(ListNth(a:binds, i + 1))
let restvarvalues = ListDrop(a:exprs, i)
call env.set(restvarname, restvarvalues)
break
else
unlet! varvalue
let varvalue = ListNth(a:exprs, i)
call env.set(varname, varvalue)
endif
let i = i + 1
endwhile
return env
endfunction
function Env.find(key) dict
if has_key(self.data, a:key)
return self
elseif empty(self.outer)
return ""
else
return self.outer.find(a:key)
endif
endfunction
function Env.set(key, value) dict
let self.data[a:key] = a:value
return a:value
endfunction
function Env.get(key) dict
let env = self.find(a:key)
if empty(env)
throw "'" . a:key . "' not found"
endif
return env.data[a:key]
endfunction
function Env.root() dict
let curr = self
while !empty(curr.outer)
let curr = curr.outer
endwhile
return curr
endfunction

60
vimscript/printer.vim Normal file
View File

@ -0,0 +1,60 @@
" printer module
function PrStr(ast, readable)
let obj = a:ast
let r = a:readable
if ListQ(obj)
let ret = []
for e in ObjValue(obj)
call add(ret, PrStr(e, r))
endfor
return "(" . join(ret, " ") . ")"
elseif VectorQ(obj)
let ret = []
for e in ObjValue(obj)
call add(ret, PrStr(e, r))
endfor
return "[" . join(ret, " ") . "]"
elseif HashQ(obj)
let ret = []
for [k, v] in items(ObjValue(obj))
let keyobj = HashParseKey(k)
call add(ret, PrStr(keyobj, r))
call add(ret, PrStr(v, r))
endfor
return "{" . join(ret, " ") . "}"
elseif MacroQ(obj)
let numargs = ListCount(ObjValue(obj).params)
return "<Macro:" . numargs . "-arguments>"
elseif FunctionQ(obj)
let numargs = ListCount(ObjValue(obj).params)
return "<Function:" . numargs . "-arguments>"
elseif NativeFunctionQ(obj)
let funcname = ObjValue(obj).name
return "<NativeFunction:" . funcname . ">"
elseif AtomQ(obj)
return "(atom " . PrStr(ObjValue(obj), 1) . ")"
elseif KeywordQ(obj)
return ':' . ObjValue(obj)
elseif StringQ(obj)
if r
let str = ObjValue(obj)
let str = substitute(str, '\\', '\\\\', "g")
let str = substitute(str, '"', '\\"', "g")
let str = substitute(str, "\n", '\\n', "g")
return '"' . str . '"'
else
return ObjValue(obj)
endif
elseif NilQ(obj)
return "nil"
elseif TrueQ(obj)
return "true"
elseif FalseQ(obj)
return "false"
elseif IntegerQ(obj) || FloatQ(obj)
return string(ObjValue(obj))
else
return ObjValue(obj)
end
endfunction

152
vimscript/reader.vim Normal file
View File

@ -0,0 +1,152 @@
" 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 ParseString(token)
let str = a:token[1:-2]
let str = substitute(str, '\\"', '"', "g")
let str = substitute(str, '\\n', "\n", "g")
return str
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 =~ "^:"
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
if token == ""
throw "expected '" . a:last . "', got EOF"
endif
call add(elements, ReadForm(a:rdr))
let token = a:rdr.peek()
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

23
vimscript/readline.vim Normal file
View File

@ -0,0 +1,23 @@
function PrintLn(str)
let lines = split(a:str, "\n", 1)
call writefile(lines, "/dev/stdout", "a")
endfunction
function s:buildlibvimreadline()
if !filereadable("libvimreadline.so")
call system("make libvimreadline.so")
endif
endfunction
" Returns [is_eof, line_string]
function Readline(prompt)
" Use the vimreadline() function defined in vimreadline.c and compiled
" into libvimreadline.so
call s:buildlibvimreadline()
let res = libcall("libvimreadline.so", "vimreadline", a:prompt)
if res[0] == "E"
return [1, ""]
else
return [0, res[1:]]
endif
endfunction

15
vimscript/run_vimscript.sh Executable file
View File

@ -0,0 +1,15 @@
#!/bin/sh
# Run Vim in ex mode (-e) and run the given script ($1) on startup. Our scripts
# end with 'qall!' which causes actual Vim UI to never start up.
#
# Set environment variable DEBUG=1 to allow more verbose error output from Vim.
#
# See: http://vim.wikia.com/wiki/Vim_as_a_system_interpreter_for_vimscript
vimscriptfile="$1"
shift
if [ x$DEBUG = x ] ; then
exec 2> /dev/null
fi
exec vim -i NONE -V1 -nNesS $vimscriptfile -- "$@"

29
vimscript/step0_repl.vim Normal file
View File

@ -0,0 +1,29 @@
source readline.vim
function READ(str)
return a:str
endfunction
function EVAL(ast, env)
return a:ast
endfunction
function PRINT(exp)
return a:exp
endfunction
function REP(str)
return PRINT(EVAL(READ(a:str), {}))
endfunction
while 1
let [eof, line] = Readline("user> ")
if eof
break
endif
if line == ""
continue
endif
call PrintLn(REP(line))
endwhile
qall!

View File

@ -0,0 +1,36 @@
source readline.vim
source types.vim
source reader.vim
source printer.vim
function READ(str)
return ReadStr(a:str)
endfunction
function EVAL(ast, env)
return a:ast
endfunction
function PRINT(exp)
return PrStr(a:exp, 1)
endfunction
function REP(str)
return PRINT(EVAL(READ(a:str), {}))
endfunction
while 1
let [eof, line] = Readline("user> ")
if eof
break
endif
if line == ""
continue
endif
try
call PrintLn(REP(line))
catch
call PrintLn("Error: " . v:exception)
endtry
endwhile
qall!

100
vimscript/step2_eval.vim Normal file
View File

@ -0,0 +1,100 @@
source readline.vim
source types.vim
source reader.vim
source printer.vim
function READ(str)
return ReadStr(a:str)
endfunction
function EvalAst(ast, env)
if SymbolQ(a:ast)
let varname = ObjValue(a:ast)
if !has_key(a:env, varname)
throw "'" . varname . "' not found"
end
return a:env[varname]
elseif ListQ(a:ast)
let ret = []
for e in ObjValue(a:ast)
call add(ret, EVAL(e, a:env))
endfor
return ListNew(ret)
elseif VectorQ(a:ast)
let ret = []
for e in ObjValue(a:ast)
call add(ret, EVAL(e, a:env))
endfor
return VectorNew(ret)
elseif HashQ(a:ast)
let ret = {}
for [k,v] in items(ObjValue(a:ast))
let keyobj = HashParseKey(k)
let newkey = EVAL(keyobj, a:env)
let newval = EVAL(v, a:env)
let keystring = HashMakeKey(newkey)
let ret[keystring] = newval
endfor
return HashNew(ret)
else
return a:ast
end
endfunction
function EVAL(ast, env)
if !ListQ(a:ast)
return EvalAst(a:ast, a:env)
end
" apply list
let el = EvalAst(a:ast, a:env)
let Fn = ObjValue(el)[0]
return Fn(ObjValue(el)[1:-1])
endfunction
function PRINT(exp)
return PrStr(a:exp, 1)
endfunction
function REP(str, env)
return PRINT(EVAL(READ(a:str), a:env))
endfunction
function MalAdd(args)
return IntegerNew(ObjValue(a:args[0]) + ObjValue(a:args[1]))
endfunction
function MalSub(args)
return IntegerNew(ObjValue(a:args[0]) - ObjValue(a:args[1]))
endfunction
function MalMul(args)
return IntegerNew(ObjValue(a:args[0]) * ObjValue(a:args[1]))
endfunction
function MalDiv(args)
return IntegerNew(ObjValue(a:args[0]) / ObjValue(a:args[1]))
endfunction
let repl_env = {}
let repl_env["+"] = function("MalAdd")
let repl_env["-"] = function("MalSub")
let repl_env["*"] = function("MalMul")
let repl_env["/"] = function("MalDiv")
while 1
let [eof, line] = Readline("user> ")
if eof
break
endif
if line == ""
continue
endif
try
call PrintLn(REP(line, repl_env))
catch
call PrintLn("ERROR: " . v:exception)
endtry
endwhile
qall!

116
vimscript/step3_env.vim Normal file
View File

@ -0,0 +1,116 @@
source readline.vim
source types.vim
source reader.vim
source printer.vim
source env.vim
function READ(str)
return ReadStr(a:str)
endfunction
function EvalAst(ast, env)
if SymbolQ(a:ast)
let varname = ObjValue(a:ast)
return a:env.get(varname)
elseif ListQ(a:ast)
let ret = []
for e in ObjValue(a:ast)
call add(ret, EVAL(e, a:env))
endfor
return ListNew(ret)
elseif VectorQ(a:ast)
let ret = []
for e in ObjValue(a:ast)
call add(ret, EVAL(e, a:env))
endfor
return VectorNew(ret)
elseif HashQ(a:ast)
let ret = {}
for [k,v] in items(ObjValue(a:ast))
let keyobj = HashParseKey(k)
let newkey = EVAL(keyobj, a:env)
let newval = EVAL(v, a:env)
let keystring = HashMakeKey(newkey)
let ret[keystring] = newval
endfor
return HashNew(ret)
else
return a:ast
end
endfunction
function EVAL(ast, env)
if !ListQ(a:ast)
return EvalAst(a:ast, a:env)
end
let first_symbol = ObjValue(ObjValue(a:ast)[0])
if first_symbol == "def!"
let a1 = ObjValue(a:ast)[1]
let a2 = ObjValue(a:ast)[2]
return a:env.set(ObjValue(a1), EVAL(a2, a:env))
elseif first_symbol == "let*"
let a1 = ObjValue(a:ast)[1]
let a2 = ObjValue(a:ast)[2]
let let_env = NewEnv(a:env)
let let_binds = ObjValue(a1)
let i = 0
while i < len(let_binds)
call let_env.set(ObjValue(let_binds[i]), EVAL(let_binds[i+1], let_env))
let i = i + 2
endwhile
return EVAL(a2, let_env)
else
" apply list
let el = EvalAst(a:ast, a:env)
let Fn = ObjValue(el)[0]
return Fn(ObjValue(el)[1:-1])
endif
endfunction
function PRINT(exp)
return PrStr(a:exp, 1)
endfunction
function REP(str, env)
return PRINT(EVAL(READ(a:str), a:env))
endfunction
function MalAdd(args)
return IntegerNew(ObjValue(a:args[0]) + ObjValue(a:args[1]))
endfunction
function MalSub(args)
return IntegerNew(ObjValue(a:args[0]) - ObjValue(a:args[1]))
endfunction
function MalMul(args)
return IntegerNew(ObjValue(a:args[0]) * ObjValue(a:args[1]))
endfunction
function MalDiv(args)
return IntegerNew(ObjValue(a:args[0]) / ObjValue(a:args[1]))
endfunction
let repl_env = NewEnv("")
call repl_env.set("+", function("MalAdd"))
call repl_env.set("-", function("MalSub"))
call repl_env.set("*", function("MalMul"))
call repl_env.set("/", function("MalDiv"))
while 1
let [eof, line] = Readline("user> ")
if eof
break
endif
if line == ""
continue
endif
try
call PrintLn(REP(line, repl_env))
catch
call PrintLn("Error: " . v:exception)
endtry
endwhile
qall!

View File

@ -0,0 +1,128 @@
source readline.vim
source types.vim
source reader.vim
source printer.vim
source env.vim
source core.vim
function READ(str)
return ReadStr(a:str)
endfunction
function EvalAst(ast, env)
if SymbolQ(a:ast)
let varname = ObjValue(a:ast)
return a:env.get(varname)
elseif ListQ(a:ast)
let ret = []
for e in ObjValue(a:ast)
call add(ret, EVAL(e, a:env))
endfor
return ListNew(ret)
elseif VectorQ(a:ast)
let ret = []
for e in ObjValue(a:ast)
call add(ret, EVAL(e, a:env))
endfor
return VectorNew(ret)
elseif HashQ(a:ast)
let ret = {}
for [k,v] in items(ObjValue(a:ast))
let keyobj = HashParseKey(k)
let newkey = EVAL(keyobj, a:env)
let newval = EVAL(v, a:env)
let keystring = HashMakeKey(newkey)
let ret[keystring] = newval
endfor
return HashNew(ret)
else
return a:ast
end
endfunction
function EVAL(ast, env)
if !ListQ(a:ast)
return EvalAst(a:ast, a:env)
end
let first = ListFirst(a:ast)
let first_symbol = SymbolQ(first) ? ObjValue(first) : ""
if first_symbol == "def!"
let a1 = ObjValue(a:ast)[1]
let a2 = ObjValue(a:ast)[2]
let ret = a:env.set(ObjValue(a1), EVAL(a2, a:env))
return ret
elseif first_symbol == "let*"
let a1 = ObjValue(a:ast)[1]
let a2 = ObjValue(a:ast)[2]
let let_env = NewEnv(a:env)
let let_binds = ObjValue(a1)
let i = 0
while i < len(let_binds)
call let_env.set(ObjValue(let_binds[i]), EVAL(let_binds[i+1], let_env))
let i = i + 2
endwhile
return EVAL(a2, let_env)
elseif first_symbol == "if"
let condvalue = EVAL(ObjValue(a:ast)[1], a:env)
if FalseQ(condvalue) || NilQ(condvalue)
if len(ObjValue(a:ast)) < 4
return g:MalNil
else
return EVAL(ObjValue(a:ast)[3], a:env)
endif
else
return EVAL(ObjValue(a:ast)[2], a:env)
endif
elseif first_symbol == "do"
let el = EvalAst(ListRest(a:ast), a:env)
return ObjValue(el)[-1]
elseif first_symbol == "fn*"
let fn = NewFn(ListNth(a:ast, 2), a:env, ListNth(a:ast, 1))
return fn
else
" apply list
let el = EvalAst(a:ast, a:env)
let funcobj = ListFirst(el)
let args = ListRest(el)
if NativeFunctionQ(funcobj)
return NativeFuncInvoke(funcobj, args)
elseif FunctionQ(funcobj)
return FuncInvoke(funcobj, args)
else
throw "Not a function"
endif
endif
endfunction
function PRINT(exp)
return PrStr(a:exp, 1)
endfunction
function REP(str, env)
return PRINT(EVAL(READ(a:str), a:env))
endfunction
let repl_env = NewEnv("")
for [k, Fn] in items(CoreNs)
call repl_env.set(k, Fn)
endfor
call REP("(def! not (fn* (a) (if a false true)))", repl_env)
while 1
let [eof, line] = Readline("user> ")
if eof
break
endif
if line == ""
continue
endif
try
call PrintLn(REP(line, repl_env))
catch
call PrintLn("Error: " . v:exception)
endtry
endwhile
qall!

141
vimscript/step5_tco.vim Normal file
View File

@ -0,0 +1,141 @@
source readline.vim
source types.vim
source reader.vim
source printer.vim
source env.vim
source core.vim
function READ(str)
return ReadStr(a:str)
endfunction
function EvalAst(ast, env)
if SymbolQ(a:ast)
let varname = ObjValue(a:ast)
return a:env.get(varname)
elseif ListQ(a:ast)
let ret = []
for e in ObjValue(a:ast)
call add(ret, EVAL(e, a:env))
endfor
return ListNew(ret)
elseif VectorQ(a:ast)
let ret = []
for e in ObjValue(a:ast)
call add(ret, EVAL(e, a:env))
endfor
return VectorNew(ret)
elseif HashQ(a:ast)
let ret = {}
for [k,v] in items(ObjValue(a:ast))
let keyobj = HashParseKey(k)
let newkey = EVAL(keyobj, a:env)
let newval = EVAL(v, a:env)
let keystring = HashMakeKey(newkey)
let ret[keystring] = newval
endfor
return HashNew(ret)
else
return a:ast
end
endfunction
function EVAL(ast, env)
let ast = a:ast
let env = a:env
while 1
if !ListQ(ast)
return EvalAst(ast, env)
end
let first = ListFirst(ast)
let first_symbol = SymbolQ(first) ? ObjValue(first) : ""
if first_symbol == "def!"
let a1 = ObjValue(ast)[1]
let a2 = ObjValue(ast)[2]
let ret = env.set(ObjValue(a1), EVAL(a2, env))
return ret
elseif first_symbol == "let*"
let a1 = ObjValue(ast)[1]
let a2 = ObjValue(ast)[2]
let env = NewEnv(env)
let let_binds = ObjValue(a1)
let i = 0
while i < len(let_binds)
call env.set(ObjValue(let_binds[i]), EVAL(let_binds[i+1], env))
let i = i + 2
endwhile
let ast = a2
" TCO
elseif first_symbol == "if"
let condvalue = EVAL(ObjValue(ast)[1], env)
if FalseQ(condvalue) || NilQ(condvalue)
if len(ObjValue(ast)) < 4
return g:MalNil
else
let ast = ObjValue(ast)[3]
endif
else
let ast = ObjValue(ast)[2]
endif
" TCO
elseif first_symbol == "do"
let astlist = ObjValue(ast)
call EvalAst(ListNew(astlist[1:-2]), env)
let ast = astlist[-1]
" TCO
elseif first_symbol == "fn*"
let fn = NewFn(ListNth(ast, 2), env, ListNth(ast, 1))
return fn
else
" apply list
let el = EvalAst(ast, env)
let funcobj = ListFirst(el)
let args = ListRest(el)
if NativeFunctionQ(funcobj)
return NativeFuncInvoke(funcobj, args)
elseif FunctionQ(funcobj)
let fn = ObjValue(funcobj)
let ast = fn.ast
let env = NewEnvWithBinds(fn.env, fn.params, args)
" TCO
else
throw "Not a function"
endif
endif
endwhile
endfunction
function PRINT(exp)
return PrStr(a:exp, 1)
endfunction
function REP(str, env)
return PRINT(EVAL(READ(a:str), a:env))
endfunction
set maxfuncdepth=10000
let repl_env = NewEnv("")
for [k, v] in items(CoreNs)
call repl_env.set(k, v)
endfor
call REP("(def! not (fn* (a) (if a false true)))", repl_env)
while 1
let [eof, line] = Readline("user> ")
if eof
break
endif
if line == ""
continue
endif
try
call PrintLn(REP(line, repl_env))
catch
call PrintLn("Error: " . v:exception)
endtry
endwhile
qall!

166
vimscript/step6_file.vim Normal file
View File

@ -0,0 +1,166 @@
source readline.vim
source types.vim
source reader.vim
source printer.vim
source env.vim
source core.vim
function READ(str)
return ReadStr(a:str)
endfunction
function EvalAst(ast, env)
if SymbolQ(a:ast)
let varname = ObjValue(a:ast)
return a:env.get(varname)
elseif ListQ(a:ast)
let ret = []
for e in ObjValue(a:ast)
call add(ret, EVAL(e, a:env))
endfor
return ListNew(ret)
elseif VectorQ(a:ast)
let ret = []
for e in ObjValue(a:ast)
call add(ret, EVAL(e, a:env))
endfor
return VectorNew(ret)
elseif HashQ(a:ast)
let ret = {}
for [k,v] in items(ObjValue(a:ast))
let keyobj = HashParseKey(k)
let newkey = EVAL(keyobj, a:env)
let newval = EVAL(v, a:env)
let keystring = HashMakeKey(newkey)
let ret[keystring] = newval
endfor
return HashNew(ret)
else
return a:ast
end
endfunction
function EVAL(ast, env)
let ast = a:ast
let env = a:env
while 1
if !ListQ(ast)
return EvalAst(ast, env)
end
let first = ListFirst(ast)
let first_symbol = SymbolQ(first) ? ObjValue(first) : ""
if first_symbol == "def!"
let a1 = ObjValue(ast)[1]
let a2 = ObjValue(ast)[2]
let ret = env.set(ObjValue(a1), EVAL(a2, env))
return ret
elseif first_symbol == "let*"
let a1 = ObjValue(ast)[1]
let a2 = ObjValue(ast)[2]
let env = NewEnv(env)
let let_binds = ObjValue(a1)
let i = 0
while i < len(let_binds)
call env.set(ObjValue(let_binds[i]), EVAL(let_binds[i+1], env))
let i = i + 2
endwhile
let ast = a2
" TCO
elseif first_symbol == "if"
let condvalue = EVAL(ObjValue(ast)[1], env)
if FalseQ(condvalue) || NilQ(condvalue)
if len(ObjValue(ast)) < 4
return g:MalNil
else
let ast = ObjValue(ast)[3]
endif
else
let ast = ObjValue(ast)[2]
endif
" TCO
elseif first_symbol == "do"
let astlist = ObjValue(ast)
call EvalAst(ListNew(astlist[1:-2]), env)
let ast = astlist[-1]
" TCO
elseif first_symbol == "fn*"
let fn = NewFn(ListNth(ast, 2), env, ListNth(ast, 1))
return fn
elseif first_symbol == "eval"
let ast = EVAL(ListNth(ast, 1), env)
let env = env.root()
" TCO
else
" apply list
let el = EvalAst(ast, env)
let funcobj = ListFirst(el)
let args = ListRest(el)
if NativeFunctionQ(funcobj)
return NativeFuncInvoke(funcobj, args)
elseif FunctionQ(funcobj)
let fn = ObjValue(funcobj)
let ast = fn.ast
let env = NewEnvWithBinds(fn.env, fn.params, args)
" TCO
else
throw "Not a function"
endif
endif
endwhile
endfunction
function PRINT(exp)
return PrStr(a:exp, 1)
endfunction
function RE(str, env)
return EVAL(READ(a:str), a:env)
endfunction
function REP(str, env)
return PRINT(EVAL(READ(a:str), a:env))
endfunction
function GetArgvList()
let args = argv()
let list = []
for arg in args[1:]
call add(list, StringNew(arg))
endfor
return ListNew(list)
endfunction
set maxfuncdepth=10000
let repl_env = NewEnv("")
for [k, v] in items(CoreNs)
call repl_env.set(k, v)
endfor
call repl_env.set("*ARGV*", GetArgvList())
call RE("(def! not (fn* (a) (if a false true)))", repl_env)
call RE("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))", repl_env)
if !empty(argv())
call RE('(load-file "' . argv(0) . '")', repl_env)
qall!
endif
while 1
let [eof, line] = Readline("user> ")
if eof
break
endif
if line == ""
continue
endif
try
call PrintLn(REP(line, repl_env))
catch
call PrintLn("Error: " . v:exception)
endtry
endwhile
qall!

189
vimscript/step7_quote.vim Normal file
View File

@ -0,0 +1,189 @@
source readline.vim
source types.vim
source reader.vim
source printer.vim
source env.vim
source core.vim
function READ(str)
return ReadStr(a:str)
endfunction
function PairQ(obj)
return SequentialQ(a:obj) && !EmptyQ(a:obj)
endfunction
function Quasiquote(ast)
if !PairQ(a:ast)
return ListNew([SymbolNew("quote"), a:ast])
endif
let a0 = ListFirst(a:ast)
if SymbolQ(a0) && ObjValue(a0) == "unquote"
return ListNth(a:ast, 1)
elseif PairQ(a0) && SymbolQ(ListFirst(a0)) && ObjValue(ListFirst(a0)) == "splice-unquote"
return ListNew([SymbolNew("concat"), ListNth(a0, 1), Quasiquote(ListRest(a:ast))])
else
return ListNew([SymbolNew("cons"), Quasiquote(a0), Quasiquote(ListRest(a:ast))])
end
endfunction
function EvalAst(ast, env)
if SymbolQ(a:ast)
let varname = ObjValue(a:ast)
return a:env.get(varname)
elseif ListQ(a:ast)
let ret = []
for e in ObjValue(a:ast)
call add(ret, EVAL(e, a:env))
endfor
return ListNew(ret)
elseif VectorQ(a:ast)
let ret = []
for e in ObjValue(a:ast)
call add(ret, EVAL(e, a:env))
endfor
return VectorNew(ret)
elseif HashQ(a:ast)
let ret = {}
for [k,v] in items(ObjValue(a:ast))
let keyobj = HashParseKey(k)
let newkey = EVAL(keyobj, a:env)
let newval = EVAL(v, a:env)
let keystring = HashMakeKey(newkey)
let ret[keystring] = newval
endfor
return HashNew(ret)
else
return a:ast
end
endfunction
function EVAL(ast, env)
let ast = a:ast
let env = a:env
while 1
if !ListQ(ast)
return EvalAst(ast, env)
end
let first = ListFirst(ast)
let first_symbol = SymbolQ(first) ? ObjValue(first) : ""
if first_symbol == "def!"
let a1 = ObjValue(ast)[1]
let a2 = ObjValue(ast)[2]
let ret = env.set(ObjValue(a1), EVAL(a2, env))
return ret
elseif first_symbol == "let*"
let a1 = ObjValue(ast)[1]
let a2 = ObjValue(ast)[2]
let env = NewEnv(env)
let let_binds = ObjValue(a1)
let i = 0
while i < len(let_binds)
call env.set(ObjValue(let_binds[i]), EVAL(let_binds[i+1], env))
let i = i + 2
endwhile
let ast = a2
" TCO
elseif first_symbol == "quote"
return ListNth(ast, 1)
elseif first_symbol == "quasiquote"
let ast = Quasiquote(ListNth(ast, 1))
" TCO
elseif first_symbol == "if"
let condvalue = EVAL(ObjValue(ast)[1], env)
if FalseQ(condvalue) || NilQ(condvalue)
if len(ObjValue(ast)) < 4
return g:MalNil
else
let ast = ObjValue(ast)[3]
endif
else
let ast = ObjValue(ast)[2]
endif
" TCO
elseif first_symbol == "do"
let astlist = ObjValue(ast)
call EvalAst(ListNew(astlist[1:-2]), env)
let ast = astlist[-1]
" TCO
elseif first_symbol == "fn*"
let fn = NewFn(ListNth(ast, 2), env, ListNth(ast, 1))
return fn
elseif first_symbol == "eval"
let ast = EVAL(ListNth(ast, 1), env)
let env = env.root()
" TCO
else
" apply list
let el = EvalAst(ast, env)
let funcobj = ListFirst(el)
let args = ListRest(el)
if NativeFunctionQ(funcobj)
return NativeFuncInvoke(funcobj, args)
elseif FunctionQ(funcobj)
let fn = ObjValue(funcobj)
let ast = fn.ast
let env = NewEnvWithBinds(fn.env, fn.params, args)
" TCO
else
throw "Not a function"
endif
endif
endwhile
endfunction
function PRINT(exp)
return PrStr(a:exp, 1)
endfunction
function RE(str, env)
return EVAL(READ(a:str), a:env)
endfunction
function REP(str, env)
return PRINT(EVAL(READ(a:str), a:env))
endfunction
function GetArgvList()
let args = argv()
let list = []
for arg in args[1:]
call add(list, StringNew(arg))
endfor
return ListNew(list)
endfunction
set maxfuncdepth=10000
let repl_env = NewEnv("")
for [k, v] in items(CoreNs)
call repl_env.set(k, v)
endfor
call repl_env.set("*ARGV*", GetArgvList())
call RE("(def! not (fn* (a) (if a false true)))", repl_env)
call RE("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))", repl_env)
if !empty(argv())
call RE('(load-file "' . argv(0) . '")', repl_env)
qall!
endif
while 1
let [eof, line] = Readline("user> ")
if eof
break
endif
if line == ""
continue
endif
try
call PrintLn(REP(line, repl_env))
catch
call PrintLn("Error: " . v:exception)
endtry
endwhile
qall!

227
vimscript/step8_macros.vim Normal file
View File

@ -0,0 +1,227 @@
source readline.vim
source types.vim
source reader.vim
source printer.vim
source env.vim
source core.vim
function READ(str)
return ReadStr(a:str)
endfunction
function PairQ(obj)
return SequentialQ(a:obj) && !EmptyQ(a:obj)
endfunction
function Quasiquote(ast)
if !PairQ(a:ast)
return ListNew([SymbolNew("quote"), a:ast])
endif
let a0 = ListFirst(a:ast)
if SymbolQ(a0) && ObjValue(a0) == "unquote"
return ListNth(a:ast, 1)
elseif PairQ(a0) && SymbolQ(ListFirst(a0)) && ObjValue(ListFirst(a0)) == "splice-unquote"
return ListNew([SymbolNew("concat"), ListNth(a0, 1), Quasiquote(ListRest(a:ast))])
else
return ListNew([SymbolNew("cons"), Quasiquote(a0), Quasiquote(ListRest(a:ast))])
end
endfunction
function IsMacroCall(ast, env)
if !ListQ(a:ast)
return 0
endif
let a0 = ListFirst(a:ast)
if !SymbolQ(a0)
return 0
endif
let macroname = ObjValue(a0)
if empty(a:env.find(macroname))
return 0
endif
return MacroQ(a:env.get(macroname))
endfunction
function MacroExpand(ast, env)
let ast = a:ast
while IsMacroCall(ast, a:env)
let macroobj = a:env.get(ObjValue(ListFirst(ast)))
let macroargs = ListRest(ast)
let ast = FuncInvoke(macroobj, macroargs)
endwhile
return ast
endfunction
function EvalAst(ast, env)
if SymbolQ(a:ast)
let varname = ObjValue(a:ast)
return a:env.get(varname)
elseif ListQ(a:ast)
let ret = []
for e in ObjValue(a:ast)
call add(ret, EVAL(e, a:env))
endfor
return ListNew(ret)
elseif VectorQ(a:ast)
let ret = []
for e in ObjValue(a:ast)
call add(ret, EVAL(e, a:env))
endfor
return VectorNew(ret)
elseif HashQ(a:ast)
let ret = {}
for [k,v] in items(ObjValue(a:ast))
let keyobj = HashParseKey(k)
let newkey = EVAL(keyobj, a:env)
let newval = EVAL(v, a:env)
let keystring = HashMakeKey(newkey)
let ret[keystring] = newval
endfor
return HashNew(ret)
else
return a:ast
end
endfunction
function EVAL(ast, env)
let ast = a:ast
let env = a:env
while 1
if !ListQ(ast)
return EvalAst(ast, env)
end
let ast = MacroExpand(ast, env)
if !ListQ(ast)
return ast
end
let first = ListFirst(ast)
let first_symbol = SymbolQ(first) ? ObjValue(first) : ""
if first_symbol == "def!"
let a1 = ObjValue(ast)[1]
let a2 = ObjValue(ast)[2]
return env.set(ObjValue(a1), EVAL(a2, env))
elseif first_symbol == "let*"
let a1 = ObjValue(ast)[1]
let a2 = ObjValue(ast)[2]
let env = NewEnv(env)
let let_binds = ObjValue(a1)
let i = 0
while i < len(let_binds)
call env.set(ObjValue(let_binds[i]), EVAL(let_binds[i+1], env))
let i = i + 2
endwhile
let ast = a2
" TCO
elseif first_symbol == "quote"
return ListNth(ast, 1)
elseif first_symbol == "quasiquote"
let ast = Quasiquote(ListNth(ast, 1))
" TCO
elseif first_symbol == "defmacro!"
let a1 = ListNth(ast, 1)
let a2 = ListNth(ast, 2)
let macro = MarkAsMacro(EVAL(a2, env))
return env.set(ObjValue(a1), macro)
elseif first_symbol == "macroexpand"
return MacroExpand(ListNth(ast, 1), env)
elseif first_symbol == "if"
let condvalue = EVAL(ObjValue(ast)[1], env)
if FalseQ(condvalue) || NilQ(condvalue)
if len(ObjValue(ast)) < 4
return g:MalNil
else
let ast = ObjValue(ast)[3]
endif
else
let ast = ObjValue(ast)[2]
endif
" TCO
elseif first_symbol == "do"
let astlist = ObjValue(ast)
call EvalAst(ListNew(astlist[1:-2]), env)
let ast = astlist[-1]
" TCO
elseif first_symbol == "fn*"
let fn = NewFn(ListNth(ast, 2), env, ListNth(ast, 1))
return fn
elseif first_symbol == "eval"
let ast = EVAL(ListNth(ast, 1), env)
let env = env.root()
" TCO
else
" apply list
let el = EvalAst(ast, env)
let funcobj = ListFirst(el)
let args = ListRest(el)
if NativeFunctionQ(funcobj)
return NativeFuncInvoke(funcobj, args)
elseif FunctionQ(funcobj)
let fn = ObjValue(funcobj)
let ast = fn.ast
let env = NewEnvWithBinds(fn.env, fn.params, args)
" TCO
else
throw "Not a function"
endif
endif
endwhile
endfunction
function PRINT(exp)
return PrStr(a:exp, 1)
endfunction
function RE(str, env)
return EVAL(READ(a:str), a:env)
endfunction
function REP(str, env)
return PRINT(EVAL(READ(a:str), a:env))
endfunction
function GetArgvList()
let args = argv()
let list = []
for arg in args[1:]
call add(list, StringNew(arg))
endfor
return ListNew(list)
endfunction
set maxfuncdepth=10000
let repl_env = NewEnv("")
for [k, v] in items(CoreNs)
call repl_env.set(k, v)
endfor
call repl_env.set("*ARGV*", GetArgvList())
call RE("(def! not (fn* (a) (if a false true)))", repl_env)
call RE("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))", repl_env)
call RE("(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))", repl_env)
call RE("(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) `(let* (or_FIXME ~(first xs)) (if or_FIXME or_FIXME (or ~@(rest xs))))))))", repl_env)
if !empty(argv())
call RE('(load-file "' . argv(0) . '")', repl_env)
qall!
endif
while 1
let [eof, line] = Readline("user> ")
if eof
break
endif
if line == ""
continue
endif
try
call PrintLn(REP(line, repl_env))
catch
call PrintLn("Error: " . v:exception)
endtry
endwhile
qall!

263
vimscript/step9_try.vim Normal file
View File

@ -0,0 +1,263 @@
source readline.vim
source types.vim
source reader.vim
source printer.vim
source env.vim
source core.vim
let MalExceptionObj = ""
function READ(str)
return ReadStr(a:str)
endfunction
function PairQ(obj)
return SequentialQ(a:obj) && !EmptyQ(a:obj)
endfunction
function Quasiquote(ast)
if !PairQ(a:ast)
return ListNew([SymbolNew("quote"), a:ast])
endif
let a0 = ListFirst(a:ast)
if SymbolQ(a0) && ObjValue(a0) == "unquote"
return ListNth(a:ast, 1)
elseif PairQ(a0) && SymbolQ(ListFirst(a0)) && ObjValue(ListFirst(a0)) == "splice-unquote"
return ListNew([SymbolNew("concat"), ListNth(a0, 1), Quasiquote(ListRest(a:ast))])
else
return ListNew([SymbolNew("cons"), Quasiquote(a0), Quasiquote(ListRest(a:ast))])
end
endfunction
function IsMacroCall(ast, env)
if !ListQ(a:ast)
return 0
endif
let a0 = ListFirst(a:ast)
if !SymbolQ(a0)
return 0
endif
let macroname = ObjValue(a0)
if empty(a:env.find(macroname))
return 0
endif
return MacroQ(a:env.get(macroname))
endfunction
function MacroExpand(ast, env)
let ast = a:ast
while IsMacroCall(ast, a:env)
let macroobj = a:env.get(ObjValue(ListFirst(ast)))
let macroargs = ListRest(ast)
let ast = FuncInvoke(macroobj, macroargs)
endwhile
return ast
endfunction
function EvalAst(ast, env)
if SymbolQ(a:ast)
let varname = ObjValue(a:ast)
return a:env.get(varname)
elseif ListQ(a:ast)
let ret = []
for e in ObjValue(a:ast)
call add(ret, EVAL(e, a:env))
endfor
return ListNew(ret)
elseif VectorQ(a:ast)
let ret = []
for e in ObjValue(a:ast)
call add(ret, EVAL(e, a:env))
endfor
return VectorNew(ret)
elseif HashQ(a:ast)
let ret = {}
for [k,v] in items(ObjValue(a:ast))
let keyobj = HashParseKey(k)
let newkey = EVAL(keyobj, a:env)
let newval = EVAL(v, a:env)
let keystring = HashMakeKey(newkey)
let ret[keystring] = newval
endfor
return HashNew(ret)
else
return a:ast
end
endfunction
function GetCatchClause(ast)
if ListCount(a:ast) < 3
return ""
end
let catch_clause = ListNth(a:ast, 2)
if ListFirst(catch_clause) == SymbolNew("catch*")
return catch_clause
else
return ""
end
endfunction
function EVAL(ast, env)
let ast = a:ast
let env = a:env
while 1
if !ListQ(ast)
return EvalAst(ast, env)
end
let ast = MacroExpand(ast, env)
if !ListQ(ast)
return ast
end
let first = ListFirst(ast)
let first_symbol = SymbolQ(first) ? ObjValue(first) : ""
if first_symbol == "def!"
let a1 = ObjValue(ast)[1]
let a2 = ObjValue(ast)[2]
return env.set(ObjValue(a1), EVAL(a2, env))
elseif first_symbol == "let*"
let a1 = ObjValue(ast)[1]
let a2 = ObjValue(ast)[2]
let env = NewEnv(env)
let let_binds = ObjValue(a1)
let i = 0
while i < len(let_binds)
call env.set(ObjValue(let_binds[i]), EVAL(let_binds[i+1], env))
let i = i + 2
endwhile
let ast = a2
" TCO
elseif first_symbol == "quote"
return ListNth(ast, 1)
elseif first_symbol == "quasiquote"
let ast = Quasiquote(ListNth(ast, 1))
" TCO
elseif first_symbol == "defmacro!"
let a1 = ListNth(ast, 1)
let a2 = ListNth(ast, 2)
let macro = MarkAsMacro(EVAL(a2, env))
return env.set(ObjValue(a1), macro)
elseif first_symbol == "macroexpand"
return MacroExpand(ListNth(ast, 1), env)
elseif first_symbol == "if"
let condvalue = EVAL(ObjValue(ast)[1], env)
if FalseQ(condvalue) || NilQ(condvalue)
if len(ObjValue(ast)) < 4
return g:MalNil
else
let ast = ObjValue(ast)[3]
endif
else
let ast = ObjValue(ast)[2]
endif
" TCO
elseif first_symbol == "try*"
try
return EVAL(ListNth(ast, 1), env)
catch
let catch_clause = GetCatchClause(ast)
if empty(catch_clause)
throw v:exception
endif
let exc_var = ObjValue(ListNth(catch_clause, 1))
if v:exception == "__MalException__"
let exc_value = g:MalExceptionObj
else
let exc_value = StringNew(v:exception)
endif
let catch_env = NewEnvWithBinds(env, ListNew([SymbolNew(exc_var)]), ListNew([exc_value]))
return EVAL(ListNth(catch_clause, 2), catch_env)
endtry
elseif first_symbol == "do"
let astlist = ObjValue(ast)
call EvalAst(ListNew(astlist[1:-2]), env)
let ast = astlist[-1]
" TCO
elseif first_symbol == "fn*"
let fn = NewFn(ListNth(ast, 2), env, ListNth(ast, 1))
return fn
elseif first_symbol == "eval"
let ast = EVAL(ListNth(ast, 1), env)
let env = env.root()
" TCO
else
" apply list
let el = EvalAst(ast, env)
let funcobj = ListFirst(el)
let args = ListRest(el)
if NativeFunctionQ(funcobj)
return NativeFuncInvoke(funcobj, args)
elseif FunctionQ(funcobj)
let fn = ObjValue(funcobj)
let ast = fn.ast
let env = NewEnvWithBinds(fn.env, fn.params, args)
" TCO
else
throw "Not a function"
endif
endif
endwhile
endfunction
function PRINT(exp)
return PrStr(a:exp, 1)
endfunction
function RE(str, env)
return EVAL(READ(a:str), a:env)
endfunction
function REP(str, env)
return PRINT(EVAL(READ(a:str), a:env))
endfunction
function GetArgvList()
let args = argv()
let list = []
for arg in args[1:]
call add(list, StringNew(arg))
endfor
return ListNew(list)
endfunction
set maxfuncdepth=10000
let repl_env = NewEnv("")
for [k, v] in items(CoreNs)
call repl_env.set(k, v)
endfor
call repl_env.set("*ARGV*", GetArgvList())
call RE("(def! not (fn* (a) (if a false true)))", repl_env)
call RE("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))", repl_env)
call RE("(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))", repl_env)
call RE("(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) `(let* (or_FIXME ~(first xs)) (if or_FIXME or_FIXME (or ~@(rest xs))))))))", repl_env)
if !empty(argv())
try
call RE('(load-file "' . argv(0) . '")', repl_env)
catch
call PrintLn("Error: " . v:exception)
endtry
qall!
endif
while 1
let [eof, line] = Readline("user> ")
if eof
break
endif
if line == ""
continue
endif
try
call PrintLn(REP(line, repl_env))
catch
call PrintLn("Error: " . v:exception)
endtry
endwhile
qall!

266
vimscript/stepA_mal.vim Normal file
View File

@ -0,0 +1,266 @@
source readline.vim
source types.vim
source reader.vim
source printer.vim
source env.vim
source core.vim
let MalExceptionObj = ""
function READ(str)
return ReadStr(a:str)
endfunction
function PairQ(obj)
return SequentialQ(a:obj) && !EmptyQ(a:obj)
endfunction
function Quasiquote(ast)
if !PairQ(a:ast)
return ListNew([SymbolNew("quote"), a:ast])
endif
let a0 = ListFirst(a:ast)
if SymbolQ(a0) && ObjValue(a0) == "unquote"
return ListNth(a:ast, 1)
elseif PairQ(a0) && SymbolQ(ListFirst(a0)) && ObjValue(ListFirst(a0)) == "splice-unquote"
return ListNew([SymbolNew("concat"), ListNth(a0, 1), Quasiquote(ListRest(a:ast))])
else
return ListNew([SymbolNew("cons"), Quasiquote(a0), Quasiquote(ListRest(a:ast))])
end
endfunction
function IsMacroCall(ast, env)
if !ListQ(a:ast)
return 0
endif
let a0 = ListFirst(a:ast)
if !SymbolQ(a0)
return 0
endif
let macroname = ObjValue(a0)
if empty(a:env.find(macroname))
return 0
endif
return MacroQ(a:env.get(macroname))
endfunction
function MacroExpand(ast, env)
let ast = a:ast
while IsMacroCall(ast, a:env)
let macroobj = a:env.get(ObjValue(ListFirst(ast)))
let macroargs = ListRest(ast)
let ast = FuncInvoke(macroobj, macroargs)
endwhile
return ast
endfunction
function EvalAst(ast, env)
if SymbolQ(a:ast)
let varname = ObjValue(a:ast)
return a:env.get(varname)
elseif ListQ(a:ast)
let ret = []
for e in ObjValue(a:ast)
call add(ret, EVAL(e, a:env))
endfor
return ListNew(ret)
elseif VectorQ(a:ast)
let ret = []
for e in ObjValue(a:ast)
call add(ret, EVAL(e, a:env))
endfor
return VectorNew(ret)
elseif HashQ(a:ast)
let ret = {}
for [k,v] in items(ObjValue(a:ast))
let keyobj = HashParseKey(k)
let newkey = EVAL(keyobj, a:env)
let newval = EVAL(v, a:env)
let keystring = HashMakeKey(newkey)
let ret[keystring] = newval
endfor
return HashNew(ret)
else
return a:ast
end
endfunction
function GetCatchClause(ast)
if ListCount(a:ast) < 3
return ""
end
let catch_clause = ListNth(a:ast, 2)
if ListFirst(catch_clause) == SymbolNew("catch*")
return catch_clause
else
return ""
end
endfunction
function EVAL(ast, env)
let ast = a:ast
let env = a:env
while 1
if !ListQ(ast)
return EvalAst(ast, env)
end
let ast = MacroExpand(ast, env)
if !ListQ(ast)
return ast
end
let first = ListFirst(ast)
let first_symbol = SymbolQ(first) ? ObjValue(first) : ""
if first_symbol == "def!"
let a1 = ObjValue(ast)[1]
let a2 = ObjValue(ast)[2]
return env.set(ObjValue(a1), EVAL(a2, env))
elseif first_symbol == "let*"
let a1 = ObjValue(ast)[1]
let a2 = ObjValue(ast)[2]
let env = NewEnv(env)
let let_binds = ObjValue(a1)
let i = 0
while i < len(let_binds)
call env.set(ObjValue(let_binds[i]), EVAL(let_binds[i+1], env))
let i = i + 2
endwhile
let ast = a2
" TCO
elseif first_symbol == "quote"
return ListNth(ast, 1)
elseif first_symbol == "quasiquote"
let ast = Quasiquote(ListNth(ast, 1))
" TCO
elseif first_symbol == "defmacro!"
let a1 = ListNth(ast, 1)
let a2 = ListNth(ast, 2)
let macro = MarkAsMacro(EVAL(a2, env))
return env.set(ObjValue(a1), macro)
elseif first_symbol == "macroexpand"
return MacroExpand(ListNth(ast, 1), env)
elseif first_symbol == "if"
let condvalue = EVAL(ObjValue(ast)[1], env)
if FalseQ(condvalue) || NilQ(condvalue)
if len(ObjValue(ast)) < 4
return g:MalNil
else
let ast = ObjValue(ast)[3]
endif
else
let ast = ObjValue(ast)[2]
endif
" TCO
elseif first_symbol == "try*"
try
return EVAL(ListNth(ast, 1), env)
catch
let catch_clause = GetCatchClause(ast)
if empty(catch_clause)
throw v:exception
endif
let exc_var = ObjValue(ListNth(catch_clause, 1))
if v:exception == "__MalException__"
let exc_value = g:MalExceptionObj
else
let exc_value = StringNew(v:exception)
endif
let catch_env = NewEnvWithBinds(env, ListNew([SymbolNew(exc_var)]), ListNew([exc_value]))
return EVAL(ListNth(catch_clause, 2), catch_env)
endtry
elseif first_symbol == "do"
let astlist = ObjValue(ast)
call EvalAst(ListNew(astlist[1:-2]), env)
let ast = astlist[-1]
" TCO
elseif first_symbol == "fn*"
let fn = NewFn(ListNth(ast, 2), env, ListNth(ast, 1))
return fn
elseif first_symbol == "eval"
let ast = EVAL(ListNth(ast, 1), env)
let env = env.root()
" TCO
else
" apply list
let el = EvalAst(ast, env)
let funcobj = ListFirst(el)
let args = ListRest(el)
if NativeFunctionQ(funcobj)
return NativeFuncInvoke(funcobj, args)
elseif FunctionQ(funcobj)
let fn = ObjValue(funcobj)
let ast = fn.ast
let env = NewEnvWithBinds(fn.env, fn.params, args)
" TCO
else
throw "Not a function"
endif
endif
endwhile
endfunction
function PRINT(exp)
return PrStr(a:exp, 1)
endfunction
function RE(str, env)
return EVAL(READ(a:str), a:env)
endfunction
function REP(str, env)
return PRINT(EVAL(READ(a:str), a:env))
endfunction
function GetArgvList()
let args = argv()
let list = []
for arg in args[1:]
call add(list, StringNew(arg))
endfor
return ListNew(list)
endfunction
set maxfuncdepth=10000
let repl_env = NewEnv("")
for [k, v] in items(CoreNs)
call repl_env.set(k, v)
endfor
call repl_env.set("*ARGV*", GetArgvList())
call RE("(def! *host-language* \"vimscript\")", repl_env)
call RE("(def! not (fn* (a) (if a false true)))", repl_env)
call RE("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))", repl_env)
call RE("(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))", repl_env)
call RE("(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) `(let* (or_FIXME ~(first xs)) (if or_FIXME or_FIXME (or ~@(rest xs))))))))", repl_env)
if !empty(argv())
try
call RE('(load-file "' . argv(0) . '")', repl_env)
catch
call PrintLn("Error: " . v:exception)
endtry
qall!
endif
call REP("(println (str \"Mal [\" *host-language* \"]\"))", repl_env)
while 1
let [eof, line] = Readline("user> ")
if eof
break
endif
if line == ""
continue
endif
try
call PrintLn(REP(line, repl_env))
catch
call PrintLn("Error: " . v:exception)
endtry
endwhile
qall!

View File

@ -0,0 +1,41 @@
;; Testing basic Vim interop with (vim* "...")
;;
(vim* "7")
;=>7
(vim* "'7'")
;=>"7"
(vim* "[7,8,9]")
;=>(7 8 9)
(vim* "{\"abc\": 789}")
;=>{"abc" 789}
;;
;; Test Vim eval() expression support
;;
(vim* "3 + 7 * 8")
;=>59
(vim* "join(['a','b','c'], '_')")
;=>"a_b_c"
(vim* "split('d@@@@e@f@@g', '@\+')")
;=>("d" "e" "f" "g")
(vim* "add([1,2,3], 4)")
;=>(1 2 3 4)
;;
;; Test access to Vim predefined variables
;;
(vim* "v:progname")
;=>"vim"
;; v:version is 704 for Vim 7.4
(> (vim* "v:version") 700)
;=>true

286
vimscript/types.vim Normal file
View File

@ -0,0 +1,286 @@
" types module
function ObjNewWithMeta(obj_type, obj_val, obj_meta)
return {"type": a:obj_type, "val": a:obj_val, "meta": a:obj_meta}
endfunction
function ObjNew(obj_type, obj_val)
return {"type": a:obj_type, "val": a:obj_val}
endfunction
function ObjType(obj)
return a:obj["type"]
endfunction
function ObjValue(obj)
return a:obj["val"]
endfunction
function ObjHasMeta(obj)
return ObjQ(a:obj) && has_key(a:obj, "meta")
endfunction
function ObjMeta(obj)
return ObjHasMeta(a:obj) ? a:obj["meta"] : g:MalNil
endfunction
function ObjSetValue(obj, newval)
let a:obj["val"] = a:newval
return a:newval
endfunction
function ObjSetMeta(obj, newmeta)
let a:obj["meta"] = a:newmeta
return a:newmeta
endfunction
function ObjQ(obj)
return type(a:obj) == type({})
endfunction
function SymbolQ(obj)
return ObjQ(a:obj) && ObjType(a:obj) == "symbol"
endfunction
function StringQ(obj)
return ObjQ(a:obj) && ObjType(a:obj) == "string"
endfunction
function KeywordQ(obj)
return ObjQ(a:obj) && ObjType(a:obj) == "keyword"
endfunction
function AtomQ(obj)
return ObjQ(a:obj) && ObjType(a:obj) == "atom"
endfunction
function NilQ(obj)
return ObjQ(a:obj) && ObjType(a:obj) == "nil"
endfunction
function TrueQ(obj)
return ObjQ(a:obj) && ObjType(a:obj) == "true"
endfunction
function FalseQ(obj)
return ObjQ(a:obj) && ObjType(a:obj) == "false"
endfunction
function IntegerQ(obj)
return ObjQ(a:obj) && ObjType(a:obj) == "integer"
endfunction
function FloatQ(obj)
return ObjQ(a:obj) && ObjType(a:obj) == "float"
endfunction
function ListQ(obj)
return ObjQ(a:obj) && ObjType(a:obj) == "list"
endfunction
function VectorQ(obj)
return ObjQ(a:obj) && ObjType(a:obj) == "vector"
endfunction
function SequentialQ(obj)
return ObjQ(a:obj) && ListQ(a:obj) || VectorQ(a:obj)
endfunction
function HashQ(obj)
return ObjQ(a:obj) && ObjType(a:obj) == "hash"
endfunction
function FunctionQ(obj)
return ObjQ(a:obj) && ObjType(a:obj) == "function" && !ObjValue(a:obj).is_macro
endfunction
function MacroQ(obj)
return ObjQ(a:obj) && ObjType(a:obj) == "function" && ObjValue(a:obj).is_macro
endfunction
function NativeFunctionQ(obj)
return ObjQ(a:obj) && ObjType(a:obj) == "nativefunction"
endfunction
function NilNew()
return ObjNew("nil", "")
endfunction
function TrueNew()
return ObjNew("true", "")
endfunction
function FalseNew()
return ObjNew("false", "")
endfunction
function BoolNew(bool)
return a:bool ? g:MalTrue : g:MalFalse
endfunction
function KeywordNew(val)
return ObjNew("keyword", a:val)
endfunction
function AtomNew(val)
return ObjNewWithMeta("atom", a:val, g:MalNil)
endfunction
function SymbolNew(val)
return ObjNew("symbol", a:val)
endfunction
function StringNew(val)
return ObjNew("string", a:val)
endfunction
function IntegerNew(val)
return ObjNew("integer", a:val)
endfunction
function FloatNew(val)
return ObjNew("float", a:val)
endfunction
function ListNew(val)
return ObjNewWithMeta("list", a:val, g:MalNil)
endfunction
function VectorNew(val)
return ObjNewWithMeta("vector", a:val, g:MalNil)
endfunction
function HashNew(val)
return ObjNewWithMeta("hash", a:val, g:MalNil)
endfunction
function HashMakeKey(obj)
if !StringQ(a:obj) && !KeywordQ(a:obj)
throw "expected hash-map key string, got: " . ObjType(a:obj));
endif
return ObjType(a:obj) . "#" . ObjValue(a:obj)
endfunction
function HashParseKey(str)
if a:str =~ "^string#"
return StringNew(a:str[7:])
elseif a:str =~ "^keyword#"
return KeywordNew(a:str[8:])
endif
endfunction
function HashBuild(elements)
if (len(a:elements) % 2) != 0
throw "Odd number of hash-map arguments"
endif
let i = 0
let hash = {}
while i < len(a:elements)
let key = a:elements[i]
let val = a:elements[i + 1]
let keystring = HashMakeKey(key)
let hash[keystring] = val
let i = i + 2
endwhile
return HashNew(hash)
endfunction
function HashEqualQ(x, y)
if len(ObjValue(a:x)) != len(ObjValue(a:y))
return 0
endif
for k in keys(ObjValue(a:x))
let vx = ObjValue(a:x)[k]
let vy = ObjValue(a:y)[k]
if empty(vy) || !EqualQ(vx, vy)
return 0
endif
endfor
return 1
endfunction
function SequentialEqualQ(x, y)
if len(ObjValue(a:x)) != len(ObjValue(a:y))
return 0
endif
let i = 0
while i < len(ObjValue(a:x))
let ex = ObjValue(a:x)[i]
let ey = ObjValue(a:y)[i]
if !EqualQ(ex, ey)
return 0
endif
let i = i +1
endwhile
return 1
endfunction
function EqualQ(x, y)
if SequentialQ(a:x) && SequentialQ(a:y)
return SequentialEqualQ(a:x, a:y)
elseif HashQ(a:x) && HashQ(a:y)
return HashEqualQ(a:x, a:y)
elseif ObjType(a:x) != ObjType(a:y)
return 0
else
return ObjValue(a:x) == ObjValue(a:y)
endif
endfunction
function EmptyQ(list)
return empty(ObjValue(a:list))
endfunction
function ListCount(list)
return len(ObjValue(a:list))
endfunction
function ListNth(list, index)
if a:index >= len(ObjValue(a:list))
throw "nth: index out of range"
endif
return ObjValue(a:list)[a:index]
endfunction
function ListFirst(list)
return get(ObjValue(a:list), 0, g:MalNil)
endfunction
function ListDrop(list, drop_elements)
return ListNew(ObjValue(a:list)[a:drop_elements :])
endfunction
function ListRest(list)
return ListDrop(a:list, 1)
endfunction
function FuncInvoke(funcobj, args)
let fn = ObjValue(a:funcobj)
let funcenv = NewEnvWithBinds(fn.env, fn.params, a:args)
return EVAL(fn.ast, funcenv)
endfunction
function NativeFuncInvoke(funcobj, argslist)
let fn = ObjValue(a:funcobj)
return fn.Func(ObjValue(a:argslist))
endfunction
function MarkAsMacro(funcobj)
let fn = ObjValue(a:funcobj)
let fn.is_macro = 1
return a:funcobj
endfunction
function NewFn(ast, env, params)
let fn = {"ast": a:ast, "env": a:env, "params": a:params, "is_macro": 0}
return ObjNewWithMeta("function", fn, g:MalNil)
endfunction
function NewNativeFn(funcname)
let fn = {"Func": function(a:funcname), "name": a:funcname}
return ObjNewWithMeta("nativefunction", fn, g:MalNil)
endfunction
let g:MalNil = NilNew()
let g:MalTrue = TrueNew()
let g:MalFalse = FalseNew()

27
vimscript/vimreadline.c Normal file
View File

@ -0,0 +1,27 @@
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <readline/readline.h>
/*
* Vim interface for the readline(3) function.
*
* Prints 'prompt' and reads a line from the input. If EOF is encountered,
* returns the string "E"; otherwise, returns the string "S<line>" where <line>
* is the line read from input.
*
* This function is not thread-safe.
*/
char* vimreadline(char* prompt) {
static char buf[1024];
char* res = readline(prompt);
if (res) {
buf[0] = 'S';
strncpy(buf + 1, res, sizeof(buf) - 1);
free(res);
} else {
buf[0] = 'E';
buf[1] = '\0';
}
return buf;
}