all repos — min @ f0803f8ce28de9a644a2e9badd6f8a4b575391ac

A small but practical concatenative programming language.

Implemented lambda, updated docs and tests.
h3rald h3rald@h3rald.com
Sat, 30 Jan 2021 20:08:01 +0000
commit

f0803f8ce28de9a644a2e9badd6f8a4b575391ac

parent

ddf6b549da1735f56a02859a199d64e6cae2528c

M minpkg/lib/min_lang.nimminpkg/lib/min_lang.nim

@@ -469,6 +469,20 @@ 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("lambda") do (i: In): + let vals = i.expect("'sym", "quot") + let sym = vals[0] + var q1 = vals[1] + var symbol: string + 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: true) + def.symbol("bind") do (i: In): let vals = i.expect("'sym", "a") let sym = vals[0]

@@ -902,23 +916,6 @@ # 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("str", "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("str", "a") - # let s = vals[0] - # let m = vals[1] - # i.push @[m].newVal - # i.push s - # i.pushSym "define" - def.symbol("args") do (i: In): var args = newSeq[MinValue](0)

@@ -1070,11 +1067,8 @@

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") + def.sigil("^") do (i: In): + i.pushSym("lambda") # Shorthand symbol aliases

@@ -1089,6 +1083,9 @@ i.pushSym("help")

def.symbol("@") do (i: In): i.pushSym("bind") + + def.symbol("^") do (i: In): + i.pushSym("lambda") def.symbol("'") do (i: In): i.pushSym("quote")
M mintool.minmintool.min

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

#!/usr/bin/env min ;Capture a reference of default symbols before more are added -symbols =min-symbols +symbols :min-symbols 2 :n-args 1 :taskspec-arg

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

; Validation (args size n-args <) ("No task specified" error 1 exit) when -args taskspec-arg get ":" split =taskspec +args taskspec-arg get ":" split :taskspec taskspec 0 get :task "default" :subtask (taskspec size 1 >) (taskspec 1 get @subtask) when
M next-release.mdnext-release.md

@@ -5,11 +5,22 @@ * Extended **operator** to support the creation of constructor symbols.

* Now using **dict:http-response** and **dict:http-response** for HTTP requests/responses. * Now using **dict:timeinfo** for time info. * Changed **parse-url** to push a **dict:url** on the stack. + +### Breaking changes + +This release also introduces quite a lot of breaking changes aiming at addressing some language inconsistencies and making the language more stable overall. + +Read this carefully! It is most likely that your code will breal when you upgrade. + + +* Removed **quote-define** (=) and **quote-bind** (#). +* **define** (:) now auto-quote quotations as well. +* To quickly bind a quotation to a symbol (and essentially create a symbol operator but with no validations or constraints), use the new **lambda** symbol or **^** (alias, sigil). * Removed **typeclass** and extended **operator** to create type classes as well. * Renamed **string** and **float** type names (used in operator signatures) to **str** and **flt** respectively. * Removed **define-sigil**, use **operator** instead. * Removed **module** and **+** (sigil); use **require** to create modules. -* Removed **call**, **^** (sigil, alias); use **invoke** to access module/dictionary symbols. +* Removed **call**, **^** (sigil, alias -- reused for **lambda**, see above); use **invoke** to access module/dictionary symbols. * Removed **set-type** symbol. * Removed **~** sigil (rarely used). * Renamed the following symbols:
M site/contents/learn-definitions.mdsite/contents/learn-definitions.md

@@ -58,38 +58,6 @@ (a dup * @a) dequote ;The value of a is now 64

) dequote ) dequote -## quote-define and quote-bind - -So far, we saw how to use the {#link-operator||lang||define#} and {#link-operator||lang||bind#} operator (or better, their shorthand sigils `:` and `@`) to define new symbols or bind values to existing ones. - -Consider the following example: - - (1 2 3 4 5) :my-list - my-list (dup *) map - -If run the program above in min shell by pasting the first and then the second line in it, you'll get an error similar to the following: - - (!) <repl>(1,19) [map]: Incorrect values found on the stack: - - expected: {top} quot quot {bottom} - - got: {top} quot int {bottom} - <repl>(1,19) in symbol: map - -This error says that when the {#link-operator||lang||map#} operator was evaluated, there were incorrect values on the stack. Two quotations were expected, but instead, a quotation and an integer were found. How did this happen? - -Basically, when `my-list` was pushed on the stack, it pushed all its items on top of the stack. If you run {#link-operator||stack||get-stack#}, it will return the following list: - - (1 2 3 4 5 (dup *)) - -This happens because by default min assumes that when you define a quotation you want to define a new operator rather than a list. The following program works as expected, and it returns a list containing the squares of the first five integer numbers: - - (dup *) :square - (1 2 3 4 5) (square) map - -To avoid this behavior -- i.e. whenever you want to define a list of items rather than an operator that will be immediately evaluated when pushed on the stack, you have to use the {#link-operator||lang||quote-define#} and the {#link-operator||lang||quote-bind#} or their respective sigils `#` and `=`: - - (1 2 3 4 5) #my-list - my-list (dup *) map ;Returns (1 4 9 16 25) - ## Sealing symbols Finally, symbols can be sealed to prevent accidental updates or deletions. By default, all symbols defined in the core min modules are sealed, so the following code if run in min shell will result in an error:
M site/contents/learn-operators.mdsite/contents/learn-operators.md

