all repos — min @ ca018c34568d8abae090a61bfc43ca37e3ddc46d

A small but practical concatenative programming language.

Changed project structure, set ops, module caching.
h3rald h3rald@h3rald.com
Tue, 05 Jan 2021 20:14:34 +0100
commit

ca018c34568d8abae090a61bfc43ca37e3ddc46d

parent

ffefc1a95d12a4e520719722a76b84e5bfcf2a25

M core/baseutils.nimminpkg/core/baseutils.nim

@@ -5,7 +5,8 @@ result = newSeq[T](xs.len)

for i, x in xs: result[result.len-i-1] = x -proc simplifyPath*(filename: string, file: string): string = +proc simplifyPath*(filename: string, f: string): string = + let file = strutils.replace(f, "\\", "/") let fn = strutils.replace(filename, "./", "") var dirs: seq[string] = fn.split("/") discard dirs.pop

@@ -14,7 +15,6 @@ if pwd == "":

result = file else: result = pwd&"/"&file - when defined(mini): import
D core/fileutils.nim

@@ -1,100 +0,0 @@

-import - os - -# Filetype and permissions - -proc filetype*(p: PathComponent): string = - case p - of pcFile: - return "file" - of pcLinkToFile: - return "filelink" - of pcDir: - return "dir" - of pcLinkToDir: - return "dirlink" - -proc unixPermissions*(s: set[FilePermission]): int = - result = 0 - for p in s: - case p: - of fpUserRead: - result += 400 - of fpUserWrite: - result += 200 - of fpUserExec: - result += 100 - of fpGroupRead: - result += 40 - of fpGroupWrite: - result += 20 - of fpGroupExec: - result += 10 - of fpOthersRead: - result += 4 - of fpOthersWrite: - result += 2 - of fpOthersExec: - result += 1 - -proc toFilePermissions*(p: BiggestInt): set[FilePermission] = - let user = ($p)[0].int - let group = ($p)[1].int - let others = ($p)[2].int - if user == 1: - result.incl fpUserExec - if user == 2: - result.incl fpUserWrite - if user == 3: - result.incl fpUserExec - result.incl fpUserWrite - if user == 4: - result.incl fpUserRead - if user == 5: - result.incl fpUserRead - result.incl fpUserExec - if user == 6: - result.incl fpUserRead - result.incl fpUserWrite - if user == 7: - result.incl fpUserRead - result.incl fpUserWrite - result.incl fpUserExec - if group == 1: - result.incl fpGroupExec - if group == 2: - result.incl fpGroupWrite - if group == 3: - result.incl fpGroupExec - result.incl fpGroupWrite - if group == 4: - result.incl fpGroupRead - if group == 5: - result.incl fpGroupRead - result.incl fpGroupExec - if group == 6: - result.incl fpGroupRead - result.incl fpGroupWrite - if group == 7: - result.incl fpGroupRead - result.incl fpGroupWrite - result.incl fpGroupExec - if others == 1: - result.incl fpOthersExec - if others == 2: - result.incl fpOthersWrite - if others == 3: - result.incl fpOthersExec - result.incl fpOthersWrite - if others == 4: - result.incl fpOthersRead - if others == 5: - result.incl fpOthersRead - result.incl fpOthersExec - if others == 6: - result.incl fpOthersRead - result.incl fpOthersWrite - if others == 7: - result.incl fpOthersRead - result.incl fpOthersWrite - result.incl fpOthersExec
D core/interpreter.nim

@@ -1,426 +0,0 @@

-import - streams, - strutils, - sequtils, - os, - critbits, - algorithm -when defined(mini): - import - minilogger -else: - import - base64, - logging -import - baseutils, - value, - scope, - parser - -type - MinTrappedException* = ref object of CatchableError - MinReturnException* = ref object of CatchableError - MinRuntimeError* = ref object of CatchableError - data*: MinValue - -var ASSETPATH* {.threadvar.}: string -ASSETPATH = "" -var COMPILEDMINFILES* {.threadvar.}: CritBitTree[MinOperatorProc] -var COMPILEDASSETS* {.threadvar.}: CritBitTree[string] - -const USER_SYMBOL_REGEX* = "^[a-zA-Z_][a-zA-Z0-9/!?+*._-]*$" - - -proc newSym*(i: In, s: string): MinValue = - return MinValue(kind: minSymbol, symVal: s, filename: i.currSym.filename, line: i.currSym.line, column: i.currSym.column, outerSym: i.currSym.symVal) - -proc copySym*(i: In, sym: MinValue): MinValue = - return MinValue(kind: minSymbol, symVal: sym.outerSym, filename: sym.filename, line: sym.line, column: sym.column, outerSym: "") - -proc raiseRuntime*(msg: string, data: MinValue) = - data.objType = "error" - raise MinRuntimeError(msg: msg, data: data) - -proc dump*(i: MinInterpreter): string = - var s = "" - for item in i.stack: - s = s & $item & " " - return s - -proc debug*(i: In, value: MinValue) = - debug("(" & i.dump & $value & ")") - -proc debug*(i: In, value: string) = - debug(value) - -template withScope*(i: In, res:ref MinScope, body: untyped): untyped = - let origScope = i.scope - try: - i.scope = newScopeRef(origScope) - body - res = i.scope - finally: - i.scope = origScope - -template withScope*(i: In, body: untyped): untyped = - let origScope = i.scope - try: - i.scope = newScopeRef(origScope) - body - finally: - i.scope = origScope - -template withDictScope*(i: In, s: ref MinScope, body: untyped): untyped = - let origScope = i.scope - try: - i.scope = s - body - finally: - i.scope = origScope - -proc newMinInterpreter*(filename = "input", pwd = ""): MinInterpreter = - var path = pwd - when not defined(mini): - if not pwd.isAbsolute: - path = joinPath(getCurrentDir(), pwd) - var stack:MinStack = newSeq[MinValue](0) - var trace:MinStack = newSeq[MinValue](0) - var stackcopy:MinStack = newSeq[MinValue](0) - var pr:MinParser - var scope = newScopeRef(nil) - var i:MinInterpreter = MinInterpreter( - filename: filename, - pwd: path, - parser: pr, - stack: stack, - trace: trace, - stackcopy: stackcopy, - scope: scope, - currSym: MinValue(column: 1, line: 1, kind: minSymbol, symVal: "") - ) - return i - -proc copy*(i: MinInterpreter, filename: string): MinInterpreter = - var path = filename - when not defined(mini): - if not filename.isAbsolute: - path = joinPath(getCurrentDir(), filename) - result = newMinInterpreter() - result.filename = filename - result.pwd = path.parentDirEx - result.stack = i.stack - result.trace = i.trace - result.stackcopy = i.stackcopy - result.scope = i.scope - result.currSym = MinValue(column: 1, line: 1, kind: minSymbol, symVal: "") - -proc formatError(sym: MinValue, message: string): string = - var name = sym.symVal - #if sym.parentSym != "": - # name = sym.parentSym - return "$1($2,$3) [$4]: $5" % [sym.filename, $sym.line, $sym.column, name, message] - -proc formatTrace(sym: MinValue): string = - var name = sym.symVal - #if sym.parentSym != "": - # name = sym.parentSym - if sym.filename == "": - return "<native> in symbol: $1" % [name] - else: - return "$1($2,$3) in symbol: $4" % [sym.filename, $sym.line, $sym.column, name] - -proc stackTrace*(i: In) = - var trace = i.trace - trace.reverse() - for sym in trace: - notice sym.formatTrace - -proc error(i: In, message: string) = - error(i.currSym.formatError(message)) - -proc open*(i: In, stream:Stream, filename: string) = - i.filename = filename - i.parser.open(stream, filename) - -proc close*(i: In) = - i.parser.close(); - -proc push*(i: In, val: MinValue) {.gcsafe, extern:"min_exported_symbol_$1".} - -proc call*(i: In, q: var MinValue): MinValue {.gcsafe, extern:"min_exported_symbol_$1".}= - var i2 = newMinInterpreter("<call>") - i2.trace = i.trace - i2.scope = i.scope - try: - i2.withScope(): - for v in q.qVal: - i2.push v - except: - i.currSym = i2.currSym - i.trace = i2.trace - raise - return i2.stack.newVal - -proc callValue*(i: In, v: var MinValue): MinValue {.gcsafe, extern:"min_exported_symbol_$1".}= - var i2 = newMinInterpreter("<call-value>") - i2.trace = i.trace - i2.scope = i.scope - try: - i2.withScope(): - i2.push v - except: - i.currSym = i2.currSym - i.trace = i2.trace - raise - return i2.stack[0] - -proc copyDict*(i: In, val: MinValue): MinValue {.gcsafe, extern:"min_exported_symbol_$1".}= - # Assuming val is a dictionary - var v = newDict(i.scope) - for item in val.scope.symbols.pairs: - v.scope.symbols[item.key] = item.val - for item in val.scope.sigils.pairs: - v.scope.sigils[item.key] = item.val - if val.objType != "": - v.objType = val.objType - if not val.obj.isNil: - v.obj = val.obj - return v - -proc applyDict*(i: In, val: MinValue): MinValue {.gcsafe, extern:"min_exported_symbol_$1".}= - # Assuming val is a dictionary - var v = i.copyDict(val) - for item in v.dVal.pairs: - var value = item.val.val - v.scope.symbols[item.key] = MinOperator(kind: minValOp, val: i.callValue(value), sealed: false) - return v - -proc apply*(i: In, op: MinOperator) {.gcsafe, extern:"min_exported_symbol_$1".}= - if op.kind == minProcOp: - op.prc(i) - else: - if op.val.kind == minQuotation: - var newscope = newScopeRef(i.scope) - i.withScope(newscope): - for e in op.val.qVal: - i.push e - else: - i.push(op.val) - -proc dequote*(i: In, q: var MinValue) = - if q.kind == minQuotation: - i.withScope(): - let qqval = deepCopy(q.qVal) - for v in q.qVal: - i.push v - q.qVal = qqval - else: - i.push(q) - -proc apply*(i: In, q: var MinValue) {.gcsafe, extern:"min_exported_symbol_$1_2".}= - var i2 = newMinInterpreter("<apply>") - i2.trace = i.trace - i2.scope = i.scope - try: - i2.withScope(): - for v in q.qVal: - if (v.kind == minQuotation): - var v2 = v - i2.dequote(v2) - else: - i2.push v - except: - i.currSym = i2.currSym - i.trace = i2.trace - raise - i.push i2.stack.newVal - -proc push*(i: In, val: MinValue) {.gcsafe, extern:"min_exported_symbol_$1".}= - if val.kind == minSymbol: - i.debug(val) - if not i.evaluating: - if val.outerSym != "": - i.currSym = i.copySym(val) - else: - i.currSym = val - i.trace.add val - let symbol = val.symVal - if symbol == "return": - raise MinReturnException(msg: "return symbol found") - if i.scope.hasSymbol(symbol): - i.apply i.scope.getSymbol(symbol) - else: - var qIndex = symbol.find('"') - if qIndex > 0: - let sigil = symbol[0..qIndex-1] - if not i.scope.hasSigil(sigil): - raiseUndefined("Undefined sigil '$1'"%sigil) - i.stack.add(MinValue(kind: minString, strVal: symbol[qIndex+1..symbol.len-2])) - i.apply(i.scope.getSigil(sigil)) - else: - let sigil = "" & symbol[0] - if symbol.len > 1 and i.scope.hasSigil(sigil): - i.stack.add(MinValue(kind: minString, strVal: symbol[1..symbol.len-1])) - i.apply(i.scope.getSigil(sigil)) - else: - raiseUndefined("Undefined symbol '$1'" % [val.symVal]) - discard i.trace.pop - elif val.kind == minDictionary and val.objType != "module": - # Dictionary must be copied every time they are interpreted, otherwise when they are used in cycles they reference each other. - var v = i.copyDict(val) - i.stack.add(v) - else: - i.stack.add(val) - -proc pop*(i: In): MinValue = - if i.stack.len > 0: - return i.stack.pop - else: - raiseEmptyStack() - -proc peek*(i: MinInterpreter): MinValue = - if i.stack.len > 0: - return i.stack[i.stack.len-1] - else: - raiseEmptyStack() - -template handleErrors*(i: In, body: untyped) = - try: - body - 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 interpret*(i: In, parseOnly=false): MinValue {.discardable, extern:"min_exported_symbol_$1".} = - var val: MinValue - var q: MinValue - if parseOnly: - q = newSeq[MinValue](0).newVal - while i.parser.token != tkEof: - if i.trace.len == 0: - i.stackcopy = i.stack - handleErrors(i) do: - val = i.parser.parseMinValue(i) - if parseOnly: - q.qVal.add val - else: - i.push val - if parseOnly: - return q - if i.stack.len > 0: - return i.stack[i.stack.len - 1] - -proc rawCompile*(i: In, indent = ""): seq[string] {.discardable, extern:"min_exported_symbol_$1".} = - while i.parser.token != tkEof: - if i.trace.len == 0: - i.stackcopy = i.stack - handleErrors(i) do: - result.add i.parser.compileMinValue(i, push = true, indent) - -proc compileFile*(i: In, main: bool): seq[string] {.discardable, extern:"min_exported_symbol_$1".} = - result = newSeq[string](0) - if not main: - result.add "COMPILEDMINFILES[\"$#\"] = proc(i: In) {.gcsafe.}=" % i.filename - result = result.concat(i.rawCompile(" ")) - else: - result = i.rawCompile("") - -proc initCompiledFile*(i: In, files: seq[string]): seq[string] {.discardable, extern:"min_exported_symbol_$1".} = - result = newSeq[string](0) - result.add "import min" - if files.len > 0 or (ASSETPATH != "" and not defined(mini)): - result.add "import critbits" - when not defined(mini): - if ASSETPATH != "": - result.add "import base64" - result.add "MINCOMPILED = true" - result.add "var i = newMinInterpreter(\"$#\")" % i.filename - result.add "i.stdLib()" - when not defined(mini): - if ASSETPATH != "": - for f in walkDirRec(ASSETPATH): - var file = f.replace("\\", "/") - logging.notice("- Including: $#" % file) - let ef = file.readFile.encode - let asset = "COMPILEDASSETS[\"$#\"] = \"$#\".decode" % [file, ef] - result.add asset - -proc eval*(i: In, s: string, name="<eval>", parseOnly=false): MinValue {.discardable, extern:"min_exported_symbol_$1".}= - var i2 = i.copy(name) - i2.open(newStringStream(s), name) - discard i2.parser.getToken() - result = i2.interpret(parseOnly) - i.trace = i2.trace - i.stackcopy = i2.stackcopy - i.stack = i2.stack - i.scope = i2.scope - -proc load*(i: In, s: string, parseOnly=false): MinValue {.discardable, extern:"min_exported_symbol_$1".}= - var fileLines = newSeq[string](0) - var contents = "" - try: - fileLines = s.readFile().splitLines() - except: - fatal("Cannot read from file: " & s) - if fileLines[0].len >= 2 and fileLines[0][0..1] == "#!": - contents = ";;\n" & fileLines[1..fileLines.len-1].join("\n") - else: - contents = fileLines.join("\n") - var i2 = i.copy(s) - i2.open(newStringStream(contents), s) - discard i2.parser.getToken() - result = i2.interpret(parseOnly) - i.trace = i2.trace - i.stackcopy = i2.stackcopy - i.stack = i2.stack - i.scope = i2.scope - -proc require*(i: In, s: string, parseOnly=false): MinValue {.discardable, extern:"min_exported_symbol_$1".}= - var fileLines = newSeq[string](0) - var contents = "" - try: - fileLines = s.readFile().splitLines() - except: - fatal("Cannot read from file: " & s) - if fileLines[0].len >= 2 and fileLines[0][0..1] == "#!": - contents = ";;\n" & fileLines[1..fileLines.len-1].join("\n") - else: - contents = fileLines.join("\n") - var i2 = i.copy(s) - let snapshot = deepCopy(i.stack) - i2.withScope: - i2.open(newStringStream(contents), s) - discard i2.parser.getToken() - discard i2.interpret(parseOnly) - if snapshot != i2.stack: - raiseInvalid("Module '$#' is polluting the stack" % s) - result = newDict(i2.scope) - result.objType = "module" - for key, value in i2.scope.symbols.pairs: - result.scope.symbols[key] = value - -proc parse*(i: In, s: string, name="<parse>"): MinValue = - return i.eval(s, name, true) - -proc read*(i: In, s: string): MinValue = - return i.load(s, true) - -# Inherit file/line/column from current symbol -proc pushSym*(i: In, s: string) = - i.push MinValue(kind: minSymbol, symVal: s, filename: i.currSym.filename, line: i.currSym.line, column: i.currSym.column, outerSym: i.currSym.symVal) -
M core/meta.nimminpkg/core/meta.nim

@@ -1,7 +1,7 @@

import strutils -const ymlconfig = "../min.yml".slurp +const ymlconfig = "../../min.yml".slurp var pkgName* {.threadvar.}: string var pkgVersion* {.threadvar.}: string
D core/parser.nim

@@ -1,866 +0,0 @@

-# Adapted from: https://github.com/Araq/Nimrod/blob/v0.9.6/lib/pure/json.nim -import - lexbase, - strutils, - sequtils, - streams, - critbits, - baseutils - -import unicode except strip - -type - MinTokenKind* = enum - tkError, - tkEof, - tkString, - tkInt, - tkFloat, - tkBracketLe, - tkBracketRi, - tkBraceLe, - tkBraceRi, - tkSymbol, - tkNull, - tkTrue, - tkFalse - MinKind* = enum - minInt, - minFloat, - minQuotation, - minDictionary, - minString, - minSymbol, - minNull, - minBool - MinEventKind* = enum ## enumeration of all events that may occur when parsing - eMinError, ## an error ocurred during parsing - eMinEof, ## end of file reached - eMinString, ## a string literal - eMinInt, ## an integer literal - eMinFloat, ## a float literal - eMinQuotationStart, ## start of an array: the ``(`` token - eMinQuotationEnd, ## start of an array: the ``)`` token - eMinDictionaryStart, ## start of a dictionary: the ``{`` token - eMinDictionaryEnd ## start of a dictionary: the ``}`` token - MinParserError* = enum ## enumeration that lists all errors that can occur - errNone, ## no error - errInvalidToken, ## invalid token - errStringExpected, ## string expected - errBracketRiExpected, ## ``)`` expected - errBraceRiExpected, ## ``}`` expected - errQuoteExpected, ## ``"`` or ``'`` expected - errEOC_Expected, ## ``*/`` expected - errEofExpected, ## EOF expected - errExprExpected - MinParserState* = enum - stateEof, - stateStart, - stateQuotation, - stateDictionary, - stateExpectValue - MinParser* = object of BaseLexer - a*: string - token*: MinTokenKind - state*: seq[MinParserState] - kind*: MinEventKind - err*: MinParserError - filename*: string - MinValue* = ref MinValueObject - MinValueObject* {.acyclic, final.} = object - line*: int - column*: int - filename*: string - outerSym*: string - case kind*: MinKind - of minNull: discard - of minInt: intVal*: BiggestInt - of minFloat: floatVal*: BiggestFloat - of minDictionary: - scope*: ref MinScope - obj*: pointer - objType*: string - of minQuotation: - qVal*: seq[MinValue] - of minString: strVal*: string - of minSymbol: symVal*: string - of minBool: boolVal*: bool - MinScopeKind* = enum - minNativeScope, - minLangScope - MinScope* {.acyclic, shallow, final.} = object - parent*: ref MinScope - symbols*: CritBitTree[MinOperator] - sigils*: CritBitTree[MinOperator] - kind*: MinScopeKind - MinOperatorProc* = proc (i: In) {.closure, gcsafe.} - MinOperatorKind* = enum - minProcOp - minValOp - MinOperator* = object - sealed*: bool - case kind*: MinOperatorKind - of minProcOp: - prc*: MinOperatorProc - of minValOp: - quotation*: bool - val*: MinValue - MinStack* = seq[MinValue] - In* = var MinInterpreter - MinInterpreter* = object - stack*: MinStack - trace*: MinStack - stackcopy*: MinStack - pwd*: string - scope*: ref MinScope - parser*: MinParser - currSym*: MinValue - filename*: string - evaluating*: bool - MinParsingError* = ref object of ValueError - MinUndefinedError* = ref object of ValueError - MinEmptyStackError* = ref object of ValueError - MinInvalidError* = ref object of ValueError - MinOutOfBoundsError* = ref object of ValueError - -var CVARCOUNT = 0 - -# Helpers - -proc raiseInvalid*(msg: string) = - raise MinInvalidError(msg: msg) - -proc raiseUndefined*(msg: string) = - raise MinUndefinedError(msg: msg) - -proc raiseOutOfBounds*(msg: string) = - raise MinOutOfBoundsError(msg: msg) - -proc raiseEmptyStack*() = - raise MinEmptyStackError(msg: "Insufficient items on the stack") - -proc dVal*(v: MinValue): CritBitTree[MinOperator] {.inline, extern:"min_exported_symbol_$1".}= - if v.kind != minDictionary: - raiseInvalid("dVal - Dictionary expected, got " & $v.kind) - if v.scope.isNil: - return CritBitTree[MinOperator]() - return v.scope.symbols - -const - errorMessages: array[MinParserError, string] = [ - "no error", - "invalid token", - "string expected", - "')' expected", - "'}' expected", - "'\"' expected", - "'*/' expected", - "EOF expected", - "expression expected" - ] - tokToStr: array[MinTokenKind, string] = [ - "invalid token", - "EOF", - "string literal", - "int literal", - "float literal", - "(", - ")", - "{", - "}", - "symbol", - "null", - "true", - "false" - ] - -proc newScope*(parent: ref MinScope, kind = minLangScope): MinScope = - result = MinScope(parent: parent, kind: kind) - -proc newScopeRef*(parent: ref MinScope, kind = minLangScope): ref MinScope = - new(result) - result[] = newScope(parent, kind) - -proc open*(my: var MinParser, input: Stream, filename: string) = - lexbase.open(my, input) - my.filename = filename - my.state = @[stateStart] - my.kind = eMinError - my.a = "" - -proc close*(my: var MinParser) {.inline, extern:"min_exported_symbol_$1".}= - lexbase.close(my) - -proc getInt*(my: MinParser): int {.inline, extern:"min_exported_symbol_$1".}= - assert(my.kind == eMinInt) - return parseint(my.a) - -proc getFloat*(my: MinParser): float {.inline, extern:"min_exported_symbol_$1".}= - assert(my.kind == eMinFloat) - return parseFloat(my.a) - -proc kind*(my: MinParser): MinEventKind {.inline, extern:"min_exported_symbol_$1".}= - return my.kind - -proc getColumn*(my: MinParser): int {.inline, extern:"min_exported_symbol_$1".}= - result = getColNumber(my, my.bufpos) - -proc getLine*(my: MinParser): int {.inline, extern:"min_exported_symbol_$1".}= - result = my.lineNumber - -proc getFilename*(my: MinParser): string {.inline, extern:"min_exported_symbol_$1".}= - result = my.filename - -proc errorMsg*(my: MinParser, msg: string): string = - assert(my.kind == eMinError) - result = "$1 [l:$2, c:$3] ERROR - $4" % [ - my.filename, $getLine(my), $getColumn(my), msg] - -proc errorMsg*(my: MinParser): string = - assert(my.kind == eMinError) - result = errorMsg(my, errorMessages[my.err]) - -proc errorMsgExpected*(my: MinParser, e: string): string = - result = errorMsg(my, e & " expected") - -proc raiseParsing*(p: MinParser, msg: string) {.noinline, noreturn, extern:"min_exported_symbol_$1".}= - raise MinParsingError(msg: errorMsgExpected(p, msg)) - -proc raiseUndefined*(p:MinParser, msg: string) {.noinline, noreturn, extern:"min_exported_symbol_$1_2".}= - raise MinUndefinedError(msg: errorMsg(p, msg)) - -proc parseNumber(my: var MinParser) = - var pos = my.bufpos - var buf = my.buf - if buf[pos] == '-': - add(my.a, '-') - inc(pos) - if buf[pos] == '.': - add(my.a, "0.") - inc(pos) - else: - while buf[pos] in Digits: - add(my.a, buf[pos]) - inc(pos) - if buf[pos] == '.': - add(my.a, '.') - inc(pos) - # digits after the dot: - while buf[pos] in Digits: - add(my.a, buf[pos]) - inc(pos) - if buf[pos] in {'E', 'e'}: - add(my.a, buf[pos]) - inc(pos) - if buf[pos] in {'+', '-'}: - add(my.a, buf[pos]) - inc(pos) - while buf[pos] in Digits: - add(my.a, buf[pos]) - inc(pos) - my.bufpos = pos - -proc handleHexChar(c: char, x: var int): bool = - result = true # Success - case c - of '0'..'9': x = (x shl 4) or (ord(c) - ord('0')) - of 'a'..'f': x = (x shl 4) or (ord(c) - ord('a') + 10) - of 'A'..'F': x = (x shl 4) or (ord(c) - ord('A') + 10) - else: result = false # error - -proc parseString(my: var MinParser): MinTokenKind = - result = tkString - var pos = my.bufpos + 1 - var buf = my.buf - while true: - case buf[pos] - of '\0': - my.err = errQuoteExpected - result = tkError - break - of '"': - inc(pos) - break - of '\\': - case buf[pos+1] - of '\\', '"', '\'', '/': - add(my.a, buf[pos+1]) - inc(pos, 2) - of 'b': - add(my.a, '\b') - inc(pos, 2) - of 'f': - add(my.a, '\f') - inc(pos, 2) - of 'n': - add(my.a, '\L') - inc(pos, 2) - of 'r': - add(my.a, '\C') - inc(pos, 2) - of 't': - add(my.a, '\t') - inc(pos, 2) - of 'u': - inc(pos, 2) - var r: int - if handleHexChar(buf[pos], r): inc(pos) - if handleHexChar(buf[pos], r): inc(pos) - if handleHexChar(buf[pos], r): inc(pos) - if handleHexChar(buf[pos], r): inc(pos) - add(my.a, toUTF8(Rune(r))) - else: - # don't bother with the error - add(my.a, buf[pos]) - inc(pos) - of '\c': - pos = lexbase.handleCR(my, pos) - buf = my.buf - add(my.a, '\c') - of '\L': - pos = lexbase.handleLF(my, pos) - buf = my.buf - add(my.a, '\L') - else: - add(my.a, buf[pos]) - inc(pos) - my.bufpos = pos # store back - -proc parseSymbol(my: var MinParser): MinTokenKind = - result = tkSymbol - var pos = my.bufpos - var buf = my.buf - if not(buf[pos] in Whitespace): - while not(buf[pos] in WhiteSpace) and not(buf[pos] in ['\0', ')', '(', '}', '{']): - if buf[pos] == '"': - add(my.a, buf[pos]) - my.bufpos = pos - let r = parseString(my) - if r == tkError: - result = tkError - return - add(my.a, buf[pos]) - return - else: - add(my.a, buf[pos]) - inc(pos) - my.bufpos = pos - -proc skip(my: var MinParser) = - var pos = my.bufpos - var buf = my.buf - while true: - case buf[pos] - of ';': - # skip line comment: - inc(pos, 2) - while true: - case buf[pos] - of '\0': - break - of '\c': - pos = lexbase.handleCR(my, pos) - buf = my.buf - break - of '\L': - pos = lexbase.handleLF(my, pos) - buf = my.buf - break - else: - inc(pos) - of '/': - if buf[pos+1] == '/': - # skip line comment: - inc(pos, 2) - while true: - case buf[pos] - of '\0': - break - of '\c': - pos = lexbase.handleCR(my, pos) - buf = my.buf - break - of '\L': - pos = lexbase.handleLF(my, pos) - buf = my.buf - break - else: - inc(pos) - elif buf[pos+1] == '*': - # skip long comment: - inc(pos, 2) - while true: - case buf[pos] - of '\0': - my.err = errEOC_Expected - break - of '\c': - pos = lexbase.handleCR(my, pos) - buf = my.buf - of '\L': - pos = lexbase.handleLF(my, pos) - buf = my.buf - of '*': - inc(pos) - if buf[pos] == '/': - inc(pos) - break - else: - inc(pos) - else: - break - of ' ', '\t': - inc(pos) - of '\c': - pos = lexbase.handleCR(my, pos) - buf = my.buf - of '\L': - pos = lexbase.handleLF(my, pos) - buf = my.buf - else: - break - my.bufpos = pos - -proc getToken*(my: var MinParser): MinTokenKind = - setLen(my.a, 0) - skip(my) - case my.buf[my.bufpos] - of '-', '.': - if my.bufpos+1 <= my.buf.len and my.buf[my.bufpos+1] in '0'..'9': - parseNumber(my) - if {'.', 'e', 'E'} in my.a: - result = tkFloat - else: - result = tkInt - else: - result = parseSymbol(my) - of '0'..'9': - parseNumber(my) - if {'.', 'e', 'E'} in my.a: - result = tkFloat - else: - result = tkInt - of '"': - result = parseString(my) - of '(': - inc(my.bufpos) - result = tkBracketLe - of ')': - inc(my.bufpos) - result = tkBracketRi - of '{': - inc(my.bufpos) - result = tkBraceLe - of '}': - inc(my.bufpos) - result = tkBraceRi - of '\0': - result = tkEof - else: - result = parseSymbol(my) - case my.a - of "null": result = tkNull - of "true": result = tkTrue - of "false": result = tkFalse - else: - discard - my.token = result - - -proc next*(my: var MinParser) = - var tk = getToken(my) - var i = my.state.len-1 - case my.state[i] - of stateEof: - if tk == tkEof: - my.kind = eMinEof - else: - my.kind = eMinError - my.err = errEofExpected - of stateStart: - case tk - of tkString, tkInt, tkFloat, tkTrue, tkFalse: - my.state[i] = stateEof # expect EOF next! - my.kind = MinEventKind(ord(tk)) - of tkBracketLe: - my.state.add(stateQuotation) # we expect any - my.kind = eMinQuotationStart - of tkBraceLe: - my.state.add(stateDictionary) # we expect any - my.kind = eMinDictionaryStart - of tkEof: - my.kind = eMinEof - else: - my.kind = eMinError - my.err = errEofExpected - of stateQuotation: - case tk - of tkString, tkInt, tkFloat, tkTrue, tkFalse: - my.kind = MinEventKind(ord(tk)) - of tkBracketLe: - my.state.add(stateQuotation) - my.kind = eMinQuotationStart - of tkBraceLe: - my.state.add(stateDictionary) - my.kind = eMinDictionaryStart - of tkBracketRi: - my.kind = eMinQuotationEnd - discard my.state.pop() - of tkBraceRi: - my.kind = eMinDictionaryEnd - discard my.state.pop() - else: - my.kind = eMinError - my.err = errBracketRiExpected - of stateDictionary: - case tk - of tkString, tkInt, tkFloat, tkTrue, tkFalse: - my.kind = MinEventKind(ord(tk)) - of tkBracketLe: - my.state.add(stateQuotation) - my.kind = eMinQuotationStart - of tkBraceLe: - my.state.add(stateDictionary) - my.kind = eMinDictionaryStart - of tkBracketRi: - my.kind = eMinQuotationEnd - discard my.state.pop() - of tkBraceRi: - my.kind = eMinDictionaryEnd - discard my.state.pop() - else: - my.kind = eMinError - my.err = errBraceRiExpected - of stateExpectValue: - case tk - of tkString, tkInt, tkFloat, tkTrue, tkFalse: - my.kind = MinEventKind(ord(tk)) - of tkBracketLe: - my.state.add(stateQuotation) - my.kind = eMinQuotationStart - of tkBraceLe: - my.state.add(stateDictionary) - my.kind = eMinDictionaryStart - else: - my.kind = eMinError - my.err = errExprExpected - -proc eat(p: var MinParser, token: MinTokenKind) = - if p.token == token: discard getToken(p) - else: raiseParsing(p, tokToStr[token]) - -proc `$`*(a: MinValue): string {.inline, extern:"min_exported_symbol_$1".}= - case a.kind: - of minNull: - return "null" - of minBool: - return $a.boolVal - of minSymbol: - return a.symVal - of minString: - return "\"$1\"" % a.strVal.replace("\"", "\\\"") - of minInt: - return $a.intVal - of minFloat: - return $a.floatVal - of minQuotation: - var q = "(" - for i in a.qVal: - q = q & $i & " " - q = q.strip & ")" - return q - of minDictionary: - var d = "{" - for i in a.dVal.pairs: - var v = "" - if i.val.kind == minProcOp: - v = "<native>" - else: - v = $i.val.val - var k = $i.key - if k.contains(" "): - k = "\"$1\"" % k - d = d & v & " :" & k & " " - if a.objType != "": - d = d & ";" & a.objType - d = d.strip & "}" - return d - -proc `$$`*(a: MinValue): string {.inline, extern:"min_exported_symbol_$1".}= - case a.kind: - of minNull: - return "null" - of minBool: - return $a.boolVal - of minSymbol: - return a.symVal - of minString: - return a.strVal - of minInt: - return $a.intVal - of minFloat: - return $a.floatVal - of minQuotation: - var q = "(" - for i in a.qVal: - q = q & $i & " " - q = q.strip & ")" - return q - of minDictionary: - var d = "{" - for i in a.dVal.pairs: - var v = "" - if i.val.kind == minProcOp: - v = "<native>" - else: - v = $i.val.val - var k = $i.key - if k.contains(" "): - k = "\"$1\"" % k - d = d & v & " :" & k & " " - if a.objType != "": - d = d & ";" & a.objType - d = d.strip & "}" - return d - -proc parseMinValue*(p: var MinParser, i: In): MinValue = - case p.token - of tkNull: - result = MinValue(kind: minNull) - discard getToken(p) - of tkTrue: - result = MinValue(kind: minBool, boolVal: true) - discard getToken(p) - of tkFalse: - result = MinValue(kind: minBool, boolVal: false) - discard getToken(p) - of tkString: - result = MinValue(kind: minString, strVal: p.a) - p.a = "" - discard getToken(p) - of tkInt: - result = MinValue(kind: minInt, intVal: parseint(p.a)) - discard getToken(p) - of tkFloat: - result = MinValue(kind: minFloat, floatVal: parseFloat(p.a)) - discard getToken(p) - of tkBracketLe: - var q = newSeq[MinValue](0) - discard getToken(p) - while p.token != tkBracketRi: - q.add p.parseMinValue(i) - eat(p, tkBracketRi) - result = MinValue(kind: minQuotation, qVal: q) - of tkBraceLe: - var scope = newScopeRef(nil) - var val: MinValue - discard getToken(p) - var c = 0 - while p.token != tkBraceRi: - c = c+1 - let v = p.parseMinValue(i) - if val.isNil: - val = v - elif v.kind == minSymbol: - let key = v.symVal - if key[0] == ':': - var offset = 0 - if key[1] == '"': - offset = 1 - scope.symbols[key[1+offset .. key.len-1-offset]] = MinOperator(kind: minValOp, val: val, 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") - result = MinValue(kind: minDictionary, scope: scope) - of tkSymbol: - result = MinValue(kind: minSymbol, symVal: p.a, column: p.getColumn, line: p.lineNumber, filename: p.filename) - p.a = "" - discard getToken(p) - else: - let err = "Undefined or invalid value: "&p.a - raiseUndefined(p, err) - result.filename = p.filename - -proc compileMinValue*(p: var MinParser, i: In, push = true, indent = ""): seq[string] = - var op = indent - if push: - op = indent&"i.push " - result = newSeq[string](0) - case p.token - of tkNull: - result = @[op&"MinValue(kind: minNull)"] - discard getToken(p) - 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.escapeEx&")"] - 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: - CVARCOUNT.inc - var qvar = "q" & $CVARCOUNT - result.add indent&"var "&qvar&" = newSeq[MinValue](0)" - discard getToken(p) - while p.token != tkBracketRi: - var instructions = p.compileMinValue(i, false, indent) - let v = instructions.pop - result = result.concat(instructions) - result.add indent&qvar&".add "&v - eat(p, tkBracketRi) - result.add op&"MinValue(kind: minQuotation, qVal: "&qvar&")" - of tkBraceLe: - result = newSeq[string](0) - var val: MinValue - discard getToken(p) - var c = 0 - var valInitialized = false - CVARCOUNT.inc - var scopevar = "scope" & $CVARCOUNT - CVARCOUNT.inc - var valvar = "val" & $CVARCOUNT - while p.token != tkBraceRi: - c = c+1 - var instructions = p.compileMinValue(i, false, indent) - let v = p.parseMinValue(i) - let vs = instructions.pop - result = result.concat(instructions) - if val.isNil: - if not valInitialized: - result.add indent&"var "&valvar&": MinValue" - valInitialized = true - result.add indent&valvar&" = "&vs - elif v.kind == minSymbol: - let key = v.symVal - if key[0] == ':': - result.add indent&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") - result.add indent&"var "&scopevar&" = newScopeRef(nil)" - result.add op&"MinValue(kind: minDictionary, scope: "&scopevar&")" - of tkSymbol: - result = @[op&"MinValue(kind: minSymbol, symVal: "&p.a.escapeEx&", column: " & $p.getColumn & ", line: " & $p.lineNumber & ", filename: "&p.filename.escapeEx&")"] - p.a = "" - discard getToken(p) - else: - raiseUndefined(p, "Undefined value: '"&p.a&"'") - -proc print*(a: MinValue) = - stdout.write($$a) - -# Predicates - -proc isNull*(s: MinValue): bool = - return s.kind == minNull - -proc isSymbol*(s: MinValue): bool = - return s.kind == minSymbol - -proc isQuotation*(s: MinValue): bool = - return s.kind == minQuotation - -proc isString*(s: MinValue): bool = - return s.kind == minString - -proc isFloat*(s: MinValue): bool = - return s.kind == minFloat - -proc isInt*(s: MinValue): bool = - return s.kind == minInt - -proc isNumber*(s: MinValue): bool = - return s.kind == minInt or s.kind == minFloat - -proc isBool*(s: MinValue): bool = - return s.kind == minBool - -proc isStringLike*(s: MinValue): bool = - return s.isSymbol or s.isString or (s.isQuotation and s.qVal.len == 1 and s.qVal[0].isSymbol) - -proc isDictionary*(q: MinValue): bool = - return q.kind == minDictionary - -proc isTypedDictionary*(q: MinValue): bool = - if q.isDictionary: - return q.objType != "" - return false - -proc isTypedDictionary*(q: MinValue, t: string): bool = - if q.isTypedDictionary: - return q.objType == t - return false - -proc `==`*(a: MinValue, b: MinValue): bool {.inline, extern:"min_exported_symbol_eqeq".}= - if not (a.kind == b.kind or (a.isNumber and b.isNumber)): - return false - if a.kind == minSymbol and b.kind == minSymbol: - return a.symVal == b.symVal - elif a.kind == minInt and b.kind == minInt: - return a.intVal == b.intVal - elif a.kind == minInt and b.kind == minFloat: - return a.intVal.float == b.floatVal.float - elif a.kind == minFloat and b.kind == minFloat: - return a.floatVal == b.floatVal - elif a.kind == minFloat and b.kind == minInt: - return a.floatVal == b.intVal.float - elif a.kind == b.kind: - if a.kind == minString: - return a.strVal == b.strVal - elif a.kind == minBool: - return a.boolVal == b.boolVal - elif a.kind == minNull: - return true - elif a.kind == minQuotation: - if a.qVal.len == b.qVal.len: - var c = 0 - for item in a.qVal: - if item == b.qVal[c]: - c.inc - else: - return false - return true - else: - return false - elif a.kind == minDictionary: - let aVal = a.dVal - let bVal = b.dVal - if aVal.len != bVal.len: - return false - else: - for t in aVal.pairs: - if not bVal.hasKey(t.key): - return false - let v1 = t.val - let v2 = bVal[t.key] - if v1.kind != v2.kind: - return false - if v1.kind == minValOp: - return v1.val == v2.val - if a.objType == "" and b.objType == "": - return true - elif a.objType != "" and b.objType != "": - return a.objType == b.objType - else: - return false - else: - return false
D core/scope.nim

