-
-
Save fowlmouth/67c4916e6bf2cb143f45 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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]) | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| [ | |
| { "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