all repos — min @ 8db97ae1667dab6465f0df6346a0fafa158d4095

A small but practical concatenative programming language.

Refactoring min.nim.
h3rald h3rald@h3rald.com
Sat, 28 Oct 2023 15:05:11 +0200
commit

8db97ae1667dab6465f0df6346a0fafa158d4095

parent

882366e6f8ccda2a91614027e9e5f4ab17d5dccf

3 files changed, 241 insertions(+), 203 deletions(-)

jump to
M min.nimmin.nim

@@ -3,11 +3,8 @@ std/[streams,

strutils, sequtils, times, - json, os, - algorithm, - logging], - minline + logging] import minpkg/core/[niftylogger, baseutils,

@@ -16,25 +13,13 @@ parser,

value, scope, interpreter, + stdlib, + shell, utils] import - minpkg/lib/[min_lang, - min_stack, - min_seq, - min_dict, - min_num, - min_str, - min_logic, - min_time, - min_sys, - min_io, - min_dstore, - min_fs, - min_xml, - min_http, - min_net, - min_crypto, - min_math] + minpkg/lib/[ + min_lang + ] export env,

@@ -42,109 +27,18 @@ parser,

interpreter, utils, value, + shell, scope, + stdlib, min_lang, niftylogger -const PRELUDE* = "prelude.min".slurp.strip var NIMOPTIONS* = "" var MINMODULES* = newSeq[string](0) -var customPrelude {.threadvar.}: string -customPrelude = "" if logging.getHandlers().len == 0: newNiftyLogger().addHandler() -proc getCompletions*(ed: LineEditor, symbols: seq[string]): seq[string] = - var words = ed.lineText.split(" ") - var word: string - if words.len == 0: - word = ed.lineText - else: - word = words[words.len-1] - if word.startsWith("'"): - return symbols.mapIt("'" & $it) - if word.startsWith("~"): - return symbols.mapIt("~" & $it) - if word.startsWith("?"): - return symbols.mapIt("?" & $it) - if word.startsWith("@"): - return symbols.mapIt("@" & $it) - if word.startsWith("#"): - return symbols.mapIt("#" & $it) - if word.startsWith(">"): - return symbols.mapIt(">" & $it) - if word.startsWith("*"): - return symbols.mapIt("*" & $it) - if word.startsWith("("): - return symbols.mapIt("(" & $it) - if word.startsWith("<"): - return toSeq(MINSYMBOLS.readFile.parseJson.pairs).mapIt("<" & $it[0]) - if word.startsWith("$"): - return toSeq(envPairs()).mapIt("$" & $it[0]) - if word.startsWith("\""): - var f = word[1..^1] - if f == "": - f = getCurrentDir().replace("\\", "/") - return toSeq(walkDir(f, true)).mapIt("\"$1" % it.path.replace("\\", "/")) - elif f.dirExists: - f = f.replace("\\", "/") - if f[f.len-1] != '/': - f = f & "/" - return toSeq(walkDir(f, true)).mapIt("\"$1$2" % [f, it.path.replace("\\", "/")]) - else: - var dir: string - if f.contains("/") or dir.contains("\\"): - dir = f.parentDir - let file = f.extractFileName - return toSeq(walkDir(dir, true)).filterIt( - it.path.toLowerAscii.startsWith(file.toLowerAscii)).mapIt( - "\"$1/$2" % [dir, it.path.replace("\\", "/")]) - else: - dir = getCurrentDir() - return toSeq(walkDir(dir, true)).filterIt( - it.path.toLowerAscii.startsWith(f.toLowerAscii)).mapIt("\"$1" % [ - it.path.replace("\\", "/")]) - return symbols - - -proc stdLib*(i: In) = - setLogFilter(logging.lvlNotice) - if not MINSYMBOLS.fileExists: - MINSYMBOLS.writeFile("{}") - if not MINHISTORY.fileExists: - MINHISTORY.writeFile("") - if not MINRC.fileExists: - MINRC.writeFile("") - i.lang_module - i.stack_module - i.seq_module - i.dict_module - i.logic_module - i.num_module - i.str_module - i.time_module - i.sys_module - i.fs_module - i.dstore_module - i.io_module - i.crypto_module - i.net_module - i.math_module - i.http_module - i.xml_module - if customPrelude == "": - i.eval PRELUDE, "<prelude>" - else: - try: - i.eval customPrelude.readFile, customPrelude - except CatchableError: - logging.warn("Unable to process custom prelude code in $1" % customPrelude) - try: - i.eval MINRC.readFile() - except CatchableError: - error "An error occurred evaluating the .minrc file." - proc interpret*(i: In, s: Stream) = i.stdLib() i.open(s, i.filename)

