Started to implement simple min -> nim compiler.
h3rald h3rald@h3rald.com
Tue, 08 Dec 2020 21:04:06 +0100
4 files changed,
134 insertions(+),
18 deletions(-)
M
core/interpreter.nim
→
core/interpreter.nim
@@ -278,6 +278,32 @@ if parseOnly:
return q if i.stack.len > 0: return i.stack[i.stack.len - 1] + +proc compile*(i: In): seq[string] {.discardable, extern:"min_exported_symbol_$1".} = + result = newSeq[string](0) + result.add "import min" + result.add "var i = newMinInterpreter(\"$#\")" % i.filename + while i.parser.token != tkEof: + if i.trace.len == 0: + i.stackcopy = i.stack + try: + result.add i.parser.compileMinValue(i) + except MinRuntimeError: + let msg = getCurrentExceptionMsg() + i.stack = i.stackcopy + error("$1:$2,$3 $4" % [i.currSym.filename, $i.currSym.line, $i.currSym.column, msg]) + i.stackTrace + i.trace = @[] + raise MinTrappedException(msg: msg) + except MinTrappedException: + raise + except: + let msg = getCurrentExceptionMsg() + i.stack = i.stackcopy + i.error(msg) + i.stackTrace + i.trace = @[] + raise MinTrappedException(msg: msg) proc eval*(i: In, s: string, name="<eval>", parseOnly=false): MinValue {.discardable, extern:"min_exported_symbol_$1".}= var i2 = i.copy(name)
M
core/parser.nim
→
core/parser.nim
@@ -2,6 +2,9 @@ # Adapted from: https://github.com/Araq/Nimrod/blob/v0.9.6/lib/pure/json.nim
import lexbase, strutils, + sequtils, + json, + oids, streams, critbits@@ -594,7 +597,6 @@ d = d.strip & "}"
return d proc parseMinValue*(p: var MinParser, i: In): MinValue {.extern:"min_exported_symbol_$1".}= - #echo p.a, " (", p.token, ")" case p.token of tkTrue: result = MinValue(kind: minBool, boolVal: true)@@ -618,7 +620,7 @@ discard getToken(p)
while p.token != tkBracketRi: q.add p.parseMinValue(i) eat(p, tkBracketRi) - result = MinValue(kind: minQuotation, qVal: q)#, scope: newscope) + result = MinValue(kind: minQuotation, qVal: q) of tkBraceLe: var scope = newScopeRef(nil) var val: MinValue@@ -649,6 +651,77 @@ discard getToken(p)
else: raiseUndefined(p, "Undefined value: '"&p.a&"'") result.filename = p.filename + +proc compileMinValue*(p: var MinParser, i: In, push = true): seq[string] {.extern:"min_exported_symbol_$1".}= + var op = "" + if push: + op = "i.push " + result = newSeq[string](0) + case p.token + of tkTrue: + result = @[op&"MinValue(kind: minBool, boolVal: true)"] + discard getToken(p) + of tkFalse: + result = @[op&"MinValue(kind: minBool, boolVal: false)"] + discard getToken(p) + of tkString: + result = @[op&"MinValue(kind: minString, strVal: "&p.a.escapeJson&")"] + p.a = "" + discard getToken(p) + of tkInt: + result = @[op&"MinValue(kind: minInt, intVal: "&p.a&")"] + discard getToken(p) + of tkFloat: + result = @[op&"MinValue(kind: minFloat, floatVal: "&p.a&")"] + discard getToken(p) + of tkBracketLe: + var qvar = "q" & $genOid() + result.add "var "&qvar&" = newSeq[MinValue](0)" + discard getToken(p) + while p.token != tkBracketRi: + var instructions = p.compileMinValue(i, false) + let v = instructions.pop + result = result.concat(instructions) + result.add qvar&".add "&v + eat(p, tkBracketRi) + result.add op&"MinValue(kind: minQuotation, qVal: "&qvar&")" + of tkBraceLe: + result = newSeq[string](0) + var val: MinValue + var scopevar = "scope" & $genOid() + var valvar = "val" & $genOid() + result.add "var "&scopevar&" = newScopeRef(nil)" + result.add "var "&valvar&": MinValue" + discard getToken(p) + var c = 0 + while p.token != tkBraceRi: + c = c+1 + var instructions = p.compileMinValue(i, false) + let v = p.parseMinValue(i) + let vs = instructions.pop + result = result.concat(instructions) + if val.isNil: + result.add valvar&" = "&vs + elif v.kind == minSymbol: + let key = v.symVal + if key[0] == ':': + result.add scopevar&".symbols["&key[1 .. key.len-1]&"] = MinOperator(kind: minValOp, val: "&valvar&", sealed: false)" + val = nil + else: + raiseInvalid("Invalid dictionary key: " & key) + else: + raiseInvalid("Invalid dictionary key: " & $v) + eat(p, tkBraceRi) + if c mod 2 != 0: + raiseInvalid("Invalid dictionary") + # TODO: check + result.add op&"MinValue(kind: minDictionary, scope: "&scopevar&")" + of tkSymbol: + result = @[op&"MinValue(kind: minSymbol, symVal: "&p.a.escapeJson&", column: " & $p.getColumn & ", line: " & $p.lineNumber & ", filename: "&p.filename.escapeJson&")"] + p.a = "" + discard getToken(p) + else: + raiseUndefined(p, "Undefined value: '"&p.a&"'") proc print*(a: MinValue) {.extern:"min_exported_symbol_$1".}= stdout.write($$a)
M
min.nim
→
min.nim
@@ -51,10 +51,6 @@ scope,
min_lang - -#-d:ssl -p:. -d:noOpenSSLHacks --dynlibOverride:ssl- --dynlibOverride:crypto- -d:sslVersion:"(" --passL:-Lpath/to/openssl/lib -#--passL:-Bstatic --passL:-lssl --passL:-lcrypto --passL:-Bdynamic - const PRELUDE* = "prelude.min".slurp.strip var customPrelude = ""@@ -196,16 +192,30 @@ result = i.interpret()
except: discard i.close() + +proc compile*(i: In, s: Stream) = + i.open(s, i.filename) + discard i.parser.getToken() + try: + let r = i.compile() + for line in r: + echo line + except: + discard + i.close() -proc minStream(s: Stream, filename: string) = +proc minStream(s: Stream, filename: string, op = "interpret") = var i = newMinInterpreter(filename = filename) i.pwd = filename.parentDir - i.interpret(s) + if op == "interpret": + i.interpret(s) + else: + i.compile(s) proc minString*(buffer: string) = minStream(newStringStream(buffer), "input") -proc minFile*(filename: string) = +proc minFile*(filename: string, op = "interpret") = var fn = filename if not filename.endsWith(".min"): fn &= ".min"@@ -220,14 +230,14 @@ if fileLines[0].len >= 2 and fileLines[0][0..1] == "#!":
contents = ";;\n" & fileLines[1..fileLines.len-1].join("\n") else: contents = fileLines.join("\n") - minStream(newStringStream(contents), fn) + minStream(newStringStream(contents), fn, op) -proc minFile*(file: File, filename="stdin") = +proc minFile*(file: File, filename="stdin", op = "interpret") = var stream = newFileStream(filename) if stream == nil: fatal("Cannot read from file: " & filename) quit(3) - minStream(stream, filename) + minStream(stream, filename, op) proc printResult(i: In, res: MinValue) = if res.isNil:@@ -300,6 +310,7 @@ var REPL = false
var SIMPLEREPL = false var INSTALL = false var UNINSTALL = false + var COMPILE = false var libfile = "" let usage* = """ $1 v$2 - a tiny concatenative shell and programming language@@ -309,13 +320,14 @@ Usage:
min [options] [filename] Arguments: - filename A $1 file to interpret (default: STDIN). + filename A $1 file to interpret or compile (default: STDIN). Options: -—install:<lib> Install dynamic library file <lib> —-uninstall:<lib> Uninstall dynamic library file <lib> + -c, --compile Compile the specified file -e, --evaluate Evaluate a $1 program inline - -h, —-help Print this help - -i, —-interactive Start $1 shell (with advanced prompt) + -h, --help Print this help + -i, --interactive Start $1 shell (with advanced prompt) -j, --interactive-simple Start $1 shell (without advanced prompt) -l, --log Set log level (debug|info|notice|warn|error|fatal) Default: notice@@ -334,6 +346,8 @@ if file == "":
file = key of cmdLongOption, cmdShortOption: case key: + of "compile", "c": + COMPILE = true of "prelude", "p": customPrelude = val of "log", "l":@@ -369,11 +383,13 @@ else:
discard else: discard - + var op = "interpret" + if COMPILE: + op = "compile" if s != "": minString(s) elif file != "": - minFile file + minFile file, op elif INSTALL: if not libfile.fileExists: fatal("Dynamic library file not found:" & libfile)@@ -400,4 +416,4 @@ elif REPL or SIMPLEREPL:
minRepl(SIMPLEREPL) quit(0) else: - minFile stdin, "stdin" + minFile stdin, "stdin", op