@@ -1,96 +0,0 @@

-import - strutils, - critbits -import - parser - -proc copy*(s: ref MinScope): ref MinScope = - var scope = newScope(s.parent) - scope.symbols = s.symbols - scope.sigils = s.sigils - new(result) - result[] = scope - -proc getSymbol*(scope: ref MinScope, key: string, acc=0): MinOperator = - if scope.symbols.hasKey(key): - return scope.symbols[key] - else: - if scope.parent.isNil: - raiseUndefined("Symbol '$1' not found." % key) - return scope.parent.getSymbol(key, acc + 1) - -proc hasSymbol*(scope: ref MinScope, key: string): bool = - if scope.isNil: - return false - elif scope.symbols.hasKey(key): - return true - elif not scope.parent.isNil: - return scope.parent.hasSymbol(key) - else: - return false - -proc delSymbol*(scope: ref MinScope, key: string): bool {.discardable, extern:"min_exported_symbol_$1".}= - if scope.symbols.hasKey(key): - if scope.symbols[key].sealed: - raiseInvalid("Symbol '$1' is sealed." % key) - scope.symbols.excl(key) - return true - return false - -proc setSymbol*(scope: ref MinScope, key: string, value: MinOperator, override = false): bool {.discardable, extern:"min_exported_symbol_$1".}= - result = false - # check if a symbol already exists in current scope - if not scope.isNil and scope.symbols.hasKey(key): - if not override and scope.symbols[key].sealed: - raiseInvalid("Symbol '$1' is sealed." % key) - scope.symbols[key] = value - result = true - else: - # Go up the scope chain and attempt to find the symbol - if not scope.parent.isNil: - result = scope.parent.setSymbol(key, value) - -proc getSigil*(scope: ref MinScope, key: string): MinOperator = - if scope.sigils.hasKey(key): - return scope.sigils[key] - elif not scope.parent.isNil: - return scope.parent.getSigil(key) - else: - raiseUndefined("Sigil '$1' not found." % key) - -proc hasSigil*(scope: ref MinScope, key: string): bool = - if scope.isNil: - return false - elif scope.sigils.hasKey(key): - return true - elif not scope.parent.isNil: - return scope.parent.hasSigil(key) - else: - return false - -proc delSigil*(scope: ref MinScope, key: string): bool {.discardable, extern:"min_exported_symbol_$1".}= - if scope.sigils.hasKey(key): - if scope.sigils[key].sealed: - raiseInvalid("Sigil '$1' is sealed." % key) - scope.sigils.excl(key) - return true - return false - -proc setSigil*(scope: ref MinScope, key: string, value: MinOperator, override = false): bool {.discardable, extern:"min_exported_symbol_$1".}= - result = false - # check if a sigil already exists in current scope - if not scope.isNil and scope.sigils.hasKey(key): - if not override and scope.sigils[key].sealed: - raiseInvalid("Sigil '$1' is sealed." % key) - scope.sigils[key] = value - result = true - else: - # Go up the scope chain and attempt to find the sigil - if not scope.parent.isNil: - result = scope.parent.setSymbol(key, value) - -proc previous*(scope: ref MinScope): ref MinScope = - if scope.parent.isNil: - return scope - else: - return scope.parent
D core/utils.nim

@@ -1,287 +0,0 @@

-import - strutils, - critbits -import - baseutils, - parser, - value, - scope, - interpreter - -when not defined(mini): - import - json - -# Library methods - -proc define*(i: In): ref MinScope = - var scope = newScopeRef(i.scope, minNativeScope) - scope.parent = i.scope - return scope - -proc symbol*(scope: ref MinScope, sym: string, p: MinOperatorProc) = - scope.symbols[sym] = MinOperator(prc: p, kind: minProcOp, sealed: true) - -proc symbol*(scope: ref MinScope, sym: string, v: MinValue) = - scope.symbols[sym] = MinOperator(val: v, kind: minValOp, sealed: true) - -proc sigil*(scope: ref MinScope, sym: string, p: MinOperatorProc) = - scope.sigils[sym] = MinOperator(prc: p, kind: minProcOp, sealed: true) - -proc sigil*(scope: ref MinScope, sym: string, v: MinValue) = - scope.sigils[sym] = MinOperator(val: v, kind: minValOp, sealed: true) - -proc finalize*(scope: ref MinScope, name: string = "") = - var mdl = newDict(scope) - mdl.scope = scope - mdl.objType = "module" - let op = proc(i: In) {.closure.} = - i.evaluating = true - i.push mdl - i.evaluating = false - if name != "": - scope.previous.symbols[name] = MinOperator(kind: minProcOp, prc: op) - -# Dictionary Methods - -proc dget*(i: In, q: MinValue, s: MinValue): MinValue = - if not q.isDictionary: - raiseInvalid("Value is not a dictionary") - if q.dVal[s.getString].kind == minProcOp: - raiseInvalid("Key '$1' is set to a native value that cannot be retrieved." % [s.getString]) - result = q.dVal[s.getString].val - -proc dget*(i: In, q: MinValue, s: string): MinValue = - if not q.isDictionary: - raiseInvalid("Value is not a dictionary") - if q.dVal[s].kind == minProcOp: - raiseInvalid("Key $1 is set to a native value that cannot be retrieved." % [s]) - result = q.dVal[s].val - -proc dhas*(q: MinValue, s: MinValue): bool = - if not q.isDictionary: - raiseInvalid("Value is not a dictionary") - return q.dVal.contains(s.getString) - -proc dhas*(q: MinValue, s: string): bool = - if not q.isDictionary: - raiseInvalid("Value is not a dictionary") - return q.dVal.contains(s) - -proc ddel*(i: In, p: var MinValue, s: MinValue): MinValue {.discardable, extern:"min_exported_symbol_$1".} = - if not p.isDictionary: - raiseInvalid("Value is not a dictionary") - excl(p.scope.symbols, s.getString) - return p - -proc ddel*(i: In, p: var MinValue, s: string): MinValue {.discardable, extern:"min_exported_symbol_$1_2".} = - if not p.isDictionary: - raiseInvalid("Value is not a dictionary") - excl(p.scope.symbols, s) - return p - -proc dset*(i: In, p: var MinValue, s: MinValue, m: MinValue): MinValue {.discardable, extern:"min_exported_symbol_$1".}= - if not p.isDictionary: - raiseInvalid("Value is not a dictionary") - var q = m - p.scope.symbols[s.getString] = MinOperator(kind: minValOp, val: q, sealed: false) - return p - -proc dset*(i: In, p: var MinValue, s: string, m: MinValue): MinValue {.discardable, extern:"min_exported_symbol_$1_2".}= - if not p.isDictionary: - raiseInvalid("Value is not a dictionary") - var q = m - p.scope.symbols[s] = MinOperator(kind: minValOp, val: q, sealed: false) - return p - -proc keys*(i: In, q: MinValue): MinValue = - # Assumes q is a dictionary - var r = newSeq[MinValue](0) - for i in q.dVal.keys: - r.add newVal(i) - return r.newVal - -proc values*(i: In, q: MinValue): MinValue = - # Assumes q is a dictionary - var r = newSeq[MinValue](0) - for item in q.dVal.values: - if item.kind == minProcOp: - raiseInvalid("Dictionary contains native values that cannot be accessed.") - r.add item.val - return r.newVal - -proc pairs*(i: In, q: MinValue): MinValue = - # Assumes q is a dictionary - var r = newSeq[MinValue](0) - for key, value in q.dVal.pairs: - if value.kind == minProcOp: - raiseInvalid("Dictionary contains native values that cannot be accessed.") - r.add key.newVal - r.add value.val - return r.newVal - -when not defined(mini): - - # JSON interop - - proc `%`*(i: In, a: MinValue): JsonNode = - case a.kind: - of minBool: - return %a.boolVal - of minNull: - return newJNull() - of minSymbol: - return %(";sym:$1" % [a.getstring]) - of minString: - return %a.strVal - of minInt: - return %a.intVal - of minFloat: - return %a.floatVal - of minQuotation: - result = newJArray() - for it in a.qVal: - result.add(i%it) - of minDictionary: - result = newJObject() - for it in a.dVal.pairs: - result[it.key] = i%i.dget(a, it.key) - - proc fromJson*(i: In, json: JsonNode): MinValue = - case json.kind: - of JNull: - result = newNull() - of JBool: - result = json.getBool.newVal - of JInt: - result = json.getBiggestInt.newVal - of JFloat: - result = json.getFloat.newVal - of JString: - let s = json.getStr - if s.startsWith(";sym:"): - result = s.replace(";sym:", "").newSym - else: - result = json.getStr.newVal - of JObject: - var res = newDict(i.scope) - for key, value in json.pairs: - discard i.dset(res, key, i.fromJson(value)) - return res - of JArray: - var res = newSeq[MinValue](0) - for value in json.items: - res.add i.fromJson(value) - return res.newVal - -# Validators - -proc validate*(value: MinValue, t: string): bool = - case t: - of "bool": - return value.isBool - of "null": - return value.isNull - of "int": - return value.isInt - of "num": - return value.isNumber - of "quot": - return value.isQuotation - of "dict": - return value.isDictionary - of "'sym": - return value.isStringLike - of "sym": - return value.isSymbol - of "float": - return value.isFloat - of "string": - return value.isString - of "a": - return true - else: - var split = t.split(":") - # Typed dictionaries - if split[0] == "dict": - if value.isTypedDictionary(split[1]): - return true - return false - -proc validType*(s: string): bool = - const ts = ["bool", "null", "int", "num", "float", "quot", "dict", "'sym", "sym", "string", "a"] - if ts.contains(s): - return true - for tt in s.split("|"): - if not ts.contains(tt) or tt.startsWith("dict:"): - return false - return true - -proc expect*(i: var MinInterpreter, elements: varargs[string]): seq[MinValue] = - let stack = elements.reverse.join(" ") - let sym = i.currSym.getString - var valid = newSeq[string](0) - result = newSeq[MinValue](0) - let message = proc(invalid: string): string = - result = "Symbol: $1 - Incorrect values found on the stack:\n" % sym - result &= "- expected: " & stack & " $1\n" % sym - var other = "" - if valid.len > 0: - other = valid.reverse.join(" ") & " " - result &= "- got: " & invalid & " " & other & sym - for element in elements: - let value = i.pop - result.add value - var split = element.split("|") - if split.len > 1: - var res = false - for t in split: - if validate(value, t): - res = true - break - if not res: - raiseInvalid(message(value.typeName)) - elif not validate(value, element): - raiseInvalid(message(value.typeName)) - valid.add element - -proc reqQuotationOfQuotations*(i: var MinInterpreter, a: var MinValue) = - a = i.pop - if not a.isQuotation: - raiseInvalid("A quotation is required on the stack") - for s in a.qVal: - if not s.isQuotation: - raiseInvalid("A quotation of quotations is required on the stack") - -proc reqQuotationOfNumbers*(i: var MinInterpreter, a: var MinValue) = - a = i.pop - if not a.isQuotation: - raiseInvalid("A quotation is required on the stack") - for s in a.qVal: - if not s.isNumber: - raiseInvalid("A quotation of numbers is required on the stack") - -proc reqQuotationOfSymbols*(i: var MinInterpreter, a: var MinValue) = - a = i.pop - if not a.isQuotation: - raiseInvalid("A quotation is required on the stack") - for s in a.qVal: - if not s.isSymbol: - raiseInvalid("A quotation of symbols is required on the stack") - -proc reqTwoNumbersOrStrings*(i: var MinInterpreter, a, b: var MinValue) = - a = i.pop - b = i.pop - if not (a.isString and b.isString or a.isNumber and b.isNumber): - raiseInvalid("Two numbers or two strings are required on the stack") - -proc reqStringOrQuotation*(i: var MinInterpreter, a: var MinValue) = - a = i.pop - if not a.isQuotation and not a.isString: - raiseInvalid("A quotation or a string is required on the stack") - -proc reqTwoQuotationsOrStrings*(i: var MinInterpreter, a, b: var MinValue) = - a = i.pop - b = i.pop - if not (a.isQuotation and b.isQuotation or a.isString and b.isString): - raiseInvalid("Two quotations or two strings are required on the stack")
D core/value.nim

@@ -1,77 +0,0 @@

-import - parser - -proc typeName*(v: MinValue): string = - case v.kind: - of minInt: - return "int" - of minFloat: - return "float" - of minDictionary: - if v.isTypedDictionary: - return "dict:" & v.objType - else: - return "dict" - of minQuotation: - return "quot" - of minString: - return "string" - of minSymbol: - return "sym" - of minNull: - return "null" - of minBool: - return "bool" - -# Constructors - -proc newNull*(): MinValue = - return MinValue(kind: minNull) - -proc newVal*(s: string): MinValue = - return MinValue(kind: minString, strVal: s) - -proc newVal*(s: cstring): MinValue = - return MinValue(kind: minString, strVal: $s) - -proc newVal*(q: seq[MinValue]): MinValue = - return MinValue(kind: minQuotation, qVal: q) - -proc newVal*(i: BiggestInt): MinValue = - return MinValue(kind: minInt, intVal: i) - -proc newVal*(f: BiggestFloat): MinValue = - return MinValue(kind: minFloat, floatVal: f) - -proc newVal*(s: bool): MinValue = - return MinValue(kind: minBool, boolVal: s) - -proc newDict*(parentScope: ref MinScope): MinValue = - return MinValue(kind: minDictionary, scope: newScopeRef(parentScope)) - -proc newSym*(s: string): MinValue = - return MinValue(kind: minSymbol, symVal: s) - -# Get string value from string or quoted symbol - -proc getFloat*(v: MinValue): float = - if v.isInt: - return v.intVal.float - elif v.isFloat: - return v.floatVal - else: - raiseInvalid("Value is not a number") - -proc getString*(v: MinValue): string = - if v.isSymbol: - return v.symVal - elif v.isString: - return v.strVal - elif v.isQuotation: - if v.qVal.len != 1: - raiseInvalid("Quotation is not a quoted symbol") - let sym = v.qVal[0] - if sym.isSymbol: - return sym.symVal - else: - raiseInvalid("Quotation is not a quoted symbol")
D lib/min_crypto.nim

@@ -1,134 +0,0 @@

-import - base64, - strutils, - times, - ../vendor/aes/aes -import - ../core/parser, - ../core/value, - ../core/interpreter, - ../core/utils - -{.compile: "../vendor/aes/libaes.c".} - -when defined(ssl): - import - openssl - - proc EVP_MD_CTX_new*(): EVP_MD_CTX {.cdecl, importc: "EVP_MD_CTX_new".} - proc EVP_MD_CTX_free*(ctx: EVP_MD_CTX) {.cdecl, importc: "EVP_MD_CTX_free".} -else: - import - std/sha1, - md5 - -proc crypto_module*(i: In)= - let def = i.define() - - - def.symbol("encode") do (i: In): - let vals = i.expect("'sym") - let s = vals[0] - i.push s.getString.encode.newVal - - def.symbol("decode") do (i: In): - let vals = i.expect("'sym") - let s = vals[0] - i.push s.getString.decode.newVal - - when defined(ssl): - - when defined(windows): - {.passL: "-static -Lvendor/openssl/windows -lssl -lcrypto -lws2_32".} - elif defined(linux): - {.passL: "-static -Lvendor/openssl/linux -lssl -lcrypto".} - elif defined(macosx): - {.passL: "-Bstatic -Lvendor/openssl/macosx -lssl -lcrypto -Bdynamic".} - - proc hash(s: string, kind: EVP_MD, size: int): string = - var hash_length: cuint = 0 - var hash = alloc[ptr cuchar](size) - let ctx = EVP_MD_CTX_new() - discard EVP_DigestInit_ex(ctx, kind, nil) - discard EVP_DigestUpdate(ctx, unsafeAddr s[0], s.len.cuint) - discard EVP_DigestFinal_ex(ctx, hash, cast[ptr cuint](hash_length)) - EVP_MD_CTX_free(ctx) - var hashStr = newString(size) - copyMem(addr(hashStr[0]), hash, size) - dealloc(hash) - return hashStr.toHex.toLowerAscii[0..size-1] - - def.symbol("md5") do (i: In): - let vals = i.expect("'sym") - let s = vals[0].getString - i.push hash(s, EVP_md5(), 32).newVal - - def.symbol("md4") do (i: In): - let vals = i.expect("'sym") - let s = vals[0].getString - i.push hash(s, EVP_md4(), 32).newVal - - def.symbol("sha1") do (i: In): - let vals = i.expect("'sym") - var s = vals[0].getString - i.push hash(s, EVP_sha1(), 40).newVal - - def.symbol("sha224") do (i: In): - let vals = i.expect("'sym") - let s = vals[0].getString - i.push hash(s, EVP_sha224(), 56).newVal - - def.symbol("sha256") do (i: In): - let vals = i.expect("'sym") - let s = vals[0].getString - i.push hash(s, EVP_sha256(), 64).newVal - - def.symbol("sha384") do (i: In): - let vals = i.expect("'sym") - let s = vals[0].getString - i.push hash(s, EVP_sha384(), 96).newVal - - def.symbol("sha512") do (i: In): - let vals = i.expect("'sym") - let s = vals[0].getString - i.push hash(s, EVP_sha512(), 128).newVal - - def.symbol("aes") do (i: In): - let vals = i.expect("'sym", "'sym") - let k = vals[0] - let s = vals[1] - var text = s.getString - var key = hash(k.getString, EVP_sha1(), 40) - var iv = hash((key & $getTime().toUnix), EVP_sha1(), 40) - var ctx = cast[ptr AES_ctx](alloc0(sizeof(AES_ctx))) - AES_init_ctx_iv(ctx, cast[ptr uint8](key[0].addr), cast[ptr uint8](iv[0].addr)); - var input = cast[ptr uint8](text[0].addr) - AES_CTR_xcrypt_buffer(ctx, input, text.len.uint32); - i.push text.newVal - - else: - - def.symbol("md5") do (i: In): - let vals = i.expect("'sym") - let s = vals[0].getString - i.push newVal($toMD5(s)) - - def.symbol("sha1") do (i: In): - let vals = i.expect("'sym") - var s = vals[0].getString - i.push newVal(toLowerAscii($secureHash(s))) - - def.symbol("aes") do (i: In): - let vals = i.expect("'sym", "'sym") - let k = vals[0] - let s = vals[1] - var text = s.getString - var key = ($secureHash(k.getString)).toLowerAscii - var iv = ($secureHash((key & $getTime().toUnix))).toLowerAscii - var ctx = cast[ptr AES_ctx](alloc0(sizeof(AES_ctx))) - AES_init_ctx_iv(ctx, cast[ptr uint8](key[0].addr), cast[ptr uint8](iv[0].addr)); - var input = cast[ptr uint8](text[0].addr) - AES_CTR_xcrypt_buffer(ctx, input, text.len.uint32); - i.push text.newVal - - def.finalize("crypto")
D lib/min_fs.nim

@@ -1,67 +0,0 @@

-import - os, - times -import - ../core/parser, - ../core/value, - ../core/interpreter, - ../core/utils, - ../core/fileutils - -proc fs_module*(i: In) = - - let def = i.define() - - def.symbol("mtime") do (i: In): - let vals = i.expect("'sym") - let s = vals[0] - i.push s.getString.getLastModificationTime.toUnix.newVal - - def.symbol("atime") do (i: In): - let vals = i.expect("'sym") - let s = vals[0] - i.push s.getString.getLastAccessTime.toUnix.newVal - - def.symbol("ctime") do (i: In): - let vals = i.expect("'sym") - let s = vals[0] - i.push s.getString.getCreationTime.toUnix.newVal - - def.symbol("hidden?") do (i: In): - let vals = i.expect("'sym") - let s = vals[0] - i.push s.getString.isHidden.newVal - - def.symbol("fsize") do (i: In): - let vals = i.expect("'sym") - let s = vals[0] - i.push s.getString.getFileSize.newVal - - def.symbol("fstats") do (i: In): - let vals = i.expect("'sym") - let s = vals[0] - let fi = s.getString.getFileInfo - var info = newDict(i.scope) - i.dset(info, "name", s) - i.dset(info, "device", fi.id.device.BiggestInt.newVal) - i.dset(info, "file", fi.id.file.BiggestInt.newVal) - i.dset(info, "type", fi.kind.filetype.newVal) - i.dset(info, "size", fi.size.newVal) - i.dset(info, "permissions", fi.permissions.unixPermissions.newVal) - i.dset(info, "nlinks", fi.linkCount.newVal) - i.dset(info, "ctime", fi.creationTime.toUnix.newVal) - i.dset(info, "atime", fi.lastAccessTime.toUnix.newVal) - i.dset(info, "mtime", fi.lastWriteTime.toUnix.newVal) - i.push info - - def.symbol("ftype") do (i: In): - let vals = i.expect("'sym") - let s = vals[0] - i.push s.getString.getFileInfo.kind.filetype.newVal - - def.symbol("fperms") do (i: In): - let vals = i.expect("'sym") - let s = vals[0] - i.push s.getString.getFilePermissions.unixPermissions.newVal - - def.finalize("fs")
M lib/min_http.nimminpkg/lib/min_http.nim

@@ -8,11 +8,11 @@ ../core/utils

when defined(ssl) and defined(amd64): when defined(windows): - {.passL: "-static -Lvendor/openssl/windows -lssl -lcrypto -lws2_32".} + {.passL: "-static -Lminpkg/vendor/openssl/windows -lssl -lcrypto -lws2_32".} elif defined(linux): - {.passL: "-static -Lvendor/openssl/linux -lssl -lcrypto".} + {.passL: "-static -Lminpkg/vendor/openssl/linux -lssl -lcrypto".} elif defined(macosx): - {.passL: "-Bstatic -Lvendor/openssl/macosx -lssl -lcrypto -Bdynamic".} + {.passL: "-Bstatic -Lminpkg/vendor/openssl/macosx -lssl -lcrypto -Bdynamic".} var minUserAgent {.threadvar.} : string minUserAgent = "$1 http-module/$2" % [pkgName, pkgVersion]
D lib/min_io.nim

@@ -1,209 +0,0 @@

-import - strutils, - logging, - critbits, - terminal -import - ../packages/nimline/nimline, - ../packages/nim-sgregex/sgregex, - ../core/parser, - ../core/value, - ../core/env, - ../core/interpreter, - ../core/utils - -var ORIGKEYMAP {.threadvar.}: CritBitTree[KeyCallback] -for key, value in KEYMAP.pairs: - ORIGKEYMAP[key] = value - -proc io_module*(i: In) = - let def = i.define() - - def.symbol("clear") do (i: In): - stdout.eraseScreen - stdout.setCursorPos(0, 0) - - def.symbol("unmapkey") do (i: In): - let vals = i.expect("'sym") - let key = vals[0].getString.toLowerAscii - if not KEYNAMES.contains(key) and not KEYSEQS.contains(key): - raiseInvalid("Unrecognized key: " & key) - if KEYMAP.hasKey(key): - if ORIGKEYMAP.hasKey(key): - KEYMAP[key] = ORIGKEYMAP[key] - else: - KEYMAP.excl(key) - - def.symbol("mapkey") do (i: In): - let vals = i.expect("'sym", "quot") - let key = vals[0].getString.toLowerAscii - var q = vals[1] - if not KEYNAMES.contains(key) and not KEYSEQS.contains(key): - raiseInvalid("Unrecognized key: " & key) - var ic = i.copy(i.filename) - KEYMAP[key] = proc (ed: var LineEditor) {.gcsafe.} = - ic.apply(q) - - def.symbol("newline") do (i: In): - echo "" - - def.symbol("notice") do (i: In): - let a = i.peek - notice $$a - - def.symbol("info") do (i: In): - let a = i.peek - info $$a - - def.symbol("error") do (i: In): - let a = i.peek - error $$a - - def.symbol("warn") do (i: In): - let a = i.peek - warn $$a - - def.symbol("debug") do (i: In): - let a = i.peek - debug $$a - - def.symbol("fatal") do (i: In): - let a = i.peek - fatal $$a - quit(100) - - def.symbol("notice!") do (i: In): - let a = i.pop - notice $$a - - def.symbol("info!") do (i: In): - let a = i.pop - info $$a - - def.symbol("error!") do (i: In): - let a = i.pop - error $$a - - def.symbol("warn!") do (i: In): - let a = i.pop - warn $$a - - def.symbol("debug!") do (i: In): - let a = i.pop - debug $$a - - def.symbol("fatal!") do (i: In): - let a = i.pop - fatal $$a - quit(100) - - def.symbol("column-print") do (i: In): - let vals = i.expect("int", "quot") - let n = vals[0] - let q = vals[1] - var c = 0 - for s in q.qVal: - c.inc - stdout.write $$s & spaces(max(0, 15 - ($$s).len)) - if c mod n.intVal == 0: - echo "" - echo "" - - def.symbol("getchr") do (i: In): - i.push getchr().newVal - - def.symbol("putchr") do (i: In): - let ch = i.expect("string") - if ch[0].getString.len != 1: - raiseInvalid("Symbol putch requires a string containing a single character.") - putchr(ch[0].getString[0].cint) - - def.symbol("password") do (i: In) {.gcsafe.}: - var ed = initEditor() - i.push ed.password("Enter Password: ").newVal - - def.symbol("ask") do (i: In) {.gcsafe.}: - var ed = initEditor() - let vals = i.expect("string") - let s = vals[0] - i.push ed.readLine(s.getString & ": ").newVal - - def.symbol("confirm") do (i: In) {.gcsafe.}: - var ed = initEditor() - let vals = i.expect("string") - let s = vals[0] - proc confirm(): bool = - let answer = ed.readLine(s.getString & " [yes/no]: ") - if answer.match("^y(es)?$", "i"): - return true - elif answer.match("^no?$", "i"): - return false - else: - stdout.write "Invalid answer. Please enter 'yes' or 'no': " - return confirm() - i.push confirm().newVal - - def.symbol("choose") do (i: In) {.gcsafe.}: - var ed = initEditor() - let vals = i.expect("'sym", "quot") - let s = vals[0] - var q = vals[1] - if q.qVal.len <= 0: - raiseInvalid("No choices to display") - stdout.writeLine(s.getString) - proc choose(): int = - var c = 0 - for item in q.qVal: - if not item.isQuotation or not item.qVal.len == 2 or not item.qVal[0].isString or not item.qVal[1].isQuotation: - raiseInvalid("Each item of the quotation must be a quotation containing a string and a quotation") - c.inc - echo "$1 - $2" % [$c, item.qVal[0].getString] - let answer = ed.readLine("Enter your choice ($1 - $2): " % ["1", $c]) - var choice: int - try: - choice = answer.parseInt - except: - choice = 0 - if choice <= 0 or choice > c: - echo "Invalid choice." - return choose() - else: - return choice - let choice = choose() - i.dequote(q.qVal[choice-1].qVal[1]) - - def.symbol("print") do (i: In): - let a = i.peek - a.print - - def.symbol("print!") do (i: In): - i.pop.print - - def.symbol("fread") do (i: In): - let vals = i.expect("string") - let file = vals[0].strVal - var contents = "" - if MINCOMPILED: - var compiledFile = strutils.replace(strutils.replace(file, "\\", "/"), "./", "") - if COMPILEDASSETS.hasKey(compiledFile): - contents = COMPILEDASSETS[compiledFile] - if contents == "": - contents = file.readFile - i.push newVal(contents) - - def.symbol("fwrite") do (i: In): - let vals = i.expect("string", "string") - let a = vals[0] - let b = vals[1] - a.strVal.writeFile(b.strVal) - - def.symbol("fappend") do (i: In): - let vals = i.expect("string", "string") - let a = vals[0] - let b = vals[1] - var f:File - discard f.open(a.strVal, fmAppend) - f.write(b.strVal) - f.close() - - def.finalize("io")
D lib/min_lang.nim

@@ -1,1071 +0,0 @@

