all repos — min @ 95b000b3d74d650ce3c0c10258627b74d80dc48c

A small but practical concatenative programming language.

Implemented while, linrec, etc.
h3rald h3rald@h3rald.com
Sun, 30 Nov 2014 15:30:43 +0100
commit

95b000b3d74d650ce3c0c10258627b74d80dc48c

parent

7f3d9c7fccf185b707821c11399c70c5ac5bb843

5 files changed, 179 insertions(+), 10 deletions(-)

jump to
M interpreter.niminterpreter.nim

@@ -3,11 +3,11 @@ import parser

type TMinInterpreter* = object - stack: TMinStack + stack*: TMinStack parser*: TMinParser currSym: TMinValue - filename: string - debugging: bool + filename*: string + debugging*: bool evaluating*: bool TMinOperator* = proc (i: var TMinInterpreter) TMinError* = enum

@@ -35,6 +35,7 @@ "Division by zero"

] var SYMBOLS* = initTable[string, TMinOperator]() +var ALIASES* = newSeq[string](0) proc newMinInterpreter*(debugging = false): TMinInterpreter = var s:TMinStack = newSeq[TMinValue](0)

@@ -44,11 +45,11 @@ return i

proc error*(i: TMinInterpreter, status: TMinError, message = "") = var msg = if message == "": ERRORS[status] else: message - if i.filename != "": + if i.filename == "": + stderr.writeln("`$1`: Error - $2" %[i.currSym.symVal, msg]) + else: stderr.writeln("$1[$2,$3] `$4`: Error - $5" %[i.filename, $i.currSym.line, $i.currSym.last, i.currSym.symVal, msg]) - else: - stderr.writeln("`$1`: Error - $2" %[i.currSym.symVal, msg]) - quit(int(status)) + quit(int(status)) proc open*(i: var TMinInterpreter, stream:PStream, filename: string) = i.filename = filename

@@ -82,6 +83,10 @@ i.error(errUndefined, "Undefined symbol: '"&val.symVal&"'")

else: i.stack.add(val) +proc push*(i: var TMinInterpreter, q: seq[TMinValue]) = + for e in q: + i.push e + proc pop*(i: var TMinInterpreter): TMinValue = if i.stack.len > 0: return i.stack.pop

@@ -102,6 +107,28 @@ val = i.parser.parseMinValue

except: i.error errParser, getCurrentExceptionMsg() i.push val + +proc eval*(i: var TMinInterpreter, s: string) = + let fn = i.filename + try: + i.open(newStringStream(s), "eval") + discard i.parser.getToken() + i.interpret() + except: + stderr.writeln getCurrentExceptionMsg() + finally: + i.filename = fn + +proc load*(i: var TMinInterpreter, s: string) = + let fn = i.filename + try: + i.open(newStringStream(s.readFile), s) + discard i.parser.getToken() + i.interpret() + except: + stderr.writeln getCurrentExceptionMsg() + finally: + i.filename = fn proc apply*(i: var TMinInterpreter, symbol: string) = SYMBOLS[symbol](i)
M minim.nimminim.nim

@@ -1,10 +1,11 @@

