all repos — min @ cc9cfbd0844a67d14852b7bb3b091ace433c6f04

A small but practical concatenative programming language.

Merge branch 'master' into js
h3rald h3rald@h3rald.com
Wed, 20 Jan 2021 22:38:24 +0000
commit

cc9cfbd0844a67d14852b7bb3b091ace433c6f04

parent

cb2b9bab08261b6f10a898b8edb9cc5d81de8e9b

101 files changed, 5645 insertions(+), 4661 deletions(-)

jump to
M .github/workflows/ci.yml.github/workflows/ci.yml

@@ -16,7 +16,7 @@ jobs:

# This workflow contains a single job called "ci" ci: # The type of runner that the job will run on - runs-on: ubuntu-latest + runs-on: ubuntu-20.04 env: CHOOSENIM_CHOOSE_VERSION: stable CHOOSENIM_NO_ANALYTICS: 1

@@ -25,6 +25,9 @@ # Steps represent a sequence of tasks that will be executed as part of the job

steps: # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it - uses: actions/checkout@v2 + + - name: install musl-gcc + run: sudo apt-get install -y musl-tools - name: Update $PATH run: echo "$HOME/.nimble/bin" >> $GITHUB_PATH

@@ -41,7 +44,7 @@ - name: Install deps

run: nifty install - name: Build - run: nimble -y build + run: nim c -d:release -d:ssl --gcc.exe:musl-gcc --gcc.linkerexe:musl-gcc --cpu:amd64 --os:linux -o:min min - name: Test run: ./min tests/all.min
M .gitignore.gitignore

@@ -18,3 +18,5 @@ .env.yml

response.json run run.nim +dstore.json +test.min
M Min_DeveloperGuide.mdMin_DeveloperGuide.md

@@ -105,6 +105,10 @@ ### `dict` Module

{@ site/contents/reference-dict.md || 1 @} +### `dstore` Module + +{@ site/contents/reference-dstore.md || 1 @} + ### `io` Module {@ site/contents/reference-io.md || 1 @}
M core/baseutils.nimminpkg/core/baseutils.nim

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

+import strutils + proc reverse*[T](xs: openarray[T]): seq[T] = result = newSeq[T](xs.len) for i, x in xs: result[result.len-i-1] = x +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 + let pwd = dirs.join("/") + if pwd == "": + result = file + else: + result = pwd&"/"&file when defined(mini): import
M core/env.nimminpkg/core/env.nim

@@ -22,4 +22,4 @@ var EDITOR* {.threadvar.}: LineEditor

EDITOR = initEditor(historyFile = MINHISTORY) var MINCOMPILED* {.threadvar.}: bool -MINCOMPILED = false+MINCOMPILED = false
D core/fileutils.nim

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

-import - os - -# Filetype and permissions - -proc filetype*(p: PathComponent): string {.extern:"min_exported_symbol_$1".}= - case p - of pcFile: - return "file" - of pcLinkToFile: - return "filelink" - of pcDir: - return "dir" - of pcLinkToDir: - return "dirlink" - -proc unixPermissions*(s: set[FilePermission]): int {.extern:"min_exported_symbol_$1".}= - 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] {.extern:"min_exported_symbol_$1".}= - 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,381 +0,0 @@

-import - streams, - strutils, - sequtils, - critbits, - algorithm -when defined(mini): - import - minilogger -else: - import - os, - logging -import - baseutils, - value, - scope, - parser - -type - MinTrappedException* = ref object of CatchableError - MinRuntimeError* = ref object of CatchableError - data*: MinValue - -var MINCOMPILEDFILES* {.threadvar.}: CritBitTree[MinOperatorProc] - -const USER_SYMBOL_REGEX* = "^[a-zA-Z_][a-zA-Z0-9/!?+*._-]*$" - -proc raiseRuntime*(msg: string, data: MinValue) {.extern:"min_exported_symbol_$1".}= - data.objType = "error" - raise MinRuntimeError(msg: msg, data: data) - -proc dump*(i: MinInterpreter): string {.extern:"min_exported_symbol_$1".}= - var s = "" - for item in i.stack: - s = s & $item & " " - return s - -proc debug*(i: In, value: MinValue) {.extern:"min_exported_symbol_$1".}= - debug("(" & i.dump & $value & ")") - -proc debug*(i: In, value: string) {.extern:"min_exported_symbol_$1_2".}= - 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 {.extern:"min_exported_symbol_$1".}= - 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 {.extern:"min_exported_symbol_$1_2".}= - 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 {.extern:"min_exported_symbol_$1".}= - if sym.filename == "": - return "[$1]: $2" % [sym.symVal, message] - else: - return "$1($2,$3) [$4]: $5" % [sym.filename, $sym.line, $sym.column, sym.symVal, message] - -proc formatTrace(sym: MinValue): string {.extern:"min_exported_symbol_$1".}= - if sym.filename == "": - return "<native> in symbol: $1" % [sym.symVal] - else: - return "$1($2,$3) in symbol: $4" % [sym.filename, $sym.line, $sym.column, sym.symVal] - -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) {.extern:"min_exported_symbol_$1_2".}= - i.filename = filename - i.parser.open(stream, filename) - -proc close*(i: In) {.extern:"min_exported_symbol_$1_2".}= - 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) {.extern:"min_exported_symbol_$1".}= - if q.kind == minQuotation: - i.withScope(): - when defined(js): - let qqval = q.qval - else: - 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) - i.trace.add val - if not i.evaluating: - i.currSym = val - let symbol = val.symVal - 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 {.extern:"min_exported_symbol_$1".}= - if i.stack.len > 0: - return i.stack.pop - else: - raiseEmptyStack() - -proc peek*(i: MinInterpreter): MinValue {.extern:"min_exported_symbol_$1".}= - if i.stack.len > 0: - return i.stack[i.stack.len-1] - else: - raiseEmptyStack() - -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 - try: - val = i.parser.parseMinValue(i) - if parseOnly: - q.qVal.add val - else: - i.push val - 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) - 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 - try: - result.add i.parser.compileMinValue(i, push = true, indent) - 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 compileFile*(i: In, main: bool): seq[string] {.discardable, extern:"min_exported_symbol_$1".} = - result = newSeq[string](0) - if not main: - result.add "MINCOMPILEDFILES[\"$#\"] = 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: - result.add "import critbits" - result.add "MINCOMPILED = true" - result.add "var i = newMinInterpreter(\"$#\")" % i.filename - result.add "i.stdLib()" - -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 parse*(i: In, s: string, name="<parse>"): MinValue {.extern:"min_exported_symbol_$1".}= - return i.eval(s, name, true) - -proc read*(i: In, s: string): MinValue {.extern:"min_exported_symbol_$1".}= - return i.load(s, true) -
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,848 +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, - tkTrue, - tkFalse - MinKind* = enum - minInt, - minFloat, - minQuotation, - minDictionary, - minString, - minSymbol, - 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 - case kind*: MinKind - 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) {.extern:"min_exported_symbol_$1".}= - raise MinInvalidError(msg: msg) - -proc raiseUndefined*(msg: string) {.extern:"min_exported_symbol_$1".}= - raise MinUndefinedError(msg: msg) - -proc raiseOutOfBounds*(msg: string) {.extern:"min_exported_symbol_$1".}= - raise MinOutOfBoundsError(msg: msg) - -proc raiseEmptyStack*() {.extern:"min_exported_symbol_$1".}= - 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", - "true", - "false" - ] - -proc newScope*(parent: ref MinScope, kind = minLangScope): MinScope {.extern:"min_exported_symbol_$1".}= - result = MinScope(parent: parent, kind: kind) - -proc newScopeRef*(parent: ref MinScope, kind = minLangScope): ref MinScope {.extern:"min_exported_symbol_$1".}= - new(result) - result[] = newScope(parent, kind) - -proc open*(my: var MinParser, input: Stream, filename: string) {.extern:"min_exported_symbol_$1".}= - 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 {.extern:"min_exported_symbol_$1".}= - assert(my.kind == eMinError) - result = "$1 [l:$2, c:$3] ERROR - $4" % [ - my.filename, $getLine(my), $getColumn(my), msg] - -proc errorMsg*(my: MinParser): string {.extern:"min_exported_symbol_$1_2".}= - assert(my.kind == eMinError) - result = errorMsg(my, errorMessages[my.err]) - -proc errorMsgExpected*(my: MinParser, e: string): string {.extern:"min_exported_symbol_$1".}= - 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 {.extern:"min_exported_symbol_$1".}= - 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 "true": result = tkTrue - of "false": result = tkFalse - else: - discard - my.token = result - - -proc next*(my: var MinParser) {.extern:"min_exported_symbol_$1".}= - 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) {.extern:"min_exported_symbol_$1".}= - 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 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 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 {.extern:"min_exported_symbol_$1".}= - case p.token - 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] {.extern:"min_exported_symbol_$1".}= - var op = indent - if push: - op = indent&"i.push " - result = newSeq[string](0) - case p.token - of tkTrue: - result = @[op&"MinValue(kind: minBool, boolVal: true)"] - discard getToken(p) - of tkFalse: - result = @[op&"MinValue(kind: minBool, boolVal: false)"] - discard getToken(p) - of tkString: - result = @[op&"MinValue(kind: minString, strVal: "&p.a.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) {.extern:"min_exported_symbol_$1".}= - when defined(js): - discard #TODOJS - else: - stdout.write($$a) - -# Predicates - -proc isSymbol*(s: MinValue): bool {.extern:"min_exported_symbol_$1".}= - return s.kind == minSymbol - -proc isQuotation*(s: MinValue): bool {.extern:"min_exported_symbol_$1".}= - return s.kind == minQuotation - -proc isString*(s: MinValue): bool {.extern:"min_exported_symbol_$1".}= - return s.kind == minString - -proc isFloat*(s: MinValue): bool {.extern:"min_exported_symbol_$1".}= - return s.kind == minFloat - -proc isInt*(s: MinValue): bool {.extern:"min_exported_symbol_$1".}= - return s.kind == minInt - -proc isNumber*(s: MinValue): bool {.extern:"min_exported_symbol_$1".}= - return s.kind == minInt or s.kind == minFloat - -proc isBool*(s: MinValue): bool = - return s.kind == minBool - -proc isStringLike*(s: MinValue): bool {.extern:"min_exported_symbol_$1".}= - return s.isSymbol or s.isString or (s.isQuotation and s.qVal.len == 1 and s.qVal[0].isSymbol) - -proc isDictionary*(q: MinValue): bool {.extern:"min_exported_symbol_$1".}= - return q.kind == minDictionary - -proc isTypedDictionary*(q: MinValue): bool {.extern:"min_exported_symbol_$1".}= - if q.isDictionary: - return q.objType != "" - return false - -proc isTypedDictionary*(q: MinValue, t: string): bool {.extern:"min_exported_symbol_$1_2".}= - 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 == 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 {.extern:"min_exported_symbol_$1".}= - 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 {.extern:"min_exported_symbol_$1".}= - 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 {.extern:"min_exported_symbol_$1".}= - 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 {.extern:"min_exported_symbol_$1".}= - 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 {.extern:"min_exported_symbol_$1".}= - 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 {.extern:"min_exported_symbol_$1".}= - if scope.parent.isNil: - return scope - else: - return scope.parent
D core/utils.nim

@@ -1,274 +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 {.extern:"min_exported_symbol_$1".}= - var scope = newScopeRef(i.scope, minNativeScope) - scope.parent = i.scope - return scope - -proc symbol*(scope: ref MinScope, sym: string, p: MinOperatorProc) {.extern:"min_exported_symbol_$1".}= - scope.symbols[sym] = MinOperator(prc: p, kind: minProcOp, sealed: true) - -proc symbol*(scope: ref MinScope, sym: string, v: MinValue) {.extern:"min_exported_symbol_$1_2".}= - scope.symbols[sym] = MinOperator(val: v, kind: minValOp, sealed: true) - -proc sigil*(scope: ref MinScope, sym: string, p: MinOperatorProc) {.extern:"min_exported_symbol_$1".}= - scope.sigils[sym] = MinOperator(prc: p, kind: minProcOp, sealed: true) - -proc sigil*(scope: ref MinScope, sym: string, v: MinValue) {.extern:"min_exported_symbol_$1_2".}= - scope.sigils[sym] = MinOperator(val: v, kind: minValOp, sealed: true) - -proc finalize*(scope: ref MinScope, name: string = "") {.extern:"min_exported_symbol_$1".}= - 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 {.extern:"min_exported_symbol_$1".}= - 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 {.extern:"min_exported_symbol_$1_2".}= - 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 {.extern:"min_exported_symbol_$1".}= - if not q.isDictionary: - raiseInvalid("Value is not a dictionary") - return q.dVal.contains(s.getString) - -proc dhas*(q: MinValue, s: string): bool {.extern:"min_exported_symbol_$1_2".}= - 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 {.extern:"min_exported_symbol_$1".}= - # 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 {.extern:"min_exported_symbol_$1".}= - # 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 {.extern:"min_exported_symbol_$1".}= - # 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 {.extern:"min_exported_symbol_percent_2".}= - case a.kind: - of minBool: - return %a.boolVal - 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 {.extern:"min_exported_symbol_$1".}= - case json.kind: - of JNull: - result = newSeq[MinValue](0).newVal - 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 {.extern:"min_exported_symbol_$1".}= - case t: - of "bool": - return value.isBool - 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 expect*(i: var MinInterpreter, elements: varargs[string]): seq[MinValue] {.extern:"min_exported_symbol_$1".}= - 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) {.extern:"min_exported_symbol_$1".}= - 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) {.extern:"min_exported_symbol_$1".}= - 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) {.extern:"min_exported_symbol_$1".}= - 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) {.extern:"min_exported_symbol_$1".}= - 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) {.extern:"min_exported_symbol_$1".}= - 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) {.extern:"min_exported_symbol_$1".}= - 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,72 +0,0 @@

-import - parser - -proc typeName*(v: MinValue): string {.extern:"min_exported_symbol_$1".}= - 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 minBool: - return "bool" - -# Constructors - -proc newVal*(s: string): MinValue {.extern:"min_exported_symbol_$1".}= - return MinValue(kind: minString, strVal: s) - -proc newVal*(s: cstring): MinValue {.extern:"min_exported_symbol_$1_2".}= - return MinValue(kind: minString, strVal: $s) - -proc newVal*(q: seq[MinValue]): MinValue {.extern:"min_exported_symbol_$1_3".}= - return MinValue(kind: minQuotation, qVal: q) - -proc newVal*(i: BiggestInt): MinValue {.extern:"min_exported_symbol_$1_4".}= - return MinValue(kind: minInt, intVal: i) - -proc newVal*(f: BiggestFloat): MinValue {.extern:"min_exported_symbol_$1_5".}= - return MinValue(kind: minFloat, floatVal: f) - -proc newVal*(s: bool): MinValue {.extern:"min_exported_symbol_$1_6".}= - return MinValue(kind: minBool, boolVal: s) - -proc newDict*(parentScope: ref MinScope): MinValue {.extern:"min_exported_symbol_$1".}= - return MinValue(kind: minDictionary, scope: newScopeRef(parentScope)) - -proc newSym*(s: string): MinValue {.extern:"min_exported_symbol_$1".}= - return MinValue(kind: minSymbol, symVal: s) - -# Get string value from string or quoted symbol - -proc getFloat*(v: MinValue): float {.extern:"min_exported_symbol_$1".}= - 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 {.extern:"min_exported_symbol_$1".}= - 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,106 +0,0 @@

-import - base64 -import - ../core/parser, - ../core/value, - ../core/interpreter, - ../core/utils - - -when defined(ssl): - import - strutils, - openssl, - times, - ../vendor/aes/aes - - {.compile: "../vendor/aes/libaes.c".} - - 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 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".} - - 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] - -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): - - 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 - - def.finalize("crypto")
M lib/min_dict.nimminpkg/lib/min_dict.nim

@@ -73,12 +73,12 @@ let vals = i.expect("dict")

i.push vals[0].objType.newVal def.sigil("?") do (i: In): - i.push("dhas?".newSym) + i.pushSym("dhas?") def.sigil("/") do (i: In): - i.push("dget".newSym) + i.pushSym("dget") def.sigil("%") do (i: In): - i.push("dset".newSym) + i.pushSym("dset") def.finalize("dict")
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]

@@ -93,7 +93,7 @@ if not port.isInt:

raiseInvalid("Port is not an integer.") var server = newAsyncHttpServer() var i {.threadvar.}: MinInterpreter - i = ii + i = ii.copy(ii.filename) proc handler(req: Request) {.async, gcsafe.} = var qreq = newDict(i.scope) qreq = i.dset(qreq, "url", newVal($req.url))

@@ -102,8 +102,9 @@ qreq = i.dset(qreq, "method", newVal($req.reqMethod))

qreq = i.dset(qreq, "hostname", newVal($req.hostname)) qreq = i.dset(qreq, "version", newVal("$1.$2" % [$req.protocol.major, $req.protocol.minor])) qreq = i.dset(qreq, "body", newVal($req.body)) - i.push qreq - i.dequote qhandler + i.handleErrors do: + i.push qreq + i.dequote qhandler let qres = i.pop var body = "".newVal var rawHeaders = newDict(i.scope)
D lib/min_io.nim

@@ -1,176 +0,0 @@

-import - strutils, - logging, - critbits, - terminal -import - ../packages/nimline/nimline, - ../packages/nim-sgregex/sgregex, - ../core/parser, - ../core/value, - ../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("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 a = vals[0] - i.push newVal(a.strVal.readFile) - - 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,887 +0,0 @@

-import - critbits, - strutils, - sequtils, - algorithm -when not defined(js): - import - parseopt -when defined(mini): - import - rdstdin, - ../core/minilogger -else: - import - os, - json, - logging, - ../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) - if MINCOMPILED: - var compiledFile = strutils.replace(strutils.replace(file, "\\", "/"), "./", "") - if MINCOMPILEDFILES.hasKey(compiledFile): - MINCOMPILEDFILES[compiledFile](i) - return - file = i.pwd.joinPath(file) - if not file.fileExists: - raiseInvalid("File '$1' does not exist." % file) - i.load file - - 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.push sym.newSym - i.evaluating = false - i.scope = origscope - qscope.scope.symbols[sym] = MinOperator(kind: minProcOp, prc: op) - - ### End of symbols not present in minimin - - 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 - - when not defined(js): #TODOJS - def.symbol("eval") do (i: In): - let vals = i.expect("string") - let s = vals[0] - i.eval s.strVal - - def.symbol("parse") do (i: In): - let vals = i.expect("string") - let s = vals[0] - i.push i.parse s.strVal - - 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("quit") do (i: In): - i.push 0.newVal - i.push "exit".newSym - - 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("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.push "bind".newSym - - 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.push "define".newSym - - 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("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 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.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.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(@[s.strVal.newSym].newVal) - - def.sigil(":") do (i: In): - i.push("define".newSym) - - def.sigil("~") do (i: In): - i.push("delete".newSym) - - def.sigil("@") do (i: In): - i.push("bind".newSym) - - def.sigil("+") do (i: In): - i.push("module".newSym) - - def.sigil("^") do (i: In): - i.push("call".newSym) - - def.sigil(">") do (i: In): - i.push("save-symbol".newSym) - - def.sigil("<") do (i: In): - i.push("load-symbol".newSym) - - def.sigil("#") do (i: In): - i.push("quote-bind".newSym) - - def.sigil("=") do (i: In): - i.push("quote-define".newSym) - - # Shorthand symbol aliases - - def.symbol("#") do (i: In): - i.push("quote-bind".newSym) - - def.symbol("=") do (i: In): - i.push("quote-define".newSym) - - def.symbol(":") do (i: In): - i.push("define".newSym) - - def.symbol("@") do (i: In): - i.push("bind".newSym) - - def.symbol("^") do (i: In): - i.push("call".newSym) - - def.symbol("'") do (i: In): - i.push("quote".newSym) - - def.symbol("->") do (i: In): - i.push("dequote".newSym) - - def.symbol("=>") do (i: In): - i.push("apply".newSym) - - def.symbol(">>") do (i: In): - i.push("prefix-dequote".newSym) - - def.symbol("><") do (i: In): - i.push("infix-dequote".newSym) - - def.finalize("ROOT")
D lib/min_logic.nim

@@ -1,221 +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("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 not resB.isBool: - raiseInvalid("Result of second quotation is not a boolean value") - i.push newVal(resA.boolVal and resB.boolVal) - - 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("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.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):
M lib/min_stack.nimminpkg/lib/min_stack.nim

@@ -122,8 +122,8 @@ q.qVal = @[v] & q.qVal

i.push q def.symbol("swons") do (i: In): - i.push "swap".newSym - i.push "cons".newSym + i.pushSym "swap" + i.pushSym "cons" def.symbol("sip") do (i: In): let vals = i.expect("quot", "quot")
D lib/min_str.nim

@@ -1,245 +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 - -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.push "apply".newSym - i.push "interpolate".newSym - - 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("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.push("from-semver".newSym) - 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.push("to-semver".newSym) - - def.symbol("semver-inc-minor") do (i: In): - i.push("from-semver".newSym) - 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.push("to-semver".newSym) - - def.symbol("semver-inc-patch") do (i: In): - i.push("from-semver".newSym) - 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.push("to-semver".newSym) - - 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.push("regex".newSym) - - def.symbol("%") do (i: In): - i.push("interpolate".newSym) - - def.symbol("=%") do (i: In): - i.push("apply-interpolate".newSym) - - def.finalize("str")
D lib/min_sys.nim

@@ -1,232 +0,0 @@

-import - os, - osproc, - strutils, - logging -when not defined(lite): - import sequtils -import - ../core/parser, - ../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] - i.push newVal(f.getString.fileExists or f.getString.dirExists) - - def.symbol("file?") do (i: In): - let vals = i.expect("'sym") - let f = vals[0] - i.push f.getString.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.push("get-env".newSym) - - def.symbol("!") do (i: In): - i.push("system".newSym) - - def.symbol("!!") do (i: In): - i.push("system!".newSym) - - def.symbol("&") do (i: In): - i.push("run".newSym) - - def.sigil("$") do (i: In): - i.push("get-env".newSym) - - def.sigil("!") do (i: In): - i.push("system".newSym) - - def.sigil("&") do (i: In): - i.push("run".newSym) - - 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

@@ -7,46 +7,46 @@ import

critbits when defined(mini): import - core/minilogger + minpkg/core/minilogger else: import json, os, algorithm, - dynlib, 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_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,

@@ -66,7 +66,8 @@

const PRELUDE* = "prelude.min".slurp.strip var NIMOPTIONS* = "" var MINMODULES* = newSeq[string](0) -var customPrelude = "" +var customPrelude {.threadvar.} : string +customPrelude = "" when not defined(mini): if logging.getHandlers().len == 0:

@@ -84,7 +85,7 @@ getFiles(dir)

res.sort(system.cmp) return res - proc getCompletions(ed: LineEditor, symbols: seq[string]): seq[string] = + proc getCompletions*(ed: LineEditor, symbols: seq[string]): seq[string] = var words = ed.lineText.split(" ") var word: string if words.len == 0:

@@ -142,28 +143,6 @@ dir = getCurrentDir()

return toSeq(walkDir(dir, true)).filterIt(it.path.toLowerAscii.startsWith(f.toLowerAscii)).mapIt("\"$1" % [it.path.replace("\\", "/")]) return symbols - type - LibProc = proc(i: In) {.nimcall.} - - proc dynLib*(i: In) = - discard MINLIBS.existsOrCreateDir - for library in walkFiles(MINLIBS & "/*"): - var modname = library.splitFile.name - var libfile = library.splitFile.name & library.splitFile.ext - if modname.len > 3 and modname[0..2] == "lib": - modname = modname[3..modname.len-1] - let dll = library.loadLib() - if dll != nil: - let modsym = dll.symAddr(modname) - if modsym != nil: - let modproc = cast[LibProc](dll.symAddr(modname)) - i.modproc() - logging.info("[$1] Dynamic module loaded successfully: $2" % [libfile, modname]) - else: - logging.warn("[$1] Library does not contain symbol $2" % [libfile, modname]) - else: - logging.warn("Unable to load dynamic library: " & libfile) - proc stdLib*(i: In) = when not defined(mini):

@@ -185,6 +164,7 @@ i.time_module

when not defined(mini): i.sys_module i.fs_module + i.dstore_module i.io_module when not defined(lite) and not defined(mini): i.crypto_module

@@ -206,8 +186,6 @@ i.eval MINRC.readFile()

proc interpret*(i: In, s: Stream) = i.stdLib() - when not defined(mini): - i.dynLib() i.open(s, i.filename) discard i.parser.getToken() try:

@@ -302,27 +280,22 @@ contents = fileLines.join("\n")

minStream(newStringStream(contents), fn, op, main) when isMainModule: - + when not defined(mini): + import terminal import parseopt, - core/meta + critbits, + minpkg/core/meta var REPL = false var SIMPLEREPL = false - var INSTALL = false - var UNINSTALL = false var COMPILE = false var MODULEPATH = "" - var libfile = "" var exeName = "min" - var installOpt = "\n -—install:<lib> Install dynamic library file <lib>\n" - var uninstallOpt = "\n —-uninstall:<lib> Uninstall dynamic library file <lib>\n" - var iOpt = "\n -i, --interactive Start $1 shell (with advanced prompt)\n" + var iOpt = "\n -i, --interactive Start $1 shell (with advanced prompt, default if no file specidied)\n" when defined(lite): exeName = "litemin" when defined(mini): - installOpt = "" - uninstallOpt = "" iOpt = "" exeName = "minimin"

@@ -355,13 +328,11 @@ echo " $1" % [$i.stack[i.stack.len - 1]]

proc minSimpleRepl*(i: var MinInterpreter) = i.stdLib() - when not defined(mini): - i.dynLib() var s = newStringStream("") i.open(s, "<repl>") var line: string while true: - i.push("prompt".newSym) + i.push(i.newSym("prompt")) let vals = i.expect("string") let v = vals[0] let prompt = v.getString()

@@ -376,16 +347,16 @@ when not defined(mini):

proc minRepl*(i: var MinInterpreter) = i.stdLib() - i.dynLib() var s = newStringStream("") i.open(s, "<repl>") var line: string + echo "$# shell v$#" % [exeName, pkgVersion] while true: let symbols = toSeq(i.scope.symbols.keys) EDITOR.completionCallback = proc(ed: LineEditor): seq[string] = return ed.getCompletions(symbols) # evaluate prompt - i.push("prompt".newSym) + i.push(i.newSym("prompt")) let vals = i.expect("string") let v = vals[0] let prompt = v.getString()

@@ -404,14 +375,16 @@ i.minSimpleRepl()

let usage* = """ $exe v$version - a tiny concatenative programming language - (c) 2014-2020 Fabio Cevasco + (c) 2014-2021 Fabio Cevasco Usage: $exe [options] [filename] Arguments: - filename A $exe file to interpret or compile (default: STDIN). - Options:$installOpt$uninstallOpt + filename A $exe file to interpret or compile + Options: + -a, --asset-path Specify a directory containing the asset files to include in the + compiled executable (if -c is set) -c, --compile Compile the specified file -e, --evaluate Evaluate a $exe program inline -h, --help Print this help$iOpt

@@ -425,8 +398,6 @@ -p, --prelude:<file.min> If specified, it loads <file.min> instead of the default prelude code

-v, —-version Print the program version""" % [ "exe", exeName, "version", pkgVersion, - "installOpt", installOpt, - "uninstallOpt", uninstallOpt, "iOpt", iOpt ]

@@ -450,6 +421,8 @@ of "compile", "c":

COMPILE = true of "module-path", "m": MODULEPATH = val + of "asset-path", "a": + ASSETPATH = val of "prelude", "p": customPrelude = val of "log", "l":

@@ -478,14 +451,6 @@ REPL = true

of "interactive-simple", "j": if file == "": SIMPLEREPL = true - of "install": - if file == "" and not defined(mini): - INSTALL = true - libfile = val - of "uninstall": - if file == "" and not defined(mini): - UNINSTALL = true - libfile = val else: discard else:

@@ -499,28 +464,6 @@ if MODULEPATH.len > 0:

for f in walkDirRec(MODULEPATH): if f.endsWith(".min"): MINMODULES.add f - if INSTALL: - if not libfile.fileExists: - logging.fatal("Dynamic library file not found:" & libfile) - quit(4) - try: - libfile.copyFile(MINLIBS/libfile.extractFilename) - except: - logging.fatal("Unable to install library file: " & libfile) - quit(5) - logging.notice("Dynamic linbrary installed successfully: " & libfile.extractFilename) - quit(0) - elif UNINSTALL: - if not (MINLIBS/libfile.extractFilename).fileExists: - logging.fatal("Dynamic library file not found:" & libfile) - quit(4) - try: - removeFile(MINLIBS/libfile.extractFilename) - except: - logging.fatal("Unable to uninstall library file: " & libfile) - quit(6) - logging.notice("Dynamic linbrary uninstalled successfully: " & libfile.extractFilename) - quit(0) elif REPL: minRepl() quit(0)

@@ -532,4 +475,11 @@ elif SIMPLEREPL:

minSimpleRepl() quit(0) else: - minStream newFileStream(stdin), "stdin", op + when defined(mini): + minStream newFileStream(stdin), "stdin", op + else: + if isatty(stdin): + minRepl() + quit(0) + else: + minStream newFileStream(stdin), "stdin", op
M min.nimblemin.nimble

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

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

@@ -8,7 +8,8 @@ author = pkgAuthor

description = pkgDescription license = "MIT" bin = @[pkgName] -installFiles = @["core/meta.nim"] +installFiles = @["min.yml", "min.nim", "prelude.min"] +installDirs = @["minpkg"] # Dependencies
M min.nimsmin.nims

@@ -1,19 +1,23 @@

-# https", //blog.filippo.io/easy-windows-and-linux-cross-compilers-for-macos/ +# https://blog.filippo.io/easy-windows-and-linux-cross-compilers-for-macos/ -# https", //gist.github.com/Drakulix/9881160 switch("amd64.windows.gcc.path", "/usr/local/bin") switch("amd64.windows.gcc.exe", "x86_64-w64-mingw32-gcc") switch("amd64.windows.gcc.linkerexe", "x86_64-w64-mingw32-gcc") -# http", //crossgcc.rts-software.org/doku.php?id=compiling_for_linux switch("amd64.linux.gcc.path", "/usr/local/bin") switch("amd64.linux.gcc.exe", "x86_64-linux-musl-gcc") switch("amd64.linux.gcc.linkerexe", "x86_64-linux-musl-gcc") switch("opt", "size") +when not defined(dev): + switch("define", "release") + +if findExe("musl-gcc") != "": + switch("gcc.exe", "musl-gcc") + switch("gcc.linkerexe", "musl-gcc") + when defined(ssl) and not defined(mini): - switch("define", "ssl") switch("threads", "on") when defined(windows): # TODO", change once issue nim#15220 is resolved
M min.vimmin.vim

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

" Vim syntax file " Language: min " Maintainer: Fabio Cevasco -" Last Change: 19 Dec 2020 -" Version: 0.25.0 +" Last Change: 18 Jan 2021 +" Version: 0.29.1 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 decode 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 dset dtype dup dvalues e encode env? error escape eval even? exists? exit expect fappend 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 insert int integer? interpolate 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 now num number? odd? opts or ord os over parse 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 rest reverse 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 when which while with xor zip +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 dget dhas? dict dictionary? difference dip dir? dirname div dkeys download dpairs dpick drop dsdelete dsdelete! dset dsget dshas? dsinit dspost dspost! dsput dsput! dsquery dsread dstore dswrite 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 line-info 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? 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 recv recv-line reduce regex reject remove remove-symbol repeat replace request require rest return reverse 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? 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 trunc try type type? union unless unmapkey unseal unseal-sigil unzip uppercase version warn warn! when which while with xor zip || syntax match minDefaultSigil ;\<[/:@'~!?$%&=<>#^*#+]; contained syntax match minQuote ;\<['];
M min.ymlmin.yml

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

author: Fabio Cevasco description: A tiny concatenative programming language and shell. -id: 35518954 +id: 36559365 name: min -version: 0.25.0+version: 0.29.1
A minNotepad++.xml

@@ -0,0 +1,64 @@

+<NotepadPlus> + <UserLang name="Min" ext="min" udlVersion="2.1"> + <Settings> + <Global caseIgnored="no" allowFoldOfComments="no" foldCompact="no" forcePureLC="0" decimalSeparator="0" /> + <Prefix Keywords1="no" Keywords2="no" Keywords3="yes" Keywords4="no" Keywords5="no" Keywords6="no" Keywords7="no" Keywords8="no" /> + </Settings> + <KeywordLists> + <Keywords name="Comments">00; 01 02 03 04</Keywords> + <Keywords name="Numbers, prefix1"></Keywords> + <Keywords name="Numbers, prefix2"></Keywords> + <Keywords name="Numbers, extras1"></Keywords> + <Keywords name="Numbers, extras2"></Keywords> + <Keywords name="Numbers, suffix1"></Keywords> + <Keywords name="Numbers, suffix2"></Keywords> + <Keywords name="Numbers, range"></Keywords> + <Keywords name="Operators1">&apos;</Keywords> + <Keywords name="Operators2"></Keywords> + <Keywords name="Folders in code1, open">{ (</Keywords> + <Keywords name="Folders in code1, middle"></Keywords> + <Keywords name="Folders in code1, close">} )</Keywords> + <Keywords name="Folders in code2, open"></Keywords> + <Keywords name="Folders in code2, middle"></Keywords> + <Keywords name="Folders in code2, close"></Keywords> + <Keywords name="Folders in comment, open"></Keywords> + <Keywords name="Folders in comment, middle"></Keywords> + <Keywords name="Folders in comment, close"></Keywords> + <Keywords name="Keywords1">puts import load apply args bind bool call case define define-sigil delete delete-sigil dequote expect expect-empty-stack float foreach format-error from-json from-yaml gets if import infix-dequote int invoke linrec load load-symbol loglevel module operator opts parse prefix-dequote prompt publish quit quote quote-bind quote-define raise read remove-symbol require reverse-expect-dequote return save-symbol scope saved-symbols scope-sigils scope-symbols seal seal-sigil set-type sigils source string symbols tap times to-json to-yaml try typeclass unless unseal unseal-sigil when while with clear-stack cleave cons dip dup get-stack id keep nip over pick pop rolldown rollup set-stack sip spread swap swons append get concat drop filter find first flatten harvest insert last map map-reduce partition prepend quote-map reduce reject remove rest reverse set shorten size slice sort take ddup ddel dget dkeys dpick dpairs dset dtype dvalues dsdelete dsget dsinit dspost dsput dsquery dsread dswrite ask choose clear column-print confirm debug error fappend fatal fread fwrite getchr info mapkey newline notice password print putchr type unmapkey warning atime ctime fperms fsize fstats ftype mtime expect-all expect-any apply-interpolate capitalize chr escape from-semver indent indexof interpolate join length lowercase match ord parse-url prefix repeat replace regex search semver-inc-major semver-inc-minor semver-inc-patch split strip substr suffix titleize to-semver uppercase chmod cd cp cpu dirname filename get-env hardlink ls ls-r mkdir mv os put-env rm rmdir run sleep symlink system unzip which zip div mod pred random succ sum now timestamp timeinfo to-timestamp datetime tformat aes decode encode md4 md5 sha1 sha224 sha256 sha384 sha512 acos asin atan ceil cos cosh d2r floor ln log10 log2 pow r2d round sin sinh sqrt tan tanh tau trunc accept close connect listen port recv recv-line send socket download get-content request start-server stop-server abs dsinit!</Keywords> + <Keywords name="Keywords2">== &gt;= &lt;= &gt; &lt; != and expect-all expect-any or not xor</Keywords> + <Keywords name="Keywords3">: + @ = # ~ ^ * &gt; &lt; / ? % $ ! &amp;</Keywords> + <Keywords name="Keywords4">eval exit ROOT version</Keywords> + <Keywords name="Keywords5">-&gt; =&gt;</Keywords> + <Keywords name="Keywords6">compiled? defined? lite? loglevel? all? any? in? dhas? hidden? boolean? dictionary? float? integer? null? number? quotation? type? semver? env? dir? exists? file? symlink? stringlike? even? odd? sealed?</Keywords> + <Keywords name="Keywords7">puts! tap! debug! error! info! notice! print! warning! system!</Keywords> + <Keywords name="Keywords8">+ - -inf * / inf nan randomize e pi</Keywords> + <Keywords name="Delimiters">00&quot; 01 02&quot; 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23</Keywords> + </KeywordLists> + <Styles> + <WordsStyle name="DEFAULT" fgColor="FF8080" bgColor="FFFFFF" fontName="" fontStyle="0" nesting="0" /> + <WordsStyle name="COMMENTS" fgColor="000000" bgColor="FFFFFF" fontName="" fontStyle="0" nesting="0" /> + <WordsStyle name="LINE COMMENTS" fgColor="008000" bgColor="FFFFFF" fontName="" fontStyle="2" nesting="0" /> + <WordsStyle name="NUMBERS" fgColor="0000FF" bgColor="FFFFFF" fontName="" fontStyle="0" nesting="0" /> + <WordsStyle name="KEYWORDS1" fgColor="FF0000" bgColor="FFFFFF" fontName="" fontStyle="0" nesting="0" /> + <WordsStyle name="KEYWORDS2" fgColor="0080FF" bgColor="FFFFFF" fontName="" fontStyle="0" nesting="0" /> + <WordsStyle name="KEYWORDS3" fgColor="0080FF" bgColor="FFFFFF" fontName="" fontStyle="0" nesting="0" /> + <WordsStyle name="KEYWORDS4" fgColor="8000FF" bgColor="FFFFFF" fontName="" fontStyle="5" nesting="0" /> + <WordsStyle name="KEYWORDS5" fgColor="00FF00" bgColor="FFFFFF" fontName="" fontStyle="0" nesting="0" /> + <WordsStyle name="KEYWORDS6" fgColor="00FFFF" bgColor="FFFFFF" fontName="" fontStyle="2" nesting="0" /> + <WordsStyle name="KEYWORDS7" fgColor="FF00FF" bgColor="FFFFFF" fontName="" fontStyle="2" nesting="0" /> + <WordsStyle name="KEYWORDS8" fgColor="000080" bgColor="FFFFFF" fontName="" fontStyle="0" nesting="0" /> + <WordsStyle name="OPERATORS" fgColor="808040" bgColor="FFFFFF" fontName="" fontStyle="0" nesting="0" /> + <WordsStyle name="FOLDER IN CODE1" fgColor="408080" bgColor="FFFFFF" fontName="" fontStyle="0" nesting="0" /> + <WordsStyle name="FOLDER IN CODE2" fgColor="000000" bgColor="FFFFFF" fontName="" fontStyle="0" nesting="0" /> + <WordsStyle name="FOLDER IN COMMENT" fgColor="000000" bgColor="FFFFFF" fontName="" fontStyle="0" nesting="0" /> + <WordsStyle name="DELIMITERS1" fgColor="FF8000" bgColor="FFFFFF" fontName="" fontStyle="2" nesting="0" /> + <WordsStyle name="DELIMITERS2" fgColor="000000" bgColor="FFFFFF" fontName="" fontStyle="0" nesting="0" /> + <WordsStyle name="DELIMITERS3" fgColor="000000" bgColor="FFFFFF" fontName="" fontStyle="0" nesting="0" /> + <WordsStyle name="DELIMITERS4" fgColor="000000" bgColor="FFFFFF" fontName="" fontStyle="0" nesting="0" /> + <WordsStyle name="DELIMITERS5" fgColor="000000" bgColor="FFFFFF" fontName="" fontStyle="0" nesting="0" /> + <WordsStyle name="DELIMITERS6" fgColor="000000" bgColor="FFFFFF" fontName="" fontStyle="0" nesting="0" /> + <WordsStyle name="DELIMITERS7" fgColor="000000" bgColor="FFFFFF" fontName="" fontStyle="0" nesting="0" /> + <WordsStyle name="DELIMITERS8" fgColor="000000" bgColor="FFFFFF" fontName="" fontStyle="0" nesting="0" /> + </Styles> + </UserLang> +</NotepadPlus>
D mindyn.nim

@@ -1,222 +0,0 @@

-{.pragma: rtl, exportc, dynlib, cdecl.} -# Everything below here is to interface with the main program -# Import for the missing types (Look into importing just type definitions) -import - lexbase, - streams, - critbits, - json, - os - -type - MinTokenKind* = enum - tkError, - tkEof, - tkString, - tkInt, - tkFloat, - tkBracketLe, - tkBracketRi, - tkSymbol, - tkTrue, - tkFalse - MinKind* = enum - minInt, - minFloat, - minQuotation, - minString, - minSymbol, - 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 - MinParserError* = enum ## enumeration that lists all errors that can occur - errNone, ## no error - errInvalidToken, ## invalid token - errStringExpected, ## string expected - errBracketRiExpected, ## ``)`` expected - errQuoteExpected, ## ``"`` or ``'`` expected - errEOC_Expected, ## ``*/`` expected - errEofExpected, ## EOF expected - errExprExpected - MinParserState* = enum - stateEof, - stateStart, - stateQuotation, - stateExpectValue - MinParser* = object of BaseLexer - a*: string - token*: MinTokenKind - state*: seq[MinParserState] - kind*: MinEventKind - err*: MinParserError - filename*: string - MinValue* = ref MinValueObject - MinValueObject* = object - line*: int - column*: int - filename*: string - case kind*: MinKind - of minInt: intVal*: BiggestInt - of minFloat: floatVal*: BiggestFloat - of minQuotation: - qVal*: seq[MinValue] - scope*: ref MinScope - of minString: strVal*: string - of minSymbol: symVal*: string - of minBool: boolVal*: bool - MinScope* = object - symbols*: CritBitTree[MinOperator] - sigils*: CritBitTree[MinOperator] - parent*: ref MinScope - name*: string - stack*: MinStack - MinOperatorProc* = proc (i: In) {.closure.} - MinOperatorKind* = enum - minProcOp - minValOp - MinOperator* = object - sealed*: bool - case kind*: MinOperatorKind - of minProcOp: - prc*: MinOperatorProc - of minValOp: - 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 - -# parser.nim -proc raiseInvalid(msg: string) {.importc, extern:"min_exported_symbol_$1".} -proc raiseUndefined(msg: string) {.importc, extern:"min_exported_symbol_$1".} -proc raiseOutOfBounds(msg: string) {.importc, extern:"min_exported_symbol_$1".} -proc raiseEmptyStack() {.importc, extern:"min_exported_symbol_$1".} -proc newScope(parent: ref MinScope): MinScope {.importc, extern:"min_exported_symbol_$1".} -proc newScopeRef(parent: ref MinScope): ref MinScope {.importc, extern:"min_exported_symbol_$1".} -proc open(my: var MinParser, input: Stream, filename: string) {.importc, extern:"min_exported_symbol_$1".} -proc close(my: var MinParser) {.importc, extern:"min_exported_symbol_$1".} -proc getInt(my: MinParser): int {.importc, extern:"min_exported_symbol_$1".} -proc getFloat(my: MinParser): float {.importc, extern:"min_exported_symbol_$1".} -proc kind(my: MinParser): MinEventKind {.importc, extern:"min_exported_symbol_$1".} -proc getColumn(my: MinParser): int {.importc, extern:"min_exported_symbol_$1".} -proc getLine(my: MinParser): int {.importc, extern:"min_exported_symbol_$1".} -proc getFilename(my: MinParser): string {.importc, extern:"min_exported_symbol_$1".} -proc errorMsg(my: MinParser, msg: string): string {.importc, extern:"min_exported_symbol_$1".} -proc errorMsg(my: MinParser): string {.importc, extern:"min_exported_symbol_$1_2".} -proc errorMsgExpected(my: MinParser, e: string): string {.importc, extern:"min_exported_symbol_$1".} -proc raiseParsing(p: MinParser, msg: string) {.importc, extern:"min_exported_symbol_$1".} -proc raiseUndefined(p:MinParser, msg: string) {.importc, extern:"min_exported_symbol_$1_2".} -proc getToken(my: var MinParser): MinTokenKind {.importc, extern:"min_exported_symbol_$1".} -proc next(my: var MinParser) {.importc, extern:"min_exported_symbol_$1".} -proc eat(p: var MinParser, token: MinTokenKind) {.importc, extern:"min_exported_symbol_$1".} -proc parseMinValue(p: var MinParser, i: In): MinValue {.importc, extern:"min_exported_symbol_$1".} -proc `$`(a: MinValue): string {.importc, extern:"min_exported_symbol_$1".} -proc `$$`(a: MinValue): string {.importc, extern:"min_exported_symbol_$1".} -proc print(a: MinValue) {.importc, extern:"min_exported_symbol_$1".} -proc `==`(a: MinValue, b: MinValue): bool {.importc, extern:"min_exported_symbol_eqeq".} - -# value.nim -proc typeName*(v: MinValue): string {.importc, extern:"min_exported_symbol_$1".} -proc isSymbol*(s: MinValue): bool {.importc, extern:"min_exported_symbol_$1".} -proc isQuotation*(s: MinValue): bool {.importc, extern:"min_exported_symbol_$1".} -proc isString*(s: MinValue): bool {.importc, extern:"min_exported_symbol_$1".} -proc isFloat*(s: MinValue): bool {.importc, extern:"min_exported_symbol_$1".} -proc isInt*(s: MinValue): bool {.importc, extern:"min_exported_symbol_$1".} -proc isNumber*(s: MinValue): bool {.importc, extern:"min_exported_symbol_$1".} -proc isBool*(s: MinValue): bool {.importc, extern:"min_exported_symbol_$1".} -proc isStringLike*(s: MinValue): bool {.importc, extern:"min_exported_symbol_$1".} -proc isDictionary*(q: MinValue): bool {.importc, extern:"min_exported_symbol_$1".} -proc isTypedDictionary*(q: MinValue): bool {.extern:"min_exported_symbol_$1".}= -proc isTypedDictionary*(q: MinValue, t: string): bool {.extern:"min_exported_symbol_$1_2".}= -proc newVal*(s: string): MinValue {.importc, extern:"min_exported_symbol_$1".} -proc newVal*(s: cstring): MinValue {.importc, extern:"min_exported_symbol_$1_2".} -proc newVal*(q: seq[MinValue], parentScope: ref MinScope): MinValue {.importc, extern:"min_exported_symbol_$1_3".} -proc newVal*(s: BiggestInt): MinValue {.importc, extern:"min_exported_symbol_$1_4".} -proc newVal*(s: BiggestFloat): MinValue {.importc, extern:"min_exported_symbol_$1_5".} -proc newVal*(s: bool): MinValue {.importc, extern:"min_exported_symbol_$1_6".} -proc newSym*(s: string): MinValue {.importc, extern:"min_exported_symbol_$1".} -proc getFloat*(v: MinValue): float {.importc, extern:"min_exported_symbol_$1".} -proc getString*(v: MinValue): string {.importc, extern:"min_exported_symbol_$1".} - -# utils.nim -proc define*(i: In): ref MinScope {.importc, extern:"min_exported_symbol_$1".} -proc symbol*(scope: ref MinScope, sym: string, p: MinOperatorProc) {.importc, extern:"min_exported_symbol_$1".} -proc symbol*(scope: ref MinScope, sym: string, v: MinValue) {.importc, extern:"min_exported_symbol_$1_2".} -proc sigil*(scope: ref MinScope, sym: string, p: MinOperatorProc) {.importc, extern:"min_exported_symbol_$1".} -proc sigil*(scope: ref MinScope, sym: string, v: MinValue) {.importc, extern:"min_exported_symbol_$1_2".} -proc finalize*(scope: ref MinScope, name: string) {.importc, extern:"min_exported_symbol_$1".} -proc dget*(q: MinValue, s: MinValue): MinValue {.importc, extern:"min_exported_symbol_$1".} -proc dhas*(q: MinValue, s: MinValue): bool {.importc, extern:"min_exported_symbol_$1".} -proc ddel*(i: In, p: MinValue, s: MinValue): MinValue {.importc, extern:"min_exported_symbol_$1".} -proc dset*(i: In, p: MinValue, s: MinValue, m: MinValue): MinValue {.importc, extern:"min_exported_symbol_$1".} -proc keys*(i: In, q: MinValue): MinValue {.importc, extern:"min_exported_symbol_$1".} -proc values*(i: In, q: MinValue): MinValue {.importc, extern:"min_exported_symbol_$1".} -proc `%`*(a: MinValue): JsonNode {.importc, extern:"min_exported_symbol_percent".} -proc fromJson*(i: In, json: JsonNode): MinValue {.importc, extern:"min_exported_symbol_$1".} -proc reverse[T](xs: openarray[T]): seq[T] {.importc, extern:"min_exported_symbol_$1".} -proc expect*(i: var MinInterpreter, elements: varargs[string]): seq[MinValue] {.importc, extern:"min_exported_symbol_$1".} -proc reqQuotationOfQuotations*(i: var MinInterpreter, a: var MinValue) {.importc, extern:"min_exported_symbol_$1".} -proc reqQuotationOfNumbers*(i: var MinInterpreter, a: var MinValue) {.importc, extern:"min_exported_symbol_$1".} -proc reqQuotationOfSymbols*(i: var MinInterpreter, a: var MinValue) {.importc, extern:"min_exported_symbol_$1".} -proc reqTwoNumbersOrStrings*(i: var MinInterpreter, a, b: var MinValue) {.importc, extern:"min_exported_symbol_$1".} -proc reqStringOrQuotation*(i: var MinInterpreter, a: var MinValue) {.importc, extern:"min_exported_symbol_$1".} -proc reqTwoQuotationsOrStrings*(i: var MinInterpreter, a, b: var MinValue) {.importc, extern:"min_exported_symbol_$1".} - -# scope.nim -proc copy*(s: ref MinScope): ref MinScope {.importc, extern:"min_exported_symbol_$1".} -proc getSymbol*(scope: ref MinScope, key: string): MinOperator {.importc, extern:"min_exported_symbol_$1".} -proc hasSymbol*(scope: ref MinScope, key: string): bool {.importc, extern:"min_exported_symbol_$1".} -proc delSymbol*(scope: ref MinScope, key: string): bool {.importc, extern:"min_exported_symbol_$1".} -proc setSymbol*(scope: ref MinScope, key: string, value: MinOperator, override: bool): bool {.importc, extern:"min_exported_symbol_$1".} -proc getSigil*(scope: ref MinScope, key: string): MinOperator {.importc, extern:"min_exported_symbol_$1".} -proc hasSigil*(scope: ref MinScope, key: string): bool {.importc, extern:"min_exported_symbol_$1".} -proc previous*(scope: ref MinScope): ref MinScope {.importc, extern:"min_exported_symbol_$1".} - -# interpreter.nim -proc raiseRuntime*(msg: string, qVal: var seq[MinValue]) {.importc, extern:"min_exported_symbol_$1".} -proc dump*(i: MinInterpreter): string {.importc, extern:"min_exported_symbol_$1".} -proc debug*(i: In, value: MinValue) {.importc, extern:"min_exported_symbol_$1".} -proc debug*(i: In, value: string) {.importc, extern:"min_exported_symbol_$1_2".} -proc newMinInterpreter*(filename: string): MinInterpreter {.importc, extern:"min_exported_symbol_$1".} -proc copy*(i: MinInterpreter, filename: string): MinInterpreter {.importc, extern:"min_exported_symbol_$1_2".} -proc formatError(sym: MinValue, message: string): string {.importc, extern:"min_exported_symbol_$1".} -proc formatTrace(sym: MinValue): string {.importc, extern:"min_exported_symbol_$1".} -proc stackTrace(i: In) {.importc, extern:"min_exported_symbol_$1".} -proc error(i: In, message: string) {.importc, extern:"min_exported_symbol_$1".} -proc open*(i: In, stream:Stream, filename: string) {.importc, extern:"min_exported_symbol_$1_2".} -proc close*(i: In) {.importc, extern:"min_exported_symbol_$1_2".} -proc apply*(i: In, op: MinOperator) {.importc, extern:"min_exported_symbol_$1".} -proc push*(i: In, val: MinValue) {.importc, extern:"min_exported_symbol_$1".} -proc dequote*(i: In, q: var MinValue) {.importc, extern:"min_exported_symbol_$1".} -proc apply*(i: In, q: var MinValue) {.importc, extern:"min_exported_symbol_$1_2".} -proc pop*(i: In): MinValue {.importc, extern:"min_exported_symbol_$1".} -proc peek*(i: MinInterpreter): MinValue {.importc, extern:"min_exported_symbol_$1".} -proc interpret*(i: In): MinValue {.importc, discardable, extern:"min_exported_symbol_$1".} -proc eval*(i: In, s: string, name: string, parseOnly: bool) {.importc, discardable, extern:"min_exported_symbol_$1".} -proc load*(i: In, s: string, parseOnly: bool): MinValue {.importc, discardable, extern:"min_exported_symbol_$1".} -proc parse*(i: In, s: string, name: string): MinValue {.importc, extern:"min_exported_symbol_$1".} -proc read*(i: In, s: string): MinValue {.importc, extern:"min_exported_symbol_$1".} - -# fileutils.nim -proc filetype*(p: PathComponent): string {.importc, extern:"min_exported_symbol_$1".} -proc unixPermissions*(s: set[FilePermission]): int {.importc, extern:"min_exported_symbol_$1".} -proc toFilePermissions*(p: BiggestInt): set[FilePermission] {.importc, extern:"min_exported_symbol_$1".}
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 diff*(a, b: seq[MinValue]): seq[MinValue] = + result = newSeq[MinValue](0) + for it in b: + if not a.contains it: + result.add it + +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 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(): + when defined(js): + let qqval = q.qval + else: + 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) + let d = snapshot.diff(i2.stack) + if d.len > 0: + raiseInvalid("Module '$#' is polluting the stack -- $#" % [s, $d.newVal]) + 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,869 @@

+# 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) = + when defined(js): + discard #TODOJS + else: + 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,353 @@

+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 basicValidate*(i: In, 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: + if t.contains(":"): + var split = t.split(":") + # Typed dictionaries + if split[0] == "dict": + if value.isTypedDictionary(split[1]): + return true + return false + elif i.scope.hasSymbol("type:$#" % t): + # Custom type class + var i2 = i.copy(i.filename) + i2.withScope(): + i2.push value + i2.pushSym("type:$#" % t) + let res = i2.pop + if not res.isBool: + raiseInvalid("Type class '$#' does not evaluate to a boolean value ($# was returned instead)" % [t, $res]) + return res.boolVal + else: + raiseInvalid("Unknown type '$#'" % t) + +proc validate*(i: In, value: MinValue, t: string, generics: var CritBitTree[string]): bool = + if generics.hasKey(t): + let ts = generics[t].split("|") + for tp in ts: + if i.basicValidate(value, tp): + generics[t] = tp # lock type for future uses within same signature + return true + return false + return i.basicValidate(value, t) + +proc validate*(i: In, value: MinValue, t: string): bool = + return i.basicValidate(value, t) + +proc validType*(i: In, s: string): bool = + const ts = ["bool", "null", "int", "num", "float", "quot", "dict", "'sym", "sym", "string", "a"] + if ts.contains(s): + return true + if i.scope.hasSymbol("type:$#" % s): + return true + for tt in s.split("|"): + if not ts.contains(tt) and not tt.startsWith("dict:") and not i.scope.hasSymbol("type:$#" % tt): + return false + return true + +proc expect*(i: var MinInterpreter, elements: varargs[string], generics: var CritBitTree[string]): seq[MinValue] = + let sym = i.currSym.getString + var valid = newSeq[string](0) + result = newSeq[MinValue](0) + let message = proc(invalid: string, elements: varargs[string], generics: CritBitTree[string]): string = + var pelements = newSeq[string](0) + for e in elements.reverse: + if generics.hasKey(e): + pelements.add(generics[e]) + else: + pelements.add e + let stack = pelements.reverse.join(" ") + result = "Incorrect values found on the stack:\n" + result &= "- expected: " & stack & " $1\n" % sym + var other = "" + if valid.len > 0: + other = valid.reverse.join(" ") & " " + result &= "- got: " & invalid & " " & other & sym + for el in elements: + var element = el + let value = i.pop + result.add value + var split = element.split("|") + if split.len > 1: + var res = false + for t in split: + if i.validate(value, t, generics): + res = true + break + if not res: + raiseInvalid(message(value.typeName, elements, generics)) + elif not i.validate(value, element, generics): + raiseInvalid(message(value.typeName, elements, generics)) + if generics.hasKey(el): + valid.add(generics[el]) + else: + valid.add element + +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 = "Incorrect values found on the stack:\n" + 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 i.validate(value, t): + res = true + break + if not res: + raiseInvalid(message(value.typeName)) + elif not i.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_dstore.nim

@@ -0,0 +1,161 @@

+import + json, + strutils, + oids +import + ../core/parser, + ../core/value, + ../core/interpreter, + ../core/utils + +proc dstore_module*(i: In)= + let def = i.define() + + def.symbol("dsinit") do (i: In): + let vals = i.expect("'sym") + let p = vals[0].getString + p.writeFile("{}") + var d = newDict(i.scope) + i.dset(d, "data", newDict(i.scope)) + i.dset(d, "path", p.newVal) + d.objType = "datastore" + i.push d + + def.symbol("dsinit!") do (i: In): + i.pushSym "dsinit" + i.pushSym "pop" + + def.symbol("dsread") do (i: In): + let vals = i.expect("'sym") + let p = vals[0].getString + var j = p.readFile.parseJson + var d = newDict(i.scope) + i.dset(d, "data", i.fromJson(j)) + i.dset(d, "path", p.newVal) + d.objType = "datastore" + i.push d + + def.symbol("dswrite") do (i: In): + let vals = i.expect("dict:datastore") + let ds = vals[0] + let p = i.dget(ds, "path".newVal).getString + let data = i%(i.dget(ds, "data".newVal)) + p.writeFile(data.pretty) + i.push ds + + def.symbol("dswrite!") do (i: In): + i.pushSym "dswrite" + i.pushSym "pop" + + def.symbol("dshas?") do (i: In): + let vals = i.expect("'sym", "dict:datastore") + let s = vals[0].getString + let ds = vals[1] + let parts = s.split("/") + let collection = parts[0] + let id = parts[1] + let data = i.dget(ds, "data".newVal) + if not dhas(data, collection): + i.push false.newVal + else: + let cll = i.dget(data, collection.newVal) + if dhas(cll, id.newVal): + i.push true.newVal + else: + i.push false.newVal + + def.symbol("dsget") do (i: In): + let vals = i.expect("'sym", "dict:datastore") + let s = vals[0].getString + let ds = vals[1] + let parts = s.split("/") + let collection = parts[0] + let id = parts[1] + let data = i.dget(ds, "data".newVal) + if not dhas(data, collection): + raiseInvalid("Collection '$#' does not exist" % collection) + let cll = i.dget(data, collection) + i.push i.dget(cll, id.newVal) + + def.symbol("dsquery") do (i: In): + let vals = i.expect("quot", "'sym", "dict:datastore") + var filter = vals[0] + let collection = vals[1] + let ds = vals[2] + let data = i.dget(ds, "data".newVal) + var res = newSeq[MinValue](0) + if not dhas(data, collection): + i.push res.newVal + return + let cll = i.dget(data, collection) + for e in i.values(cll).qVal: + i.push e + try: + i.dequote(filter) + var check = i.pop + if check.isBool and check.boolVal == true: + res.add e + except: + discard + i.push res.newVal + + def.symbol("dspost") do (i: In): + let vals = i.expect("dict", "'sym", "dict:datastore") + var d = vals[0] + let collection = vals[1] + var ds = vals[2] + let id = $genOid() + i.dset(d, "id", id.newVal) + var data = i.dget(ds, "data".newVal) + if not dhas(data, collection): + i.dset(data, collection, newDict(i.scope)) + var cll = i.dget(data, collection) + i.dset(cll, id, d) + i.push ds + + def.symbol("dspost!") do (i: In): + i.pushSym "dspost" + i.pushSym "pop" + + def.symbol("dsput") do (i: In): + let vals = i.expect("dict", "'sym", "dict:datastore") + var d = vals[0] + let s = vals[1].getString + let ds = vals[2] + let parts = s.split("/") + let collection = parts[0] + if parts.len < 2: + raiseInvalid("collection/id not specified") + let id = parts[1] + var data = i.dget(ds, "data".newVal) + if not dhas(data, collection): + i.dset(data, collection, newDict(i.scope)) + var cll = i.dget(data, collection) + i.dset(cll, id, d) + i.push ds + + def.symbol("dsput!") do (i: In): + i.pushSym "dsput" + i.pushSym "pop" + + def.symbol("dsdelete") do (i: In): + let vals = i.expect("'sym", "dict:datastore") + let s = vals[0].getString + let ds = vals[1] + let parts = s.split("/") + if parts.len < 2: + raiseInvalid("collection/id not specified") + let collection = parts[0] + let id = parts[1] + var data = i.dget(ds, "data".newVal) + if not dhas(data, collection): + raiseInvalid("Collection '$#' does not exist" % collection) + var cll = i.dget(data, collection) + i.ddel(cll, id) + i.push ds + + def.symbol("dsdelete!") do (i: In): + i.pushSym "dsdelete" + i.pushSym "pop" + + def.finalize("dstore")
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,1106 @@

+import + critbits, + strutils, + sequtils, + algorithm +when not defined(js): + import + parseopt +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("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 generics: CritBitTree[string] + var origGenerics: CritBitTree[string] + var o= false + for vv in sv.qVal: + if not vv.isSymbol and not vv.isQuotation: + raiseInvalid("Signature must be a quotation of symbols/quotations") + var v: string + var check = c mod 2 == 0 + if o: + check = c mod 2 != 0 + if vv.isQuotation: + if vv.qVal.len != 2 or not vv.qVal[0].isSymbol or not vv.qVal[1].isSymbol: + raiseInvalid("Generic quotation must contain exactly two symbols") + let t = vv.qVal[0].getString + let g = vv.qVal[1].getString + if not i.validType(t): + raiseInvalid("Invalid type '$#' in generic in signature at position $#" % [$t, $(c+1)]) + if g[0] != ':': + raiseInvalid("No mapping symbol specified in generic in signature at position $#" % $(c+1)) + v = g[1..g.len-1] + generics[v] = t + else: + v = vv.symVal + if check: + if v == "==>": + o = true + elif not i.validType(v) and not generics.hasKey(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") + origGenerics = deepCopy(generics) + # 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: seq[MinValue] + try: + inVals = i.expect(inExpects, generics) + except: + generics = origGenerics + raise + 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 + let d= snapshot.diff(endSnapshot) + if d.len > 0 : + raiseInvalid("Operator '$#' is polluting the stack -- $#" % [n, $d.newVal]) + 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 i.validate(x, ut, generics): + r = true + break + else: + r = i.validate(x, o, generics) + if not r: + discard i.pop + var tp = t + if generics.hasKey(o): + tp = generics[o] + generics = origGenerics + raiseInvalid("Invalid value for output symbol '$#'. Expected $#, found $#" % [outVars[k], tp, $x]) + generics = origGenerics + # 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") + var prog = vals[0] + i.apply 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("sealed?") do (i: In): + let vals = i.expect("'sym") + i.push i.scope.getSymbol(vals[0].getString).sealed.newVal + + def.symbol("sealed-sigil?") do (i: In): + let vals = i.expect("'sym") + i.push i.scope.getSigil(vals[0].getString).sealed.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("typeclass") do (i: In): + let vals = i.expect("'sym", "quot") + let name = vals[0].getString + let symbol = "type:$#" % name + let code = vals[1] + info "[typeclass] $1 = $2" % [symbol, $code] + if i.scope.symbols.hasKey(symbol) and i.scope.symbols[symbol].sealed: + raiseUndefined("Attempting to redefine sealed typeclass '$1'" % [name]) + i.scope.symbols[symbol] = MinOperator(kind: minValOp, val: code, sealed: false, quotation: true) + + 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 + + when not defined(js): #TODOJS + def.symbol("eval") do (i: In): + let vals = i.expect("string") + let s = vals[0] + i.eval s.strVal + + def.symbol("parse") do (i: In): + let vals = i.expect("string") + let s = vals[0] + i.push i.parse s.strVal + + 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("quit") do (i: In): + i.push 0.newVal + i.pushSym "exit" + + 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] + i.dequote(b) + var check = i.pop + while check.boolVal == true: + i.dequote(d) + i.dequote(b) + check = 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("expect") do (i: In): + var q: MinValue + i.reqQuotationOfSymbols q + i.push(i.expect(q.qVal.mapIt(it.getString())).reversed.newVal) + + 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 + + def.symbol("line-info") do (i: In): + var d = newDict(i.scope) + i.dset(d, "filename", i.currSym.filename.newVal) + i.dset(d, "line", i.currSym.line.newVal) + i.dset(d, "column", i.currSym.column.newVal) + i.push d + + # 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("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,241 @@

+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("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("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("stringlike?") do (i: In): + if i.pop.isStringLike: + 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,118 @@

+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 BiggestInt(vals[0].getFloat.floor).newVal + + def.symbol("ceil") do (i: In): + let vals = i.expect("num") + i.push BiggestInt(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("abs") do (i: In): + let vals = i.expect("num") + let n = vals[0] + if n.kind == minFloat: + i.push n.floatVal.abs.newVal + else: + i.push n.intVal.abs.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,10 +1,1 @@

-* Added support for sigils on double-quoted strings. -* Added support for arbitrary strings as dictionary keys. -* Added **define-sigil**, **delete-sigil**, **seal-sigil**, **unseal-sigil**, **defined-sigil?**. -* Fixed behavior of **semver-inc-mahor** and **semver-inc-minor** to set lower digits to zero. -* Now using OpenSSL for all hashing symbols in the crypto module. -* Added **md4** symbol. -* Re-added the possibility to exclude OpenSSL by not defining the **ssl** flag. -* Added **clear** symbol to clear the screen. -* Added the **mapkey** and the **unmapkey** symbols to configure key mappings. -* Fixed execution of scripts from stdin (when running min with no parameters). +* Fixes #98.
M nifty.jsonnifty.json

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

{ - "storage": "packages", + "storage": "minpkg/packages", "commands": { "install": { "git+src": {
M prelude.minprelude.min

@@ -11,6 +11,7 @@ (

'io import 'fs import 'sys import + 'dstore import ) ROOT with ) unless (lite? mini? or) (
M run.minrun.min

@@ -20,7 +20,7 @@ taskspec 0 get :task

"default" :subtask (taskspec size 1 >) (taskspec 1 get @subtask) when -"./tasks/$#.min" (task) =% :task-file +"tasks/$#.min" (task) =% :task-file ; Load task module task-file load
M site/assets/styles/min-lang.csssite/assets/styles/min-lang.css

@@ -56,6 +56,7 @@ }

header .luxbar-menu i { vertical-align: text-bottom; + line-height: 18px; } .luxbar-menu-light {
M site/contents/_defs_.mdsite/contents/_defs_.md

@@ -9,9 +9,11 @@ {{3 => [<sub>3</sub>](class:kwd)}}

{{4 => [<sub>4</sub>](class:kwd)}} {{e => [dict:error](class:kwd)}} {{tinfo => [dict:timeinfo](class:kwd)}} +{{dstore => [dict:datastore](class:kwd)}} {{d => [dict](class:kwd)}} {{d1 => [dict<sub>1</sub>](class:kwd)}} {{d2 => [dict<sub>2</sub>](class:kwd)}} +{{d0p => [dict<sub>\*</sub>](class:kwd)}} {{flt => [float](class:kwd)}} {{i => [int](class:kwd)}} {{i1 => [int<sub>1</sub>](class:kwd)}}

@@ -44,8 +46,10 @@ {{sl1 => [&apos;sym<sub>1</sub>](class:kwd)}}

{{sl2 => [&apos;sym<sub>2</sub>](class:kwd)}} {{f => [false](class:kwd)}} {{t => [true](class:kwd)}} -{{null => &#x2205;}} +{{null => [null](class:kwd)}} +{{none => &#x2205;}} {{sock => [dict:socket](class:kwd)}} +{{url => [url](class:kwd)}} {{req => [request](class:kwd)}} {{res => [response](class:kwd)}} {{sock1 => [dict:socket<sub>1</sub>](class:kwd)}}
M site/contents/about.mdsite/contents/about.md

@@ -19,9 +19,9 @@ Because creating a programming language is something that every programmer needs to do, at some point in life. And also because there are way too few [concatenative](http://concatenative.org/wiki/view/Front%20Page) programming language out there -- so people are likely to be _less_ pissed off than if I made a yet another Lisp instead.

I always wanted to build a minimalist language, but that could also be used for real work and provided a standard library for common tasks and functionalities like regular expression support, cryptography, execution of external programs, shell-like operators, and keywords to work with files, and more. -Also, I wanted it to be fully self-contained, cross-platform, and tiny. About 1MB (depending on the platform) is not really tiny, but I feel it's a good compromise compared to the alternatives out there, considering that you only need _one file_ to run any min program. +Additionally, I wanted it to be fully self-contained, cross-platform, and tiny. About 1MB (or more, depending on the platform and whether SSL support is enabled or not) is not really tiny, but I feel it's a good compromise compared to the alternatives out there, considering that you only need _one file_ to run any min program. -I am currently building a static site generator called [HastySite](https://github.com/h3rald/hastysite), which also powers <https://min-lang.org>. HastySite internally uses min as the language to write the [rules](https://github.com/h3rald/min/blob/master/site/rules.min) to process the source files of the site, and also all its [scripts](https://github.com/h3rald/min/tree/master/site/scripts). + I also created a static site generator called [HastySite](https://github.com/h3rald/hastysite), which also powers <https://min-lang.org>. HastySite internally uses min as the language to write the [rules](https://github.com/h3rald/min/blob/master/site/rules.min) to process the source files of the site, and also all its [scripts](https://github.com/h3rald/min/tree/master/site/scripts). Finally, I think more and more people should get to know concatenative programming languages, because [concatenative programming matters](http://evincarofautumn.blogspot.it/2012/02/why-concatenative-programming-matters.html).

@@ -33,7 +33,7 @@ min's parser started off as a fork of Nim's JSON parser -- adapted to process a concatenative programming language with less primitive types than JSON. It is interpreted in the traditional sense: no bytecode, no JIT, just plain read, parse, and run.

## Who? -min was created and implemented by [Fabio Cevasco](https://h3rald.com), with contributions by [Peter Munch-Ellingsen](https://peterme.net). +min was created and implemented by [Fabio Cevasco](https://h3rald.com), with contributions by [Peter Munch-Ellingsen](https://peterme.net) and [baykus871](https://github.com/baykus871). Special thanks to [mwgkgk](https://github.com/mwgkgk) for contributing to the design of native dictionaries.
M site/contents/get-started.mdsite/contents/get-started.md

@@ -42,12 +42,16 @@ #### -d:ssl

If the **-d:ssl** flag is specified when compiling, min will be built with SSL support, so it will be possible to: * perform HTTPS requests with the {#link-module||http#}. -* use the cryptographic symbols defined in the {#link-module||crypto#}. +* use all the cryptographic symbols defined in the {#link-module||crypto#}. -> %note% -> Note -> -> By default, this flag is enabled when compiling the executable files included with official releases. +If this flag is not specified: +* It will not be possible to perform HTTPS requests +* Only the following symbols will be exposed by the {#link-module||crypto#}: + * {#link-operator||crypto||md5#} + * {#link-operator||crypto||sha1#} + * {#link-operator||crypto||encode#} + * {#link-operator||crypto||decode#} + * {#link-operator||crypto||aes#} #### -d:lite

@@ -72,7 +76,7 @@ * The {#link-module||fs#}

* The {#link-module||sys#} * The following operators: * {#link-operator||lang||load#} - * {#link-operator||lang||read#} + * {#link-operator||lang||require#} * {#link-operator||lang||to-json#} * {#link-operator||lang||from-json#} * {#link-operator||lang||raw-args#}

@@ -86,6 +90,9 @@ * {#link-operator||str||replace#}

* {#link-operator||str||regex#} * {#link-operator||str||semver?#} * {#link-operator||str||from-semver#} + * {#link-operator||str||parse-url#} + * {#link-operator||str||decode-url#} + * {#link-operator||str||encode-url#} * {#link-operator||sys||zip#} * {#link-operator||sys||unzip#}

@@ -94,14 +101,15 @@

* No checks will be performed when defining symbols. * Only the simple REPL will be available. * There will be no support for dynamic libraries. -* The **-m, \-\-module-path** option has no effect. +* The **-m, \-\-module-path** and **-a, \-\-asset-path** options have no effect. * No environment configuration files ([.minrc](class:file), [.min_symbols](class:file)) are used. ## Running the min Shell -To start min shell, run [min -i](class:cmd). You will be presented with a prompt displaying the path to the current directory: +To start the min shell, run [min](class:cmd) with no arguments. You will be presented with a prompt displaying the path to the current directory: > %min-terminal% +> min shell v$versio > [[/Users/h3rald/test]$](class:prompt) You can type min code and press [ENTER](class:kbd) to evaluate it immediately:

@@ -157,13 +165,15 @@ > %min-terminal%

> > [$](class:prompt) min -c myfile.min -n:-d:release -Additionally, you can also use `-m:<path>` (or `--module-path`) to specify one path containing [.min](class:ext) files which will be compiled as well (but not executed) along with the specified file. Whenever a {#link-operator||lang||load#} symbol is used to load an external [.min](class:ext) file, it will attempt to load from the pre-loaded files first before searching the filesystem. +Additionally, you can also use `-m:<path>` (or `--module-path`) to specify one path containing [.min](class:ext) files which will be compiled as well (but not executed) along with the specified file. Whenever a {#link-operator||lang||load#} or a {#link-operator||lang||require#} symbol is used to load/require an external [.min](class:ext) file, it will attempt to retrieve its contents from the pre-loaded files first before searching the filesystem. -For example, the following command executed in the root folder of the min project will compile [run.min](class:file) along with all [.min](class:ext) files included in the [task](class:dir) and its subfolders: +For example, the following command executed in the root folder of the min project will compile [run.min](class:file) along with all [.min](class:ext) files included in the [tasks](class:dir) folder and its subfolders: > %min-terminal% > > [$](class:prompt) min -c run.min -m:tasks + +Similarly, you can also bundle additional files in the executable by specifying the `-a:<path>` (or `--asset-path`) option. At runtime, the compiled min program will attempt to lookup bundled asset files before checking the filesystem. > %note% > Note

@@ -175,3 +185,4 @@

* If you are using [Visual Studio Code](https://code.visualstudio.com/), you can install the official [min extension](https://marketplace.visualstudio.com/items?itemName=h3rald.vscode-min-lang) which provides syntax highlighting support, code folding, and auto-indentation. * If you are using [Vim](https://www.vim.org), a [min.vim](https://github.com/h3rald/min/blob/master/min.vim) syntax definition file is available in the min repo. * If you are using [Sublime Text 3](https://www.sublimetext.com/3), Rafael Carrasco created a min syntax definition file that is available [here](https://github.com/rscarrasco/min-sublime-syntax). +* If you are hsing [Notepad++](https://notepad-plus-plus.org), a [Notepad++ language file](https://github.com/h3rald/min/blob/master/minNotepad++.xml) contributed by baykus871 is available in tbe repo.
M site/contents/home.mdsite/contents/home.md

@@ -12,7 +12,8 @@ <section class="centered pure-u-1 pure-u-md-1-3">

<a class="pure-button pure-button-primary" href="/get-started/"><i class="ti-download"></i> download min v{{$version}}</a><br /> <small> <a href="https://github.com/h3rald/min">Repository</a> | - <a href="https://github.com/h3rald/min/issues">Issue Tracking</a> | + <a href="https://github.com/h3rald/min/issues">Issues</a> | + <a href="https://github.com/h3rald/min/discussions">Discussions</a> | <a href="https://h3rald.com/min/Min_DeveloperGuide.htm">Guide</a> </small> </section>
M site/contents/learn-data-types.mdsite/contents/learn-data-types.md

@@ -5,8 +5,10 @@ -----

{@ _defs_.md || 0 @} -The type system of min is very simple -- only the following data types are available: +The following data types are availanle in {{m}}: +null +: null value. boolean : **true** or **false**. integer

@@ -31,9 +33,26 @@ > "concatenative" :paradigm

> 2017 :"first release year" > } -The {#link-module||logic#} provides predicate operators to check if an element belongs to a particular data type or pseudo-type (`boolean?`, `number?`, `integer?`, `float?`, `string?`, `quotation?`, `dictionary?`). +Additionally, dictionaries can also be typed to denote complex objects like sockets, errors, etc. For example, the following dictionary defines an error: + + { + "MyError" :error + "An error occurred" :message + "symbol1" :symbol + "dir1/file1.min" :filename + 3 :line + 13 :column + ;error + } -Additionally, the {#link-module||lang#} provides operators to convert values from a data type to another (e.g. {#link-operator||lang||int#}, {#link-operator||lang||string#}, and so on). +> %tip% +> Tip +> +> The {#link-operator||dict||dtype#} operator can be used to set the type of a dictionary. + +The {#link-module||logic#} provides predicate operators to check if an element belongs to a particular data type or pseudo-type (`boolean?`, `number?`, `integer?`, `float?`, ...). + +Additionally, the {#link-module||lang#} provides operators to convert values from a data type to another (e.g. {#link-operator||lang||integer#}, {#link-operator||lang||string#}, and so on). > %note% > Note
M site/contents/learn-extending.mdsite/contents/learn-extending.md

@@ -6,11 +6,11 @@ {@ _defs_.md || 0 @}

min provides a fairly complete standard library with many useful modules. However, you may feel the need to extend min in order to perform more specialized tasks. -In such situations, you basically have three options: +In such situations, you basically have the following options: -* Implement new min modules in min -* Embed min in your [Nim](https://nim-lang.org) program -* Implemet min modules as dynamic libraries in Nim +* Implementing new min modules using min itself +* Specifying your custom prelude program +* Embedding min in your [Nim](https://nim-lang.org) program ## Implementing new min modules using min itself

@@ -123,74 +123,3 @@ > Tip

> > For more information on how to create new modules with Nim, have a look in the [lib folder](https://github.com/h3rald/min/tree/master/lib) of the min repository, which contains all the min modules included in the standard library. - -## Implementing min modules as dynamic libraries - -> %warning% -> Warning -> -> This technique is currently highly experimental, it has not been tested extensively and it may not even work properly. - -If you just want to add a new module to min providing functinalities that cannot be built natively with min operators, you can also implement a min module in Nim and compile it to a dynamic library which can be linked dynamically when min is started. - -In order to do this, you don't even need to download the whole min source code, you just need to download the [mindyn.nim](https://github.com/h3rald/min/blob/master/mindyn.nim) file and import it in your Nim program. - -The following code shows how to create a simple min module called *dyntest* containing only a single operator *dynplus*, which essentially returns the sum of two numbers: - -``` -import mindyn - -proc dyntest*(i: In) {.dynlib, exportc.} = - - let def = i.define() - - def.symbol("dynplus") 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.finalize("dyntest") -``` - -Note that the `mindym.nim` file contains the signatures of all the `proc`s that are commonly used to define min modules, but not their implementation. Such `proc`s will become available at run time when the dynamic library is linked to the min executable. - -You can compile the following library by running the following command: - -> %min-terminal% -> [$](class:prompt) nim c \-\-app:lib -d:release \ -> \-\-noMain dyntest.nim - -If you are using [clang](https://clang.llvm.org/) to compile Nim code, you may need to run the following command instead: - -> %min-terminal% -> [$](class:prompt) nim c \-\-app:lib -d:release \-\-noMain \ -> -l:&#34;-undefined dynamic\_lookup&#34; dyntest.nim - -Now you should have a `libdyntest.so|dyn|dll` file. To make min load it and link it automatically when it starts, just run: - -> %min-terminal% -> [$](class:prompt) min \-\-install:libdyntest.dyn - -This command will copy the library file to `$HOME/.minlibs/` (`%HOMEPATH%\.minlibs\` on Windows). min looks for dynamic libraries in this folder when it starts. - -> %note% -> Notes -> -> * The dynamic library file must have the same name as the module it defines (*dyntest* in this case). -> * At startup, min links all your installed dynamic libraries but does not import the modules automatically. - -If you wish to uninstall the library, run the following command instead: - -> %min-terminal% -> [$](class:prompt) min \-\-uninstall:libdyntest.dyn -
M site/contents/learn-operators.mdsite/contents/learn-operators.md

@@ -20,8 +20,18 @@

It is possible to define symbols using the {#link-operator||lang||define#} symbol. The following min program defines a new symbol called square that duplicates the first element on the stack and multiplies the two elements: (dup *) "square" define + +Now, while the {#link-operator||lang||define#} symbol can be fine to define (the equivalent of) variables and simple operators, it is typically better to use the {#link-operator||lang||operator#} symbol instead, as it provides better readability, additional checks and automatic input/output capturing. The previous `square` symbol could also be defined with the {#link-operator||lang||operator#} operator like this: -Besides symbols, you can also define sigila. min provides a set of predefined _sigils_ as abbreviations for for commonly-used symbols. For example, the previous definition could be rewritten as follows using sigils: + ( + symbol square + (num :n ==> num :result) + (n dup * @result) + ) operator + +In this case, note how inputs and outputs are captured into the `n` and `result` symbols in the signature quotation and then referenced in the body quotation. Sure, the original version was much more succinct, but this is definitely more readable. + +Besides symbols, you can also define sigils. min provides a set of predefined _sigils_ as abbreviations for for commonly-used symbols. For example, the previous definition could be rewritten as follows using sigils: (dup *) :square

@@ -43,6 +53,8 @@ \:

: Alias for {#link-operator||lang||define#}. ^ : Alias for {#link-operator||lang||call#}. +* +: Alias for {#link-operator||lang||invoke#}. @ : Alias for {#link-operator||lang||bind#}. >

@@ -79,5 +91,102 @@

'from-json 'j define-sigil This will define a `j` sigil that will parse any string as JSON and convert it to its corresponding min representation. + +Sigils can also (and should!) be defined with the {#link-operator||lang||operator#} operator to add additional checks. The sigil definition above could be rewritten like this, for example: + + ( + sigil j + (str :json ==> a :result) + (json from-json @result) + ) operator + +## Operator signatures + +When defining symbols and sigils witb the {#link-operator||lang||operator#} operator, you must specify a *signature* that will be used to validate and captuee input and output values: + + ( + symbol square + (num :n ==> num :result) + (n dup * @result) + ) operator + +In this case for example tbe `square` symbol expects a number on the stack, which will be captured to tbe symbol `n` and it will place a number on the stack which needs to be bound in the operator body to the symbol `result`. + +In a signature, a type expression must precede the capturing symbol. Such type expression can be: + +* One of the following shorthand symbols identifying a well-known {{m}} base type (see the {#link-page||reference||reference#} section for more information): `a`, `bool`, `null`, `str`, `int`, `num`, `float`, `'sym`, `quot`, or `dict`. +* A typed dictionary like `dict:module` or `dict:datastore`. +* A type class (see below). +* a union of types/typed dictionaries/type classes, like `str|int`. + +> %note% +> Note +> +> If the operator you are defining doesn't require any input value or doesn't leave ang output value on the srack, simply don't put anything before or after the `==>` separator, respectively. For example, the signature of the {#link-operator||lang||puts!#} operator could be written like `(a ==>)`. + +### Type classes + +Besides standard base types, you can define your own *type classes* to express custom constraints/validations for operator input and output values. + +Consider the following type class definition validating a quotation containing strings: + + ((string?) all?) 'strquot typeclass + +The {#link-operator||lang||typeclass#} operator defines a symbol prefixed with `type:` (`type:strquot` in this case) corresponding to a type class that can be used in operator signatures in place of a type, like this: + + ( + symbol join-strings + (strquot :q ==> string :result) + ( + q "" (suffix) reduce @result + ) + ) + +This operator will raise an error if anything other than a quotation of strings is found on the stack. + +> %tip% +> Tip +> +> `type:`-prefixed symbols are just like ordinary shmbols: they are lexically scoped, they can be sealed, unsealed and deleted. + +### Generics + +{{m}} supports generics in operator signatures. in other words, you can define a custom type alias on-the-fly directly in an operator signature, like this: + +``` +( + symbol add + ((string|num|quot :t) :a t :b ==> t :result) + ( + (a type "string" ==) + (a b suffix @result return) + when + (a type "num" ==) + (a b + @result return) + when + (a type "quot" ==) + (a b concat #result return) + when + ) +) :: +``` + +In this case, `t` is set to the type union `stribg|num|quot`, and the `add` method above can be use too sum two numbers or join two strings or quotations. + +Note that the value of `t` is evaluated to the type of the first value that is processed. In other words, the following programs will work as expected: + + 3 5 add ;outputs 8 + + "hello, " "world" ;outputs "hello, world" + +while tbe fullowing will raise an error, because the value of `t` from `num` to `quot` within the same operator use: + + 12 "test" add ;raises an error + +> %sidebar% +> Generics vs type unions +> +> Generics allow to specify a type as a type union, but the type will remain the same one throughout the same operator call. +> By contrast, using the same type union several times within the same signature allows different types to be used in the same call, and that is probably something you dont want! {#link-learn||quotations||Quotations#}
M site/contents/learn.mdsite/contents/learn.md

@@ -6,7 +6,7 @@ {@ _defs_.md || 0 @}

{{learn-links}} -*min* is a stack-based, concatenative programming language that uses postfix notation. If you already know [Forth](http://www.forth.org/), [Factor](http://factorcode.org/) or [Joy](http://www.kevinalbrecht.com/code/joy-mirror/), or if you ever used an [RPN](https://en.wikipedia.org/wiki/Reverse_Polish_notation) calculator, then min will look somewhat familiar to you. +{{m}} is a stack-based, concatenative programming language that uses postfix notation. If you already know [Forth](http://www.forth.org/), [Factor](http://factorcode.org/) or [Joy](http://www.kevinalbrecht.com/code/joy-mirror/), or if you ever used an [RPN](https://en.wikipedia.org/wiki/Reverse_Polish_notation) calculator, then min will look somewhat familiar to you. If not, well, here's how a short min program looks like:
A site/contents/reference-dstore.md

@@ -0,0 +1,59 @@

+----- +content-type: "page" +title: "dstore Module" +----- +{@ _defs_.md || 0 @} + +{#op||dsdelete||{{dstore}} {{sl}}||{{dstore}}|| +Removes an item from the datastore {{dstore}}. The item is uniquely identified by {{sl}}, which contains the collection containing the item and the item id, separated by a forward slash (/). Puts the reference to the modified datastore back on tbe stack. + #} + +{#op||dsdelete!||{{dstore}} {{sl}}||{{none}}|| +Same as `dsdelete`, but doesn't leave anything on the stack. #} + +{#op||dsget||{{dstore}} {{sl}}||{{d}}|| +Retrieves item {{d}} from datastore {{dstore}}. {{d}} is retrieved by specifying {{sl}}, which contains the collection containing the item and the item id, separated by a forward slash (/). + #} + +{#op||dsinit||{{sl}}||{{dstore}}|| +Initializes a bew datastore by creating the {{sl}} JSON file. Puts the datastore instance on the stack. #} + +{#op||dsinit!||{{sl}} {{d}}||{{none}}|| +Same as `dsinit`, but doesn't leave anything on the stack. #} + +{#op||dspost||{{dstore}} {{sl}} {{d}}||{{dstore}}|| +Adds the dictionary {{d}} to the datastore {{dstore}} inside collection {{sl}}, generating and adding a unique **id** field to {{d}}. If the collection {{sl}} does not exist it is created. Puts the reference to the modified datastore back on tbe stack. + #} + +{#op||dspost!||{{dstore}} {{sl}} {{d}}||{{none}}|| +Same as `dspost`, but doesn't leave anything on the stack. #} + +{#op||dsput||{{dstore}} {{sl}} {{d}}||{{dstore}}|| +Adds the dictionary {{d}} to the datastore {{dstore}}. {{sl}} contains the collection where {{d}} will be placed and the id of {{d}}, separated by a forward slash (/). If the collection {{sl}} does not exist it is created. Puts the reference to the modified datastore back on tbe stack. + #} + +{#op||dsput!||{{dstore}} {{sl}}||{{none}}|| +Same as `dsput`, but doesn't leave anything on the stack. #} + +{#op||dsquery||{{dstore}} {{sl}} {{q}}||({{d0p}})|| +> Retrieves a quotation of dictionaries from the collection {{sl}} of datastore {{dstore}} obtained by applying {{q}} as a filter to each item of the collection, picking only the elements that match the filter. +> +> > %sidebar% +> > Example +> > +> > Assuming that **ds** is a datastore, the following program retrieves all elements of teh collection **posts** whose author field is set to "h3rald": +> > +> > ds "posts" (/author "h3rald" ==) dsquery + #} + +{#op||dsread||{{sl}}||{{dstore}}|| +Reads the previously-created datastore from the file {{sl}} and puts the resulting datastore instance on the stack. + #} + +{#op||dswrite||{{dstore}}||{{dstore}}|| +Writes the contents of the datastore {{dstore}} to the filesystem. + #} + +{#op||dswrite!||{{dstore}}||{{none}}|| +Same as `dswrite`, but doesn't leave anything on the stack. #} +
M site/contents/reference-http.mdsite/contents/reference-http.md

@@ -4,7 +4,7 @@ title: "http Module"

----- {@ _defs_.md || 0 @} -{#op||download||{{s1}} {{s2}}||{{null}}|| +{#op||download||{{s1}} {{s2}}||{{none}}|| Downloads the contents of URL {{s1}} to the local file {{s2}}. #} {#op||get-content||{{s1}}||{{s2}}||

@@ -24,7 +24,7 @@ > > "http://httpbin.org/ip" %url

> > request #} -{#op||start-server||{{d}}||{{null}}|| +{#op||start-server||{{d}}||{{none}}|| > Starts an HTTP server based on the configuration provided in {{d}}. > > {{d}} is a dictionary containing the following keys:

@@ -80,5 +80,5 @@ > > "Press Ctrl+C to stop." puts!

> > start-server #} -{#op||stop-server||{{null}}||{{null}}|| -Stops the currently-running HTTP server. This operator should be used within an HTTP server handler quotation.#} +{#op||stop-server||{{none}}||{{none}}|| +Stops the currently-running HTTP server. This operator should be used within an HTTP server handler quotation.#}
M site/contents/reference-io.mdsite/contents/reference-io.md

@@ -12,7 +12,7 @@ > Prints {{s2}}, then prints all {{s1}} included in the quotation prepended with a number, and waits from valid input from the user.

> > If the user enters a number that matches one of the choices, then the corresponding quotation {{q1}} is executed, otherwise the choice menu is displayed again until a valid choice is made. #} -{#op||clear||{{null}}||{{null}}|| +{#op||clear||{{none}}||{{none}}|| Clears the screen.#} {#op||column-print||{{q}} {{i}}||{{any}}||

@@ -21,17 +21,23 @@

{#op||confirm||{{s}}||{{b}}|| > Prints {{s}} (prompt) appending `" [yes/no]: "`, reads a line from STDIN and: > -> * if it matches `/^y(es)$/i`, puts {{t}} on the stack. +> * if it matches `/^y(es)?$/i`, puts {{t}} on the stack. > * if it matches `/^no?$/i`, puts {{f}} on the stack. > * Otherwise, it prints `Invalid answer. Please enter 'yes' or 'no': ` and waits for a new answer. #} {#op||debug||{{any}}||{{any}}|| Prints {{any}} and a new line to STDOUT, if logging level is set to [debug](class:kwd) or lower.#} +{#op||debug!||{{any}}||{{none}}|| +Prints {{any}} (removing it from the stack) and a new line to STDOUT, if logging level is set to [debug](class:kwd) or lower.#} + {#op||error||{{any}}||{{any}}|| Prints {{any}} and a new line to STDERR, if logging level is set to [error](class:kwd) or lower.#} -{#op||fappend||{{s1}} {{s2}}||{{null}}|| +{#op||error!||{{any}}||{{none}}|| +Prints {{any}} (removing it from the stack) and a new line to STDERR, if logging level is set to [error](class:kwd) or lower.#} + +{#op||fappend||{{s1}} {{s2}}||{{none}}|| Appends {{s1}} to the end of file {{s2}}. #} {#op||fatal||{{any}}||{{any}}||

@@ -40,16 +46,19 @@

{#op||fread||{{s}}||{{s}}|| Reads the file {{s}} and puts its contents on the top of the stack as a string.#} -{#op||fwrite||{{s1}} {{s2}}||{{null}}|| +{#op||fwrite||{{s1}} {{s2}}||{{none}}|| Writes {{s1}} to the file {{s2}}, erasing all its contents first. #} -{#op||getchr||{{null}}||{{i}}|| +{#op||getchr||{{none}}||{{i}}|| Reads single character from STDIN without waiting for ENTER key and places its ASCII code on top of the stack.#} {#op||info||{{any}}||{{any}}|| Prints {{any}} and a new line to STDOUT, if logging level is set to [info](class:kwd) or lower.#} -{#op||mapkey||{{q}} {{sl}}||{{null}}|| +{#op||info!||{{any}}||{{none}}|| +Prints {{any}} (removing it from the stack) and a new line to STDOUT, if logging level is set to [info](class:kwd) or lower.#} + +{#op||mapkey||{{q}} {{sl}}||{{none}}|| > Maps the named key/key combination {{sl}} to the quotation {{q}}, so that {{q}} is executed when key {{sl}} is pressed. > > > %note%

@@ -67,19 +76,22 @@ > > (clear) 'ctrl+l keymap

> > > > causes the `CTRL+L` key to clear the screen. #} -{#op||newline||{{null}}||{{null}}|| +{#op||newline||{{none}}||{{none}}|| Prints a new line to STDOUT.#} {#op||notice||{{any}}||{{any}}|| Prints {{any}} and a new line to STDOUT, if logging level is set to [notice](class:kwd) (default) or lower.#} -{#op||password||{{null}}||{{s}}|| +{#op||notice!||{{any}}||{{none}}|| +Prints {{any}} (removing it from the stack) and a new line to STDOUT, if logging level is set to [notice](class:kwd) (default) or lower.#} + +{#op||password||{{none}}||{{s}}|| Reads a line from STDIN displaying \* for each typed character, and places it on top of the stack as a string.#} {#op||print||{{any}}||{{any}}|| Prints {{any}} to STDOUT.#} -{#op||print!||{{any}}||{{null}}|| +{#op||print!||{{any}}||{{none}}|| Prints {{any}} to STDOUT and removes {{any}} from the stack.#} {#op||putchr||{{s}}||{{any}}||

@@ -88,7 +100,7 @@

{#op||type||{{any}}||{{s}}|| Puts the data type of {{any}} on the stack. In cased of typed dictionaries, the type name is prefixed by `dict:`, e.g. `dict:module`, `dict:socket`, etc.#} -{#op||unmapkey||{{sl}}||{{null}}|| +{#op||unmapkey||{{sl}}||{{none}}|| > Unmaps a previously-mapped key or key-combination {{sl}}, restoring the default mapping if available. > > > %note%

@@ -96,8 +108,10 @@ > > Notes

> > > > * At present, only the key names and sequences defined in the [nimline](https://h3rald.com/nimline/nimline.html) library are supported. > > * At present, all the default mappings of min are those provided by the [nimline](https://h3rald.com/nimline/nimline.html) library. -#} + #} {#op||warning||{{any}}||{{any}}|| Prints {{any}} and a new line to STDERR, if logging level is set to [warning](class:kwd) or lower.#} +{#op||warning!||{{any}}||{{none}}|| +Prints {{any}} (removing it from the stack) and a new line to STDERR, if logging level is set to [warning](class:kwd) or lower.#}
M site/contents/reference-lang.mdsite/contents/reference-lang.md

@@ -12,6 +12,8 @@ {#sig||:||define#}

{#alias||:||define#} +{#alias||::||operator#} + {#sig||~||delete#} {#sig||+||module#}

@@ -19,6 +21,8 @@

{#sig||^||call#} {#alias||^||call#} + +{#sig||*||invoke#} {#sig||@||bind#}

@@ -36,6 +40,11 @@ {#alias||><||infix-dequote#}

{#alias||=>||apply#} +{#op||==>||{{none}}||{{none}}|| +Symbol used to separate input and output calues in operator signatures.#} + +{#alias||=-=||expect-empty-stack#} + {#sig||#||quote-bind#} {#alias||#||quote-bind#}

@@ -45,21 +54,19 @@

{#alias||=||quote-define#} {#op||apply||{{q}}|{{d}}||({{a0p}})|{{{a0p}}}|| -> This operator can be used on a quotation or a dictionary: -> -> * If a quotation {{q}} is passed, it returns a new quotation obtained by evaluating each element of {{q}} in a separate stack. -> * If a dictionary {{d}} (with values and keys) is passed, it returns a new dictionary obtained by evaluating each value in the dict that is a symbol in a separate stack (values that aren't symbols stay as they are).#} +Returns a new quotation obtained by evaluating each element of {{q}} in a separate stack. #} -{#op||args||{{null}}||{{q}}|| +{#op||args||{{none}}||{{q}}|| Returns a list of all arguments passed to the current program.#} -{#op||bind||{{any}} {{sl}}||{{null}}|| +{#op||bind||{{any}} {{sl}}||{{none}}|| Binds the specified value (auto-quoted) to an existing symbol {{sl}}.#} {#op||bool||{{any}}||{{b}}|| > Converts {{any}} to a boolean value based on the following rules: > > * If {{any}} is a boolean value, no conversion is performed. +> * If {{any}} is {{null}}, it is converted to {{f}}. > * If {{any}} is a numeric value, zero is converted to {{f}}, otherwise it is converted to {{t}}. > * If {{any}} is a quotation or a dictionary, the empty quotation or dictionary is converted to {{f}}, otherwise it is converted to {{t}}. > * If {{any}} is a string, the empty string, and `"false"` are converted to {{f}}, otherwise it is converted to {{t}}.#}

@@ -86,13 +93,13 @@ > > ((< 3) ("Smaller than 3" put!))

> > ((true) ("Exactly 3" put!)) > > ) case #} -{#op||compiled?||{{null}}||{{b}}|| +{#op||compiled?||{{none}}||{{b}}|| Returns {{t}} if the current program has been compiled.#} -{#op||define||{{any}} {{sl}}||{{null}}|| +{#op||define||{{any}} {{sl}}||{{none}}|| Defines a new symbol {{sl}}, containing the specified value (auto-quoted if not already a quotation).#} -{#op||define-sigil||{{any}} {{sl}}||{{null}}|| +{#op||define-sigil||{{any}} {{sl}}||{{none}}|| Defines a new sigil {{sl}}, containing the specified value (auto-quoted if not already a quotation).#} {#op||defined?||{{sl}}||{{b}}||

@@ -101,10 +108,10 @@

{#op||defined-sigil?||{{sl}}||{{b}}|| Returns {{t}} if the symbol {{sl}} is defined, {{f}} otherwise.#} -{#op||delete||{{sl}}||{{null}}|| +{#op||delete||{{sl}}||{{none}}|| Deletes the specified symbol {{sl}}.#} -{#op||delete-sigil||{{sl}}||{{null}}|| +{#op||delete-sigil||{{sl}}||{{none}}|| Deletes the specified user-defined sigil {{sl}}.#} {#op||dequote||{{q}}||{{a0p}}||

@@ -115,7 +122,7 @@

{#op||eval||{{s}}||{{a0p}}|| Parses and interprets {{s}}. #} -{#op||exit||{{i}}||{{null}}|| +{#op||exit||{{i}}||{{none}}|| Exits the program or shell with {{i}} as return code. #} {#op||expect||{{q1}}||{{q2}}||

@@ -138,12 +145,16 @@ > > the following program evaluates to `true`:

> > > > `(int string num) expect (3.4 "test" 1) ==`#} +{#op||expect-empty-stack||{{none}}||{{none}}|| +Raises an error if the stack is not empty.#} + {#op||float||{{any}}||{{flt}}|| > Converts {{any}} to an integer value based on the following rules: > > * If {{any}} is {{t}}, it is converted to `1.0`. > * If {{any}} is {{f}}, it is converted to `0.0`. -> * If {{any}} is a integer, it is converted to float value. +> * If {{any}} is {{null}}, it is converted to `0.0` +>. * If {{any}} is a integer, it is converted to float value. > * If {{any}} is a float, no conversion is performed. > * If {{any}} is a string, it is parsed as a float value.#}

@@ -171,13 +182,13 @@ > > Note

> > > > At present, only YAML objects containing string values are supported.#} -{#op||gets||{{null}}||{{s}}|| +{#op||gets||{{none}}||{{s}}|| Reads a line from STDIN and places it on top of the stack as a string.#} {#op||if||{{q1}} {{q2}} {{q3}}||{{a0p}}|| If {{q1}} evaluates to {{t}} then evaluates {{q2}}, otherwise evaluates {{q3}}.#} -{#op||import||{{sl}}||{{null}}|| +{#op||import||{{sl}}||{{none}}|| Imports the a previously-loaded module {{sl}}, defining all its symbols in the current scope. #} {#op||infix-dequote||{{q}}||{{any}}||

@@ -202,10 +213,25 @@ > Converts {{any}} to an integer value based on the following rules:

> > * If {{any}} is {{t}}, it is converted to `1`. > * If {{any}} is {{f}}, it is converted to `0`. +> * If {{any}} is {{null}}, it is converted to `0`. > * If {{any}} is an integer, no conversion is performed. > * If {{any}} is a float, it is converted to an integer value by truncating its decimal part. > * If {{any}} is a string, it is parsed as an integer value.#} +{#op||invoke||{{sl}}||{{a0p}}|| +> Assming that {{sl}} is a formatted like *dictionary*/*symbol*, calls *symbol* defined in *dictionary* (note that this also works for nested dictionaries. +> +> > %sidebar% +> > Example +> > +> > The following program leaves `100` on the stack: +> > +> > {{100 :b} :a} :test *test/a/b + #} + +{#op||line-info||{{none}}||{{d}}|| +Returns a dictionary {{d}} containing a **filename**, **line**, and **column** properties identifying the filename, line and column of the current symbol.#} + {#op||linrec||{{q1}} {{q2}} {{q3}} {{q4}}||{{a0p}}|| > Implements linear recursions as follows: >

@@ -222,16 +248,16 @@ > >

> > 5 (dup 0 ==) 'succ (dup pred) '* linrec #} -{#op||lite?||{{null}}||{{b}}|| +{#op||lite?||{{none}}||{{b}}|| Returns {{t}} if min was built in _lite_ mode. #} {#op||load||{{sl}}||{{a0p}}|| -Parses and interprets the specified {{m}} file, adding [.min](class:ext) if not specified. #} +Parses and interprets the specified {{m}} file {{sl}}, adding [.min](class:ext) if not specified. #} {#op||load-symbol||{{sl}}||{{a0p}}|| Loads the contents of symbol {{sl}} from the [.min\_symbols](class:file) file. #} -{#op||loglevel||{{sl}}||{{null}}|| +{#op||loglevel||{{sl}}||{{none}}|| > Sets the current logging level to {{sl}}. {{sl}} must be one of the following strings or quoted symbols: > > * debug

@@ -246,13 +272,54 @@ > > Note

> > > > The default logging level is _notice_.#} -{#op||loglevel?||{{null}}||{{s}}|| +{#op||loglevel?||{{none}}||{{s}}|| Returns the current log level (debug, info, notive, warn, error or fatal). #} -{#op||module||{{d}} {{sl}}||{{null}}|| +{#op||module||{{d}} {{sl}}||{{none}}|| Creates a new module {{sl}} based on dictionary {{d}}. #} -{#op||opts||{{null}}||{{d}}|| +{#op||operator||{{q}}||{{a0p}}|| +> Provides a way to define a new operator (symbol or sigil) on the current scope performing additional checks (compared to `define` and `define-sigil`), and automatically mapping inputs and outputs. +> +> {{q}} is a quotation containing: +> +> * A symbol identifying the type of operator to define (`symbol` or `sigil`). +> * A symbol identifying the name of the operator. +> * A quotation defining the signature of the operatorm containing input and output values identified by their type and a capturing symbol, separated by the `==>` symbol. +> * A quotation identifying the body of the operator. +> +> The main additional features offered by this way of defining operators are the following: +> +> * Both input and output values are checked against a type (like when using the `expect` operator *and* automatically captured in a symbol that can be referenced in the operator body quotation. +> * The full signature of the operator is declared, making the resulting code easier to understand at quick glance. +> * An exception is automatically raised if the operator body pollutes the stack by adding or removing elementa from the stack (besides adding the declared output values). +> * It is possible to use the `return` symbol within the body quotation to immediately stop the evaluation of the body quotation and automatically push the output values on the stack. +> +> > %sidebar% +> > Example +> > +> > The following program defines a `pow` operator that calculates the power of a number providing its base and exponent, and handling some NaN results using the `return` symbol: +> > +> > ( +> > symbol pow +> > (num :base int :exp ==> num :result) +> > ( +> > (base 0 == exp 0 == and) +> > (nan @result return) +> > when +> > (base 1 == exp inf == and) +> > (nan @result return) +> > when +> > (base inf == exp 0 == and) +> > (nan @result return) +> > when +> > exp 1 - :n +> > base (dup) n times (*) n times @result +> > ) +> > ) :: + #} + +{#op||opts||{{none}}||{{d}}|| Returns a dictionary of all options passed to the current program, with their respective values.#} {#op||parse||{{s}}||{{q}}||

@@ -269,14 +336,14 @@ > >

> > (* 8 4) prefix-dequote #} -{#op||prompt||{{null}}||{{s}}|| +{#op||prompt||{{none}}||{{s}}|| > This symbol is used to configure the prompt of the min shell. By default, it is set to the following quotation: > > ("[$1]$$ " (.) => %) > > Unlike other predefined symbols, this symbol is _unsealed_, which means it can be modified.#} -{#op||publish||{{sl}} {{d}}||{{null}}|| +{#op||publish||{{sl}} {{d}}||{{none}}|| > Publishes symbol {{sl}} to the scope of {{d}}. > > > %sidebar%

@@ -288,43 +355,51 @@

{#op||puts||{{any}}||{{any}}|| Prints {{any}} and a new line to STDOUT.#} -{#op||puts!||{{any}}||{{null}}|| +{#op||puts!||{{any}}||{{none}}|| Prints {{any}} and a new line to STDOUT, removing {{any}} from the stack.#} -{#op||quit||{{null}}||{{null}}|| +{#op||quit||{{none}}||{{none}}|| Exits the program or shell with 0 as return code. #} {#op||quote||{{any}}||({{any}})|| Wraps {{any}} in a quotation. #} -{#op||quote-bind||{{any}} {{sl}}||{{null}}|| +{#op||quote-bind||{{any}} {{sl}}||{{none}}|| Quotes {{any}} and binds the quotation to the existing symbol {{sl}}. #} -{#op||quote-define||{{any}} {{sl}}||{{null}}|| +{#op||quote-define||{{any}} {{sl}}||{{none}}|| Quotes {{any}} and assigns the quotation to the symbol {{sl}}, creating it if not already defined. #} -{#op||raise||{{e}}||{{null}}|| +{#op||raise||{{e}}||{{none}}|| Raises the error specified via the dictionary {{e}}.#} -{#op||read||{{sl}}||{{q}}|| -Reads and parses the specified {{m}} file {{sl}} and returns a quoted program {{q}}. #} +{#op||raw-args||{{none}}||{{q}}|| +Returns a list of all arguments and (non-parsed) options passed to the current program.#} -{#op||remove-symbol||{{sl}}||{{null}}|| +{#op||remove-symbol||{{sl}}||{{none}}|| Removes the symbol {{sl}} from the [.min\_symbols](class:file) file. #} -{#op||ROOT||{{null}}||{{d}}|| +{#op||require||{{sl}}||{{d}}|| +Parses and interprets (in a separater interpreter) the specified {{m}} file {{sl}}, adding [.min](class:ext) if not specified, and returns a module dictionary {{d}} containing all the symbols defined in {{sl}}. #} + +{#op||return||{{none}}||{{none}}|| +If used within the body quotation of an operator definition, causes the interpreter to stop pushing further body elements on the stack and start pushing tbe operator output values on the stack. + +If used outside of the body quotation of an operator definition, it raises an exception.#} + +{#op||ROOT||{{none}}||{{d}}|| Returns a module holding a reference to the [ROOT](class:kwd) scope. > > %tip% > > Tip > > > > This symbol is very useful in conjunction with the **with** operator. -#} + #} -{#op||save-symbol||{{sl}}||{{null}}|| +{#op||save-symbol||{{sl}}||{{none}}|| Saves the contents of symbol {{sl}} to the [.min\_symbols](class:file) file. #} -{#op||scope||{{null}}||{{d}}|| +{#op||scope||{{none}}||{{d}}|| > Returns a dictionary {{d}} holding a reference to the current scope. > > This can be useful to save a reference to a given execution scope to access later on.

@@ -334,10 +409,10 @@ > > Example

> > > > The following program leaves `{(2) :two ;module}` on the stack: > > -> > {} :myscope (2 :due scope @myscope) -> +> > {} :myscope (2 :two scope @myscope) -> #} -{#op||saved-symbols||{{null}}||({{s0p}})|| +{#op||saved-symbols||{{none}}||({{s0p}})|| Returns a quotation containing all symbols saved in the [.min\_symbols](class:file) file. #} {#op||scope-sigils||{{d}}||({{s0p}})||

@@ -346,16 +421,22 @@

{#op||scope-symbols||{{d}}||({{s0p}})|| Returns a list of all symbols defined in dictionary {{d}}.#} -{#op||seal||{{sl}}||{{null}}|| +{#op||seal||{{sl}}||{{none}}|| Seals symbol {{sl}}, so that it cannot be re-assigned. #} -{#op||seal-sigil||{{sl}}||{{null}}|| +{#op||seal-sigil||{{sl}}||{{none}}|| Seals the user-defined sigil {{sl}}, so that it cannot be re-defined. #} + +{#op||sealed?||{{sl}}||{{b}}|| +Returns {{t}} if the symbol {{sl}} is sealed, {{f}} otherwise.#} + +{#op||sealed-sigil?||{{sl}}||{{b}}|| +Returns {{t}} if the sigil {{sl}} is sealed, {{f}} otherwise.#} {#op||set-type||{{d}} {{sl}}||{{d}}|| Sets the type for dictionary {{d}} to {{sl}}.#} -{#op||sigils||{{null}}||({{s0p}})|| +{#op||sigils||{{none}}||({{s0p}})|| Returns a list of all sigils defined in the [ROOT](class:kwd) scope.#} {#op||source||{{sl}}||{{q}}||

@@ -364,7 +445,7 @@

{#op||string||{{any}}||{{s}}|| Converts {{any}} to its string representation.#} -{#op||symbols||{{null}}||({{s0p}})|| +{#op||symbols||{{none}}||({{s0p}})|| Returns a list of all symbols defined in the [ROOT](class:kwd) scope.#} {#op||tap||{{any}} {{q}}||{{any}}||

@@ -381,13 +462,12 @@ > > Example

> > > > The following program: > > -> > ( -> > (("a" 1) ("b" 2) ("c" 3)) ( +> > {1 :a 2 :b 3 :c} ( > > (dup /a succ succ %a) > > (dup /b succ %b) > > ) tap > > -> > Returns `(("a" 3) ("b" 3) ("c" 3))`.#} +> > Returns `{3 :a 3 :b 3 :c}`.#} {#op||tap!||{{any}} {{q}}||{{any}}|| > Performs the following operations:

@@ -419,6 +499,7 @@ Converts {{any}} into a JSON string.#}

{#op||to-yaml||{{any}}||{{s}}|| > Converts {{any}} into a YAML string. +> > > %note% > > Note > >

@@ -444,16 +525,34 @@ > > (format-error puts)

> > (0) > > ) try #} +{#op||typeclass||{{q}} {{sl}}||{{none}}|| +> Defines a new type class {{sl}} set to quotation {{q}}, which can be used in operator signatures. +> +> > %sidebar% +> > Example +> > +> > Consider the following type class which defines a natural number: +> > +> > (:n ((n integer?) (n 0 >)) &&) 'natural typeclass +> > +> > It can now be used in operator signatures, like this: +> > +> > ( +> > symbol natural-sum +> > (natural :n natural :m ==> natural :result) +> > (n m + @result) +> > ) :: #} + {#op||unless||{{q1}} {{q2}}||{{a0p}}|| If {{1}} evaluates to {{f}} then evaluates {{2}}.#} -{#op||unseal||{{sl}}||{{null}}|| +{#op||unseal||{{sl}}||{{none}}|| Unseals the user-defined symbol {{sl}}, so that it can be re-assigned. #} -{#op||unseal-sigil||{{sl}}||{{null}}|| +{#op||unseal-sigil||{{sl}}||{{none}}|| Unseals sigil {{sl}}, so that it can be re-defined (system sigils cannot be unsealed). #} -{#op||version||{{null}}||{{s}}|| +{#op||version||{{none}}||{{s}}|| Returns the current min version number. #} {#op||when||{{q1}} {{q2}}||{{a0p}}||

@@ -487,4 +586,4 @@ > > 'net import

> > 'http import > > ) ROOT with > > ) unless -#} + #}
M site/contents/reference-logic.mdsite/contents/reference-logic.md

@@ -44,51 +44,16 @@

{#op||boolean?||{{any}}||{{b}}|| Returns {{t}} if {{any}} is a boolean, {{f}} otherwise. #} -{#op||dequote-and||{{a1}} {{a2}}||{{b}}|| -> Short-circuited logical and. It performs the following operations: -> -> 1. Pops {{a1}} and {{a2}} off the stack. -> 2. Dequotes {{a1}}, if {{f}} is on the stack, it pushes {{f}} on the stack and stops, otherwise it carries on. -> 3. Dequotes {{a2}}. -> 4. If {{a2}} is {{t}}, it pushes {{t}} on the stack. -> -> > %note% -> > Note -> > -> > {{a1}} (and {{a2}}, if dequoted) must evaluate to a boolean value, otherwise an exception is raised. -> -> > %sidebar% -> > Example -> > -> > The following program returns {{f}} and never executes the second quotation. -> > -> > "test" :x (x number?) (x 5 <) dequote-and +{#op||dictionary?||{{any}}||{{b}}|| +Returns {{t}} if {{any}} is a dictionary, {{f}} otherwise. #} +{#op||expect-all||{{q}}||{{b}}|| +Assuming that {{q}} is a quotation of quotations each evaluating to a boolean value, it pushes {{t}} on the stack if they all evaluate to {{t}}, {{f}} otherwise. #} - -{#op||dequote-or||{{a1}} {{a2}}||{{b}}|| -> Short-circuited logical or. It performs the following operations: -> -> 1. Pops {{a1}} and {{a2}} off the stack. -> 2. Dequotes {{a1}}, if {{t}} is on the stack, it pushes {{t}} on the stack and stops, otherwise it carries on. -> 3. Dequotes {{a2}}. -> 4. If {{a2}} is {{f}}, it pushes {{f}} on the stack. -> -> > %note% -> > Note -> > -> > {{a1}} (and {{a2}}, if dequoted) must evaluate to a boolean value, otherwise an exception is raised. -> -> > %sidebar% -> > Example -> > -> > The following program returns {{t}} and never executes the second quotation. -> > -> > "test" :x (x string?) (x quotation?) dequote-or + +{#op||expect-any||{{q}}||{{b}}|| +Assuming that {{q}} is a quotation of quotations each evaluating to a boolean value, it pushes {{t}} on the stack if any evaluates to {{t}}, {{f}} otherwise. #} - -{#op||dictionary?||{{any}}||{{b}}|| -Returns {{t}} if {{any}} is a dictionary, {{f}} otherwise. #} {#op||float?||{{any}}||{{b}}|| Returns {{t}} if {{any}} is a float, {{f}} otherwise. #}

@@ -102,11 +67,20 @@

{#op||not||{{b1}}||{{b2}}|| Negates {{b1}}.#} +{#op||null?||{{any}}||{{b}}|| +Returns {{t}} if {{any}} is {{null}}, {{f}} otherwise. #} + {#op||number?||{{any}}||{{b}}|| Returns {{t}} if {{any}} is a number, {{f}} otherwise. #} {#op||quotation?||{{any}}||{{b}}|| Returns {{t}} if {{any}} is a quotation, {{f}} otherwise. #} + +{#op||string?||{{any}}||{{b}}|| +Returns {{t}} if {{any}} is a string, {{f}} otherwise. #} + +{#op||stringlike?||{{any}}||{{b}}|| +Returns {{t}} if {{any}} is a string or a quoted symbol, {{f}} otherwise. #} {#op||type?||{{any}} {{sl}}||{{b}}|| Returns {{t}} if the data type of {{any}} is the specified type {{sl}}, {{f}} otherwise. #}
M site/contents/reference-math.mdsite/contents/reference-math.md

@@ -4,6 +4,9 @@ title: "math Module"

----- {@ _defs_.md || 0 @} +{#op||abs||{{n1}}||{{n2}}|| +Calculates tbe absolute value of {{n1}}. #} + {#op||acos||{{n1}}||{{n2}}|| Calculates the arc cosine of {{n1}} (in radians). #}

@@ -25,7 +28,7 @@

{#op||d2r||{{n1}}||{{n2}}|| Converts {{n1}} from degrees to radians. #} -{#op||e||{{null}}||{{n}}|| +{#op||e||{{none}}||{{n}}|| Returns the value of the _e_ constant (Euler's number). #} {#op||floor||{{n}}||{{i}}||

@@ -40,7 +43,7 @@

{#op||log2||{{n1}}||{{n2}}|| Calculates the binary logarithm of {{n1}}. #} -{#op||pi||{{null}}||{{n}}|| +{#op||pi||{{none}}||{{n}}|| Returns the value of the &pi; constant. #} {#op||pow||{{n1}} {{n2}}||{{n3}}||

@@ -67,9 +70,8 @@

{#op||tanh||{{n1}}||{{n2}}|| Calculates the hyperbolic tangent of {{n1}} (in radians). #} -{#op||tau||{{null}}||{{n}}|| +{#op||tau||{{none}}||{{n}}|| Returns the value of the &tau; constant (2&pi;). #} {#op||trunc||{{n1}}||{{n2}}|| Truncates {{n}} to the decimal point. #} -
M site/contents/reference-net.mdsite/contents/reference-net.md

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

{#op||accept||{{sock1}} {{sock2}}||{{sock1}}|| Makes {{sock2}} (server) accept a connection from {{sock1}} (client). Returns the client socket {{sock1}} from which it will be possible to receive data from. #} -{#op||close||{{sock}}||{{null}}|| +{#op||close||{{sock}}||{{none}}|| Closes a previously-opened socket. #} {#op||connect||{{sock}} {{s}} {{i}}||{{sock}}||

@@ -85,7 +85,7 @@ > > cli recv-line puts @line

> > ) while #} -{#op||send||{{sock}} {{s}}||{{null}}|| +{#op||send||{{sock}} {{s}}||{{none}}|| Sends {{s}} to the connected socket {{sock}}. #} {#op||socket||{{d}}||{{sock}}||

@@ -116,4 +116,3 @@ > * **ipv6**: Internet Protocol version 6 {{no-win}}.

> * **raw**: Raw IP Packets protocol {{no-win}}. > * **icmp**: Internet Control Message Protocol {{no-win}}. #} -
M site/contents/reference-num.mdsite/contents/reference-num.md

@@ -10,7 +10,7 @@

{#op||-||{{n1}} {{n2}}||{{n3}}|| Subtracts {{n2}} from {{n1}}. #} -{#op||-inf||{{null}}||{{n}}|| +{#op||-inf||{{none}}||{{n}}|| Returns negative infinity. #} {#op||\*||{{n1}} {{n2}}||{{n3}}||

@@ -25,13 +25,13 @@

{#op||div||{{i1}} {{i2}}||{{i3}}|| Divides {{i1}} by {{i2}} (integer division). #} -{#op||inf||{{null}}||{{n}}|| +{#op||inf||{{none}}||{{n}}|| Returns infinity. #} {#op||mod||{{i1}} {{i2}}||{{i3}}|| Returns the integer module of {{i1}} divided by {{i2}}. #} -{#op||nan||{{null}}||nan|| +{#op||nan||{{none}}||nan|| Returns **NaN** (not a number). #} {#op||odd?||{{i}}||{{b}}||

@@ -48,11 +48,11 @@ > > Note

> > > > You must call `randomize` to initialize the random number generator, otherwise the same sequence of numbers will be returned.#} -{#op||randomize||{{null}}||{{null}|| +{#op||randomize||{{none}}||{{null}|| Initializes the random number generator using a seed based on the current timestamp. #} {#op||succ||{{i1}}||{{i2}}|| Returns the successor of {{i1}}.#} {#op||sum||{{q}}||{{i}}|| -Returns the sum of all items of {{q}}. {{q}} is a quotation of integers. #} +Returns the sum of all items of {{q}}. {{q}} is a quotation of integers. #}
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}}. #}

@@ -34,7 +44,7 @@ > > (1 37 34 2 6 8 12 21)

> > (dup 20 < swap even? and) filter #} {#op||find||{{q1}} {{q2}}||{{i}}|| -> Returns the index of the first element within {{q1}} that satisfies predicate {{q2}}. +> Returns the index of the first element within {{q1}} that satisfies predicate {{q2}}, or -1 if no element satisfies it. > > > %sidebar% > > Example

@@ -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}}. #}

@@ -98,10 +118,13 @@ >

> > %sidebar% > > Example > > -> > The following program leaves `(1 2 3) (2 4 6)` on the stack: +> > The following program leaves `(1 3 5) (2 4 6)` on the stack: > > > > (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 site/contents/reference-stack.mdsite/contents/reference-stack.md

@@ -4,7 +4,7 @@ title: "stack Module"

----- {@ _defs_.md || 0 @} -{#op||clear-stack||{{any}}||{{null}}|| +{#op||clear-stack||{{any}}||{{none}}|| Empties the stack.#} {#op||cleave||{{a1}} ({{q}}{{0p}})||{{a0p}}||

@@ -25,10 +25,10 @@

{#op||dup||{{a1}}||{{a1}} {{a1}}|| Duplicates the first element on the stack.#} -{#op||get-stack||{{null}}||({{a0p}})|| +{#op||get-stack||{{none}}||({{a0p}})|| Puts a quotation containing the contents of the stack on the stack.#} -{#op||id||{{null}}||{{null}}|| +{#op||id||{{none}}||{{none}}|| Does nothing.#} {#op||keep||{{a1}} {{q}}||{{a0p}} {{a1}}||

@@ -49,7 +49,7 @@

{#op||pick||{{a1}} {{a2}} {{a3}}||{{a1}} {{a2}} {{a3}} {{a1}}|| Pushes a copy of the third element on top of the stack.#} -{#op||pop||{{any}}||{{null}}|| +{#op||pop||{{any}}||{{none}}|| Removes the first element from the stack.#} {#op||rolldown||{{a1}} {{a2}} {{a3}}||{{a2}} {{a3}} {{a1}}||

@@ -77,4 +77,4 @@ {#op||swap||{{a1}} {{a2}}||{{a2}} {{a1}}||

Swaps the first two elements on the stack. #} {#op||swons||({{a0p}}) {{a1}}||({{a1}} {{a0p}})|| -Prepends {{a1}} to the quotation that follows it.#} +Prepends {{a1}} to the quotation that follows it.#}
M site/contents/reference-str.mdsite/contents/reference-str.md

@@ -71,6 +71,9 @@

{#op||ord||{{s}}||{{i}}|| Returns the ASCII code {{i}} corresponding to the single character {{s}}.#} +{#op||parse-url||{{s}}||{{url}}|| +Parses the url {{s}} into its components and stores them into {{url}}.#} + {#op||prefix||{{sl1}} {{sl2}}||{{s}}|| Prepends {{sl2}} to {{sl1}}.#}
M site/contents/reference-sys.mdsite/contents/reference-sys.md

@@ -18,13 +18,13 @@ {#sig||&||run#}

{#alias||&||run#} -{#op||.||{{null}}||{{s}}|| +{#op||.||{{none}}||{{s}}|| Returns the full path to the current directory. #} -{#op||..||{{null}}||{{s}}|| +{#op||..||{{none}}||{{s}}|| Returns the full path to the parent directory. #} -{#op||chmod||{{sl}} {{i}}||{{null}}|| +{#op||chmod||{{sl}} {{i}}||{{none}}|| > Sets the permissions of file or directory {{sl}} to {{i}}. {{i}} is a three-digit representation of user, group and other permissions. See the [Unix Permissions Calculator](http://permissions-calculator.org/) for examples and conversions. > > > %sidebar%

@@ -34,13 +34,13 @@ > > The following program makes the file **/tmp/test.txt** readable, writable and executable by its owner, and readable and executable by users of the same group and all other users:

> > > > `/tmp/test.txt 755 chmod`#} -{#op||cd||{{sl}}||{{null}}|| +{#op||cd||{{sl}}||{{none}}|| Change the current directory to {{{sl}}. #} -{#op||cp||{{sl1}} {{sl2}}||{{null}}|| +{#op||cp||{{sl1}} {{sl2}}||{{none}}|| Copies the file or directory {{sl1}} to {{sl2}}. #} -{#op||cpu||{{null}}||{{s}}|| +{#op||cpu||{{none}}||{{s}}|| Returns the host CPU. It can be one of the following strings i386, alpha, powerpc, powerpc64, powerpc64el, sparc, amd64, mips, mipsel, arm, arm64. #} {#op||env?||{{sl}}||{{b}}||

@@ -64,7 +64,7 @@

{#op||get-env||{{sl}}||{{s}}|| Returns environment variable {{sl}}. #} -{#op||hardlink||{{sl1}} {{sl2}}||{{null}}|| +{#op||hardlink||{{sl1}} {{sl2}}||{{none}}|| Creates hardlink {{sl2}} for file or directory {{sl1}}. #} {#op||ls||{{sl}}||{{q}}||

@@ -73,31 +73,31 @@

{#op||ls-r||{{sl}}||{{q}}|| Returns a quotation {{q}} containing all children (files and directories) of the directory {{sl}}, recursively. #} -{#op||mkdir||{{sl}}||{{null}}|| +{#op||mkdir||{{sl}}||{{none}}|| Creates the specified directory {{sl}}. #} -{#op||mv||{{sl1}} {{sl2}}||{{null}}|| +{#op||mv||{{sl1}} {{sl2}}||{{none}}|| Moves the file or directory {{sl1}} to {{sl2}}. #} -{#op||os||{{null}}||{{s}}|| +{#op||os||{{none}}||{{s}}|| Returns the host operating system. It can be one of the following strings: windows, macosx, linux, netbsd, freebsd, openbsd, solaris, aix, standalone. #} {#op||put-env||{{sl1}} {{sl2}}||{{s}}|| Sets environment variable {{sl2}} to {{sl1}}. #} -{#op||rm||{{sl}}||{{null}}|| +{#op||rm||{{sl}}||{{none}}|| Deletes the specified file {{sl}}. #} -{#op||rmdir||{{sl}}||{{null}}|| +{#op||rmdir||{{sl}}||{{none}}|| Deletes the specified directory {{sl}} and all its subdirectories recursively. #} {#op||run||{{sl}}||{{d}}|| Executes the external command {{sl}} in the current directory without displaying its output. Returns a dictionary containing the command output and return code (in keys **output** and **code** respectively). #} -{#op||sleep||{{i}}||{{null}}|| +{#op||sleep||{{i}}||{{none}}|| Halts program execution for {{i}} milliseconds.#} -{#op||symlink||{{sl1}} {{sl2}}||{{null}}|| +{#op||symlink||{{sl1}} {{sl2}}||{{none}}|| Creates symlink {{sl2}} for file or directory {{sl1}}. #} {#op||symlink?||{{sl}}||{{b}}||

@@ -106,14 +106,14 @@

{#op||system||{{sl}}||{{i}}|| Executes the external command {{sl}} in the current directory and pushes its return code on the stack. #} -{#op||system!||{{sl}}||{{null}}|| +{#op||system!||{{sl}}||{{none}}|| Executes the external command {{sl}} in the current directory without pushing its return code on the stack. #} -{#op||unzip||{{sl1}} {{sl2}}||{{null}}|| +{#op||unzip||{{sl1}} {{sl2}}||{{none}}|| Decompresses zip file {{sl1}} to directory {{sl2}} (created if not present).#} {#op||which||{{sl}}||{{s}}|| Returns the full path to the directory containing executable {{sl}}, or an empty string if the executable is not found in **$PATH**. #} -{#op||zip||{{sl}} {{q}}||{{null}}|| -Compresses files included in quotation {{q}} into zip file {{sl}}.#} +{#op||zip||{{sl}} {{q}}||{{none}}|| +Compresses files included in quotation {{q}} into zip file {{sl}}.#}
M site/contents/reference-time.mdsite/contents/reference-time.md

@@ -4,10 +4,10 @@ title: "time Module"

----- {@ _defs_.md || 0 @} -{#op||now||{{null}}||{{flt}}|| +{#op||now||{{none}}||{{flt}}|| Returns the current time as Unix timestamp with microseconds. #} -{#op||timestamp||{{null}}||{{i}}|| +{#op||timestamp||{{none}}||{{i}}|| Returns the current time as Unix timestamp. #} {#op||timeinfo||{{i}}||{{tinfo}}||

@@ -26,4 +26,4 @@ >

> > %tip% > > Tip > > -> > For information on special characters in the format string, see the [format](https://nim-lang.org/docs/times.html#format,TimeInfo,string) nim method. #} +> > For information on special characters in the format string, see the [format](https://nim-lang.org/docs/times.html#format,TimeInfo,string) nim method. #}
M site/contents/reference.mdsite/contents/reference.md

@@ -14,6 +14,8 @@ {#link-module||seq#}

: Defines operators for quotations, like map, filter, reduce, etc. {#link-module||dict#} : Defines operators for dictionaries, like dget, ddup, dset, etc. +{#link-module||dstore#} +: Provides support for simple, persistent, in-memory JSON stores. {#link-module||io#} : Provides operators for reading and writing files as well as printing to STDOUT and reading from STDIN. {#link-module||fs#}

@@ -29,7 +31,7 @@ : Provides operators to perform simple mathematical operations on integer and floating point numbers.

{#link-module||time#} : Provides a few basic operators to manage dates, times, and timestamps. {#link-module||crypto#} -: Provides operators to compute hashes (MD5, SHA1, SHA224, SHA256, SHA384, sha512), base64 encoding/decoding, and AES encryption/decryption. +: Provides operators to compute hashes (MD4, MD5, SHA1, SHA224, SHA256, SHA384, sha512), base64 encoding/decoding, and AES encryption/decryption. {#link-module||math#} : Provides many mathematical operators and constants such as trigonometric functions, square root, logarithms, etc. {#link-module||net#}

@@ -44,8 +46,10 @@ The following notation is used in the signature of all min operators:

### Types and Values -{{null}} +{{none}} : No value. +{{null}} +: null value {{any}} : A value of any type. {{b}}

@@ -64,6 +68,19 @@ {{q}}

: A quotation (also expressed as parenthesis enclosing other values). {{d}} : A dictionary value. +{{url}} +: An URL dictionary: + + { + "http" :scheme + "h3rald" :hostname + "" :port + "" :username + "" :password + "/min" :path + "" :anchor + "" :query + } {{tinfo}} : A timeinfo dictionary:

@@ -100,6 +117,14 @@ "ipv4" :domain

"stream" :type "tcp" :protocol ;socket + } +{{dstore}} +: A datastore dictionary that must be created through the {#link-operator||dstore||dsinit#} or {#link-operator||dstore||dsread#} operator: + + { + {} :data + "path/to/file.json" :path + ;datastore } {{req}} : A request dictionary, representing an HTTP request to be performed through the operators exposed by the {#link-module||http#}:
M site/settings.jsonsite/settings.json

@@ -6,5 +6,5 @@ "rules": "rules.min",

"temp": "temp", "templates": "templates", "title": "min language", - "version": "0.25.0" + "version": "0.29.1" }
M site/templates/_footer.mustachesite/templates/_footer.mustache

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

<footer> -<p>&copy; 2017&mdash;2020 &bull; <a href="https://h3rald.com">Fabio Cevasco</a></p> +<p>&copy; 2017&mdash;2021 &bull; <a href="https://h3rald.com">Fabio Cevasco</a></p> <p> <a href="http://creativecommons.org/licenses/by-sa/4.0/"> <img src="https://img.shields.io/badge/content-CC%20BY--SA%204.0 License-yellow.svg"
M tasks/build.mintasks/build.min

@@ -6,6 +6,11 @@

( :target-os :variant + :stage + "-d:release" :d-stage + (stage "dev" ==) + ("-d:dev" @d-stage) + when " " :d-variant "min" :o-variant (variant length 0 >) (

@@ -14,12 +19,19 @@ "$#min" (variant) =% @o-variant

) when "nim" required "Building $# - $# (x64)" (o-variant target-os) =% notice - "nim c -d:release -d:ssl --cpu:amd64 --os:$# $#-o:$# min" (target-os d-variant o-variant) =% puts ! + "" :musl + "musl-gcc" which :musl-gcc + (musl-gcc length 0 >) + ("--gcc.exe:musl-gcc --gcc.linkerexe:musl-gcc" @musl) + when + "nim c $# -d:ssl $# --cpu:amd64 --os:$# $#-o:$# min" (d-stage musl target-os d-variant o-variant) =% puts ! {} target-os %os config /version %version o-variant %exe - pack + (stage "dev" !=) + (pack) + when ) :cz ( :vdata

@@ -35,6 +47,7 @@ (

"hastyscribe" required "Building - guide" notice "hastyscribe Min_DeveloperGuide.md --field/version=$#" (version) =% ! + !"cp Min_DeveloperGuide.htm site/output/guide.dev.html" ) :build-guide ( "hastysite" required

@@ -54,40 +67,43 @@

; Module symbols {} ( - "lite" os cz + "" "lite" os cz ) %lite ( - "mini" os cz + "" "mini" os cz ) %mini ( - "" os cz + "dev" "" os cz +) %dev +( + "" "" os cz ) %default ( - "" "linux" cz + "" "" "linux" cz ) %linux ( - "lite" "linux" cz + "" "lite" "linux" cz ) %linux-lite ( - "mini" "linux" cz + "" "mini" "linux" cz ) %linux-mini ( - "" "macosx" cz + "" "" "macosx" cz ) %macosx ( - "lite" "macosx" cz + "" "lite" "macosx" cz ) %macosx-lite ( - "mini" "macosx" cz + "" "mini" "macosx" cz ) %macosx-mini ( - "" "windows" cz + "" "" "windows" cz ) %windows ( - "lite" "windows" cz + "" "lite" "windows" cz ) %windows-lite ( - "mini" "windows" cz + "" "mini" "windows" cz ) %windows-mini 'build-guide %guide 'build-site %site
M tasks/clean.mintasks/clean.min

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

{} ( "Cleaning up build files" notice - . ls ("\.(htm|zip)$" match) filter =files + . ls ("(\.(htm|zip|exe)|[\/\\]min)$" match) filter =files files 'rm foreach ) %default +clean-tasks
M tasks/github.mintasks/github.min

@@ -69,6 +69,7 @@ ) foreach

) :delete-assets ; Module symbols +{} ( {} "/releases" %endpoint
M tests/all.mintests/all.min

@@ -17,6 +17,7 @@ 'num load

'str load 'sys load 'time load +'dstore load 'fs load 'crypto load 'math load
A tests/dstore.min

@@ -0,0 +1,37 @@

+'test load +'test import + +"dstore" describe + + ("dstore.json" dsinit type "dict:datastore" ==) assert + + ( + "dstore.json" dsread :ds + ds "tests" + {} + 1 %test1 + 2 %test2 + dspost dswrite + "tests" (pop true) dsquery size 1 == + ) assert + + ( + "dstore.json" dsread :ds + ds "tests/aaa" + {} + 1 %test1 + 3 %test3 + dsput dswrite + "tests" (/test1 1 ==) dsquery size 2 == + ) assert + + ( + "dstore.json" dsread :ds + ds "tests/aaa" dsdelete + "tests" (/id "aaa" ==) dsquery size 0 == + ) assert + + "dstore.json" rm + + report + clear-stack
M tests/lang.mintests/lang.min

@@ -50,6 +50,9 @@

("2 2 +" "tests/testload.min" fwrite 'testload load 4 ==) assert "tests/testload.min" rm + ("2 :two 3 :three" "tests/testrequire.min" fwrite 'testrequire require :tm *tm/two *tm/three + 5 ==) assert + "tests/testrequire.min" rm + (2 2 mymath ^myplus 4 ==) assert

@@ -200,10 +203,6 @@ ) assert

(3.4 "test" 1 (int string num) expect (3.4 "test" 1) ==) assert - ("2 2 +" "testread.min" fwrite "testread.min" read (2 2 +) ==) assert - - "testread.min" rm - ("aaa bbb ccc 2 2 + (2 3 4)" parse (aaa bbb ccc 2 2 + (2 3 4)) ==) assert (lite? false ==) assert

@@ -226,6 +225,78 @@ ("x" prefix) 'x define-sigil

'x unseal-sigil 'x delete-sigil 'x defined-sigil? false == ) assert + + ( + {{100 :b} :a} :test *test/a/b 100 == + ) assert + + ( + ( + symbol pow-mul + (num :base int :exp ==> num :pr num :mr) + ( + exp 1 - :n + base exp * @mr + base (dup) n times (*) n times @pr + ) + ) operator + 2 4 pow-mul - 8 == + ) assert + + ( + ( + sigil nt + (string :s ==> bool :result) + ( + ("int" s ==) + (true @result return) + when + ("num" s ==) + (true @result return) + when + ("float" s ==) + (true @result return) + when + "not gonna be printed if true!" puts + false @result + ) + ) :: + nt"float" + ) assert + + (:n ((n integer?) (n 0 >)) &&) 'natural typeclass + ("type:natural" defined?) assert + ( + symbol natural-sum + (natural :n natural :m ==> natural :result) + (n m + @result) + ) :: + null :err + ( + (3 -3 natural-sum) + (@err) + ) try + pop ;Remove 3 that was left on the stack. + (err format-error "expected: natural natural natural-sum" match) assert + (2 3 natural-sum 5 ==) assert + + ( + symbol add + ((string|num|quot :t) :a t :b ==> t :result) + ( + (a type "string" ==) + (a b suffix @result return) + when + (a type "num" ==) + (a b + @result return) + when + (a type "quot" ==) + (a b concat #result return) + when + ) + ) :: + ("a" "b" add "ab" ==) assert + ((1 2 3) (4 5) add (1 2 3 4 5) ==) assert report ; Tidy up
M tests/logic.mintests/logic.min

@@ -132,6 +132,10 @@ (true quotation? false ==) assert

(false quotation? false ==) assert (("a" 2 c) quotation?) assert + ("a" stringlike?) assert + (1 stringlike? false ==) assert + ('test stringlike?) assert + ({} 'module type? false ==) assert ((1 2 3) 'module type? false ==) assert (4 'module type? false ==) assert

@@ -149,8 +153,24 @@ (3 "a" == not) assert

(1 () != true) assert (3.3 'test == not) assert - ("a" :x (x number?) (x 6 <) dequote-and not) assert - (1 :x (x number?) (dsagasdgsg) dequote-or) assert - + ( + ( + (true) + (2 1 >) + ("a" "b" ==) + ("never printed" puts!) + ) && + false == + ) assert + + ( + ( + (false) + (2 1 <) + ("a" "a" ==) + ("never printed" puts!) + ) || + ) assert + report clear-stack
M tests/math.mintests/math.min

@@ -28,6 +28,8 @@

(pi ceil 4 ==) assert (pi trunc 3 ==) assert + + (-2.87 abs 2.87 ==) assert report clear-stack
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
M tests/str.mintests/str.min

@@ -38,6 +38,8 @@

("This is a difficult test" "s/difficult/simple/" =~ ("This is a simple test") ==) assert ("This is a DIFFICULT\n test" "s/difficult/simple/mis" =~ ("This is a simple\n test") ==) assert + + ("/api/items/test-1" "/\\/api\\/items\\/(.+)/" regex 1 get "test-1" ==) assert ("this is a test" uppercase "THIS IS A TEST" ==) assert

@@ -72,6 +74,12 @@

("fix" "pre" prefix "prefix" ==) assert ("suf" "fix" suffix "suffix" ==) assert + + ("http://test.com?€%,,!{}" encode-url "http%3A%2F%2Ftest.com%3F%E2%82%AC%25%2C%2C%21%7B%7D" ==) assert + + ("http%3A%2F%2Ftest.com%3F%E2%82%AC%25%2C%2C%21%7B%7D" decode-url "http://test.com?€%,,!{}" ==) assert + + ("http://h3rald.com/a/b/c?test=1#123" parse-url {"123" :anchor "h3rald.com" :hostname "" :password "/a/b/c" :path "" :port "test=1" :query "http" :scheme "" :username} ==) assert report clear-stack