@@ -154,15 +48,6 @@ i.interpret()

except CatchableError: discard i.close() - -proc interpret*(i: In, s: string): MinValue = - i.open(newStringStream(s), i.filename) - discard i.parser.getToken() - try: - result = i.interpret() - except CatchableError: - discard - i.close() proc minFile*(fn: string, op = "interpret", main = true): seq[ string] {.discardable.}

@@ -231,90 +116,11 @@ when isMainModule:

import terminal, parseopt, - critbits, minpkg/core/meta var REPL = false var SIMPLEREPL = false var MODULEPATH = "" - var exeName = "min" - - proc printResult(i: In, res: MinValue) = - if res.isNil: - return - if i.stack.len > 0: - let n = $i.stack.len - if res.isQuotation and res.qVal.len > 1: - 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: - var v = "" - if item.val.kind == minProcOp: - v = "<native>" - else: - v = $item.val.val - echo " " & v & " :" & $item.key - if res.objType == "": - echo " ".repeat(n.len) & "}" - else: - echo " ".repeat(n.len) & " ;" & res.objType - echo " ".repeat(n.len) & "}" - else: - echo " $1" % [$i.stack[i.stack.len - 1]] - - proc minSimpleRepl*(i: var MinInterpreter) = - i.stdLib() - var s = newStringStream("") - i.open(s, "<repl>") - var line: string - while true: - i.push(i.newSym("prompt")) - let vals = i.expect("str") - let v = vals[0] - let prompt = v.getString() - stdout.write(prompt) - stdout.flushFile() - line = stdin.readLine() - let r = i.interpret($line) - if $line != "": - i.printResult(r) - - proc minRepl*(i: var MinInterpreter) = - DEV = true - i.stdLib() - var s = newStringStream("") - i.open(s, "<repl>") - var line: string - echo "$# shell v$#" % [exeName, pkgVersion] - while true: - let symbols = toSeq(i.scope.symbols.keys) - EDITOR.completionCallback = proc(ed: LineEditor): seq[string] = - var completions = ed.getCompletions(symbols) - completions.sort() - return completions - # evaluate prompt - i.push(i.newSym("prompt")) - let vals = i.expect("str") - let v = vals[0] - let prompt = v.getString() - line = EDITOR.readLine(prompt) - let r = i.interpret($line) - if $line != "": - i.printResult(r) - - proc minRepl*() = - var i = newMinInterpreter(filename = "<repl>") - i.minRepl() - - proc minSimpleRepl*() = - var i = newMinInterpreter(filename = "<repl>") - i.minSimpleRepl() proc resolveFile(file: string): string = if (file.endsWith(".min") or file.endsWith(".mn")) and fileExists(file):

@@ -352,7 +158,7 @@ compiled executable (if -c is set)

