all repos — min @ 94db3f912d1b617bff39b5a615dc5c3bed1824bc

A small but practical concatenative programming language.

Fixed problems with dot notation.
h3rald h3rald@h3rald.com
Sun, 16 Jun 2024 12:04:37 +0200
commit

94db3f912d1b617bff39b5a615dc5c3bed1824bc

parent

5b8fa4e695d99f94b8c13077b74ce3ab1c820a8e

M minpkg/core/interpreter.nimminpkg/core/interpreter.nim

@@ -246,6 +246,7 @@ proc pop*(i: In): MinValue =

if i.stack.len > 0: return i.stack.pop else: + debug "pop - empty stack!" raiseEmptyStack() # Inherit file/line/column from current symbol

@@ -271,27 +272,36 @@ i.trace.add val

let symbol = val.symVal if symbol == "return": raise MinReturnException(msg: "return symbol found") + i.debug("push: $#" % [symbol]) if i.scope.hasSymbol(symbol): + i.debug("push: symbol found: $#" % [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] == '!': + i.debug("push - checking auto-popping symbol: $#" % [symbol]) let apSymbol = symbol[0..symbol.len-2] if i.scope.hasSymbol(apSymbol): i.apply i.scope.getSymbol(apSymbol) discard i.pop else: + i.debug("push - checking sigil: $#" % [symbol]) + # Check user-defined sigil var qIndex = symbol.find('"') if qIndex > 0: let sigil = symbol[0..qIndex-1] + i.debug("push - checking user sigil: $#" % [sigil]) if not i.scope.hasSigil(sigil): raiseUndefined("Undefined sigil '$1'"%sigil) i.stack.add(MinValue(kind: minString, strVal: symbol[ qIndex+1..symbol.len-2])) i.apply(i.scope.getSigil(sigil)) else: + # Check system sigil let sigil = "" & symbol[0] + i.debug("push - checking system sigil: $#" % [sigil]) if symbol.len > 1 and i.scope.hasSigil(sigil): + i.debug("Processing sigil: $# ($#)" % [sigil, symbol]) i.stack.add(MinValue(kind: minString, strVal: symbol[ 1..symbol.len-1])) i.apply(i.scope.getSigil(sigil))

@@ -313,6 +323,7 @@ proc peek*(i: MinInterpreter): MinValue =

if i.stack.len > 0: return i.stack[i.stack.len-1] else: + debug "peek - empty stack!" raiseEmptyStack() template handleErrors*(i: In, body: untyped) =
M minpkg/core/scope.nimminpkg/core/scope.nim

@@ -1,5 +1,5 @@

import - std/[strutils, critbits] + std/[strutils, critbits, logging, sequtils] import parser

@@ -23,6 +23,7 @@ proc getSymbolFromPath(scope: ref MinScope, keys: var seq[

string], acc = 0): MinOperator proc getSymbol*(scope: ref MinScope, key: string, acc = 0): MinOperator = + debug "getSymbol: $#" % [key] if scope.symbols.hasKey(key): return scope.symbols[key] elif key.contains ".":

@@ -51,26 +52,35 @@ proc hasSymbolFromPath(scope: ref MinScope, keys: var seq[

string]): bool proc hasSymbol*(scope: ref MinScope, key: string): bool = + debug "hasSymbol: $#" % [key] + if scope.isNil: return false - elif scope.symbols.hasKey(key): - return true - elif key.contains("."): - var keys = key.split(".") - if keys[0] == "": - raiseInvalid("Symbols cannot start with a dot") - return hasSymbolFromPath(scope, keys) - elif not scope.parent.isNil: - return scope.parent.hasSymbol(key) else: - return false + if scope.symbols.hasKey(key): + debug "hasSymbol - found $#" % [key] + return true + elif key.contains("."): + var keys = key.split(".") + if keys[0] == "": + raiseInvalid("Symbols cannot start with a dot") + return hasSymbolFromPath(scope, keys) + elif not scope.parent.isNil: + return scope.parent.hasSymbol(key) + else: + return false proc hasSymbolFromPath(scope: ref MinScope, keys: var seq[ string]): bool = let sym = keys[0] keys.delete(0) - let d = scope.getSymbol(sym) + var d: MinOperator + try: + d = scope.getSymbol(sym) + except CatchableError: + return false let dict = d.getDictionary + debug "hasSymbolFromPath: Found dictionary $# - keys: $#" % [sym, keys.join(".")] if not dict.isNil: if keys.len > 1: return dict.scope.hasSymbolFromPath(keys)

@@ -108,40 +118,49 @@ else:

raiseInvalid("Symbol '$1' is not a dictionary." % sym) proc setSymbolFromPath(scope: ref MinScope, keys: var seq[ - string], value: MinOperator, override = false): bool {.discardable.} + string], value: MinOperator, override = false, + define = false): bool {.discardable.} proc setSymbol*(scope: ref MinScope, key: string, value: MinOperator, override = false, define = false): bool {.discardable.} = result = false # check if a symbol already exists in current scope + debug "setSymbol: $#" % [key] + debug "setSymbol: scope symbols: $#" % [$scope.symbols.keys.toSeq] 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 + debug "setSymbol (existing): $# = $#" % [key, $value] result = true elif key.contains ".": var keys = key.split(".") - return setSymbolFromPath(scope, keys, value, override) + return setSymbolFromPath(scope, keys, value, override, define) # define new symbol elif not scope.isNil and define: + debug "setSymbol (new): $# = $#" % [key, $value] 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, override) + result = scope.parent.setSymbol(key, value, override, define) + else: + debug "setSymbol: failure to set: $# = $#" % [key, $value] proc setSymbolFromPath(scope: ref MinScope, keys: var seq[ - string], value: MinOperator, override = false): bool {.discardable.} = + string], value: MinOperator, override = false, + define = false): bool {.discardable.} = let sym = keys[0] keys.delete(0) let d = scope.getSymbol(sym) let dict = d.getDictionary + debug "setSymbolFromPath: Found dictionary $# - keys: $#" % [sym, keys.join(".")] if not dict.isNil: if keys.len > 1: - return dict.scope.setSymbolFromPath(keys, value, override) + return dict.scope.setSymbolFromPath(keys, value, override, define) else: - return dict.scope.setSymbol(keys[0], value, override) + return dict.scope.setSymbol(keys[0], value, override, define) else: raiseInvalid("Symbol '$1' is not a dictionary." % sym)
M site/rules.minsite/rules.min

