all repos — min @ 82e5d0eb5a7aadd582a4b3702d14baa2b434ad0d

A small but practical concatenative programming language.

Implemented help system (undocumented ;)
h3rald h3rald@h3rald.com
Wed, 20 Jan 2021 20:32:16 +0100
commit

82e5d0eb5a7aadd582a4b3702d14baa2b434ad0d

parent

757db0e3dc855a469b91d8a931021a8395491065

M min.vimmin.vim

@@ -20,6 +20,7 @@ syntax match minBinding ;@;

syntax keyword minCommentTodo TODO FIXME XXX TBD contained syntax match minComment /;.*$/ contains=minCommentTodo +syntax region minComment start=;#|; end=;|#; contains=minCommentTodo syntax match minNumber ;[-+]\=\d\+\(\.\d*\)\=; syntax keyword minBoolean true false
M min.ymlmin.yml

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

author: Fabio Cevasco description: A tiny concatenative programming language and shell. +id: 36603301 name: min -version: 0.30.0 +version: 0.30.0
M minpkg/core/interpreter.nimminpkg/core/interpreter.nim

@@ -42,7 +42,7 @@ proc newSym*(i: In, s: string): MinValue =

return MinValue(kind: minSymbol, symVal: s, filename: i.currSym.filename, line: i.currSym.line, column: i.currSym.column, outerSym: i.currSym.symVal) proc copySym*(i: In, sym: MinValue): MinValue = - return MinValue(kind: minSymbol, symVal: sym.outerSym, filename: sym.filename, line: sym.line, column: sym.column, outerSym: "") + return MinValue(kind: minSymbol, symVal: sym.outerSym, filename: sym.filename, line: sym.line, column: sym.column, outerSym: "", docComment: sym.docComment) proc raiseRuntime*(msg: string, data: MinValue) = data.objType = "error"

@@ -424,4 +424,11 @@ return i.load(s, true)

# Inherit file/line/column from current symbol proc pushSym*(i: In, s: string) = - i.push MinValue(kind: minSymbol, symVal: s, filename: i.currSym.filename, line: i.currSym.line, column: i.currSym.column, outerSym: i.currSym.symVal) + i.push MinValue( + kind: minSymbol, + symVal: s, + filename: i.currSym.filename, + line: i.currSym.line, + column: i.currSym.column, + outerSym: i.currSym.symVal, + docComment: i.currSym.docComment)
M minpkg/core/parser.nimminpkg/core/parser.nim

@@ -5,6 +5,7 @@ strutils,

sequtils, streams, critbits, + json, baseutils import unicode except strip

@@ -61,6 +62,8 @@ stateDictionary,

stateExpectValue MinParser* = object of BaseLexer a*: string + doc*: bool + currSym*: MinValue token*: MinTokenKind state*: seq[MinParserState] kind*: MinEventKind

@@ -72,6 +75,7 @@ line*: int

column*: int filename*: string outerSym*: string + docComment*: string case kind*: MinKind of minNull: discard of minInt: intVal*: BiggestInt

@@ -99,6 +103,7 @@ minProcOp

minValOp MinOperator* = object sealed*: bool + doc*: JsonNode case kind*: MinOperatorKind of minProcOp: prc*: MinOperatorProc

@@ -346,6 +351,15 @@ add(my.a, buf[pos])

inc(pos) my.bufpos = pos +proc addDoc(my: var MinParser, docComment: string, reset = true) = + if my.doc and not my.currSym.isNil and my.currSym.kind == minSymbol: + if reset: + my.doc = false + if my.currSym.docComment.len == 0 or my.currSym.docComment.len > 0 and my.currSym.docComment[my.currSym.docComment.len-1] == '\n': + my.currSym.docComment &= docComment.strip(true, false) + else: + my.currSym.docComment &= docComment + proc skip(my: var MinParser) = var pos = my.bufpos var buf = my.buf

@@ -353,6 +367,8 @@ while true:

case buf[pos] of ';': # skip line comment: + if buf[pos+1] == ';': + my.doc = true inc(pos, 2) while true: case buf[pos]

