all repos — min @ 3145eddc0803c49749071ddceb003ae721630532

A small but practical concatenative programming language.

Merge branch 'master' of github.com:h3rald/min
h3rald h3rald@h3rald.com
Wed, 17 Jun 2020 17:31:50 +0200
commit

3145eddc0803c49749071ddceb003ae721630532

parent

4d6a557b01de5a2c87c414fd1db1039ef890eefc

M core/interpreter.nimcore/interpreter.nim

@@ -51,9 +51,7 @@

template withDictScope*(i: In, s: ref MinScope, body: untyped): untyped = let origScope = i.scope try: - var scope = s.copy - scope.parent = origscope - i.scope = scope + i.scope = s body finally: i.scope = origScope
M lib/min_dict.nimlib/min_dict.nim

@@ -64,6 +64,10 @@ if d.dhas(k):

i.dset(res, k, i.dget(d, k)) i.push res + def.symbol("dtype") do (i: In): + let vals = i.expect("dict") + i.push vals[0].objType.newVal + def.sigil("?") do (i: In): i.push("dhas?".newSym)
M lib/min_io.nimlib/min_io.nim

@@ -66,6 +66,15 @@ def.symbol("gets") do (i: In):

var ed = initEditor() i.push ed.readLine().newVal + 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): var ed = initEditor() i.push ed.password("Enter Password: ").newVal
M lib/min_lang.nimlib/min_lang.nim

@@ -142,6 +142,17 @@ 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]

@@ -199,7 +210,7 @@

def.symbol("with") do (i: In): let vals = i.expect("dict", "quot") var qscope = vals[0] - let qprog = vals[1] + var qprog = vals[1] i.withDictScope(qscope.scope): for v in qprog.qVal: i.push v
M lib/min_logic.nimlib/min_logic.nim

@@ -220,6 +220,6 @@ let vals = i.expect("'sym", "a")

if vals[1].isTypedDictionary(vals[0].getString): i.push true.newVal else: - i.push false.newVal + i.push (vals[1].typename == vals[0].getString).newVal def.finalize("logic")
M lib/min_str.nimlib/min_str.nim

@@ -74,6 +74,19 @@ 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]
M min.nimmin.nim

@@ -314,7 +314,7 @@ Default: notice

