Merge branch 'next'
@@ -8,7 +8,7 @@ author = pkgAuthor
description = pkgDescription license = "MIT" bin = @[pkgName] -installFiles = @["min.yml", "min.nim", "prelude.min"] +installFiles = @["min.yml", "min.nim", "prelude.min", "help.json"] installDirs = @["minpkg"] # Dependencies
@@ -4,6 +4,7 @@ strutils,
sequtils, os, critbits, + json, algorithm when defined(mini): import@@ -194,7 +195,7 @@ if not val.obj.isNil:
v.obj = val.obj return v -proc apply*(i: In, op: MinOperator) {.gcsafe, extern:"min_exported_symbol_$1".}= +proc apply*(i: In, op: MinOperator, sym = "") {.gcsafe, extern:"min_exported_symbol_$1".}= if op.kind == minProcOp: op.prc(i) else:@@ -202,6 +203,8 @@ if op.val.kind == minQuotation:
var newscope = newScopeRef(i.scope) i.withScope(newscope): for e in op.val.qVal: + if e.isSymbol and e.symVal == sym: + raiseInvalid("Symbol '$#' evaluates to itself" % sym) i.push e else: i.push(op.val)@@ -264,7 +267,7 @@ let symbol = val.symVal
if symbol == "return": raise MinReturnException(msg: "return symbol found") if i.scope.hasSymbol(symbol): - i.apply i.scope.getSymbol(symbol) + i.apply i.scope.getSymbol(symbol), symbol else: # Check if symbol ends with ! (auto-popping) if symbol.len > 1 and symbol[symbol.len-1] == '!':
@@ -45,14 +45,14 @@ 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]) + raiseInvalid("Key '$1' is set to an operator and it 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]) + raiseInvalid("Key $1 is set to an operator and it cannot be retrieved." % [s]) result = q.dVal[s].val proc dhas*(q: MinValue, s: MinValue): bool =@@ -103,7 +103,7 @@ # 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.") + raiseInvalid("Dictionary contains operators that cannot be accessed.") r.add item.val return r.newVal@@ -112,7 +112,7 @@ # 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.") + raiseInvalid("Dictionary contains operators that cannot be accessed.") r.add key.newVal r.add value.val return r.newVal@@ -198,6 +198,12 @@ if andr:
result = true break +proc validateValueType*(i: var MinInterpreter, element: string, value: MinValue): bool {.gcsafe.} = + var g: CritBitTree[string] + var s = newSeq[string](0) + var c = 0 + return i.validateValueType(element, value, g, s, c) + proc basicValidate*(i: In, value: MinValue, t: string): bool = case t: of "bool":@@ -235,10 +241,7 @@ return false
elif i.scope.hasSymbol(ta): # Custom type alias let element = i.scope.getSymbol(ta).val.getString - var fakeGenerics: CritBitTree[string] - var vTypes = newSeq[string](0) - var c = 0 - return i.validateValueType(element, value, fakeGenerics, vTypes, c) + return i.validateValueType(element, value) elif i.scope.hasSymbol(tc): # Custom type class var i2 = i.copy(i.filename)
@@ -277,7 +277,7 @@ 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) + i.scope.symbols[outVars[k]] = MinOperator(kind: minValOp, sealed: false, val: @[newNull()].newVal, quotation: true) # Actually execute the body of the operator var endSnapshot: seq[MinValue] var snapShot: seq[MinValue]@@ -501,7 +501,7 @@ symbol = sym.getString
when not defined(mini): if not symbol.match USER_SYMBOL_REGEX: raiseInvalid("Symbol identifier '$1' contains invalid characters." % symbol) - info "[lambd] $1 = $2" % [symbol, $q1] + info "[lambda] $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: true)@@ -686,7 +686,7 @@ let sym = i.scope.getSymbol(str)
if sym.kind == minValOp: i.push sym.val else: - raiseInvalid("No source available for native symbol '$1'." % str) + raiseInvalid("Unable to display source: '$1' is an operator." % str) def.symbol("invoke") do (i: In): let vals = i.expect("'sym")@@ -701,7 +701,8 @@ let mdl = vals[0]
let symId = parts[p+1] let origScope = i.scope i.scope = mdl.scope - i.scope.parent = origScope + if not i.scope.parent.isNil: + i.scope.parent = origScope let sym = i.scope.getSymbol(symId) i.apply(sym) i.scope = origScope@@ -1089,12 +1090,15 @@ i.push "$ ".newVal
else: i.eval(""""[$1]\n$$ " (.) => %""") - # Sigils - - def.sigil("'") do (i: In): + def.symbol("quotesym") do (i: In): let vals = i.expect("str") let s = vals[0] i.push(@[i.newSym(s.strVal)].newVal) + + # Sigils + + def.sigil("'") do (i: In): + i.pushSym("quotesym") def.sigil(":") do (i: In): i.pushSym("define")@@ -1141,7 +1145,7 @@ def.symbol("~") do (i: In):
i.pushSym("lambda-bind") def.symbol("'") do (i: In): - i.pushSym("quote") + i.pushSym("quotesym") def.symbol("->") do (i: In): i.pushSym("dequote")
@@ -227,10 +227,10 @@ 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 + let t = vals[0].getString + let v = vals[1] + let res = i.validateValueType(t, v) + i.push res.newVal def.symbol("&&") do (i: In): i.pushSym("expect-all")
@@ -1,100 +1,16 @@
- -### Fixes - -* Added `===` at the end of integrated help descriptions (#127). -* Fixed override propagation when setting isymbols in upper scopes (#133). - -### Mew additions - -* New symbol: [parent-scope](https://min-lang.org/reference-lang/#op-parent-scope) (#117). - -### Notable changes - -#### Lambda capturing in operator output values - -You can now specify a lambda to be captured to an output value, like this: - - ( - symbol square - (==> quot ^o) - ( - (dup *) ~o - ) - ) :: - -Essentially, this allows you to push a lambda on the stack from an operator. - -Note that: - -* Lambdas must be captured using the `^` sigil in signatures and bound using `lambda-bind` in the operator body. -* Lambdas cannot be captured in input values (they have already been pushed on the stack). -* Requiring a lambda as an output value effectively bypasses stack pollution checks. While this can be useful at times, use with caution! - -#### Type Expressions - -When specifying types in operator signatures or through the `expect` operator, you can specify a logical expression containing types and type classes joined with one of the following operators: - -* `|` (or) -* `&` (and) -* `!` (not) - -Suppose for example you defined the following type classes: - -``` -(typeclass fiveplus - (int :n ==> bool :o) - ( - n 5 > @o - ) -) :: - -(typeclass tenminus - (int :n ==> bool :o) - ( - n 10 < @o - ) -) :: +### Breaking changes -(typeclass even - (int :n ==> bool :o) - ( - n 2 mod 0 == @o - ) -) :: -``` +* #141 - The **'** symbol is now an alias of the **quotesym** symbol (but its behavior remains the same: it can be used to created quotes symbols from a string), and the **'** sigil is equivalent to the new **quotesym** symbol, not **quote**. -You can combine them in a type expression as following: +### Fixes -``` -(symbol test - (!even|tenminus&fiveplus :n ==> bool :o) - ( - true @o - ) -) :: -4 test ; error -6 test ; true -11 test ; true -``` +* #140 - It is now possible to invoke symbols on ROOT. +* #147 - Fixed an error when processing operator output values. +* Now adding **help.json** to installation folder when installing via nimble. +* #151 - Added documentation for **integer**. +* #152 - Now preventing infinite recursion in case a symbol evaluates to itsel. -### Type aliases - -You can now define *type aliases* using the `typealias` operator. - -For example, you can create an alias of part of the type expression used in the previous example, like this: - -``` -'tenminus&fiveplus 'five-to-ten typealias - -(symbol test - (!even|five-to-ten :n ==> bool :o) - ( - true @o - ) -) :: -``` +### New features -Note that: -* Type aliases be used to create an alias for any type expression. -* Aliased type expressions can contain standard min types, dictionary types, type classes, and even other type aliases. -* The `typealias` operator actually creates lexically-scoped, `typealias:`-prefixed symbols that can be sealed, unsealed, and deleted exactly like other symbols. +* #144 - The symbol **type?** is now able to check if a value satisfies a type expression, not only a simple type. Note however that it is now necessary to prepend dictionary types with `dict:` (as in type expressions). +* #141 - A new **quotesym** symbol has been added to transform a string into a quoted symbol. This is equivalent to the behavior of the **'** sigil.
@@ -4,9 +4,9 @@ title: "lang Module"
----- {@ _defs_.md || 0 @} -{#sig||'||quote#} +{#sig||'||quotesym#} -{#alias||'||quote#} +{#alias||'||quotesym#} {#sig||:||define#}@@ -138,7 +138,7 @@ {#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: +> Converts {{any}} to a float 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`.@@ -205,7 +205,7 @@ > >
> > (2 + 3 * 5) infix-dequote #} -{#op||int||{{any}}||{{i}}|| +{#op||integer||{{any}}||{{i}}|| > Converts {{any}} to an integer value based on the following rules: > > * If {{any}} is {{t}}, it is converted to `1`.@@ -362,6 +362,10 @@ Exits the program or shell with 0 as return code. #}
{#op||quote||{{any}}||({{any}})|| Wraps {{any}} in a quotation. #} + +{#op||quotesym||{{str}}||({{sym}})|| +Creates a symbol with the value of {{str}} and wraps it in a quotation. #} + {#op||raise||{{e}}||{{none}}|| Raises the error specified via the dictionary {{e}}.#}
@@ -83,7 +83,7 @@ {#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. #} +Returns {{t}} if the data type of {{any}} satisfies the specified type expression {{sl}}, {{f}} otherwise. #} {#op||xor||{{b1}} {{b2}}||{{b3}}|| Returns {{t}} if {{b1}} and {{b2}} are different, {{f}} otherwise.#}
@@ -6,5 +6,5 @@ "rules": "rules.min",
"temp": "temp", "templates": "templates", "title": "min language", - "version": "0.32.0" + "version": "0.33.0" }
@@ -291,7 +291,7 @@ in %test
@out ) ) :: - ("aaa" test-c 'test-c type?) *test/assert + ("aaa" test-c 'dict:test-c type?) *test/assert ( symbol add
@@ -136,13 +136,13 @@ ("a" stringlike?) *test/assert
(1 stringlike? false ==) *test/assert ('test stringlike?) *test/assert - ({} 'module type? false ==) *test/assert - ((1 2 3) 'module type? false ==) *test/assert - (4 'module type? false ==) *test/assert - (logic 'module type?) *test/assert + ({} 'dict:module type? false ==) *test/assert + ((1 2 3) 'dict:module type? false ==) *test/assert + (4 'dict:module type? false ==) *test/assert + (logic 'dict:module type?) *test/assert (1 "int" type?) *test/assert ("test" "str" type?) *test/assert - (ROOT "module" type?) *test/assert + (ROOT "dict:module" type?) *test/assert (7 0 / inf ==) *test/assert (-7 0 / -inf ==) *test/assert@@ -173,4 +173,4 @@ ) ||
) *test/assert *test/report - clear-stack+ clear-stack