all repos — min @ a410d018e5c1c59ba5d1f1a4057cee0f0948f522

A small but practical concatenative programming language.

Refactoring.
h3rald h3rald@h3rald.com
Sat, 13 Dec 2014 22:07:17 +0100
commit

a410d018e5c1c59ba5d1f1a4057cee0f0948f522

parent

66db4625882a303b9847f316f0030fa37612095a

M interpreter.nimcore/interpreter.nim

@@ -1,5 +1,5 @@

import streams, strutils, tables -import parser, linenoise +import parser, ../vendor/linenoise type TMinInterpreter* = object
A lib/io.nim

@@ -0,0 +1,39 @@

+import tables, os, strutils +import ../core/parser, ../core/interpreter, ../core/utils + +# I/O + +minsym "puts": + let a = i.peek + echo a + +minsym "gets": + i.push newVal(stdin.readLine()) + +minsym "print": + let a = i.peek + a.print + +minsym "read": + let a = i.pop + if a.isString: + if a.strVal.fileExists: + try: + i.push newVal(a.strVal.readFile) + except: + warn getCurrentExceptionMsg() + else: + warn "File '$1' not found" % [a.strVal] + else: + i.error(errIncorrect, "A string is required on the stack") + +minsym "write": + let a = i.pop + let b = i.pop + if a.isString and b.isString: + try: + a.strVal.writeFile(b.strVal) + except: + warn getCurrentExceptionMsg() + else: + i.error(errIncorrect, "Two strings are required on the stack")
A lib/lang.nim

@@ -0,0 +1,199 @@

+import tables, strutils +import ../core/parser, ../core/interpreter, ../core/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.push i.debugging.newVal + +minsym "debug": + i.debugging = not i.debugging + echo "Debugging: $1" % [$i.debugging] + +# Language constructs + +minsym "def": + let q1 = i.pop + let q2 = i.pop + if q1.isQuotation and q2.isQuotation: + if q1.qVal.len == 1 and q1.qVal[0].kind == minSymbol: + minsym q1.qVal[0].symVal: + i.evaluating = true + i.push q2.qVal + i.evaluating = false + else: + i.error errIncorrect, "The top quotation must contain only one symbol value" + else: + i.error errIncorrect, "Two quotations are 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") + + +# Operations on the whole stack + +minsym "clear": + while i.stack.len > 0: + discard i.pop + +minsym "dump": + echo i.dump + +minsym "stack": + var s = i.stack + i.push s + +# Operations on quotations + +minsym "quote": + let a = i.pop + i.push TMinValue(kind: minQuotation, qVal: @[a]) + +minsym "unquote": + let q = i.pop + if not q.isQuotation: + i.error errNoQuotation + for item in q.qVal: + i.push item + +minsym "cons": + var q = i.pop + let v = i.pop + if not q.isQuotation: + i.error errNoQuotation + q.qVal.add v + i.push q + +minsym "map": + let prog = i.pop + let list = i.pop + if prog.isQuotation and list.isQuotation: + i.push newVal(newSeq[TMinValue](0)) + for litem in list.qVal: + i.push litem + for pitem in prog.qVal: + i.push pitem + i.apply("swap") + 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 "filter": + let filter = i.pop + let list = i.pop + var res = newSeq[TMinValue](0) + if filter.isQuotation and list.isQuotation: + for e in list.qVal: + i.push e + i.push filter.qVal + var check = i.pop + if check.isBool and check.boolVal == true: + res.add e + i.push res.newVal + 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 quotations or strings + +minsym "concat": + var q1 = i.pop + var q2 = i.pop + if q1.isString and q2.isString: + let s = q2.strVal & q1.strVal + i.push newVal(s) + elif q1.isQuotation and q2.isQuotation: + let q = q2.qVal & q1.qVal + i.push newVal(q) + else: + i.error(errIncorrect, "Two quotations or two strings are required on the stack") + +minsym "first": + var q = i.pop + if q.isQuotation: + i.push q.qVal[0] + elif q.isString: + i.push newVal($q.strVal[0]) + else: + i.error(errIncorrect, "A quotation or a string is required on the stack") + +minsym "rest": + var q = i.pop + if q.isQuotation: + i.push newVal(q.qVal[1..q.qVal.len-1]) + elif q.isString: + i.push newVal(q.strVal[1..q.strVal.len-1]) + else: + i.error(errIncorrect, "A quotation or a string is required on the stack") + +# Operations on strings + + +minsym "split": + let sep = i.pop + let s = i.pop + if s.isString and sep.isString: + for e in s.strVal.split(sep.strVal): + i.push e.newVal + else: + i.error errIncorrect, "Two strings are required on the stack"
A lib/logic.nim

