Implemented support for [ ] to execute commands.
@@ -267,6 +267,8 @@ echo " ("
for item in res.qVal: echo " " & $item echo " ".repeat(n.len) & ")" + elif res.isCommand: + echo " [" & res.cmdVal & "]" elif res.isDictionary and res.dVal.len > 1: echo " {" for item in res.dVal.pairs:@@ -430,4 +432,4 @@ if isatty(stdin):
minRepl() quit(0) else: - minStream newFileStream(stdin), "stdin", op+ minStream newFileStream(stdin), "stdin", op
@@ -3,6 +3,7 @@ streams,
strutils, sequtils, os, + std/osproc, critbits, json, algorithm,@@ -282,6 +283,10 @@ i.apply(i.scope.getSigil(sigil))
else: raiseUndefined("Undefined symbol '$1'" % [val.symVal]) discard i.trace.pop + elif val.kind == minCommand: + i.debug(val) + let res = execCmdEx(val.cmdVal) + i.push res.output.strip.newVal elif val.kind == minDictionary and val.objType != "module": # Dictionary must be copied every time they are interpreted, otherwise when they are used in cycles they reference each other. var v = i.copyDict(val)
@@ -15,6 +15,7 @@ MinTokenKind* = enum
tkError, tkEof, tkString, + tkCommand, tkInt, tkFloat, tkBracketLe,@@ -29,6 +30,7 @@ MinKind* = enum
minInt, minFloat, minQuotation, + minCommand, minDictionary, minString, minSymbol,@@ -51,6 +53,7 @@ errStringExpected, ## string expected
errBracketRiExpected, ## ``)`` expected errBraceRiExpected, ## ``}`` expected errQuoteExpected, ## ``"`` or ``'`` expected + errSqBracketRiExpected,## ``]`` expected errEOC_Expected, ## ``*/`` expected errEofExpected, ## EOF expected errExprExpected@@ -80,6 +83,7 @@ case kind*: MinKind
of minNull: discard of minInt: intVal*: BiggestInt of minFloat: floatVal*: BiggestFloat + of minCommand: cmdVal*: string of minDictionary: scope*: ref MinScope obj*: pointer@@ -159,6 +163,7 @@ "string expected",
"')' expected", "'}' expected", "'\"' expected", + "']' expected", "'*/' expected", "EOF expected", "expression expected"@@ -167,6 +172,7 @@ tokToStr: array[MinTokenKind, string] = [
"invalid token", "EOF", "string literal", + "command literal", "int literal", "float literal", "(",@@ -331,12 +337,70 @@ add(my.a, buf[pos])
inc(pos) my.bufpos = pos # store back +proc parseCommand(my: var MinParser): MinTokenKind = + result = tkCommand + var pos = my.bufpos + 1 + var buf = my.buf + while true: + case buf[pos] + of '\0': + my.err = errSqBracketRiExpected + result = tkError + break + of ']': + inc(pos) + break + of '\\': + case buf[pos+1] + of '\\', '"', '\'', '/': + add(my.a, buf[pos+1]) + inc(pos, 2) + of 'b': + add(my.a, '\b') + inc(pos, 2) + of 'f': + add(my.a, '\f') + inc(pos, 2) + of 'n': + add(my.a, '\L') + inc(pos, 2) + of 'r': + add(my.a, '\C') + inc(pos, 2) + of 't': + add(my.a, '\t') + inc(pos, 2) + of 'u': + inc(pos, 2) + var r: int + if handleHexChar(buf[pos], r): inc(pos) + if handleHexChar(buf[pos], r): inc(pos) + if handleHexChar(buf[pos], r): inc(pos) + if handleHexChar(buf[pos], r): inc(pos) + add(my.a, toUTF8(Rune(r))) + else: + # don't bother with the error + add(my.a, buf[pos]) + inc(pos) + of '\c': + pos = lexbase.handleCR(my, pos) + buf = my.buf + add(my.a, '\c') + of '\L': + pos = lexbase.handleLF(my, pos) + buf = my.buf + add(my.a, '\L') + else: + add(my.a, buf[pos]) + inc(pos) + my.bufpos = pos # store back + proc parseSymbol(my: var MinParser): MinTokenKind = result = tkSymbol var pos = my.bufpos var buf = my.buf if not(buf[pos] in Whitespace): - while not(buf[pos] in WhiteSpace) and not(buf[pos] in ['\0', ')', '(', '}', '{']): + while not(buf[pos] in WhiteSpace) and not(buf[pos] in ['\0', ')', '(', '}', '{', '[', ']']): if buf[pos] == '"': add(my.a, buf[pos]) my.bufpos = pos@@ -459,6 +523,8 @@ result = tkBracketLe
of ')': inc(my.bufpos) result = tkBracketRi + of '[': + result = parseCommand(my) of '{': inc(my.bufpos) result = tkBraceLe@@ -596,6 +662,8 @@ if a.objType != "":
d = d & ";" & a.objType d = d.strip & "}" return d + of minCommand: + return "[" & a.cmdVal & "]" proc `$$`*(a: MinValue): string {.inline, extern:"min_exported_symbol_$1".}= case a.kind:@@ -617,6 +685,8 @@ for i in a.qVal:
q = q & $i & " " q = q.strip & ")" return q + of minCommand: + return "[" & a.cmdVal & "]" of minDictionary: var d = "{" for i in a.dVal.pairs:@@ -647,6 +717,10 @@ result = MinValue(kind: minBool, boolVal: false)
discard getToken(p) of tkString: result = MinValue(kind: minString, strVal: p.a) + p.a = "" + discard getToken(p) + of tkCommand: + result = MinValue(kind: minCommand, cmdVal: p.a) p.a = "" discard getToken(p) of tkInt:@@ -791,6 +865,9 @@ return s.kind == minSymbol
proc isQuotation*(s: MinValue): bool = return s.kind == minQuotation + +proc isCommand*(s: MinValue): bool = + return s.kind == minCommand proc isString*(s: MinValue): bool = return s.kind == minString
@@ -128,6 +128,8 @@ of minNull:
return newJNull() of minSymbol: return %(";sym:$1" % [a.getstring]) + of minCommand: + return %(";cmd:$1" % [a.getstring]) of minString: return %a.strVal of minInt:@@ -157,6 +159,8 @@ of JString:
let s = json.getStr if s.startsWith(";sym:"): result = s.replace(";sym:", "").newSym + elif s.startsWith(";cmd:"): + result = s.replace(";cmd:", "").newCmd else: result = json.getStr.newVal of JObject:@@ -217,6 +221,8 @@ of "num":
return value.isNumber of "quot": return value.isQuotation + of "cmd": + return value.isCommand of "dict": return value.isDictionary of "'sym":
@@ -8,6 +8,8 @@ of minInt:
return "int" of minFloat: return "flt" + of minCommand: + return "cmd" of minDictionary: if v.isTypedDictionary: return "dict:" & v.objType@@ -53,6 +55,9 @@
proc newSym*(s: string): MinValue = return MinValue(kind: minSymbol, symVal: s) +proc newCmd*(s: string): MinValue = + return MinValue(kind: minCommand, cmdVal: s) + proc hash*(v: MinValue): Hash = return hash($v)@@ -71,6 +76,8 @@ if v.isSymbol:
return v.symVal elif v.isString: return v.strVal + elif v.isCommand: + return v.cmdVal elif v.isQuotation: if v.qVal.len != 1: raiseInvalid("Quotation is not a quoted symbol")
@@ -56,7 +56,7 @@ let vals = i.expect("'sym")
let cmd = vals[0] let res = execCmdEx(cmd.getString) var d = newDict(i.scope) - i.dset(d, "output", res.output.newVal) + i.dset(d, "output", res.output.strip.newVal) i.dset(d, "code", res.exitCode.newVal) i.push(d)
@@ -1,5 +1,6 @@
### New Features +* Implemented support for executing commands by wrapping strings in `[ ]`, like in [mn](https://h3rald.com/mn) ### Fixes and Improvements
@@ -19,6 +19,8 @@ string (str)
: A series of characters wrapped in double quotes: "Hello, World!". quotation (quot) : A list of elements, which may also contain symbols. Quotations can be used to create heterogenous lists of elements of any data type, and also to create a block of code that will be evaluated later on (quoted program). Example: (1 2 3 + \*) +command (cmd) +: A command string wrapped in square brackets that will be immediately executed on the current shell and converted into the command standard output. Example: `[ls -a]` dictionary (dict) : A key/value table. Dictionaries are implemented as an immediately-dequoted quotation, are enclosed in curly braces, and are represented by their symbol definitions. Note that dictionary keys must start with `:`and be followed by a double-quoted string, or a single word (which can be written without double quotes). The {#link-module||dict#} provides some operators on dictionaries.
@@ -63,6 +63,8 @@ .. cd
("systest" rmdir . ls (. "systest") => "/" join in? false ==) *test/assert + ([ls] &ls /output ==) *test/assert + *test/report clear-stack "systest" rmdir