-import - critbits, - strutils, - sequtils, - parseopt, - algorithm -when defined(mini): - import - rdstdin, - ../core/minilogger -else: - import - os, - json, - logging, - ../core/baseutils, - ../packages/niftylogger, - ../packages/nimline/nimline, - ../packages/nim-sgregex/sgregex -import - ../core/env, - ../core/meta, - ../core/parser, - ../core/value, - ../core/interpreter, - ../core/utils, - ../core/scope - -proc lang_module*(i: In) = - let def = i.scope - - when not defined(mini): - - def.symbol("from-json") do (i: In): - let vals = i.expect("string") - let s = vals[0] - i.push i.fromJson(s.getString.parseJson) - - def.symbol("to-json") do (i: In): - let vals = i.expect "a" - let q = vals[0] - i.push(($((i%q).pretty)).newVal) - - # Save/load symbols - - def.symbol("save-symbol") do (i: In) {.gcsafe.}: - let vals = i.expect("'sym") - let s = vals[0] - let sym = s.getString - let op = i.scope.getSymbol(sym) - if op.kind == minProcOp: - raiseInvalid("Symbol '$1' cannot be serialized." % sym) - let json = MINSYMBOLS.readFile.parseJson - json[sym] = i%op.val - MINSYMBOLS.writeFile(json.pretty) - - def.symbol("load-symbol") do (i: In): - let vals = i.expect("'sym") - let s = vals[0] - let sym = s.getString - let json = MINSYMBOLS.readFile.parseJson - if not json.hasKey(sym): - raiseUndefined("Symbol '$1' not found." % sym) - let val = i.fromJson(json[sym]) - i.scope.symbols[sym] = MinOperator(kind: minValOp, val: val, quotation: true) - - def.symbol("saved-symbols") do (i: In): - var q = newSeq[MinValue](0) - let json = MINSYMBOLS.readFile.parseJson - for k,v in json.pairs: - q.add k.newVal - i.push q.newVal - - def.symbol("remove-symbol") do (i: In): - let vals = i.expect("'sym") - let s = vals[0] - let sym = s.getString - var json = MINSYMBOLS.readFile.parseJson - if not json.hasKey(sym): - raiseUndefined("Symbol '$1' not found." % sym) - json.delete(sym) - MINSYMBOLS.writeFile(json.pretty) - - def.symbol("load") do (i: In): - let vals = i.expect("'sym") - let s = vals[0] - var file = s.getString - if not file.endsWith(".min"): - file = file & ".min" - info("[load] File: ", file) - let f = simplifyPath(i.filename, file) - if MINCOMPILED and COMPILEDMINFILES.hasKey(f): - COMPILEDMINFILES[f](i) - else: - if not f.fileExists: - raiseInvalid("File '$1' does not exist." % file) - i.load f - - def.symbol("require") do (i: In): - let vals = i.expect("'sym") - let s = vals[0] - var file = s.getString - if not file.endsWith(".min"): - file = file & ".min" - info("[require] File: ", file) - let f = simplifyPath(i.filename, file) - if MINCOMPILED and COMPILEDMINFILES.hasKey(f): - var i2 = i.copy(f) - i2.withScope(): - COMPILEDMINFILES[f](i2) - var mdl = newDict(i2.scope) - mdl.objType = "module" - for key, value in i2.scope.symbols.pairs: - mdl.scope.symbols[key] = value - i.push(mdl) - else: - if not f.fileExists: - raiseInvalid("File '$1' does not exist." % file) - i.push i.require(f) - - def.symbol("read") do (i: In): - let vals = i.expect("'sym") - let s = vals[0] - var file = s.getString - if not file.endsWith(".min"): - file = file & ".min" - info("[read] File: ", file) - if not file.fileExists: - raiseInvalid("File '$1' does not exist." % file) - i.push i.read file - - def.symbol("raw-args") do (i: In): - var args = newSeq[MinValue](0) - for par in commandLineParams(): - args.add par.newVal - i.push args.newVal - - def.symbol("with") do (i: In): - let vals = i.expect("dict", "quot") - var qscope = vals[0] - var qprog = vals[1] - i.withDictScope(qscope.scope): - for v in qprog.qVal: - i.push v - - def.symbol("publish") do (i: In): - let vals = i.expect("dict", "'sym") - let qscope = vals[0] - let str = vals[1] - let sym = str.getString - if qscope.scope.symbols.hasKey(sym) and qscope.scope.symbols[sym].sealed: - raiseUndefined("Attempting to redefine sealed symbol '$1'" % [sym]) - let scope = i.scope - info("[publish] Symbol: $2" % [sym]) - let op = proc(i: In) {.closure.} = - let origscope = i.scope - i.scope = scope - i.evaluating = true - i.pushSym sym - i.evaluating = false - i.scope = origscope - qscope.scope.symbols[sym] = MinOperator(kind: minProcOp, prc: op) - - ### End of symbols not present in minimin - - def.symbol("operator") do (i: In): - let vals = i.expect("quot"); - let q = vals[0] - if q.qVal.len != 4: - raiseInvalid("Invalid operator definition") - let tv = q.qVal[0] - if not tv.isSymbol or (tv.symVal != "symbol" and tv.symVal != "sigil"): - raiseInvalid("Incorrect operator type specified (it must be 'symbol' or 'sigil', found '$#')" % tv.symVal) - let t = tv.symVal - let nv = q.qVal[1] - if not nv.isSymbol: - raiseInvalid("Operator name must be a symbol") - let n = nv.symVal - when not defined(mini): - if not n.match(USER_SYMBOL_REGEX): - raiseInvalid("Operator name must not contain ivalid characters") - # Validate signature - let sv = q.qVal[2] - if not sv.isQuotation: - raiseInvalid("Signature must be a quotation") - elif sv.qVal.len == 0: - raiseInvalid("No signature specified") - elif sv.qVal.len == 1 and sv.qVal[0] != "==>".newSym: - raiseInvalid("Invalid signature") - elif sv.qVal.len mod 2 == 0: - raiseInvalid("Invalid signature") - var c = 0 - # Process signature - var inExpects= newSeq[string](0) - var inVars = newSeq[string](0) - var outExpects= newSeq[string](0) - var outVars = newSeq[string](0) - var o= false - for vv in sv.qVal: - if not vv.isSymbol: - raiseInvalid("Signature must be a quotation id symbols") - let v = vv.symVal - var check = c mod 2 == 0 - if o: - check = c mod 2 != 0 - if check: - if v == "==>": - o = true - elif not validType(v): - raiseInvalid("Invalid type specified in signature at position $#" % $(c+1)) - else: - if o: - outExpects.add v - else: - inExpects.add v - else: - if v[0] != ':': - raiseInvalid("No mapping symbol specified in signature at position $#" % $(c+1)) - else: - if o: - outVars.add v[1..v.len-1] - else: - inVars.add v[1..v.len-1] - c.inc() - if not o: - raiseInvalid("No output specified in signature") - # Process body - var bv = q.qVal[3] - if not bv.isQuotation: - raiseInvalid("Body must be a quotation") - inExpects.reverse - inVars.reverse - var p: MinOperatorProc = proc (i: In) = - var inVals = i.expect(inExpects) - i.withScope(): - # Inject variables for mapped inputs - for k in 0..inVars.len-1: - var iv = inVals[k] - if iv.isQuotation: - iv = @[iv].newVal - i.scope.symbols[inVars[k]] = MinOperator(kind: minValOp, sealed: false, val: iv, quotation: inVals[k].isQuotation) - # Inject variables for mapped outputs - for k in 0..outVars.len-1: - i.scope.symbols[outVars[k]] = MinOperator(kind: minValOp, sealed: false, val: newNull(), quotation: false) - # Actually execute the body of the operator - var endSnapshot: seq[MinValue] - var snapShot: seq[MinValue] - try: - snapshot = deepCopy(i.stack) - i.dequote bv - endSnapshot = i.stack - if endSnapshot != snapshot: - raiseInvalid("Operator '$#' is polluting the stack" % n) - except MinReturnException: - discard - # Validate output - for k in 0..outVars.len-1: - i.pushSym outVars[k] - let x = i.peek - let o = outExpects[k] - var r = false; - if o.contains("|"): - let types = o.split("|") - for ut in types: - if validate(x, ut): - r = true - break - else: - r = validate(x, o) - if not r: - discard i.pop - raiseInvalid("Invalid value for output symbol '$#'. Expected $#, found $#" % [outVars[k], o, $x]) - # Define symbol/sigil - if t == "symbol": - if i.scope.symbols.hasKey(n) and i.scope.symbols[n].sealed: - raiseUndefined("Attempting to redefine sealed symbol '$1'" % [n]) - i.scope.symbols[n] = MinOperator(kind: minProcOp, prc: p, sealed: false) - else: - if i.scope.sigils.hasKey(n) and i.scope.sigils[n].sealed: - raiseUndefined("Attempting to redefine sealed sigil '$1'" % [n]) - i.scope.sigils[n] = MinOperator(kind: minProcOp, prc: p, sealed: true) - - def.symbol("expect-empty-stack") do (i: In): - let l = i.stack.len - if l != 0: - raiseInvalid("Expected empty stack, found $# elements instead" % $l) - - def.symbol("exit") do (i: In): - let vals = i.expect("int") - quit(vals[0].intVal.int) - - def.symbol("puts") do (i: In): - let a = i.peek - echo $$a - - def.symbol("puts!") do (i: In): - echo $$i.pop - - def.symbol("gets") do (i: In) {.gcsafe.}: - when defined(mini): - i.push readLineFromStdin("").newVal - else: - var ed = initEditor() - i.push ed.readLine().newVal - - def.symbol("apply") do (i: In): - let vals = i.expect("quot|dict") - var prog = vals[0] - if prog.kind == minQuotation: - i.apply prog - else: - i.push i.applyDict(prog) - - def.symbol("symbols") do (i: In): - var q = newSeq[MinValue](0) - var scope = i.scope - while not scope.isNil: - for s in scope.symbols.keys: - q.add s.newVal - scope = scope.parent - i.push q.newVal - - def.symbol("defined?") do (i: In): - let vals = i.expect("'sym") - i.push(i.scope.hasSymbol(vals[0].getString).newVal) - - def.symbol("defined-sigil?") do (i: In): - let vals = i.expect("'sym") - i.push(i.scope.hasSigil(vals[0].getString).newVal) - - def.symbol("sigils") do (i: In): - var q = newSeq[MinValue](0) - var scope = i.scope - while not scope.isNil: - for s in scope.sigils.keys: - q.add s.newVal - scope = scope.parent - i.push q.newVal - - def.symbol("scope-symbols") do (i: In): - let vals = i.expect("dict") - let m = vals[0] - var q = newSeq[MinValue](0) - for s in m.scope.symbols.keys: - q.add s.newVal - i.push q.newVal - - def.symbol("scope-sigils") do (i: In): - let vals = i.expect("dict") - let m = vals[0] - var q = newSeq[MinValue](0) - for s in m.scope.sigils.keys: - q.add s.newVal - i.push q.newVal - - def.symbol("lite?") do (i: In): - i.push defined(lite).newVal - - def.symbol("mini?") do (i: In): - i.push defined(mini).newVal - - def.symbol("from-yaml") do (i: In): - let vals = i.expect("string") - let s = vals[0] - try: - var dict = newDict(i.scope) - let lines = s.strVal.split("\n") - for line in lines: - let pair = line.split(":") - if pair.len == 1 and pair[0].len == 0: - continue - i.dset(dict, pair[0].strip, pair[1].strip.newVal) - i.push(dict) - except: - raiseInvalid("Invalid/unsupported YAML object (only dictionaries with string values are supported)") - - def.symbol("to-yaml") do (i: In): - let vals = i.expect "a" - let a = vals[0] - let err = "YAML conversion is only supported from dictionaries with string values" - if a.kind != minDictionary: - raiseInvalid(err) - var yaml = "" - try: - for key in i.keys(a).qVal: - let value = i.dget(a, key) - if value.kind != minString: - raiseInvalid(err) - yaml &= "$1: $2\n" % [key.strVal, value.strVal] - i.push(yaml.strip.newVal) - except: - raiseInvalid(err) - - def.symbol("loglevel") do (i: In): - let vals = i.expect("'sym") - let s = vals[0] - var str = s.getString - when defined(mini): - echo "Log level: ", minilogger.setLogLevel(str) - else: - echo "Log level: ", niftylogger.setLogLevel(str) - - def.symbol("loglevel?") do (i: In): - when defined(mini): - i.push minilogger.getLogLevel().newVal - else: - i.push niftylogger.getLogLevel().newVal - - # Language constructs - - def.symbol("define") do (i: In): - let vals = i.expect("'sym", "a") - let sym = vals[0] - var q1 = vals[1] # existing (auto-quoted) - var symbol: string - var isQuot = true - if not q1.isQuotation: - q1 = @[q1].newVal - isQuot = false - symbol = sym.getString - when not defined(mini): - if not symbol.match USER_SYMBOL_REGEX: - raiseInvalid("Symbol identifier '$1' contains invalid characters." % symbol) - info "[define] $1 = $2" % [symbol, $q1] - if i.scope.symbols.hasKey(symbol) and i.scope.symbols[symbol].sealed: - raiseUndefined("Attempting to redefine sealed symbol '$1'" % [symbol]) - i.scope.symbols[symbol] = MinOperator(kind: minValOp, val: q1, sealed: false, quotation: isQuot) - - def.symbol("define-sigil") do (i: In): - let vals = i.expect("'sym", "a") - let sym = vals[0] - var q1 = vals[1] # existing (auto-quoted) - var sigil: string - var isQuot = true - if not q1.isQuotation: - q1 = @[q1].newVal - isQuot = false - sigil = sym.getString - when not defined(mini): - if not sigil.match USER_SYMBOL_REGEX: - raiseInvalid("Sigil identifier '$1' contains invalid characters." % sigil) - info "[sigil] $1 = $2" % [sigil, $q1] - if i.scope.sigils.hasKey(sigil) and i.scope.sigils[sigil].sealed: - raiseUndefined("Attempting to redefine sealed sigil '$1'" % [sigil]) - i.scope.sigils[sigil] = MinOperator(kind: minValOp, val: q1, sealed: true, quotation: isQuot) - - def.symbol("bind") do (i: In): - let vals = i.expect("'sym", "a") - let sym = vals[0] - var q1 = vals[1] # existing (auto-quoted) - var symbol: string - var isQuot = true - if not q1.isQuotation: - q1 = @[q1].newVal - isQuot = false - symbol = sym.getString - info "[bind] $1 = $2" % [symbol, $q1] - let res = i.scope.setSymbol(symbol, MinOperator(kind: minValOp, val: q1, quotation: isQuot)) - if not res: - raiseUndefined("Attempting to bind undefined symbol: " & symbol) - - def.symbol("delete") do (i: In): - let vals = i.expect("'sym") - let sym = vals[0] - let res = i.scope.delSymbol(sym.getString) - if not res: - raiseUndefined("Attempting to delete undefined symbol: " & sym.getString) - - def.symbol("delete-sigil") do (i: In): - let vals = i.expect("'sym") - let sym = vals[0] - let res = i.scope.delSigil(sym.getString) - if not res: - raiseUndefined("Attempting to delete undefined sigil: " & sym.getString) - - def.symbol("module") do (i: In): - let vals = i.expect("'sym", "dict") - let name = vals[0] - var code = vals[1] - code.objType = "module" - code.filename = i.filename - info("[module] $1 ($2 symbols)" % [name.getString, $code.scope.symbols.len]) - i.scope.symbols[name.getString] = MinOperator(kind: minValOp, val: code) - - def.symbol("scope") do (i: In): - var dict = newDict(i.scope.parent) - dict.objType = "module" - dict.filename = i.filename - dict.scope = i.scope - i.push dict - - def.symbol("type") do (i: In): - let vals = i.expect("a") - i.push vals[0].typeName.newVal - - def.symbol("import") do (i: In): - var vals = i.expect("'sym") - let rawName = vals[0] - var name: string - name = rawName.getString - var op = i.scope.getSymbol(name) - i.apply(op) - vals = i.expect("dict:module") - let mdl = vals[0] - info("[import] Importing: $1 ($2 symbols, $3 sigils)" % [name, $mdl.scope.symbols.len, $mdl.scope.sigils.len]) - for sym, val in mdl.scope.symbols.pairs: - if i.scope.symbols.hasKey(sym) and i.scope.symbols[sym].sealed: - raiseUndefined("Attempting to redefine sealed symbol '$1'" % [sym]) - i.debug "[import] $1" % [sym] - i.scope.symbols[sym] = val - for sig, val in mdl.scope.sigils.pairs: - if i.scope.sigils.hasKey(sig) and i.scope.sigils[sig].sealed: - raiseUndefined("Attempting to redefine sealed sigil '$1'" % [sig]) - i.debug "[import] $1" % [sig] - i.scope.sigils[sig] = val - - def.symbol("eval") do (i: In): - let vals = i.expect("string") - let s = vals[0] - i.eval s.strVal - - def.symbol("quit") do (i: In): - i.push 0.newVal - i.pushSym "exit" - - def.symbol("parse") do (i: In): - let vals = i.expect("string") - let s = vals[0] - i.push i.parse s.strVal - - def.symbol("source") do (i: In): - let vals = i.expect("'sym") - let s = vals[0] - let str = s.getString - let sym = i.scope.getSymbol(str) - if sym.kind == minValOp: - i.push sym.val - else: - raiseInvalid("No source available for native symbol '$1'." % str) - - def.symbol("call") do (i: In): - let vals = i.expect("'sym", "dict") - let symbol = vals[0] - let q = vals[1] - let s = symbol.getString - let origScope = i.scope - i.scope = q.scope - i.scope.parent = origScope - let sym = i.scope.getSymbol(s) - i.apply(sym) - i.scope = origScope - - def.symbol("invoke") do (i: In): - let vals = i.expect("'sym") - let s = vals[0].getString - let parts = s.split("/") - if parts.len < 2: - raiseInvalid("Dictionary identifier not specified") - i.pushSym parts[0] - for p in 0..parts.len-2: - let vals = i.expect("dict") - let mdl = vals[0] - let symId = parts[p+1] - i.push mdl - i.push symId.newVal - i.pushSym "call" - - def.symbol("set-type") do (i: In): - let vals = i.expect("'sym", "dict") - let symbol = vals[0] - var d = vals[1] - d.objType = symbol.getString - i.push d - - def.symbol("raise") do (i: In): - let vals = i.expect("dict") - let err = vals[0] - if err.dhas("error".newVal) and err.dhas("message".newVal): - raiseRuntime("($1) $2" % [i.dget(err, "error".newVal).getString, i.dget(err, "message").getString], err) - else: - raiseInvalid("Invalid error dictionary") - - def.symbol("format-error") do (i: In): - let vals = i.expect("dict:error") - let err = vals[0] - if err.dhas("error".newVal) and err.dhas("message".newVal): - var msg: string - var list = newSeq[MinValue]() - list.add i.dget(err, "message") - if err.dhas("symbol"): - list.add i.dget(err, "symbol") - if err.dhas("filename"): - list.add i.dget(err, "filename") - if err.dhas("line"): - list.add i.dget(err, "line") - if err.dhas("column"): - list.add i.dget(err, "column") - if list.len <= 3: - msg = "$1" % $$list[0] - else: - msg = "$3($4,$5) `$2`: $1" % [$$list[0], $$list[1], $$list[2], $$list[3], $$list[4]] - i.push msg.newVal - else: - raiseInvalid("Invalid error dictionary") - - def.symbol("try") do (i: In): - let vals = i.expect("quot") - let prog = vals[0] - if prog.qVal.len == 0: - raiseInvalid("Quotation must contain at least one element") - var code = prog.qVal[0] - var final, catch: MinValue - var hasFinally = false - var hasCatch = false - if prog.qVal.len > 1: - catch = prog.qVal[1] - hasCatch = true - if prog.qVal.len > 2: - final = prog.qVal[2] - hasFinally = true - if (not code.isQuotation) or (hasCatch and not catch.isQuotation) or (hasFinally and not final.isQuotation): - raiseInvalid("Quotation must contain at least one quotation") - try: - i.dequote(code) - except MinRuntimeError: - if not hasCatch: - return - let e = (MinRuntimeError)getCurrentException() - i.push e.data - i.dequote(catch) - except: - if not hasCatch: - return - let e = getCurrentException() - var res = newDict(i.scope) - var err = $e.name - let col = err.find(":") - if col >= 0: - err = err[0..col-1] - res.objType = "error" - i.dset(res, "error", err.newVal) - i.dset(res, "message", e.msg.newVal) - if i.currSym.getString != "": # TODO investigate when this happens - i.dset(res, "symbol", i.currSym) - i.dset(res, "filename", i.currSym.filename.newVal) - i.dset(res, "line", i.currSym.line.newVal) - i.dset(res, "column", i.currSym.column.newVal) - i.push res - i.dequote(catch) - finally: - if hasFinally: - i.dequote(final) - - def.symbol("quote") do (i: In): - let vals = i.expect("a") - let a = vals[0] - i.push @[a].newVal - - def.symbol("dequote") do (i: In): - let vals = i.expect("quot") - var q = vals[0] - i.dequote(q) - - def.symbol("tap") do (i: In): - let vals = i.expect("quot", "a") - let programs = vals[0] - var a = vals[1] - for program in programs.qVal: - var p = program - i.push(a) - i.dequote(p) - a = i.pop - i.push(a) - - def.symbol("tap!") do (i: In): - let vals = i.expect("quot", "a") - let programs = vals[0] - var a = vals[1] - for program in programs.qVal: - var p = program - i.push(a) - i.dequote(p) - a = i.pop - - # Conditionals - - def.symbol("if") do (i: In): - let vals = i.expect("quot", "quot", "quot") - var fpath = vals[0] - var tpath = vals[1] - var check = vals[2] - var stack = i.stack - i.dequote(check) - let res = i.pop - i.stack = stack - if not res.isBool: - raiseInvalid("Result of check is not a boolean value") - if res.boolVal == true: - i.dequote(tpath) - else: - i.dequote(fpath) - - def.symbol("when") do (i: In): - let vals = i.expect("quot", "quot") - var tpath = vals[0] - var check = vals[1] - var stack = i.stack - i.dequote(check) - let res = i.pop - i.stack = stack - if not res.isBool: - raiseInvalid("Result of check is not a boolean value") - if res.boolVal == true: - i.dequote(tpath) - - def.symbol("unless") do (i: In): - let vals = i.expect("quot", "quot") - var tpath = vals[0] - var check = vals[1] - var stack = i.stack - i.dequote(check) - let res = i.pop - i.stack = stack - if not res.isBool: - raiseInvalid("Result of check is not a boolean value") - if res.boolVal == false: - i.dequote(tpath) - - # 4 ( - # ((> 3) ("Greater than 3" put!)) - # ((< 3) ("Smaller than 3" put!)) - # ((true) ("Exactly 3" put!)) - # ) case - def.symbol("case") do (i: In): - let vals = i.expect("quot") - var cases = vals[0] - if cases.qVal.len == 0: - raiseInvalid("Empty case operator") - var k = 0 - let stack = i.stack - for c in cases.qVal: - i.stack = stack - if not c.isQuotation: - raiseInvalid("A quotation of quotations is required") - k.inc - if c.qVal.len != 2 or not c.qVal[0].isQuotation or not c.qVal[1].isQuotation: - raiseInvalid("Inner quotations in case operator must contain two quotations") - var q = c.qVal[0] - i.dequote(q) - let res = i.pop - if not res.isBool(): - raiseInvalid("Result of case #$1 is not a boolean value" % $k) - if res.boolVal == true: - var t = c.qVal[1] - i.dequote(t) - break - - # Loops - - def.symbol("foreach") do (i: In): - let vals = i.expect("quot", "quot") - var prog = vals[0] - var list = vals[1] - for litem in list.qVal: - i.push litem - i.dequote(prog) - - def.symbol("times") do (i: In): - let vals = i.expect("int", "quot") - var t = vals[0] - var prog = vals[1] - if t.intVal < 1: - raiseInvalid("A non-zero natural number is required") - for c in 1..t.intVal: - i.dequote(prog) - - def.symbol("while") do (i: In): - let vals = i.expect("quot", "quot") - var d = vals[0] - var b = vals[1] - for e in b.qVal: - i.push e - i.dequote(b) - var check = i.pop - while check.isBool and check.boolVal == true: - i.dequote(d) - i.dequote(b) - check = i.pop - discard i.pop - - # Other - - def.symbol("linrec") do (i: In): - let vals = i.expect("quot", "quot", "quot", "quot") - var r2 = vals[0] - var r1 = vals[1] - var t = vals[2] - var p = vals[3] - proc linrec(i: In, p, t, r1, r2: var MinValue) = - i.dequote(p) - var check = i.pop - if check.isBool and check.boolVal == true: - i.dequote(t) - else: - i.dequote(r1) - i.linrec(p, t, r1, r2) - i.dequote(r2) - i.linrec(p, t, r1, r2) - - def.symbol("version") do (i: In): - i.push pkgVersion.newVal - - def.symbol("seal") do (i: In): - let vals = i.expect("'sym") - let sym = vals[0].getString - var s = i.scope.getSymbol(sym) - s.sealed = true - i.scope.setSymbol(sym, s) - - def.symbol("seal-sigil") do (i: In): - let vals = i.expect("'sym") - let sym = vals[0].getString - var s = i.scope.getSigil(sym) - s.sealed = true - i.scope.setSigil(sym, s) - - def.symbol("unseal") do (i: In): - let vals = i.expect("'sym") - let sym = vals[0].getString - var s = i.scope.getSymbol(sym) - s.sealed = false - i.scope.setSymbol(sym, s, true) - - def.symbol("unseal-sigil") do (i: In): - let vals = i.expect("'sym") - let sym = vals[0].getString - var s = i.scope.getSigil(sym) - when not defined(mini): - if not sym.match USER_SYMBOL_REGEX: - # Prevent accidentally unsealing system sigils - # Not that they can redefined, but still - raiseInvalid("Attempting to unseal system sigil: " & sym) - s.sealed = false - i.scope.setSigil(sym, s, true) - - def.symbol("quote-bind") do (i: In): - let vals = i.expect("string", "a") - let s = vals[0] - let m = vals[1] - i.push @[m].newVal - i.push s - i.pushSym "bind" - - def.symbol("quote-define") do (i: In): - let vals = i.expect("string", "a") - let s = vals[0] - let m = vals[1] - i.push @[m].newVal - i.push s - i.pushSym "define" - - - def.symbol("args") do (i: In): - var args = newSeq[MinValue](0) - for kind, key, val in getopt(): - case kind: - of cmdArgument: - args.add key.newVal - else: - discard - i.push args.newVal - - def.symbol("opts") do (i: In): - var opts = newDict(i.scope) - for kind, key, val in getopt(): - case kind: - of cmdLongOption, cmdShortOption: - if val == "": - opts = i.dset(opts, key.newVal, true.newVal) - else: - opts = i.dset(opts, key.newVal, val.newVal) - else: - discard - i.push opts - - def.symbol("expect") do (i: In): - var q: MinValue - i.reqQuotationOfSymbols q - i.push(i.expect(q.qVal.mapIt(it.getString())).reversed.newVal) - - def.symbol("reverse-expect-dequote") do (i: In): - var q: MinValue - i.reqQuotationOfSymbols q - var req = i.expect(q.qVal.reversed.mapIt(it.getString())).newVal - i.dequote(req) - - def.symbol("infix-dequote") do (i: In): - let vals = i.expect("quot") - let q = vals[0] - proc infix(i: In, q: MinValue): MinValue = - var ops = newSeq[MinValue](0) - var res = newSeq[MinValue](0).newVal - for x in q.qVal: - if x.isSymbol: - ops.add x - else: - if x.isQuotation: - res.qVal.add i.infix(x) - else: - res.qVal.add x - if ops.len > 0: - res.qVal.add ops.pop - i.dequote(res) - res = newSeq[MinValue](0).newVal - return i.pop - i.push i.infix(q) - - def.symbol("prefix-dequote") do (i: In): - let vals = i.expect("quot") - var q = vals[0] - q.qVal.reverse - i.dequote(q) - - def.symbol("compiled?") do (i: In): - i.push MINCOMPILED.newVal - - # Converters - - def.symbol("string") do (i: In): - let s = i.pop - i.push(($$s).newVal) - - def.symbol("bool") do (i: In): - let v = i.pop - let strcheck = (v.isString and (v.getString == "false" or v.getString == "")) - let intcheck = v.isInt and v.intVal == 0 - let floatcheck = v.isFloat and v.floatVal == 0 - let boolcheck = v.isBool and v.boolVal == false - let quotcheck = v.isQuotation and v.qVal.len == 0 - if v.isNull or strcheck or intcheck or floatcheck or boolcheck or quotcheck: - i.push false.newVal - else: - i.push true.newVal - - def.symbol("int") do (i: In): - let s = i.pop - if s.isString: - i.push s.getString.parseInt.newVal - elif s.isNull: - i.push 0.int.newVal - elif s.isFloat: - i.push s.floatVal.int.newVal - elif s.isInt: - i.push s - elif s.isBool: - if s.boolVal == true: - i.push 1.int.newVal - else: - i.push 0.int.newVal - else: - raiseInvalid("Cannot convert a quotation to an integer.") - - def.symbol("float") do (i: In): - let s = i.pop - if s.isString: - i.push s.getString.parseFloat.newVal - elif s.isNull: - i.push 0.int.newVal - elif s.isInt: - i.push s.intVal.float.newVal - elif s.isFloat: - i.push s - elif s.isBool: - if s.boolVal == true: - i.push 1.float.newVal - else: - i.push 0.float.newVal - else: - raiseInvalid("Cannot convert a quotation to float.") - - def.symbol("prompt") do (i: In): - when defined(mini): - i.push "$ ".newVal - else: - i.eval(""""[$1]\n$$ " (.) => %""") - - # Sigils - - def.sigil("'") do (i: In): - let vals = i.expect("string") - let s = vals[0] - i.push(@[i.newSym(s.strVal)].newVal) - - def.sigil(":") do (i: In): - i.pushSym("define") - - def.sigil("~") do (i: In): - i.pushSym("delete") - - def.sigil("@") do (i: In): - i.pushSym("bind") - - def.sigil("+") do (i: In): - i.pushSym("module") - - def.sigil("^") do (i: In): - i.pushSym("call") - - def.sigil("*") do (i: In): - i.pushSym("invoke") - - def.sigil(">") do (i: In): - i.pushSym("save-symbol") - - def.sigil("<") do (i: In): - i.pushSym("load-symbol") - - def.sigil("#") do (i: In): - i.pushSym("quote-bind") - - def.sigil("=") do (i: In): - i.pushSym("quote-define") - - # Shorthand symbol aliases - - def.symbol("#") do (i: In): - i.pushSym("quote-bind") - - def.symbol("=") do (i: In): - i.pushSym("quote-define") - - def.symbol("=-=") do (i: In): - i.pushSym("expect-empty-stack") - - def.symbol(":") do (i: In): - i.pushSym("define") - - def.symbol("@") do (i: In): - i.pushSym("bind") - - def.symbol("^") do (i: In): - i.pushSym("call") - - def.symbol("'") do (i: In): - i.pushSym("quote") - - def.symbol("->") do (i: In): - i.pushSym("dequote") - - def.symbol("--") do (i: In): - i.pushSym("reverse-expect-dequote") - - def.symbol("::") do (i: In): - i.pushSym("operator") - - def.symbol("=>") do (i: In): - i.pushSym("apply") - - def.symbol("==>") do (i: In): - discard # used within operator defs - - def.symbol("return") do (i: In): - discard # used within operator defs - - def.symbol(">>") do (i: In): - i.pushSym("prefix-dequote") - - def.symbol("><") do (i: In): - i.pushSym("infix-dequote") - - def.finalize("ROOT")
D lib/min_logic.nim

@@ -1,269 +0,0 @@