@@ -0,0 +1,122 @@

+import tables +import ../core/parser, ../core/interpreter, ../core/utils + +# Comparison operators + +minsym ">": + let n2 = i.pop + let n1 = i.pop + if n1.isNumber and n2.isNumber: + if n1.isInt and n2.isInt: + i.push newVal(n1.intVal > n2.intVal) + elif n1.isInt and n2.isFloat: + i.push newVal(n1.intVal.float > n2.floatVal) + elif n1.isFloat and n2.isFloat: + i.push newVal(n1.floatVal > n2.floatVal) + elif n1.isFloat and n2.isInt: + i.push newVal(n1.floatVal > n2.intVal.float) + elif n1.isString and n2.isString: + i.push newVal(n1.strVal > n2.strVal) + else: + i.error(errIncorrect, "Two numbers or two strings are required on the stack") + +minsym ">=": + let n2 = i.pop + let n1 = i.pop + if n1.isNumber and n2.isNumber: + if n1.isInt and n2.isInt: + i.push newVal(n1.intVal >= n2.intVal) + elif n1.isInt and n2.isFloat: + i.push newVal(n1.intVal.float >= n2.floatVal) + elif n1.isFloat and n2.isFloat: + i.push newVal(n1.floatVal >= n2.floatVal) + elif n1.isFloat and n2.isInt: + i.push newVal(n1.floatVal >= n2.intVal.float) + elif n1.isString and n2.isString: + i.push newVal(n1.strVal >= n2.strVal) + else: + i.error(errIncorrect, "Two numbers or two strings are required on the stack") + +minsym "<": + let n1 = i.pop + let n2 = i.pop + if n1.isNumber and n2.isNumber: + if n1.isInt and n2.isInt: + i.push newVal(n1.intVal > n2.intVal) + elif n1.isInt and n2.isFloat: + i.push newVal(n1.intVal.float > n2.floatVal) + elif n1.isFloat and n2.isFloat: + i.push newVal(n1.floatVal > n2.floatVal) + elif n1.isFloat and n2.isInt: + i.push newVal(n1.floatVal > n2.intVal.float) + elif n1.isString and n2.isString: + i.push newVal(n1.strVal > n2.strVal) + else: + i.error(errIncorrect, "Two numbers or two strings are required on the stack") + +minsym "<=": + let n1 = i.pop + let n2 = i.pop + if n1.isNumber and n2.isNumber: + if n1.isInt and n2.isInt: + i.push newVal(n1.intVal >= n2.intVal) + elif n1.isInt and n2.isFloat: + i.push newVal(n1.intVal.float >= n2.floatVal) + elif n1.isFloat and n2.isFloat: + i.push newVal(n1.floatVal >= n2.floatVal) + elif n1.isFloat and n2.isInt: + i.push newVal(n1.floatVal >= n2.intVal.float) + elif n1.isString and n2.isString: + i.push newVal(n1.strVal >= n2.strVal) + else: + i.error(errIncorrect, "Two numbers or two strings are required on the stack") + +minsym "==": + let n2 = i.pop + let n1 = i.pop + if (n1.kind == n2.kind or (n1.isNumber and n2.isNumber)) and not n1.isSymbol: + i.push newVal(n1 == n2) + else: + i.error(errIncorrect, "Two non-symbol values of similar type are required") + +minsym "!=": + let n2 = i.pop + let n1 = i.pop + if (n1.kind == n2.kind or (n1.isNumber and n2.isNumber)) and not n1.isSymbol: + i.push newVal(not (n1 == n2)) + else: + i.error(errIncorrect, "Two non-symbol values of similar type are required") + +# Boolean Logic + +minsym "not": + let b = i.pop + if b.isBool: + i.push newVal(not b.boolVal) + else: + i.error(errIncorrect, "A bool value is required on the stack") + +minsym "and": + let a = i.pop + let b = i.pop + if a.isBool and b.isBool: + i.push newVal(a.boolVal and b.boolVal) + else: + i.error(errIncorrect, "Two bool values are required on the stack") + +minsym "or": + let a = i.pop + let b = i.pop + if a.isBool and b.isBool: + i.push newVal(a.boolVal or b.boolVal) + else: + i.error(errIncorrect, "Two bool values are required on the stack") + +minsym "xor": + let a = i.pop + let b = i.pop + if a.isBool and b.isBool: + i.push newVal(a.boolVal xor b.boolVal) + else: + i.error(errIncorrect, "Two bool values are required on the stack") +
A lib/numbers.nim