-p, --prelude:<file.min> If specified, it loads <file.min> instead of the default prelude code -v, —-version Print the program version""" % [pkgName, pkgVersion] - var file, s, prelude: string = "" + var file, s: string = "" var args = newSeq[string](0) setLogFilter(lvlNotice)
M site/contents/learn-data-types.mdsite/contents/learn-data-types.md

@@ -10,9 +10,9 @@

boolean : **true** or **false**. integer -: An integer number like 1, 27 or -15. +: A 64-bit integer number like 1, 27 or -15. float -: A floating-point number like 3.14 or -56.9876. +: A 64-bit floating-point number like 3.14 or -56.9876. string : A series of characters wrapped in double quotes: "Hello, World!". quotation
M site/contents/learn-extending.mdsite/contents/learn-extending.md

@@ -19,14 +19,14 @@

The {#link-operator||lang||module#} (and the **+** sigil) allows you to create a new min module: ``` -( +{ (dup *) :pow2 (dup dup * *) :pow3 (dup dup dup * * *) :pow4 -) +quickpows +} +quickpows ```

@@ -38,6 +38,39 @@ 'quickpows import

2 pow3 pow2 puts ;prints 64 ``` + +## Specifying your custom prelude program + +By default, when min is started it loads the following *prelude.min* program: + +``` +; Imports +'str import +'io import +'logic import +'num import +'sys import +'stack import +'seq import +'dict import +'time import +'fs import +'lite? ( + ( + 'crypto import + 'math import + 'net import + 'http import + ) ROOT with +) unless +; Unseal prompt symbol +'prompt unseal +``` + +Essentially, this causes min to import *all* the modules (except for some if the **lite** flag was defined at compilation time) and unseals the {#link-operator||lang||prompt#} symbol so that it can be customized. If you want, you can provide your own prelude file to specify your custom behaviors, selectively import modules, and define your own symbols, like this: + +> %min-terminal% +> [$](class:prompt) min -i -p:myfile.min ## Embedding min in your Nim program
M site/contents/reference-dict.mdsite/contents/reference-dict.md

@@ -47,5 +47,8 @@

{#op||dset||{{d}} {{any}} {{sl}}||{{d}}|| Sets the value of the {{sl}} of {{d1}} to {{any}}, and returns the modified dictionary {{d}}. #} +{#op||dtype||{{d}}||{{s}}|| +Returns a string set to the type of {{d}} (empty if the dictionary has no type). #} + {#op||dvalues||{{d}}||({{a0p}})|| Returns a quotation containing all the values of dictionary {{d}}. #}
M site/contents/reference-io.mdsite/contents/reference-io.md

@@ -40,6 +40,9 @@

{#op||fwrite||{{s1}} {{s2}}||{{null}}|| Writes {{s1}} to the file {{s2}}, erasing all its contents first. #} +{#op||getchr||{{null}}||{{i}}|| +Reads single character from STDIN without waiting for ENTER key and places its ASCII code on top of the stack.#} + {#op||gets||{{null}}||{{s}}|| Reads a line from STDIN and places it on top of the stack as a string.#}

@@ -61,11 +64,17 @@

{#op||print!||{{any}}||{{null}}|| Prints {{any}} to STDOUT and removes {{any}} from the stack.#} +{#op||putchr||{{s}}||{{any}}|| +Prints {{s}} to STDOUT without printing a new line ({{s}} must contain only one character).#} + {#op||puts||{{any}}||{{any}}|| Prints {{any}} and a new line to STDOUT.#} {#op||puts!||{{any}}||{{null}}|| Prints {{any}} and a new line to STDOUT, removing {{any}} from the stack.#} + +{#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||warning||{{any}}||{{any}}|| Prints {{any}} 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

@@ -44,7 +44,7 @@ {#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}} is passed, it returns a new dictionary obtained by evaluating each symbol of {{d}} 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).#} {#op||args||{{null}}||{{q}}|| Returns a list of all arguments passed to the current program.#}

@@ -56,9 +56,9 @@ {#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 a non-zero numeric value, it is converted to {{t}}, otherwise it is converted to {{f}}. -> * If {{any}} is a non-empty quotation, it is converted to {{t}}, otherwise it is converted to {{f}}. -> * If {{any}} is a non-empty string or not `"false"`, it is converted to {{t}}, otherwise 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}}.#} {#op||call||{{d}} {{sl}}||{{a0p}}|| Calls operator {{sl}} defined in dictionary {{d}}. #}

@@ -92,7 +92,9 @@ {#op||delete||{{sl}}||{{null}}||

Deletes the specified symbol {{sl}}.#} {#op||dequote||{{q}}||{{a0p}}|| -Pushes the contents of quotation {{q}} on the stack. #} +Pushes the contents of quotation {{q}} on the stack. + +Each element is pushed on the stack one by one. If any error occurs, {{q}} is restored on the stack.#} {#op||eval||{{s}}||{{a0p}}|| Parses and interprets {{s}}. #}

@@ -174,7 +176,7 @@ > > Example

> > > > The following program leaves `120` on the stack, the factorial of 5: > > -> > (dup 0 ==) 'succ (dup pred) '* linrec +> > 5 (dup 0 ==) 'succ (dup pred) '* linrec #} {#op||lite?||{{null}}||{{b}}||

@@ -206,12 +208,6 @@ Returns the current log level (debug, info, notive, warn, error or fatal). #}

{#op||module||{{d}} {{sl}}||{{null}}|| Creates a new module {{sl}} based on dictionary {{d}}. #} - -{#op||scope-sigils||{{d}}||({{s0p}})|| -Returns a list of all sigils defined in dictionary {{d}}.#} - -{#op||scope-symbols||{{d}}||({{s0p}})|| -Returns a list of all symbols defined in dictionary {{d}}.#} {#op||opts||{{null}}||{{d}}|| Returns a dictionary of all options passed to the current program, with their respective values.#}

@@ -253,8 +249,8 @@

{#op||remove-symbol||{{sl}}||{{null}}|| Removes the symbol {{sl}} from the [.min\_symbols](class:file) file. #} -{#op||ROOT||{{null}}||{{q}}|| -Returns an empty quotation holding a reference to the [ROOT](class:kwd) scope. +{#op||ROOT||{{null}}||{{d}}|| +Returns a module holding a reference to the [ROOT](class:kwd) scope. > > %tip% > > Tip

@@ -265,6 +261,25 @@

{#op||save-symbol||{{sl}}||{{null}}|| Saves the contents of symbol {{sl}} to the [.min\_symbols](class:file) file. #} +{#op||scope||{{null}}||{{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. +> +> > %sidebar% +> > Example +> > +> > The following program leaves `{(2) :two ;module}` on the stack: +> > +> > {} :myscope (2 :due scope @myscope) -> + #} + +{#op||scope-sigils||{{d}}||({{s0p}})|| +Returns a list of all sigils defined in dictionary {{d}}.#} + +{#op||scope-symbols||{{d}}||({{s0p}})|| +Returns a list of all symbols defined in dictionary {{d}}.#} + {#op||seal||{{sl}}||{{null}}|| Seals symbol {{sl}}, so that it cannot be re-assigned. #}

@@ -381,12 +396,12 @@ > > (count 10 <=)

> > (count puts succ @count) while #} {#op||with||{{q1}} {{q2}}||{{a0p}}|| -Dequotes a quotation {{q1}} within the scope of {{q2}}. +Pushes each item of {{q1}} on the stack using the scope of {{q2}} as scope. > > %sidebar% > > Example > > -> > This operator is useful to define symbols on the [ROOT](class:kwd) scope or a parent scope. For example min's prelude includes the following code used to import certain modules only if min was not compiled in lite mode: +> > This operator is useful to define symbols on the [ROOT](class:kwd) scope or another scope. For example min's prelude includes the following code used to import certain modules only if min was not compiled in lite mode: > > > > 'lite? ( > > (
M site/contents/reference-logic.mdsite/contents/reference-logic.md

@@ -109,7 +109,7 @@ {#op||quotation?||{{any}}||{{b}}||

Returns {{t}} if {{any}} is a quotation, {{f}} otherwise. #} {#op||type?||{{any}} {{sl}}||{{b}}|| -Returns {{t}} if {{any}} is a dictionary of the specified type {{sl}}, {{f}} otherwise. #} +Returns {{t}} if the data type of {{any}} is the specified type {{sl}}, {{f}} otherwise. #} {#op||xor||{{b1}} {{b2}}||{{b3}}|| Returns {{t}} if {{b1}} and {{b2}} are different, {{f}} otherwise.#}
M site/contents/reference-stack.mdsite/contents/reference-stack.md

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

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

@@ -26,7 +26,7 @@ {#op||dup||{{a1}}||{{a1}} {{a1}}||

Duplicates the first element on the stack.#} {#op||get-stack||{{null}}||({{a0p}})|| -Returns a quotation containing the contents of the stack.#} +Puts a quotation containing the contents of the stack on the stack.#} {#op||id||{{null}}||{{null}}|| Does nothing.#}
M site/contents/reference-str.mdsite/contents/reference-str.md

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

{#op||capitalize||{{sl}}||{{s}}|| Returns a copy of {{sl}} with the first character capitalized.#} +{#op||chr||{{i}}||{{s}}|| +Returns the single character {{s}} obtained by interpreting {{i}} as an ASCII code.#} + {#op||indent||{{sl}} {{i}}||{{s}}|| Returns {{s}} containing {{sl}} indented with {{i}} spaces.#}

@@ -50,6 +53,9 @@ > > %tip%

> > Tip > > > > {{s2}} can be a {{sgregex}}-compatible regular expression.#} + +{#op||chr||{{s}}||{{i}}|| +Returns the ASCII code {{i}} corresponding to the single character {{s}}.#} {#op||repeat||{{sl}} {{i}}||{{s}}|| Returns {{s}} containing {{sl}} repeated {{i}} times.#}
M site/contents/reference-sys.mdsite/contents/reference-sys.md

@@ -104,8 +104,8 @@

{#op||system||{{sl}}||{{i}}|| Executes the external command {{sl}} in the current directory and pushes its return code on the stack. #} -{#op||unzip||{{sl}}||{{null}}|| -Decompresses zip file {{sl}}.#} +{#op||unzip||{{sl1}} {{sl2}}||{{null}}|| +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**. #}
M tests/dict.mintests/dict.min

@@ -19,6 +19,10 @@ ({1 :a 2 :b 3 :c} dkeys ("a" "b" "c") ==) assert

({1 :a 2 :b 3 :c} dvalues (1 2 3) ==) assert + (ROOT dtype "module" ==) assert + + ({} dtype "" ==) assert + ({1 :a 2 :b 3 :c 4 :d} ("b" "c") dpick {2 :b 3 :c} ==) assert (2 2 {+ :plus} ^plus 4 ==) assert
M tests/lang.mintests/lang.min

@@ -76,6 +76,9 @@ ((2 3 <) ("YES") when "YES" ==) assert

((2 3 >) ("YES") unless "YES" ==) assert + (1 type "int" ==) assert + ({} type "dict" ==) assert + (ROOT type "dict:module" ==) assert (5 (dup 0 ==) (1 +) (dup 1 -) ( * ) linrec 120 ==) assert ;factorial of 5

@@ -214,6 +217,8 @@

(lite? false ==) assert ({3 :x 5 :y} "point" set-type 'point type?) assert + + ({} :myscope (2 :two 3 :three scope @myscope) -> myscope scope-symbols ("three" "two") ==) assert report ; Tidy up
M tests/logic.mintests/logic.min

@@ -136,6 +136,9 @@ ({} 'module type? false ==) assert

((1 2 3) 'module type? false ==) assert (4 'module type? false ==) assert (logic 'module type?) assert + (1 "int" type?) assert + ("test" "string" type?) assert + (ROOT "module" type?) assert (7 0 / inf ==) assert (-7 0 / -inf ==) assert
M tests/str.mintests/str.min

@@ -23,6 +23,10 @@ ("This is a test" "is" indexof 2 ==) assert

("test #1" "[0-9]" search ("1") ==) assert + ("a" ord 97 ==) assert + + (97 chr "a" ==) assert + ("This is test #1" "test #([0-9])" search ("test #1" "1") ==) assert ("This is a random string" "random" match true ==) assert