-import - math -import - ../core/parser, - ../core/value, - ../core/interpreter, - ../core/utils - -proc floatCompare(n1, n2: MinValue): bool = - let - a:float = if n1.kind != minFloat: n1.intVal.float else: n1.floatVal - b:float = if n2.kind != minFloat: n2.intVal.float else: n2.floatVal - if a.classify == fcNan and b.classify == fcNan: - return true - else: - const - FLOAT_MIN_NORMAL = 2e-1022 - FLOAT_MAX_VALUE = (2-2e-52)*2e1023 - epsilon = 0.00001 - let - absA = abs(a) - absB = abs(b) - diff = abs(a - b) - - if a == b: - return true - elif a == 0 or b == 0 or diff < FLOAT_MIN_NORMAL: - return diff < (epsilon * FLOAT_MIN_NORMAL) - else: - return diff / min((absA + absB), FLOAT_MAX_VALUE) < epsilon - -proc logic_module*(i: In)= - let def = i.define() - - def.symbol(">") do (i: In): - var n1, n2: MinValue - i.reqTwoNumbersOrStrings n2, n1 - 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) - else: - i.push newVal(n1.strVal > n2.strVal) - - def.symbol(">=") do (i: In): - var n1, n2: MinValue - i.reqTwoNumbersOrStrings n2, n1 - 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 or floatCompare(n1, n2)) - elif n1.isFloat and n2.isFloat: - i.push newVal(n1.floatVal > n2.floatVal or floatCompare(n1, n2)) - elif n1.isFloat and n2.isInt: - i.push newVal(n1.floatVal > n2.intVal.float or floatCompare(n1, n2)) - else: - i.push newVal(n1.strVal >= n2.strVal) - - def.symbol("<") do (i: In): - var n1, n2: MinValue - i.reqTwoNumbersOrStrings n1, n2 - 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) - else: - i.push newVal(n1.strVal > n2.strVal) - - def.symbol("<=") do (i: In): - var n1, n2: MinValue - i.reqTwoNumbersOrStrings n1, n2 - 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 or floatCompare(n1, n2)) - elif n1.isFloat and n2.isFloat: - i.push newVal(n1.floatVal > n2.floatVal or floatCompare(n1, n2)) - elif n1.isFloat and n2.isInt: - i.push newVal(n1.floatVal > n2.intVal.float or floatCompare(n1, n2)) - else: - i.push newVal(n1.strVal >= n2.strVal) - - def.symbol("==") do (i: In): - var n1, n2: MinValue - let vals = i.expect("a", "a") - n1 = vals[0] - n2 = vals[1] - if (n1.kind == minFloat or n2.kind == minFloat) and n1.isNumber and n2.isNumber: - i.push newVal(floatCompare(n1, n2)) - else: - i.push newVal(n1 == n2) - - def.symbol("!=") do (i: In): - var n1, n2: MinValue - let vals = i.expect("a", "a") - n1 = vals[0] - n2 = vals[1] - if (n1.kind == minFloat or n2.kind == minFloat) and n1.isNumber and n2.isNumber: - i.push newVal(not floatCompare(n1, n2)) - i.push newVal(not (n1 == n2)) - - def.symbol("not") do (i: In): - let vals = i.expect("bool") - let b = vals[0] - i.push newVal(not b.boolVal) - - def.symbol("and") do (i: In): - let vals = i.expect("bool", "bool") - let a = vals[0] - let b = vals[1] - i.push newVal(a.boolVal and b.boolVal) - - def.symbol("dequote-and") do (i: In): - let vals = i.expect("a", "a") - var a = vals[0] - var b = vals[1] - i.dequote(b) - let resB = i.pop - if (resB.isBool and resB.boolVal == false): - i.push(false.newVal) - else: - i.dequote(a) - let resA = i.pop - if not resA.isBool: - raiseInvalid("Result of first quotation is not a boolean value") - if not resB.isBool: - raiseInvalid("Result of second quotation is not a boolean value") - i.push newVal(resA.boolVal and resB.boolVal) - - def.symbol("expect-all") do (i: In): - let vals = i.expect("quot") - let q = vals[0] - var c = 0 - for v in q.qVal: - if not v.isQuotation: - raiseInvalid("A quotation of quotations is expected") - var vv = v - i.dequote vv - let r = i.pop - c.inc() - if not r.isBool: - raiseInvalid("Quotation #$# does not evaluate to a boolean value") - if not r.boolVal: - i.push r - return - i.push true.newVal - - def.symbol("or") do (i: In): - let vals = i.expect("bool", "bool") - let a = vals[0] - let b = vals[1] - i.push newVal(a.boolVal or b.boolVal) - - def.symbol("dequote-or") do (i: In): - let vals = i.expect("a", "a") - var a = vals[0] - var b = vals[1] - i.dequote(b) - let resB = i.pop - if (resB.isBool and resB.boolVal == true): - i.push(true.newVal) - else: - i.dequote(a) - let resA = i.pop - if not resA.isBool: - raiseInvalid("Result of first quotation is not a boolean value") - if resB.isBool: - raiseInvalid("Result of second quotation is not a boolean value") - i.push newVal(resA.boolVal and resB.boolVal) - - def.symbol("expect-any") do (i: In): - let vals = i.expect("quot") - let q = vals[0] - var c = 0 - for v in q.qVal: - if not v.isQuotation: - raiseInvalid("A quotation of quotations is expected") - var vv = v - i.dequote vv - let r = i.pop - c.inc() - if not r.isBool: - raiseInvalid("Quotation #$# does not evaluate to a boolean value") - if r.boolVal: - i.push r - return - i.push false.newVal - - def.symbol("xor") do (i: In): - let vals = i.expect("bool", "bool") - let a = vals[0] - let b = vals[1] - i.push newVal(a.boolVal xor b.boolVal) - - def.symbol("string?") do (i: In): - if i.pop.kind == minString: - i.push true.newVal - else: - i.push false.newVal - - def.symbol("integer?") do (i: In): - if i.pop.kind == minInt: - i.push true.newVal - else: - i.push false.newVal - - def.symbol("float?") do (i: In): - if i.pop.kind == minFloat: - i.push true.newVal - else: - i.push false.newVal - - def.symbol("null?") do (i: In): - if i.pop.kind == minNull: - i.push true.newVal - else: - i.push false.newVal - - def.symbol("number?") do (i: In): - let a = i.pop - if a.kind == minFloat or a.kind == minInt: - i.push true.newVal - else: - i.push false.newVal - - def.symbol("boolean?") do (i: In): - if i.pop.kind == minBool: - i.push true.newVal - else: - i.push false.newVal - - def.symbol("quotation?") do (i: In): - if i.pop.kind == minQuotation: - i.push true.newVal - else: - i.push false.newVal - - def.symbol("dictionary?") do (i: In): - if i.pop.isDictionary: - i.push true.newVal - else: - i.push false.newVal - - def.symbol("type?") do (i: In): - let vals = i.expect("'sym", "a") - if vals[1].isTypedDictionary(vals[0].getString): - i.push true.newVal - else: - i.push (vals[1].typename == vals[0].getString).newVal - - def.symbol("&&") do (i: In): - i.pushSym("expect-all") - - def.symbol("||") do (i: In): - i.pushSym("expect-any") - - def.finalize("logic")
D lib/min_math.nim

@@ -1,110 +0,0 @@

-import - math, - strformat, - strutils -import - ../core/parser, - ../core/value, - ../core/interpreter, - ../core/utils - -proc math_module*(i: In)= - - let def = i.define() - - def.symbol("floor") do (i: In): - let vals = i.expect("num") - i.push vals[0].getFloat.floor.newVal - - def.symbol("ceil") do (i: In): - let vals = i.expect("num") - i.push vals[0].getFloat.ceil.newVal - - def.symbol("trunc") do (i: In): - let vals = i.expect("num") - i.push vals[0].getFloat.trunc.newVal - - def.symbol("round") do (i: In): - let vals = i.expect("int", "num") - let places = vals[0].intVal.int - let n = vals[1].getFloat - var res = "" - formatValue(res, n, "." & $places & "f") - i.push parseFloat(res).newVal - - def.symbol("e") do (i: In): - i.push E.newVal - - def.symbol("pi") do (i: In): - i.push PI.newVal - - def.symbol("tau") do (i: In): - i.push TAU.newVal - - def.symbol("ln") do (i: In): - let vals = i.expect("num") - i.push vals[0].getFloat.ln.newVal - - def.symbol("log2") do (i: In): - let vals = i.expect("num") - i.push vals[0].getFloat.log2.newVal - - def.symbol("log10") do (i: In): - let vals = i.expect("num") - i.push vals[0].getFloat.log10.newVal - - def.symbol("pow") do (i: In): - let vals = i.expect("num", "num") - let y = vals[0].getFloat - let x = vals[1].getFloat - i.push x.pow(y).newVal - - def.symbol("sqrt") do (i: In): - let vals = i.expect("num") - i.push vals[0].getFloat.sqrt.newVal - - def.symbol("sin") do (i: In): - let vals = i.expect("num") - i.push vals[0].getFloat.sin.newVal - - def.symbol("cos") do (i: In): - let vals = i.expect("num") - i.push vals[0].getFloat.cos.newVal - - def.symbol("tan") do (i: In): - let vals = i.expect("num") - i.push vals[0].getFloat.tan.newVal - - def.symbol("sinh") do (i: In): - let vals = i.expect("num") - i.push vals[0].getFloat.sinh.newVal - - def.symbol("cosh") do (i: In): - let vals = i.expect("num") - i.push vals[0].getFloat.cosh.newVal - - def.symbol("tanh") do (i: In): - let vals = i.expect("num") - i.push vals[0].getFloat.tanh.newVal - - def.symbol("asin") do (i: In): - let vals = i.expect("num") - i.push vals[0].getFloat.arcsin.newVal - - def.symbol("acos") do (i: In): - let vals = i.expect("num") - i.push vals[0].getFloat.arccos.newVal - - def.symbol("atan") do (i: In): - let vals = i.expect("num") - i.push vals[0].getFloat.arctan.newVal - - def.symbol("d2r") do (i: In): - let vals = i.expect("num") - i.push vals[0].getFloat.degToRad.newVal - - def.symbol("r2g") do (i: In): - let vals = i.expect("num") - i.push vals[0].getFloat.radToDeg.newVal - - def.finalize("math")
D lib/min_num.nim

@@ -1,138 +0,0 @@

-import - random -import - ../core/parser, - ../core/value, - ../core/interpreter, - ../core/utils - -proc num_module*(i: In)= - - let def = i.define() - - def.symbol("nan") do (i: In): - i.push newVal(NaN) - - def.symbol("inf") do (i: In): - i.push newVal(Inf) - - def.symbol("-inf") do (i: In): - i.push newVal(NegInf) - - def.symbol("+") do (i: In): - let vals = i.expect("num", "num") - let a = vals[0] - let b = vals[1] - if a.isInt: - if b.isInt: - i.push newVal(a.intVal + b.intVal) - else: - i.push newVal(a.intVal.float + b.floatVal) - else: - if b.isFloat: - i.push newVal(a.floatVal + b.floatVal) - else: - i.push newVal(a.floatVal + b.intVal.float) - - def.symbol("-") do (i: In): - let vals = i.expect("num", "num") - let a = vals[0] - let b = vals[1] - if a.isInt: - if b.isInt: - i.push newVal(b.intVal - a.intVal) - else: - i.push newVal(b.floatVal - a.intVal.float) - else: - if b.isFloat: - i.push newVal(b.floatVal - a.floatVal) - else: - i.push newVal(b.intVal.float - a.floatVal) - - def.symbol("*") do (i: In): - let vals = i.expect("num", "num") - let a = vals[0] - let b = vals[1] - if a.isInt: - if b.isInt: - i.push newVal(a.intVal * b.intVal) - else: - i.push newVal(a.intVal.float * b.floatVal) - else: - if b.isFloat: - i.push newVal(a.floatVal * b.floatVal) - else: - i.push newVal(a.floatVal * b.intVal.float) - - def.symbol("/") do (i: In): - let vals = i.expect("num", "num") - let a = vals[0] - let b = vals[1] - if a.isInt: - if b.isInt: - i.push newVal(b.intVal.int / a.intVal.int) - else: - i.push newVal(b.floatVal / a.intVal.float) - else: - if b.isFloat: - i.push newVal(b.floatVal / a.floatVal) - else: - i.push newVal(b.intVal.float / a.floatVal) - - def.symbol("randomize") do (i: In): - randomize() - - def.symbol("random") do (i: In): - let vals = i.expect("int") - let n = vals[0] - i.push n.intVal.int.rand.newVal - - def.symbol("div") do (i: In): - let vals = i.expect("int", "int") - let b = vals[0] - let a = vals[1] - i.push(newVal(a.intVal div b.intVal)) - - def.symbol("mod") do (i: In): - let vals = i.expect("int", "int") - let b = vals[0] - let a = vals[1] - i.push(newVal(a.intVal mod b.intVal)) - - def.symbol("succ") do (i: In): - let vals = i.expect("int") - let n = vals[0] - i.push newVal(n.intVal + 1) - - def.symbol("pred") do (i: In): - let vals = i.expect("int") - let n = vals[0] - i.push newVal(n.intVal - 1) - - def.symbol("even?") do (i: In): - let vals = i.expect("int") - let n = vals[0] - i.push newVal(n.intVal mod 2 == 0) - - def.symbol("odd?") do (i: In): - let vals = i.expect("int") - let n = vals[0] - i.push newVal(n.intVal mod 2 != 0) - - def.symbol("sum") do (i: In): - var s: MinValue - i.reqQuotationOfNumbers s - var c = 0.float - var isInt = true - for n in s.qVal: - if n.isFloat: - isInt = false - c = + n.floatVal - else: - c = c + n.intVal.float - if isInt: - i.push c.int.newVal - else: - i.push c.newVal - - def.finalize("num")
M lib/min_seq.nimminpkg/lib/min_seq.nim

@@ -1,6 +1,8 @@

import tables, - algorithm + algorithm, + sets, + sequtils import ../core/parser, ../core/value,

@@ -10,6 +12,30 @@

proc seq_module*(i: In)= let def = i.define() + + def.symbol("intersection") do (i: In): + let vals = i.expect("quot", "quot") + let q1 = toHashSet(vals[0].qVal) + let q2 = toHashSet(vals[1].qVal) + i.push toSeq(items(q2.intersection(q1))).newVal + + def.symbol("union") do (i: In): + let vals = i.expect("quot", "quot") + let q1 = toHashSet(vals[0].qVal) + let q2 = toHashSet(vals[1].qVal) + i.push toSeq(items(q2.union(q1))).newVal + + def.symbol("difference") do (i: In): + let vals = i.expect("quot", "quot") + let q1 = toHashSet(vals[0].qVal) + let q2 = toHashSet(vals[1].qVal) + i.push toSeq(items(q2.difference(q1))).newVal + + def.symbol("symmetric-difference") do (i: In): + let vals = i.expect("quot", "quot") + let q1 = toHashSet(vals[0].qVal) + let q2 = toHashSet(vals[1].qVal) + i.push toSeq(items(q2.symmetricDifference(q1))).newVal def.symbol("concat") do (i: In): let vals = i.expect("quot", "quot")

@@ -176,6 +202,22 @@ var check = i.pop

if check.isBool and check.boolVal == true: res = true.newVal break + i.push res + + def.symbol("one?") do (i: In): + let vals = i.expect("quot", "quot") + var filter = vals[0] + let list = vals[1] + var res = false.newVal + for e in list.qVal: + i.push e + i.dequote(filter) + var check = i.pop + if check.isBool and check.boolVal == true: + if res == true.newVal: + res = false.newVal + break + res = true.newVal i.push res def.symbol("all?") do (i: In):
D lib/min_str.nim

@@ -1,272 +0,0 @@

-import - strutils, - sequtils -import - ../core/parser, - ../core/value, - ../core/interpreter, - ../core/baseutils, - ../core/utils - -when not defined(mini): - import - ../packages/nim-sgregex/sgregex, - uri - -proc str_module*(i: In) = - let def = i.define() - - def.symbol("interpolate") do (i: In): - let vals = i.expect("quot", "string") - var q = vals[0] - let s = vals[1] - var strings = newSeq[string](0) - for el in q.qVal: - strings.add $$el - let res = s.strVal % strings - i.push res.newVal - - def.symbol("apply-interpolate") do (i: In): - i.pushSym "apply" - i.pushSym "interpolate" - - def.symbol("strip") do (i: In): - let vals = i.expect("'sym") - let s = vals[0] - i.push s.getString.strip.newVal - - def.symbol("substr") do (i: In): - let vals = i.expect("int", "int", "'sym") - let length = vals[0].intVal - let start = vals[1].intVal - let s = vals[2].getString - let index = min(start+length-1, s.len-1) - i.push s[start..index].newVal - - def.symbol("split") do (i: In): - let vals = i.expect("'sym", "'sym") - let sep = vals[0].getString - let s = vals[1].getString - var q = newSeq[MinValue](0) - if (sep == ""): - for c in s: - q.add ($c).newVal - else: - for e in s.split(sep): - q.add e.newVal - i.push q.newVal - - def.symbol("join") do (i: In): - let vals = i.expect("'sym", "quot") - let s = vals[0] - let q = vals[1] - i.push q.qVal.mapIt($$it).join(s.getString).newVal - - def.symbol("length") do (i: In): - let vals = i.expect("'sym") - let s = vals[0] - i.push s.getString.len.newVal - - def.symbol("lowercase") do (i: In): - let vals = i.expect("'sym") - let s = vals[0] - i.push s.getString.toLowerAscii.newVal - - def.symbol("uppercase") do (i: In): - let vals = i.expect("'sym") - let s = vals[0] - i.push s.getString.toUpperAscii.newVal - - def.symbol("capitalize") do (i: In): - let vals = i.expect("'sym") - let s = vals[0] - i.push s.getString.capitalizeAscii.newVal - - def.symbol("ord") do (i: In): - let vals = i.expect("'sym") - let s = vals[0] - if s.getString.len != 1: - raiseInvalid("Symbol ord requires a string containing a single character.") - i.push s.getString[0].ord.newVal - - def.symbol("chr") do (i: In): - let vals = i.expect("int") - let n = vals[0] - let c = n.intVal.chr - i.push ($c).newVal - - def.symbol("titleize") do (i: In): - let vals = i.expect("'sym") - let s = vals[0] - i.push s.getString.split(" ").mapIt(it.capitalizeAscii).join(" ").newVal - - def.symbol("repeat") do (i: In): - let vals = i.expect("int", "string") - let n = vals[0] - let s = vals[1] - i.push s.getString.repeat(n.intVal).newVal - - def.symbol("indent") do (i: In): - let vals = i.expect("int", "string") - let n = vals[0] - let s = vals[1] - i.push s.getString.indent(n.intVal).newVal - - def.symbol("indexof") do (i: In): - let vals = i.expect("string", "string") - let reg = vals[0] - let str = vals[1] - let index = str.strVal.find(reg.strVal) - i.push index.newVal - - when not defined(mini): - - def.symbol("encode-url") do (i: In): - let vals = i.expect("string") - let s = vals[0].strVal - i.push s.encodeUrl.newVal - - def.symbol("decode-url") do (i: In): - let vals = i.expect("string") - let s = vals[0].strVal - i.push s.decodeUrl.newVal - - def.symbol("parse-url") do (i: In): - let vals = i.expect("string") - let s = vals[0].strVal - let u = s.parseUri - var d = newDict(i.scope) - i.dset(d, "scheme", u.scheme.newVal) - i.dset(d, "username", u.username.newVal) - i.dset(d, "password", u.password.newVal) - i.dset(d, "hostname", u.hostname.newVal) - i.dset(d, "port", u.port.newVal) - i.dset(d, "path", u.path.newVal) - i.dset(d, "query", u.query.newVal) - i.dset(d, "anchor", u.anchor.newVal) - i.push d - - def.symbol("search") do (i: In): - let vals = i.expect("string", "string") - let reg = vals[0] - let str = vals[1] - var matches = str.strVal.search(reg.strVal) - var res = newSeq[MinValue](matches.len) - for i in 0..matches.len-1: - res[i] = matches[i].newVal - i.push res.newVal - - def.symbol("match") do (i: In): - let vals = i.expect("string", "string") - let reg = vals[0] - let str = vals[1] - if str.strVal.match(reg.strVal): - i.push true.newVal - else: - i.push false.newVal - - def.symbol("replace") do (i: In): - let vals = i.expect("string", "string", "string") - let s_replace = vals[0] - let reg = vals[1] - let s_find = vals[2] - i.push sgregex.replace(s_find.strVal, reg.strVal, s_replace.strVal).newVal - - def.symbol("regex") do (i: In): - let vals = i.expect("string", "string") - let reg = vals[0] - let str = vals[1] - let results = str.strVal =~ reg.strVal - var res = newSeq[MinValue](0) - for r in results: - res.add(r.newVal) - i.push res.newVal - - def.symbol("semver?") do (i: In): - let vals = i.expect("string") - let v = vals[0].strVal - i.push v.match("^\\d+\\.\\d+\\.\\d+$").newVal - - def.symbol("from-semver") do (i: In): - let vals = i.expect("string") - let v = vals[0].strVal - let parts = v.search("^(\\d+)\\.(\\d+)\\.(\\d+)$") - if parts[0].len == 0: - raiseInvalid("String '$1' is not a basic semver" % v) - var d = newDict(i.scope) - i.dset(d, "major", parts[1].parseInt.newVal) - i.dset(d, "minor", parts[2].parseInt.newVal) - i.dset(d, "patch", parts[3].parseInt.newVal) - i.push d - - def.symbol("to-semver") do (i: In): - let vals = i.expect("dict") - let v = vals[0] - if not v.dhas("major") or not v.dhas("minor") or not v.dhas("patch"): - raiseInvalid("Dictionary does not contain major, minor and patch keys") - let major = i.dget(v, "major") - let minor = i.dget(v, "minor") - let patch = i.dget(v, "patch") - if major.kind != minInt or minor.kind != minInt or patch.kind != minInt: - raiseInvalid("major, minor, and patch values are not integers") - i.push(newVal("$#.$#.$#" % [$major, $minor, $patch])) - - def.symbol("semver-inc-major") do (i: In): - i.pushSym("from-semver") - var d = i.pop - let cv = i.dget(d, "major") - let v = cv.intVal + 1 - i.dset(d, "major", v.newVal) - i.dset(d, "minor", 0.newVal) - i.dset(d, "patch", 0.newVal) - i.push(d) - i.pushSym("to-semver") - - def.symbol("semver-inc-minor") do (i: In): - i.pushSym("from-semver") - var d = i.pop - let cv = i.dget(d, "minor") - let v = cv.intVal + 1 - i.dset(d, "minor", v.newVal) - i.dset(d, "patch", 0.newVal) - i.push(d) - i.pushSym("to-semver") - - def.symbol("semver-inc-patch") do (i: In): - i.pushSym("from-semver") - var d = i.pop - let cv = i.dget(d, "patch") - let v = cv.intVal + 1 - i.dset(d, "patch", v.newVal) - i.push(d) - i.pushSym("to-semver") - - def.symbol("escape") do (i: In): - let vals = i.expect("'sym") - let a = vals[0].getString - i.push a.escapeEx(true).newVal - - def.symbol("prefix") do (i: In): - let vals = i.expect("'sym", "'sym") - let a = vals[1].getString - let b = vals[0].getString - var s = b & a - i.push s.newVal - - def.symbol("suffix") do (i: In): - let vals = i.expect("'sym", "'sym") - let a = vals[1].getString - let b = vals[0].getString - var s = a & b - i.push s.newVal - - def.symbol("=~") do (i: In): - i.pushSym("regex") - - def.symbol("%") do (i: In): - i.pushSym("interpolate") - - def.symbol("=%") do (i: In): - i.pushSym("apply-interpolate") - - def.finalize("str")
D lib/min_sys.nim

@@ -1,250 +0,0 @@

-import - os, - osproc, - strutils, - critbits, - logging -when not defined(lite): - import sequtils -import - ../core/parser, - ../core/env, - ../core/value, - ../core/interpreter, - ../core/utils, - ../core/fileutils - -when not defined(lite): - import ../packages/nim-miniz/src/nim_miniz - -proc unix(s: string): string = - return s.replace("\\", "/") - -proc sys_module*(i: In)= - let def = i.define() - - def.symbol(".") do (i: In): - i.push newVal(getCurrentDir().unix) - - def.symbol("..") do (i: In): - i.push newVal(getCurrentDir().parentDir.unix) - - def.symbol("cd") do (i: In): - let vals = i.expect("'sym") - let f = vals[0].getString - i.pwd = joinPath(getCurrentDir(), f) - info("Current directory changed to: ", i.pwd) - f.setCurrentDir - - def.symbol("ls") do (i: In): - let vals = i.expect("'sym") - let a = vals[0] - var list = newSeq[MinValue](0) - for i in walkDir(a.getString): - list.add newVal(i.path.unix) - i.push list.newVal - - def.symbol("ls-r") do (i: In): - let vals = i.expect("'sym") - let a = vals[0] - var list = newSeq[MinValue](0) - for i in walkDirRec(a.getString): - list.add newVal(i.unix) - i.push list.newVal - - def.symbol("system") do (i: In): - let vals = i.expect("'sym") - let a = vals[0] - i.push execShellCmd(a.getString).newVal - - def.symbol("system!") do (i: In): - let vals = i.expect("'sym") - let a = vals[0] - discard execShellCmd(a.getString) - - def.symbol("run") do (i: In): - let vals = i.expect("'sym") - let cmd = vals[0] - let res = execCmdEx(cmd.getString) - var d = newDict(i.scope) - i.dset(d, "output", res.output.newVal) - i.dset(d, "code", res.exitCode.newVal) - i.push(d) - - def.symbol("get-env") do (i: In): - let vals = i.expect("'sym") - let a = vals[0] - i.push a.getString.getEnv.newVal - - def.symbol("put-env") do (i: In): - let vals = i.expect("'sym", "'sym") - let key = vals[0] - let value = vals[1] - key.getString.putEnv value.getString - - def.symbol("env?") do (i: In): - let vals = i.expect("'sym") - let s = vals[0] - i.push s.getString.existsEnv.newVal - - def.symbol("which") do (i: In): - let vals = i.expect("'sym") - let s = vals[0] - i.push s.getString.findExe.newVal - - def.symbol("os") do (i: In): - i.push hostOS.newVal - - def.symbol("cpu") do (i: In): - i.push hostCPU.newVal - - def.symbol("exists?") do (i: In): - let vals = i.expect("'sym") - let f = vals[0].getString - var found = false - if MINCOMPILED: - let cf = strutils.replace(strutils.replace(f, "\\", "/"), "./", "") - - found = COMPILEDASSETS.hasKey(cf) - if found: - i.push true.newVal - else: - i.push newVal(f.fileExists or f.dirExists) - - def.symbol("file?") do (i: In): - let vals = i.expect("'sym") - let f = vals[0].getString - var found = false - if MINCOMPILED: - let cf = strutils.replace(strutils.replace(f, "\\", "/"), "./", "") - - found = COMPILEDASSETS.hasKey(cf) - if found: - i.push true.newVal - else: - i.push f.fileExists.newVal - - def.symbol("dir?") do (i: In): - let vals = i.expect("'sym") - let f = vals[0] - i.push f.getString.dirExists.newVal - - def.symbol("rm") do (i: In): - let vals = i.expect("'sym") - let v = vals[0] - let f = v.getString - if f.fileExists: - f.removeFile - else: - raiseInvalid("File '$1' does not exist." % f) - - def.symbol("cp") do (i: In): - let vals = i.expect("'sym", "'sym") - let a = vals[0] - let b = vals[1] - let src = b.getString - var dest = a.getString - if src.dirExists: - copyDirWithPermissions src, dest - elif dest.dirExists: - if src.dirExists: - copyDirWithPermissions src, dest - else: - copyFileWithPermissions src, dest / src.extractFilename - else: - copyFileWithPermissions src, dest - - def.symbol("mv") do (i: In): - let vals = i.expect("'sym", "'sym") - let a = vals[0] - let b = vals[1] - let src = b.getString - var dest = a.getString - if dest.dirExists: - dest = dest / src.extractFilename - moveFile src, dest - - def.symbol("rmdir") do (i: In): - let vals = i.expect("'sym") - let f = vals[0] - f.getString.removeDir - - def.symbol("mkdir") do (i: In): - let vals = i.expect("'sym") - let f = vals[0] - f.getString.createDir - - def.symbol("sleep") do (i: In): - let vals = i.expect("int") - let ms = vals[0] - sleep ms.intVal.int - - def.symbol("chmod") do (i: In): - let vals = i.expect("int", "string") - let perms = vals[0] - let s = vals[1] - s.getString.setFilePermissions(perms.intVal.toFilePermissions) - - def.symbol("symlink?") do (i: In): - let vals = i.expect("'sym") - let s = vals[0] - i.push s.getString.symlinkExists.newVal - - def.symbol("symlink") do (i: In): - let vals = i.expect("'sym", "'sym") - let dest = vals[0] - let src = vals[1] - src.getString.createSymlink dest.getString - - def.symbol("hardlink") do (i: In): - let vals = i.expect("'sym", "'sym") - let dest = vals[0] - let src = vals[1] - src.getString.createHardlink dest.getString - - def.symbol("filename") do (i: In): - let vals = i.expect("'sym") - let f = vals[0] - i.push f.getString.extractFilename.unix.newVal - - def.symbol("dirname") do (i: In): - let vals = i.expect("'sym") - let f = vals[0] - i.push f.getString.parentDir.unix.newVal - - def.symbol("$") do (i: In): - i.pushSym("get-env") - - def.symbol("!") do (i: In): - i.pushSym("system") - - def.symbol("!!") do (i: In): - i.pushSym("system!") - - def.symbol("&") do (i: In): - i.pushSym("run") - - def.sigil("$") do (i: In): - i.pushSym("get-env") - - def.sigil("!") do (i: In): - i.pushSym("system") - - def.sigil("&") do (i: In): - i.pushSym("run") - - when not defined(lite): - def.symbol("unzip") do (i: In): - let vals = i.expect("'sym", "'sym") - let dir = vals[0] - let f = vals[1] - nim_miniz.unzip(f.getString, dir.getString) - - def.symbol("zip") do (i: In): - let vals = i.expect("'sym", "quot") - let file = vals[0] - let files = vals[1] - nim_miniz.zip(files.qVal.mapIt(it.getString), file.getString) - - def.finalize("sys") -
D lib/min_time.nim

@@ -1,78 +0,0 @@

-import - times -import - ../core/parser, - ../core/value, - ../core/interpreter, - ../core/utils - -proc time_module*(i: In)= - let def = i.define() - - def.symbol("timestamp") do (i: In): - i.push getTime().toUnix().newVal - - def.symbol("now") do (i: In): - i.push epochTime().newVal - - def.symbol("timeinfo") do (i: In): - let vals = i.expect("num") - let t = vals[0] - var time: Time - if t.kind == minInt: - time = t.intVal.fromUnix - else: - time = t.floatVal.int64.fromUnix - let tinfo = time.local - var info = newDict(i.scope) - info.objType = "timeinfo" - i.dset info, "year", tinfo.year.newVal - i.dset info, "month", (tinfo.month.int+1).newVal - i.dset info, "day", tinfo.monthday.newVal - i.dset info, "weekday", (tinfo.weekday.int+1).newVal - i.dset info, "yearday", tinfo.yearday.newVal - i.dset info, "hour", tinfo.hour.newVal - i.dset info, "minute", tinfo.minute.newVal - i.dset info, "second", tinfo.second.newVal - i.dset info, "dst", tinfo.isDST.newVal - i.dset info, "timezone", tinfo.utcOffset.newVal - i.push info - - def.symbol("to-timestamp") do (i: In): - let vals = i.expect("dict:timeinfo") - let dict = vals[0] - try: - let year = i.dget(dict, "year").intVal.int - let month = Month(i.dget(dict, "month").intVal.int - 1) - let monthday = i.dget(dict, "day").intVal.int - let hour: HourRange = i.dget(dict, "hour").intVal.int - let minute: MinuteRange = i.dget(dict, "minute").intVal.int - let second: SecondRange = i.dget(dict, "second").intVal.int - let timezone = i.dget(dict, "timezone").intVal.int - let tinfo = initDatetime(monthday, month, year, hour, minute, second, 00, utc()) - i.push (tinfo + timezone.seconds).toTime.toUnix.int.newVal - except: - raiseInvalid("An invalid timeinfo dictionary was provided.") - - def.symbol("datetime") do (i: In): - let vals = i.expect("num") - let t = vals[0] - var time: Time - if t.kind == minInt: - time = t.intVal.fromUnix - else: - time = t.floatVal.int64.fromUnix - i.push time.utc.format("yyyy-MM-dd'T'HH:mm:ss'Z'").newVal - - def.symbol("tformat") do (i: In): - let vals = i.expect("string", "num") - let s = vals[0] - let t = vals[1] - var time: Time - if t.kind == minInt: - time = t.intVal.fromUnix - else: - time = t.floatVal.int64.fromUnix - i.push time.local.format(s.getString).newVal - - def.finalize("time")
M min.nimmin.nim

@@ -1,51 +1,50 @@

import streams, - critbits, strutils, sequtils when defined(mini): import - core/minilogger + minpkg/core/minilogger else: import json, os, algorithm, logging, - packages/niftylogger + minpkg/packages/niftylogger import - core/baseutils, - core/env, - core/parser, - core/value, - core/scope, - core/interpreter, - core/utils + minpkg/core/baseutils, + minpkg/core/env, + minpkg/core/parser, + minpkg/core/value, + minpkg/core/scope, + minpkg/core/interpreter, + minpkg/core/utils import - lib/min_lang, - lib/min_stack, - lib/min_seq, - lib/min_dict, - lib/min_num, - lib/min_str, - lib/min_logic, - lib/min_time + minpkg/lib/min_lang, + minpkg/lib/min_stack, + minpkg/lib/min_seq, + minpkg/lib/min_dict, + minpkg/lib/min_num, + minpkg/lib/min_str, + minpkg/lib/min_logic, + minpkg/lib/min_time when not defined(mini): import - packages/nimline/nimline, - lib/min_sys, - lib/min_io, - lib/min_dstore, - lib/min_fs + minpkg/packages/nimline/nimline, + minpkg/lib/min_sys, + minpkg/lib/min_io, + minpkg/lib/min_dstore, + minpkg/lib/min_fs when not defined(lite) and not defined(mini): import - lib/min_http, - lib/min_net, - lib/min_crypto, - lib/min_math + minpkg/lib/min_http, + minpkg/lib/min_net, + minpkg/lib/min_crypto, + minpkg/lib/min_math export env,

@@ -282,7 +281,8 @@ when isMainModule:

import parseopt, - core/meta + critbits, + minpkg/core/meta var REPL = false var SIMPLEREPL = false
M min.nimblemin.nimble

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

import - core/meta + minpkg/core/meta # Package

@@ -9,7 +9,7 @@ description = pkgDescription

license = "MIT" bin = @[pkgName] installFiles = @["min.yml", "min.nim", "prelude.min"] -installDirs = @["vendor", "lib", "core", "packages"] +installDirs = @["minpkg"] # Dependencies
M min.vimmin.vim

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

" Vim syntax file " Language: min " Maintainer: Fabio Cevasco -" Last Change: 02 Jan 2021 -" Version: 0.27.1 +" Last Change: 05 Jan 2021 +" Version: 0.28.0 if exists("b:current_syntax") finish

@@ -11,7 +11,7 @@