@@ -0,0 +1,95 @@

+import tables +import ../core/interpreter, ../core/utils + +# Arithmetic + +minsym "+": + let a = i.pop + let b = i.pop + if a.isInt: + if b.isInt: + i.push newVal(a.intVal + b.intVal) + elif b.isFloat: + i.push newVal(a.intVal.float + b.floatVal) + else: + i.error(errTwoNumbersRequired) + elif a.isFloat: + if b.isFloat: + i.push newVal(a.floatVal + b.floatVal) + elif b.isInt: + i.push newVal(a.floatVal + b.intVal.float) + else: + i.error(errTwoNumbersRequired) + +minsym "-": + let a = i.pop + let b = i.pop + if a.isInt: + if b.isInt: + i.push newVal(b.intVal - a.intVal) + elif b.isFloat: + i.push newVal(b.floatVal - a.intVal.float) + else: + i.error(errTwoNumbersRequired) + elif a.isFloat: + if b.isFloat: + i.push newVal(b.floatVal - a.floatVal) + elif b.isInt: + i.push newVal(b.intVal.float - a.floatVal) + else: + i.error(errTwoNumbersRequired) + +minsym "*": + let a = i.pop + let b = i.pop + if a.isInt: + if b.isInt: + i.push newVal(a.intVal * b.intVal) + elif b.isFloat: + i.push newVal(a.intVal.float * b.floatVal) + else: + i.error(errTwoNumbersRequired) + elif a.isFloat: + if b.isFloat: + i.push newVal(a.floatVal * b.floatVal) + elif b.isInt: + i.push newVal(a.floatVal * b.intVal.float) + else: + i.error(errTwoNumbersRequired) + +minsym "/": + let a = i.pop + let b = i.pop + if b.isInt and b.intVal == 0: + i.error errDivisionByZero + if a.isInt: + if b.isInt: + i.push newVal(b.intVal / a.intVal) + elif b.isFloat: + i.push newVal(b.floatVal / a.intVal.float) + else: + i.error(errTwoNumbersRequired) + elif a.isFloat: + if b.isFloat: + i.push newVal(b.floatVal / a.floatVal) + elif b.isInt: + i.push newVal(b.intVal.float / a.floatVal) + else: + i.error(errTwoNumbersRequired) + +minsym "div": + let b = i.pop + let a = i.pop + if a.isInt and b.isInt: + i.push(newVal(a.intVal div b.intVal)) + else: + i.error errIncorrect, "Two integers are required on the stack" + +minsym "mod": + let b = i.pop + let a = i.pop + if a.isInt and b.isInt: + i.push(newVal(a.intVal mod b.intVal)) + else: + i.error errIncorrect, "Two integers are required on the stack" +
A lib/stack.nim

@@ -0,0 +1,39 @@

+import tables +import ../core/interpreter, ../core/utils + +# Common stack operations + +minsym "id": + discard + +minsym "pop": + discard i.pop + +minsym "dup": + i.push i.peek + +minsym "dip": + let q = i.pop + if not q.isQuotation: + i.error errNoQuotation + let v = i.pop + for item in q.qVal: + i.push item + i.push v + +minsym "swap": + let a = i.pop + let b = i.pop + i.push a + i.push b + +minsym "sip": + let a = i.pop + let b = i.pop + if a.isQuotation and b.isQuotation: + i.push b + i.push a.qVal + i.push b + else: + i.error(errIncorrect, "Two quotations are required on the stack") +
A lib/sys.nim