@@ -12,6 +12,8 @@ * Provide side effects (read/print to standard input/output/files, etc.)

There are two types of operators: _symbols_ and _sigils_. +## Symbols + _Symbols_ are the most common type of operator. A min symbol is a single word that is either provided by one of the predefined min {#link-page||reference||modules#} like `dup` or `.` or defined by the user. User-defined symbols must: * Start with a letter or an underscore (\_).

@@ -32,6 +34,21 @@ * Specify a signature to identify the type of the input and output values (in this case, the operator takes a numeric input value and produces a numeric output value). Also, note how inputs and outputs are captured into the `n` and `result` symbols in the signature quotation and then referenced in the body quotation.

* Specify a quotation containing the code that the operator will execute. Also, symbol operator definitions can be annotated with documentation comments (starting with `;;` or wrapped in `#|| ... ||#`)) so that a help text can be displayed using the {#link-operator||lang||help#} symbol. + +### Using the lambda operator + +Sometimes you just want to bind a piece of code to a symbol to reuse it later, typically something simple and easy-to-read. In these cases, you can use the {#link-operator||lang||lambda#} operator (or the `^` sigil). For example, the previous `square` operator definition could be rewritten simply as the following. + + (dup *) ^square + +Note that this feels like using {#link-operator||lang||define#}, but the main difference between {#link-operator||lang||lambda#} and {#link-operator||lang||define#} is that `lambda` only works on quotations doesn't auto-quote them, so that they are immediately evaluated when the corresponding symbol is pushed on the stack. + +Also note that unlike with {#link-operator||lang||operator#}, symbols defined with {#link-operator||lang||lambda#}: +* have no built-in validation of input and output values. +* do not support the `return` symbol to immediately end their execution. +* have no built-in stack pollution checks. + +## Sigils Besides symbols, you can also define sigils. min provides a set of predefined _sigils_ as abbreviations for for commonly-used symbols.
M site/contents/reference-lang.mdsite/contents/reference-lang.md

@@ -41,13 +41,9 @@ Symbol used to separate input and output values in operator signatures.#}

{#alias||=-=||expect-empty-stack#} -{#sig||#||quote-bind#} - -{#alias||#||quote-bind#} - -{#sig||=||quote-define#} +{#sig||^||lambda#} -{#alias||=||quote-define#} +{#alias||^||lambda#} {#op||apply||{{q}}||({{a0p}})|| Returns a new quotation obtained by evaluating each element of {{q}} in a separate stack. #}

@@ -90,7 +86,7 @@ {#op||compiled?||{{none}}||{{b}}||

Returns {{t}} if the current program has been compiled.#} {#op||define||{{any}} {{sl}}||{{none}}|| -Defines a new symbol {{sl}}, containing the specified value (auto-quoted if not already a quotation).#} +Defines a new symbol {{sl}}, containing the specified value.#} {#op||define-sigil||{{any}} {{sl}}||{{none}}|| Defines a new sigil {{sl}}, containing the specified value (auto-quoted if not already a quotation).#}

@@ -229,6 +225,11 @@ > > The following program leaves `100` on the stack:

> > > > {{100 :b} :a} :test *test/a/b #} + + {#op||lambda||{{q}} {{sl}}||{{none}}|| +Defines a new symbol {{sl}}, containing the specified quotation {{q}}. Unlike with `define`, in this case {{q}} will not be quoted, so its values will be pushed on the stack when the symbol {{sl}} is pushed on the stack. + +Essentially, this symbol allows you to define an operator without any validation of constraints and bind it to a symbol.#} {#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.#}

@@ -358,12 +359,6 @@ Exits the program or shell with 0 as return code. #}

{#op||quote||{{any}}||({{any}})|| Wraps {{any}} in a quotation. #} - -{#op||quote-bind||{{any}} {{sl}}||{{none}}|| -Quotes {{any}} and binds the quotation to the existing symbol {{sl}}. #} - -{#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}}||{{none}}|| Raises the error specified via the dictionary {{e}}.#}
M tests/lang.mintests/lang.min

@@ -29,6 +29,11 @@ _2 -> _5 -> _1 -> + +

30 == ) *test/assert + ( + (dup *) ^square + 3 square 9 == + ) *test/assert + 'five delete-symbol (symbols "five" in? false ==) *test/assert

@@ -111,11 +116,7 @@

(0 :temp (1 2 3) (temp + @temp) foreach 6 temp ==) *test/assert ( - ( - symbol mysucc - (num :n ==> num :o) - (n 1 + @o) - ) :: + (1 +) ^mysucc 'mysucc source (1 +) ==) *test/assert (6