mirror of
https://github.com/kanaka/mal.git
synced 2024-09-11 13:55:55 +03:00
Vimscript implementation
This commit is contained in:
parent
406761e769
commit
50a964ce14
8
Makefile
8
Makefile
@ -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)))
|
||||
|
15
README.md
15
README.md
@ -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
2
vimscript/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
/*.o
|
||||
/*.so
|
24
vimscript/Dockerfile
Normal file
24
vimscript/Dockerfile
Normal 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
21
vimscript/Makefile
Normal 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
397
vimscript/core.vim
Normal 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
62
vimscript/env.vim
Normal 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
60
vimscript/printer.vim
Normal 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
152
vimscript/reader.vim
Normal 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
23
vimscript/readline.vim
Normal 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
15
vimscript/run_vimscript.sh
Executable 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
29
vimscript/step0_repl.vim
Normal 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!
|
36
vimscript/step1_read_print.vim
Normal file
36
vimscript/step1_read_print.vim
Normal 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
100
vimscript/step2_eval.vim
Normal 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
116
vimscript/step3_env.vim
Normal 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!
|
128
vimscript/step4_if_fn_do.vim
Normal file
128
vimscript/step4_if_fn_do.vim
Normal 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
141
vimscript/step5_tco.vim
Normal 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
166
vimscript/step6_file.vim
Normal 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
189
vimscript/step7_quote.vim
Normal 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
227
vimscript/step8_macros.vim
Normal 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
263
vimscript/step9_try.vim
Normal 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
266
vimscript/stepA_mal.vim
Normal 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!
|
41
vimscript/tests/stepA_mal.mal
Normal file
41
vimscript/tests/stepA_mal.mal
Normal 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
286
vimscript/types.vim
Normal 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
27
vimscript/vimreadline.c
Normal 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;
|
||||
}
|
Loading…
Reference in New Issue
Block a user