@@ -0,0 +1,136 @@

+import tables, os, osproc, strutils +import ../core/parser, ../core/interpreter, ../core/utils + +# OS + +minsym "pwd": + i.push newVal(getCurrentDir()) + +minsym "cd": + let f = i.pop + if f.isString: + try: + f.strVal.setCurrentDir + except: + warn getCurrentExceptionMsg() + else: + i.error errIncorrect, "A string is required on the stack" + +minsym "ls": + let a = i.pop + var list = newSeq[TMinValue](0) + if a.isString: + if a.strVal.existsDir: + for i in walkdir(a.strVal): + list.add newVal(i.path) + i.push list.newVal + else: + 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 "getenv": + 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 "putenv": + let value = i.pop + let key = i.pop + if value.isString and key.isString: + key.strVal.putEnv value.strVal + else: + i.error(errIncorrect, "Two strings are required on the stack") + +minsym "os": + i.push hostOS.newVal + +minsym "cpu": + i.push hostCPU.newVal + +minsym "file?": + let f = i.pop + if f.isString: + i.push f.strVal.fileExists.newVal + else: + i.error errIncorrect, "A string is required on the stack" + +minsym "dir?": + let f = i.pop + if f.isString: + i.push f.strVal.dirExists.newVal + else: + i.error errIncorrect, "A string is required on the stack" + +minsym "rm": + let f = i.pop + if f.isString: + try: + f.strVal.removeFile + except: + warn getCurrentExceptionMsg() + else: + i.error errIncorrect, "A string is required on the stack" + +minsym "cp": + let b = i.pop + let a = i.pop + if a.isString and b.isString: + try: + copyFile a.strVal, b.strVal + except: + warn getCurrentExceptionMsg() + else: + i.error errIncorrect, "Two strings are required on the stack" + +minsym "mv": + let b = i.pop + let a = i.pop + if a.isString and b.isString: + try: + moveFile a.strVal, b.strVal + except: + warn getCurrentExceptionMsg() + else: + i.error errIncorrect, "Two strings are required on the stack" + +minsym "rmdir": + let f = i.pop + if f.isString: + try: + f.strVal.removeDir + except: + warn getCurrentExceptionMsg() + else: + i.error errIncorrect, "A string is required on the stack" + +minsym "mkdir": + let f = i.pop + if f.isString: + try: + f.strVal.createDir + except: + warn getCurrentExceptionMsg() + else: + i.error errIncorrect, "A string is required on the stack"
A lib/time.nim

@@ -0,0 +1,11 @@

+import times, tables +import ../core/interpreter, ../core/utils + +# Time + +minsym "timestamp": + i.push getTime().int.newVal + +minsym "now": + i.push epochTime().newVal +
M linenoise.nimvendor/linenoise.nim

@@ -34,7 +34,7 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE

# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # -{.compile: "linenoise/liblinenoise.c".} +{.compile: "vendor/linenoise/liblinenoise.c".} {.push importc.} when not(defined(LINENOISE_H)): const
M minim.nimminim.nim

@@ -1,11 +1,25 @@

import streams, tables, parseopt2, strutils -import parser, interpreter, primitives, utils, linenoise - +import + core/parser, + core/interpreter, + core/utils +import + lib/lang, + lib/stack, + lib/numbers, + lib/logic, + lib/time, + lib/io, + lib/sys const version* = "0.1.0" var debugging = false var repl = false -const prelude = "prelude.min".slurp.strip +const prelude = "lib/prelude.min".slurp.strip + +const + USE_LINENOISE = (defined(i386) or defined(amd64)) and not defined(windows) + let usage* = " MiNiM v" & version & " - a tiny concatenative system programming language" & """

@@ -22,6 +36,26 @@ -h, --help Print this help

-v, --version Print the program version -i, --interactive Starts MiNiM's Read Evel Print Loop""" +when USE_LINENOISE: + import vendor/linenoise + proc completionCallback*(str: cstring, completions: ptr linenoiseCompletions) = + var words = ($str).split(" ") + var w = if words.len > 0: words.pop else: "" + var sep = "" + if words.len > 0: + sep = " " + for s in SYMBOLS.keys: + if startsWith(s, w): + linenoiseAddCompletion completions, words.join(" ") & sep & s + proc prompt(s: string): string = + var res = linenoise(s) + discard $linenoiseHistoryAdd(res) + return $res +else: + proc prompt(s: string): string = + stdout.print(s) + return stdin.readLine + proc minimStream(s: PStream, filename: string) = var i = newMinInterpreter(debugging) i.eval prelude

