diff options
| author | Bruce Hill <bitbucket@bruce-hill.com> | 2018-09-16 16:57:14 -0700 |
|---|---|---|
| committer | Bruce Hill <bitbucket@bruce-hill.com> | 2018-09-16 16:57:24 -0700 |
| commit | 96e5e567cb208194793a56462b90fb02c9cfe927 (patch) | |
| tree | 2e4be316de4988929788420fbfc15b16b60ba743 /nomsu_compiler.moon | |
| parent | 2282085e13f11581d2d5c0ac1491d54da4a3e19d (diff) | |
Working module system.
Diffstat (limited to 'nomsu_compiler.moon')
| -rw-r--r-- | nomsu_compiler.moon | 219 |
1 files changed, 116 insertions, 103 deletions
diff --git a/nomsu_compiler.moon b/nomsu_compiler.moon index 944f7b8..09e114e 100644 --- a/nomsu_compiler.moon +++ b/nomsu_compiler.moon @@ -25,6 +25,7 @@ unpack or= table.unpack {:NomsuCode, :LuaCode, :Source} = require "code_obj" AST = require "syntax_tree" make_parser = require("parser") +pretty_error = require("pretty_errors") -- Mapping from source string (e.g. "@core/metaprogramming.nom[1:100]") to a mapping -- from lua line number to nomsu line number export SOURCE_MAP @@ -84,13 +85,63 @@ for version=1,999 else break MAX_LINE = 80 -- For beautification purposes, try not to make lines much longer than this value -NomsuCompiler = setmetatable {}, __tostring: => "Nomsu" +NomsuCompiler = setmetatable {}, {__tostring: => "Nomsu"} _anon_chunk = 0 with NomsuCompiler - .NOMSU_COMPILER_VERSION = 8 + .NOMSU_COMPILER_VERSION = 9 .NOMSU_SYNTAX_VERSION = max_parser_version - .nomsu = NomsuCompiler - .global = Dict{} + .can_optimize = -> false + + -- Discretionary/convenience stuff + .environment = { + NOMSU_COMPILER_VERSION: 8, NOMSU_SYNTAX_VERSION: max_parser_version + -- Lua stuff: + :next, :unpack, :setmetatable, :coroutine, :rawequal, :getmetatable, :pcall, + :error, :package, :os, :require, :tonumber, :tostring, :string, :xpcall, :module, + :print, :loadfile, :rawset, :_VERSION, :collectgarbage, :rawget, :rawlen, + :table, :assert, :dofile, :loadstring, :type, :select, :math, :io, :load, + :pairs, :ipairs, + -- Nomsu types: + _List:List, _Dict:Dict, + -- Utilities and misc. + repr:repr, stringify:stringify, utils:utils, lpeg:lpeg, re:re, Files:Files, + :AST, TESTS: Dict{}, globals: Dict{} + :LuaCode, :NomsuCode, :Source + nomsu:NomsuCompiler + __imported: Dict{} + __parent: nil + } + setmetatable(.environment, { + __index: (key)=> + if imported = rawget(@, "__imported") + ret = imported[key] + return ret unless ret == nil + if parent = rawget(@, "__parent") + return parent[key] + }) + + if _VERSION == "Lua 5.4" + .environment.ipairs = (x)-> + if mt = getmetatable(x) + if mt.__ipairs then return mt.__ipairs(x) + return ipairs(x) + if jit or _VERSION == "Lua 5.2" + .environment.bit = require("bitops") + for k,v in pairs(AST) do .environment[k] = v + + .fork = => + f = setmetatable({}, {__index:@}) + f.environment = setmetatable({ + __parent: @environment + __imported: Dict{} + nomsu: f + COMPILE_ACTIONS: setmetatable({ + __parent: @environment.COMPILE_ACTIONS + __imported: Dict{} + }, getmetatable(@environment)) + }, getmetatable(@environment)) + return f + .parse = (nomsu_code, source=nil, version=nil)=> source or= nomsu_code.source nomsu_code = tostring(nomsu_code) @@ -101,7 +152,6 @@ with NomsuCompiler syntax_version = version and tonumber(version\match("^[0-9]+")) or max_parser_version parse = Parsers[syntax_version] or Parsers[max_parser_version] tree = parse(nomsu_code, source.filename) - pretty_error = require("pretty_errors") find_errors = (t)-> if t.type == "Error" coroutine.yield t @@ -121,52 +171,6 @@ with NomsuCompiler table.insert(err_strings, "\027[31;1m +#{num_errs-#errs} additional errors...\027[0m\n") error(table.concat(err_strings, '\n\n'), 0) return tree - .can_optimize = -> false - - -- Discretionary/convenience stuff - to_add = { - repr:repr, stringify:stringify, utils:utils, lpeg:lpeg, re:re, Files:Files, - -- Lua stuff: - :next, :unpack, :setmetatable, :coroutine, :rawequal, :getmetatable, :pcall, - :error, :package, :os, :require, :tonumber, :tostring, :string, :xpcall, :module, - :print, :loadfile, :rawset, :_VERSION, :collectgarbage, :rawget, :rawlen, - :table, :assert, :dofile, :loadstring, :type, :select, :math, :io, :load, - :pairs, :ipairs, - -- Nomsu types: - _List:List, _Dict:Dict, - } - 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 - .NomsuCode = NomsuCode - .Source = Source - .LOADED = {} - .TESTS = {} - .AST = AST - - .fork = => - f = setmetatable({}, {__index:@}) - f.COMPILE_ACTIONS = setmetatable({}, {__index:@COMPILE_ACTIONS}) - f.nomsu = f - return f - - -- This is a bit of a hack, but this code handles arbitrarily complex - -- math expressions like 2*x + 3^2 without having to define a single - -- action for every possibility. - math_expression = re.compile [[ ([+-] " ")* [0-9]+ (" " [*/^+-] (" " [+-])* " " [0-9]+)+ !. ]] - - .get_compile_action = (stub)=> - ret = @COMPILE_ACTIONS[stub] - return ret unless ret == nil - if math_expression\match(stub) - return @COMPILE_ACTIONS["# compile math expr #"] -- TODO: use pretty_error instead of this .compile_error = (source, err_format_string, ...)=> @@ -183,7 +187,7 @@ with NomsuCompiler err_msg = err_format_string\format(src, ...) error("#{source.filename}:#{line_no}: "..err_msg, 0) - add_lua_bits = (val_or_stmt, code)=> + add_lua_bits = (val_or_stmt, code, compile_actions)=> cls = val_or_stmt == "value" and LuaCode.Value or LuaCode operate_on_text = (text)-> lua = cls(text.source) @@ -193,7 +197,7 @@ with NomsuCompiler elseif bit.type == "Text" lua\append(operate_on_text(bit)) else - bit_lua = @compile(bit) + bit_lua = @compile(bit, compile_actions) unless bit_lua.is_value @compile_error bit.source, "Cannot use:\n%s\nas a string interpolation value, since it's not an expression." @@ -226,23 +230,25 @@ with NomsuCompiler return lua return operate_on_text code - .COMPILE_ACTIONS = { - ["# compile math expr #"]: (tree, ...)=> - lua = LuaCode.Value(tree.source) - for i,tok in ipairs tree - if type(tok) == 'string' - lua\append tok - else - tok_lua = @compile(tok) - unless tok_lua.is_value - @compile_error tok.source, "Non-expression value inside math expression:\n%s" - if tok.type == "Action" - tok_lua\parenthesize! - lua\append tok_lua - if i < #tree - lua\append " " - return lua - + -- This is a bit of a hack, but this code handles arbitrarily complex + -- math expressions like 2*x + 3^2 without having to define a single + -- action for every possibility. + math_expression = re.compile [[ ([+-] " ")* [0-9]+ (" " [*/^+-] (" " [+-])* " " [0-9]+)+ !. ]] + compile_math_expression = (tree, ...)=> + lua = LuaCode.Value(tree.source) + for i,tok in ipairs tree + if type(tok) == 'string' + lua\append tok + else + tok_lua = @compile(tok, compile_actions) + unless tok_lua.is_value + @compile_error tok.source, "Non-expression value inside math expression:\n%s" + tok_lua\parenthesize! if tok.type == "Action" + lua\append tok_lua + lua\append " " if i < #tree + return lua + .environment.COMPILE_ACTIONS = setmetatable({ + __imported: Dict{} ["Lua 1"]: (tree, code)=> return add_lua_string_bits(@, 'statements', code) @@ -276,15 +282,23 @@ with NomsuCompiler ["Lua version"]: (tree, code)=> return LuaCode.Value(tree.source, repr(_VERSION)) - } + + __parent: setmetatable({}, { + __index: (key)=> + if type(key) == 'string' and math_expression\match(key) + return compile_math_expression + }) + }, getmetatable(.environment)) .import = (mod)=> for k,v in pairs(mod) - @[k] = v unless k == "COMPILE_ACTIONS" or k == "nomsu" or k == "_ENV" + continue if k == "__imported" or k == "__parent" + @environment.__imported[k] = v for k,v in pairs(mod.COMPILE_ACTIONS) - @COMPILE_ACTIONS[k] = v + continue if k == "__imported" or k == "__parent" + @environment.COMPILE_ACTIONS.__imported[k] or= v - .run = (to_run)=> + .run = (to_run, compile_actions)=> source = to_run.source or Source(to_run, 1, #to_run) if type(source) == 'string' then source = Source\from_string(source) if not Files.read(source.filename) then Files.spoof(source.filename, to_run) @@ -299,7 +313,7 @@ with NomsuCompiler ret = nil all_lua = {} for chunk in *tree - lua = @compile(chunk)\as_statements("return ") + lua = @compile(chunk, compile_actions)\as_statements("return ") lua\declare_locals! lua\prepend "-- File: #{source.filename\gsub("\n.*", "...")}\n" insert all_lua, tostring(lua) @@ -307,10 +321,11 @@ with NomsuCompiler return ret _running_files = {} -- For detecting circular imports - .run_file = (filename)=> + _loaded_files = {} + .run_file = (filename, compile_actions)=> -- Filename should be an absolute path, i.e. package.nomsupath will not be searched for it - if @LOADED[filename] - return @LOADED[filename] + if _loaded_files[filename] + return _loaded_files[filename] -- Check for circular import -- TODO: optimize? for i,running in ipairs _running_files @@ -321,34 +336,31 @@ with NomsuCompiler insert _running_files, filename mod = @fork! + ret = mod.environment mod.from_file = filename if match(filename, "%.lua$") file = assert(Files.read(filename), "Could not find file: #{filename}") - ret = mod\run_lua(LuaCode(Source(filename, 1, #file), file)) + ret = mod\run_lua(LuaCode(Source(filename, 1, #file), file)) or ret elseif match(filename, "%.nom$") or match(filename, "^/dev/fd/[012]$") ran_lua = if @.can_optimize(filename) -- Look for precompiled version lua_filename = gsub(filename, "%.nom$", ".lua") if file = Files.read(lua_filename) - ret = mod\run_lua(LuaCode(Source(lua_filename, 1, #file), file)) - if type(ret) == 'table' - mod = ret + ret = mod\run_lua(LuaCode(Source(lua_filename, 1, #file), file)) or ret true unless ran_lua file = Files.read(filename) if not file error("Tried to run file that does not exist: #{filename}") - ret = mod\run(NomsuCode(Source(filename,1,#file), file)) - if type(ret) == 'table' - mod = ret + ret = mod\run(NomsuCode(Source(filename,1,#file), file), compile_actions) or ret else error("Invalid filetype for #{filename}", 0) - @LOADED[filename] = mod + _loaded_files[filename] = ret remove _running_files - return mod + return ret .run_lua = (lua)=> lua_string = tostring(lua) - run_lua_fn, err = load(lua_string, nil and tostring(source or lua.source), "t", self) + run_lua_fn, err = load(lua_string, nil and tostring(source or lua.source), "t", @environment) if not run_lua_fn line_numbered_lua = concat( [format("%3d|%s",i,line) for i, line in ipairs Files.get_lines(lua_string)], @@ -382,7 +394,8 @@ with NomsuCompiler return run_lua_fn! - .compile = (tree)=> + .compile = (tree, compile_actions)=> + compile_actions or= @environment.COMPILE_ACTIONS if tree.version if get_version = @[("Nomsu version")\as_lua_id!] if upgrade = @[("1 upgraded from 2 to 3")\as_lua_id!] @@ -390,7 +403,7 @@ with NomsuCompiler switch tree.type when "Action" stub = tree.stub - if compile_action = @get_compile_action(stub) + if compile_action = compile_actions[stub] args = [arg for arg in *tree when type(arg) != "string"] -- Force Lua to avoid tail call optimization for debugging purposes -- TODO: use tail call? @@ -402,7 +415,7 @@ with NomsuCompiler lua = LuaCode.Value(tree.source) if tree.target -- Method call - target_lua = @compile tree.target + target_lua = @compile tree.target, compile_actions if tostring(target_lua)\match("^%(.*%)$") or tostring(target_lua)\match("^[_a-zA-Z][_a-zA-Z0-9]*$") lua\append target_lua, ":" else @@ -411,7 +424,7 @@ with NomsuCompiler args = {} for i, tok in ipairs tree if type(tok) == "string" then continue - arg_lua = @compile(tok) + arg_lua = @compile(tok, compile_actions) unless arg_lua.is_value if tok.type == "Block" @compile_error tok.source, @@ -438,17 +451,17 @@ with NomsuCompiler elseif type(k) == 'string' and match(k,"[_a-zA-Z][_a-zA-Z0-9]*") lua\append(k, "= ") else - lua\append("[", (AST.is_syntax_tree(k) and @compile(k) or repr(k)), "]= ") + lua\append("[", (AST.is_syntax_tree(k) and @compile(k, compile_actions) or repr(k)), "]= ") if k == "source" lua\append repr(tostring(v)) else - lua\append(AST.is_syntax_tree(v) and @compile(v) or repr(v)) + lua\append(AST.is_syntax_tree(v) and @compile(v, compile_actions) or repr(v)) lua\append "}" return lua when "Block" lua = LuaCode(tree.source) - lua\concat_append([@compile(line)\as_statements! for line in *tree], "\n") + lua\concat_append([@compile(line, compile_actions)\as_statements! for line in *tree], "\n") return lua when "Text" @@ -462,9 +475,9 @@ with NomsuCompiler if #lua.bits > 0 then lua\append ".." lua\append repr(string_buffer) string_buffer = "" - bit_lua = @compile(bit) + bit_lua = @compile(bit, compile_actions) unless bit_lua.is_value - src = ' '..gsub(tostring(@compile(bit)), '\n','\n ') + src = ' '..gsub(tostring(@compile(bit, compile_actions)), '\n','\n ') line = "#{bit.source.filename}:#{Files.get_line_number(Files.read(bit.source.filename), bit.source.start)}" @compile_error bit.source, "Cannot use:\n%s\nas a string interpolation value, since it's not an expression." @@ -483,23 +496,23 @@ with NomsuCompiler when "List" lua = LuaCode.Value tree.source, "_List{" - lua\concat_append([@compile(e) for e in *tree], ", ", ",\n ") + lua\concat_append([@compile(e, compile_actions) for e in *tree], ", ", ",\n ") lua\append "}" return lua when "Dict" lua = LuaCode.Value tree.source, "_Dict{" - lua\concat_append([@compile(e) for e in *tree], ", ", ",\n ") + lua\concat_append([@compile(e, compile_actions) for e in *tree], ", ", ",\n ") lua\append "}" return lua when "DictEntry" key, value = tree[1], tree[2] - key_lua = @compile(key) + key_lua = @compile(key, compile_actions) unless key_lua.is_value @compile_error tree[1].source, "Cannot use:\n%s\nas a dict key, since it's not an expression." - value_lua = value and @compile(value) or LuaCode.Value(key.source, "true") + value_lua = value and @compile(value, compile_actions) or LuaCode.Value(key.source, "true") unless value_lua.is_value @compile_error tree[2].source, "Cannot use:\n%s\nas a dict value, since it's not an expression." @@ -516,7 +529,7 @@ with NomsuCompiler LuaCode tree.source, "[",key_lua,"]=",value_lua when "IndexChain" - lua = @compile(tree[1]) + lua = @compile(tree[1], compile_actions) unless lua.is_value @compile_error tree[1].source, "Cannot index:\n%s\nsince it's not an expression." @@ -526,7 +539,7 @@ with NomsuCompiler for i=2,#tree key = tree[i] - key_lua = @compile(key) + key_lua = @compile(key, compile_actions) unless key_lua.is_value @compile_error key.source, "Cannot use:\n%s\nas an index, since it's not an expression." |