setl iskeyword=@,36-39,+,-,*,.,/,:,~,!,48-57,60-65,94-95,192-255 setl iskeyword+=^ -syntax keyword minDefaultSymbol ! !! != # $ % & && ' * + - -- -> -inf . .. / : :: < <= = =% =-= == ==> => =~ > >< >= >> @ ROOT ^ accept acos aes all? and any? append apply apply-interpolate args asin ask atan atime bind bool boolean? call capitalize case cd ceil chmod choose chr clear clear-stack cleave close column-print compiled? concat confirm connect cons cos cosh cp cpu crypto ctime d2r datetime ddel ddup debug debug! decode decode-url define define-sigil defined-sigil? defined? delete delete-sigil dequote dequote-and dequote-or dget dhas? dict dictionary? dip dir? dirname div dkeys download dpairs dpick drop dsdelete dset dsget dshas? dsinit dspost dsput dsquery dsread dstore dswrite dtype dup dvalues e encode encode-url env? error error! escape eval even? exists? exit expect expect-all expect-any expect-empty-stack fappend fatal fatal! file? filename filter find first flatten float float? floor foreach format-error fperms fread from-json from-semver from-yaml fs fsize fstats ftype fwrite get get-content get-env get-stack getchr gets hardlink harvest hidden? http id if import in? indent indexof inf infix-dequote info info! insert int integer? interpolate invoke io join keep last length linrec listen lite? ln load load-symbol log10 log2 logic loglevel loglevel? lowercase ls ls-r map map-reduce mapkey match math md4 md5 mini? mkdir mod module mtime mv nan net newline nip not notice notice! now null? num number? odd? operator opts or ord os over parse parse-url partition password pi pick pop pow pred prefix prefix-dequote prepend print print! prompt publish put-env putchr puts puts! quit quotation? quote quote-bind quote-define quote-map r2g raise random randomize raw-args read recv recv-line reduce regex reject remove remove-symbol repeat replace request require rest return reverse reverse-expect-dequote rm rmdir rolldown rollup round run save-symbol saved-symbols scope scope-sigils scope-symbols seal seal-sigil search semver-inc-major semver-inc-minor semver-inc-patch semver? send seq set set-stack set-type sha1 sha224 sha256 sha384 sha512 shorten sigils sin sinh sip size sleep slice socket sort source split spread sqrt stack start-server stop-server str string string? strip substr succ suffix sum swap swons symbols symlink symlink? sys system system! take tan tanh tap tap! tau tformat time timeinfo times timestamp titleize to-json to-semver to-timestamp to-yaml trunc try type type? unless unmapkey unseal unseal-sigil unzip uppercase version warn warn! when which while with xor zip || +syntax keyword minDefaultSymbol ! !! != # $ % & && ' * + - -- -> -inf . .. / : :: < <= = =% =-= == ==> => =~ > >< >= >> @ ROOT ^ _demo _dev _github _min _one _shell accept acos aes all? and any? append apply apply-interpolate args asin ask atan atime bind bool boolean? call capitalize case cd ceil chmod choose chr clear clear-stack cleave close cmd column-print compiled? concat confirm connect cons cos cosh cp cpu crypto ctime d2r datetime ddel ddup debug debug! decode decode-url define define-sigil defined-sigil? defined? delete delete-sigil dequote dequote-and dequote-or devconfig dget dhas? dict dictionary? difference dip dir? dirname div dkeys download dpairs dpick drop dsdelete dset dsget dshas? dsinit dspost dsput dsquery dsread dstore dswrite dtype dup dvalues e encode encode-url env? error error! escape eval even? exists? exit expect expect-all expect-any expect-empty-stack fappend fatal fatal! file? filename filter find first flatten float float? floor foreach format-error fperms fread from-json from-semver from-yaml fs fsize fstats ftype fwrite get get-content get-env get-stack getchr gets hardlink harvest hidden? http id if import in? indent indexof inf infix-dequote info info! insert int integer? interpolate intersection invoke io join keep last length linrec listen lite? ln load load-symbol log10 log2 logic loglevel loglevel? lowercase ls ls-r map map-reduce mapkey match math md5 min_symbols mini? minrc mkdir mod module mtime mv nan net netstart netstop newline nip not notice notice! now null? num number? odd? one? operator opts or ord os over parse parse-url partition password pi pick pop pow pred prefix prefix-dequote prepend print print! prompt publish put-env putchr puts puts! quit quotation? quote quote-bind quote-define quote-map r2g raise random randomize raw-args read rebuild recv recv-line reduce regex reject remove remove-symbol repeat replace request require rest restart return reverse reverse-expect-dequote rm rmdir rolldown rollup round run save-symbol saved-symbols scope scope-sigils scope-symbols seal seal-sigil search semver-inc-major semver-inc-minor semver-inc-patch semver? send seq set set-stack set-type sha1 shorten sigils sin sinh sip size sleep slice socket sort source split spread sqrt stack start-server startshell stop-server stopshell str string string? strip substr succ suffix sum swap swons symbols symdir symlink symlink? symmetric-difference sys system system! take tan tanh tap tap! tau tformat time timeinfo times timestamp titleize to-json to-semver to-timestamp to-yaml tobin trunc try type type? union unless unmapkey unseal unseal-sigil unzip uppercase version vscode warn warn! when which while with xor zip || syntax match minDefaultSigil ;\<[/:@'~!?$%&=<>#^*#+]; contained syntax match minQuote ;\<['];
A minpkg/core/fileutils.nim

@@ -0,0 +1,100 @@

+import + os + +# Filetype and permissions + +proc filetype*(p: PathComponent): string = + case p + of pcFile: + return "file" + of pcLinkToFile: + return "filelink" + of pcDir: + return "dir" + of pcLinkToDir: + return "dirlink" + +proc unixPermissions*(s: set[FilePermission]): int = + result = 0 + for p in s: + case p: + of fpUserRead: + result += 400 + of fpUserWrite: + result += 200 + of fpUserExec: + result += 100 + of fpGroupRead: + result += 40 + of fpGroupWrite: + result += 20 + of fpGroupExec: + result += 10 + of fpOthersRead: + result += 4 + of fpOthersWrite: + result += 2 + of fpOthersExec: + result += 1 + +proc toFilePermissions*(p: BiggestInt): set[FilePermission] = + let user = ($p)[0].int + let group = ($p)[1].int + let others = ($p)[2].int + if user == 1: + result.incl fpUserExec + if user == 2: + result.incl fpUserWrite + if user == 3: + result.incl fpUserExec + result.incl fpUserWrite + if user == 4: + result.incl fpUserRead + if user == 5: + result.incl fpUserRead + result.incl fpUserExec + if user == 6: + result.incl fpUserRead + result.incl fpUserWrite + if user == 7: + result.incl fpUserRead + result.incl fpUserWrite + result.incl fpUserExec + if group == 1: + result.incl fpGroupExec + if group == 2: + result.incl fpGroupWrite + if group == 3: + result.incl fpGroupExec + result.incl fpGroupWrite + if group == 4: + result.incl fpGroupRead + if group == 5: + result.incl fpGroupRead + result.incl fpGroupExec + if group == 6: + result.incl fpGroupRead + result.incl fpGroupWrite + if group == 7: + result.incl fpGroupRead + result.incl fpGroupWrite + result.incl fpGroupExec + if others == 1: + result.incl fpOthersExec + if others == 2: + result.incl fpOthersWrite + if others == 3: + result.incl fpOthersExec + result.incl fpOthersWrite + if others == 4: + result.incl fpOthersRead + if others == 5: + result.incl fpOthersRead + result.incl fpOthersExec + if others == 6: + result.incl fpOthersRead + result.incl fpOthersWrite + if others == 7: + result.incl fpOthersRead + result.incl fpOthersWrite + result.incl fpOthersExec
A minpkg/core/interpreter.nim

@@ -0,0 +1,430 @@

+import + streams, + strutils, + sequtils, + os, + critbits, + algorithm +when defined(mini): + import + minilogger +else: + import + base64, + logging +import + baseutils, + value, + scope, + parser + +type + MinTrappedException* = ref object of CatchableError + MinReturnException* = ref object of CatchableError + MinRuntimeError* = ref object of CatchableError + data*: MinValue + +var ASSETPATH* {.threadvar.}: string +ASSETPATH = "" +var COMPILEDMINFILES* {.threadvar.}: CritBitTree[MinOperatorProc] +var COMPILEDASSETS* {.threadvar.}: CritBitTree[string] +var CACHEDMODULES* {.threadvar.}: CritBitTree[MinValue] + +const USER_SYMBOL_REGEX* = "^[a-zA-Z_][a-zA-Z0-9/!?+*._-]*$" + + +proc newSym*(i: In, s: string): MinValue = + return MinValue(kind: minSymbol, symVal: s, filename: i.currSym.filename, line: i.currSym.line, column: i.currSym.column, outerSym: i.currSym.symVal) + +proc copySym*(i: In, sym: MinValue): MinValue = + return MinValue(kind: minSymbol, symVal: sym.outerSym, filename: sym.filename, line: sym.line, column: sym.column, outerSym: "") + +proc raiseRuntime*(msg: string, data: MinValue) = + data.objType = "error" + raise MinRuntimeError(msg: msg, data: data) + +proc dump*(i: MinInterpreter): string = + var s = "" + for item in i.stack: + s = s & $item & " " + return s + +proc debug*(i: In, value: MinValue) = + debug("(" & i.dump & $value & ")") + +proc debug*(i: In, value: string) = + debug(value) + +template withScope*(i: In, res:ref MinScope, body: untyped): untyped = + let origScope = i.scope + try: + i.scope = newScopeRef(origScope) + body + res = i.scope + finally: + i.scope = origScope + +template withScope*(i: In, body: untyped): untyped = + let origScope = i.scope + try: + i.scope = newScopeRef(origScope) + body + finally: + i.scope = origScope + +template withDictScope*(i: In, s: ref MinScope, body: untyped): untyped = + let origScope = i.scope + try: + i.scope = s + body + finally: + i.scope = origScope + +proc newMinInterpreter*(filename = "input", pwd = ""): MinInterpreter = + var path = pwd + when not defined(mini): + if not pwd.isAbsolute: + path = joinPath(getCurrentDir(), pwd) + var stack:MinStack = newSeq[MinValue](0) + var trace:MinStack = newSeq[MinValue](0) + var stackcopy:MinStack = newSeq[MinValue](0) + var pr:MinParser + var scope = newScopeRef(nil) + var i:MinInterpreter = MinInterpreter( + filename: filename, + pwd: path, + parser: pr, + stack: stack, + trace: trace, + stackcopy: stackcopy, + scope: scope, + currSym: MinValue(column: 1, line: 1, kind: minSymbol, symVal: "") + ) + return i + +proc copy*(i: MinInterpreter, filename: string): MinInterpreter = + var path = filename + when not defined(mini): + if not filename.isAbsolute: + path = joinPath(getCurrentDir(), filename) + result = newMinInterpreter() + result.filename = filename + result.pwd = path.parentDirEx + result.stack = i.stack + result.trace = i.trace + result.stackcopy = i.stackcopy + result.scope = i.scope + result.currSym = MinValue(column: 1, line: 1, kind: minSymbol, symVal: "") + +proc formatError(sym: MinValue, message: string): string = + var name = sym.symVal + #if sym.parentSym != "": + # name = sym.parentSym + return "$1($2,$3) [$4]: $5" % [sym.filename, $sym.line, $sym.column, name, message] + +proc formatTrace(sym: MinValue): string = + var name = sym.symVal + #if sym.parentSym != "": + # name = sym.parentSym + if sym.filename == "": + return "<native> in symbol: $1" % [name] + else: + return "$1($2,$3) in symbol: $4" % [sym.filename, $sym.line, $sym.column, name] + +proc stackTrace*(i: In) = + var trace = i.trace + trace.reverse() + for sym in trace: + notice sym.formatTrace + +proc error(i: In, message: string) = + error(i.currSym.formatError(message)) + +proc open*(i: In, stream:Stream, filename: string) = + i.filename = filename + i.parser.open(stream, filename) + +proc close*(i: In) = + i.parser.close(); + +proc push*(i: In, val: MinValue) {.gcsafe, extern:"min_exported_symbol_$1".} + +proc call*(i: In, q: var MinValue): MinValue {.gcsafe, extern:"min_exported_symbol_$1".}= + var i2 = newMinInterpreter("<call>") + i2.trace = i.trace + i2.scope = i.scope + try: + i2.withScope(): + for v in q.qVal: + i2.push v + except: + i.currSym = i2.currSym + i.trace = i2.trace + raise + return i2.stack.newVal + +proc callValue*(i: In, v: var MinValue): MinValue {.gcsafe, extern:"min_exported_symbol_$1".}= + var i2 = newMinInterpreter("<call-value>") + i2.trace = i.trace + i2.scope = i.scope + try: + i2.withScope(): + i2.push v + except: + i.currSym = i2.currSym + i.trace = i2.trace + raise + return i2.stack[0] + +proc copyDict*(i: In, val: MinValue): MinValue {.gcsafe, extern:"min_exported_symbol_$1".}= + # Assuming val is a dictionary + var v = newDict(i.scope) + for item in val.scope.symbols.pairs: + v.scope.symbols[item.key] = item.val + for item in val.scope.sigils.pairs: + v.scope.sigils[item.key] = item.val + if val.objType != "": + v.objType = val.objType + if not val.obj.isNil: + v.obj = val.obj + return v + +proc applyDict*(i: In, val: MinValue): MinValue {.gcsafe, extern:"min_exported_symbol_$1".}= + # Assuming val is a dictionary + var v = i.copyDict(val) + for item in v.dVal.pairs: + var value = item.val.val + v.scope.symbols[item.key] = MinOperator(kind: minValOp, val: i.callValue(value), sealed: false) + return v + +proc apply*(i: In, op: MinOperator) {.gcsafe, extern:"min_exported_symbol_$1".}= + if op.kind == minProcOp: + op.prc(i) + else: + if op.val.kind == minQuotation: + var newscope = newScopeRef(i.scope) + i.withScope(newscope): + for e in op.val.qVal: + i.push e + else: + i.push(op.val) + +proc dequote*(i: In, q: var MinValue) = + if q.kind == minQuotation: + i.withScope(): + let qqval = deepCopy(q.qVal) + for v in q.qVal: + i.push v + q.qVal = qqval + else: + i.push(q) + +proc apply*(i: In, q: var MinValue) {.gcsafe, extern:"min_exported_symbol_$1_2".}= + var i2 = newMinInterpreter("<apply>") + i2.trace = i.trace + i2.scope = i.scope + try: + i2.withScope(): + for v in q.qVal: + if (v.kind == minQuotation): + var v2 = v + i2.dequote(v2) + else: + i2.push v + except: + i.currSym = i2.currSym + i.trace = i2.trace + raise + i.push i2.stack.newVal + +proc push*(i: In, val: MinValue) {.gcsafe, extern:"min_exported_symbol_$1".}= + if val.kind == minSymbol: + i.debug(val) + if not i.evaluating: + if val.outerSym != "": + i.currSym = i.copySym(val) + else: + i.currSym = val + i.trace.add val + let symbol = val.symVal + if symbol == "return": + raise MinReturnException(msg: "return symbol found") + if i.scope.hasSymbol(symbol): + i.apply i.scope.getSymbol(symbol) + else: + var qIndex = symbol.find('"') + if qIndex > 0: + let sigil = symbol[0..qIndex-1] + if not i.scope.hasSigil(sigil): + raiseUndefined("Undefined sigil '$1'"%sigil) + i.stack.add(MinValue(kind: minString, strVal: symbol[qIndex+1..symbol.len-2])) + i.apply(i.scope.getSigil(sigil)) + else: + let sigil = "" & symbol[0] + if symbol.len > 1 and i.scope.hasSigil(sigil): + i.stack.add(MinValue(kind: minString, strVal: symbol[1..symbol.len-1])) + i.apply(i.scope.getSigil(sigil)) + else: + raiseUndefined("Undefined symbol '$1'" % [val.symVal]) + discard i.trace.pop + elif val.kind == minDictionary and val.objType != "module": + # Dictionary must be copied every time they are interpreted, otherwise when they are used in cycles they reference each other. + var v = i.copyDict(val) + i.stack.add(v) + else: + i.stack.add(val) + +proc pop*(i: In): MinValue = + if i.stack.len > 0: + return i.stack.pop + else: + raiseEmptyStack() + +proc peek*(i: MinInterpreter): MinValue = + if i.stack.len > 0: + return i.stack[i.stack.len-1] + else: + raiseEmptyStack() + +template handleErrors*(i: In, body: untyped) = + try: + body + 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 interpret*(i: In, parseOnly=false): MinValue {.discardable, extern:"min_exported_symbol_$1".} = + var val: MinValue + var q: MinValue + if parseOnly: + q = newSeq[MinValue](0).newVal + while i.parser.token != tkEof: + if i.trace.len == 0: + i.stackcopy = i.stack + handleErrors(i) do: + val = i.parser.parseMinValue(i) + if parseOnly: + q.qVal.add val + else: + i.push val + if parseOnly: + return q + if i.stack.len > 0: + return i.stack[i.stack.len - 1] + +proc rawCompile*(i: In, indent = ""): seq[string] {.discardable, extern:"min_exported_symbol_$1".} = + while i.parser.token != tkEof: + if i.trace.len == 0: + i.stackcopy = i.stack + handleErrors(i) do: + result.add i.parser.compileMinValue(i, push = true, indent) + +proc compileFile*(i: In, main: bool): seq[string] {.discardable, extern:"min_exported_symbol_$1".} = + result = newSeq[string](0) + if not main: + result.add "COMPILEDMINFILES[\"$#\"] = proc(i: In) {.gcsafe.}=" % i.filename + result = result.concat(i.rawCompile(" ")) + else: + result = i.rawCompile("") + +proc initCompiledFile*(i: In, files: seq[string]): seq[string] {.discardable, extern:"min_exported_symbol_$1".} = + result = newSeq[string](0) + result.add "import min" + if files.len > 0 or (ASSETPATH != "" and not defined(mini)): + result.add "import critbits" + when not defined(mini): + if ASSETPATH != "": + result.add "import base64" + result.add "MINCOMPILED = true" + result.add "var i = newMinInterpreter(\"$#\")" % i.filename + result.add "i.stdLib()" + when not defined(mini): + if ASSETPATH != "": + for f in walkDirRec(ASSETPATH): + let file = simplifyPath(i.filename, f) + logging.notice("- Including: $#" % file) + let ef = file.readFile.encode + let asset = "COMPILEDASSETS[\"$#\"] = \"$#\".decode" % [file, ef] + result.add asset + +proc eval*(i: In, s: string, name="<eval>", parseOnly=false): MinValue {.discardable, extern:"min_exported_symbol_$1".}= + var i2 = i.copy(name) + i2.open(newStringStream(s), name) + discard i2.parser.getToken() + result = i2.interpret(parseOnly) + i.trace = i2.trace + i.stackcopy = i2.stackcopy + i.stack = i2.stack + i.scope = i2.scope + +proc load*(i: In, s: string, parseOnly=false): MinValue {.discardable, extern:"min_exported_symbol_$1".}= + var fileLines = newSeq[string](0) + var contents = "" + try: + fileLines = s.readFile().splitLines() + except: + fatal("Cannot read from file: " & s) + if fileLines[0].len >= 2 and fileLines[0][0..1] == "#!": + contents = ";;\n" & fileLines[1..fileLines.len-1].join("\n") + else: + contents = fileLines.join("\n") + var i2 = i.copy(s) + i2.open(newStringStream(contents), s) + discard i2.parser.getToken() + result = i2.interpret(parseOnly) + i.trace = i2.trace + i.stackcopy = i2.stackcopy + i.stack = i2.stack + i.scope = i2.scope + +proc require*(i: In, s: string, parseOnly=false): MinValue {.discardable, extern:"min_exported_symbol_$1".}= + if CACHEDMODULES.hasKey(s): + return CACHEDMODULES[s] + var fileLines = newSeq[string](0) + var contents = "" + try: + fileLines = s.readFile().splitLines() + except: + fatal("Cannot read from file: " & s) + if fileLines[0].len >= 2 and fileLines[0][0..1] == "#!": + contents = ";;\n" & fileLines[1..fileLines.len-1].join("\n") + else: + contents = fileLines.join("\n") + var i2 = i.copy(s) + let snapshot = deepCopy(i.stack) + i2.withScope: + i2.open(newStringStream(contents), s) + discard i2.parser.getToken() + discard i2.interpret(parseOnly) + if snapshot != i2.stack: + raiseInvalid("Module '$#' is polluting the stack" % s) + result = newDict(i2.scope) + result.objType = "module" + for key, value in i2.scope.symbols.pairs: + result.scope.symbols[key] = value + CACHEDMODULES[s] = result + +proc parse*(i: In, s: string, name="<parse>"): MinValue = + return i.eval(s, name, true) + +proc read*(i: In, s: string): MinValue = + return i.load(s, true) + +# Inherit file/line/column from current symbol +proc pushSym*(i: In, s: string) = + i.push MinValue(kind: minSymbol, symVal: s, filename: i.currSym.filename, line: i.currSym.line, column: i.currSym.column, outerSym: i.currSym.symVal) +
A minpkg/core/parser.nim

@@ -0,0 +1,866 @@

+# Adapted from: https://github.com/Araq/Nimrod/blob/v0.9.6/lib/pure/json.nim +import + lexbase, + strutils, + sequtils, + streams, + critbits, + baseutils + +import unicode except strip + +type + MinTokenKind* = enum + tkError, + tkEof, + tkString, + tkInt, + tkFloat, + tkBracketLe, + tkBracketRi, + tkBraceLe, + tkBraceRi, + tkSymbol, + tkNull, + tkTrue, + tkFalse + MinKind* = enum + minInt, + minFloat, + minQuotation, + minDictionary, + minString, + minSymbol, + minNull, + minBool + MinEventKind* = enum ## enumeration of all events that may occur when parsing + eMinError, ## an error ocurred during parsing + eMinEof, ## end of file reached + eMinString, ## a string literal + eMinInt, ## an integer literal + eMinFloat, ## a float literal + eMinQuotationStart, ## start of an array: the ``(`` token + eMinQuotationEnd, ## start of an array: the ``)`` token + eMinDictionaryStart, ## start of a dictionary: the ``{`` token + eMinDictionaryEnd ## start of a dictionary: the ``}`` token + MinParserError* = enum ## enumeration that lists all errors that can occur + errNone, ## no error + errInvalidToken, ## invalid token + errStringExpected, ## string expected + errBracketRiExpected, ## ``)`` expected + errBraceRiExpected, ## ``}`` expected + errQuoteExpected, ## ``"`` or ``'`` expected + errEOC_Expected, ## ``*/`` expected + errEofExpected, ## EOF expected + errExprExpected + MinParserState* = enum + stateEof, + stateStart, + stateQuotation, + stateDictionary, + stateExpectValue + MinParser* = object of BaseLexer + a*: string + token*: MinTokenKind + state*: seq[MinParserState] + kind*: MinEventKind + err*: MinParserError + filename*: string + MinValue* = ref MinValueObject + MinValueObject* {.acyclic, final.} = object + line*: int + column*: int + filename*: string + outerSym*: string + case kind*: MinKind + of minNull: discard + of minInt: intVal*: BiggestInt + of minFloat: floatVal*: BiggestFloat + of minDictionary: + scope*: ref MinScope + obj*: pointer + objType*: string + of minQuotation: + qVal*: seq[MinValue] + of minString: strVal*: string + of minSymbol: symVal*: string + of minBool: boolVal*: bool + MinScopeKind* = enum + minNativeScope, + minLangScope + MinScope* {.acyclic, shallow, final.} = object + parent*: ref MinScope + symbols*: CritBitTree[MinOperator] + sigils*: CritBitTree[MinOperator] + kind*: MinScopeKind + MinOperatorProc* = proc (i: In) {.closure, gcsafe.} + MinOperatorKind* = enum + minProcOp + minValOp + MinOperator* = object + sealed*: bool + case kind*: MinOperatorKind + of minProcOp: + prc*: MinOperatorProc + of minValOp: + quotation*: bool + val*: MinValue + MinStack* = seq[MinValue] + In* = var MinInterpreter + MinInterpreter* = object + stack*: MinStack + trace*: MinStack + stackcopy*: MinStack + pwd*: string + scope*: ref MinScope + parser*: MinParser + currSym*: MinValue + filename*: string + evaluating*: bool + MinParsingError* = ref object of ValueError + MinUndefinedError* = ref object of ValueError + MinEmptyStackError* = ref object of ValueError + MinInvalidError* = ref object of ValueError + MinOutOfBoundsError* = ref object of ValueError + +var CVARCOUNT = 0 + +# Helpers + +proc raiseInvalid*(msg: string) = + raise MinInvalidError(msg: msg) + +proc raiseUndefined*(msg: string) = + raise MinUndefinedError(msg: msg) + +proc raiseOutOfBounds*(msg: string) = + raise MinOutOfBoundsError(msg: msg) + +proc raiseEmptyStack*() = + raise MinEmptyStackError(msg: "Insufficient items on the stack") + +proc dVal*(v: MinValue): CritBitTree[MinOperator] {.inline, extern:"min_exported_symbol_$1".}= + if v.kind != minDictionary: + raiseInvalid("dVal - Dictionary expected, got " & $v.kind) + if v.scope.isNil: + return CritBitTree[MinOperator]() + return v.scope.symbols + +const + errorMessages: array[MinParserError, string] = [ + "no error", + "invalid token", + "string expected", + "')' expected", + "'}' expected", + "'\"' expected", + "'*/' expected", + "EOF expected", + "expression expected" + ] + tokToStr: array[MinTokenKind, string] = [ + "invalid token", + "EOF", + "string literal", + "int literal", + "float literal", + "(", + ")", + "{", + "}", + "symbol", + "null", + "true", + "false" + ] + +proc newScope*(parent: ref MinScope, kind = minLangScope): MinScope = + result = MinScope(parent: parent, kind: kind) + +proc newScopeRef*(parent: ref MinScope, kind = minLangScope): ref MinScope = + new(result) + result[] = newScope(parent, kind) + +proc open*(my: var MinParser, input: Stream, filename: string) = + lexbase.open(my, input) + my.filename = filename + my.state = @[stateStart] + my.kind = eMinError + my.a = "" + +proc close*(my: var MinParser) {.inline, extern:"min_exported_symbol_$1".}= + lexbase.close(my) + +proc getInt*(my: MinParser): int {.inline, extern:"min_exported_symbol_$1".}= + assert(my.kind == eMinInt) + return parseint(my.a) + +proc getFloat*(my: MinParser): float {.inline, extern:"min_exported_symbol_$1".}= + assert(my.kind == eMinFloat) + return parseFloat(my.a) + +proc kind*(my: MinParser): MinEventKind {.inline, extern:"min_exported_symbol_$1".}= + return my.kind + +proc getColumn*(my: MinParser): int {.inline, extern:"min_exported_symbol_$1".}= + result = getColNumber(my, my.bufpos) + +proc getLine*(my: MinParser): int {.inline, extern:"min_exported_symbol_$1".}= + result = my.lineNumber + +proc getFilename*(my: MinParser): string {.inline, extern:"min_exported_symbol_$1".}= + result = my.filename + +proc errorMsg*(my: MinParser, msg: string): string = + assert(my.kind == eMinError) + result = "$1 [l:$2, c:$3] ERROR - $4" % [ + my.filename, $getLine(my), $getColumn(my), msg] + +proc errorMsg*(my: MinParser): string = + assert(my.kind == eMinError) + result = errorMsg(my, errorMessages[my.err]) + +proc errorMsgExpected*(my: MinParser, e: string): string = + result = errorMsg(my, e & " expected") + +proc raiseParsing*(p: MinParser, msg: string) {.noinline, noreturn, extern:"min_exported_symbol_$1".}= + raise MinParsingError(msg: errorMsgExpected(p, msg)) + +proc raiseUndefined*(p:MinParser, msg: string) {.noinline, noreturn, extern:"min_exported_symbol_$1_2".}= + raise MinUndefinedError(msg: errorMsg(p, msg)) + +proc parseNumber(my: var MinParser) = + var pos = my.bufpos + var buf = my.buf + if buf[pos] == '-': + add(my.a, '-') + inc(pos) + if buf[pos] == '.': + add(my.a, "0.") + inc(pos) + else: + while buf[pos] in Digits: + add(my.a, buf[pos]) + inc(pos) + if buf[pos] == '.': + add(my.a, '.') + inc(pos) + # digits after the dot: + while buf[pos] in Digits: + add(my.a, buf[pos]) + inc(pos) + if buf[pos] in {'E', 'e'}: + add(my.a, buf[pos]) + inc(pos) + if buf[pos] in {'+', '-'}: + add(my.a, buf[pos]) + inc(pos) + while buf[pos] in Digits: + add(my.a, buf[pos]) + inc(pos) + my.bufpos = pos + +proc handleHexChar(c: char, x: var int): bool = + result = true # Success + case c + of '0'..'9': x = (x shl 4) or (ord(c) - ord('0')) + of 'a'..'f': x = (x shl 4) or (ord(c) - ord('a') + 10) + of 'A'..'F': x = (x shl 4) or (ord(c) - ord('A') + 10) + else: result = false # error + +proc parseString(my: var MinParser): MinTokenKind = + result = tkString + var pos = my.bufpos + 1 + var buf = my.buf + while true: + case buf[pos] + of '\0': + my.err = errQuoteExpected + result = tkError + break + of '"': + inc(pos) + break + of '\\': + case buf[pos+1] + of '\\', '"', '\'', '/': + add(my.a, buf[pos+1]) + inc(pos, 2) + of 'b': + add(my.a, '\b') + inc(pos, 2) + of 'f': + add(my.a, '\f') + inc(pos, 2) + of 'n': + add(my.a, '\L') + inc(pos, 2) + of 'r': + add(my.a, '\C') + inc(pos, 2) + of 't': + add(my.a, '\t') + inc(pos, 2) + of 'u': + inc(pos, 2) + var r: int + if handleHexChar(buf[pos], r): inc(pos) + if handleHexChar(buf[pos], r): inc(pos) + if handleHexChar(buf[pos], r): inc(pos) + if handleHexChar(buf[pos], r): inc(pos) + add(my.a, toUTF8(Rune(r))) + else: + # don't bother with the error + add(my.a, buf[pos]) + inc(pos) + of '\c': + pos = lexbase.handleCR(my, pos) + buf = my.buf + add(my.a, '\c') + of '\L': + pos = lexbase.handleLF(my, pos) + buf = my.buf + add(my.a, '\L') + else: + add(my.a, buf[pos]) + inc(pos) + my.bufpos = pos # store back + +proc parseSymbol(my: var MinParser): MinTokenKind = + result = tkSymbol + var pos = my.bufpos + var buf = my.buf + if not(buf[pos] in Whitespace): + while not(buf[pos] in WhiteSpace) and not(buf[pos] in ['\0', ')', '(', '}', '{']): + if buf[pos] == '"': + add(my.a, buf[pos]) + my.bufpos = pos + let r = parseString(my) + if r == tkError: + result = tkError + return + add(my.a, buf[pos]) + return + else: + add(my.a, buf[pos]) + inc(pos) + my.bufpos = pos + +proc skip(my: var MinParser) = + var pos = my.bufpos + var buf = my.buf + while true: + case buf[pos] + of ';': + # skip line comment: + inc(pos, 2) + while true: + case buf[pos] + of '\0': + break + of '\c': + pos = lexbase.handleCR(my, pos) + buf = my.buf + break + of '\L': + pos = lexbase.handleLF(my, pos) + buf = my.buf + break + else: + inc(pos) + of '/': + if buf[pos+1] == '/': + # skip line comment: + inc(pos, 2) + while true: + case buf[pos] + of '\0': + break + of '\c': + pos = lexbase.handleCR(my, pos) + buf = my.buf + break + of '\L': + pos = lexbase.handleLF(my, pos) + buf = my.buf + break + else: + inc(pos) + elif buf[pos+1] == '*': + # skip long comment: + inc(pos, 2) + while true: + case buf[pos] + of '\0': + my.err = errEOC_Expected + break + of '\c': + pos = lexbase.handleCR(my, pos) + buf = my.buf + of '\L': + pos = lexbase.handleLF(my, pos) + buf = my.buf + of '*': + inc(pos) + if buf[pos] == '/': + inc(pos) + break + else: + inc(pos) + else: + break + of ' ', '\t': + inc(pos) + of '\c': + pos = lexbase.handleCR(my, pos) + buf = my.buf + of '\L': + pos = lexbase.handleLF(my, pos) + buf = my.buf + else: + break + my.bufpos = pos + +proc getToken*(my: var MinParser): MinTokenKind = + setLen(my.a, 0) + skip(my) + case my.buf[my.bufpos] + of '-', '.': + if my.bufpos+1 <= my.buf.len and my.buf[my.bufpos+1] in '0'..'9': + parseNumber(my) + if {'.', 'e', 'E'} in my.a: + result = tkFloat + else: + result = tkInt + else: + result = parseSymbol(my) + of '0'..'9': + parseNumber(my) + if {'.', 'e', 'E'} in my.a: + result = tkFloat + else: + result = tkInt + of '"': + result = parseString(my) + of '(': + inc(my.bufpos) + result = tkBracketLe + of ')': + inc(my.bufpos) + result = tkBracketRi + of '{': + inc(my.bufpos) + result = tkBraceLe + of '}': + inc(my.bufpos) + result = tkBraceRi + of '\0': + result = tkEof + else: + result = parseSymbol(my) + case my.a + of "null": result = tkNull + of "true": result = tkTrue + of "false": result = tkFalse + else: + discard + my.token = result + + +proc next*(my: var MinParser) = + var tk = getToken(my) + var i = my.state.len-1 + case my.state[i] + of stateEof: + if tk == tkEof: + my.kind = eMinEof + else: + my.kind = eMinError + my.err = errEofExpected + of stateStart: + case tk + of tkString, tkInt, tkFloat, tkTrue, tkFalse: + my.state[i] = stateEof # expect EOF next! + my.kind = MinEventKind(ord(tk)) + of tkBracketLe: + my.state.add(stateQuotation) # we expect any + my.kind = eMinQuotationStart + of tkBraceLe: + my.state.add(stateDictionary) # we expect any + my.kind = eMinDictionaryStart + of tkEof: + my.kind = eMinEof + else: + my.kind = eMinError + my.err = errEofExpected + of stateQuotation: + case tk + of tkString, tkInt, tkFloat, tkTrue, tkFalse: + my.kind = MinEventKind(ord(tk)) + of tkBracketLe: + my.state.add(stateQuotation) + my.kind = eMinQuotationStart + of tkBraceLe: + my.state.add(stateDictionary) + my.kind = eMinDictionaryStart + of tkBracketRi: + my.kind = eMinQuotationEnd + discard my.state.pop() + of tkBraceRi: + my.kind = eMinDictionaryEnd + discard my.state.pop() + else: + my.kind = eMinError + my.err = errBracketRiExpected + of stateDictionary: + case tk + of tkString, tkInt, tkFloat, tkTrue, tkFalse: + my.kind = MinEventKind(ord(tk)) + of tkBracketLe: + my.state.add(stateQuotation) + my.kind = eMinQuotationStart + of tkBraceLe: + my.state.add(stateDictionary) + my.kind = eMinDictionaryStart + of tkBracketRi: + my.kind = eMinQuotationEnd + discard my.state.pop() + of tkBraceRi: + my.kind = eMinDictionaryEnd + discard my.state.pop() + else: + my.kind = eMinError + my.err = errBraceRiExpected + of stateExpectValue: + case tk + of tkString, tkInt, tkFloat, tkTrue, tkFalse: + my.kind = MinEventKind(ord(tk)) + of tkBracketLe: + my.state.add(stateQuotation) + my.kind = eMinQuotationStart + of tkBraceLe: + my.state.add(stateDictionary) + my.kind = eMinDictionaryStart + else: + my.kind = eMinError + my.err = errExprExpected + +proc eat(p: var MinParser, token: MinTokenKind) = + if p.token == token: discard getToken(p) + else: raiseParsing(p, tokToStr[token]) + +proc `$`*(a: MinValue): string {.inline, extern:"min_exported_symbol_$1".}= + case a.kind: + of minNull: + return "null" + of minBool: + return $a.boolVal + of minSymbol: + return a.symVal + of minString: + return "\"$1\"" % a.strVal.replace("\"", "\\\"") + of minInt: + return $a.intVal + of minFloat: + return $a.floatVal + of minQuotation: + var q = "(" + for i in a.qVal: + q = q & $i & " " + q = q.strip & ")" + return q + of minDictionary: + var d = "{" + for i in a.dVal.pairs: + var v = "" + if i.val.kind == minProcOp: + v = "<native>" + else: + v = $i.val.val + var k = $i.key + if k.contains(" "): + k = "\"$1\"" % k + d = d & v & " :" & k & " " + if a.objType != "": + d = d & ";" & a.objType + d = d.strip & "}" + return d + +proc `$$`*(a: MinValue): string {.inline, extern:"min_exported_symbol_$1".}= + case a.kind: + of minNull: + return "null" + of minBool: + return $a.boolVal + of minSymbol: + return a.symVal + of minString: + return a.strVal + of minInt: + return $a.intVal + of minFloat: + return $a.floatVal + of minQuotation: + var q = "(" + for i in a.qVal: + q = q & $i & " " + q = q.strip & ")" + return q + of minDictionary: + var d = "{" + for i in a.dVal.pairs: + var v = "" + if i.val.kind == minProcOp: + v = "<native>" + else: + v = $i.val.val + var k = $i.key + if k.contains(" "): + k = "\"$1\"" % k + d = d & v & " :" & k & " " + if a.objType != "": + d = d & ";" & a.objType + d = d.strip & "}" + return d + +proc parseMinValue*(p: var MinParser, i: In): MinValue = + case p.token + of tkNull: + result = MinValue(kind: minNull) + discard getToken(p) + of tkTrue: + result = MinValue(kind: minBool, boolVal: true) + discard getToken(p) + of tkFalse: + result = MinValue(kind: minBool, boolVal: false) + discard getToken(p) + of tkString: + result = MinValue(kind: minString, strVal: p.a) + p.a = "" + discard getToken(p) + of tkInt: + result = MinValue(kind: minInt, intVal: parseint(p.a)) + discard getToken(p) + of tkFloat: + result = MinValue(kind: minFloat, floatVal: parseFloat(p.a)) + discard getToken(p) + of tkBracketLe: + var q = newSeq[MinValue](0) + discard getToken(p) + while p.token != tkBracketRi: + q.add p.parseMinValue(i) + eat(p, tkBracketRi) + result = MinValue(kind: minQuotation, qVal: q) + of tkBraceLe: + var scope = newScopeRef(nil) + var val: MinValue + discard getToken(p) + var c = 0 + while p.token != tkBraceRi: + c = c+1 + let v = p.parseMinValue(i) + if val.isNil: + val = v + elif v.kind == minSymbol: + let key = v.symVal + if key[0] == ':': + var offset = 0 + if key[1] == '"': + offset = 1 + scope.symbols[key[1+offset .. key.len-1-offset]] = MinOperator(kind: minValOp, val: val, 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") + result = MinValue(kind: minDictionary, scope: scope) + of tkSymbol: + result = MinValue(kind: minSymbol, symVal: p.a, column: p.getColumn, line: p.lineNumber, filename: p.filename) + p.a = "" + discard getToken(p) + else: + let err = "Undefined or invalid value: "&p.a + raiseUndefined(p, err) + result.filename = p.filename + +proc compileMinValue*(p: var MinParser, i: In, push = true, indent = ""): seq[string] = + var op = indent + if push: + op = indent&"i.push " + result = newSeq[string](0) + case p.token + of tkNull: + result = @[op&"MinValue(kind: minNull)"] + discard getToken(p) + 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.escapeEx&")"] + 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: + CVARCOUNT.inc + var qvar = "q" & $CVARCOUNT + result.add indent&"var "&qvar&" = newSeq[MinValue](0)" + discard getToken(p) + while p.token != tkBracketRi: + var instructions = p.compileMinValue(i, false, indent) + let v = instructions.pop + result = result.concat(instructions) + result.add indent&qvar&".add "&v + eat(p, tkBracketRi) + result.add op&"MinValue(kind: minQuotation, qVal: "&qvar&")" + of tkBraceLe: + result = newSeq[string](0) + var val: MinValue + discard getToken(p) + var c = 0 + var valInitialized = false + CVARCOUNT.inc + var scopevar = "scope" & $CVARCOUNT + CVARCOUNT.inc + var valvar = "val" & $CVARCOUNT + while p.token != tkBraceRi: + c = c+1 + var instructions = p.compileMinValue(i, false, indent) + let v = p.parseMinValue(i) + let vs = instructions.pop + result = result.concat(instructions) + if val.isNil: + if not valInitialized: + result.add indent&"var "&valvar&": MinValue" + valInitialized = true + result.add indent&valvar&" = "&vs + elif v.kind == minSymbol: + let key = v.symVal + if key[0] == ':': + result.add indent&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") + result.add indent&"var "&scopevar&" = newScopeRef(nil)" + result.add op&"MinValue(kind: minDictionary, scope: "&scopevar&")" + of tkSymbol: + result = @[op&"MinValue(kind: minSymbol, symVal: "&p.a.escapeEx&", column: " & $p.getColumn & ", line: " & $p.lineNumber & ", filename: "&p.filename.escapeEx&")"] + p.a = "" + discard getToken(p) + else: + raiseUndefined(p, "Undefined value: '"&p.a&"'") + +proc print*(a: MinValue) = + stdout.write($$a) + +# Predicates + +proc isNull*(s: MinValue): bool = + return s.kind == minNull + +proc isSymbol*(s: MinValue): bool = + return s.kind == minSymbol + +proc isQuotation*(s: MinValue): bool = + return s.kind == minQuotation + +proc isString*(s: MinValue): bool = + return s.kind == minString + +proc isFloat*(s: MinValue): bool = + return s.kind == minFloat + +proc isInt*(s: MinValue): bool = + return s.kind == minInt + +proc isNumber*(s: MinValue): bool = + return s.kind == minInt or s.kind == minFloat + +proc isBool*(s: MinValue): bool = + return s.kind == minBool + +proc isStringLike*(s: MinValue): bool = + return s.isSymbol or s.isString or (s.isQuotation and s.qVal.len == 1 and s.qVal[0].isSymbol) + +proc isDictionary*(q: MinValue): bool = + return q.kind == minDictionary + +proc isTypedDictionary*(q: MinValue): bool = + if q.isDictionary: + return q.objType != "" + return false + +proc isTypedDictionary*(q: MinValue, t: string): bool = + if q.isTypedDictionary: + return q.objType == t + return false + +proc `==`*(a: MinValue, b: MinValue): bool {.inline, extern:"min_exported_symbol_eqeq".}= + if not (a.kind == b.kind or (a.isNumber and b.isNumber)): + return false + if a.kind == minSymbol and b.kind == minSymbol: + return a.symVal == b.symVal + elif a.kind == minInt and b.kind == minInt: + return a.intVal == b.intVal + elif a.kind == minInt and b.kind == minFloat: + return a.intVal.float == b.floatVal.float + elif a.kind == minFloat and b.kind == minFloat: + return a.floatVal == b.floatVal + elif a.kind == minFloat and b.kind == minInt: + return a.floatVal == b.intVal.float + elif a.kind == b.kind: + if a.kind == minString: + return a.strVal == b.strVal + elif a.kind == minBool: + return a.boolVal == b.boolVal + elif a.kind == minNull: + return true + elif a.kind == minQuotation: + if a.qVal.len == b.qVal.len: + var c = 0 + for item in a.qVal: + if item == b.qVal[c]: + c.inc + else: + return false + return true + else: + return false + elif a.kind == minDictionary: + let aVal = a.dVal + let bVal = b.dVal + if aVal.len != bVal.len: + return false + else: + for t in aVal.pairs: + if not bVal.hasKey(t.key): + return false + let v1 = t.val + let v2 = bVal[t.key] + if v1.kind != v2.kind: + return false + if v1.kind == minValOp: + return v1.val == v2.val + if a.objType == "" and b.objType == "": + return true + elif a.objType != "" and b.objType != "": + return a.objType == b.objType + else: + return false + else: + return false
A minpkg/core/scope.nim