@@ -47,16 +81,6 @@ stderr.writeln("Error - Cannot read from "& filename)

stderr.flushFile() minimStream(stream, filename) -proc completionCallback*(str: cstring, completions: ptr linenoiseCompletions) = - var words = ($str).split(" ") - var w = words.pop - var sep = "" - if words.len > 0: - sep = " " - for s in SYMBOLS.keys: - if startsWith(s, w): - linenoiseAddCompletion completions, words.join(" ") & sep & s - proc minimRepl*() = var i = newMinInterpreter(debugging) var s = newStringStream("")

@@ -65,12 +89,11 @@ echo "MiNiM v"&version&" - REPL initialized."

i.eval prelude echo "Prelude loaded." echo "-> Type 'exit' or 'quit' to exit." - discard linenoiseSetCompletionCallback completionCallback - var line: cstring + if USE_LINENOISE: + discard linenoiseSetCompletionCallback completionCallback + var line: string while true: - line = linenoise(": ") - discard linenoiseHistoryAdd line - s.writeln(line) + line = prompt(": ") i.parser.buf = $i.parser.buf & $line i.parser.bufLen = i.parser.buf.len discard i.parser.getToken()
M prelude.minlib/prelude.min

@@ -1,3 +1,26 @@

+// Aliases +[def] [:] def +[exit] [quit] : +[concat] [,] : +[print] [%] : +[==] [eq] : +[!=] [noteq] : +[>] [gt] : +[<] [lt] : +[>=] [gte] : +[<=] [lte] : +[puts] [echo] : +[system] [!] : +[run] [&] : +[getenv] [$] : +[setenv] [$=] : +[pop] [zap] : +[quote] [unit] : +[unquote] [i] : +[unquote] [apply] : +[filter] [select] : +[clear] [empty] : + // Common Environment Variables [os "windows" ==] [
D primitives.nim

@@ -1,653 +0,0 @@