-n, --passN Pass options to the nim compiler (if -c is set) -p, --prelude:<file.min> If specified, it loads <file.min> instead of the default prelude code -v, —-version Print the program version""" % [ - "exe", exeName, + "exe", pkgName, "version", pkgVersion, "year", $(now().year) ]
A minpkg/core/shell.nim

@@ -0,0 +1,161 @@

+ +import + std/[ + strutils, + sequtils, + json, + critbits, + algorithm, + streams, + os + ] + +import + env, + meta, + interpreter, + parser, + stdlib, + utils, + value + +import + minline + +proc interpret*(i: In, s: string): MinValue = + i.open(newStringStream(s), i.filename) + discard i.parser.getToken() + try: + result = i.interpret() + except CatchableError: + discard + i.close() + +proc getCompletions*(ed: LineEditor, symbols: seq[string]): seq[string] = + var words = ed.lineText.split(" ") + var word: string + if words.len == 0: + word = ed.lineText + else: + word = words[words.len-1] + if word.startsWith("'"): + return symbols.mapIt("'" & $it) + if word.startsWith("~"): + return symbols.mapIt("~" & $it) + if word.startsWith("?"): + return symbols.mapIt("?" & $it) + if word.startsWith("@"): + return symbols.mapIt("@" & $it) + if word.startsWith("#"): + return symbols.mapIt("#" & $it) + if word.startsWith(">"): + return symbols.mapIt(">" & $it) + if word.startsWith("*"): + return symbols.mapIt("*" & $it) + if word.startsWith("("): + return symbols.mapIt("(" & $it) + if word.startsWith("<"): + return toSeq(MINSYMBOLS.readFile.parseJson.pairs).mapIt("<" & $it[0]) + if word.startsWith("$"): + return toSeq(envPairs()).mapIt("$" & $it[0]) + if word.startsWith("\""): + var f = word[1..^1] + if f == "": + f = getCurrentDir().replace("\\", "/") + return toSeq(walkDir(f, true)).mapIt("\"$1" % it.path.replace("\\", "/")) + elif f.dirExists: + f = f.replace("\\", "/") + if f[f.len-1] != '/': + f = f & "/" + return toSeq(walkDir(f, true)).mapIt("\"$1$2" % [f, it.path.replace("\\", "/")]) + else: + var dir: string + if f.contains("/") or dir.contains("\\"): + dir = f.parentDir + let file = f.extractFileName + return toSeq(walkDir(dir, true)).filterIt( + it.path.toLowerAscii.startsWith(file.toLowerAscii)).mapIt( + "\"$1/$2" % [dir, it.path.replace("\\", "/")]) + else: + dir = getCurrentDir() + return toSeq(walkDir(dir, true)).filterIt( + it.path.toLowerAscii.startsWith(f.toLowerAscii)).mapIt("\"$1" % [ + it.path.replace("\\", "/")]) + return symbols + +proc printResult(i: In, res: MinValue) = + if res.isNil: + return + if i.stack.len > 0: + let n = $i.stack.len + if res.isQuotation and res.qVal.len > 1: + 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: + var v = "" + if item.val.kind == minProcOp: + v = "<native>" + else: + v = $item.val.val + echo " " & v & " :" & $item.key + if res.objType == "": + echo " ".repeat(n.len) & "}" + else: + echo " ".repeat(n.len) & " ;" & res.objType + echo " ".repeat(n.len) & "}" + else: + echo " $1" % [$i.stack[i.stack.len - 1]] + +proc minSimpleRepl*(i: var MinInterpreter) = + i.stdLib() + var s = newStringStream("") + i.open(s, "<repl>") + var line: string + while true: + i.push(i.newSym("prompt")) + let vals = i.expect("str") + let v = vals[0] + let prompt = v.getString() + stdout.write(prompt) + stdout.flushFile() + line = stdin.readLine() + let r = i.interpret($line) + if $line != "": + i.printResult(r) + +proc minRepl*(i: var MinInterpreter) = + DEV = true + i.stdLib() + var s = newStringStream("") + i.open(s, "<repl>") + var line: string + echo "$# shell v$#" % [pkgName, pkgVersion] + while true: + let symbols = toSeq(i.scope.symbols.keys) + EDITOR.completionCallback = proc(ed: LineEditor): seq[string] = + var completions = ed.getCompletions(symbols) + completions.sort() + return completions + # evaluate prompt + i.push(i.newSym("prompt")) + let vals = i.expect("str") + let v = vals[0] + let prompt = v.getString() + line = EDITOR.readLine(prompt) + let r = i.interpret($line) + if $line != "": + i.printResult(r) + +proc minRepl*() = + var i = newMinInterpreter(filename = "<repl>") + i.minRepl() + +proc minSimpleRepl*() = + var i = newMinInterpreter(filename = "<repl>") + i.minSimpleRepl()
A minpkg/core/stdlib.nim

@@ -0,0 +1,71 @@

+import + std/[ + logging, + os, + strutils + ] + +import + env, + parser, + interpreter + +import + ../lib/[min_lang, + min_stack, + min_seq, + min_dict, + min_num, + min_str, + min_logic, + min_time, + min_sys, + min_io, + min_dstore, + min_fs, + min_xml, + min_http, + min_net, + min_crypto, + min_math] + +const PRELUDE* = "../../prelude.min".slurp.strip +var customPrelude* {.threadvar.}: string +customPrelude = "" + +proc stdLib*(i: In) = + setLogFilter(logging.lvlNotice) + if not MINSYMBOLS.fileExists: + MINSYMBOLS.writeFile("{}") + if not MINHISTORY.fileExists: + MINHISTORY.writeFile("") + if not MINRC.fileExists: + MINRC.writeFile("") + i.lang_module + i.stack_module + i.seq_module + i.dict_module + i.logic_module + i.num_module + i.str_module + i.time_module + i.sys_module + i.fs_module + i.dstore_module + i.io_module + i.crypto_module + i.net_module + i.math_module + i.http_module + i.xml_module + if customPrelude == "": + i.eval PRELUDE, "<prelude>" + else: + try: + i.eval customPrelude.readFile, customPrelude + except CatchableError: + logging.warn("Unable to process custom prelude code in $1" % customPrelude) + try: + i.eval MINRC.readFile() + except CatchableError: + error "An error occurred evaluating the .minrc file."