@@ -0,0 +1,96 @@

+import + strutils, + critbits +import + parser + +proc copy*(s: ref MinScope): ref MinScope = + var scope = newScope(s.parent) + scope.symbols = s.symbols + scope.sigils = s.sigils + new(result) + result[] = scope + +proc getSymbol*(scope: ref MinScope, key: string, acc=0): MinOperator = + if scope.symbols.hasKey(key): + return scope.symbols[key] + else: + if scope.parent.isNil: + raiseUndefined("Symbol '$1' not found." % key) + return scope.parent.getSymbol(key, acc + 1) + +proc hasSymbol*(scope: ref MinScope, key: string): bool = + if scope.isNil: + return false + elif scope.symbols.hasKey(key): + return true + elif not scope.parent.isNil: + return scope.parent.hasSymbol(key) + else: + return false + +proc delSymbol*(scope: ref MinScope, key: string): bool {.discardable, extern:"min_exported_symbol_$1".}= + if scope.symbols.hasKey(key): + if scope.symbols[key].sealed: + raiseInvalid("Symbol '$1' is sealed." % key) + scope.symbols.excl(key) + return true + return false + +proc setSymbol*(scope: ref MinScope, key: string, value: MinOperator, override = false): bool {.discardable, extern:"min_exported_symbol_$1".}= + result = false + # check if a symbol already exists in current scope + if not scope.isNil and scope.symbols.hasKey(key): + if not override and scope.symbols[key].sealed: + raiseInvalid("Symbol '$1' is sealed." % key) + scope.symbols[key] = value + result = true + else: + # Go up the scope chain and attempt to find the symbol + if not scope.parent.isNil: + result = scope.parent.setSymbol(key, value) + +proc getSigil*(scope: ref MinScope, key: string): MinOperator = + if scope.sigils.hasKey(key): + return scope.sigils[key] + elif not scope.parent.isNil: + return scope.parent.getSigil(key) + else: + raiseUndefined("Sigil '$1' not found." % key) + +proc hasSigil*(scope: ref MinScope, key: string): bool = + if scope.isNil: + return false + elif scope.sigils.hasKey(key): + return true + elif not scope.parent.isNil: + return scope.parent.hasSigil(key) + else: + return false + +proc delSigil*(scope: ref MinScope, key: string): bool {.discardable, extern:"min_exported_symbol_$1".}= + if scope.sigils.hasKey(key): + if scope.sigils[key].sealed: + raiseInvalid("Sigil '$1' is sealed." % key) + scope.sigils.excl(key) + return true + return false + +proc setSigil*(scope: ref MinScope, key: string, value: MinOperator, override = false): bool {.discardable, extern:"min_exported_symbol_$1".}= + result = false + # check if a sigil already exists in current scope + if not scope.isNil and scope.sigils.hasKey(key): + if not override and scope.sigils[key].sealed: + raiseInvalid("Sigil '$1' is sealed." % key) + scope.sigils[key] = value + result = true + else: + # Go up the scope chain and attempt to find the sigil + if not scope.parent.isNil: + result = scope.parent.setSymbol(key, value) + +proc previous*(scope: ref MinScope): ref MinScope = + if scope.parent.isNil: + return scope + else: + return scope.parent
A minpkg/core/utils.nim

@@ -0,0 +1,287 @@

+import + strutils, + critbits +import + baseutils, + parser, + value, + scope, + interpreter + +when not defined(mini): + import + json + +# Library methods + +proc define*(i: In): ref MinScope = + var scope = newScopeRef(i.scope, minNativeScope) + scope.parent = i.scope + return scope + +proc symbol*(scope: ref MinScope, sym: string, p: MinOperatorProc) = + scope.symbols[sym] = MinOperator(prc: p, kind: minProcOp, sealed: true) + +proc symbol*(scope: ref MinScope, sym: string, v: MinValue) = + scope.symbols[sym] = MinOperator(val: v, kind: minValOp, sealed: true) + +proc sigil*(scope: ref MinScope, sym: string, p: MinOperatorProc) = + scope.sigils[sym] = MinOperator(prc: p, kind: minProcOp, sealed: true) + +proc sigil*(scope: ref MinScope, sym: string, v: MinValue) = + scope.sigils[sym] = MinOperator(val: v, kind: minValOp, sealed: true) + +proc finalize*(scope: ref MinScope, name: string = "") = + var mdl = newDict(scope) + mdl.scope = scope + mdl.objType = "module" + let op = proc(i: In) {.closure.} = + i.evaluating = true + i.push mdl + i.evaluating = false + if name != "": + scope.previous.symbols[name] = MinOperator(kind: minProcOp, prc: op) + +# Dictionary Methods + +proc dget*(i: In, q: MinValue, s: MinValue): MinValue = + if not q.isDictionary: + raiseInvalid("Value is not a dictionary") + if q.dVal[s.getString].kind == minProcOp: + raiseInvalid("Key '$1' is set to a native value that cannot be retrieved." % [s.getString]) + result = q.dVal[s.getString].val + +proc dget*(i: In, q: MinValue, s: string): MinValue = + if not q.isDictionary: + raiseInvalid("Value is not a dictionary") + if q.dVal[s].kind == minProcOp: + raiseInvalid("Key $1 is set to a native value that cannot be retrieved." % [s]) + result = q.dVal[s].val + +proc dhas*(q: MinValue, s: MinValue): bool = + if not q.isDictionary: + raiseInvalid("Value is not a dictionary") + return q.dVal.contains(s.getString) + +proc dhas*(q: MinValue, s: string): bool = + if not q.isDictionary: + raiseInvalid("Value is not a dictionary") + return q.dVal.contains(s) + +proc ddel*(i: In, p: var MinValue, s: MinValue): MinValue {.discardable, extern:"min_exported_symbol_$1".} = + if not p.isDictionary: + raiseInvalid("Value is not a dictionary") + excl(p.scope.symbols, s.getString) + return p + +proc ddel*(i: In, p: var MinValue, s: string): MinValue {.discardable, extern:"min_exported_symbol_$1_2".} = + if not p.isDictionary: + raiseInvalid("Value is not a dictionary") + excl(p.scope.symbols, s) + return p + +proc dset*(i: In, p: var MinValue, s: MinValue, m: MinValue): MinValue {.discardable, extern:"min_exported_symbol_$1".}= + if not p.isDictionary: + raiseInvalid("Value is not a dictionary") + var q = m + p.scope.symbols[s.getString] = MinOperator(kind: minValOp, val: q, sealed: false) + return p + +proc dset*(i: In, p: var MinValue, s: string, m: MinValue): MinValue {.discardable, extern:"min_exported_symbol_$1_2".}= + if not p.isDictionary: + raiseInvalid("Value is not a dictionary") + var q = m + p.scope.symbols[s] = MinOperator(kind: minValOp, val: q, sealed: false) + return p + +proc keys*(i: In, q: MinValue): MinValue = + # Assumes q is a dictionary + var r = newSeq[MinValue](0) + for i in q.dVal.keys: + r.add newVal(i) + return r.newVal + +proc values*(i: In, q: MinValue): MinValue = + # Assumes q is a dictionary + var r = newSeq[MinValue](0) + for item in q.dVal.values: + if item.kind == minProcOp: + raiseInvalid("Dictionary contains native values that cannot be accessed.") + r.add item.val + return r.newVal + +proc pairs*(i: In, q: MinValue): MinValue = + # Assumes q is a dictionary + var r = newSeq[MinValue](0) + for key, value in q.dVal.pairs: + if value.kind == minProcOp: + raiseInvalid("Dictionary contains native values that cannot be accessed.") + r.add key.newVal + r.add value.val + return r.newVal + +when not defined(mini): + + # JSON interop + + proc `%`*(i: In, a: MinValue): JsonNode = + case a.kind: + of minBool: + return %a.boolVal + of minNull: + return newJNull() + of minSymbol: + return %(";sym:$1" % [a.getstring]) + of minString: + return %a.strVal + of minInt: + return %a.intVal + of minFloat: + return %a.floatVal + of minQuotation: + result = newJArray() + for it in a.qVal: + result.add(i%it) + of minDictionary: + result = newJObject() + for it in a.dVal.pairs: + result[it.key] = i%i.dget(a, it.key) + + proc fromJson*(i: In, json: JsonNode): MinValue = + case json.kind: + of JNull: + result = newNull() + of JBool: + result = json.getBool.newVal + of JInt: + result = json.getBiggestInt.newVal + of JFloat: + result = json.getFloat.newVal + of JString: + let s = json.getStr + if s.startsWith(";sym:"): + result = s.replace(";sym:", "").newSym + else: + result = json.getStr.newVal + of JObject: + var res = newDict(i.scope) + for key, value in json.pairs: + discard i.dset(res, key, i.fromJson(value)) + return res + of JArray: + var res = newSeq[MinValue](0) + for value in json.items: + res.add i.fromJson(value) + return res.newVal + +# Validators + +proc validate*(value: MinValue, t: string): bool = + case t: + of "bool": + return value.isBool + of "null": + return value.isNull + of "int": + return value.isInt + of "num": + return value.isNumber + of "quot": + return value.isQuotation + of "dict": + return value.isDictionary + of "'sym": + return value.isStringLike + of "sym": + return value.isSymbol + of "float": + return value.isFloat + of "string": + return value.isString + of "a": + return true + else: + var split = t.split(":") + # Typed dictionaries + if split[0] == "dict": + if value.isTypedDictionary(split[1]): + return true + return false + +proc validType*(s: string): bool = + const ts = ["bool", "null", "int", "num", "float", "quot", "dict", "'sym", "sym", "string", "a"] + if ts.contains(s): + return true + for tt in s.split("|"): + if not ts.contains(tt) or tt.startsWith("dict:"): + return false + return true + +proc expect*(i: var MinInterpreter, elements: varargs[string]): seq[MinValue] = + let stack = elements.reverse.join(" ") + let sym = i.currSym.getString + var valid = newSeq[string](0) + result = newSeq[MinValue](0) + let message = proc(invalid: string): string = + result = "Symbol: $1 - Incorrect values found on the stack:\n" % sym + result &= "- expected: " & stack & " $1\n" % sym + var other = "" + if valid.len > 0: + other = valid.reverse.join(" ") & " " + result &= "- got: " & invalid & " " & other & sym + for element in elements: + let value = i.pop + result.add value + var split = element.split("|") + if split.len > 1: + var res = false + for t in split: + if validate(value, t): + res = true + break + if not res: + raiseInvalid(message(value.typeName)) + elif not validate(value, element): + raiseInvalid(message(value.typeName)) + valid.add element + +proc reqQuotationOfQuotations*(i: var MinInterpreter, a: var MinValue) = + a = i.pop + if not a.isQuotation: + raiseInvalid("A quotation is required on the stack") + for s in a.qVal: + if not s.isQuotation: + raiseInvalid("A quotation of quotations is required on the stack") + +proc reqQuotationOfNumbers*(i: var MinInterpreter, a: var MinValue) = + a = i.pop + if not a.isQuotation: + raiseInvalid("A quotation is required on the stack") + for s in a.qVal: + if not s.isNumber: + raiseInvalid("A quotation of numbers is required on the stack") + +proc reqQuotationOfSymbols*(i: var MinInterpreter, a: var MinValue) = + a = i.pop + if not a.isQuotation: + raiseInvalid("A quotation is required on the stack") + for s in a.qVal: + if not s.isSymbol: + raiseInvalid("A quotation of symbols is required on the stack") + +proc reqTwoNumbersOrStrings*(i: var MinInterpreter, a, b: var MinValue) = + a = i.pop + b = i.pop + if not (a.isString and b.isString or a.isNumber and b.isNumber): + raiseInvalid("Two numbers or two strings are required on the stack") + +proc reqStringOrQuotation*(i: var MinInterpreter, a: var MinValue) = + a = i.pop + if not a.isQuotation and not a.isString: + raiseInvalid("A quotation or a string is required on the stack") + +proc reqTwoQuotationsOrStrings*(i: var MinInterpreter, a, b: var MinValue) = + a = i.pop + b = i.pop + if not (a.isQuotation and b.isQuotation or a.isString and b.isString): + raiseInvalid("Two quotations or two strings are required on the stack")
A minpkg/core/value.nim

@@ -0,0 +1,81 @@

+import + parser, + hashes + +proc typeName*(v: MinValue): string = + case v.kind: + of minInt: + return "int" + of minFloat: + return "float" + of minDictionary: + if v.isTypedDictionary: + return "dict:" & v.objType + else: + return "dict" + of minQuotation: + return "quot" + of minString: + return "string" + of minSymbol: + return "sym" + of minNull: + return "null" + of minBool: + return "bool" + +# Constructors + +proc newNull*(): MinValue = + return MinValue(kind: minNull) + +proc newVal*(s: string): MinValue = + return MinValue(kind: minString, strVal: s) + +proc newVal*(s: cstring): MinValue = + return MinValue(kind: minString, strVal: $s) + +proc newVal*(q: seq[MinValue]): MinValue = + return MinValue(kind: minQuotation, qVal: q) + +proc newVal*(i: BiggestInt): MinValue = + return MinValue(kind: minInt, intVal: i) + +proc newVal*(f: BiggestFloat): MinValue = + return MinValue(kind: minFloat, floatVal: f) + +proc newVal*(s: bool): MinValue = + return MinValue(kind: minBool, boolVal: s) + +proc newDict*(parentScope: ref MinScope): MinValue = + return MinValue(kind: minDictionary, scope: newScopeRef(parentScope)) + +proc newSym*(s: string): MinValue = + return MinValue(kind: minSymbol, symVal: s) + +proc hash*(v: MinValue): Hash = + return hash($v) + +# Get string value from string or quoted symbol + +proc getFloat*(v: MinValue): float = + if v.isInt: + return v.intVal.float + elif v.isFloat: + return v.floatVal + else: + raiseInvalid("Value is not a number") + +proc getString*(v: MinValue): string = + if v.isSymbol: + return v.symVal + elif v.isString: + return v.strVal + elif v.isQuotation: + if v.qVal.len != 1: + raiseInvalid("Quotation is not a quoted symbol") + let sym = v.qVal[0] + if sym.isSymbol: + return sym.symVal + else: + raiseInvalid("Quotation is not a quoted symbol")
A minpkg/lib/min_crypto.nim

@@ -0,0 +1,134 @@

+import + base64, + strutils, + times, + ../vendor/aes/aes +import + ../core/parser, + ../core/value, + ../core/interpreter, + ../core/utils + +{.compile: "../vendor/aes/libaes.c".} + +when defined(ssl): + import + openssl + + proc EVP_MD_CTX_new*(): EVP_MD_CTX {.cdecl, importc: "EVP_MD_CTX_new".} + proc EVP_MD_CTX_free*(ctx: EVP_MD_CTX) {.cdecl, importc: "EVP_MD_CTX_free".} +else: + import + std/sha1, + md5 + +proc crypto_module*(i: In)= + let def = i.define() + + + def.symbol("encode") do (i: In): + let vals = i.expect("'sym") + let s = vals[0] + i.push s.getString.encode.newVal + + def.symbol("decode") do (i: In): + let vals = i.expect("'sym") + let s = vals[0] + i.push s.getString.decode.newVal + + when defined(ssl): + + when defined(windows): + {.passL: "-static -Lminpkg/vendor/openssl/windows -lssl -lcrypto -lws2_32".} + elif defined(linux): + {.passL: "-static -Lminpkg/vendor/openssl/linux -lssl -lcrypto".} + elif defined(macosx): + {.passL: "-Bstatic -Lminpkg/vendor/openssl/macosx -lssl -lcrypto -Bdynamic".} + + proc hash(s: string, kind: EVP_MD, size: int): string = + var hash_length: cuint = 0 + var hash = alloc[ptr cuchar](size) + let ctx = EVP_MD_CTX_new() + discard EVP_DigestInit_ex(ctx, kind, nil) + discard EVP_DigestUpdate(ctx, unsafeAddr s[0], s.len.cuint) + discard EVP_DigestFinal_ex(ctx, hash, cast[ptr cuint](hash_length)) + EVP_MD_CTX_free(ctx) + var hashStr = newString(size) + copyMem(addr(hashStr[0]), hash, size) + dealloc(hash) + return hashStr.toHex.toLowerAscii[0..size-1] + + def.symbol("md5") do (i: In): + let vals = i.expect("'sym") + let s = vals[0].getString + i.push hash(s, EVP_md5(), 32).newVal + + def.symbol("md4") do (i: In): + let vals = i.expect("'sym") + let s = vals[0].getString + i.push hash(s, EVP_md4(), 32).newVal + + def.symbol("sha1") do (i: In): + let vals = i.expect("'sym") + var s = vals[0].getString + i.push hash(s, EVP_sha1(), 40).newVal + + def.symbol("sha224") do (i: In): + let vals = i.expect("'sym") + let s = vals[0].getString + i.push hash(s, EVP_sha224(), 56).newVal + + def.symbol("sha256") do (i: In): + let vals = i.expect("'sym") + let s = vals[0].getString + i.push hash(s, EVP_sha256(), 64).newVal + + def.symbol("sha384") do (i: In): + let vals = i.expect("'sym") + let s = vals[0].getString + i.push hash(s, EVP_sha384(), 96).newVal + + def.symbol("sha512") do (i: In): + let vals = i.expect("'sym") + let s = vals[0].getString + i.push hash(s, EVP_sha512(), 128).newVal + + def.symbol("aes") do (i: In): + let vals = i.expect("'sym", "'sym") + let k = vals[0] + let s = vals[1] + var text = s.getString + var key = hash(k.getString, EVP_sha1(), 40) + var iv = hash((key & $getTime().toUnix), EVP_sha1(), 40) + var ctx = cast[ptr AES_ctx](alloc0(sizeof(AES_ctx))) + AES_init_ctx_iv(ctx, cast[ptr uint8](key[0].addr), cast[ptr uint8](iv[0].addr)); + var input = cast[ptr uint8](text[0].addr) + AES_CTR_xcrypt_buffer(ctx, input, text.len.uint32); + i.push text.newVal + + else: + + def.symbol("md5") do (i: In): + let vals = i.expect("'sym") + let s = vals[0].getString + i.push newVal($toMD5(s)) + + def.symbol("sha1") do (i: In): + let vals = i.expect("'sym") + var s = vals[0].getString + i.push newVal(toLowerAscii($secureHash(s))) + + def.symbol("aes") do (i: In): + let vals = i.expect("'sym", "'sym") + let k = vals[0] + let s = vals[1] + var text = s.getString + var key = ($secureHash(k.getString)).toLowerAscii + var iv = ($secureHash((key & $getTime().toUnix))).toLowerAscii + var ctx = cast[ptr AES_ctx](alloc0(sizeof(AES_ctx))) + AES_init_ctx_iv(ctx, cast[ptr uint8](key[0].addr), cast[ptr uint8](iv[0].addr)); + var input = cast[ptr uint8](text[0].addr) + AES_CTR_xcrypt_buffer(ctx, input, text.len.uint32); + i.push text.newVal + + def.finalize("crypto")
A minpkg/lib/min_fs.nim

@@ -0,0 +1,67 @@

+import + os, + times +import + ../core/parser, + ../core/value, + ../core/interpreter, + ../core/utils, + ../core/fileutils + +proc fs_module*(i: In) = + + let def = i.define() + + def.symbol("mtime") do (i: In): + let vals = i.expect("'sym") + let s = vals[0] + i.push s.getString.getLastModificationTime.toUnix.newVal + + def.symbol("atime") do (i: In): + let vals = i.expect("'sym") + let s = vals[0] + i.push s.getString.getLastAccessTime.toUnix.newVal + + def.symbol("ctime") do (i: In): + let vals = i.expect("'sym") + let s = vals[0] + i.push s.getString.getCreationTime.toUnix.newVal + + def.symbol("hidden?") do (i: In): + let vals = i.expect("'sym") + let s = vals[0] + i.push s.getString.isHidden.newVal + + def.symbol("fsize") do (i: In): + let vals = i.expect("'sym") + let s = vals[0] + i.push s.getString.getFileSize.newVal + + def.symbol("fstats") do (i: In): + let vals = i.expect("'sym") + let s = vals[0] + let fi = s.getString.getFileInfo + var info = newDict(i.scope) + i.dset(info, "name", s) + i.dset(info, "device", fi.id.device.BiggestInt.newVal) + i.dset(info, "file", fi.id.file.BiggestInt.newVal) + i.dset(info, "type", fi.kind.filetype.newVal) + i.dset(info, "size", fi.size.newVal) + i.dset(info, "permissions", fi.permissions.unixPermissions.newVal) + i.dset(info, "nlinks", fi.linkCount.newVal) + i.dset(info, "ctime", fi.creationTime.toUnix.newVal) + i.dset(info, "atime", fi.lastAccessTime.toUnix.newVal) + i.dset(info, "mtime", fi.lastWriteTime.toUnix.newVal) + i.push info + + def.symbol("ftype") do (i: In): + let vals = i.expect("'sym") + let s = vals[0] + i.push s.getString.getFileInfo.kind.filetype.newVal + + def.symbol("fperms") do (i: In): + let vals = i.expect("'sym") + let s = vals[0] + i.push s.getString.getFilePermissions.unixPermissions.newVal + + def.finalize("fs")
A minpkg/lib/min_io.nim

@@ -0,0 +1,209 @@

+import + strutils, + logging, + critbits, + terminal +import + ../packages/nimline/nimline, + ../packages/nim-sgregex/sgregex, + ../core/parser, + ../core/value, + ../core/env, + ../core/interpreter, + ../core/utils + +var ORIGKEYMAP {.threadvar.}: CritBitTree[KeyCallback] +for key, value in KEYMAP.pairs: + ORIGKEYMAP[key] = value + +proc io_module*(i: In) = + let def = i.define() + + def.symbol("clear") do (i: In): + stdout.eraseScreen + stdout.setCursorPos(0, 0) + + def.symbol("unmapkey") do (i: In): + let vals = i.expect("'sym") + let key = vals[0].getString.toLowerAscii + if not KEYNAMES.contains(key) and not KEYSEQS.contains(key): + raiseInvalid("Unrecognized key: " & key) + if KEYMAP.hasKey(key): + if ORIGKEYMAP.hasKey(key): + KEYMAP[key] = ORIGKEYMAP[key] + else: + KEYMAP.excl(key) + + def.symbol("mapkey") do (i: In): + let vals = i.expect("'sym", "quot") + let key = vals[0].getString.toLowerAscii + var q = vals[1] + if not KEYNAMES.contains(key) and not KEYSEQS.contains(key): + raiseInvalid("Unrecognized key: " & key) + var ic = i.copy(i.filename) + KEYMAP[key] = proc (ed: var LineEditor) {.gcsafe.} = + ic.apply(q) + + def.symbol("newline") do (i: In): + echo "" + + def.symbol("notice") do (i: In): + let a = i.peek + notice $$a + + def.symbol("info") do (i: In): + let a = i.peek + info $$a + + def.symbol("error") do (i: In): + let a = i.peek + error $$a + + def.symbol("warn") do (i: In): + let a = i.peek + warn $$a + + def.symbol("debug") do (i: In): + let a = i.peek + debug $$a + + def.symbol("fatal") do (i: In): + let a = i.peek + fatal $$a + quit(100) + + def.symbol("notice!") do (i: In): + let a = i.pop + notice $$a + + def.symbol("info!") do (i: In): + let a = i.pop + info $$a + + def.symbol("error!") do (i: In): + let a = i.pop + error $$a + + def.symbol("warn!") do (i: In): + let a = i.pop + warn $$a + + def.symbol("debug!") do (i: In): + let a = i.pop + debug $$a + + def.symbol("fatal!") do (i: In): + let a = i.pop + fatal $$a + quit(100) + + def.symbol("column-print") do (i: In): + let vals = i.expect("int", "quot") + let n = vals[0] + let q = vals[1] + var c = 0 + for s in q.qVal: + c.inc + stdout.write $$s & spaces(max(0, 15 - ($$s).len)) + if c mod n.intVal == 0: + echo "" + echo "" + + def.symbol("getchr") do (i: In): + i.push getchr().newVal + + def.symbol("putchr") do (i: In): + let ch = i.expect("string") + if ch[0].getString.len != 1: + raiseInvalid("Symbol putch requires a string containing a single character.") + putchr(ch[0].getString[0].cint) + + def.symbol("password") do (i: In) {.gcsafe.}: + var ed = initEditor() + i.push ed.password("Enter Password: ").newVal + + def.symbol("ask") do (i: In) {.gcsafe.}: + var ed = initEditor() + let vals = i.expect("string") + let s = vals[0] + i.push ed.readLine(s.getString & ": ").newVal + + def.symbol("confirm") do (i: In) {.gcsafe.}: + var ed = initEditor() + let vals = i.expect("string") + let s = vals[0] + proc confirm(): bool = + let answer = ed.readLine(s.getString & " [yes/no]: ") + if answer.match("^y(es)?$", "i"): + return true + elif answer.match("^no?$", "i"): + return false + else: + stdout.write "Invalid answer. Please enter 'yes' or 'no': " + return confirm() + i.push confirm().newVal + + def.symbol("choose") do (i: In) {.gcsafe.}: + var ed = initEditor() + let vals = i.expect("'sym", "quot") + let s = vals[0] + var q = vals[1] + if q.qVal.len <= 0: + raiseInvalid("No choices to display") + stdout.writeLine(s.getString) + proc choose(): int = + var c = 0 + for item in q.qVal: + if not item.isQuotation or not item.qVal.len == 2 or not item.qVal[0].isString or not item.qVal[1].isQuotation: + raiseInvalid("Each item of the quotation must be a quotation containing a string and a quotation") + c.inc + echo "$1 - $2" % [$c, item.qVal[0].getString] + let answer = ed.readLine("Enter your choice ($1 - $2): " % ["1", $c]) + var choice: int + try: + choice = answer.parseInt + except: + choice = 0 + if choice <= 0 or choice > c: + echo "Invalid choice." + return choose() + else: + return choice + let choice = choose() + i.dequote(q.qVal[choice-1].qVal[1]) + + def.symbol("print") do (i: In): + let a = i.peek + a.print + + def.symbol("print!") do (i: In): + i.pop.print + + def.symbol("fread") do (i: In): + let vals = i.expect("string") + let file = vals[0].strVal + var contents = "" + if MINCOMPILED: + var compiledFile = strutils.replace(strutils.replace(file, "\\", "/"), "./", "") + if COMPILEDASSETS.hasKey(compiledFile): + contents = COMPILEDASSETS[compiledFile] + if contents == "": + contents = file.readFile + i.push newVal(contents) + + def.symbol("fwrite") do (i: In): + let vals = i.expect("string", "string") + let a = vals[0] + let b = vals[1] + a.strVal.writeFile(b.strVal) + + def.symbol("fappend") do (i: In): + let vals = i.expect("string", "string") + let a = vals[0] + let b = vals[1] + var f:File + discard f.open(a.strVal, fmAppend) + f.write(b.strVal) + f.close() + + def.finalize("io")
A minpkg/lib/min_lang.nim

