From c1c32688a4afc43f6addb99b8b5fa878944a70e3 Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Mon, 14 Jan 2019 15:42:48 -0800 Subject: [PATCH] Overhaul in progress, mostly working. Moved all the nomsu packages into lib/, including core/*. Changes to how nomsu environments and importing work. --- .gitignore | 3 +- Makefile | 41 +++-- README.md | 15 +- bootstrap.lua | 6 + bootstrap.moon | 2 + compatibility/compatibility.nom | 42 ++--- compatibility/init.nom | 19 +++ consolecolors.lua | 41 ----- error_handling.lua | 23 +-- error_handling.moon | 15 +- examples/how_do_i.nom | 6 +- files.lua | 8 - files.moon | 7 - lib/{base64.nom => base64/init.nom} | 0 lib/commandline/init.nom | 11 ++ .../init.nom} | 0 {core => lib/core}/collections.nom | 0 {core => lib/core}/control_flow.nom | 0 {core => lib/core}/coroutines.nom | 0 {core => lib/core}/errors.nom | 1 + {core => lib/core}/id.nom | 0 {core => lib/core}/init.nom | 0 {core => lib/core}/io.nom | 0 {core => lib/core}/math.nom | 0 {core => lib/core}/metaprogramming.nom | 9 +- {core => lib/core}/operators.nom | 0 {core => lib/core}/text.nom | 0 lib/{file_hash.nom => file_hash/init.nom} | 4 +- lib/{os.nom => filesystem/init.nom} | 15 +- lib/progressbar/init.nom | 16 ++ lib/shell/init.nom | 12 ++ lib/things.nom | 2 +- lib/tools/find.nom | 95 +++++++++++ lib/tools/format.nom | 45 ++++++ {tools => lib/tools}/parse.nom | 18 ++- lib/tools/repl.nom | 86 ++++++++++ lib/tools/replace.nom | 147 ++++++++++++++++++ lib/tools/test.nom | 65 ++++++++ lib/tools/upgrade.nom | 43 +++++ nomsu.lua | 91 ++++++----- nomsu.moon | 69 ++++---- nomsu_compiler.lua | 2 + nomsu_compiler.moon | 2 + nomsu_environment.lua | 68 ++++++-- nomsu_environment.moon | 47 ++++-- string2.lua | 6 + string2.moon | 3 + tools/find.nom | 93 ----------- tools/format.nom | 43 ----- tools/repl.nom | 84 ---------- tools/replace.nom | 145 ----------------- tools/test.nom | 44 ------ tools/upgrade.nom | 41 ----- 53 files changed, 831 insertions(+), 704 deletions(-) create mode 100644 compatibility/init.nom delete mode 100644 consolecolors.lua rename lib/{base64.nom => base64/init.nom} (100%) create mode 100644 lib/commandline/init.nom rename lib/{consolecolor.nom => consolecolor/init.nom} (100%) rename {core => lib/core}/collections.nom (100%) rename {core => lib/core}/control_flow.nom (100%) rename {core => lib/core}/coroutines.nom (100%) rename {core => lib/core}/errors.nom (99%) rename {core => lib/core}/id.nom (100%) rename {core => lib/core}/init.nom (100%) rename {core => lib/core}/io.nom (100%) rename {core => lib/core}/math.nom (100%) rename {core => lib/core}/metaprogramming.nom (97%) rename {core => lib/core}/operators.nom (100%) rename {core => lib/core}/text.nom (100%) rename lib/{file_hash.nom => file_hash/init.nom} (98%) rename lib/{os.nom => filesystem/init.nom} (74%) create mode 100644 lib/progressbar/init.nom create mode 100644 lib/shell/init.nom create mode 100755 lib/tools/find.nom create mode 100755 lib/tools/format.nom rename {tools => lib/tools}/parse.nom (76%) create mode 100755 lib/tools/repl.nom create mode 100755 lib/tools/replace.nom create mode 100755 lib/tools/test.nom create mode 100755 lib/tools/upgrade.nom delete mode 100755 tools/find.nom delete mode 100755 tools/format.nom delete mode 100755 tools/repl.nom delete mode 100755 tools/replace.nom delete mode 100755 tools/test.nom delete mode 100755 tools/upgrade.nom diff --git a/.gitignore b/.gitignore index ab1178f..458db87 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,3 @@ -core/*.lua lib/*.lua -tests/*.lua +lib/*/*.lua .pending-post-commit diff --git a/Makefile b/Makefile index 6f4befe..2497dde 100644 --- a/Makefile +++ b/Makefile @@ -14,12 +14,11 @@ UNINSTALL_VERSION= MOON_FILES= code_obj.moon error_handling.moon files.moon nomsu.moon nomsu_compiler.moon \ syntax_tree.moon containers.moon bitops.moon parser.moon pretty_errors.moon \ string2.moon nomsu_decompiler.moon nomsu_environment.moon bootstrap.moon -LUA_FILES= code_obj.lua consolecolors.lua error_handling.lua files.lua nomsu.lua nomsu_compiler.lua \ +LUA_FILES= code_obj.lua error_handling.lua files.lua nomsu.lua nomsu_compiler.lua \ syntax_tree.lua containers.lua bitops.lua parser.lua pretty_errors.lua \ string2.lua nomsu_decompiler.lua nomsu_environment.lua bootstrap.lua -CORE_NOM_FILES= $(wildcard core/**.nom) -CORE_LUA_FILES= $(patsubst %.nom,%.lua,$(CORE_NOM_FILES)) -LIB_NOM_FILES= $(wildcard lib/**.nom) +CORE_NOM_FILES=$(shell cat lib/core/init.nom | sed -n 's;export "\(.*\)";lib/\1.nom;p') lib/core/init.nom +LIB_NOM_FILES= $(CORE_NOM_FILES) $(wildcard lib/*.nom) $(filter-out $(CORE_NOM_FILES),$(wildcard lib/*/*.nom)) LIB_LUA_FILES= $(patsubst %.nom,%.lua,$(LIB_NOM_FILES)) PEG_FILES= $(wildcard nomsu.*.peg) GET_VERSION= $(LUA_BIN) nomsu.lua --version @@ -29,9 +28,9 @@ all: lua optimize .PHONY: test test: lua optimize @echo "\033[1;4mRunning unoptimized tests...\033[0m" - @$(LUA_BIN) nomsu.lua -O0 tools/test.nom $(CORE_NOM_FILES) $(LIB_NOM_FILES) + @$(LUA_BIN) nomsu.lua -O0 -t test $(LIB_NOM_FILES) @echo "\n\033[1;4mRunning optimized tests...\033[0m" - @$(LUA_BIN) nomsu.lua -O1 tools/test.nom $(CORE_LUA_FILES) $(LIB_LUA_FILES) + @$(LUA_BIN) nomsu.lua -O1 -t test $(LIB_LUA_FILES) %.lua: %.moon @moonc $< @@ -40,18 +39,18 @@ test: lua optimize @$(LUA_BIN) nomsu.lua -c $< .DELETE_ON_ERROR: version -version: $(LUA_FILES) $(CORE_NOM_FILES) $(LIB_NOM_FILES) +version: $(LUA_FILES) $(LIB_NOM_FILES) @$(LUA_BIN) nomsu.lua --version > version || exit lua: $(LUA_FILES) .PHONY: optimize -optimize: lua $(CORE_LUA_FILES) $(LIB_LUA_FILES) +optimize: lua $(LIB_LUA_FILES) .PHONY: clean clean: @echo "\033[1mDeleting...\033[0m" - @rm -rvf version core/**.lua lib/**.lua tools/**.lua compatibility/**.lua + @rm -rvf version lib/*.lua lib/*/*.lua compatibility/*.lua .PHONY: install install: lua version optimize @@ -74,12 +73,12 @@ install: lua version optimize fi; \ version="`cat version`"; \ mkdir -pv $$prefix/bin $$prefix/lib/nomsu/$$version $$prefix/share/nomsu/$$version $$prefix/share/man/man1 $$packagepath/nomsu \ - && echo "#!$(LUA_BIN)\\nlocal NOMSU_VERSION, NOMSU_PREFIX, NOMSU_PACKAGEPATH = [[$$version]], [[$$prefix]], [[$$packagepath]]" \ + && echo "#!$(LUA_BIN)\\nlocal NOMSU_VERSION, NOMSU_PREFIX, NOMSU_PACKAGEPATH = [[$$version]], [[$$prefix]], [[$$packagepath/nomsu]]" \ | cat - nomsu.lua > $$prefix/bin/nomsu$$version \ && chmod +x $$prefix/bin/nomsu$$version \ && cp -v nomsu $$prefix/bin \ && cp -v doc/nomsu.1 $$prefix/share/man/man1 \ - && cp -rv $(LUA_FILES) $(PEG_FILES) core lib compatibility tools $$prefix/share/nomsu/$$version; + && cp -rv $(LUA_FILES) $(PEG_FILES) lib compatibility $$prefix/share/nomsu/$$version; .PHONY: uninstall uninstall: version @@ -88,17 +87,10 @@ uninstall: version read -p $$'\033[1mWhere do you want to uninstall Nomsu from? (default: /usr/local) \033[0m' prefix; \ fi; \ if [[ ! $$prefix ]]; then prefix="/usr/local"; fi; \ - packagepath="$(PACKAGEPATH)"; \ - if [[ ! $$packagepath ]]; then \ - read -p $$'\033[1mWhere have your Nomsu packages been installed? (default: /opt) \033[0m' packagepath; \ - fi; \ - if [[ ! $$packagepath ]]; then packagepath="/opt"; fi; \ echo "\033[1mNomsu will be uninstalled from:\033[0m"; \ echo " $$prefix/bin"; \ echo " $$prefix/lib"; \ echo " $$prefix/share"; \ - echo "\033[1mNomsu packages will be uninstalled from:\033[0m"; \ - echo " $$packagepath/nomsu"; \ read -p $$'\033[1mis this okay? [Y/n]\033[0m ' ans; \ if [[ $$ans =~ ^[Nn] ]]; then exit; fi; \ echo "\033[1mDeleting...\033[0m"; \ @@ -118,12 +110,17 @@ uninstall: version fi; \ if [ "`ls $$prefix/lib/nomsu 2>/dev/null`" == "" ]; then rm -rvf $$prefix/lib/nomsu; fi;\ if [ "`ls $$prefix/share/nomsu 2>/dev/null`" == "" ]; then rm -rvf $$prefix/share/nomsu; fi;\ + echo $$'\033[1mDone.\033[0m'; + +uninstallpackages: + @packagepath="$(PACKAGEPATH)"; \ + if [[ ! $$packagepath ]]; then \ + read -p $$'\033[1mWhere have your Nomsu packages been installed? (default: /opt) \033[0m' packagepath; \ + fi; \ + if [[ ! $$packagepath ]]; then packagepath="/opt"; fi; \ if [ -d $$packagepath/nomsu ]; then \ read -p $$'\033[1mDo you want to delete all installed libraries from /opt? [y/n] \033[0m' confirm; \ if [[ $$confirm == "y" ]]; then \ rm -rvf $$packagepath/nomsu; \ fi; \ - fi; \ - echo $$'\033[1mDone.\033[0m'; - -# eof + fi; diff --git a/README.md b/README.md index a8873f0..695fbde 100644 --- a/README.md +++ b/README.md @@ -62,8 +62,7 @@ All `.moon` files have been precompiled into corresponding `.lua` files, so you * [string2.moon](string2.moon) - A library defining some extra functionality for strings. * [syntax\_tree.moon](syntax_tree.moon) - Datastructures used for Nomsu Abstract Syntax Trees. * [examples/how\_do\_i.nom](examples/how_do_i.nom) - A simple walkthrough of some of the features of Nomsu, written in Nomsu code. **This is a good place to start.** -* [core/\*.nom](core) - Core language definitions of stuff like control flow, operators, and metaprogramming, broken down into different files. -* [lib/\*.nom](lib) - Optional language libraries for stuff you might want, like interfacing with the OS, or doing Object Oriented Programming. +* [lib/\*/\*.nom](lib) - Language libraries, including the core language stuff like control flow, operators, and metaprogramming (in [lib/core](lib/core)) and optional language libraries for stuff you might want. * [compatibility/\*.nom](compatibility) - Code for automatically upgrading Nomsu code from old versions to the current version. * [tools/\*.nom](tools) - A set of utilities useful for doing code manipulation actions. * [Makefile](Makefile) - Rules for building/installing the compiler. @@ -72,14 +71,14 @@ All `.moon` files have been precompiled into corresponding `.lua` files, so you ## Versioning -Nomsu uses the following versioning scheme: `[syntax version].[core library API version].[compiler internal API version].[lib/ API version]`. Which means: +Nomsu uses the following versioning scheme: `[syntax version].[core library API version].[compiler internal API version].[minor version]`. Which means: -* Any code that parses on `Nomsu X.a.b.c.d` will also parse on Nomsu `X.p.w.r.s` -* Any code that compiles on Nomsu `X.Y.a.b.c` will also compile on Nomsu `X.Y.p.q.r` and run without any differences, as long as it only depends on the behavior of the core library functions (i.e. stuff defined in [core/\*.nom](core)), and doesn't mess with the compiler internals at all. -* Any code that compiles on Nomsu `X.Y.Z.a.b` will also compile on Nomsu `X.Y.Z.p.q` and run without any differences, even if it messes with the compiler internals, as long as it doesn't use anything from [lib/\*.nom](lib). -* Any code that compiles on Nomsu `X.Y.Z.W` will also compile on any other Nomsu `X.Y.Z.W` and run without any differences, even if it uses stuff from [lib/\*.nom](lib). +* Any code that parses on `Nomsu X.a.b.c` will also parse on Nomsu `X.p.w.r` +* Any code that compiles on Nomsu `X.Y.a.b` will also compile on Nomsu `X.Y.p.q` and run without any differences, as long as it only depends on the behavior of the core library functions (i.e. stuff defined in [lib/core/\*.nom](lib/core)), and doesn't mess with the compiler internals at all. +* Any code that compiles on Nomsu `X.Y.Z.a` will also compile on Nomsu `X.Y.Z.p` and run without any differences, unless if it messes with the compiler internals. +* Any code that compiles on Nomsu `X.Y.Z.W` will also compile on any other Nomsu `X.Y.Z.W` and run without any differences. -When Nomsu is istalled via `make install`, all of Nomsu's lua files and [core/\*.nom](core) and [lib/\*.nom](lib) files are stored in `$PREFIX/lib/nomsu/$NOMSU_VERSION` and the Nomsu executable is installed to `$PREFIX/bin/nomsu$NOMSU_VERSION`, along with the file `nomsu` (the version-selection script), which goes to `$PREFIX/bin/nomsu`. When `make uninstall` is run, all those files are deleted (except for `nomsu`, if there are other versions installed). +When Nomsu is istalled via `make install`, all of Nomsu's lua files and [lib/](lib) files are stored in `$PREFIX/share/nomsu/$NOMSU_VERSION` and the Nomsu executable is installed to `$PREFIX/bin/nomsu$NOMSU_VERSION`, along with the file `nomsu` (the version-selection script), which goes to `$PREFIX/bin/nomsu`. When `make uninstall` is run, all those files are deleted (except for `nomsu`, if there are other versions installed). To run different versions, use the `-V` flag, which will select the latest version matching the specified pattern. For example, if you have v1.0.0.0, v1.0.2.1, and 1.1.0.0 installed, then `nomsu` will run v1.1.0.0, `nomsu -V 1.0` will run v1.0.2.1, and `nomsu -V 1.0.0.0` will run v1.0.0.0. diff --git a/bootstrap.lua b/bootstrap.lua index 10d6f3b..c257965 100644 --- a/bootstrap.lua +++ b/bootstrap.lua @@ -157,6 +157,12 @@ local compile_actions = { end, ["nomsu environment name"] = function(self) return LuaCode('"_ENV"') + end, + ["this file was run directly"] = function(self) + return LuaCode('WAS_RUN_DIRECTLY') + end, + ["the command line arguments"] = function(self) + return LuaCode('COMMAND_LINE_ARGS') end } return compile_actions diff --git a/bootstrap.moon b/bootstrap.moon index f5b5900..05bd2a6 100644 --- a/bootstrap.moon +++ b/bootstrap.moon @@ -118,6 +118,8 @@ compile_actions = { ["Lua version"]: (code)=> LuaCode("_VERSION") ["nomsu environment"]: ()=> LuaCode("_ENV") ["nomsu environment name"]: ()=> LuaCode('"_ENV"') + ["this file was run directly"]: => LuaCode('WAS_RUN_DIRECTLY') + ["the command line arguments"]: => LuaCode('COMMAND_LINE_ARGS') } return compile_actions diff --git a/compatibility/compatibility.nom b/compatibility/compatibility.nom index e0449c8..444be11 100644 --- a/compatibility/compatibility.nom +++ b/compatibility/compatibility.nom @@ -3,7 +3,7 @@ This file contains code for defining ways to upgrade code between different versions of Nomsu. -use "lib/os" +use "filesystem" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -68,18 +68,33 @@ externally [ $tree upgraded to $end_version from $start_version ] all mean: unless ($tree is syntax tree): return $tree + ($ver as list) means (($ as number) for $ in $ver matching "[0-9]+") + + (Ver $) means: + [$lib, $ver] = ($, match "(.*)/([0-9.]+)") + if $lib: + return {.lib = $lib, .version = ($ver as list)} + return {.version = ($ as list)} + + $start = (Ver $start_version) + $end = (Ver $end_version) + assume $start.lib == $end.lib + + $seen = {} $versions = {} - for $v = $ in $UPGRADES: - $versions.$v = (yes) + for $v = $ in $UPGRADES: $versions.$v = (yes) + for $v = $ in $ACTION_UPGRADES: $versions.$v = (yes) + $versions = [ + :for $v = $ in $versions: + if ((Ver $v).lib == $start.lib): + add $v + ] - for $v = $ in $ACTION_UPGRADES: - $versions.$v = (yes) - - $versions = ((keys in $versions) sorted by $ -> ($ as list)) + sort $versions by $ -> ($ as list) for $ver in $versions: - if (($ver as list) <= ($start_version as list)): do next $ver - if (($ver as list) > ($end_version as list)): stop $ver + if (($ver as list) <= $start.version): do next $ver + if (($ver as list) > $end.version): stop $ver if $ACTION_UPGRADES.$ver: $tree = $tree with $ ->: @@ -115,12 +130,3 @@ externally ($tree upgraded to $end_version) means externally ($tree upgraded) means $tree upgraded from ($tree.version or (Nomsu version)) to (Nomsu version) - -externally (use $path from version $version) means: - for $filename in (files for $path): - if (=lua "LOADED[\$filename]"): - do next $filename - $file = (read file $filename) - $tree = (parse $file from $filename) - $tree = (upgrade $tree from $version) - run tree $tree diff --git a/compatibility/init.nom b/compatibility/init.nom new file mode 100644 index 0000000..056428f --- /dev/null +++ b/compatibility/init.nom @@ -0,0 +1,19 @@ + +export "compatibility/compatibility" +export "compatibility/2" +export "compatibility/2.3" +export "compatibility/2.4" +export "compatibility/2.5" +export "compatibility/2.5.5.5" +export "compatibility/3" +export "compatibility/3.5.5.6" +export "compatibility/3.6" +export "compatibility/3.7" +export "compatibility/3.8" +export "compatibility/4.8.10" +export "compatibility/4.9" +export "compatibility/4.10.12.7" +export "compatibility/4.11" +export "compatibility/4.12" +export "compatibility/5.13" +export "compatibility/6.14" diff --git a/consolecolors.lua b/consolecolors.lua deleted file mode 100644 index 650bc81..0000000 --- a/consolecolors.lua +++ /dev/null @@ -1,41 +0,0 @@ --- --- This file contains the ANSI escapes for console colors --- - -local colors = { - -- attributes - reset = 0, - clear = 0, - bright = 1, - dim = 2, - underscore = 4, - blink = 5, - reverse = 7, - hidden = 8, - - -- foreground - black = 30, - red = 31, - green = 32, - yellow = 33, - blue = 34, - magenta = 35, - cyan = 36, - white = 37, - - -- background - onblack = 40, - onred = 41, - ongreen = 42, - onyellow = 43, - onblue = 44, - onmagenta = 45, - oncyan = 46, - onwhite = 47, -} - -local _M = {} -for c, v in pairs(colors) do - _M[c] = string.char(27)..("[%dm"):format(v) -end -return _M diff --git a/error_handling.lua b/error_handling.lua index 76b4390..27022aa 100644 --- a/error_handling.lua +++ b/error_handling.lua @@ -1,5 +1,5 @@ -local files = require("files") local debug_getinfo = debug.getinfo +local Files = require("files") local RED = "\027[31m" local BRIGHT_RED = "\027[31;1m" local RESET = "\027[0m" @@ -119,7 +119,6 @@ print_error = function(error_message, start_fn, stop_fn) filename, start = calling_fn.source:match('@([^[]*)%[([0-9]+)]') end assert(filename) - local file = files.read(filename) if calling_fn.name then do local tmp = calling_fn.name:match("^A_([a-zA-Z0-9_]*)$") @@ -134,8 +133,10 @@ print_error = function(error_message, start_fn, stop_fn) else name = "main chunk" end + local file = Files.read(filename) + local lines = file and file:lines() or { } do - local err_line = files.get_line(file, calling_fn.currentline) + local err_line = lines[calling_fn.currentline] if err_line then local offending_statement = tostring(BRIGHT_RED) .. tostring(err_line:match("^[ ]*(.*)")) .. tostring(RESET) line = tostring(YELLOW) .. tostring(filename) .. ":" .. tostring(calling_fn.currentline) .. " in " .. tostring(name) .. "\n " .. tostring(offending_statement) .. tostring(RESET) @@ -144,13 +145,6 @@ print_error = function(error_message, start_fn, stop_fn) end end else - local file - ok, file = pcall(function() - return files.read(calling_fn.short_src) - end) - if not ok then - file = nil - end local line_num if name == nil then local search_level = level @@ -189,6 +183,13 @@ print_error = function(error_message, start_fn, stop_fn) end end end + local file, lines + do + file = Files.read(calling_fn.short_src) + if file then + lines = file:lines() + end + end if file and (calling_fn.short_src:match("%.moon$") or file:match("^#![^\n]*moon\n")) and type(MOON_SOURCE_MAP[file]) == 'table' then local char = MOON_SOURCE_MAP[file][calling_fn.currentline] line_num = 1 @@ -206,7 +207,7 @@ print_error = function(error_message, start_fn, stop_fn) end if file then do - local err_line = files.get_line(file, line_num) + local err_line = lines[line_num] if err_line then local offending_statement = tostring(BRIGHT_RED) .. tostring(err_line:match("^[ ]*(.*)$")) .. tostring(RESET) line = line .. ("\n " .. offending_statement) diff --git a/error_handling.moon b/error_handling.moon index 46580d6..c22a3ae 100644 --- a/error_handling.moon +++ b/error_handling.moon @@ -1,6 +1,6 @@ -- This file contains the logic for making nicer error messages -files = require "files" debug_getinfo = debug.getinfo +Files = require "files" export SOURCE_MAP RED = "\027[31m" @@ -78,7 +78,6 @@ print_error = (error_message, start_fn, stop_fn)-> if not filename filename,start = calling_fn.source\match('@([^[]*)%[([0-9]+)]') assert(filename) - file = files.read(filename) -- TODO: get name properly name = if calling_fn.name if tmp = calling_fn.name\match("^A_([a-zA-Z0-9_]*)$") @@ -86,14 +85,14 @@ print_error = (error_message, start_fn, stop_fn)-> else "action '#{calling_fn.name}'" else "main chunk" - if err_line = files.get_line(file, calling_fn.currentline) + file = Files.read(filename) + lines = file and file\lines! or {} + if err_line = lines[calling_fn.currentline] offending_statement = "#{BRIGHT_RED}#{err_line\match("^[ ]*(.*)")}#{RESET}" line = "#{YELLOW}#{filename}:#{calling_fn.currentline} in #{name}\n #{offending_statement}#{RESET}" else line = "#{YELLOW}#{filename}:#{calling_fn.currentline} in #{name}#{RESET}" else - ok, file = pcall ->files.read(calling_fn.short_src) - if not ok then file = nil local line_num if name == nil search_level = level @@ -117,6 +116,10 @@ print_error = (error_message, start_fn, stop_fn)-> name = "upvalue '#{varname}'" if not varname\match("%(") break + + local file, lines + if file = Files.read(calling_fn.short_src) + lines = file\lines! if file and (calling_fn.short_src\match("%.moon$") or file\match("^#![^\n]*moon\n")) and type(MOON_SOURCE_MAP[file]) == 'table' char = MOON_SOURCE_MAP[file][calling_fn.currentline] @@ -131,7 +134,7 @@ print_error = (error_message, start_fn, stop_fn)-> line = "#{BLUE}#{calling_fn.short_src}:#{calling_fn.currentline} in #{name or '?'}#{RESET}" if file - if err_line = files.get_line(file, line_num) + if err_line = lines[line_num] offending_statement = "#{BRIGHT_RED}#{err_line\match("^[ ]*(.*)$")}#{RESET}" line ..= "\n "..offending_statement io.stderr\write(line,"\n") diff --git a/examples/how_do_i.nom b/examples/how_do_i.nom index 14e7481..906ef74 100644 --- a/examples/how_do_i.nom +++ b/examples/how_do_i.nom @@ -6,11 +6,9 @@ is considered part of the comment (including any deeper-level indented text) The comment ends when the indentation ends -# How do I import a file? -use "lib/os" -# How do I import all the files in a directory? -use "lib" +# How do I import a libarary? +use "consolecolor" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/files.lua b/files.lua index 036471f..0c8558d 100644 --- a/files.lua +++ b/files.lua @@ -213,12 +213,4 @@ Files.get_line = function(str, line_no) end return (str:sub(start, stop - 2)) end -local get_lines = re.compile([[ lines <- {| line (%nl line)* |} - line <- {[^%nl]*} -]], { - nl = lpeg.P("\r") ^ -1 * lpeg.P("\n") -}) -Files.get_lines = function(str) - return get_lines:match(str) -end return Files diff --git a/files.moon b/files.moon index 3950ce3..a9fbb5c 100644 --- a/files.moon +++ b/files.moon @@ -134,11 +134,4 @@ Files.get_line = (str, line_no)-> return unless stop return (str\sub(start, stop - 2)) -get_lines = re.compile([[ - lines <- {| line (%nl line)* |} - line <- {[^%nl]*} -]], nl:lpeg.P("\r")^-1 * lpeg.P("\n")) - -Files.get_lines = (str)-> get_lines\match(str) - return Files diff --git a/lib/base64.nom b/lib/base64/init.nom similarity index 100% rename from lib/base64.nom rename to lib/base64/init.nom diff --git a/lib/commandline/init.nom b/lib/commandline/init.nom new file mode 100644 index 0000000..f28e02b --- /dev/null +++ b/lib/commandline/init.nom @@ -0,0 +1,11 @@ +# + A library defining some command line program functionality + +(command line program with $args $body) parses as: + externally (run with $args) means $body + if (this file was run directly): + run with (the command line arguments) + +externally (usage $) means: + say "Usage: \$" + exit 1 diff --git a/lib/consolecolor.nom b/lib/consolecolor/init.nom similarity index 100% rename from lib/consolecolor.nom rename to lib/consolecolor/init.nom diff --git a/core/collections.nom b/lib/core/collections.nom similarity index 100% rename from core/collections.nom rename to lib/core/collections.nom diff --git a/core/control_flow.nom b/lib/core/control_flow.nom similarity index 100% rename from core/control_flow.nom rename to lib/core/control_flow.nom diff --git a/core/coroutines.nom b/lib/core/coroutines.nom similarity index 100% rename from core/coroutines.nom rename to lib/core/coroutines.nom diff --git a/core/errors.nom b/lib/core/errors.nom similarity index 99% rename from core/errors.nom rename to lib/core/errors.nom index 45fc8c5..c878bb7 100644 --- a/core/errors.nom +++ b/lib/core/errors.nom @@ -4,6 +4,7 @@ use "core/metaprogramming" use "core/operators" +use "core/control_flow" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/core/id.nom b/lib/core/id.nom similarity index 100% rename from core/id.nom rename to lib/core/id.nom diff --git a/core/init.nom b/lib/core/init.nom similarity index 100% rename from core/init.nom rename to lib/core/init.nom diff --git a/core/io.nom b/lib/core/io.nom similarity index 100% rename from core/io.nom rename to lib/core/io.nom diff --git a/core/math.nom b/lib/core/math.nom similarity index 100% rename from core/math.nom rename to lib/core/math.nom diff --git a/core/metaprogramming.nom b/lib/core/metaprogramming.nom similarity index 97% rename from core/metaprogramming.nom rename to lib/core/metaprogramming.nom index fa29a8a..936f9bd 100644 --- a/core/metaprogramming.nom +++ b/lib/core/metaprogramming.nom @@ -85,7 +85,7 @@ test: fail "compile to is leaking variables" lua> (" - COMPILE_RULES["1 compiles to"] = function(env, \$action, \$body) + COMPILE_RULES["1 compiles to"] = function(\(nomsu environment), \$action, \$body) local \$args = List{"\(nomsu environment)", unpack(\$action:get_args())} if \$body.type == "Text" then \$body = SyntaxTree{source=\$body.source, type="Action", "Lua", \$body} @@ -274,7 +274,7 @@ test: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ [$action parses as $body] all parse as ([$action] all parse as $body) -externally ($tree as lua expr) means: +externally (in (nomsu environment) $tree as lua expr) means: lua> (" local tree_lua = \(nomsu environment):compile(\$tree) if \$tree.type == 'Block' then @@ -287,6 +287,11 @@ externally ($tree as lua expr) means: return tree_lua ") +# Need to make sure the proper environment is used for compilation (i.e. the caller's environment) +($tree as lua expr) compiles to (\(in \(nomsu environment) $tree as lua expr) as lua) + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + externally [$var as lua identifier, $var as lua id] all mean: lua> (" local lua = \($var as lua) diff --git a/core/operators.nom b/lib/core/operators.nom similarity index 100% rename from core/operators.nom rename to lib/core/operators.nom diff --git a/core/text.nom b/lib/core/text.nom similarity index 100% rename from core/text.nom rename to lib/core/text.nom diff --git a/lib/file_hash.nom b/lib/file_hash/init.nom similarity index 98% rename from lib/file_hash.nom rename to lib/file_hash/init.nom index 70446ca..7b428cd 100644 --- a/lib/file_hash.nom +++ b/lib/file_hash/init.nom @@ -2,8 +2,8 @@ # This file defines some actions for hashing files and looking up files by hash. -use "lib/os" -use "lib/base64" +use "filesystem" +use "base64" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/lib/os.nom b/lib/filesystem/init.nom similarity index 74% rename from lib/os.nom rename to lib/filesystem/init.nom index 87b3426..a228bdc 100644 --- a/lib/os.nom +++ b/lib/filesystem/init.nom @@ -1,6 +1,6 @@ #!/usr/bin/env nomsu -V6.14 # - This file defines some actions that interact with the operating system and filesystem. + This file defines some actions that interact with the filesystem. externally (files for $path) means: $files = (=lua "Files.list(\$path)") @@ -8,19 +8,6 @@ externally (files for $path) means: $files = (List $files) return $files -externally (=sh $cmd) means: - lua> (" - local result = io.popen(\$cmd) - local contents = result:read("*a") - result:close() - return contents - ") - -external $(sh> $) = $os.execute - -test: - read file "lib/os.nom" - external $(read file $filename) = $Files.read externally [ write to file $filename $text, to file $filename write $text diff --git a/lib/progressbar/init.nom b/lib/progressbar/init.nom new file mode 100644 index 0000000..a7ff5aa --- /dev/null +++ b/lib/progressbar/init.nom @@ -0,0 +1,16 @@ +# A progress bar + +externally ($x / $w progress bar) means: + $x = ($x clamped between 0 and $w) + $bits = [" ", "▏", "▎", "▍", "▌", "▋", "▊", "▉", "█"] + $middle = + "" if ($x == $w) else + $bits.(1 + (floor ((#$bits) * ($x mod 1)))) + + return (" + \027[32;40m\($bits, last, rep (floor $x))\$middle\ + ..\(" ", rep ($w - ((floor $x) + 1)))\027[0m + ") + +externally ($w wide $ progress bar) means + ($ * $w) / $w progress bar diff --git a/lib/shell/init.nom b/lib/shell/init.nom new file mode 100644 index 0000000..6a8970e --- /dev/null +++ b/lib/shell/init.nom @@ -0,0 +1,12 @@ +# + This file defines some actions for running shell commands. + +externally (=sh $cmd) means: + lua> (" + local result = io.popen(\$cmd) + local contents = result:read("*a") + result:close() + return contents + ") + +external $(sh> $) = $os.execute diff --git a/lib/things.nom b/lib/things.nom index 84682b3..95f175d 100644 --- a/lib/things.nom +++ b/lib/things.nom @@ -36,7 +36,7 @@ test: assume (($d, bark) == "Bark!") a (Corgi) is a thing: - $it [set up, gets pissed off] like a (Dog) + $it can [set up, gets pissed off] like a (Dog) ($it, as text) means "Dogloaf \{: for $k = $v in $it: add $k = $v}" ($its, sploot) means "sploooot" [($its, bark), ($its, woof)] all mean: diff --git a/lib/tools/find.nom b/lib/tools/find.nom new file mode 100755 index 0000000..aa4bc7b --- /dev/null +++ b/lib/tools/find.nom @@ -0,0 +1,95 @@ +#!/usr/bin/env nomsu -V6.14 +# + This is a tool to find syntax trees matching a pattern. "*" is a wildcard + that will match any subtree, and "**" is a wildcard that will match any + 0 or more subtrees. "**" is greedy, so extra arguments after it will + not match. + + nomsu -t find [flags] "* squared" file1 file2... + + Flags: + -l List only the names of the files with matches + --wildcard= Specify a custom wildcard (in case you need to + match an action with a "*" in the name) + + Output: + :: + + +use "filesystem" +use "consolecolor" +use "commandline" + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +command line program with $args: + $wildcard = ($args.wildcard or "%*") + $pattern = $args.extras.1 + if (any of [not $pattern, $pattern == "*", $pattern == "**"]): + usage (" + nomsu -t find [-l] [--wildcard=] , where is valid Nomsu code + ") + $pattern = ($pattern, with "\$wildcard\$wildcard" -> "$multi_wildcard") + $pattern = ($pattern, with $wildcard -> "$wildcard") + $pattern_tree = ($pattern parsed) + ($tree matches $patt) means: + when: + (not ($tree is syntax tree)): return (no) + (($patt.type == "Var") and ($patt.1 == "wildcard")): return (yes) + ($tree.type != $patt.type): return (no) + ($tree.type == "Action"): + if (($tree, get stub) != ($patt, get stub)): return (no) + + for $ in 1 to (#$patt): + if ($patt.$ is syntax tree): + if ($patt.$ == \$multi_wildcard): return (yes) + unless ($tree.$ matches $patt.$): return (no) + ..else: + unless ($tree.$ == $patt.$): return (no) + + if ((#$tree) != (#$patt)): return (no) + return (yes) + $filenames = ($args.extras, from 2 to -1) + if ((#$filenames) == 0): + say (" + Warning: searching stdin (ctrl-d to abort). To avoid this message, use nomsu -t find - + ") + $filenames = ["stdin"] + + for $filename in $filenames: + $file = (read file $filename) + unless $file: + fail "File does not exist: \$filename" + $code = (NomsuCode from ($Source $filename 1 (size of $file)) $file) + try: + $tree = ($code parsed) + ..if it fails $msg: + say + red (" + \$filename failed to parse: + \$msg + ") + $tree = (nil) + + unless $tree: + do next $filename + + $results = [] + for $t in recursive $tree: + if ($t matches $pattern_tree): + $line_num = ($file, line number at $t.source.start) + $results, add { + .line = $line_num, .text = "\(blue "\$filename:\$line_num:")\n\(source lines of $t)" + } + + for $sub in $t: + if ($sub is syntax tree): + recurse $t on $sub + + if $args.l: + if ((#$results) > 0): + say $filename + ..else: + sort $results by $ -> $.line + for $ in $results: + say $.text diff --git a/lib/tools/format.nom b/lib/tools/format.nom new file mode 100755 index 0000000..e60d12e --- /dev/null +++ b/lib/tools/format.nom @@ -0,0 +1,45 @@ +#!/usr/bin/env nomsu -V6.14 +# + Auto-format Nomsu code. Usage: + nomsu -t format [-i] file1 file2... + + If the "-i" flag is used, the file will be edited in-place. + If the "-q" flag is used and an error occurs, the original file will be printed. + If no files are passed in, this will read from stdin. + +use "filesystem" +use "commandline" + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +command line program with $args: + $filenames = $args.extras + if ((#$filenames) == 0): + say (" + Warning: reading from stdin (ctrl-d to abort). To avoid this message, use nomsu -t format - + ") + $filenames = ["stdin"] + + for $filename in $filenames: + $file = (read file $filename) + unless $file: + fail "File does not exist: \$filename" + $leading_indent = ($file, matching "\n*([ ]*)") + $code = (NomsuCode from ($Source $filename 1 (size of $file)) $file) + try: + $tree = ($code parsed) + ..if it fails $msg: + if $args.q: + $formatted = $file + ..else: + say $msg + + if ($tree and (not $formatted)): + $formatted = + "\$leading_indent\($tree as nomsu, text, with "\n" -> "\n\$leading_indent")" + + if $formatted: + if $args.i: + write $formatted to file $filename + ..else: + say $formatted inline diff --git a/tools/parse.nom b/lib/tools/parse.nom similarity index 76% rename from tools/parse.nom rename to lib/tools/parse.nom index 22bfdc3..63cc247 100755 --- a/tools/parse.nom +++ b/lib/tools/parse.nom @@ -3,7 +3,8 @@ Tool to print out a parse tree of files in an easy-to-read format. Usage: nomsu tools/parse.nom file1 file2 directory1 ... -use "lib/os" +use "filesystem" +use "commandline" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -36,10 +37,11 @@ externally (print tree $t at indent $indent) means: else: say "\$indent \(quote $arg)" -for $filename in $(COMMAND LINE ARGS).extras: - $file = (read file $filename) - unless $file: - fail "File does not exist: \$filename" - $nomsu = (NomsuCode from (Source $filename 1 (size of $file)) $file) - $tree = ($nomsu parsed) - print tree $tree at indent "" +command line program with $args: + for $filename in $args.extras: + $file = (read file $filename) + unless $file: + fail "File does not exist: \$filename" + $nomsu = (NomsuCode from (Source $filename 1 (size of $file)) $file) + $tree = ($nomsu parsed) + print tree $tree at indent "" diff --git a/lib/tools/repl.nom b/lib/tools/repl.nom new file mode 100755 index 0000000..2c0d3df --- /dev/null +++ b/lib/tools/repl.nom @@ -0,0 +1,86 @@ +#!/usr/bin/env nomsu -V6.14 +# + This file defines a Read-Evaluate-Print-Loop (REPL) for Nomsu + +use "consolecolor" +use "filesystem" +use "commandline" + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +externally (help) means: + say (" + This is the Nomsu v\(Nomsu version) interactive console. + You can type in Nomsu code here and hit 'enter' twice to run it. + To exit, type 'exit' or 'quit' and hit enter twice. + ") + +command line program with $args: + say (" + + \(bright)\(underscore)Welcome to the Nomsu v\(Nomsu version) interactive console!\ + ..\(reset color) + press 'enter' twice to run a command + + ") + + repeat: + say (bright (yellow ">> ")) inline + $buff = [] + repeat: + say (bright) inline + $line = ($io.read "*L") + say (reset color) inline + if (($line == "\n") or (not $line)): + if ((size of $buff) > 0): + # clear the line + say "\027[1A\027[2K" inline + go to (run buffer) + $buff, add ($line, with "\t" -> " ") + say (dim (yellow ".. ")) inline + + --- (run buffer) --- + + if ((size of $buff) == 0): stop + $buff = ($buff, joined) + spoof file $buff + try: + $tree = ($buff parsed) + ..if it fails with $err: + say $err + do next + + unless $tree: + do next + + for $chunk in $tree: + try: + $lua = ($chunk as lua) + ..if it fails with $err: say $err + + unless $lua: + do next + + # TODO: this is a bit hacky, it just defaults variables to global + so that stuff mostly works across multiple lines. It would be + nicer if local variables actually worked. + $lua, remove free vars + try: + $ret = (run $lua) + ..if it fails with $err: say $err + ..if it succeeds: + if (type of $ret) is: + "nil": + do nothing + + "boolean": + say "= \("yes" if $ret else "no")" + + "table": + if $ret.as_nomsu: + say "= \($ret, as nomsu)" + ..else: + say "= \$ret" + + else: + say "= \$ret" diff --git a/lib/tools/replace.nom b/lib/tools/replace.nom new file mode 100755 index 0000000..fdb8e38 --- /dev/null +++ b/lib/tools/replace.nom @@ -0,0 +1,147 @@ +#!/usr/bin/env nomsu -V6.14 +# + This is a tool to replace syntax trees with something new. + + Usage: + nomsu -t replace [-i] [-f] [-q] [--literal="$v1 $v2..."] file1 file2... + + Example: + nomsu -t replace "($1 and $2) and $3" "all of [$1, $2, $3]" my_file.nom + + If the "-i" flag is used, the file(s) will be edited in-place. + When editing in-place, if the "-f" flag is not used, each change will be + run past the user first. + If the "-q" flag is used and a file fails to parse, the original file + contents will be output. + If no files are passed in, this will read from stdin. + +use "filesystem" +use "consolecolor" +use "commandline" + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +command line program with $args: + if ((#$args.extras) < 2): + fail (" + Usage: nomsu -t replace [--literal="$v1 $v2..."] file1 file2... + ") + $pattern = $args.extras.1 + $replacement = $args.extras.2 + $pattern_tree = ($pattern parsed) + $replacement_tree = ($replacement parsed) + $literal_vars = {} + if $args.literal: + for $var in ($args.literal, all matches of "$([^ ]*)"): + $literal_vars.$var = (yes) + + if (($pattern_tree.type == "Var") and (not $literal_vars.($pattern_tree.1))): + fail "Pattern matches every part of the file." + + $pattern_vars = { + : for $ in recursive $pattern_tree: + if (($.type == "Var") and (not $literal_vars.($.1))): add $.1 + for $child in $: + if ($child is a "Syntax Tree"): + recurse $ on $child + } + + # TODO: support wildcards and unpacking + e.g. nomsu -t replace "test(: $test; *$more_tests)" "*$more_tests; *$test" + ($tree matches $patt with $substitution_values) means: + # TODO: optimize + $substitution_values = {: for $k = $v in $substitution_values: add $k = $v} + when: + (not ($tree is syntax tree)): return (no) + (($patt.type == "Var") and $pattern_vars.($patt.1)): + if $substitution_values.($patt.1): + if ($tree == $substitution_values.($patt.1)): + return $substitution_values + ..else: + return (nil) + ..else: + $substitution_values.($patt.1) = $tree + return $substitution_values + ($tree.type != $patt.type): return (nil) + ($tree.type == "Action"): + if (($tree, get stub) != ($patt, get stub)): return (nil) + + for $ in 1 to (#$patt): + if ($patt.$ is syntax tree): + $new_values = ($tree.$ matches $patt.$ with $substitution_values) + unless $new_values: + return (nil) + + for $k = $v in $new_values: + $substitution_values.$k = $v + ..else: + unless ($tree.$ == $patt.$): return (nil) + + if ((#$tree) != (#$patt)): return (nil) + return $substitution_values + $filenames = ($args.extras, from 3 to -1) + if ((#$filenames) == 0): + say (" + Warning: searching stdin (ctrl-d to abort). To avoid this message, use nomsu -t find - + ") + $filenames = ["stdin"] + + for $filename in $filenames: + $file = (read file $filename) + unless $file: + fail "File does not exist: \$filename" + $code = (NomsuCode from ($Source $filename 1 (size of $file)) $file) + try: + $tree = ($code parsed) + ..if it fails $msg: + if $args.q: + unless $args.i: say $code + ..else: + say $msg + + unless $tree: + do next $filename + + $replaced = {} + $matched = {} + $user_answers = {} + ($tree with replacements) means + $tree, map + for $t: + $values = ($t matches $pattern_tree with {}) + if $values: + $matched.$t = (yes) + for $k = $v in $values: + $values.$k = ($v with replacements) + $ret = ($replacement_tree with vars $values) + if ($args.i and (not $args.f)): + if ($user_answers.$t == (nil)): + if ((#$user_answers) > 0): say "" + $user_answers.$t = "n" + say "\(bright)Should this:" + say (" + \(bright)\(yellow)\("\(($t with replacements) as nomsu)", with "\n" -> "\n ")\ + ..\(reset color) + ") + say "\(bright)..be replaced with:" + + say (" + \(bright)\(blue)\("\($ret as nomsu)", with "\n" -> "\n ")\(reset color) + ") + + $user_answers.$t = (ask "\(bright)..? [Y/n]\(reset color) ") + + if ($user_answers.$t == "n"): return (nil) + $replaced.$t = (yes) + return $ret + $tree2 = ($tree with replacements) + if $args.i: + if ((#$user_answers) > 0): say "" + say (" + \(#$replaced)/\(#$matched) replacement\("" if ((#$replaced) == 1) else "s") in \$filename + ") + + if ((#$replaced) > 0): + write "\($tree2 as nomsu)" to file $filename + ..else: + say ($tree2 as nomsu) diff --git a/lib/tools/test.nom b/lib/tools/test.nom new file mode 100755 index 0000000..4663bd4 --- /dev/null +++ b/lib/tools/test.nom @@ -0,0 +1,65 @@ +#!/usr/bin/env nomsu -V6.14 +# + Tool to run all tests in a file (i.e. the code block inside a call to 'test $'). Usage: + nomsu tools/test.nom file1 file2 directory1 ... + +use "filesystem" +use "consolecolor" +use "commandline" + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +command line program with $args: + for $filename in $args.extras at $i: + $file = (read file $filename) + unless $file: + fail "Couldn't find \$filename" + + $(test environment) = (new environment) + $(test environment), export $filename + $version = + $file, matching (" + #![^ + ]* nomsu %-V[ ]*([^ + ]*) + ") + $file_tests = [] + for $src = $test in $(test environment).TESTS: + if $version: + $test = (" + #!/usr/bin/env nomsu -V\$version + \$test + ") + $file_tests, add {.test = $test, .source = $src} + + unless ($file_tests is empty): + sort $file_tests by $ -> $.source + say "[ .. ] \$filename" inline + $io.flush() + + if $args.v: say "" + + $failures = [] + for $ in $file_tests: + if $args.v: + say " \(yellow ($.test, with "\n" -> "\n "))" + try: + $(test environment), run $.test + ..if it fails with $msg: + $src = ($Source, from string $.source) + $l1 = ($file, line number at $src.start) + $l2 = ($file, line number at $src.stop) + $failures, add "\(yellow "\($src.filename):\($l1)-\$l2:")\n\(bright (red ($msg, indented)))" + + if ($failures is empty): + if $args.v: + say (green "PASS") + ..else: + say "\r[\(green "PASS")" + ..else: + if $args.v: + say (red (bright "FAIL")) + ..else: + say "\r[\(red (bright "FAIL"))" + say "\($failures, joined with "\n", indented)" + diff --git a/lib/tools/upgrade.nom b/lib/tools/upgrade.nom new file mode 100755 index 0000000..1ef91b9 --- /dev/null +++ b/lib/tools/upgrade.nom @@ -0,0 +1,43 @@ +#!/usr/bin/env nomsu -V6.14 +# + Tool to automatically update code from old versions of Nomsu. Usage: + nomsu tools/upgrade.nom [-i] file1 file2 directory1 ... + If "-i" is the first argument, upgrades will be performed in-place. Otherwise, the + upgraded code will be printed. + +use "compatibility" +use "filesystem" +use "consolecolor" +use "commandline" + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +command line program with $args: + $inplace = ($args.i or $args.inplace) + $start_version = $args."upgrade-from" + $version = ($args."upgrade-to" or (Nomsu version)) + $test = ($args.t or $args.test) + for $filename in $args.extras: + $file = (read file $filename) + unless $file: + fail "File does not exist: \$filename" + $leading_indent = ($file, matching "\n*([ ]*)") + $code = (NomsuCode from (Source $filename 1 (size of $file)) $file) + $tree = ($code parsed $start_version) + $uptree = + $tree upgraded from ($start_version or ($tree.version or (Nomsu version))) to + $version + $text = "\$leading_indent\($uptree as nomsu, text, with "\n" -> "\n\$leading_indent")" + when: + $inplace: + say "Upgraded \$filename" + write $text to file $filename + + $test: + if ($uptree == $tree): + say (dim "\$filename will not be changed") + ..else: + say (bright "\$filename will be changed") + + else: + say $text inline diff --git a/nomsu.lua b/nomsu.lua index 88bf986..6f523c8 100644 --- a/nomsu.lua +++ b/nomsu.lua @@ -48,16 +48,16 @@ end local sep = "\3" local parser = re.compile([[ args <- {| (flag %sep)* {:files: {| - ( ("-m" %sep)? (!("--" %sep) file)* ("--" %sep) - / file + ( ("-m" %sep)? (file %sep)+ "--" %sep + / file %sep / {~ '' %sep? -> 'nomsu://tools/repl.nom' ~}) |} :} {:nomsu_args: {| (nomsu_flag %sep)* {:extras: {| ({[^%sep]+} %sep)* |} :} |} :} - |} !. + |} ({.+}?) file <- - ( "-e" %sep ({[^%sep]+} -> spoof) %sep - / "-t" %sep {~ {[^%sep]+} -> "nomsu://tools/%1.nom" ~} %sep - / {[^%sep]+} %sep) + ( "-e" %sep ({[^%sep]+} -> spoof) + / "-t" %sep {~ {[^%sep]+} -> "nomsu://tools/%1.nom" ~} + / !"--" {[^%sep]+}) flag <- longflag / shortflag / "-" shortboolflag+ longflag <- @@ -84,8 +84,11 @@ local parser = re.compile([[ args <- {| (flag %sep)* spoof = Files.spoof }) local arg_string = table.concat(arg, sep) .. sep -local args = parser:match(arg_string) -if not args or args.help then +local args, err = parser:match(arg_string) +if not args or err or args.help then + if err then + print("Didn't understand: \x1b[31;1m" .. tostring(err) .. "\x1b[0m") + end print(usage) os.exit(EXIT_FAILURE) end @@ -98,28 +101,35 @@ end nomsu_args.extras = List(args.nomsu_args.extras or { }) local optimization = tonumber(args.optimization or 1) local nomsupath = { } -if NOMSU_VERSION and NOMSU_PREFIX then - if optimization > 0 then - table.insert(nomsupath, tostring(NOMSU_PREFIX) .. "/share/nomsu/" .. tostring(NOMSU_VERSION) .. "/?.lua") - table.insert(nomsupath, tostring(NOMSU_PREFIX) .. "/share/nomsu/" .. tostring(NOMSU_VERSION) .. "/?/init.lua") - end - table.insert(nomsupath, tostring(NOMSU_PREFIX) .. "/share/nomsu/" .. tostring(NOMSU_VERSION) .. "/?.nom") - table.insert(nomsupath, tostring(NOMSU_PREFIX) .. "/share/nomsu/" .. tostring(NOMSU_VERSION) .. "/?/init.nom") -end -if NOMSU_PACKAGEPATH then - if optimization > 0 then - table.insert(nomsupath, tostring(NOMSU_PACKAGEPATH) .. "/nomsu/?.lua") - table.insert(nomsupath, tostring(NOMSU_PACKAGEPATH) .. "/nomsu/?/init.lua") - end - table.insert(nomsupath, tostring(NOMSU_PACKAGEPATH) .. "/nomsu/?.nom") - table.insert(nomsupath, tostring(NOMSU_PACKAGEPATH) .. "/nomsu/?/init.nom") -end +local suffixes if optimization > 0 then - table.insert(nomsupath, "./?.lua") - table.insert(nomsupath, "./?/init.lua") + suffixes = { + "?.lua", + "?/init.lua", + "?.nom", + "?/init.nom" + } +else + suffixes = { + "?.nom", + "?/init.nom" + } end -table.insert(nomsupath, "./?.nom") -table.insert(nomsupath, "./?/init.nom") +local add_path +add_path = function(p) + for _index_0 = 1, #suffixes do + local s = suffixes[_index_0] + table.insert(nomsupath, p .. "/" .. s) + end +end +if NOMSU_VERSION and NOMSU_PREFIX then + add_path(tostring(NOMSU_PREFIX) .. "/share/nomsu/" .. tostring(NOMSU_VERSION) .. "/lib") +else + add_path("./lib") +end +local NOMSU_PACKAGEPATH = NOMSU_PACKAGEPATH or "/opt" +add_path(NOMSU_PACKAGEPATH) +add_path(".") package.nomsupath = table.concat(nomsupath, ";") local nomsu_environment = require('nomsu_environment') nomsu_environment.COMMAND_LINE_ARGS = nomsu_args @@ -143,7 +153,8 @@ run = function() do local nomsu_name = f:match("^nomsu://(.*)%.nom") if nomsu_name then - local path, err = package.searchpath(nomsu_name, package.nomsupath, "/") + local path + path, err = package.searchpath(nomsu_name, package.nomsupath, "/") if not path then error(err) end @@ -171,20 +182,22 @@ run = function() local code = Files.read(filename) local source = Source(filename, 1, #code) code = NomsuCode:from(source, code) - local tree = nomsu_environment._1_parsed(code) + local env = nomsu_environment.new_environment() + env.MODULE_NAME = filename + local tree = env._1_parsed(code) if not (tree.type == 'FileChunks') then tree = { tree } end for chunk_no, chunk in ipairs(tree) do - local lua = nomsu_environment:compile(chunk) + local lua = env:compile(chunk) lua:declare_locals() lua:prepend((chunk_no > 1) and '\n' or '', "-- File " .. tostring(filename) .. " chunk #" .. tostring(chunk_no) .. "\n") if args.verbose then print(lua:text()) end - nomsu_environment:run(chunk) + env:run(chunk) output:write(lua:text(), "\n") end print(("Compiled %-25s -> %s"):format(filename, filename:gsub("%.nom$", ".lua"))) @@ -193,25 +206,31 @@ run = function() local code = Files.read(filename) local source = Source(filename, 1, #code) code = NomsuCode:from(source, code) - local tree = nomsu_environment._1_parsed(code) + local env = nomsu_environment.new_environment() + env.MODULE_NAME = filename + env.WAS_RUN_DIRECTLY = true + local tree = env._1_parsed(code) if not (tree.type == 'FileChunks') then tree = { tree } end for chunk_no, chunk in ipairs(tree) do - local lua = nomsu_environment:compile(chunk) + local lua = env:compile(chunk) lua:declare_locals() lua:prepend((chunk_no > 1) and '\n' or '', "-- File " .. tostring(filename) .. " chunk #" .. tostring(chunk_no) .. "\n") print(lua:text()) - nomsu_environment:run(lua) + env:run(lua) end else local f = Files.read(filename) if filename:match("%.lua$") then f = LuaCode:from(Source(filename, 1, #f), f) end - nomsu_environment:run(f) + local env = nomsu_environment.new_environment() + env.MODULE_NAME = filename + env.WAS_RUN_DIRECTLY = true + env:run(f) end end end diff --git a/nomsu.moon b/nomsu.moon index b837170..472bed5 100755 --- a/nomsu.moon +++ b/nomsu.moon @@ -48,16 +48,16 @@ sep = "\3" parser = re.compile([[ args <- {| (flag %sep)* {:files: {| - ( ("-m" %sep)? (!("--" %sep) file)* ("--" %sep) - / file + ( ("-m" %sep)? (file %sep)+ "--" %sep + / file %sep / {~ '' %sep? -> 'nomsu://tools/repl.nom' ~}) |} :} {:nomsu_args: {| (nomsu_flag %sep)* {:extras: {| ({[^%sep]+} %sep)* |} :} |} :} - |} !. + |} ({.+}?) file <- - ( "-e" %sep ({[^%sep]+} -> spoof) %sep - / "-t" %sep {~ {[^%sep]+} -> "nomsu://tools/%1.nom" ~} %sep - / {[^%sep]+} %sep) + ( "-e" %sep ({[^%sep]+} -> spoof) + / "-t" %sep {~ {[^%sep]+} -> "nomsu://tools/%1.nom" ~} + / !"--" {[^%sep]+}) flag <- longflag / shortflag / "-" shortboolflag+ longflag <- @@ -82,8 +82,10 @@ parser = re.compile([[ spoof: Files.spoof }) arg_string = table.concat(arg, sep)..sep -args = parser\match(arg_string) -if not args or args.help +args, err = parser\match(arg_string) +if not args or err or args.help + if err + print("Didn't understand: \x1b[31;1m#{err}\x1b[0m") print usage os.exit(EXIT_FAILURE) nomsu_args = Dict{} @@ -93,25 +95,18 @@ nomsu_args.extras = List(args.nomsu_args.extras or {}) optimization = tonumber(args.optimization or 1) nomsupath = {} +suffixes = if optimization > 0 + {"?.lua", "?/init.lua", "?.nom", "?/init.nom"} +else {"?.nom", "?/init.nom"} +add_path = (p)-> + for s in *suffixes do table.insert(nomsupath, p.."/"..s) if NOMSU_VERSION and NOMSU_PREFIX - if optimization > 0 - table.insert nomsupath, "#{NOMSU_PREFIX}/share/nomsu/#{NOMSU_VERSION}/?.lua" - table.insert nomsupath, "#{NOMSU_PREFIX}/share/nomsu/#{NOMSU_VERSION}/?/init.lua" - table.insert nomsupath, "#{NOMSU_PREFIX}/share/nomsu/#{NOMSU_VERSION}/?.nom" - table.insert nomsupath, "#{NOMSU_PREFIX}/share/nomsu/#{NOMSU_VERSION}/?/init.nom" - -if NOMSU_PACKAGEPATH - if optimization > 0 - table.insert nomsupath, "#{NOMSU_PACKAGEPATH}/nomsu/?.lua" - table.insert nomsupath, "#{NOMSU_PACKAGEPATH}/nomsu/?/init.lua" - table.insert nomsupath, "#{NOMSU_PACKAGEPATH}/nomsu/?.nom" - table.insert nomsupath, "#{NOMSU_PACKAGEPATH}/nomsu/?/init.nom" - -if optimization > 0 - table.insert nomsupath, "./?.lua" - table.insert nomsupath, "./?/init.lua" -table.insert nomsupath, "./?.nom" -table.insert nomsupath, "./?/init.nom" + add_path "#{NOMSU_PREFIX}/share/nomsu/#{NOMSU_VERSION}/lib" +else + add_path "./lib" +NOMSU_PACKAGEPATH or= "/opt" +add_path NOMSU_PACKAGEPATH +add_path "." package.nomsupath = table.concat(nomsupath, ";") nomsu_environment = require('nomsu_environment') @@ -152,14 +147,16 @@ run = -> code = Files.read(filename) source = Source(filename, 1, #code) code = NomsuCode\from(source, code) - tree = nomsu_environment._1_parsed(code) + env = nomsu_environment.new_environment! + env.MODULE_NAME = filename + tree = env._1_parsed(code) tree = {tree} unless tree.type == 'FileChunks' for chunk_no, chunk in ipairs tree - lua = nomsu_environment\compile(chunk) + lua = env\compile(chunk) lua\declare_locals! lua\prepend((chunk_no > 1) and '\n' or '', "-- File #{filename} chunk ##{chunk_no}\n") if args.verbose then print(lua\text!) - nomsu_environment\run(chunk) + env\run(chunk) output\write(lua\text!, "\n") print ("Compiled %-25s -> %s")\format(filename, filename\gsub("%.nom$", ".lua")) output\close! @@ -167,20 +164,26 @@ run = -> code = Files.read(filename) source = Source(filename, 1, #code) code = NomsuCode\from(source, code) - tree = nomsu_environment._1_parsed(code) + env = nomsu_environment.new_environment! + env.MODULE_NAME = filename + env.WAS_RUN_DIRECTLY = true + tree = env._1_parsed(code) tree = {tree} unless tree.type == 'FileChunks' for chunk_no, chunk in ipairs tree - lua = nomsu_environment\compile(chunk) + lua = env\compile(chunk) lua\declare_locals! lua\prepend((chunk_no > 1) and '\n' or '', "-- File #{filename} chunk ##{chunk_no}\n") print(lua\text!) - nomsu_environment\run(lua) + env\run(lua) else -- Just run the file f = Files.read(filename) if filename\match("%.lua$") f = LuaCode\from(Source(filename, 1, #f), f) - nomsu_environment\run(f) + env = nomsu_environment.new_environment! + env.MODULE_NAME = filename + env.WAS_RUN_DIRECTLY = true + env\run(f) --nomsu_environment.run_file_1_in(filename, nomsu_environment, 0) debugger = if args.debugger == "nil" then {} diff --git a/nomsu_compiler.lua b/nomsu_compiler.lua index 9c8c9ac..fee94f3 100644 --- a/nomsu_compiler.lua +++ b/nomsu_compiler.lua @@ -97,6 +97,8 @@ compile = function(self, tree) if ret ~= tree then return self:compile(ret) end + elseif tree.stub == "1 if 2 else" then + require('ldt').breakpoint() end local lua = LuaCode:from(tree.source) lua:add((stub):as_lua_id(), "(") diff --git a/nomsu_compiler.moon b/nomsu_compiler.moon index f3530e9..fd7403f 100644 --- a/nomsu_compiler.moon +++ b/nomsu_compiler.moon @@ -68,6 +68,8 @@ compile = (tree)=> return ret if ret != tree return @compile(ret) + elseif tree.stub == "1 if 2 else" + require('ldt').breakpoint! lua = LuaCode\from(tree.source) lua\add((stub)\as_lua_id!,"(") diff --git a/nomsu_environment.lua b/nomsu_environment.lua index 9181d92..94e68e0 100644 --- a/nomsu_environment.lua +++ b/nomsu_environment.lua @@ -64,9 +64,20 @@ do compile, compile_error = _obj_0.compile, _obj_0.compile_error end local _currently_running_files = List({ }) -local nomsu_environment local _module_imports = { } -nomsu_environment = setmetatable({ +local _importer_mt = { + __index = function(self, k) + return _module_imports[self][k] + end +} +local Importer +Importer = function(t, imports) + _module_imports[t] = imports or { } + t._IMPORTS = _module_imports[t] + return setmetatable(t, _importer_mt) +end +local nomsu_environment +nomsu_environment = Importer({ NOMSU_COMPILER_VERSION = 13, NOMSU_SYNTAX_VERSION = max_parser_version, next = next, @@ -230,7 +241,6 @@ nomsu_environment = setmetatable({ end local mod = self:new_environment() mod.MODULE_NAME = package_name - mod.TESTS = Dict({ }) local code = Files.read(path) if path:match("%.lua$") then code = LuaCode:from(Source(path, 1, #code), code) @@ -250,19 +260,41 @@ nomsu_environment = setmetatable({ for k, v in pairs(mod) do imports[k] = v end + local cr_imports = assert(_module_imports[self.COMPILE_RULES]) + for k, v in pairs(mod.COMPILE_RULES) do + cr_imports[k] = v + end return mod end, export = function(self, package_name) local mod = self:load_module(package_name) local imports = assert(_module_imports[self]) for k, v in pairs(_module_imports[mod]) do - imports[k] = v + if rawget(imports, k) == nil then + imports[k] = v + end end for k, v in pairs(mod) do - if k ~= "_G" and k ~= "_ENV" then + if rawget(self, k) == nil then self[k] = v end end + local cr_imports = assert(_module_imports[self.COMPILE_RULES]) + for k, v in pairs(_module_imports[mod.COMPILE_RULES]) do + if rawget(cr_imports, k) == nil then + cr_imports[k] = v + end + end + for k, v in pairs(mod.COMPILE_RULES) do + if rawget(self.COMPILE_RULES, k) == nil then + self.COMPILE_RULES[k] = v + end + end + for k, v in pairs(mod.TESTS) do + if rawget(self.TESTS, k) == nil then + self.TESTS[k] = v + end + end return mod end, run = function(self, to_run) @@ -303,7 +335,7 @@ nomsu_environment = setmetatable({ do local _accum_0 = { } local _len_0 = 1 - for i, line in ipairs(Files.get_lines(lua_string)) do + for i, line in ipairs(lua_string:lines()) do _accum_0[_len_0] = ("%3d|%s"):format(i, line) _len_0 = _len_0 + 1 end @@ -351,27 +383,29 @@ nomsu_environment = setmetatable({ end end, new_environment = function() - local env = { } - do + local env = Importer({ }, (function() local _tbl_0 = { } for k, v in pairs(nomsu_environment) do _tbl_0[k] = v end - _module_imports[env] = _tbl_0 - end + return _tbl_0 + end)()) env._ENV = env env._G = env - setmetatable(env, getmetatable(nomsu_environment)) + env.TESTS = Dict({ }) + env.COMPILE_RULES = Importer({ }, (function() + local _tbl_0 = { } + for k, v in pairs(nomsu_environment.COMPILE_RULES) do + _tbl_0[k] = v + end + return _tbl_0 + end)()) return env end -}, { - __index = function(self, k) - return _module_imports[self][k] - end }) nomsu_environment._ENV = nomsu_environment nomsu_environment._G = nomsu_environment -nomsu_environment.COMPILE_RULES = require('bootstrap') -_module_imports[nomsu_environment] = { } +nomsu_environment.COMPILE_RULES = Importer(require('bootstrap')) +nomsu_environment.MODULE_NAME = "nomsu" SOURCE_MAP = nomsu_environment.SOURCE_MAP return nomsu_environment diff --git a/nomsu_environment.moon b/nomsu_environment.moon index f82bb4e..6049555 100644 --- a/nomsu_environment.moon +++ b/nomsu_environment.moon @@ -34,9 +34,15 @@ for version=1,999 {:tree_to_nomsu, :tree_to_inline_nomsu} = require "nomsu_decompiler" {:compile, :compile_error} = require('nomsu_compiler') _currently_running_files = List{} -- Used to check for circular imports in run_file_1_in -local nomsu_environment _module_imports = {} -nomsu_environment = setmetatable({ +_importer_mt = {__index: (k)=> _module_imports[@][k]} +Importer = (t, imports)-> + _module_imports[t] = imports or {} + t._IMPORTS = _module_imports[t] + return setmetatable(t, _importer_mt) + +local nomsu_environment +nomsu_environment = Importer{ NOMSU_COMPILER_VERSION: 13, NOMSU_SYNTAX_VERSION: max_parser_version -- Lua stuff: :next, unpack: unpack or table.unpack, :setmetatable, :rawequal, :getmetatable, :pcall, @@ -118,7 +124,6 @@ nomsu_environment = setmetatable({ error("Circular import detected:\n "..circle\joined_with("\n..imports ")) mod = @new_environment! mod.MODULE_NAME = package_name - mod.TESTS = Dict{} code = Files.read(path) if path\match("%.lua$") code = LuaCode\from(Source(path, 1, #code), code) @@ -136,16 +141,31 @@ nomsu_environment = setmetatable({ imports = assert _module_imports[@] for k,v in pairs(mod) imports[k] = v + cr_imports = assert _module_imports[@COMPILE_RULES] + for k,v in pairs(mod.COMPILE_RULES) + cr_imports[k] = v return mod export: (package_name)=> mod = @load_module(package_name) imports = assert _module_imports[@] for k,v in pairs(_module_imports[mod]) - imports[k] = v + if rawget(imports, k) == nil + imports[k] = v for k,v in pairs(mod) - if k != "_G" and k != "_ENV" + if rawget(@, k) == nil + --if k != "_G" and k != "_ENV" and k != "COMPILE_RULES" and k != "MODULE_NAME" @[k] = v + cr_imports = assert _module_imports[@COMPILE_RULES] + for k,v in pairs(_module_imports[mod.COMPILE_RULES]) + if rawget(cr_imports, k) == nil + cr_imports[k] = v + for k,v in pairs(mod.COMPILE_RULES) + if rawget(@COMPILE_RULES, k) == nil + @COMPILE_RULES[k] = v + for k,v in pairs(mod.TESTS) + if rawget(@TESTS, k) == nil + @TESTS[k] = v return mod run: (to_run)=> @@ -179,7 +199,7 @@ nomsu_environment = setmetatable({ -- If you replace tostring(source) with "nil", source mapping won't happen run_lua_fn, err = load(lua_string, tostring(source), "t", @) if not run_lua_fn - lines =[("%3d|%s")\format(i,line) for i, line in ipairs Files.get_lines(lua_string)] + lines =[("%3d|%s")\format(i,line) for i, line in ipairs lua_string\lines!] line_numbered_lua = table.concat(lines, "\n") error("Failed to compile generated code:\n\027[1;34m#{line_numbered_lua}\027[0m\n\n#{err}", 0) source_key = tostring(source) @@ -210,19 +230,18 @@ nomsu_environment = setmetatable({ error("Attempt to run unknown thing: "..tostring(to_run)) new_environment: -> - env = {} - _module_imports[env] = {k,v for k,v in pairs(nomsu_environment)} + env = Importer({}, {k,v for k,v in pairs(nomsu_environment)}) env._ENV = env env._G = env - setmetatable(env, getmetatable(nomsu_environment)) + env.TESTS = Dict{} + env.COMPILE_RULES = Importer({}, {k,v for k,v in pairs(nomsu_environment.COMPILE_RULES)}) return env -}, { - __index: (k)=> _module_imports[@][k] -}) +} + nomsu_environment._ENV = nomsu_environment nomsu_environment._G = nomsu_environment -nomsu_environment.COMPILE_RULES = require('bootstrap') -_module_imports[nomsu_environment] = {} +nomsu_environment.COMPILE_RULES = Importer(require('bootstrap')) +nomsu_environment.MODULE_NAME = "nomsu" -- Hacky use of globals: export SOURCE_MAP diff --git a/string2.lua b/string2.lua index 09cb5ea..429ab7f 100644 --- a/string2.lua +++ b/string2.lua @@ -130,6 +130,12 @@ local string2 = { end return table.concat(lines, "\n") end, + indented = function(self, indent) + if indent == nil then + indent = " " + end + return indent .. (gsub(self, "\n", "\n" .. indent)) + end, as_lua = function(self) local escaped = gsub(self, "\\", "\\\\") escaped = gsub(escaped, "\n", "\\n") diff --git a/string2.moon b/string2.moon index e6db628..de2980d 100644 --- a/string2.moon +++ b/string2.moon @@ -51,6 +51,9 @@ string2 = { lines[#lines+1] = line return table.concat(lines, "\n") + indented: (indent=" ")=> + indent..(gsub(@, "\n", "\n"..indent)) + as_lua: => escaped = gsub(@, "\\", "\\\\") escaped = gsub(escaped, "\n", "\\n") diff --git a/tools/find.nom b/tools/find.nom deleted file mode 100755 index f9a63b9..0000000 --- a/tools/find.nom +++ /dev/null @@ -1,93 +0,0 @@ -#!/usr/bin/env nomsu -V6.14 -# - This is a tool to find syntax trees matching a pattern. "*" is a wildcard - that will match any subtree, and "**" is a wildcard that will match any - 0 or more subtrees. "**" is greedy, so extra arguments after it will - not match. - - nomsu -t find [flags] "* squared" file1 file2... - - Flags: - -l List only the names of the files with matches - --wildcard= Specify a custom wildcard (in case you need to - match an action with a "*" in the name) - - Output: - :: - - -use "lib/os" -use "lib/consolecolor" - -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -$wildcard = ($(COMMAND LINE ARGS).wildcard or "%*") -$pattern = $(COMMAND LINE ARGS).extras.1 -if (any of [not $pattern, $pattern == "*", $pattern == "**"]): - fail (" - Usage: nomsu -t find [-l] [--wildcard=] , where is valid Nomsu code - ") -$pattern = ($pattern, with "\$wildcard\$wildcard" -> "$multi_wildcard") -$pattern = ($pattern, with $wildcard -> "$wildcard") -$pattern_tree = ($pattern parsed) -($tree matches $patt) means: - when: - (not ($tree is syntax tree)): return (no) - (($patt.type == "Var") and ($patt.1 == "wildcard")): return (yes) - ($tree.type != $patt.type): return (no) - ($tree.type == "Action"): - if (($tree, get stub) != ($patt, get stub)): return (no) - - for $ in 1 to (#$patt): - if ($patt.$ is syntax tree): - if ($patt.$ == \$multi_wildcard): return (yes) - unless ($tree.$ matches $patt.$): return (no) - ..else: - unless ($tree.$ == $patt.$): return (no) - - if ((#$tree) != (#$patt)): return (no) - return (yes) -$filenames = ($(COMMAND LINE ARGS).extras, from 2 to -1) -if ((#$filenames) == 0): - say (" - Warning: searching stdin (ctrl-d to abort). To avoid this message, use nomsu -t find - - ") - $filenames = ["stdin"] - -for $filename in $filenames: - $file = (read file $filename) - unless $file: - fail "File does not exist: \$filename" - $code = (NomsuCode from ($Source $filename 1 (size of $file)) $file) - try: - $tree = ($code parsed) - ..if it fails $msg: - say - red (" - \$filename failed to parse: - \$msg - ") - $tree = (nil) - - unless $tree: - do next $filename - - $results = [] - for $t in recursive $tree: - if ($t matches $pattern_tree): - $line_num = ($file, line number at $t.source.start) - $results, add { - .line = $line_num, .text = "\(blue "\$filename:\$line_num:")\n\(source lines of $t)" - } - - for $sub in $t: - if ($sub is syntax tree): - recurse $t on $sub - - if $(COMMAND LINE ARGS).l: - if ((#$results) > 0): - say $filename - ..else: - sort $results by $ -> $.line - for $ in $results: - say $.text diff --git a/tools/format.nom b/tools/format.nom deleted file mode 100755 index 07f7980..0000000 --- a/tools/format.nom +++ /dev/null @@ -1,43 +0,0 @@ -#!/usr/bin/env nomsu -V6.14 -# - Auto-format Nomsu code. Usage: - nomsu -t format [-i] file1 file2... - - If the "-i" flag is used, the file will be edited in-place. - If the "-q" flag is used and an error occurs, the original file will be printed. - If no files are passed in, this will read from stdin. - -use "lib/os" - -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -$filenames = $(COMMAND LINE ARGS).extras -if ((#$filenames) == 0): - say (" - Warning: reading from stdin (ctrl-d to abort). To avoid this message, use nomsu -t format - - ") - $filenames = ["stdin"] - -for $filename in $filenames: - $file = (read file $filename) - unless $file: - fail "File does not exist: \$filename" - $leading_indent = ($file, matching "\n*([ ]*)") - $code = (NomsuCode from ($Source $filename 1 (size of $file)) $file) - try: - $tree = ($code parsed) - ..if it fails $msg: - if $(COMMAND LINE ARGS).q: - $formatted = $file - ..else: - say $msg - - if ($tree and (not $formatted)): - $formatted = - "\$leading_indent\($tree as nomsu, text, with "\n" -> "\n\$leading_indent")" - - if $formatted: - if $(COMMAND LINE ARGS).i: - write $formatted to file $filename - ..else: - say $formatted inline diff --git a/tools/repl.nom b/tools/repl.nom deleted file mode 100755 index 175d068..0000000 --- a/tools/repl.nom +++ /dev/null @@ -1,84 +0,0 @@ -#!/usr/bin/env nomsu -V6.14 -# - This file defines a Read-Evaluate-Print-Loop (REPL) for Nomsu - -use "lib/consolecolor" -use "lib/os" - -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -externally (help) means: - say (" - This is the Nomsu v\(Nomsu version) interactive console. - You can type in Nomsu code here and hit 'enter' twice to run it. - To exit, type 'exit' or 'quit' and hit enter twice. - ") - -say (" - - \(bright)\(underscore)Welcome to the Nomsu v\(Nomsu version) interactive console!\ - ..\(reset color) - press 'enter' twice to run a command - -") - -repeat: - say (bright (yellow ">> ")) inline - $buff = [] - repeat: - say (bright) inline - $line = ($io.read "*L") - say (reset color) inline - if (($line == "\n") or (not $line)): - if ((size of $buff) > 0): - # clear the line - say "\027[1A\027[2K" inline - go to (run buffer) - $buff, add ($line, with "\t" -> " ") - say (dim (yellow ".. ")) inline - - --- (run buffer) --- - - if ((size of $buff) == 0): stop - $buff = ($buff, joined) - spoof file $buff - try: - $tree = ($buff parsed) - ..if it fails with $err: - say $err - do next - - unless $tree: - do next - - for $chunk in $tree: - try: - $lua = ($chunk as lua) - ..if it fails with $err: say $err - - unless $lua: - do next - - # TODO: this is a bit hacky, it just defaults variables to global - so that stuff mostly works across multiple lines. It would be - nicer if local variables actually worked. - $lua, remove free vars - try: - $ret = (run $lua) - ..if it fails with $err: say $err - ..if it succeeds: - if (type of $ret) is: - "nil": - do nothing - - "boolean": - say "= \("yes" if $ret else "no")" - - "table": - if $ret.as_nomsu: - say "= \($ret, as nomsu)" - ..else: - say "= \$ret" - - else: - say "= \$ret" diff --git a/tools/replace.nom b/tools/replace.nom deleted file mode 100755 index f4df0e4..0000000 --- a/tools/replace.nom +++ /dev/null @@ -1,145 +0,0 @@ -#!/usr/bin/env nomsu -V6.14 -# - This is a tool to replace syntax trees with something new. - - Usage: - nomsu -t replace [-i] [-f] [-q] [--literal="$v1 $v2..."] file1 file2... - - Example: - nomsu -t replace "($1 and $2) and $3" "all of [$1, $2, $3]" my_file.nom - - If the "-i" flag is used, the file(s) will be edited in-place. - When editing in-place, if the "-f" flag is not used, each change will be - run past the user first. - If the "-q" flag is used and a file fails to parse, the original file - contents will be output. - If no files are passed in, this will read from stdin. - -use "lib/os" -use "lib/consolecolor" - -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -if ((#$(COMMAND LINE ARGS).extras) < 2): - fail (" - Usage: nomsu -t replace [--literal="$v1 $v2..."] file1 file2... - ") -$pattern = $(COMMAND LINE ARGS).extras.1 -$replacement = $(COMMAND LINE ARGS).extras.2 -$pattern_tree = ($pattern parsed) -$replacement_tree = ($replacement parsed) -$literal_vars = {} -if $(COMMAND LINE ARGS).literal: - for $var in ($(COMMAND LINE ARGS).literal, all matches of "$([^ ]*)"): - $literal_vars.$var = (yes) - -if (($pattern_tree.type == "Var") and (not $literal_vars.($pattern_tree.1))): - fail "Pattern matches every part of the file." - -$pattern_vars = { - : for $ in recursive $pattern_tree: - if (($.type == "Var") and (not $literal_vars.($.1))): add $.1 - for $child in $: - if ($child is a "Syntax Tree"): - recurse $ on $child -} - -# TODO: support wildcards and unpacking - e.g. nomsu -t replace "test(: $test; *$more_tests)" "*$more_tests; *$test" -($tree matches $patt with $substitution_values) means: - # TODO: optimize - $substitution_values = {: for $k = $v in $substitution_values: add $k = $v} - when: - (not ($tree is syntax tree)): return (no) - (($patt.type == "Var") and $pattern_vars.($patt.1)): - if $substitution_values.($patt.1): - if ($tree == $substitution_values.($patt.1)): - return $substitution_values - ..else: - return (nil) - ..else: - $substitution_values.($patt.1) = $tree - return $substitution_values - ($tree.type != $patt.type): return (nil) - ($tree.type == "Action"): - if (($tree, get stub) != ($patt, get stub)): return (nil) - - for $ in 1 to (#$patt): - if ($patt.$ is syntax tree): - $new_values = ($tree.$ matches $patt.$ with $substitution_values) - unless $new_values: - return (nil) - - for $k = $v in $new_values: - $substitution_values.$k = $v - ..else: - unless ($tree.$ == $patt.$): return (nil) - - if ((#$tree) != (#$patt)): return (nil) - return $substitution_values -$filenames = ($(COMMAND LINE ARGS).extras, from 3 to -1) -if ((#$filenames) == 0): - say (" - Warning: searching stdin (ctrl-d to abort). To avoid this message, use nomsu -t find - - ") - $filenames = ["stdin"] - -for $filename in $filenames: - $file = (read file $filename) - unless $file: - fail "File does not exist: \$filename" - $code = (NomsuCode from ($Source $filename 1 (size of $file)) $file) - try: - $tree = ($code parsed) - ..if it fails $msg: - if $(COMMAND LINE ARGS).q: - unless $(COMMAND LINE ARGS).i: say $code - ..else: - say $msg - - unless $tree: - do next $filename - - $replaced = {} - $matched = {} - $user_answers = {} - ($tree with replacements) means - $tree, map - for $t: - $values = ($t matches $pattern_tree with {}) - if $values: - $matched.$t = (yes) - for $k = $v in $values: - $values.$k = ($v with replacements) - $ret = ($replacement_tree with vars $values) - if ($(COMMAND LINE ARGS).i and (not $(COMMAND LINE ARGS).f)): - if ($user_answers.$t == (nil)): - if ((#$user_answers) > 0): say "" - $user_answers.$t = "n" - say "\(bright)Should this:" - say (" - \(bright)\(yellow)\("\(($t with replacements) as nomsu)", with "\n" -> "\n ")\ - ..\(reset color) - ") - say "\(bright)..be replaced with:" - - say (" - \(bright)\(blue)\("\($ret as nomsu)", with "\n" -> "\n ")\(reset color) - ") - - $user_answers.$t = (ask "\(bright)..? [Y/n]\(reset color) ") - - if ($user_answers.$t == "n"): return (nil) - $replaced.$t = (yes) - return $ret - $tree2 = ($tree with replacements) - if $(COMMAND LINE ARGS).i: - if ((#$user_answers) > 0): say "" - say (" - \(#$replaced)/\(#$matched) replacement\("" if ((#$replaced) == 1) else "s") in \$filename - ") - - if ((#$replaced) > 0): - write "\($tree2 as nomsu)" to file $filename - ..else: - say ($tree2 as nomsu) diff --git a/tools/test.nom b/tools/test.nom deleted file mode 100755 index 44d783b..0000000 --- a/tools/test.nom +++ /dev/null @@ -1,44 +0,0 @@ -#!/usr/bin/env nomsu -V6.14 -# - Tool to run all tests in a file (i.e. the code block inside a call to 'test $'). Usage: - nomsu tools/test.nom file1 file2 directory1 ... - -use "lib/os" -use "lib/consolecolor" - -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -for $filename in $(COMMAND LINE ARGS).extras: - $(test environment) = (new environment) - $(test environment), use $filename - $file = (read file $filename) - $version = - $file, matching (" - #![^ - ]* nomsu %-V[ ]*([^ - ]*) - ") - $file_tests = [] - for $src = $test in $(test environment).TESTS: - if $version: - $test = (" - #!/usr/bin/env nomsu -V\$version - \$test - ") - $file_tests, add {.test = $test, .source = $src} - - unless ($file_tests is empty): - sort $file_tests by $ -> $.source - lua> "io.write('[ .. ] ', \$filename); io.flush()" - - if (command line args).v: say "" - - for $ in $file_tests: - if (command line args).v: - say " \(yellow ($.test, with "\n" -> "\n "))" - $(test environment), run $.test - - if (command line args).v: - say (green "PASS") - ..else: - say "\r[\(green "PASS")" diff --git a/tools/upgrade.nom b/tools/upgrade.nom deleted file mode 100755 index b9b8255..0000000 --- a/tools/upgrade.nom +++ /dev/null @@ -1,41 +0,0 @@ -#!/usr/bin/env nomsu -V6.14 -# - Tool to automatically update code from old versions of Nomsu. Usage: - nomsu tools/upgrade.nom [-i] file1 file2 directory1 ... - If "-i" is the first argument, upgrades will be performed in-place. Otherwise, the - upgraded code will be printed. - -use "compatibility" -use "lib/os" -use "lib/consolecolor" - -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -$inplace = ($(COMMAND LINE ARGS).i or $(COMMAND LINE ARGS).inplace) -$start_version = $(COMMAND LINE ARGS)."upgrade-from" -$version = ($(COMMAND LINE ARGS)."upgrade-to" or (Nomsu version)) -$test = ($(COMMAND LINE ARGS).t or $(COMMAND LINE ARGS).test) -for $filename in $(COMMAND LINE ARGS).extras: - $file = (read file $filename) - unless $file: - fail "File does not exist: \$filename" - $leading_indent = ($file, matching "\n*([ ]*)") - $code = (NomsuCode from (Source $filename 1 (size of $file)) $file) - $tree = ($code parsed $start_version) - $uptree = - $tree upgraded from ($start_version or ($tree.version or (Nomsu version))) to - $version - $text = "\$leading_indent\($uptree as nomsu, text, with "\n" -> "\n\$leading_indent")" - when: - $inplace: - say "Upgraded \$filename" - write $text to file $filename - - $test: - if ($uptree == $tree): - say (dim "\$filename will not be changed") - ..else: - say (bright "\$filename will be changed") - - else: - say $text inline