@@ -361,33 +377,22 @@ break

of '\c': pos = lexbase.handleCR(my, pos) buf = my.buf + my.addDoc "\n" break of '\L': pos = lexbase.handleLF(my, pos) buf = my.buf + my.addDoc "\n" break else: + my.addDoc $my.buf[pos], false inc(pos) - of '/': - if buf[pos+1] == '/': - # skip line comment: - inc(pos, 2) - while true: - case buf[pos] - of '\0': - break - of '\c': - pos = lexbase.handleCR(my, pos) - buf = my.buf - break - of '\L': - pos = lexbase.handleLF(my, pos) - buf = my.buf - break - else: - inc(pos) - elif buf[pos+1] == '*': + of '#': + if buf[pos+1] == '|': # skip long comment: + if buf[pos+2] == '|': + inc(pos) + my.doc = true inc(pos, 2) while true: case buf[pos]

@@ -396,16 +401,22 @@ my.err = errEOC_Expected

break of '\c': pos = lexbase.handleCR(my, pos) + my.addDoc "\n", false buf = my.buf of '\L': pos = lexbase.handleLF(my, pos) + my.addDoc "\n", false buf = my.buf - of '*': + of '|': inc(pos) - if buf[pos] == '/': + if buf[pos] == '|': + inc(pos) + if buf[pos] == '#': inc(pos) break + my.addDoc $buf[pos], false else: + my.addDoc $my.buf[pos], false inc(pos) else: break

@@ -680,6 +691,7 @@ result = MinValue(kind: minDictionary, scope: scope)

of tkSymbol: result = MinValue(kind: minSymbol, symVal: p.a, column: p.getColumn, line: p.lineNumber, filename: p.filename) p.a = "" + p.currSym = result discard getToken(p) else: let err = "Undefined or invalid value: "&p.a
M minpkg/lib/min_dict.nimminpkg/lib/min_dict.nim

@@ -72,9 +72,6 @@ def.symbol("dtype") do (i: In):

let vals = i.expect("dict") i.push vals[0].objType.newVal - def.sigil("?") do (i: In): - i.pushSym("dhas?") - def.sigil("/") do (i: In): i.pushSym("dget")
M minpkg/lib/min_lang.nimminpkg/lib/min_lang.nim

@@ -191,9 +191,10 @@ var inExpects= newSeq[string](0)

var inVars = newSeq[string](0) var outExpects= newSeq[string](0) var outVars = newSeq[string](0) + var docSig = newSeq[string](0) var generics: CritBitTree[string] var origGenerics: CritBitTree[string] - var o= false + var o = false for vv in sv.qVal: if not vv.isSymbol and not vv.isQuotation: raiseInvalid("Signature must be a quotation of symbols/quotations")

@@ -217,9 +218,11 @@ v = vv.symVal

if check: if v == "==>": o = true + docSig.add "==>" elif not i.validType(v) and not generics.hasKey(v): raiseInvalid("Invalid type specified in signature at position $#" % $(c+1)) else: + docSig.add $vv if o: outExpects.add v else:

@@ -294,14 +297,19 @@ generics = origGenerics

raiseInvalid("Invalid value for output symbol '$#'. Expected $#, found $#" % [outVars[k], tp, $x]) generics = origGenerics # Define symbol/sigil + var doc = newJObject() + doc["operator"] = %n + doc["kind"] = %t + doc["signature"] = %docSig.join(" ") + doc["description"] = %i.currSym.docComment.strip if t == "symbol": if i.scope.symbols.hasKey(n) and i.scope.symbols[n].sealed: raiseUndefined("Attempting to redefine sealed symbol '$1'" % [n]) - i.scope.symbols[n] = MinOperator(kind: minProcOp, prc: p, sealed: false) + i.scope.symbols[n] = MinOperator(kind: minProcOp, prc: p, sealed: false, doc: doc) else: if i.scope.sigils.hasKey(n) and i.scope.sigils[n].sealed: raiseUndefined("Attempting to redefine sealed sigil '$1'" % [n]) - i.scope.sigils[n] = MinOperator(kind: minProcOp, prc: p, sealed: true) + i.scope.sigils[n] = MinOperator(kind: minProcOp, prc: p, sealed: true, doc: doc) def.symbol("expect-empty-stack") do (i: In): let l = i.stack.len