@@ -0,0 +1,1078 @@

+import + critbits, + strutils, + sequtils, + parseopt, + algorithm +when defined(mini): + import + rdstdin, + ../core/minilogger +else: + import + os, + json, + logging, + ../core/baseutils, + ../packages/niftylogger, + ../packages/nimline/nimline, + ../packages/nim-sgregex/sgregex +import + ../core/env, + ../core/meta, + ../core/parser, + ../core/value, + ../core/interpreter, + ../core/utils, + ../core/scope + +proc lang_module*(i: In) = + let def = i.scope + + when not defined(mini): + + def.symbol("from-json") do (i: In): + let vals = i.expect("string") + let s = vals[0] + i.push i.fromJson(s.getString.parseJson) + + def.symbol("to-json") do (i: In): + let vals = i.expect "a" + let q = vals[0] + i.push(($((i%q).pretty)).newVal) + + # Save/load symbols + + def.symbol("save-symbol") do (i: In) {.gcsafe.}: + let vals = i.expect("'sym") + let s = vals[0] + let sym = s.getString + let op = i.scope.getSymbol(sym) + if op.kind == minProcOp: + raiseInvalid("Symbol '$1' cannot be serialized." % sym) + let json = MINSYMBOLS.readFile.parseJson + json[sym] = i%op.val + MINSYMBOLS.writeFile(json.pretty) + + def.symbol("load-symbol") do (i: In): + let vals = i.expect("'sym") + let s = vals[0] + let sym = s.getString + let json = MINSYMBOLS.readFile.parseJson + if not json.hasKey(sym): + raiseUndefined("Symbol '$1' not found." % sym) + let val = i.fromJson(json[sym]) + i.scope.symbols[sym] = MinOperator(kind: minValOp, val: val, quotation: true) + + def.symbol("saved-symbols") do (i: In): + var q = newSeq[MinValue](0) + let json = MINSYMBOLS.readFile.parseJson + for k,v in json.pairs: + q.add k.newVal + i.push q.newVal + + def.symbol("remove-symbol") do (i: In): + let vals = i.expect("'sym") + let s = vals[0] + let sym = s.getString + var json = MINSYMBOLS.readFile.parseJson + if not json.hasKey(sym): + raiseUndefined("Symbol '$1' not found." % sym) + json.delete(sym) + MINSYMBOLS.writeFile(json.pretty) + + def.symbol("load") do (i: In): + let vals = i.expect("'sym") + let s = vals[0] + var file = s.getString + if not file.endsWith(".min"): + file = file & ".min" + info("[load] File: ", file) + let f = simplifyPath(i.filename, file) + if MINCOMPILED and COMPILEDMINFILES.hasKey(f): + var i2 = i.copy(f) + i2.withScope(): + COMPILEDMINFILES[f](i2) + i = i2.copy(i.filename) + return + else: + if not f.fileExists: + raiseInvalid("File '$1' does not exist." % file) + i.load f + + def.symbol("require") do (i: In): + let vals = i.expect("'sym") + let s = vals[0] + var file = s.getString + if not file.endsWith(".min"): + file = file & ".min" + info("[require] File: ", file) + let f = simplifyPath(i.filename, file) + if MINCOMPILED and COMPILEDMINFILES.hasKey(f): + var i2 = i.copy(f) + i2.withScope(): + var mdl: MinValue + if not CACHEDMODULES.hasKey(f): + COMPILEDMINFILES[f](i2) + CACHEDMODULES[f] = newDict(i2.scope) + CACHEDMODULES[f].objType = "module" + mdl = CACHEDMODULES[f] + for key, value in i2.scope.symbols.pairs: + mdl.scope.symbols[key] = value + i.push(mdl) + else: + if not f.fileExists: + raiseInvalid("File '$1' does not exist." % file) + i.push i.require(f) + + def.symbol("read") do (i: In): + let vals = i.expect("'sym") + let s = vals[0] + var file = s.getString + if not file.endsWith(".min"): + file = file & ".min" + info("[read] File: ", file) + if not file.fileExists: + raiseInvalid("File '$1' does not exist." % file) + i.push i.read file + + def.symbol("raw-args") do (i: In): + var args = newSeq[MinValue](0) + for par in commandLineParams(): + args.add par.newVal + i.push args.newVal + + def.symbol("with") do (i: In): + let vals = i.expect("dict", "quot") + var qscope = vals[0] + var qprog = vals[1] + i.withDictScope(qscope.scope): + for v in qprog.qVal: + i.push v + + def.symbol("publish") do (i: In): + let vals = i.expect("dict", "'sym") + let qscope = vals[0] + let str = vals[1] + let sym = str.getString + if qscope.scope.symbols.hasKey(sym) and qscope.scope.symbols[sym].sealed: + raiseUndefined("Attempting to redefine sealed symbol '$1'" % [sym]) + let scope = i.scope + info("[publish] Symbol: $2" % [sym]) + let op = proc(i: In) {.closure.} = + let origscope = i.scope + i.scope = scope + i.evaluating = true + i.pushSym sym + i.evaluating = false + i.scope = origscope + qscope.scope.symbols[sym] = MinOperator(kind: minProcOp, prc: op) + + ### End of symbols not present in minimin + + def.symbol("operator") do (i: In): + let vals = i.expect("quot"); + let q = vals[0] + if q.qVal.len != 4: + raiseInvalid("Invalid operator definition") + let tv = q.qVal[0] + if not tv.isSymbol or (tv.symVal != "symbol" and tv.symVal != "sigil"): + raiseInvalid("Incorrect operator type specified (it must be 'symbol' or 'sigil', found '$#')" % tv.symVal) + let t = tv.symVal + let nv = q.qVal[1] + if not nv.isSymbol: + raiseInvalid("Operator name must be a symbol") + let n = nv.symVal + when not defined(mini): + if not n.match(USER_SYMBOL_REGEX): + raiseInvalid("Operator name must not contain ivalid characters") + # Validate signature + let sv = q.qVal[2] + if not sv.isQuotation: + raiseInvalid("Signature must be a quotation") + elif sv.qVal.len == 0: + raiseInvalid("No signature specified") + elif sv.qVal.len == 1 and sv.qVal[0] != "==>".newSym: + raiseInvalid("Invalid signature") + elif sv.qVal.len mod 2 == 0: + raiseInvalid("Invalid signature") + var c = 0 + # Process signature + var inExpects= newSeq[string](0) + var inVars = newSeq[string](0) + var outExpects= newSeq[string](0) + var outVars = newSeq[string](0) + var o= false + for vv in sv.qVal: + if not vv.isSymbol: + raiseInvalid("Signature must be a quotation id symbols") + let v = vv.symVal + var check = c mod 2 == 0 + if o: + check = c mod 2 != 0 + if check: + if v == "==>": + o = true + elif not validType(v): + raiseInvalid("Invalid type specified in signature at position $#" % $(c+1)) + else: + if o: + outExpects.add v + else: + inExpects.add v + else: + if v[0] != ':': + raiseInvalid("No mapping symbol specified in signature at position $#" % $(c+1)) + else: + if o: + outVars.add v[1..v.len-1] + else: + inVars.add v[1..v.len-1] + c.inc() + if not o: + raiseInvalid("No output specified in signature") + # Process body + var bv = q.qVal[3] + if not bv.isQuotation: + raiseInvalid("Body must be a quotation") + inExpects.reverse + inVars.reverse + var p: MinOperatorProc = proc (i: In) = + var inVals = i.expect(inExpects) + i.withScope(): + # Inject variables for mapped inputs + for k in 0..inVars.len-1: + var iv = inVals[k] + if iv.isQuotation: + iv = @[iv].newVal + i.scope.symbols[inVars[k]] = MinOperator(kind: minValOp, sealed: false, val: iv, quotation: inVals[k].isQuotation) + # Inject variables for mapped outputs + for k in 0..outVars.len-1: + i.scope.symbols[outVars[k]] = MinOperator(kind: minValOp, sealed: false, val: newNull(), quotation: false) + # Actually execute the body of the operator + var endSnapshot: seq[MinValue] + var snapShot: seq[MinValue] + try: + snapshot = deepCopy(i.stack) + i.dequote bv + endSnapshot = i.stack + if endSnapshot != snapshot: + raiseInvalid("Operator '$#' is polluting the stack" % n) + except MinReturnException: + discard + # Validate output + for k in 0..outVars.len-1: + i.pushSym outVars[k] + let x = i.peek + let o = outExpects[k] + var r = false; + if o.contains("|"): + let types = o.split("|") + for ut in types: + if validate(x, ut): + r = true + break + else: + r = validate(x, o) + if not r: + discard i.pop + raiseInvalid("Invalid value for output symbol '$#'. Expected $#, found $#" % [outVars[k], o, $x]) + # Define symbol/sigil + if t == "symbol": + if i.scope.symbols.hasKey(n) and i.scope.symbols[n].sealed: + raiseUndefined("Attempting to redefine sealed symbol '$1'" % [n]) + i.scope.symbols[n] = MinOperator(kind: minProcOp, prc: p, sealed: false) + else: + if i.scope.sigils.hasKey(n) and i.scope.sigils[n].sealed: + raiseUndefined("Attempting to redefine sealed sigil '$1'" % [n]) + i.scope.sigils[n] = MinOperator(kind: minProcOp, prc: p, sealed: true) + + def.symbol("expect-empty-stack") do (i: In): + let l = i.stack.len + if l != 0: + raiseInvalid("Expected empty stack, found $# elements instead" % $l) + + def.symbol("exit") do (i: In): + let vals = i.expect("int") + quit(vals[0].intVal.int) + + def.symbol("puts") do (i: In): + let a = i.peek + echo $$a + + def.symbol("puts!") do (i: In): + echo $$i.pop + + def.symbol("gets") do (i: In) {.gcsafe.}: + when defined(mini): + i.push readLineFromStdin("").newVal + else: + var ed = initEditor() + i.push ed.readLine().newVal + + def.symbol("apply") do (i: In): + let vals = i.expect("quot|dict") + var prog = vals[0] + if prog.kind == minQuotation: + i.apply prog + else: + i.push i.applyDict(prog) + + def.symbol("symbols") do (i: In): + var q = newSeq[MinValue](0) + var scope = i.scope + while not scope.isNil: + for s in scope.symbols.keys: + q.add s.newVal + scope = scope.parent + i.push q.newVal + + def.symbol("defined?") do (i: In): + let vals = i.expect("'sym") + i.push(i.scope.hasSymbol(vals[0].getString).newVal) + + def.symbol("defined-sigil?") do (i: In): + let vals = i.expect("'sym") + i.push(i.scope.hasSigil(vals[0].getString).newVal) + + def.symbol("sigils") do (i: In): + var q = newSeq[MinValue](0) + var scope = i.scope + while not scope.isNil: + for s in scope.sigils.keys: + q.add s.newVal + scope = scope.parent + i.push q.newVal + + def.symbol("scope-symbols") do (i: In): + let vals = i.expect("dict") + let m = vals[0] + var q = newSeq[MinValue](0) + for s in m.scope.symbols.keys: + q.add s.newVal + i.push q.newVal + + def.symbol("scope-sigils") do (i: In): + let vals = i.expect("dict") + let m = vals[0] + var q = newSeq[MinValue](0) + for s in m.scope.sigils.keys: + q.add s.newVal + i.push q.newVal + + def.symbol("lite?") do (i: In): + i.push defined(lite).newVal + + def.symbol("mini?") do (i: In): + i.push defined(mini).newVal + + def.symbol("from-yaml") do (i: In): + let vals = i.expect("string") + let s = vals[0] + try: + var dict = newDict(i.scope) + let lines = s.strVal.split("\n") + for line in lines: + let pair = line.split(":") + if pair.len == 1 and pair[0].len == 0: + continue + i.dset(dict, pair[0].strip, pair[1].strip.newVal) + i.push(dict) + except: + raiseInvalid("Invalid/unsupported YAML object (only dictionaries with string values are supported)") + + def.symbol("to-yaml") do (i: In): + let vals = i.expect "a" + let a = vals[0] + let err = "YAML conversion is only supported from dictionaries with string values" + if a.kind != minDictionary: + raiseInvalid(err) + var yaml = "" + try: + for key in i.keys(a).qVal: + let value = i.dget(a, key) + if value.kind != minString: + raiseInvalid(err) + yaml &= "$1: $2\n" % [key.strVal, value.strVal] + i.push(yaml.strip.newVal) + except: + raiseInvalid(err) + + def.symbol("loglevel") do (i: In): + let vals = i.expect("'sym") + let s = vals[0] + var str = s.getString + when defined(mini): + echo "Log level: ", minilogger.setLogLevel(str) + else: + echo "Log level: ", niftylogger.setLogLevel(str) + + def.symbol("loglevel?") do (i: In): + when defined(mini): + i.push minilogger.getLogLevel().newVal + else: + i.push niftylogger.getLogLevel().newVal + + # Language constructs + + def.symbol("define") do (i: In): + let vals = i.expect("'sym", "a") + let sym = vals[0] + var q1 = vals[1] # existing (auto-quoted) + var symbol: string + var isQuot = true + if not q1.isQuotation: + q1 = @[q1].newVal + isQuot = false + symbol = sym.getString + when not defined(mini): + if not symbol.match USER_SYMBOL_REGEX: + raiseInvalid("Symbol identifier '$1' contains invalid characters." % symbol) + info "[define] $1 = $2" % [symbol, $q1] + if i.scope.symbols.hasKey(symbol) and i.scope.symbols[symbol].sealed: + raiseUndefined("Attempting to redefine sealed symbol '$1'" % [symbol]) + i.scope.symbols[symbol] = MinOperator(kind: minValOp, val: q1, sealed: false, quotation: isQuot) + + def.symbol("define-sigil") do (i: In): + let vals = i.expect("'sym", "a") + let sym = vals[0] + var q1 = vals[1] # existing (auto-quoted) + var sigil: string + var isQuot = true + if not q1.isQuotation: + q1 = @[q1].newVal + isQuot = false + sigil = sym.getString + when not defined(mini): + if not sigil.match USER_SYMBOL_REGEX: + raiseInvalid("Sigil identifier '$1' contains invalid characters." % sigil) + info "[sigil] $1 = $2" % [sigil, $q1] + if i.scope.sigils.hasKey(sigil) and i.scope.sigils[sigil].sealed: + raiseUndefined("Attempting to redefine sealed sigil '$1'" % [sigil]) + i.scope.sigils[sigil] = MinOperator(kind: minValOp, val: q1, sealed: true, quotation: isQuot) + + def.symbol("bind") do (i: In): + let vals = i.expect("'sym", "a") + let sym = vals[0] + var q1 = vals[1] # existing (auto-quoted) + var symbol: string + var isQuot = true + if not q1.isQuotation: + q1 = @[q1].newVal + isQuot = false + symbol = sym.getString + info "[bind] $1 = $2" % [symbol, $q1] + let res = i.scope.setSymbol(symbol, MinOperator(kind: minValOp, val: q1, quotation: isQuot)) + if not res: + raiseUndefined("Attempting to bind undefined symbol: " & symbol) + + def.symbol("delete") do (i: In): + let vals = i.expect("'sym") + let sym = vals[0] + let res = i.scope.delSymbol(sym.getString) + if not res: + raiseUndefined("Attempting to delete undefined symbol: " & sym.getString) + + def.symbol("delete-sigil") do (i: In): + let vals = i.expect("'sym") + let sym = vals[0] + let res = i.scope.delSigil(sym.getString) + if not res: + raiseUndefined("Attempting to delete undefined sigil: " & sym.getString) + + def.symbol("module") do (i: In): + let vals = i.expect("'sym", "dict") + let name = vals[0] + var code = vals[1] + code.objType = "module" + code.filename = i.filename + info("[module] $1 ($2 symbols)" % [name.getString, $code.scope.symbols.len]) + i.scope.symbols[name.getString] = MinOperator(kind: minValOp, val: code) + + def.symbol("scope") do (i: In): + var dict = newDict(i.scope.parent) + dict.objType = "module" + dict.filename = i.filename + dict.scope = i.scope + i.push dict + + def.symbol("type") do (i: In): + let vals = i.expect("a") + i.push vals[0].typeName.newVal + + def.symbol("import") do (i: In): + var vals = i.expect("'sym") + let rawName = vals[0] + var name: string + name = rawName.getString + var op = i.scope.getSymbol(name) + i.apply(op) + vals = i.expect("dict:module") + let mdl = vals[0] + info("[import] Importing: $1 ($2 symbols, $3 sigils)" % [name, $mdl.scope.symbols.len, $mdl.scope.sigils.len]) + for sym, val in mdl.scope.symbols.pairs: + if i.scope.symbols.hasKey(sym) and i.scope.symbols[sym].sealed: + raiseUndefined("Attempting to redefine sealed symbol '$1'" % [sym]) + i.debug "[import] $1" % [sym] + i.scope.symbols[sym] = val + for sig, val in mdl.scope.sigils.pairs: + if i.scope.sigils.hasKey(sig) and i.scope.sigils[sig].sealed: + raiseUndefined("Attempting to redefine sealed sigil '$1'" % [sig]) + i.debug "[import] $1" % [sig] + i.scope.sigils[sig] = val + + def.symbol("eval") do (i: In): + let vals = i.expect("string") + let s = vals[0] + i.eval s.strVal + + def.symbol("quit") do (i: In): + i.push 0.newVal + i.pushSym "exit" + + def.symbol("parse") do (i: In): + let vals = i.expect("string") + let s = vals[0] + i.push i.parse s.strVal + + def.symbol("source") do (i: In): + let vals = i.expect("'sym") + let s = vals[0] + let str = s.getString + let sym = i.scope.getSymbol(str) + if sym.kind == minValOp: + i.push sym.val + else: + raiseInvalid("No source available for native symbol '$1'." % str) + + def.symbol("call") do (i: In): + let vals = i.expect("'sym", "dict") + let symbol = vals[0] + let q = vals[1] + let s = symbol.getString + let origScope = i.scope + i.scope = q.scope + i.scope.parent = origScope + let sym = i.scope.getSymbol(s) + i.apply(sym) + i.scope = origScope + + def.symbol("invoke") do (i: In): + let vals = i.expect("'sym") + let s = vals[0].getString + let parts = s.split("/") + if parts.len < 2: + raiseInvalid("Dictionary identifier not specified") + i.pushSym parts[0] + for p in 0..parts.len-2: + let vals = i.expect("dict") + let mdl = vals[0] + let symId = parts[p+1] + i.push mdl + i.push symId.newVal + i.pushSym "call" + + def.symbol("set-type") do (i: In): + let vals = i.expect("'sym", "dict") + let symbol = vals[0] + var d = vals[1] + d.objType = symbol.getString + i.push d + + def.symbol("raise") do (i: In): + let vals = i.expect("dict") + let err = vals[0] + if err.dhas("error".newVal) and err.dhas("message".newVal): + raiseRuntime("($1) $2" % [i.dget(err, "error".newVal).getString, i.dget(err, "message").getString], err) + else: + raiseInvalid("Invalid error dictionary") + + def.symbol("format-error") do (i: In): + let vals = i.expect("dict:error") + let err = vals[0] + if err.dhas("error".newVal) and err.dhas("message".newVal): + var msg: string + var list = newSeq[MinValue]() + list.add i.dget(err, "message") + if err.dhas("symbol"): + list.add i.dget(err, "symbol") + if err.dhas("filename"): + list.add i.dget(err, "filename") + if err.dhas("line"): + list.add i.dget(err, "line") + if err.dhas("column"): + list.add i.dget(err, "column") + if list.len <= 3: + msg = "$1" % $$list[0] + else: + msg = "$3($4,$5) `$2`: $1" % [$$list[0], $$list[1], $$list[2], $$list[3], $$list[4]] + i.push msg.newVal + else: + raiseInvalid("Invalid error dictionary") + + def.symbol("try") do (i: In): + let vals = i.expect("quot") + let prog = vals[0] + if prog.qVal.len == 0: + raiseInvalid("Quotation must contain at least one element") + var code = prog.qVal[0] + var final, catch: MinValue + var hasFinally = false + var hasCatch = false + if prog.qVal.len > 1: + catch = prog.qVal[1] + hasCatch = true + if prog.qVal.len > 2: + final = prog.qVal[2] + hasFinally = true + if (not code.isQuotation) or (hasCatch and not catch.isQuotation) or (hasFinally and not final.isQuotation): + raiseInvalid("Quotation must contain at least one quotation") + try: + i.dequote(code) + except MinRuntimeError: + if not hasCatch: + return + let e = (MinRuntimeError)getCurrentException() + i.push e.data + i.dequote(catch) + except: + if not hasCatch: + return + let e = getCurrentException() + var res = newDict(i.scope) + var err = $e.name + let col = err.find(":") + if col >= 0: + err = err[0..col-1] + res.objType = "error" + i.dset(res, "error", err.newVal) + i.dset(res, "message", e.msg.newVal) + if i.currSym.getString != "": # TODO investigate when this happens + i.dset(res, "symbol", i.currSym) + i.dset(res, "filename", i.currSym.filename.newVal) + i.dset(res, "line", i.currSym.line.newVal) + i.dset(res, "column", i.currSym.column.newVal) + i.push res + i.dequote(catch) + finally: + if hasFinally: + i.dequote(final) + + def.symbol("quote") do (i: In): + let vals = i.expect("a") + let a = vals[0] + i.push @[a].newVal + + def.symbol("dequote") do (i: In): + let vals = i.expect("quot") + var q = vals[0] + i.dequote(q) + + def.symbol("tap") do (i: In): + let vals = i.expect("quot", "a") + let programs = vals[0] + var a = vals[1] + for program in programs.qVal: + var p = program + i.push(a) + i.dequote(p) + a = i.pop + i.push(a) + + def.symbol("tap!") do (i: In): + let vals = i.expect("quot", "a") + let programs = vals[0] + var a = vals[1] + for program in programs.qVal: + var p = program + i.push(a) + i.dequote(p) + a = i.pop + + # Conditionals + + def.symbol("if") do (i: In): + let vals = i.expect("quot", "quot", "quot") + var fpath = vals[0] + var tpath = vals[1] + var check = vals[2] + var stack = i.stack + i.dequote(check) + let res = i.pop + i.stack = stack + if not res.isBool: + raiseInvalid("Result of check is not a boolean value") + if res.boolVal == true: + i.dequote(tpath) + else: + i.dequote(fpath) + + def.symbol("when") do (i: In): + let vals = i.expect("quot", "quot") + var tpath = vals[0] + var check = vals[1] + var stack = i.stack + i.dequote(check) + let res = i.pop + i.stack = stack + if not res.isBool: + raiseInvalid("Result of check is not a boolean value") + if res.boolVal == true: + i.dequote(tpath) + + def.symbol("unless") do (i: In): + let vals = i.expect("quot", "quot") + var tpath = vals[0] + var check = vals[1] + var stack = i.stack + i.dequote(check) + let res = i.pop + i.stack = stack + if not res.isBool: + raiseInvalid("Result of check is not a boolean value") + if res.boolVal == false: + i.dequote(tpath) + + # 4 ( + # ((> 3) ("Greater than 3" put!)) + # ((< 3) ("Smaller than 3" put!)) + # ((true) ("Exactly 3" put!)) + # ) case + def.symbol("case") do (i: In): + let vals = i.expect("quot") + var cases = vals[0] + if cases.qVal.len == 0: + raiseInvalid("Empty case operator") + var k = 0 + let stack = i.stack + for c in cases.qVal: + i.stack = stack + if not c.isQuotation: + raiseInvalid("A quotation of quotations is required") + k.inc + if c.qVal.len != 2 or not c.qVal[0].isQuotation or not c.qVal[1].isQuotation: + raiseInvalid("Inner quotations in case operator must contain two quotations") + var q = c.qVal[0] + i.dequote(q) + let res = i.pop + if not res.isBool(): + raiseInvalid("Result of case #$1 is not a boolean value" % $k) + if res.boolVal == true: + var t = c.qVal[1] + i.dequote(t) + break + + # Loops + + def.symbol("foreach") do (i: In): + let vals = i.expect("quot", "quot") + var prog = vals[0] + var list = vals[1] + for litem in list.qVal: + i.push litem + i.dequote(prog) + + def.symbol("times") do (i: In): + let vals = i.expect("int", "quot") + var t = vals[0] + var prog = vals[1] + if t.intVal < 1: + raiseInvalid("A non-zero natural number is required") + for c in 1..t.intVal: + i.dequote(prog) + + def.symbol("while") do (i: In): + let vals = i.expect("quot", "quot") + var d = vals[0] + var b = vals[1] + for e in b.qVal: + i.push e + i.dequote(b) + var check = i.pop + while check.isBool and check.boolVal == true: + i.dequote(d) + i.dequote(b) + check = i.pop + discard i.pop + + # Other + + def.symbol("linrec") do (i: In): + let vals = i.expect("quot", "quot", "quot", "quot") + var r2 = vals[0] + var r1 = vals[1] + var t = vals[2] + var p = vals[3] + proc linrec(i: In, p, t, r1, r2: var MinValue) = + i.dequote(p) + var check = i.pop + if check.isBool and check.boolVal == true: + i.dequote(t) + else: + i.dequote(r1) + i.linrec(p, t, r1, r2) + i.dequote(r2) + i.linrec(p, t, r1, r2) + + def.symbol("version") do (i: In): + i.push pkgVersion.newVal + + def.symbol("seal") do (i: In): + let vals = i.expect("'sym") + let sym = vals[0].getString + var s = i.scope.getSymbol(sym) + s.sealed = true + i.scope.setSymbol(sym, s) + + def.symbol("seal-sigil") do (i: In): + let vals = i.expect("'sym") + let sym = vals[0].getString + var s = i.scope.getSigil(sym) + s.sealed = true + i.scope.setSigil(sym, s) + + def.symbol("unseal") do (i: In): + let vals = i.expect("'sym") + let sym = vals[0].getString + var s = i.scope.getSymbol(sym) + s.sealed = false + i.scope.setSymbol(sym, s, true) + + def.symbol("unseal-sigil") do (i: In): + let vals = i.expect("'sym") + let sym = vals[0].getString + var s = i.scope.getSigil(sym) + when not defined(mini): + if not sym.match USER_SYMBOL_REGEX: + # Prevent accidentally unsealing system sigils + # Not that they can redefined, but still + raiseInvalid("Attempting to unseal system sigil: " & sym) + s.sealed = false + i.scope.setSigil(sym, s, true) + + def.symbol("quote-bind") do (i: In): + let vals = i.expect("string", "a") + let s = vals[0] + let m = vals[1] + i.push @[m].newVal + i.push s + i.pushSym "bind" + + def.symbol("quote-define") do (i: In): + let vals = i.expect("string", "a") + let s = vals[0] + let m = vals[1] + i.push @[m].newVal + i.push s + i.pushSym "define" + + + def.symbol("args") do (i: In): + var args = newSeq[MinValue](0) + for kind, key, val in getopt(): + case kind: + of cmdArgument: + args.add key.newVal + else: + discard + i.push args.newVal + + def.symbol("opts") do (i: In): + var opts = newDict(i.scope) + for kind, key, val in getopt(): + case kind: + of cmdLongOption, cmdShortOption: + if val == "": + opts = i.dset(opts, key.newVal, true.newVal) + else: + opts = i.dset(opts, key.newVal, val.newVal) + else: + discard + i.push opts + + def.symbol("expect") do (i: In): + var q: MinValue + i.reqQuotationOfSymbols q + i.push(i.expect(q.qVal.mapIt(it.getString())).reversed.newVal) + + def.symbol("reverse-expect-dequote") do (i: In): + var q: MinValue + i.reqQuotationOfSymbols q + var req = i.expect(q.qVal.reversed.mapIt(it.getString())).newVal + i.dequote(req) + + def.symbol("infix-dequote") do (i: In): + let vals = i.expect("quot") + let q = vals[0] + proc infix(i: In, q: MinValue): MinValue = + var ops = newSeq[MinValue](0) + var res = newSeq[MinValue](0).newVal + for x in q.qVal: + if x.isSymbol: + ops.add x + else: + if x.isQuotation: + res.qVal.add i.infix(x) + else: + res.qVal.add x + if ops.len > 0: + res.qVal.add ops.pop + i.dequote(res) + res = newSeq[MinValue](0).newVal + return i.pop + i.push i.infix(q) + + def.symbol("prefix-dequote") do (i: In): + let vals = i.expect("quot") + var q = vals[0] + q.qVal.reverse + i.dequote(q) + + def.symbol("compiled?") do (i: In): + i.push MINCOMPILED.newVal + + # Converters + + def.symbol("string") do (i: In): + let s = i.pop + i.push(($$s).newVal) + + def.symbol("bool") do (i: In): + let v = i.pop + let strcheck = (v.isString and (v.getString == "false" or v.getString == "")) + let intcheck = v.isInt and v.intVal == 0 + let floatcheck = v.isFloat and v.floatVal == 0 + let boolcheck = v.isBool and v.boolVal == false + let quotcheck = v.isQuotation and v.qVal.len == 0 + if v.isNull or strcheck or intcheck or floatcheck or boolcheck or quotcheck: + i.push false.newVal + else: + i.push true.newVal + + def.symbol("int") do (i: In): + let s = i.pop + if s.isString: + i.push s.getString.parseInt.newVal + elif s.isNull: + i.push 0.int.newVal + elif s.isFloat: + i.push s.floatVal.int.newVal + elif s.isInt: + i.push s + elif s.isBool: + if s.boolVal == true: + i.push 1.int.newVal + else: + i.push 0.int.newVal + else: + raiseInvalid("Cannot convert a quotation to an integer.") + + def.symbol("float") do (i: In): + let s = i.pop + if s.isString: + i.push s.getString.parseFloat.newVal + elif s.isNull: + i.push 0.int.newVal + elif s.isInt: + i.push s.intVal.float.newVal + elif s.isFloat: + i.push s + elif s.isBool: + if s.boolVal == true: + i.push 1.float.newVal + else: + i.push 0.float.newVal + else: + raiseInvalid("Cannot convert a quotation to float.") + + def.symbol("prompt") do (i: In): + when defined(mini): + i.push "$ ".newVal + else: + i.eval(""""[$1]\n$$ " (.) => %""") + + # Sigils + + def.sigil("'") do (i: In): + let vals = i.expect("string") + let s = vals[0] + i.push(@[i.newSym(s.strVal)].newVal) + + def.sigil(":") do (i: In): + i.pushSym("define") + + def.sigil("~") do (i: In): + i.pushSym("delete") + + def.sigil("@") do (i: In): + i.pushSym("bind") + + def.sigil("+") do (i: In): + i.pushSym("module") + + def.sigil("^") do (i: In): + i.pushSym("call") + + def.sigil("*") do (i: In): + i.pushSym("invoke") + + def.sigil(">") do (i: In): + i.pushSym("save-symbol") + + def.sigil("<") do (i: In): + i.pushSym("load-symbol") + + def.sigil("#") do (i: In): + i.pushSym("quote-bind") + + def.sigil("=") do (i: In): + i.pushSym("quote-define") + + # Shorthand symbol aliases + + def.symbol("#") do (i: In): + i.pushSym("quote-bind") + + def.symbol("=") do (i: In): + i.pushSym("quote-define") + + def.symbol("=-=") do (i: In): + i.pushSym("expect-empty-stack") + + def.symbol(":") do (i: In): + i.pushSym("define") + + def.symbol("@") do (i: In): + i.pushSym("bind") + + def.symbol("^") do (i: In): + i.pushSym("call") + + def.symbol("'") do (i: In): + i.pushSym("quote") + + def.symbol("->") do (i: In): + i.pushSym("dequote") + + def.symbol("--") do (i: In): + i.pushSym("reverse-expect-dequote") + + def.symbol("::") do (i: In): + i.pushSym("operator") + + def.symbol("=>") do (i: In): + i.pushSym("apply") + + def.symbol("==>") do (i: In): + discard # used within operator defs + + def.symbol("return") do (i: In): + discard # used within operator defs + + def.symbol(">>") do (i: In): + i.pushSym("prefix-dequote") + + def.symbol("><") do (i: In): + i.pushSym("infix-dequote") + + def.finalize("ROOT")
A minpkg/lib/min_logic.nim

@@ -0,0 +1,269 @@

