all repos — min @ 5589094ee29393bffed7091295f3d906d315aade

A small but practical concatenative programming language.

Added the possibility to define, delete, seal, unseal sigils
h3rald h3rald@h3rald.com
Sun, 13 Dec 2020 08:22:00 +0100
commit

5589094ee29393bffed7091295f3d906d315aade

parent

1b5b31f4f2ced89983f88e60eeea212026b66133

4 files changed, 151 insertions(+), 86 deletions(-)

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

@@ -24,6 +24,8 @@ 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)
M core/scope.nimcore/scope.nim

@@ -1,75 +1,96 @@

-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 previous*(scope: ref MinScope): ref MinScope {.extern:"min_exported_symbol_$1".}= - if scope.parent.isNil: - return scope - else: - return scope.parent +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
M lib/min_lang.nimlib/min_lang.nim

@@ -112,8 +112,6 @@ 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]

@@ -159,12 +157,30 @@ q1 = @[q1].newVal

isQuot = false symbol = sym.getString when not defined(mini): - if not symbol.match "^[a-zA-Z_][a-zA-Z0-9/!?+*._-]*$": + 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")

@@ -187,6 +203,13 @@ 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")

@@ -624,18 +647,36 @@ i.push pkgVersion.newVal

def.symbol("seal") do (i: In): let vals = i.expect("'sym") - let sym = vals[0] - var s = i.scope.getSymbol(sym.getString) + let sym = vals[0].getString + var s = i.scope.getSymbol(sym) s.sealed = true - i.scope.setSymbol(sym.getString, s) + 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] - var s = i.scope.getSymbol(sym.getString) + let sym = vals[0].getString + var s = i.scope.getSymbol(sym) s.sealed = false - i.scope.setSymbol(sym.getString, s, true) - + 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) + 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]
M next-release.mdnext-release.md

@@ -1,1 +1,2 @@

- +* Added support for sigils on double-quoted strings +* Added support for arbitrary strings as dictionary keys