-import tables, strutils, os, osproc, times -import parser, interpreter, utils, trex - -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.push i.debugging.newVal - -minsym "debug": - i.debugging = not i.debugging - echo "Debugging: $1" % [$i.debugging] - -minsym "clear": - while i.stack.len > 0: - discard i.pop - -# Common stack operations - -minsym "id": - discard - -minsym "pop": - discard i.pop - -minsym "dup": - i.push i.peek - -minsym "dip": - let q = i.pop - if not q.isQuotation: - i.error errNoQuotation - let v = i.pop - for item in q.qVal: - i.push item - i.push v - -minsym "swap": - let a = i.pop - let b = i.pop - i.push a - i.push b - -minsym "sip": - let a = i.pop - let b = i.pop - if a.isQuotation and b.isQuotation: - i.push b - i.push a.qVal - i.push b - else: - i.error(errIncorrect, "Two quotations are required on the stack") - -# Operations on quotations - -minsym "quote": - let a = i.pop - i.push TMinValue(kind: minQuotation, qVal: @[a]) - -minsym "unquote": - let q = i.pop - if not q.isQuotation: - i.error errNoQuotation - for item in q.qVal: - i.push item - -minsym "cons": - var q = i.pop - let v = i.pop - if not q.isQuotation: - i.error errNoQuotation - q.qVal.add v - i.push q - -minsym "map": - let prog = i.pop - let list = i.pop - if prog.isQuotation and list.isQuotation: - i.push newVal(newSeq[TMinValue](0)) - for litem in list.qVal: - i.push litem - for pitem in prog.qVal: - i.push pitem - i.apply("swap") - 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 "filter": - let filter = i.pop - let list = i.pop - var res = newSeq[TMinValue](0) - if filter.isQuotation and list.isQuotation: - for e in list.qVal: - i.push e - i.push filter.qVal - var check = i.pop - if check.isBool and check.boolVal == true: - res.add e - i.push res.newVal - 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": - var q1 = i.pop - var q2 = i.pop - if q1.isString and q2.isString: - let s = q2.strVal & q1.strVal - i.push newVal(s) - elif q1.isQuotation and q2.isQuotation: - let q = q2.qVal & q1.qVal - i.push newVal(q) - else: - i.error(errIncorrect, "Two quotations or two strings are required on the stack") - -minsym "first": - var q = i.pop - if q.isQuotation: - i.push q.qVal[0] - elif q.isString: - i.push newVal($q.strVal[0]) - else: - i.error(errIncorrect, "A quotation or a string is required on the stack") - -minsym "rest": - var q = i.pop - if q.isQuotation: - i.push newVal(q.qVal[1..q.qVal.len-1]) - elif q.isString: - i.push newVal(q.strVal[1..q.strVal.len-1]) - else: - i.error(errIncorrect, "A quotation or a string is required on the stack") - -# Operations on strings - - -minsym "split": - let sep = i.pop - let s = i.pop - if s.isString and sep.isString: - for e in s.strVal.split(sep.strVal): - i.push e.newVal - else: - i.error errIncorrect, "Two strings are required on the stack" - - -# Arithmetic - -minsym "+": - let a = i.pop - let b = i.pop - if a.isInt: - if b.isInt: - i.push newVal(a.intVal + b.intVal) - elif b.isFloat: - i.push newVal(a.intVal.float + b.floatVal) - else: - i.error(errTwoNumbersRequired) - elif a.isFloat: - if b.isFloat: - i.push newVal(a.floatVal + b.floatVal) - elif b.isInt: - i.push newVal(a.floatVal + b.intVal.float) - else: - i.error(errTwoNumbersRequired) - -minsym "-": - let a = i.pop - let b = i.pop - if a.isInt: - if b.isInt: - i.push newVal(b.intVal - a.intVal) - elif b.isFloat: - i.push newVal(b.floatVal - a.intVal.float) - else: - i.error(errTwoNumbersRequired) - elif a.isFloat: - if b.isFloat: - i.push newVal(b.floatVal - a.floatVal) - elif b.isInt: - i.push newVal(b.intVal.float - a.floatVal) - else: - i.error(errTwoNumbersRequired) - -minsym "*": - let a = i.pop - let b = i.pop - if a.isInt: - if b.isInt: - i.push newVal(a.intVal * b.intVal) - elif b.isFloat: - i.push newVal(a.intVal.float * b.floatVal) - else: - i.error(errTwoNumbersRequired) - elif a.isFloat: - if b.isFloat: - i.push newVal(a.floatVal * b.floatVal) - elif b.isInt: - i.push newVal(a.floatVal * b.intVal.float) - else: - i.error(errTwoNumbersRequired) - -minsym "/": - let a = i.pop - let b = i.pop - if b.isInt and b.intVal == 0: - i.error errDivisionByZero - if a.isInt: - if b.isInt: - i.push newVal(b.intVal / a.intVal) - elif b.isFloat: - i.push newVal(b.floatVal / a.intVal.float) - else: - i.error(errTwoNumbersRequired) - elif a.isFloat: - if b.isFloat: - i.push newVal(b.floatVal / a.floatVal) - elif b.isInt: - i.push newVal(b.intVal.float / a.floatVal) - else: - i.error(errTwoNumbersRequired) - -minsym "div": - let b = i.pop - let a = i.pop - if a.isInt and b.isInt: - i.push(newVal(a.intVal div b.intVal)) - else: - i.error errIncorrect, "Two integers are required on the stack" - -minsym "mod": - let b = i.pop - let a = i.pop - if a.isInt and b.isInt: - i.push(newVal(a.intVal mod b.intVal)) - else: - i.error errIncorrect, "Two integers are required on the stack" - -# Language constructs - -minsym "def": - let q1 = i.pop - let q2 = i.pop - if q1.isQuotation and q2.isQuotation: - if q1.qVal.len == 1 and q1.qVal[0].kind == minSymbol: - minsym q1.qVal[0].symVal: - i.evaluating = true - echo q2 - i.push q2.qVal - i.evaluating = false - else: - i.error errIncorrect, "The top quotation must contain only one symbol value" - else: - i.error errIncorrect, "Two quotations are 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 - -minsym ">": - let n2 = i.pop - let n1 = i.pop - if n1.isNumber and n2.isNumber: - if n1.isInt and n2.isInt: - i.push newVal(n1.intVal > n2.intVal) - elif n1.isInt and n2.isFloat: - i.push newVal(n1.intVal.float > n2.floatVal) - elif n1.isFloat and n2.isFloat: - i.push newVal(n1.floatVal > n2.floatVal) - elif n1.isFloat and n2.isInt: - i.push newVal(n1.floatVal > n2.intVal.float) - elif n1.isString and n2.isString: - i.push newVal(n1.strVal > n2.strVal) - else: - i.error(errIncorrect, "Two numbers or two strings are required on the stack") - -minsym ">=": - let n2 = i.pop - let n1 = i.pop - if n1.isNumber and n2.isNumber: - if n1.isInt and n2.isInt: - i.push newVal(n1.intVal >= n2.intVal) - elif n1.isInt and n2.isFloat: - i.push newVal(n1.intVal.float >= n2.floatVal) - elif n1.isFloat and n2.isFloat: - i.push newVal(n1.floatVal >= n2.floatVal) - elif n1.isFloat and n2.isInt: - i.push newVal(n1.floatVal >= n2.intVal.float) - elif n1.isString and n2.isString: - i.push newVal(n1.strVal >= n2.strVal) - else: - i.error(errIncorrect, "Two numbers or two strings are required on the stack") - -minsym "<": - let n1 = i.pop - let n2 = i.pop - if n1.isNumber and n2.isNumber: - if n1.isInt and n2.isInt: - i.push newVal(n1.intVal > n2.intVal) - elif n1.isInt and n2.isFloat: - i.push newVal(n1.intVal.float > n2.floatVal) - elif n1.isFloat and n2.isFloat: - i.push newVal(n1.floatVal > n2.floatVal) - elif n1.isFloat and n2.isInt: - i.push newVal(n1.floatVal > n2.intVal.float) - elif n1.isString and n2.isString: - i.push newVal(n1.strVal > n2.strVal) - else: - i.error(errIncorrect, "Two numbers or two strings are required on the stack") - -minsym "<=": - let n1 = i.pop - let n2 = i.pop - if n1.isNumber and n2.isNumber: - if n1.isInt and n2.isInt: - i.push newVal(n1.intVal >= n2.intVal) - elif n1.isInt and n2.isFloat: - i.push newVal(n1.intVal.float >= n2.floatVal) - elif n1.isFloat and n2.isFloat: - i.push newVal(n1.floatVal >= n2.floatVal) - elif n1.isFloat and n2.isInt: - i.push newVal(n1.floatVal >= n2.intVal.float) - elif n1.isString and n2.isString: - i.push newVal(n1.strVal >= n2.strVal) - else: - i.error(errIncorrect, "Two numbers or two strings are required on the stack") - -minsym "==": - let n2 = i.pop - let n1 = i.pop - if (n1.kind == n2.kind or (n1.isNumber and n2.isNumber)) and not n1.isSymbol: - i.push newVal(n1 == n2) - else: - i.error(errIncorrect, "Two non-symbol values of similar type are required") - -minsym "!=": - let n2 = i.pop - let n1 = i.pop - if (n1.kind == n2.kind or (n1.isNumber and n2.isNumber)) and not n1.isSymbol: - i.push newVal(not (n1 == n2)) - else: - i.error(errIncorrect, "Two non-symbol values of similar type are required") - -# Boolean Logic - -minsym "not": - let b = i.pop - if b.isBool: - i.push newVal(not b.boolVal) - else: - i.error(errIncorrect, "A bool value is required on the stack") - -minsym "and": - let a = i.pop - let b = i.pop - if a.isBool and b.isBool: - i.push newVal(a.boolVal and b.boolVal) - else: - i.error(errIncorrect, "Two bool values are required on the stack") - -minsym "or": - let a = i.pop - let b = i.pop - if a.isBool and b.isBool: - i.push newVal(a.boolVal or b.boolVal) - else: - i.error(errIncorrect, "Two bool values are required on the stack") - -minsym "xor": - let a = i.pop - let b = i.pop - if a.isBool and b.isBool: - i.push newVal(a.boolVal xor b.boolVal) - else: - i.error(errIncorrect, "Two bool values are required on the stack") - -# Time - -minsym "timestamp": - i.push getTime().int.newVal - -minsym "now": - i.push epochTime().newVal - -# I/O - -minsym "puts": - let a = i.peek - echo a - -minsym "gets": - i.push newVal(stdin.readLine()) - -minsym "print": - let a = i.peek - a.print - -minsym "read": - let a = i.pop - if a.isString: - if a.strVal.fileExists: - try: - i.push newVal(a.strVal.readFile) - except: - warn getCurrentExceptionMsg() - else: - warn "File '$1' not found" % [a.strVal] - else: - i.error(errIncorrect, "A string is required on the stack") - -minsym "write": - let a = i.pop - let b = i.pop - if a.isString and b.isString: - try: - a.strVal.writeFile(b.strVal) - except: - warn getCurrentExceptionMsg() - else: - i.error(errIncorrect, "Two strings are required on the stack") - -# OS - -minsym "pwd": - i.push newVal(getCurrentDir()) - -minsym "cd": - let f = i.pop - if f.isString: - try: - f.strVal.setCurrentDir - except: - warn getCurrentExceptionMsg() - else: - i.error errIncorrect, "A string is required on the stack" - -minsym "ls": - let a = i.pop - var list = newSeq[TMinValue](0) - if a.isString: - if a.strVal.existsDir: - for i in walkdir(a.strVal): - list.add newVal(i.path) - i.push list.newVal - else: - 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 "getenv": - 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 "putenv": - let value = i.pop - let key = i.pop - if value.isString and key.isString: - key.strVal.putEnv value.strVal - else: - i.error(errIncorrect, "Two strings are required on the stack") - -minsym "os": - i.push hostOS.newVal - -minsym "cpu": - i.push hostCPU.newVal - -minsym "file?": - let f = i.pop - if f.isString: - i.push f.strVal.fileExists.newVal - else: - i.error errIncorrect, "A string is required on the stack" - -minsym "dir?": - let f = i.pop - if f.isString: - i.push f.strVal.dirExists.newVal - else: - i.error errIncorrect, "A string is required on the stack" - -minsym "rm": - let f = i.pop - if f.isString: - try: - f.strVal.removeFile - except: - warn getCurrentExceptionMsg() - else: - i.error errIncorrect, "A string is required on the stack" - -minsym "cp": - let b = i.pop - let a = i.pop - if a.isString and b.isString: - try: - copyFile a.strVal, b.strVal - except: - warn getCurrentExceptionMsg() - else: - i.error errIncorrect, "Two strings are required on the stack" - -minsym "mv": - let b = i.pop - let a = i.pop - if a.isString and b.isString: - try: - moveFile a.strVal, b.strVal - except: - warn getCurrentExceptionMsg() - else: - i.error errIncorrect, "Two strings are required on the stack" - -minsym "rmdir": - let f = i.pop - if f.isString: - try: - f.strVal.removeDir - except: - warn getCurrentExceptionMsg() - else: - i.error errIncorrect, "A string is required on the stack" - -minsym "mkdir": - let f = i.pop - if f.isString: - try: - f.strVal.createDir - except: - warn getCurrentExceptionMsg() - else: - i.error errIncorrect, "A string is required on the stack" - -# Aliases - -minalias "quit", "exit" -minalias "&", "concat" -minalias "cat", "concat" -minalias "%", "print" -minalias ":", "def" -minalias "eq", "==" -minalias "noteq", "!=" -minalias "gt", ">" -minalias "lt", "<" -minalias "gte", ">=" -minalias "lte", "<=" -minalias "echo", "puts" -minalias "shell", "system" -minalias "sh", "system" -minalias "!", "system" -minalias "!&", "run" -minalias "$", "getenv" -minalias "$=", "putenv" -minalias "zap", "pop" -minalias "unit", "quote" -minalias "i", "unquote" -minalias "i", "apply" -minalias "select", "filter" -minalias "empty", "clear"
M trex.nimvendor/trex.nim

@@ -1,4 +1,4 @@

-{.compile: "T-Rex/libtrex.c".} +{.compile: "vendor/T-Rex/libtrex.c".} {.push importc.} when not(defined(TREX_H)): const