@@ -11,19 +11,19 @@ (

symbol set-destination (dict :meta ==> dict :result) ( - meta /id :id - meta /ext :ext + meta "id" dget :id + meta "ext" dget :ext ( ((id "home" ==) ( meta ( - ("index" %id) - (".html" %ext) + ("index" "id" dset) + (".html" "ext" dset) ) tap )) ((ext ".md" ==) ( meta ( - (".html" %ext) - ("$1/index" (id) => % %id) + (".html" "ext" dset) + ("$1/index" (id) => % "id" dset) ) tap )) ) case

@@ -56,24 +56,24 @@ "" :page

"" :contents meta ( (input-fread @contents meta) - (settings /title %site) - (settings /version %version) + (settings "title" dget "site" dset) + (settings "version" dget "version" dset) ( :temp contents temp markdown highlight-codeblocks highlight-codes @contents temp ) - (contents %contents) + (contents "contents" dset) ( ; Save post content (plain HTML fragment, no template) :temp - (temp /content-type "post" ==) + (temp "content-type" dget "post" ==) ; Need to save html fragment separately because contents is overwritten. - (temp dup /contents convert-html-entities %fragment posts append @posts) + (temp dup "contents" dget convert-html-entities "fragment" dset posts append @posts) when temp ) (:temp tpl temp mustache @page temp) - (page %contents) + (page "contents" dset) ) tap @result ) ) ::

