From 4f30e02acb666c52e0254eb9a3bf89a9cabb5e6d Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Wed, 29 Aug 2018 19:38:14 -0700 Subject: [PATCH] Handling more compatibility stuff, including Lua 5.4, and a backup for if openssl module is not found, and moving containers (List/Dict) into their own file, as well as bit operators (and support for __bxor, etc. metamethods in Lua 5.2/LuaJIT) --- Makefile | 5 +- README.md | 2 +- bitops.lua | 123 ++++++++++++++++++++ bitops.moon | 44 +++++++ compatibility/3.7.nom | 30 +++++ containers.lua | 240 +++++++++++++++++++++++++++++++++++++++ containers.moon | 99 ++++++++++++++++ core/collections.nom | 76 ++++--------- core/metaprogramming.nom | 2 +- core/operators.nom | 6 +- core/text.nom | 4 + lib/file_hash.nom | 37 +++++- nomsu_compiler.lua | 105 ++++------------- nomsu_compiler.moon | 52 ++------- 14 files changed, 637 insertions(+), 188 deletions(-) create mode 100644 bitops.lua create mode 100644 bitops.moon create mode 100644 compatibility/3.7.nom create mode 100644 containers.lua create mode 100644 containers.moon diff --git a/Makefile b/Makefile index 29b3737..a28a030 100644 --- a/Makefile +++ b/Makefile @@ -10,9 +10,10 @@ PREFIX= UNINSTALL_VERSION= # ========= You shouldn't need to mess with any of these variables below ================ -MOON_FILES= code_obj.moon error_handling.moon files.moon nomsu.moon nomsu_compiler.moon syntax_tree.moon parser.moon +MOON_FILES= code_obj.moon error_handling.moon files.moon nomsu.moon nomsu_compiler.moon \ + syntax_tree.moon parser.moon containers.moon LUA_FILES= code_obj.lua consolecolors.lua error_handling.lua files.lua nomsu.lua nomsu_compiler.lua \ - syntax_tree.lua parser.lua utils.lua uuid.lua + syntax_tree.lua parser.lua containers.lua utils.lua uuid.lua CORE_NOM_FILES= $(wildcard core/*.nom) CORE_LUA_FILES= $(patsubst %.nom,%.lua,$(CORE_NOM_FILES)) LIB_NOM_FILES= $(wildcard lib/*.nom) diff --git a/README.md b/README.md index 877877b..1f561eb 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ revolving around natural language rule-making and self modification. ## Dependencies -Nomsu's only dependencies are [Lua 5.2+](https://www.lua.org/) or [Luajit 2.0+](http://luajit.org/) and [LPEG](http://www.inf.puc-rio.br/~roberto/lpeg/) (`luarocks install lpeg`). Nomsu's compiler was written in [Moonscript](http://moonscript.org/), but all of the .moon files have been compiled into lua for convenience, so Moonscript is not a dependency. Optionally, if luafilesystem ([Lua version](https://github.com/keplerproject/luafilesystem) or [LuaJIT version](https://github.com/spacewander/luafilesystem)) is installed, it will be used. Otherwise Nomsu will fall back to using system commands (`find` and `ls`), which is slower and a bit less safe. Nomsu has been tested on Mac and Linux, but not Windows. +Nomsu's only dependencies are [Lua 5.2+](https://www.lua.org/) (or [Luajit 2.0+](http://luajit.org/), compiled with `XCFLAGS=-DLUAJIT_ENABLE_LUA52COMPAT` for Lua 5.2 compatibility) and [LPEG](http://www.inf.puc-rio.br/~roberto/lpeg/) (`luarocks install lpeg`). Nomsu's compiler was written in [Moonscript](http://moonscript.org/), but all of the .moon files have been compiled into lua for convenience, so Moonscript is not a dependency. Optionally, if luafilesystem ([Lua version](https://github.com/keplerproject/luafilesystem) or [LuaJIT version](https://github.com/spacewander/luafilesystem)) is installed, it will be used. Otherwise Nomsu will fall back to using system commands (`find` and `ls`), which is slower and a bit less safe. Nomsu has been tested on Mac and Linux, but not Windows. ## Usage diff --git a/bitops.lua b/bitops.lua new file mode 100644 index 0000000..f87be64 --- /dev/null +++ b/bitops.lua @@ -0,0 +1,123 @@ +local bitlib +if jit then + bitlib = require('bit') +elseif _VERSION == "Lua 5.2" then + bitlib = bit32 +else + bitlib = error("no bit library for Lua 5.3+") +end +local ret +do + local _tbl_0 = { } + for k, v in pairs(bitlib) do + _tbl_0[k] = v + end + ret = _tbl_0 +end +ret.bnot = function(x) + do + local mt = getmetatable(x) + if mt then + if mt.__bnot then + return mt.__bnot(x) + end + end + end + return bitlib.bnot(x) +end +ret.band = function(x, y) + do + local mt_x = getmetatable(x) + if mt_x then + if mt_x.__band then + return mt_x.__band(x, y) + end + end + end + do + local mt_y = getmetatable(x) + if mt_y then + if mt_y.__band then + return mt_y.__band(x, y) + end + end + end + return bitlib.band(x, y) +end +ret.bor = function(x, y) + do + local mt_x = getmetatable(x) + if mt_x then + if mt_x.__bor then + return mt_x.__bor(x, y) + end + end + end + do + local mt_y = getmetatable(x) + if mt_y then + if mt_y.__bor then + return mt_y.__bor(x, y) + end + end + end + return bitlib.bor(x, y) +end +ret.bxor = function(x, y) + do + local mt_x = getmetatable(x) + if mt_x then + if mt_x.__bxor then + return mt_x.__bxor(x, y) + end + end + end + do + local mt_y = getmetatable(x) + if mt_y then + if mt_y.__bxor then + return mt_y.__bxor(x, y) + end + end + end + return bitlib.bxor(x, y) +end +ret.lshift = function(x, y) + do + local mt_x = getmetatable(x) + if mt_x then + if mt_x.__shl then + return mt_x.__shl(x, y) + end + end + end + do + local mt_y = getmetatable(x) + if mt_y then + if mt_y.__shl then + return mt_y.__shl(x, y) + end + end + end + return bitlib.lshift(x, y) +end +ret.rshift = function(x, y) + do + local mt_x = getmetatable(x) + if mt_x then + if mt_x.__shr then + return mt_x.__shr(x, y) + end + end + end + do + local mt_y = getmetatable(x) + if mt_y then + if mt_y.__shr then + return mt_y.__shr(x, y) + end + end + end + return bitlib.rshift(x, y) +end +return ret diff --git a/bitops.moon b/bitops.moon new file mode 100644 index 0000000..27752eb --- /dev/null +++ b/bitops.moon @@ -0,0 +1,44 @@ +-- This file defines wrapper functions around the Lua 5.2/LuaJIT bitwise operators +-- The wrapper functions check for the appropriate metatable functions like +-- "__bxor" for bit.bxor(), etc. +bitlib = if jit then require('bit') +elseif _VERSION == "Lua 5.2" bit32 +else error("no bit library for Lua 5.3+") + +ret = {k,v for k,v in pairs(bitlib)} +ret.bnot = (x)-> + if mt = getmetatable(x) + if mt.__bnot then return mt.__bnot(x) + return bitlib.bnot(x) +ret.band = (x,y)-> + if mt_x = getmetatable(x) + if mt_x.__band then return mt_x.__band(x, y) + if mt_y = getmetatable(x) + if mt_y.__band then return mt_y.__band(x, y) + return bitlib.band(x, y) +ret.bor = (x,y)-> + if mt_x = getmetatable(x) + if mt_x.__bor then return mt_x.__bor(x, y) + if mt_y = getmetatable(x) + if mt_y.__bor then return mt_y.__bor(x, y) + return bitlib.bor(x, y) +ret.bxor = (x,y)-> + if mt_x = getmetatable(x) + if mt_x.__bxor then return mt_x.__bxor(x, y) + if mt_y = getmetatable(x) + if mt_y.__bxor then return mt_y.__bxor(x, y) + return bitlib.bxor(x, y) +ret.lshift = (x,y)-> + if mt_x = getmetatable(x) + if mt_x.__shl then return mt_x.__shl(x, y) + if mt_y = getmetatable(x) + if mt_y.__shl then return mt_y.__shl(x, y) + return bitlib.lshift(x, y) +ret.rshift = (x,y)-> + if mt_x = getmetatable(x) + if mt_x.__shr then return mt_x.__shr(x, y) + if mt_y = getmetatable(x) + if mt_y.__shr then return mt_y.__shr(x, y) + return bitlib.rshift(x, y) + +return ret diff --git a/compatibility/3.7.nom b/compatibility/3.7.nom new file mode 100644 index 0000000..1980f92 --- /dev/null +++ b/compatibility/3.7.nom @@ -0,0 +1,30 @@ +#!/usr/bin/env nomsu -V3.6.5.6 +# + This file defines upgrades from Nomsu <3.7 to 3.7 + +use "compatibility/compatibility.nom" + +# Indexing +upgrade action [%index st to last in %list] to "3.7" as (%list::%index st to last) +upgrade action [%index nd to last in %list] to "3.7" as (%list::%index nd to last) +upgrade action [%index rd to last in %list] to "3.7" as (%list::%index rd to last) +upgrade action [%index th to last in %list] to "3.7" as (%list::%index rd th last) +upgrade action [last in %list] to "3.7" (%list::last) +upgrade action [first in %list] to "3.7" (%list::first) +upgrade action [%item is in %list, %list contains %item, %list has %item] to "3.7" as (..) + %list::has %item + +upgrade action [..] + %item isn't in %list, %item is not in %list, %list doesn't contain %item + %list does not contain %item, %list doesn't have %item, %list does not have %item +..to "3.7" as (not (%list::has %item)) + +upgrade action [%list has key %index, %list has index %index] to "3.7" as (%list.%index != (nil)) +upgrade action [..] + %list doesn't have key %index, %list does not have key %index + %list doesn't have index %index, %list does not have index %index +..to "3.7" as (%list.%index == (nil)) + +upgrade action [..] + number of keys in %list, len %list, || %list ||, length %list, length of %list +..to "3.7" as (size of %list) diff --git a/containers.lua b/containers.lua new file mode 100644 index 0000000..09de4e5 --- /dev/null +++ b/containers.lua @@ -0,0 +1,240 @@ +local insert, remove, concat +do + local _obj_0 = table + insert, remove, concat = _obj_0.insert, _obj_0.remove, _obj_0.concat +end +local repr, stringify, equivalent, nth_to_last, size +do + local _obj_0 = require('utils') + repr, stringify, equivalent, nth_to_last, size = _obj_0.repr, _obj_0.stringify, _obj_0.equivalent, _obj_0.nth_to_last, _obj_0.size +end +local list, dict +local _list_mt = { + __eq = equivalent, + __tostring = function(self) + return "[" .. concat((function() + local _accum_0 = { } + local _len_0 = 1 + for _index_0 = 1, #self do + local b = self[_index_0] + _accum_0[_len_0] = repr(b) + _len_0 = _len_0 + 1 + end + return _accum_0 + end)(), ", ") .. "]" + end, + __lt = function(self, other) + assert(type(self) == 'table' and type(other) == 'table', "Incompatible types for comparison") + for i = 1, math.max(#self, #other) do + if not self[i] and other[i] then + return true + elseif self[i] and not other[i] then + return false + elseif self[i] < other[i] then + return true + elseif self[i] > other[i] then + return false + end + end + return false + end, + __le = function(self, other) + assert(type(self) == 'table' and type(other) == 'table', "Incompatible types for comparison") + for i = 1, math.max(#self, #other) do + if not self[i] and other[i] then + return true + elseif self[i] and not other[i] then + return false + elseif self[i] < other[i] then + return true + elseif self[i] > other[i] then + return false + end + end + return true + end, + __add = function(self, other) + local ret = list((function() + local _accum_0 = { } + local _len_0 = 1 + for _index_0 = 1, #self do + local x = self[_index_0] + _accum_0[_len_0] = x + _len_0 = _len_0 + 1 + end + return _accum_0 + end)()) + for _index_0 = 1, #other do + local x = other[_index_0] + insert(ret, x) + end + return ret + end, + __index = { + add_1 = insert, + append_1 = insert, + add_1_at_index_2 = function(t, x, i) + return insert(t, i, x) + end, + at_index_1_add_2 = insert, + pop = remove, + remove_last = remove, + remove_index_1 = remove, + last = (function(self) + return self[#self] + end), + first = (function(self) + return self[1] + end), + _1_st_to_last = nth_to_last, + _1_nd_to_last = nth_to_last, + _1_rd_to_last = nth_to_last, + _1_th_to_last = nth_to_last, + has_1 = function(self, item) + for _index_0 = 1, #self do + local x = self[_index_0] + if x == item then + return true + end + end + return false + end, + index_of_1 = function(self, item) + for i, x in ipairs(self) do + if x == item then + return i + end + end + return nil + end + } +} +list = function(t) + return setmetatable(t, _list_mt) +end +local walk_items +walk_items = function(self, i) + i = i + 1 + local k, v = next(self.table, self.key) + if k ~= nil then + self.key = k + return i, dict({ + key = k, + value = v + }) + end +end +local _dict_mt = { + __eq = equivalent, + __len = size, + __tostring = function(self) + return "{" .. concat((function() + local _accum_0 = { } + local _len_0 = 1 + for k, v in pairs(self) do + _accum_0[_len_0] = tostring(repr(k)) .. ": " .. tostring(repr(v)) + _len_0 = _len_0 + 1 + end + return _accum_0 + end)(), ", ") .. "}" + end, + __ipairs = function(self) + return walk_items, { + table = self, + key = nil + }, 0 + end, + __band = function(self, other) + return dict((function() + local _tbl_0 = { } + for k, v in pairs(self) do + if other[k] ~= nil then + _tbl_0[k] = v + end + end + return _tbl_0 + end)()) + end, + __bor = function(self, other) + local ret + do + local _tbl_0 = { } + for k, v in pairs(self) do + _tbl_0[k] = v + end + ret = _tbl_0 + end + for k, v in pairs(other) do + if ret[k] == nil then + ret[k] = v + end + end + return dict(ret) + end, + __bxor = function(self, other) + local ret + do + local _tbl_0 = { } + for k, v in pairs(self) do + _tbl_0[k] = v + end + ret = _tbl_0 + end + for k, v in pairs(other) do + if ret[k] == nil then + ret[k] = v + else + ret[k] = nil + end + end + return dict(ret) + end, + __add = function(self, other) + local ret + do + local _tbl_0 = { } + for k, v in pairs(self) do + _tbl_0[k] = v + end + ret = _tbl_0 + end + for k, v in pairs(other) do + if ret[k] == nil then + ret[k] = v + else + ret[k] = ret[k] + v + end + end + return dict(ret) + end, + __sub = function(self, other) + local ret + do + local _tbl_0 = { } + for k, v in pairs(self) do + _tbl_0[k] = v + end + ret = _tbl_0 + end + for k, v in pairs(other) do + if ret[k] == nil then + ret[k] = -v + else + ret[k] = ret[k] - v + end + end + return dict(ret) + end +} +dict = function(t) + return setmetatable(t, _dict_mt) +end +for i, entry in ipairs(dict({ + x = 99 +})) do + assert(i == 1 and entry.key == "x" and entry.value == 99, "ipairs compatibility issue") +end +return { + list = list, + dict = dict +} diff --git a/containers.moon b/containers.moon new file mode 100644 index 0000000..deb5684 --- /dev/null +++ b/containers.moon @@ -0,0 +1,99 @@ +-- This file contains container classes, i.e. Lists, Dicts, and Sets +{:insert,:remove,:concat} = table +{:repr, :stringify, :equivalent, :nth_to_last, :size} = require 'utils' + +local list, dict + +-- List and Dict classes to provide basic equality/tostring functionality for the tables +-- used in Nomsu. This way, they retain a notion of whether they were originally lists or dicts. +_list_mt = + __eq:equivalent + -- Could consider adding a __newindex to enforce list-ness, but would hurt performance + __tostring: => + "["..concat([repr(b) for b in *@], ", ").."]" + __lt: (other)=> + assert type(@) == 'table' and type(other) == 'table', "Incompatible types for comparison" + for i=1,math.max(#@, #other) + if not @[i] and other[i] then return true + elseif @[i] and not other[i] then return false + elseif @[i] < other[i] then return true + elseif @[i] > other[i] then return false + return false + __le: (other)=> + assert type(@) == 'table' and type(other) == 'table', "Incompatible types for comparison" + for i=1,math.max(#@, #other) + if not @[i] and other[i] then return true + elseif @[i] and not other[i] then return false + elseif @[i] < other[i] then return true + elseif @[i] > other[i] then return false + return true + __add: (other)=> + ret = list[x for x in *@] + for x in *other + insert(ret, x) + return ret + __index: + add_1: insert, append_1: insert + add_1_at_index_2: (t,x,i)-> insert(t,i,x) + at_index_1_add_2: insert + pop: remove, remove_last: remove, remove_index_1: remove + last: (=> @[#@]), first: (=> @[1]) + _1_st_to_last:nth_to_last, _1_nd_to_last:nth_to_last + _1_rd_to_last:nth_to_last, _1_th_to_last:nth_to_last + has_1: (item)=> + for x in *@ + if x == item + return true + return false + index_of_1: (item)=> + for i,x in ipairs @ + if x == item + return i + return nil + +list = (t)-> setmetatable(t, _list_mt) + +walk_items = (i)=> + i = i + 1 + k, v = next(@table, @key) + if k != nil + @key = k + return i, dict{key:k, value:v} + +_dict_mt = + __eq:equivalent + __len:size + __tostring: => + "{"..concat(["#{repr(k)}: #{repr(v)}" for k,v in pairs @], ", ").."}" + __ipairs: => walk_items, {table:@, key:nil}, 0 + __band: (other)=> + dict{k,v for k,v in pairs(@) when other[k] != nil} + __bor: (other)=> + ret = {k,v for k,v in pairs(@)} + for k,v in pairs(other) + if ret[k] == nil then ret[k] = v + return dict(ret) + __bxor: (other)=> + ret = {k,v for k,v in pairs(@)} + for k,v in pairs(other) + if ret[k] == nil then ret[k] = v + else ret[k] = nil + return dict(ret) + __add: (other)=> + ret = {k,v for k,v in pairs(@)} + for k,v in pairs(other) + if ret[k] == nil then ret[k] = v + else ret[k] += v + return dict(ret) + __sub: (other)=> + ret = {k,v for k,v in pairs(@)} + for k,v in pairs(other) + if ret[k] == nil then ret[k] = -v + else ret[k] -= v + return dict(ret) +dict = (t)-> setmetatable(t, _dict_mt) + +for i,entry in ipairs(dict({x:99})) + assert(i == 1 and entry.key == "x" and entry.value == 99, "ipairs compatibility issue") + +return {:list, :dict} diff --git a/core/collections.nom b/core/collections.nom index 00bb5d8..938466d 100644 --- a/core/collections.nom +++ b/core/collections.nom @@ -7,63 +7,37 @@ use "core/metaprogramming.nom" use "core/control_flow.nom" use "core/operators.nom" -# List/dict functions: -# Indexing -test: - assume ((2 nd to last in [1, 2, 3, 4, 5]) is 4) -compile [..] - %index st to last in %list, %index nd to last in %list - %index rd to last in %list, %index th to last in %list -..to (Lua value "utils.nth_to_last(\(%list as lua expr), \(%index as lua expr))") - -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -parse [last in %list] as (1 st to last in %list) -parse [first in %list] as %list.1 - -# Membership testing -test: - assume (3 is in [1, 2, 3, 4, 5]) -action [%item is in %list, %list contains %item, %list has %item]: - for %key = %value in %list: - if (%key is %item): return (yes) - return (no) - -test: - assume (99 isn't in [1, 2, 3]) -action [..] - %item isn't in %list, %item is not in %list, %list doesn't contain %item - %list does not contain %item, %list doesn't have %item, %list does not have %item -..: - for %key = %value in %list: - if (%key is %item): return (no) - return (yes) - -test: - assume ({x:no} has key "x") -parse [%list has key %index, %list has index %index] as (%list.%index != (nil)) - -test: - assume ({x:no} doesn't have key "y") - assume (not ({x:no} doesn't have key "x")) -parse [..] - %list doesn't have key %index, %list does not have key %index - %list doesn't have index %index, %list does not have index %index -..as (%list.%index == (nil)) - -compile [number of keys in %list] to (..) - Lua value "utils.size(\(%list as lua expr))" - +# List functionality: test: %list = [1, 2, 3, 4, 5] + %visited = {} + for %i = %x in %list: %visited.%i = (yes) + assume (%visited == {1:yes, 2:yes, 3:yes, 4:yes, 5:yes}) + %visited = {} + for %x in %list: %visited.%x = (yes) + assume (%visited == {1:yes, 2:yes, 3:yes, 4:yes, 5:yes}) + assume ((%list::2 nd to last) == 4) + assume ((%list::first) == 1) + assume (%list::has 3) + assume ((%list::index of 3) == 3) + assume ((size of %list) == 5) %list::add 6 - assume ((last in %list) is 6) + assume ((%list::last) == 6) %list::pop - assume ((last in %list) is 5) + assume ((%list::last) == 5) %list::remove index 1 - assume ((first in %list) is 2) + assume ((%list::first) == 2) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# Dict functionality +test: + %dict = {x:1, y:2, z:3} + assume ((size of %dict) == 3) + assume ((% for % in {x:1}) == [{key:"x", value:1}]) + assume (({key:%k, value:%v} for %k = %v in {x:1}) == [{key:"x", value:1}]) + assume (({x:1, y:1} + {y:10, z:10}) == {x:1, y:11, z:10}) + assume (({x:1, y:1} | {y:10, z:10}) == {x:1, y:1, z:10}) + assume (({x:1, y:1} & {y:10, z:10}) == {y:1}) + assume (({x:1, y:1} ~ {y:10, z:10}) == {x:1, z:10}) # List Comprehension test: diff --git a/core/metaprogramming.nom b/core/metaprogramming.nom index bd65a7e..3657e02 100644 --- a/core/metaprogramming.nom +++ b/core/metaprogramming.nom @@ -3,7 +3,7 @@ This File contains actions for making actions and compile-time actions and some helper functions to make that easier. -lua> "NOMSU_CORE_VERSION = 6" +lua> "NOMSU_CORE_VERSION = 7" lua> ".." COMPILE_ACTIONS["1 -> 2"] = function(nomsu, tree, \%args, \%body) local lua = LuaCode.Value(tree.source, "(function(") diff --git a/core/operators.nom b/core/operators.nom index fbf88bb..863ebc8 100644 --- a/core/operators.nom +++ b/core/operators.nom @@ -238,7 +238,7 @@ compile [%x or %y] to (Lua value "(\(%x as lua expr) or \(%y as lua expr))") # Bitwise Operators # TODO: implement OR, XOR, AND for multiple operands? test: - assume ((~ 5) == -6) + assume ((~ (~ 5)) == 5) assume ((1 | 4) == 5) assume ((1 ~ 3) == 2) assume ((1 & 3) == 1) @@ -285,8 +285,8 @@ compile [- %] to (Lua value "(- \(% as lua expr))") compile [not %] to (Lua value "(not \(% as lua expr))") test: - assume ((length of [1, 2, 3]) == 3) -compile [length of %list, len %list, || %list ||] to (..) + assume ((size of [1, 2, 3]) == 3) +compile [size of %list, length of %list, len %list, || %list ||] to (..) Lua value "(#\(%list as lua expr))" compile [%list is empty] to (Lua value "(#\(%list as lua expr) == 0)") diff --git a/core/text.nom b/core/text.nom index 25d4066..3cc979c 100644 --- a/core/text.nom +++ b/core/text.nom @@ -49,6 +49,10 @@ compile [bytes %start to %stop of %text] to (..) Lua value ".." list{(\(%text as lua expr)):byte(\(%start as lua expr), \(%stop as lua expr))} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +action [bytes of %text] (bytes 1 to (=lua "#\%text") of %text) + test: assume (("asdf" capitalized) == "Asdf") compile [capitalized %text, %text capitalized] to (..) diff --git a/lib/file_hash.nom b/lib/file_hash.nom index 188df78..bce8c08 100644 --- a/lib/file_hash.nom +++ b/lib/file_hash.nom @@ -5,13 +5,38 @@ use "lib/os.nom" use "lib/base64.nom" -%hashlib = (=lua "require('openssl.digest')") +lua> ".." + local \%use_sha1, \%hashlib = pcall(require, 'openssl.digest') test: - assume ((hash "hello world") == "Kq5sNclPz7QV2+lfQIuc6R7oRu0=") -action [hash %, sha1 %]: - %hash = (=lua "\%hashlib.new('sha1'):final(\%)") - return (base64 %hash) + assume ((hash "hello world") == (hash "hello world")) + assume ((hash "hello world") != (hash "goodbye")) or barf ".." + Hash collision: + (hash "hello world") = \(hash "hello world") + (hash "goodbye") = \(hash "goodbye") + assume (..) + (..) + hash ".." + This is a really long string meant to stress test the hashing function and + ensure that it's not overflowing with long inputs. + ..!= "inf" + assume ((hash "\0") != (hash "\0\0\0\0\0")) or barf "Incorrect hashing of null strings" + if %use_sha1: + assume ((hash "hello world") == "Kq5sNclPz7QV2+lfQIuc6R7oRu0=") +if %use_sha1: + action [hash %]: + %hash = (=lua "\%hashlib.new('sha1'):final(\%)") + return (base64 %hash) +..else: + # TODO: remove warning? + say "\027[31;1mWARNING: OpenSSL module not found. Defaulting to a non-cryptographically secure hash function.\027[0m" + action [hash %]: + %bytes = (bytes of %) + %hash = (%bytes.1 << 7) + for %i in 2 to (size of %bytes): + %hash = ((1000003 * %hash) ~ %bytes.%i) + %hash = (%hash ~ (size of %bytes)) + return "\%hash" action [file with hash %hash]: for file %filename in ".": @@ -19,4 +44,4 @@ action [file with hash %hash]: %file_hash = (hash %contents) if (%file_hash == %hash): return %filename -parse [hash of file %filename] as (sha1 (read file %filename)) +parse [hash of file %filename] as (hash (read file %filename)) diff --git a/nomsu_compiler.lua b/nomsu_compiler.lua index 4855476..11bdc9d 100644 --- a/nomsu_compiler.lua +++ b/nomsu_compiler.lua @@ -4,6 +4,11 @@ local utils = require('utils') local Files = require('files') local repr, stringify, equivalent repr, stringify, equivalent = utils.repr, utils.stringify, utils.equivalent +local list, dict +do + local _obj_0 = require('containers') + list, dict = _obj_0.list, _obj_0.dict +end colors = require('consolecolors') colored = setmetatable({ }, { __index = function(_, color) @@ -82,84 +87,6 @@ do end end end -local _list_mt = { - __eq = equivalent, - __tostring = function(self) - return "[" .. concat((function() - local _accum_0 = { } - local _len_0 = 1 - for _index_0 = 1, #self do - local b = self[_index_0] - _accum_0[_len_0] = repr(b) - _len_0 = _len_0 + 1 - end - return _accum_0 - end)(), ", ") .. "]" - end, - __lt = function(self, other) - assert(type(self) == 'table' and type(other) == 'table', "Incompatible types for comparison") - for i = 1, math.max(#self, #other) do - if not self[i] and other[i] then - return true - elseif self[i] and not other[i] then - return false - elseif self[i] < other[i] then - return true - elseif self[i] > other[i] then - return false - end - end - return false - end, - __le = function(self, other) - assert(type(self) == 'table' and type(other) == 'table', "Incompatible types for comparison") - for i = 1, math.max(#self, #other) do - if not self[i] and other[i] then - return true - elseif self[i] and not other[i] then - return false - elseif self[i] < other[i] then - return true - elseif self[i] > other[i] then - return false - end - end - return true - end, - __index = { - add_1 = insert, - append_1 = insert, - add_1_at_index_2 = function(t, x, i) - return insert(t, i, x) - end, - at_index_1_add_2 = insert, - pop = table.remove, - remove_last = table.remove, - remove_index_1 = table.remove - } -} -local list -list = function(t) - return setmetatable(t, _list_mt) -end -local _dict_mt = { - __eq = equivalent, - __tostring = function(self) - return "{" .. concat((function() - local _accum_0 = { } - local _len_0 = 1 - for k, v in pairs(self) do - _accum_0[_len_0] = tostring(repr(k)) .. ": " .. tostring(repr(v)) - _len_0 = _len_0 + 1 - end - return _accum_0 - end)(), ", ") .. "}" - end -} -local dict -dict = function(t) - return setmetatable(t, _dict_mt) -end local MAX_LINE = 80 local NomsuCompiler = setmetatable({ name = "Nomsu" @@ -232,10 +159,21 @@ do list = list, dict = dict } - if jit then - to_add.bit = require('bit') - elseif _VERSION == "Lua 5.2" then - to_add.bit = bit32 + if _VERSION == "Lua 5.4" then + to_add.ipairs = function(x) + do + local mt = getmetatable(x) + if mt then + if mt.__ipairs then + return mt.__ipairs(x) + end + end + end + return ipairs(x) + end + end + if jit or _VERSION == "Lua 5.2" then + to_add.bit = require("bitops") end for k, v in pairs(to_add) do NomsuCompiler[k] = v @@ -591,6 +529,9 @@ do local lua = LuaCode.Value(tree.source) if tree.target then lua:append(self:compile(tree.target), ":") + if string.as_lua_id(stub):match("^[0-9]") then + lua:append("_") + end else lua:append("A_") end diff --git a/nomsu_compiler.moon b/nomsu_compiler.moon index 5ac6a87..70e3f40 100644 --- a/nomsu_compiler.moon +++ b/nomsu_compiler.moon @@ -14,6 +14,7 @@ re = require 're' utils = require 'utils' Files = require 'files' {:repr, :stringify, :equivalent} = utils +{:list, :dict} = require 'containers' export colors, colored colors = require 'consolecolors' colored = setmetatable({}, {__index:(_,color)-> ((msg)-> colors[color]..tostring(msg or '')..colors.reset)}) @@ -57,46 +58,6 @@ do if type(i) == 'number' then return sub(@, i, i) elseif type(i) == 'table' then return sub(@, i[1], i[2]) --- List and Dict classes to provide basic equality/tostring functionality for the tables --- used in Nomsu. This way, they retain a notion of whether they were originally lists or dicts. -_list_mt = - __eq:equivalent - -- Could consider adding a __newindex to enforce list-ness, but would hurt performance - __tostring: => - "["..concat([repr(b) for b in *@], ", ").."]" - __lt: (other)=> - assert type(@) == 'table' and type(other) == 'table', "Incompatible types for comparison" - for i=1,math.max(#@, #other) - if not @[i] and other[i] then return true - elseif @[i] and not other[i] then return false - elseif @[i] < other[i] then return true - elseif @[i] > other[i] then return false - return false - __le: (other)=> - assert type(@) == 'table' and type(other) == 'table', "Incompatible types for comparison" - for i=1,math.max(#@, #other) - if not @[i] and other[i] then return true - elseif @[i] and not other[i] then return false - elseif @[i] < other[i] then return true - elseif @[i] > other[i] then return false - return true - __index: - add_1: insert - append_1: insert - add_1_at_index_2: (t,x,i)-> insert(t,i,x) - at_index_1_add_2: insert - pop: table.remove - remove_last: table.remove - remove_index_1: table.remove - -list = (t)-> setmetatable(t, _list_mt) - -_dict_mt = - __eq:equivalent - __tostring: => - "{"..concat(["#{repr(k)}: #{repr(v)}" for k,v in pairs @], ", ").."}" -dict = (t)-> setmetatable(t, _dict_mt) - MAX_LINE = 80 -- For beautification purposes, try not to make lines much longer than this value NomsuCompiler = setmetatable {name:"Nomsu"}, __index: (k)=> if _self = rawget(@, "self") then _self[k] else nil @@ -120,8 +81,13 @@ with NomsuCompiler -- Nomsu types: :list, :dict, } - if jit then to_add.bit = require('bit') - elseif _VERSION == "Lua 5.2" then to_add.bit = bit32 + if _VERSION == "Lua 5.4" + to_add.ipairs = (x)-> + if mt = getmetatable(x) + if mt.__ipairs then return mt.__ipairs(x) + return ipairs(x) + if jit or _VERSION == "Lua 5.2" + to_add.bit = require("bitops") for k,v in pairs(to_add) do NomsuCompiler[k] = v for k,v in pairs(AST) do NomsuCompiler[k] = v .LuaCode = LuaCode @@ -365,6 +331,8 @@ with NomsuCompiler lua = LuaCode.Value(tree.source) if tree.target lua\append @compile(tree.target), ":" + if string.as_lua_id(stub)\match("^[0-9]") + lua\append "_" else lua\append "A_" lua\append(string.as_lua_id(stub),"(")