Skip to content

Instantly share code, notes, and snippets.

@fowlmouth
Last active August 29, 2015 14:01
Show Gist options
  • Save fowlmouth/67c4916e6bf2cb143f45 to your computer and use it in GitHub Desktop.
Save fowlmouth/67c4916e6bf2cb143f45 to your computer and use it in GitHub Desktop.
import parsers/xjson, json, fowltek/maybe_t
import tables, strutils, future
type
TVariable = tuple[mutable:bool, val:PJsonNode]
Env = ref object
scope: TTable[string, TVariable]
parent: TMaybe[Env]
iset: InstructionSet
InstructionSet* = ref object
instructions: TTable[string, TInstruction]
TInstruction* = proc(e:Env; statement:PJsonNode): PJsonNode
proc `$`* (some:Env): string =
result = "Env "& $some.scope.len
for k,v in some.scope.pairs:
result.add "\L "
result.add k
result.add " = "
result.add($v)
if some.parent.has:
result.add '\L'
result.add($some.parent.val)
proc resolve_ident (e:Env; s:string): PJsonNode =
result = e.scope[s].val
if result.isNil and e.parent.has:
result = e.parent.val.resolve_ident(s)
proc evaluate_stmt (e: Env; statement: PJsonNode): PJsonNode
proc resolve (e:Env; value:PJsonNode): PJsonNode =
case value.kind
of jString:
return e.resolve_ident(value.str)
of jArray:
return e.evaluate_stmt(value)
else:
return value
proc findVar* (e:Env; name:string): TMaybe[ptr TVariable] =
if e.scope.hasKey(name):
result = just e.scope.mget(name).addr
elif e.parent.has:
result = e.parent.val.findVar(name)
proc copy* (iset: InstructionSet): InstructionSet =
InstructionSet(instructions: iset.instructions)
# call iset std lib
var
iset = InstructionSet(instructions: initTable[string,TInstruction]())
template def_cmd(cmd; body:stmt):stmt {.immediate.}=
#
iset.instructions[cmd] = proc(e:Env; statement:PJsonNode): PJsonNode =
body
template each_arg (name;body2:stmt):stmt{.immediate.}=
for idx in 1 .. < len(statement):
let name = statement[idx]
body2
def_cmd("let"):
each_arg(arg):
let n = arg[0].str
let v = e.resolve(arg[1]).copy
e.scope[n] = (false, v)
def_cmd("variable"):
each_arg(arg):
var
name: PJsonNode
value: TMaybe[PJsonNode]
if arg.kind == jArray:
name = arg[0]
value = e.resolve(arg[1]).copy.maybe
else:
name = arg
if not value.has:
value = just(new_j_null())
e.scope[name.str] = (true, value.val)
def_cmd("print"):
each_arg(arg):
let x = e.resolve(arg)
stdout.write case x.kind
of jString: x.str
else: $ x
stdout.write '\L'
def_cmd("string"):
result = statement[1]
def_cmd("to-string"):
result = e.resolve(statement[1])
if result.kind != jString:
result = new_j_string($ result)
def_cmd("array"):
result = new_j_array(statement.elems[1.. -1])
def_cmd( "+=" ):
result = e.resolve(statement[1])
assert result.kind == jInt
result.num += e.resolve(statement[2]).num
proc newEnv* (parent: Env = nil): Env =
result = Env( scope: initTable[string,TVariable](), parent: maybe(parent) )
result.iset = (if result.parent.has: result.parent.val.iset else: iset)
proc execute_instructions* (e:Env; iseq: PJsonNode): PJsonNode
def_cmd( "while" ):
while e.resolve(statement[1]).bval:
result = newEnv(e).execute_instructions(statement[2])
def_cmd("return"):
result = e.resolve(statement[1])
def_cmd("scope"):
# open a new scope, the argument is an iseq
result = newEnv(e).execute_instructions(statement[1])
def_cmd("format"):
result = %(statement[1].str.format(statement.elems[2.. -1].map((it:PJsonNode) => $e.resolve(it))))
def_cmd("<="):
let
a = e.resolve(statement[1])
b = e.resolve(statement[2])
%(a.num <= b.num)
def_cmd("<"):
let
a = e.resolve(statement[1])
b = e.resolve(statement[2])
%(a.num < b.num)
def_cmd("set"):
# set a variable to a new value
if (let(has,v) = e.findVar(statement[1].str); has):
if v.mutable:
v.val = e.resolve(statement[2]).copy
else:
quit "variable "& statement[1].str& " is not mutable."
else:
quit "not find variable "& statement[1].str
def_cmd("eval"):
newEnv(e).execute_instructions(statement[1])
proc evaluate_stmt (e: Env; statement: PJsonNode): PJsonNode =
let f = e.iset.instructions[statement[0].str]
if not f.isNil:
result = f(e, statement)
return
echo "Unhandled instruction ", statement
proc execute_instructions (e:Env; iseq: PJsonNode): PJsonNode =
let H = iseq.elems.high
for idx, statement in iseq.elems.pairs:
result = e.evaluate_stmt statement
proc call (e:Env; f:PJsonNode; args:openarray[PJsonNode]): PJsonNode =
let e = newEnv(e)
# insert args as params
echo f
for i, argname in f["args"].elems.pairs:
e.scope[argname.str] = (false, args[i])
e.execute_instructions f["instructions"]
type Program = tuple[program:PJsonNode, e:Env]
template dump_scope (e ; indent=2): stmt =
for k,v in e.scope.pairs:
echo repeatChar(indent,' '), k," = ",v
proc execute (p: Program; func: string; args: openarray[PJsonNode] = []): PJsonNode =
var f: PJsonNode
if p[0].isnil: quit "program is nil"
for tls in p[0].elems:
if tls.hasKey"function":
let
args = tls["args"]
instructions = tls["instructions"]
name = tls["function"]
if name.str == func: f = tls
p.e.iset.instructions[tls["function"].str] = proc(e:Env; statement:PJsonNode):PJsonNode =
# entering function
let new_e = newEnv(e)
for idx, arg in args.elems.pairs:
new_e.scope[arg.str] = (false, new_e.resolve(statement[idx+1]))
result = new_e.execute_instructions(instructions)
if not f.isNil:
result = p.e.call(f, args)
return
raise newException(EIO, "function $# not found" % func)
proc loadFile (f:string): Program =
result[0] = json.parseFile(f)
result[1] = newEnv()
result[1].iset = iset.copy
let p = loadFile("t1.json")
let res = p.execute("main", [%1])
[
{ "import-module": "std-lib" },
{
"function":"main","args":["argv"],"instructions":[
["print", ["string","hello"]],
["variable",
["n", 10],
["i", 0]
],
["while", ["<=", "i", "n"], [
["print", ["format", "fib($1) = $2", "i", ["fib", "i"]]],
["+=", "i", 1]
]],
]
},
{
"function": "fib", "args": ["num"], "returns": "int",
"instructions": [
["variable",
["a",0], ["b",0],
["i",0]
],
["while", ["<", "i", "num"], [
["let", ["x", "b"]],
["+=", "b", "a"],
["set", "a", "x"],
["+=", "i", 1],
]],
["return", "a"]
]
},
{
"function":"greet", "args":["user"],
"instructions":[
["variable", ["msg", ["string", ""]]],
["print", "msg"]
]
}
]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment