all repos — min @ ece28e8edb437c639e050bbc9a1297c7625516e8

A small but practical concatenative programming language.

Implemented support for parsing and evaluating dictionaries.
h3rald h3rald@h3rald.com
Sat, 26 May 2018 16:02:24 +0200
commit

ece28e8edb437c639e050bbc9a1297c7625516e8

parent

cfeb4ab896ca06d6f9fa6e19f7cb5bcfd0b42282

5 files changed, 117 insertions(+), 15 deletions(-)

jump to
M core/interpreter.nimcore/interpreter.nim

@@ -41,6 +41,16 @@ res = i.scope

finally: i.scope = origScope +template withScope*(i: In, q: MinValue, body: untyped): untyped = + let origScope = i.scope + try: + i.scope = q.scope.copy + i.scope.parent = origScope + body + q.scope = i.scope + finally: + i.scope = origScope + proc newMinInterpreter*(filename = "input", pwd = ""): MinInterpreter {.extern:"min_exported_symbol_$1".}= var path = pwd if not pwd.isAbsolute:

@@ -120,10 +130,10 @@ else:

i.push(op.val) proc dequote*(i: In, q: var MinValue) {.extern:"min_exported_symbol_$1".}= - if not q.isQuotation: + if q.kind != minQuotation and q.kind != minDictionary: i.push(q) else: - i.withScope(q, q.scope): + i.withScope(q): for v in q.qVal: i.push v

@@ -132,7 +142,7 @@ var i2 = newMinInterpreter("<apply>")

i2.trace = i.trace i2.scope = i.scope try: - i2.withScope(q, q.scope): + i2.withScope(q): for v in q.qVal: if (v.kind == minQuotation): var v2 = v

@@ -168,7 +178,10 @@ else:

raiseUndefined("Undefined symbol '$1'" % [val.symVal]) discard i.trace.pop else: - i.stack.add(val) + var v = val + if (v.kind == minDictionary): + i.dequote(v) + i.stack.add(v) proc pop*(i: In): MinValue {.extern:"min_exported_symbol_$1".}= if i.stack.len > 0:
M core/parser.nimcore/parser.nim

@@ -19,6 +19,8 @@ tkInt,

tkFloat, tkBracketLe, tkBracketRi, + tkBraceLe, + tkBraceRi, tkSymbol, tkTrue, tkFalse

@@ -26,22 +28,26 @@ MinKind* = enum

minInt, minFloat, minQuotation, + minDictionary, minString, minSymbol, minBool - MinEventKind* = enum ## enumeration of all events that may occur when parsing + 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 + 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

@@ -50,6 +56,7 @@ MinParserState* = enum

stateEof, stateStart, stateQuotation, + stateDictionary, stateExpectValue MinParser* = object of BaseLexer a*: string

@@ -66,7 +73,7 @@ filename*: string

case kind*: MinKind of minInt: intVal*: BiggestInt of minFloat: floatVal*: BiggestFloat - of minQuotation: + of minQuotation, minDictionary: qVal*: seq[MinValue] scope*: ref MinScope obj*: pointer

@@ -109,7 +116,7 @@ MinEmptyStackError* = ref object of ValueError

MinInvalidError* = ref object of ValueError MinOutOfBoundsError* = ref object of ValueError -# Error Helpers +# Helpers proc raiseInvalid*(msg: string) {.extern:"min_exported_symbol_$1".}= raise MinInvalidError(msg: msg)

@@ -123,6 +130,9 @@

proc raiseEmptyStack*() {.extern:"min_exported_symbol_$1".}= raise MinEmptyStackError(msg: "Insufficient items on the stack") +proc dVal*(v: MinValue): CritBitTree[MinOperator] {.extern:"min_exported_symbol_$1".}= + if v.kind == minDictionary: + return v.scope.symbols const errorMessages: array[MinParserError, string] = [

@@ -130,6 +140,7 @@ "no error",

"invalid token", "string expected", "')' expected", + "'}' expected", "'\"' or \"'\" expected", "'*/' expected", "EOF expected",

@@ -143,6 +154,8 @@ "int literal",

"float literal", "(", ")", + "{", + "}", "symbol", "true", "false"

@@ -198,7 +211,6 @@ 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".}=

@@ -306,7 +318,7 @@ 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', ')', '(']): + while not(buf[pos] in WhiteSpace) and not(buf[pos] in ['\0', ')', '(', '}', '{']): add(my.a, buf[pos]) inc(pos) my.bufpos = pos

@@ -413,6 +425,12 @@ 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:

@@ -443,6 +461,9 @@ 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:

@@ -455,12 +476,37 @@ 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:

@@ -468,6 +514,9 @@ 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

@@ -506,6 +555,17 @@ q.add p.parseMinValue(i)

eat(p, tkBracketRi) i.scope = oldscope result = MinValue(kind: minQuotation, qVal: q, column: p.getColumn, line: p.lineNumber, scope: newscope, filename: p.filename) + of tkBraceLe: + var q = newSeq[MinValue](0) + var oldscope = i.scope + var newscope = newScopeRef(i.scope) + i.scope = newscope + discard getToken(p) + while p.token != tkBraceRi: + q.add p.parseMinValue(i) + eat(p, tkBraceRi) + i.scope = oldscope + result = MinValue(kind: minDictionary, qVal: q, column: p.getColumn, line: p.lineNumber, scope: newscope, filename: p.filename) of tkSymbol: result = MinValue(kind: minSymbol, symVal: p.a, column: p.getColumn, line: p.lineNumber, filename: p.filename) p.a = ""

@@ -534,6 +594,14 @@ if not a.objType.isNil:

q = q & ";" & a.objType q = q.strip & ")" return q + of minDictionary: + var d = "{" + for i in a.dVal.pairs: + d = d & $i.val.val & " :" & $i.key & " " + if not a.objType.isNil: + d = d & ";" & a.objType + d = d.strip & "}" + return d proc `$$`*(a: MinValue): string {.extern:"min_exported_symbol_$1".}= case a.kind:

@@ -555,6 +623,14 @@ if not a.objType.isNil:

q = q & ";" & a.objType q = q.strip & ")" return q + of minDictionary: + var d = "{" + for i in a.dVal.pairs: + d = d & $i.val.val & " :" & $i.key & " " + if not a.objType.isNil: + d = d & ";" & a.objType + d = d.strip & "}" + return d proc print*(a: MinValue) {.extern:"min_exported_symbol_$1".}= stdout.write($$a)
M core/utils.nimcore/utils.nim

@@ -110,7 +110,10 @@ result.qVal.add v.qVal[1]

# JSON interop -proc `%`*(a: MinValue): JsonNode {.extern:"min_exported_symbol_percent".}= +proc `%`*(p: MinOperatorProc): JsonNode {.extern:"min_exported_symbol_percent_1".}= + return %nil + +proc `%`*(a: MinValue): JsonNode {.extern:"min_exported_symbol_percent_2".}= case a.kind: of minBool: return %a.boolVal

@@ -123,6 +126,7 @@ return %a.intVal

of minFloat: return %a.floatVal of minQuotation: + # TODO Review if a.isDictionary: result = newJObject() for i in a.qVal:

@@ -131,6 +135,10 @@ else:

result = newJArray() for i in a.qVal: result.add %i + of minDictionary: + result = newJObject() + for i in a.dVal.pairs: + result[$i.key] = %i.val proc fromJson*(i: In, json: JsonNode): MinValue {.extern:"min_exported_symbol_$1".}= case json.kind:
M core/value.nimcore/value.nim

@@ -10,13 +10,15 @@ of minInt:

return "int" of minFloat: return "float" - of minQuotation: + of minQuotation: #TODO review if v.isTypedDictionary: return "dict:" & v.objType elif v.isDictionary: return "dict" else: return "quot" + of minDictionary: + return "dict" of minString: return "string" of minSymbol:

@@ -32,8 +34,11 @@

proc newVal*(s: cstring): MinValue {.extern:"min_exported_symbol_$1_2".}= return MinValue(kind: minString, strVal: $s) -proc newVal*(q: seq[MinValue], parentScope: ref MinScope): MinValue {.extern:"min_exported_symbol_$1_3".}= - return MinValue(kind: minQuotation, qVal: q, scope: newScopeRef(parentScope)) +proc newVal*(q: seq[MinValue], parentScope: ref MinScope, dictionary = false): MinValue {.extern:"min_exported_symbol_$1_3".}= + if dictionary: + return MinValue(kind: minDictionary, qVal: q, scope: newScopeRef(parentScope)) + else: + return MinValue(kind: minQuotation, qVal: q, scope: newScopeRef(parentScope)) proc newVal*(i: BiggestInt): MinValue {.extern:"min_exported_symbol_$1_4".}= return MinValue(kind: minInt, intVal: i)
M lib/min_lang.nimlib/min_lang.nim

@@ -196,7 +196,7 @@ let qprog = vals[1]

if qscope.qVal.len > 0: # System modules are empty quotes and don't need to be dequoted i.dequote(qscope) - i.withScope(qscope, qscope.scope): + i.withScope(qscope): for v in qprog.qVal: i.push v