@@ -530,6 +538,62 @@ info "[typeclass] $1 = $2" % [symbol, $code]

if i.scope.symbols.hasKey(symbol) and i.scope.symbols[symbol].sealed: raiseUndefined("Attempting to redefine sealed typeclass '$1'" % [name]) i.scope.symbols[symbol] = MinOperator(kind: minValOp, val: code, sealed: false, quotation: true) + + def.symbol("symbol-help") do (i: In): + let vals = i.expect("'sym") + let s = vals[0].getString + if i.scope.hasSymbol(s): + let sym = i.scope.getSymbol(s) + if not sym.doc.isNil and sym.doc.kind == JObject: + var doc = i.fromJson(sym.doc) + doc.objType = "help" + i.push doc + return + i.push nil.newVal + + def.symbol("sigil-help") do (i: In): + let vals = i.expect("'sym") + let s = vals[0].getString + if i.scope.hasSigil(s): + let sym = i.scope.getSigil(s) + if not sym.doc.isNil and sym.doc.kind == JObject: + i.push i.fromJson(sym.doc) + return + i.push nil.newVal + + def.symbol("help") do (i: In): + if i.stack.len == 0 or not i.stack[i.stack.len-1].isStringLike: + warn "Specify a quoted symbol or string to show its help documentation, e.g. 'puts help" + return + let s = i.pop.getString + var found = false + var foundDoc = false + let displayDoc = proc (j: JsonNode) = + echo "=== $# [$#]" % [j["operator"].getStr, j["kind"].getStr] + echo j["signature"].getStr + if j.hasKey("description"): + let desc = j["description"].getStr + if desc.len != 0: + let lines = desc.split("\n") + echo "" + for l in lines: + echo " " & l + if i.scope.hasSymbol(s): + found = true + let sym = i.scope.getSymbol(s) + if not sym.doc.isNil and sym.doc.kind == JObject: + foundDoc = true + displayDoc(sym.doc) + if i.scope.hasSigil(s): + found = true + let sym = i.scope.getSigil(s) + if not sym.doc.isNil and sym.doc.kind == JObject: + foundDoc = true + displayDoc(sym.doc) + if not found: + warn "Undefined symbol or sigil: $#" % s + elif not foundDoc: + warn "No documentation found for symbol or sigil: $#" % s def.symbol("import") do (i: In): var vals = i.expect("'sym")

@@ -1033,6 +1097,9 @@

def.sigil("~") do (i: In): i.pushSym("delete") + def.sigil("?") do (i: In): + i.pushSym("help") + def.sigil("@") do (i: In): i.pushSym("bind")

@@ -1070,6 +1137,9 @@ i.pushSym("expect-empty-stack")

def.symbol(":") do (i: In): i.pushSym("define") + + def.symbol("?") do (i: In): + i.pushSym("help") def.symbol("@") do (i: In): i.pushSym("bind")
M next-release.mdnext-release.md

@@ -0,0 +1,4 @@

+* Added support for Scheme-style block comments using hashpipe style: `#| ... |#`. +* Implemented support for documentation comments (`;;` or `#|| ... ||#`) placed right after an operator definition. +* **BREAKING CHANGE** -- **?** is now used as a sigil for **help**, not **dget**. +* Added **help** (and also **?** alias and **?** sigil), **symbol-help**, **sigil-help** -- UNDOCUMENTED!
M tasks/templates/min.vimtasks/templates/min.vim

@@ -20,6 +20,7 @@ syntax match minBinding ;@;

syntax keyword minCommentTodo TODO FIXME XXX TBD contained syntax match minComment /;.*$$/ contains=minCommentTodo +syntax region minComment start=;#|; end=;|#; contains=minCommentTodo syntax match minNumber ;[-+]\=\d\+\(\.\d*\)\=; syntax keyword minBoolean true false