@@ -82,7 +82,7 @@ (

symbol process-md-content (dict :meta ==> dict :result) ( - meta /content-type :ct + meta "content-type" dget :ct meta ct process-md-with-template @result )

@@ -92,8 +92,8 @@ (

symbol process-content (dict :meta ==> dict :result) ( - meta /ext :ext - meta /id :id + meta "ext" dget :ext + meta "id" dget :id meta ( ((".md" ext ==) (process-md-content))

@@ -110,7 +110,7 @@ "" :contents

meta ( (input-fread @contents meta) (:temp contents preprocess-css @contents temp) - (contents %contents) + (contents "contents" dset) ) tap output-fwrite )

@@ -123,10 +123,10 @@ (

timestamp :ts data input-fread from-json :contents ; Save modules - contents /modules @modules + contents "modules" dget @modules ; Update timestamp - contents ts %updated @contents - data contents to-json %contents + contents ts "updated" dset @contents + data contents to-json "contents" dset output-fwrite ; Add timesyamp file {

@@ -135,7 +135,7 @@ "mmm" :id

".timestamp" :ext "asset" :type } - ts string %contents + ts string "contents" dset output-fwrite ) ) ::

@@ -145,7 +145,7 @@ symbol download-latest-min-exes

(==>) ( "Downloading latest min executables..." notice! - settings /version :version + settings "version" dget :version ("windows" "macosx" "linux") ( :opsys

@@ -173,7 +173,7 @@

contents ( (dict) expect -> :meta ( - ((meta /id "^_" match?) ()) ;Ignore files starting with underscore. + ((meta "id" dget "^_" match?) ()) ;Ignore files starting with underscore. ((true) (meta process-content set-destination output-fwrite)) ) case ) foreach

@@ -182,8 +182,8 @@ "Processing assets..." notice!

assets ( (dict) expect -> :meta ( - ((meta /ext ".css" ==) (meta process-css-asset)) - ((meta /path "mmm.json" ==) (meta process-mmm-json)) + ((meta "ext" dget ".css" ==) (meta process-css-asset)) + ((meta "path" dget "mmm.json" ==) (meta process-mmm-json)) ((true) (meta output-cp)) ) case ) foreach

@@ -192,54 +192,54 @@ "Generating mmm index..." notice!

; Process deps of each module -> name@version modules ( :module - module /deps dkeys ( + module "deps" dget dkeys ( :name - module /deps name dget :version + module "deps" dget name dget :version "$#@$#" (name version) =% ) map ", " join :dependencies (dependencies "" ==) ("n/a" @dependencies) when - module dependencies %dependencies + module dependencies "dependencies" dset ) map @modules {} - "mmm/index" %id - "mmm.html" %path - ".html" %ext - "mmm" %title - "min language" %site - "page" %content-type - modules %modules + "mmm/index" "id" dset + "mmm.html" "path" dset + ".html" "ext" dset + "mmm" "title" dset + "min language" "site" dset + "page" "content-type" dset + modules "modules" dset dup -"mmm" swap mustache %contents output-fwrite +"mmm" swap mustache "contents" dset output-fwrite "Generating news page..." notice! ; Get news posts - (:a :b a /date b /date >) + (:a :b a "date" dget b "date" dget >) sort @posts {} - "news/index" %id - "news.html" %path - ".html" %ext - "News" %title - "min language" %site - "page" %content-type - posts %posts + "news/index" "id" dset + "news.html" "path" dset + ".html" "ext" dset + "News" "title" dset + "min language" "site" dset + "page" "content-type" dset + posts "posts" dset dup -"news" swap mustache %contents output-fwrite +"news" swap mustache "contents" dset output-fwrite {} - "rss" %id - "rss.xml" %path - ".xml" %ext - "min programming language" %title - "min language" %site - "rss" %content-type - posts %posts + "rss" "id" dset + "rss.xml" "path" dset + ".xml" "ext" dset + "min programming language" "title" dset + "min language" "site" dset + "rss" "content-type" dset + posts "posts" dset dup -"rss" swap mustache %contents output-fwrite +"rss" swap mustache "contents" dset output-fwrite
M tests/global.mintests/global.min

@@ -107,9 +107,9 @@ ((1 2 3 "aaa" 'q q true) to-json "\r\n" "" replace "\n" "" replace " " "" replace "[1,2,3,\"aaa\",\";sym:'q\",\";sym:q\",true]" ==) test.assert

((1 2 3 "aaa" 'q q true) to-json from-json (1 2 3 "aaa" 'q q true) ==) test.assert - (((1 2 3)) :sym1 >sym1 saved-symbols "sym1" in?) test.assert + (((1 2 3)) :sym1 "sym1" save-symbol saved-symbols "sym1" in?) test.assert - (<sym1 symbols "sym1" in?) test.assert + ("sym1" load-symbol symbols "sym1" in?) test.assert ('sym1 remove-symbol saved-symbols "sym1" in? false ==) test.assert

@@ -129,7 +129,7 @@ ) test.assert

(time scope-symbols ("datetime" "now" "tformat" "timeinfo" "timestamp" "to-timestamp") ==) test.assert - (sys scope-sigils ("$") ==) test.assert + (global scope-sigils "$" in?) test.assert ({3 :a 5 :b} scope-symbols ("a" "b") ==) test.assert

@@ -354,6 +354,18 @@

( {{1 :aa} :a 2 :b} :test1 test1.b test1.a.aa + 3 == + ) test.assert + + ( + {} :obj + 3 :obj.three + 2 :obj.two + (obj.three 6 <) + ( + obj.three 1 + @obj.three + ) + while + obj.three 6 == ) test.assert test.report
M tests/net.mintests/net.min

@@ -23,7 +23,8 @@ (line "HTTP/1.1 200 OK" == not)

( cli1 recv-line @line (response line) => "\n" join puts @response - ) while + ) + while (response "200 OK" match?) test.assert
M tests/num.mintests/num.min

@@ -64,7 +64,7 @@

(16 3 shr 2 ==) test.assert (0 :c - (c 10 <) (c succ @c) while + (c 10 <) (c succ @c) while c 10 ==) test.assert ((1 2 3 4 5) (even?) filter (2 4) ==) test.assert