+import + math +import + ../core/parser, + ../core/value, + ../core/interpreter, + ../core/utils + +proc floatCompare(n1, n2: MinValue): bool = + let + a:float = if n1.kind != minFloat: n1.intVal.float else: n1.floatVal + b:float = if n2.kind != minFloat: n2.intVal.float else: n2.floatVal + if a.classify == fcNan and b.classify == fcNan: + return true + else: + const + FLOAT_MIN_NORMAL = 2e-1022 + FLOAT_MAX_VALUE = (2-2e-52)*2e1023 + epsilon = 0.00001 + let + absA = abs(a) + absB = abs(b) + diff = abs(a - b) + + if a == b: + return true + elif a == 0 or b == 0 or diff < FLOAT_MIN_NORMAL: + return diff < (epsilon * FLOAT_MIN_NORMAL) + else: + return diff / min((absA + absB), FLOAT_MAX_VALUE) < epsilon + +proc logic_module*(i: In)= + let def = i.define() + + def.symbol(">") do (i: In): + var n1, n2: MinValue + i.reqTwoNumbersOrStrings n2, n1 + 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) + else: + i.push newVal(n1.strVal > n2.strVal) + + def.symbol(">=") do (i: In): + var n1, n2: MinValue + i.reqTwoNumbersOrStrings n2, n1 + 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 or floatCompare(n1, n2)) + elif n1.isFloat and n2.isFloat: + i.push newVal(n1.floatVal > n2.floatVal or floatCompare(n1, n2)) + elif n1.isFloat and n2.isInt: + i.push newVal(n1.floatVal > n2.intVal.float or floatCompare(n1, n2)) + else: + i.push newVal(n1.strVal >= n2.strVal) + + def.symbol("<") do (i: In): + var n1, n2: MinValue + i.reqTwoNumbersOrStrings n1, n2 + 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) + else: + i.push newVal(n1.strVal > n2.strVal) + + def.symbol("<=") do (i: In): + var n1, n2: MinValue + i.reqTwoNumbersOrStrings n1, n2 + 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 or floatCompare(n1, n2)) + elif n1.isFloat and n2.isFloat: + i.push newVal(n1.floatVal > n2.floatVal or floatCompare(n1, n2)) + elif n1.isFloat and n2.isInt: + i.push newVal(n1.floatVal > n2.intVal.float or floatCompare(n1, n2)) + else: + i.push newVal(n1.strVal >= n2.strVal) + + def.symbol("==") do (i: In): + var n1, n2: MinValue + let vals = i.expect("a", "a") + n1 = vals[0] + n2 = vals[1] + if (n1.kind == minFloat or n2.kind == minFloat) and n1.isNumber and n2.isNumber: + i.push newVal(floatCompare(n1, n2)) + else: + i.push newVal(n1 == n2) + + def.symbol("!=") do (i: In): + var n1, n2: MinValue + let vals = i.expect("a", "a") + n1 = vals[0] + n2 = vals[1] + if (n1.kind == minFloat or n2.kind == minFloat) and n1.isNumber and n2.isNumber: + i.push newVal(not floatCompare(n1, n2)) + i.push newVal(not (n1 == n2)) + + def.symbol("not") do (i: In): + let vals = i.expect("bool") + let b = vals[0] + i.push newVal(not b.boolVal) + + def.symbol("and") do (i: In): + let vals = i.expect("bool", "bool") + let a = vals[0] + let b = vals[1] + i.push newVal(a.boolVal and b.boolVal) + + def.symbol("dequote-and") do (i: In): + let vals = i.expect("a", "a") + var a = vals[0] + var b = vals[1] + i.dequote(b) + let resB = i.pop + if (resB.isBool and resB.boolVal == false): + i.push(false.newVal) + else: + i.dequote(a) + let resA = i.pop + if not resA.isBool: + raiseInvalid("Result of first quotation is not a boolean value") + if not resB.isBool: + raiseInvalid("Result of second quotation is not a boolean value") + i.push newVal(resA.boolVal and resB.boolVal) + + def.symbol("expect-all") do (i: In): + let vals = i.expect("quot") + let q = vals[0] + var c = 0 + for v in q.qVal: + if not v.isQuotation: + raiseInvalid("A quotation of quotations is expected") + var vv = v + i.dequote vv + let r = i.pop + c.inc() + if not r.isBool: + raiseInvalid("Quotation #$# does not evaluate to a boolean value") + if not r.boolVal: + i.push r + return + i.push true.newVal + + def.symbol("or") do (i: In): + let vals = i.expect("bool", "bool") + let a = vals[0] + let b = vals[1] + i.push newVal(a.boolVal or b.boolVal) + + def.symbol("dequote-or") do (i: In): + let vals = i.expect("a", "a") + var a = vals[0] + var b = vals[1] + i.dequote(b) + let resB = i.pop + if (resB.isBool and resB.boolVal == true): + i.push(true.newVal) + else: + i.dequote(a) + let resA = i.pop + if not resA.isBool: + raiseInvalid("Result of first quotation is not a boolean value") + if resB.isBool: + raiseInvalid("Result of second quotation is not a boolean value") + i.push newVal(resA.boolVal and resB.boolVal) + + def.symbol("expect-any") do (i: In): + let vals = i.expect("quot") + let q = vals[0] + var c = 0 + for v in q.qVal: + if not v.isQuotation: + raiseInvalid("A quotation of quotations is expected") + var vv = v + i.dequote vv + let r = i.pop + c.inc() + if not r.isBool: + raiseInvalid("Quotation #$# does not evaluate to a boolean value") + if r.boolVal: + i.push r + return + i.push false.newVal + + def.symbol("xor") do (i: In): + let vals = i.expect("bool", "bool") + let a = vals[0] + let b = vals[1] + i.push newVal(a.boolVal xor b.boolVal) + + def.symbol("string?") do (i: In): + if i.pop.kind == minString: + i.push true.newVal + else: + i.push false.newVal + + def.symbol("integer?") do (i: In): + if i.pop.kind == minInt: + i.push true.newVal + else: + i.push false.newVal + + def.symbol("float?") do (i: In): + if i.pop.kind == minFloat: + i.push true.newVal + else: + i.push false.newVal + + def.symbol("null?") do (i: In): + if i.pop.kind == minNull: + i.push true.newVal + else: + i.push false.newVal + + def.symbol("number?") do (i: In): + let a = i.pop + if a.kind == minFloat or a.kind == minInt: + i.push true.newVal + else: + i.push false.newVal + + def.symbol("boolean?") do (i: In): + if i.pop.kind == minBool: + i.push true.newVal + else: + i.push false.newVal + + def.symbol("quotation?") do (i: In): + if i.pop.kind == minQuotation: + i.push true.newVal + else: + i.push false.newVal + + def.symbol("dictionary?") do (i: In): + if i.pop.isDictionary: + i.push true.newVal + else: + i.push false.newVal + + def.symbol("type?") do (i: In): + let vals = i.expect("'sym", "a") + if vals[1].isTypedDictionary(vals[0].getString): + i.push true.newVal + else: + i.push (vals[1].typename == vals[0].getString).newVal + + def.symbol("&&") do (i: In): + i.pushSym("expect-all") + + def.symbol("||") do (i: In): + i.pushSym("expect-any") + + def.finalize("logic")
A minpkg/lib/min_math.nim

@@ -0,0 +1,110 @@

+import + math, + strformat, + strutils +import + ../core/parser, + ../core/value, + ../core/interpreter, + ../core/utils + +proc math_module*(i: In)= + + let def = i.define() + + def.symbol("floor") do (i: In): + let vals = i.expect("num") + i.push vals[0].getFloat.floor.newVal + + def.symbol("ceil") do (i: In): + let vals = i.expect("num") + i.push vals[0].getFloat.ceil.newVal + + def.symbol("trunc") do (i: In): + let vals = i.expect("num") + i.push vals[0].getFloat.trunc.newVal + + def.symbol("round") do (i: In): + let vals = i.expect("int", "num") + let places = vals[0].intVal.int + let n = vals[1].getFloat + var res = "" + formatValue(res, n, "." & $places & "f") + i.push parseFloat(res).newVal + + def.symbol("e") do (i: In): + i.push E.newVal + + def.symbol("pi") do (i: In): + i.push PI.newVal + + def.symbol("tau") do (i: In): + i.push TAU.newVal + + def.symbol("ln") do (i: In): + let vals = i.expect("num") + i.push vals[0].getFloat.ln.newVal + + def.symbol("log2") do (i: In): + let vals = i.expect("num") + i.push vals[0].getFloat.log2.newVal + + def.symbol("log10") do (i: In): + let vals = i.expect("num") + i.push vals[0].getFloat.log10.newVal + + def.symbol("pow") do (i: In): + let vals = i.expect("num", "num") + let y = vals[0].getFloat + let x = vals[1].getFloat + i.push x.pow(y).newVal + + def.symbol("sqrt") do (i: In): + let vals = i.expect("num") + i.push vals[0].getFloat.sqrt.newVal + + def.symbol("sin") do (i: In): + let vals = i.expect("num") + i.push vals[0].getFloat.sin.newVal + + def.symbol("cos") do (i: In): + let vals = i.expect("num") + i.push vals[0].getFloat.cos.newVal + + def.symbol("tan") do (i: In): + let vals = i.expect("num") + i.push vals[0].getFloat.tan.newVal + + def.symbol("sinh") do (i: In): + let vals = i.expect("num") + i.push vals[0].getFloat.sinh.newVal + + def.symbol("cosh") do (i: In): + let vals = i.expect("num") + i.push vals[0].getFloat.cosh.newVal + + def.symbol("tanh") do (i: In): + let vals = i.expect("num") + i.push vals[0].getFloat.tanh.newVal + + def.symbol("asin") do (i: In): + let vals = i.expect("num") + i.push vals[0].getFloat.arcsin.newVal + + def.symbol("acos") do (i: In): + let vals = i.expect("num") + i.push vals[0].getFloat.arccos.newVal + + def.symbol("atan") do (i: In): + let vals = i.expect("num") + i.push vals[0].getFloat.arctan.newVal + + def.symbol("d2r") do (i: In): + let vals = i.expect("num") + i.push vals[0].getFloat.degToRad.newVal + + def.symbol("r2g") do (i: In): + let vals = i.expect("num") + i.push vals[0].getFloat.radToDeg.newVal + + def.finalize("math")
A minpkg/lib/min_num.nim

@@ -0,0 +1,138 @@

+import + random +import + ../core/parser, + ../core/value, + ../core/interpreter, + ../core/utils + +proc num_module*(i: In)= + + let def = i.define() + + def.symbol("nan") do (i: In): + i.push newVal(NaN) + + def.symbol("inf") do (i: In): + i.push newVal(Inf) + + def.symbol("-inf") do (i: In): + i.push newVal(NegInf) + + def.symbol("+") do (i: In): + let vals = i.expect("num", "num") + let a = vals[0] + let b = vals[1] + if a.isInt: + if b.isInt: + i.push newVal(a.intVal + b.intVal) + else: + i.push newVal(a.intVal.float + b.floatVal) + else: + if b.isFloat: + i.push newVal(a.floatVal + b.floatVal) + else: + i.push newVal(a.floatVal + b.intVal.float) + + def.symbol("-") do (i: In): + let vals = i.expect("num", "num") + let a = vals[0] + let b = vals[1] + if a.isInt: + if b.isInt: + i.push newVal(b.intVal - a.intVal) + else: + i.push newVal(b.floatVal - a.intVal.float) + else: + if b.isFloat: + i.push newVal(b.floatVal - a.floatVal) + else: + i.push newVal(b.intVal.float - a.floatVal) + + def.symbol("*") do (i: In): + let vals = i.expect("num", "num") + let a = vals[0] + let b = vals[1] + if a.isInt: + if b.isInt: + i.push newVal(a.intVal * b.intVal) + else: + i.push newVal(a.intVal.float * b.floatVal) + else: + if b.isFloat: + i.push newVal(a.floatVal * b.floatVal) + else: + i.push newVal(a.floatVal * b.intVal.float) + + def.symbol("/") do (i: In): + let vals = i.expect("num", "num") + let a = vals[0] + let b = vals[1] + if a.isInt: + if b.isInt: + i.push newVal(b.intVal.int / a.intVal.int) + else: + i.push newVal(b.floatVal / a.intVal.float) + else: + if b.isFloat: + i.push newVal(b.floatVal / a.floatVal) + else: + i.push newVal(b.intVal.float / a.floatVal) + + def.symbol("randomize") do (i: In): + randomize() + + def.symbol("random") do (i: In): + let vals = i.expect("int") + let n = vals[0] + i.push n.intVal.int.rand.newVal + + def.symbol("div") do (i: In): + let vals = i.expect("int", "int") + let b = vals[0] + let a = vals[1] + i.push(newVal(a.intVal div b.intVal)) + + def.symbol("mod") do (i: In): + let vals = i.expect("int", "int") + let b = vals[0] + let a = vals[1] + i.push(newVal(a.intVal mod b.intVal)) + + def.symbol("succ") do (i: In): + let vals = i.expect("int") + let n = vals[0] + i.push newVal(n.intVal + 1) + + def.symbol("pred") do (i: In): + let vals = i.expect("int") + let n = vals[0] + i.push newVal(n.intVal - 1) + + def.symbol("even?") do (i: In): + let vals = i.expect("int") + let n = vals[0] + i.push newVal(n.intVal mod 2 == 0) + + def.symbol("odd?") do (i: In): + let vals = i.expect("int") + let n = vals[0] + i.push newVal(n.intVal mod 2 != 0) + + def.symbol("sum") do (i: In): + var s: MinValue + i.reqQuotationOfNumbers s + var c = 0.float + var isInt = true + for n in s.qVal: + if n.isFloat: + isInt = false + c = + n.floatVal + else: + c = c + n.intVal.float + if isInt: + i.push c.int.newVal + else: + i.push c.newVal + + def.finalize("num")
A minpkg/lib/min_str.nim

@@ -0,0 +1,272 @@

+import + strutils, + sequtils +import + ../core/parser, + ../core/value, + ../core/interpreter, + ../core/baseutils, + ../core/utils + +when not defined(mini): + import + ../packages/nim-sgregex/sgregex, + uri + +proc str_module*(i: In) = + let def = i.define() + + def.symbol("interpolate") do (i: In): + let vals = i.expect("quot", "string") + var q = vals[0] + let s = vals[1] + var strings = newSeq[string](0) + for el in q.qVal: + strings.add $$el + let res = s.strVal % strings + i.push res.newVal + + def.symbol("apply-interpolate") do (i: In): + i.pushSym "apply" + i.pushSym "interpolate" + + def.symbol("strip") do (i: In): + let vals = i.expect("'sym") + let s = vals[0] + i.push s.getString.strip.newVal + + def.symbol("substr") do (i: In): + let vals = i.expect("int", "int", "'sym") + let length = vals[0].intVal + let start = vals[1].intVal + let s = vals[2].getString + let index = min(start+length-1, s.len-1) + i.push s[start..index].newVal + + def.symbol("split") do (i: In): + let vals = i.expect("'sym", "'sym") + let sep = vals[0].getString + let s = vals[1].getString + var q = newSeq[MinValue](0) + if (sep == ""): + for c in s: + q.add ($c).newVal + else: + for e in s.split(sep): + q.add e.newVal + i.push q.newVal + + def.symbol("join") do (i: In): + let vals = i.expect("'sym", "quot") + let s = vals[0] + let q = vals[1] + i.push q.qVal.mapIt($$it).join(s.getString).newVal + + def.symbol("length") do (i: In): + let vals = i.expect("'sym") + let s = vals[0] + i.push s.getString.len.newVal + + def.symbol("lowercase") do (i: In): + let vals = i.expect("'sym") + let s = vals[0] + i.push s.getString.toLowerAscii.newVal + + def.symbol("uppercase") do (i: In): + let vals = i.expect("'sym") + let s = vals[0] + i.push s.getString.toUpperAscii.newVal + + def.symbol("capitalize") do (i: In): + let vals = i.expect("'sym") + let s = vals[0] + i.push s.getString.capitalizeAscii.newVal + + def.symbol("ord") do (i: In): + let vals = i.expect("'sym") + let s = vals[0] + if s.getString.len != 1: + raiseInvalid("Symbol ord requires a string containing a single character.") + i.push s.getString[0].ord.newVal + + def.symbol("chr") do (i: In): + let vals = i.expect("int") + let n = vals[0] + let c = n.intVal.chr + i.push ($c).newVal + + def.symbol("titleize") do (i: In): + let vals = i.expect("'sym") + let s = vals[0] + i.push s.getString.split(" ").mapIt(it.capitalizeAscii).join(" ").newVal + + def.symbol("repeat") do (i: In): + let vals = i.expect("int", "string") + let n = vals[0] + let s = vals[1] + i.push s.getString.repeat(n.intVal).newVal + + def.symbol("indent") do (i: In): + let vals = i.expect("int", "string") + let n = vals[0] + let s = vals[1] + i.push s.getString.indent(n.intVal).newVal + + def.symbol("indexof") do (i: In): + let vals = i.expect("string", "string") + let reg = vals[0] + let str = vals[1] + let index = str.strVal.find(reg.strVal) + i.push index.newVal + + when not defined(mini): + + def.symbol("encode-url") do (i: In): + let vals = i.expect("string") + let s = vals[0].strVal + i.push s.encodeUrl.newVal + + def.symbol("decode-url") do (i: In): + let vals = i.expect("string") + let s = vals[0].strVal + i.push s.decodeUrl.newVal + + def.symbol("parse-url") do (i: In): + let vals = i.expect("string") + let s = vals[0].strVal + let u = s.parseUri + var d = newDict(i.scope) + i.dset(d, "scheme", u.scheme.newVal) + i.dset(d, "username", u.username.newVal) + i.dset(d, "password", u.password.newVal) + i.dset(d, "hostname", u.hostname.newVal) + i.dset(d, "port", u.port.newVal) + i.dset(d, "path", u.path.newVal) + i.dset(d, "query", u.query.newVal) + i.dset(d, "anchor", u.anchor.newVal) + i.push d + + def.symbol("search") do (i: In): + let vals = i.expect("string", "string") + let reg = vals[0] + let str = vals[1] + var matches = str.strVal.search(reg.strVal) + var res = newSeq[MinValue](matches.len) + for i in 0..matches.len-1: + res[i] = matches[i].newVal + i.push res.newVal + + def.symbol("match") do (i: In): + let vals = i.expect("string", "string") + let reg = vals[0] + let str = vals[1] + if str.strVal.match(reg.strVal): + i.push true.newVal + else: + i.push false.newVal + + def.symbol("replace") do (i: In): + let vals = i.expect("string", "string", "string") + let s_replace = vals[0] + let reg = vals[1] + let s_find = vals[2] + i.push sgregex.replace(s_find.strVal, reg.strVal, s_replace.strVal).newVal + + def.symbol("regex") do (i: In): + let vals = i.expect("string", "string") + let reg = vals[0] + let str = vals[1] + let results = str.strVal =~ reg.strVal + var res = newSeq[MinValue](0) + for r in results: + res.add(r.newVal) + i.push res.newVal + + def.symbol("semver?") do (i: In): + let vals = i.expect("string") + let v = vals[0].strVal + i.push v.match("^\\d+\\.\\d+\\.\\d+$").newVal + + def.symbol("from-semver") do (i: In): + let vals = i.expect("string") + let v = vals[0].strVal + let parts = v.search("^(\\d+)\\.(\\d+)\\.(\\d+)$") + if parts[0].len == 0: + raiseInvalid("String '$1' is not a basic semver" % v) + var d = newDict(i.scope) + i.dset(d, "major", parts[1].parseInt.newVal) + i.dset(d, "minor", parts[2].parseInt.newVal) + i.dset(d, "patch", parts[3].parseInt.newVal) + i.push d + + def.symbol("to-semver") do (i: In): + let vals = i.expect("dict") + let v = vals[0] + if not v.dhas("major") or not v.dhas("minor") or not v.dhas("patch"): + raiseInvalid("Dictionary does not contain major, minor and patch keys") + let major = i.dget(v, "major") + let minor = i.dget(v, "minor") + let patch = i.dget(v, "patch") + if major.kind != minInt or minor.kind != minInt or patch.kind != minInt: + raiseInvalid("major, minor, and patch values are not integers") + i.push(newVal("$#.$#.$#" % [$major, $minor, $patch])) + + def.symbol("semver-inc-major") do (i: In): + i.pushSym("from-semver") + var d = i.pop + let cv = i.dget(d, "major") + let v = cv.intVal + 1 + i.dset(d, "major", v.newVal) + i.dset(d, "minor", 0.newVal) + i.dset(d, "patch", 0.newVal) + i.push(d) + i.pushSym("to-semver") + + def.symbol("semver-inc-minor") do (i: In): + i.pushSym("from-semver") + var d = i.pop + let cv = i.dget(d, "minor") + let v = cv.intVal + 1 + i.dset(d, "minor", v.newVal) + i.dset(d, "patch", 0.newVal) + i.push(d) + i.pushSym("to-semver") + + def.symbol("semver-inc-patch") do (i: In): + i.pushSym("from-semver") + var d = i.pop + let cv = i.dget(d, "patch") + let v = cv.intVal + 1 + i.dset(d, "patch", v.newVal) + i.push(d) + i.pushSym("to-semver") + + def.symbol("escape") do (i: In): + let vals = i.expect("'sym") + let a = vals[0].getString + i.push a.escapeEx(true).newVal + + def.symbol("prefix") do (i: In): + let vals = i.expect("'sym", "'sym") + let a = vals[1].getString + let b = vals[0].getString + var s = b & a + i.push s.newVal + + def.symbol("suffix") do (i: In): + let vals = i.expect("'sym", "'sym") + let a = vals[1].getString + let b = vals[0].getString + var s = a & b + i.push s.newVal + + def.symbol("=~") do (i: In): + i.pushSym("regex") + + def.symbol("%") do (i: In): + i.pushSym("interpolate") + + def.symbol("=%") do (i: In): + i.pushSym("apply-interpolate") + + def.finalize("str")
A minpkg/lib/min_sys.nim

@@ -0,0 +1,250 @@

+import + os, + osproc, + strutils, + critbits, + logging +when not defined(lite): + import sequtils +import + ../core/parser, + ../core/env, + ../core/value, + ../core/interpreter, + ../core/utils, + ../core/fileutils + +when not defined(lite): + import ../packages/nim-miniz/src/nim_miniz + +proc unix(s: string): string = + return s.replace("\\", "/") + +proc sys_module*(i: In)= + let def = i.define() + + def.symbol(".") do (i: In): + i.push newVal(getCurrentDir().unix) + + def.symbol("..") do (i: In): + i.push newVal(getCurrentDir().parentDir.unix) + + def.symbol("cd") do (i: In): + let vals = i.expect("'sym") + let f = vals[0].getString + i.pwd = joinPath(getCurrentDir(), f) + info("Current directory changed to: ", i.pwd) + f.setCurrentDir + + def.symbol("ls") do (i: In): + let vals = i.expect("'sym") + let a = vals[0] + var list = newSeq[MinValue](0) + for i in walkDir(a.getString): + list.add newVal(i.path.unix) + i.push list.newVal + + def.symbol("ls-r") do (i: In): + let vals = i.expect("'sym") + let a = vals[0] + var list = newSeq[MinValue](0) + for i in walkDirRec(a.getString): + list.add newVal(i.unix) + i.push list.newVal + + def.symbol("system") do (i: In): + let vals = i.expect("'sym") + let a = vals[0] + i.push execShellCmd(a.getString).newVal + + def.symbol("system!") do (i: In): + let vals = i.expect("'sym") + let a = vals[0] + discard execShellCmd(a.getString) + + def.symbol("run") do (i: In): + let vals = i.expect("'sym") + let cmd = vals[0] + let res = execCmdEx(cmd.getString) + var d = newDict(i.scope) + i.dset(d, "output", res.output.newVal) + i.dset(d, "code", res.exitCode.newVal) + i.push(d) + + def.symbol("get-env") do (i: In): + let vals = i.expect("'sym") + let a = vals[0] + i.push a.getString.getEnv.newVal + + def.symbol("put-env") do (i: In): + let vals = i.expect("'sym", "'sym") + let key = vals[0] + let value = vals[1] + key.getString.putEnv value.getString + + def.symbol("env?") do (i: In): + let vals = i.expect("'sym") + let s = vals[0] + i.push s.getString.existsEnv.newVal + + def.symbol("which") do (i: In): + let vals = i.expect("'sym") + let s = vals[0] + i.push s.getString.findExe.newVal + + def.symbol("os") do (i: In): + i.push hostOS.newVal + + def.symbol("cpu") do (i: In): + i.push hostCPU.newVal + + def.symbol("exists?") do (i: In): + let vals = i.expect("'sym") + let f = vals[0].getString + var found = false + if MINCOMPILED: + let cf = strutils.replace(strutils.replace(f, "\\", "/"), "./", "") + + found = COMPILEDASSETS.hasKey(cf) + if found: + i.push true.newVal + else: + i.push newVal(f.fileExists or f.dirExists) + + def.symbol("file?") do (i: In): + let vals = i.expect("'sym") + let f = vals[0].getString + var found = false + if MINCOMPILED: + let cf = strutils.replace(strutils.replace(f, "\\", "/"), "./", "") + + found = COMPILEDASSETS.hasKey(cf) + if found: + i.push true.newVal + else: + i.push f.fileExists.newVal + + def.symbol("dir?") do (i: In): + let vals = i.expect("'sym") + let f = vals[0] + i.push f.getString.dirExists.newVal + + def.symbol("rm") do (i: In): + let vals = i.expect("'sym") + let v = vals[0] + let f = v.getString + if f.fileExists: + f.removeFile + else: + raiseInvalid("File '$1' does not exist." % f) + + def.symbol("cp") do (i: In): + let vals = i.expect("'sym", "'sym") + let a = vals[0] + let b = vals[1] + let src = b.getString + var dest = a.getString + if src.dirExists: + copyDirWithPermissions src, dest + elif dest.dirExists: + if src.dirExists: + copyDirWithPermissions src, dest + else: + copyFileWithPermissions src, dest / src.extractFilename + else: + copyFileWithPermissions src, dest + + def.symbol("mv") do (i: In): + let vals = i.expect("'sym", "'sym") + let a = vals[0] + let b = vals[1] + let src = b.getString + var dest = a.getString + if dest.dirExists: + dest = dest / src.extractFilename + moveFile src, dest + + def.symbol("rmdir") do (i: In): + let vals = i.expect("'sym") + let f = vals[0] + f.getString.removeDir + + def.symbol("mkdir") do (i: In): + let vals = i.expect("'sym") + let f = vals[0] + f.getString.createDir + + def.symbol("sleep") do (i: In): + let vals = i.expect("int") + let ms = vals[0] + sleep ms.intVal.int + + def.symbol("chmod") do (i: In): + let vals = i.expect("int", "string") + let perms = vals[0] + let s = vals[1] + s.getString.setFilePermissions(perms.intVal.toFilePermissions) + + def.symbol("symlink?") do (i: In): + let vals = i.expect("'sym") + let s = vals[0] + i.push s.getString.symlinkExists.newVal + + def.symbol("symlink") do (i: In): + let vals = i.expect("'sym", "'sym") + let dest = vals[0] + let src = vals[1] + src.getString.createSymlink dest.getString + + def.symbol("hardlink") do (i: In): + let vals = i.expect("'sym", "'sym") + let dest = vals[0] + let src = vals[1] + src.getString.createHardlink dest.getString + + def.symbol("filename") do (i: In): + let vals = i.expect("'sym") + let f = vals[0] + i.push f.getString.extractFilename.unix.newVal + + def.symbol("dirname") do (i: In): + let vals = i.expect("'sym") + let f = vals[0] + i.push f.getString.parentDir.unix.newVal + + def.symbol("$") do (i: In): + i.pushSym("get-env") + + def.symbol("!") do (i: In): + i.pushSym("system") + + def.symbol("!!") do (i: In): + i.pushSym("system!") + + def.symbol("&") do (i: In): + i.pushSym("run") + + def.sigil("$") do (i: In): + i.pushSym("get-env") + + def.sigil("!") do (i: In): + i.pushSym("system") + + def.sigil("&") do (i: In): + i.pushSym("run") + + when not defined(lite): + def.symbol("unzip") do (i: In): + let vals = i.expect("'sym", "'sym") + let dir = vals[0] + let f = vals[1] + nim_miniz.unzip(f.getString, dir.getString) + + def.symbol("zip") do (i: In): + let vals = i.expect("'sym", "quot") + let file = vals[0] + let files = vals[1] + nim_miniz.zip(files.qVal.mapIt(it.getString), file.getString) + + def.finalize("sys") +
A minpkg/lib/min_time.nim

@@ -0,0 +1,78 @@

+import + times +import + ../core/parser, + ../core/value, + ../core/interpreter, + ../core/utils + +proc time_module*(i: In)= + let def = i.define() + + def.symbol("timestamp") do (i: In): + i.push getTime().toUnix().newVal + + def.symbol("now") do (i: In): + i.push epochTime().newVal + + def.symbol("timeinfo") do (i: In): + let vals = i.expect("num") + let t = vals[0] + var time: Time + if t.kind == minInt: + time = t.intVal.fromUnix + else: + time = t.floatVal.int64.fromUnix + let tinfo = time.local + var info = newDict(i.scope) + info.objType = "timeinfo" + i.dset info, "year", tinfo.year.newVal + i.dset info, "month", (tinfo.month.int+1).newVal + i.dset info, "day", tinfo.monthday.newVal + i.dset info, "weekday", (tinfo.weekday.int+1).newVal + i.dset info, "yearday", tinfo.yearday.newVal + i.dset info, "hour", tinfo.hour.newVal + i.dset info, "minute", tinfo.minute.newVal + i.dset info, "second", tinfo.second.newVal + i.dset info, "dst", tinfo.isDST.newVal + i.dset info, "timezone", tinfo.utcOffset.newVal + i.push info + + def.symbol("to-timestamp") do (i: In): + let vals = i.expect("dict:timeinfo") + let dict = vals[0] + try: + let year = i.dget(dict, "year").intVal.int + let month = Month(i.dget(dict, "month").intVal.int - 1) + let monthday = i.dget(dict, "day").intVal.int + let hour: HourRange = i.dget(dict, "hour").intVal.int + let minute: MinuteRange = i.dget(dict, "minute").intVal.int + let second: SecondRange = i.dget(dict, "second").intVal.int + let timezone = i.dget(dict, "timezone").intVal.int + let tinfo = initDatetime(monthday, month, year, hour, minute, second, 00, utc()) + i.push (tinfo + timezone.seconds).toTime.toUnix.int.newVal + except: + raiseInvalid("An invalid timeinfo dictionary was provided.") + + def.symbol("datetime") do (i: In): + let vals = i.expect("num") + let t = vals[0] + var time: Time + if t.kind == minInt: + time = t.intVal.fromUnix + else: + time = t.floatVal.int64.fromUnix + i.push time.utc.format("yyyy-MM-dd'T'HH:mm:ss'Z'").newVal + + def.symbol("tformat") do (i: In): + let vals = i.expect("string", "num") + let s = vals[0] + let t = vals[1] + var time: Time + if t.kind == minInt: + time = t.intVal.fromUnix + else: + time = t.floatVal.int64.fromUnix + i.push time.local.format(s.getString).newVal + + def.finalize("time")
M next-release.mdnext-release.md

@@ -1,3 +1,7 @@

* Improved diagnostics for native symbols calling other symbols. * Refactored **crypto** module to use Nim StdLib's md5 and sha1 module when OpenSSL support is not enabled. * Fixed detection of musl executables when running build task. +* Added **union**, **intersection**, **difference**, **symmetric-difference**, **one?** symbols to **seq** module. +* Fixed compilation for loaded files and assets. +* Refacored code to satisfy nimble package structure. +* Now caching required modules so that their code is executed only once.
M nifty.jsonnifty.json

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

{ - "storage": "packages", + "storage": "minpkg/packages", "commands": { "install": { "git+src": {
M site/contents/reference-seq.mdsite/contents/reference-seq.md

@@ -19,6 +19,16 @@

{#op||concat||{{q1}} {{q2}}||{{q3}}|| Concatenates {{q1}} with {{q2}}. #} +{#op||difference||{{q1}} {{q2}}||{{q3}}|| +> Calculates the difference {{q3}} of {{q1}} and {{q2}}. +> +> > %sidebar% +> > Example +> > +> > The following program leaves `(2)` on the stack: +> > +> > (1 2 "test") ("test" "a" true 1) difference #} + {#op||drop||{{q1}} {{i}}||{{q2}}|| Returns a quotation {{q2}} containing the remaining elements after the first _n_ values of the input quotation {{q1}}, or an empty quotation if {{i}} is greater than the length of {{q1}}. #}

@@ -75,6 +85,16 @@

{#op||insert||{{q1}} {{any}} {{i}}||{{q2}}|| Inserts {{any}} as the value of the _n^th_ element {{q1}} (zero-based), and returns the modified copy of the quotation {{q2}}. #} +{#op||intersection||{{q1}} {{q2}}||{{q3}}|| +> Calculates the intersection {{q3}} of {{q1}} and {{q2}}. +> +> > %sidebar% +> > Example +> > +> > The following program leaves `(1 "test")` on the stack: +> > +> > (1 2 "test") ("test" "a" true 1) intersection #} + {#op||last||{{q}}||{{any}}|| Returns the last element of {{q}}. #}

@@ -103,6 +123,9 @@ > >

> > (1 2 3 4 5 6) > > (odd?) partition #} +{#op||one?||{{q1}} {{q2}}||{{b}}|| +Applies predicate {{q2}} to each element of {{q1}} and returns {{t}} if only one element of {{q1}} satisfies predicate {{q2}}, {{f}} otherwise. #} + {#op||prepend||{{any}} {{q}}||({{any}} {{a0p}})|| Returns a new quotation containing the contents of {{q}} with {{any}} prepended. #}

@@ -162,6 +185,25 @@ > > The following program leaves `(1 3 5 7 9 13 16)` on the stack:

> > > > (1 9 5 13 16 3 7) '> sort #} +{#op||symmetric-difference||{{q1}} {{q2}}||{{q3}}|| +> Calculates the symmetric difference {{q3}} of {{q1}} and {{q2}}. +> +> > %sidebar% +> > Example +> > +> > The following program leaves `(true "a" 2)` on the stack: +> > +> > (1 2 "test") ("test" "a" true 1) symmetric-difference #} + {#op||take||{{q1}} {{i}}||{{q2}}|| Returns a quotation {{q2}} containing the first _n_ values of the input quotation {{q1}}, or {{q1}} itself if {{i}} is greater than the length of {{q1}}. #} +{#op||union||{{q1}} {{q2}}||{{q3}}|| +> Calculates the union {{q3}} of {{q1}} and {{q2}}. +> +> > %sidebar% +> > Example +> > +> > The following program leaves `(true 1 "test" "a" 2)` on the stack: +> > +> > (1 2 "test") ("test" "a" true 1) union #}
M tests/seq.mintests/seq.min

@@ -69,9 +69,21 @@ ((2 4 6 8) 'even? all?) assert

((2 4 3 6 8) 'even? all? not) assert + ((1 2 3 4) 'odd? one? not) assert + + ((1 2 4) 'odd? one?) assert + ((1 2 3 4) 'odd? any?) assert ((2 4 6 8) 'odd? any? not) assert + + ((1 2 "test") ("test" "a" true 1) intersection (1 "test") ==) assert + + ((1 2 "test") ("test" "a" true 1) union (true 1 "test" "a" 2) ==) assert + + ((1 2 "test") ("test" "a" true 1) difference (2) ==) assert + + ((1 2 "test") ("test" "a" true 1) symmetric-difference (true "a" 2) ==) assert report clear-stack