-import streams, tables, parseopt2 +import streams, tables, parseopt2, strutils import parser, interpreter, primitives, utils const version* = "0.1.0" var debugging = false var repl = false +const prelude = "prelude.min".slurp.strip let usage* = " MiNiM v" & version & " - a tiny concatenative programming language" & """

@@ -56,6 +57,8 @@ var s = newStringStream("")

i.open(s, "") setControlCHook(handleReplCtrlC) echo "MiNiM v"&version&" - REPL initialized." + i.eval prelude + echo "Prelude loaded." echo "-> Press Ctrl+C to exit." var pos = 0 var line: string

@@ -90,6 +93,7 @@ of "evaluate", "e":

str = val of "help", "h": echo usage + quit(0) of "version", "v": echo version of "interactive", "i":
A prelude.min

@@ -0,0 +1,20 @@

+// Common Environment Variables +[os "windows" ==] + [ + ["HOMEPATH" $] [$HOME] : + ["USERNAME" $] [$USER] : + ] + [ + ["HOME" $] [$HOME] : + ["USER" $] [$USER] : + ] +ifte + +["PATH" $] [$PATH] : +[$HOME] [$HOMEPATH] : +[$USER] [$USERNAME] : + + +// Other + +[[ dup 0 == ] [ 1 + ] [ dup 1 - ] [ * ] linrec] [factorial] :
M primitives.nimprimitives.nim

@@ -1,8 +1,24 @@

-import tables, strutils, os +import tables, strutils, os, osproc import parser, interpreter, utils minsym "exit": quit(0) + +minsym "symbols": + var q = newSeq[TMinValue](0) + for s in SYMBOLS.keys: + q.add s.newVal + i.push q.newVal + +minsym "aliases": + var q = newSeq[TMinValue](0) + for s in ALIASES: + q.add s.newVal + i.push q.newVal + +minsym "debug": + i.debugging = not i.debugging + echo "Debugging: $1" % [$i.debugging] # Common stack operations

@@ -65,11 +81,52 @@ i.apply("cons")

else: i.error(errIncorrect, "Two quotations are required on the stack") +minsym "ifte": + let fpath = i.pop + let tpath = i.pop + let check = i.pop + if check.isQuotation and tpath.isQuotation and fpath.isQuotation: + i.push check.qVal + let res = i.pop + if res.isBool and res.boolVal == true: + i.push tpath.qVal + else: + i.push fpath.qVal + else: + i.error(errIncorrect, "Three quotations are required on the stack") + +minsym "while": + let d = i.pop + let b = i.pop + if b.isQuotation and d.isQuotation: + i.push b.qVal + var check = i.pop + while check.isBool and check.boolVal == true: + i.push d.qVal + i.push b.qVal + check = i.pop + else: + i.error(errIncorrect, "Two quotations are required on the stack") + +minsym "linrec": + var r2 = i.pop + var r1 = i.pop + var t = i.pop + var p = i.pop + if p.isQuotation and t.isQuotation and r1.isQuotation and r2.isQuotation: + i.linrec(p, t, r1, r2) + else: + i.error(errIncorrect, "Four quotations are required on the stack") + # Operations on the whole stack minsym "dump": echo i.dump +minsym "stack": + var s = i.stack + i.push s + # Operations on quotations or strings minsym "concat":

@@ -194,6 +251,20 @@ else:

i.error(errIncorrect, "The top quotation must contain only one symbol value") else: i.error(errIncorrect, "Two quotations or two strings is required on the stack") + +minsym "eval": + let s = i.pop + if s.isString: + i.eval s.strVal + else: + i.error(errIncorrect, "A string is required on the stack") + +minsym "load": + let s = i.pop + if s.isString: + i.load s.strVal + else: + i.error(errIncorrect, "A string is required on the stack") # Comparison operators

@@ -369,6 +440,37 @@ warn "Directory '$1' not found" % [a.strVal]

else: i.error(errIncorrect, "A string is required on the stack") +minsym "system": + let a = i.pop + if a.isString: + i.push execShellCmd(a.strVal).newVal + else: + i.error(errIncorrect, "A string is required on the stack") + +minsym "run": + let a = i.pop + if a.isString: + let words = a.strVal.split(" ") + let cmd = words[0] + var args = newSeq[string](0) + if words.len > 1: + args = words[1..words.len-1] + i.push execProcess(cmd, args, nil, {poUsePath}).newVal + else: + i.error(errIncorrect, "A string is required on the stack") + +minsym "env": + let a = i.pop + if a.isString: + i.push a.strVal.getEnv.newVal + else: + i.error(errIncorrect, "A string is required on the stack") + +minsym "os": + i.push hostOS.newVal + +minsym "cpu": + i.push hostCPU.newVal # Aliases

@@ -382,4 +484,9 @@ minalias "gt", ">"

minalias "lt", "<" minalias "gte", ">=" minalias "lte", "<=" -minalias "echi", "puts" +minalias "echo", "puts" +minalias "shell", "system" +minalias "sh", "system" +minalias "!", "system" +minalias "!&", "run" +minalias "$", "env"
M utils.nimutils.nim

@@ -7,6 +7,7 @@ body

proc minalias*(newname: string, oldname: string) = SYMBOLS[newname] = SYMBOLS[oldname] + ALIASES.add newname proc isSymbol*(s: TMinValue): bool = return s.kind == minSymbol

@@ -46,3 +47,13 @@ return TMinValue(kind: minBool, boolVal: s)

proc warn*(s: string) = stderr.writeln s + +proc linrec*(i: var TMinInterpreter, p, t, r1, r2: TMinValue) = + i.push p.qVal + var check = i.pop + if check.isBool and check.boolVal == true: + i.push t.qVal + else: + i.push r1.qVal + i.linrec(p, t, r1, r2) + i.push r2.qVal