diff options
| author | Li Jin <dragon-fly@qq.com> | 2020-02-07 17:29:34 +0800 |
|---|---|---|
| committer | Li Jin <dragon-fly@qq.com> | 2020-02-07 17:29:34 +0800 |
| commit | c241ea241e8e9c152f6eb14f163b2ae39749f7bf (patch) | |
| tree | 2fd05ca6866ea60ca778fb6ff31c7ec429e138c4 | |
| parent | 2e50c15bfe67d4709880a0377d37fca191be2f3e (diff) | |
| download | yuescript-c241ea241e8e9c152f6eb14f163b2ae39749f7bf.tar.gz yuescript-c241ea241e8e9c152f6eb14f163b2ae39749f7bf.tar.bz2 yuescript-c241ea241e8e9c152f6eb14f163b2ae39749f7bf.zip | |
releasing moonplus as a lib.
Diffstat (limited to '')
| -rw-r--r-- | .gitignore | 1 | ||||
| -rw-r--r-- | CMakeLists.txt | 36 | ||||
| -rw-r--r-- | LICENSE | 2 | ||||
| -rw-r--r-- | makefile | 3 | ||||
| -rw-r--r-- | moonplus-0.1-1.rockspec | 27 | ||||
| -rw-r--r-- | spec/inputs/plus.moon | 6 | ||||
| -rw-r--r-- | spec/inputs/syntax.moon | 8 | ||||
| -rw-r--r-- | src/MoonP/moon_compiler.cpp | 33 | ||||
| -rw-r--r-- | src/MoonPlus.h | 125 | ||||
| -rw-r--r-- | src/StackTracePlus.h | 550 | ||||
| -rw-r--r-- | src/moonp.cpp (renamed from src/moonc.cpp) | 128 |
11 files changed, 896 insertions, 23 deletions
| @@ -1,3 +1,4 @@ | |||
| 1 | moonp | 1 | moonp |
| 2 | build | 2 | build |
| 3 | build.luarocks | ||
| 3 | spec/outputs | 4 | spec/outputs |
diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..195895c --- /dev/null +++ b/CMakeLists.txt | |||
| @@ -0,0 +1,36 @@ | |||
| 1 | project ( moonp CXX ) | ||
| 2 | cmake_minimum_required ( VERSION 3.1 ) | ||
| 3 | |||
| 4 | MESSAGE(STATUS "Lua: using information from luarocks") | ||
| 5 | |||
| 6 | MESSAGE(STATUS "LUA_LIBDIR: " ${LUA_INCDIR}/../../lib) | ||
| 7 | MESSAGE(STATUS "LUA_INCDIR: " ${LUA_INCDIR}) | ||
| 8 | MESSAGE(STATUS "LUA: " ${LUA}) | ||
| 9 | |||
| 10 | SET(LUA_EXECUTABLE "${LUA}") | ||
| 11 | SET(LUA_INCLUDE_DIR "${LUA_INCDIR}") | ||
| 12 | |||
| 13 | GET_FILENAME_COMPONENT(LUA_EXEC_NAME ${LUA} NAME_WE) | ||
| 14 | IF(LUA_EXEC_NAME STREQUAL "luajit") | ||
| 15 | FIND_LIBRARY(LUA_LIBRARIES | ||
| 16 | NAMES luajit libluajit | ||
| 17 | PATHS ${LUA_INCDIR}/../../lib | ||
| 18 | NO_DEFAULT_PATH) | ||
| 19 | ELSEIF(LUA_EXEC_NAME MATCHES "lua.*") | ||
| 20 | FIND_LIBRARY(LUA_LIBRARIES | ||
| 21 | NAMES lua lua53 lua52 lua51 liblua liblua53 liblua52 liblua51 | ||
| 22 | PATHS ${LUA_INCDIR}/../../lib | ||
| 23 | NO_DEFAULT_PATH) | ||
| 24 | ENDIF() | ||
| 25 | MESSAGE(STATUS "Lua library: ${LUA_LIBRARIES}") | ||
| 26 | |||
| 27 | enable_language( CXX ) | ||
| 28 | find_package( Lua REQUIRED ) | ||
| 29 | include_directories( src ${LUA_INCLUDE_DIR} ) | ||
| 30 | add_definitions( -std=c++17 -O3 -DLIBMOONP ) | ||
| 31 | |||
| 32 | add_library( moonp MODULE src/MoonP/ast.cpp src/MoonP/parser.cpp src/MoonP/moon_parser.cpp src/MoonP/moon_compiler.cpp src/moonp.cpp) | ||
| 33 | set_target_properties( moonp PROPERTIES PREFIX "") | ||
| 34 | target_link_libraries( moonp ${LUA_LIBRARIES} ) | ||
| 35 | |||
| 36 | install(CODE "") | ||
| @@ -1,6 +1,6 @@ | |||
| 1 | MIT License | 1 | MIT License |
| 2 | 2 | ||
| 3 | Copyright (c) 2017 Li Jin | 3 | Copyright (c) 2020 Li Jin |
| 4 | 4 | ||
| 5 | Permission is hereby granted, free of charge, to any person obtaining a copy | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy |
| 6 | of this software and associated documentation files (the "Software"), to deal | 6 | of this software and associated documentation files (the "Software"), to deal |
| @@ -206,7 +206,10 @@ clean: | |||
| 206 | .PHONY: test | 206 | .PHONY: test |
| 207 | test: release | 207 | test: release |
| 208 | @echo "Compiling Moonscript codes..." | 208 | @echo "Compiling Moonscript codes..." |
| 209 | @$(START_TIME) | ||
| 209 | @./$(BIN_NAME) $(TEST_INPUT)/*.moon -t $(TEST_OUTPUT) | 210 | @./$(BIN_NAME) $(TEST_INPUT)/*.moon -t $(TEST_OUTPUT) |
| 211 | @echo -en "Compile time: " | ||
| 212 | @$(END_TIME) | ||
| 210 | 213 | ||
| 211 | # Main rule, checks the executable and symlinks to the output | 214 | # Main rule, checks the executable and symlinks to the output |
| 212 | all: $(BIN_PATH)/$(BIN_NAME) | 215 | all: $(BIN_PATH)/$(BIN_NAME) |
diff --git a/moonplus-0.1-1.rockspec b/moonplus-0.1-1.rockspec new file mode 100644 index 0000000..aeebe69 --- /dev/null +++ b/moonplus-0.1-1.rockspec | |||
| @@ -0,0 +1,27 @@ | |||
| 1 | package = "MoonPlus" | ||
| 2 | version = "0.1-1" | ||
| 3 | source = { | ||
| 4 | url = "git+https://github.com/pigpigyyy/MoonPlus" | ||
| 5 | } | ||
| 6 | description = { | ||
| 7 | summary = "MoonPlus is a compiler for Moonscript written in C++.", | ||
| 8 | detailed = [[ | ||
| 9 | MoonPlus is a compiler with features from Moonscript language 0.5.0 and more advanced features. could be faster than the original Moonscript compiler. ]], | ||
| 10 | homepage = "https://github.com/pigpigyyy/MoonPlus", | ||
| 11 | license = "MIT" | ||
| 12 | } | ||
| 13 | dependencies = { | ||
| 14 | "lua >= 5.1", | ||
| 15 | } | ||
| 16 | build = { | ||
| 17 | type = "cmake", | ||
| 18 | variables = { | ||
| 19 | LUA_INCDIR="$(LUA_INCDIR)", | ||
| 20 | LUA="$(LUA)", | ||
| 21 | }, | ||
| 22 | install = { | ||
| 23 | lib = { | ||
| 24 | "build.luarocks/moonp.so" | ||
| 25 | } | ||
| 26 | } | ||
| 27 | } | ||
diff --git a/spec/inputs/plus.moon b/spec/inputs/plus.moon index 8a91c93..f455845 100644 --- a/spec/inputs/plus.moon +++ b/spec/inputs/plus.moon | |||
| @@ -11,5 +11,9 @@ if fcolor = message\match "<%w*>" then message = message\gsub "<%->", fcolor | |||
| 11 | 11 | ||
| 12 | message = message\gsub "<%->", fcolor if fcolor = message\match "<%w*>" | 12 | message = message\gsub "<%->", fcolor if fcolor = message\match "<%w*>" |
| 13 | 13 | ||
| 14 | func val if val = getvalue! | 14 | valA = func! if func = getfunc! |
| 15 | |||
| 16 | valB = do | ||
| 17 | func = getfunc! | ||
| 18 | func?! | ||
| 15 | 19 | ||
diff --git a/spec/inputs/syntax.moon b/spec/inputs/syntax.moon index ae3fc0c..5442b61 100644 --- a/spec/inputs/syntax.moon +++ b/spec/inputs/syntax.moon | |||
| @@ -242,7 +242,15 @@ hello "comma", | |||
| 242 | another hello, one, | 242 | another hello, one, |
| 243 | two, three, four, yeah: man | 243 | two, three, four, yeah: man |
| 244 | okay: yeah | 244 | okay: yeah |
| 245 | fine: alright | ||
| 245 | 246 | ||
| 247 | another hello, one, two, three, four, | ||
| 248 | yeah: man | ||
| 249 | okay: yeah | ||
| 250 | |||
| 251 | another hello, one, two, three, four, yeah: man | ||
| 252 | okay: yeah | ||
| 253 | |||
| 246 | -- | 254 | -- |
| 247 | a += 3 - 5 | 255 | a += 3 - 5 |
| 248 | a *= 3 + 5 | 256 | a *= 3 + 5 |
diff --git a/src/MoonP/moon_compiler.cpp b/src/MoonP/moon_compiler.cpp index d3427d3..30adbae 100644 --- a/src/MoonP/moon_compiler.cpp +++ b/src/MoonP/moon_compiler.cpp | |||
| @@ -690,7 +690,7 @@ private: | |||
| 690 | break; | 690 | break; |
| 691 | } | 691 | } |
| 692 | } | 692 | } |
| 693 | throw std::logic_error(_info.errorMessage("Expression list must appear at the end of body block."sv, expList)); | 693 | throw std::logic_error(_info.errorMessage("Expression list must appear at the end of body or block."sv, expList)); |
| 694 | } | 694 | } |
| 695 | break; | 695 | break; |
| 696 | } | 696 | } |
| @@ -1252,26 +1252,31 @@ private: | |||
| 1252 | bool oneLined = defs.size() == expList->exprs.objects().size() && | 1252 | bool oneLined = defs.size() == expList->exprs.objects().size() && |
| 1253 | traversal::Stop != action->traverse([&](ast_node* n) { | 1253 | traversal::Stop != action->traverse([&](ast_node* n) { |
| 1254 | switch (n->getId()) { | 1254 | switch (n->getId()) { |
| 1255 | case "Callable"_id: { | 1255 | case "ChainValue"_id: { |
| 1256 | auto callable = static_cast<Callable_t*>(n); | 1256 | auto chainValue = static_cast<ChainValue_t*>(n); |
| 1257 | switch (callable->item->getId()) { | 1257 | const auto& items = chainValue->items.objects(); |
| 1258 | case "Variable"_id: | 1258 | BLOCK_START |
| 1259 | for (const auto& def : defs) { | 1259 | auto callable = ast_cast<Callable_t>(*items.begin()); |
| 1260 | BREAK_IF(!callable); | ||
| 1261 | auto next = items.begin(); ++next; | ||
| 1262 | BREAK_IF(next == items.end()); | ||
| 1263 | BREAK_IF((!ast_is<Invoke_t,InvokeArgs_t>(*next))); | ||
| 1264 | for (const auto& def : defs) { | ||
| 1265 | switch (callable->item->getId()) { | ||
| 1266 | case "Variable"_id: | ||
| 1260 | if (def == _parser.toString(callable->item)) { | 1267 | if (def == _parser.toString(callable->item)) { |
| 1261 | return traversal::Stop; | 1268 | return traversal::Stop; |
| 1262 | } | 1269 | } |
| 1263 | } | 1270 | return traversal::Return; |
| 1264 | return traversal::Return; | 1271 | case "SelfName"_id: |
| 1265 | case "SelfName"_id: | ||
| 1266 | for (const auto& def : defs) { | ||
| 1267 | if (def == "self"sv) { | 1272 | if (def == "self"sv) { |
| 1268 | return traversal::Stop; | 1273 | return traversal::Stop; |
| 1269 | } | 1274 | } |
| 1270 | } | 1275 | return traversal::Return; |
| 1271 | return traversal::Return; | 1276 | } |
| 1272 | default: | ||
| 1273 | return traversal::Continue; | ||
| 1274 | } | 1277 | } |
| 1278 | BLOCK_END | ||
| 1279 | return traversal::Continue; | ||
| 1275 | } | 1280 | } |
| 1276 | default: | 1281 | default: |
| 1277 | return traversal::Continue; | 1282 | return traversal::Continue; |
diff --git a/src/MoonPlus.h b/src/MoonPlus.h new file mode 100644 index 0000000..da5f3ac --- /dev/null +++ b/src/MoonPlus.h | |||
| @@ -0,0 +1,125 @@ | |||
| 1 | R"moonscript_codes( | ||
| 2 | import "moonp" | ||
| 3 | import concat, insert, remove from table | ||
| 4 | unpack = unpack or table.unpack | ||
| 5 | lua = :loadstring, :load | ||
| 6 | |||
| 7 | local * | ||
| 8 | |||
| 9 | dirsep = "/" | ||
| 10 | |||
| 11 | moonp.moon_compiled = {} | ||
| 12 | |||
| 13 | split = (str, delim) -> | ||
| 14 | return {} if str == "" | ||
| 15 | str ..= delim | ||
| 16 | [m for m in str\gmatch("(.-)"..delim)] | ||
| 17 | |||
| 18 | get_options = (...) -> | ||
| 19 | count = select "#", ... | ||
| 20 | opts = select count, ... | ||
| 21 | if type(opts) == "table" | ||
| 22 | opts, unpack {...}, nil, count - 1 | ||
| 23 | else | ||
| 24 | {}, ... | ||
| 25 | |||
| 26 | -- create moon path package from lua package path | ||
| 27 | create_moonpath = (package_path) -> | ||
| 28 | moonpaths = for path in *split package_path, ";" | ||
| 29 | prefix = path\match "^(.-)%.lua$" | ||
| 30 | continue unless prefix | ||
| 31 | prefix .. ".moon" | ||
| 32 | concat moonpaths, ";" | ||
| 33 | |||
| 34 | moon_loader = (name) -> | ||
| 35 | name_path = name\gsub "%.", dirsep | ||
| 36 | |||
| 37 | local file, file_path | ||
| 38 | for path in package.moonpath\gmatch "[^;]+" | ||
| 39 | file_path = path\gsub "?", name_path | ||
| 40 | file = io.open file_path | ||
| 41 | break if file | ||
| 42 | |||
| 43 | if file | ||
| 44 | text = file\read "*a" | ||
| 45 | file\close! | ||
| 46 | res, err = loadstring text, "@#{file_path}" | ||
| 47 | if not res | ||
| 48 | error file_path .. ": " .. err | ||
| 49 | |||
| 50 | return res | ||
| 51 | |||
| 52 | return nil, "Could not find moon file" | ||
| 53 | |||
| 54 | |||
| 55 | loadstring = (...) -> | ||
| 56 | options, str, chunk_name, mode, env = get_options ... | ||
| 57 | chunk_name or= "=(moonscript.loadstring)" | ||
| 58 | |||
| 59 | code, err = moonp.to_lua str, options | ||
| 60 | unless code | ||
| 61 | return nil, err | ||
| 62 | |||
| 63 | moonp.moon_compiled[chunk_name] = code if chunk_name | ||
| 64 | -- the unpack prevents us from passing nil | ||
| 65 | (lua.loadstring or lua.load) code, chunk_name, unpack { mode, env } | ||
| 66 | |||
| 67 | loadfile = (fname, ...) -> | ||
| 68 | file, err = io.open fname | ||
| 69 | return nil, err unless file | ||
| 70 | text = assert file\read "*a" | ||
| 71 | file\close! | ||
| 72 | loadstring text, "@#{fname}", ... | ||
| 73 | |||
| 74 | -- throws errros | ||
| 75 | dofile = (...) -> | ||
| 76 | f = assert loadfile ... | ||
| 77 | f! | ||
| 78 | |||
| 79 | insert_loader = (pos=2) -> | ||
| 80 | if not package.moonpath | ||
| 81 | package.moonpath = create_moonpath package.path | ||
| 82 | |||
| 83 | loaders = package.loaders or package.searchers | ||
| 84 | for loader in *loaders | ||
| 85 | return false if loader == moon_loader | ||
| 86 | |||
| 87 | insert loaders, pos, moon_loader | ||
| 88 | true | ||
| 89 | |||
| 90 | remove_loader = -> | ||
| 91 | loaders = package.loaders or package.searchers | ||
| 92 | |||
| 93 | for i, loader in ipairs loaders | ||
| 94 | if loader == moon_loader | ||
| 95 | remove loaders, i | ||
| 96 | return true | ||
| 97 | |||
| 98 | false | ||
| 99 | |||
| 100 | moon_require = (name)-> | ||
| 101 | insert_loader! | ||
| 102 | xpcall (-> require name), (err)-> | ||
| 103 | msg = moonp.stp.stacktrace err, 2 | ||
| 104 | print msg | ||
| 105 | |||
| 106 | setmetatable moonp, { | ||
| 107 | __index: (key)=> | ||
| 108 | return nil unless key == "stp" | ||
| 109 | stp = rawget moonp,"stp" | ||
| 110 | unless stp | ||
| 111 | stp = with moonp.load_stacktraceplus! | ||
| 112 | .dump_locals = false | ||
| 113 | .simplified = true | ||
| 114 | rawset moonp,"stp",stp | ||
| 115 | rawset moonp,"load_stacktraceplus",nil | ||
| 116 | stp | ||
| 117 | __call: (name)=> @.require name | ||
| 118 | } | ||
| 119 | |||
| 120 | moonp[k] = v for k,v in pairs { | ||
| 121 | :insert_loader, :remove_loader, :moon_loader, :dirsep, | ||
| 122 | :dofile, :loadfile, :loadstring, :create_moonpath, | ||
| 123 | require:moon_require | ||
| 124 | } | ||
| 125 | )moonscript_codes"; | ||
diff --git a/src/StackTracePlus.h b/src/StackTracePlus.h new file mode 100644 index 0000000..1a909fd --- /dev/null +++ b/src/StackTracePlus.h | |||
| @@ -0,0 +1,550 @@ | |||
| 1 | R"lua_codes( | ||
| 2 | --[[ | ||
| 3 | The MIT License | ||
| 4 | |||
| 5 | Copyright (c) 2010 Ignacio Burgueño | ||
| 6 | |||
| 7 | Permission is hereby granted, free of charge, to any person obtaining a copy | ||
| 8 | of this software and associated documentation files (the "Software"), to deal | ||
| 9 | in the Software without restriction, including without limitation the rights | ||
| 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
| 11 | copies of the Software, and to permit persons to whom the Software is | ||
| 12 | furnished to do so, subject to the following conditions: | ||
| 13 | |||
| 14 | The above copyright notice and this permission notice shall be included in | ||
| 15 | all copies or substantial portions of the Software. | ||
| 16 | |||
| 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
| 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
| 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
| 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
| 23 | THE SOFTWARE.]] | ||
| 24 | |||
| 25 | -- tables | ||
| 26 | local _G = _G | ||
| 27 | local string, io, debug, coroutine = string, io, debug, coroutine | ||
| 28 | |||
| 29 | -- functions | ||
| 30 | local tostring, require = tostring, require | ||
| 31 | local next, assert = next, assert | ||
| 32 | local pcall, type, pairs, ipairs = pcall, type, pairs, ipairs | ||
| 33 | local error = error | ||
| 34 | |||
| 35 | assert(debug, "debug table must be available at this point") | ||
| 36 | |||
| 37 | local string_gmatch = string.gmatch | ||
| 38 | local string_sub = string.sub | ||
| 39 | local table_concat = table.concat | ||
| 40 | |||
| 41 | local _M = { | ||
| 42 | max_tb_output_len = 70, -- controls the maximum length of the 'stringified' table before cutting with ' (more...)' | ||
| 43 | dump_locals = true, | ||
| 44 | simplified = false | ||
| 45 | } | ||
| 46 | |||
| 47 | -- this tables should be weak so the elements in them won't become uncollectable | ||
| 48 | local m_known_tables = { [_G] = "_G (global table)" } | ||
| 49 | local function add_known_module(name, desc) | ||
| 50 | local ok, mod = pcall(require, name) | ||
| 51 | if ok then | ||
| 52 | m_known_tables[mod] = desc | ||
| 53 | end | ||
| 54 | end | ||
| 55 | |||
| 56 | add_known_module("string", "string module") | ||
| 57 | add_known_module("io", "io module") | ||
| 58 | add_known_module("os", "os module") | ||
| 59 | add_known_module("table", "table module") | ||
| 60 | add_known_module("math", "math module") | ||
| 61 | add_known_module("package", "package module") | ||
| 62 | add_known_module("debug", "debug module") | ||
| 63 | add_known_module("coroutine", "coroutine module") | ||
| 64 | |||
| 65 | -- lua5.2 | ||
| 66 | add_known_module("bit32", "bit32 module") | ||
| 67 | -- luajit | ||
| 68 | add_known_module("bit", "bit module") | ||
| 69 | add_known_module("jit", "jit module") | ||
| 70 | -- lua5.3 | ||
| 71 | if _VERSION >= "Lua 5.3" then | ||
| 72 | add_known_module("utf8", "utf8 module") | ||
| 73 | end | ||
| 74 | |||
| 75 | |||
| 76 | local m_user_known_tables = {} | ||
| 77 | |||
| 78 | local m_known_functions = {} | ||
| 79 | for _, name in ipairs{ | ||
| 80 | -- Lua 5.2, 5.1 | ||
| 81 | "assert", | ||
| 82 | "collectgarbage", | ||
| 83 | "dofile", | ||
| 84 | "error", | ||
| 85 | "getmetatable", | ||
| 86 | "ipairs", | ||
| 87 | "load", | ||
| 88 | "loadfile", | ||
| 89 | "next", | ||
| 90 | "pairs", | ||
| 91 | "pcall", | ||
| 92 | "print", | ||
| 93 | "rawequal", | ||
| 94 | "rawget", | ||
| 95 | "rawlen", | ||
| 96 | "rawset", | ||
| 97 | "require", | ||
| 98 | "select", | ||
| 99 | "setmetatable", | ||
| 100 | "tonumber", | ||
| 101 | "tostring", | ||
| 102 | "type", | ||
| 103 | "xpcall", | ||
| 104 | |||
| 105 | -- Lua 5.1 | ||
| 106 | "gcinfo", | ||
| 107 | "getfenv", | ||
| 108 | "loadstring", | ||
| 109 | "module", | ||
| 110 | "newproxy", | ||
| 111 | "setfenv", | ||
| 112 | "unpack", | ||
| 113 | -- TODO: add table.* etc functions | ||
| 114 | } do | ||
| 115 | if _G[name] then | ||
| 116 | m_known_functions[_G[name]] = name | ||
| 117 | end | ||
| 118 | end | ||
| 119 | |||
| 120 | local m_user_known_functions = {} | ||
| 121 | |||
| 122 | local function safe_tostring (value) | ||
| 123 | local ok, err = pcall(tostring, value) | ||
| 124 | if ok then return err else return ("<failed to get printable value>: '%s'"):format(err) end | ||
| 125 | end | ||
| 126 | |||
| 127 | -- Private: | ||
| 128 | -- Parses a line, looking for possible function definitions (in a very naive way) | ||
| 129 | -- Returns '(anonymous)' if no function name was found in the line | ||
| 130 | local function ParseLine(line) | ||
| 131 | assert(type(line) == "string") | ||
| 132 | local match = line:match("^%s*function%s+(%w+)") | ||
| 133 | if match then | ||
| 134 | --print("+++++++++++++function", match) | ||
| 135 | return match | ||
| 136 | end | ||
| 137 | match = line:match("^%s*local%s+function%s+(%w+)") | ||
| 138 | if match then | ||
| 139 | --print("++++++++++++local", match) | ||
| 140 | return match | ||
| 141 | end | ||
| 142 | match = line:match("^%s*local%s+(%w+)%s+=%s+function") | ||
| 143 | if match then | ||
| 144 | --print("++++++++++++local func", match) | ||
| 145 | return match | ||
| 146 | end | ||
| 147 | match = line:match("%s*function%s*%(") -- this is an anonymous function | ||
| 148 | if match then | ||
| 149 | --print("+++++++++++++function2", match) | ||
| 150 | return "(anonymous)" | ||
| 151 | end | ||
| 152 | return "(anonymous)" | ||
| 153 | end | ||
| 154 | |||
| 155 | -- Private: | ||
| 156 | -- Tries to guess a function's name when the debug info structure does not have it. | ||
| 157 | -- It parses either the file or the string where the function is defined. | ||
| 158 | -- Returns '?' if the line where the function is defined is not found | ||
| 159 | local function GuessFunctionName(info) | ||
| 160 | -- print("guessing function name") | ||
| 161 | if type(info.source) == "string" and info.source:sub(1,1) == "@" then | ||
| 162 | local file = io.open(info.source:sub(2)) | ||
| 163 | local text | ||
| 164 | if file then | ||
| 165 | text = file:read("*a") | ||
| 166 | file:close() | ||
| 167 | end | ||
| 168 | if not text then | ||
| 169 | -- print("file not found: "..tostring(err)) -- whoops! | ||
| 170 | return "?" | ||
| 171 | end | ||
| 172 | local line | ||
| 173 | local count = 0 | ||
| 174 | for lineText in (text.."\n"):gmatch("(.-)\n") do | ||
| 175 | line = lineText | ||
| 176 | count = count + 1 | ||
| 177 | if count == info.linedefined then | ||
| 178 | break | ||
| 179 | end | ||
| 180 | end | ||
| 181 | if not line then | ||
| 182 | --print("line not found") -- whoops! | ||
| 183 | return "?" | ||
| 184 | end | ||
| 185 | return ParseLine(line) | ||
| 186 | else | ||
| 187 | local line | ||
| 188 | local lineNumber = 0 | ||
| 189 | for l in string_gmatch(info.source, "([^\n]+)\n-") do | ||
| 190 | lineNumber = lineNumber + 1 | ||
| 191 | if lineNumber == info.linedefined then | ||
| 192 | line = l | ||
| 193 | break | ||
| 194 | end | ||
| 195 | end | ||
| 196 | if not line then | ||
| 197 | -- print("line not found") -- whoops! | ||
| 198 | return "?" | ||
| 199 | end | ||
| 200 | return ParseLine(line) | ||
| 201 | end | ||
| 202 | end | ||
| 203 | |||
| 204 | --- | ||
| 205 | -- Dumper instances are used to analyze stacks and collect its information. | ||
| 206 | -- | ||
| 207 | local Dumper = {} | ||
| 208 | |||
| 209 | Dumper.new = function(thread) | ||
| 210 | local t = { lines = {} } | ||
| 211 | for k,v in pairs(Dumper) do t[k] = v end | ||
| 212 | |||
| 213 | t.dumping_same_thread = (thread == coroutine.running()) | ||
| 214 | |||
| 215 | -- if a thread was supplied, bind it to debug.info and debug.get | ||
| 216 | -- we also need to skip this additional level we are introducing in the callstack (only if we are running | ||
| 217 | -- in the same thread we're inspecting) | ||
| 218 | if type(thread) == "thread" then | ||
| 219 | t.getinfo = function(level, what) | ||
| 220 | if t.dumping_same_thread and type(level) == "number" then | ||
| 221 | level = level + 1 | ||
| 222 | end | ||
| 223 | return debug.getinfo(thread, level, what) | ||
| 224 | end | ||
| 225 | t.getlocal = function(level, loc) | ||
| 226 | if t.dumping_same_thread then | ||
| 227 | level = level + 1 | ||
| 228 | end | ||
| 229 | return debug.getlocal(thread, level, loc) | ||
| 230 | end | ||
| 231 | else | ||
| 232 | t.getinfo = debug.getinfo | ||
| 233 | t.getlocal = debug.getlocal | ||
| 234 | end | ||
| 235 | |||
| 236 | return t | ||
| 237 | end | ||
| 238 | |||
| 239 | -- helpers for collecting strings to be used when assembling the final trace | ||
| 240 | function Dumper:add (text) | ||
| 241 | self.lines[#self.lines + 1] = text | ||
| 242 | end | ||
| 243 | function Dumper:add_f (fmt, ...) | ||
| 244 | self:add(fmt:format(...)) | ||
| 245 | end | ||
| 246 | function Dumper:concat_lines () | ||
| 247 | return table_concat(self.lines) | ||
| 248 | end | ||
| 249 | |||
| 250 | --- | ||
| 251 | -- Private: | ||
| 252 | -- Iterates over the local variables of a given function. | ||
| 253 | -- | ||
| 254 | -- @param level The stack level where the function is. | ||
| 255 | -- | ||
| 256 | function Dumper:DumpLocals (level) | ||
| 257 | if not _M.dump_locals then return end | ||
| 258 | |||
| 259 | local prefix = "\t " | ||
| 260 | local i = 1 | ||
| 261 | |||
| 262 | if self.dumping_same_thread then | ||
| 263 | level = level + 1 | ||
| 264 | end | ||
| 265 | |||
| 266 | local name, value = self.getlocal(level, i) | ||
| 267 | if not name then | ||
| 268 | return | ||
| 269 | end | ||
| 270 | self:add("\tLocal variables:\r\n") | ||
| 271 | while name do | ||
| 272 | if type(value) == "number" then | ||
| 273 | self:add_f("%s%s = number: %g\r\n", prefix, name, value) | ||
| 274 | elseif type(value) == "boolean" then | ||
| 275 | self:add_f("%s%s = boolean: %s\r\n", prefix, name, tostring(value)) | ||
| 276 | elseif type(value) == "string" then | ||
| 277 | self:add_f("%s%s = string: %q\r\n", prefix, name, value) | ||
| 278 | elseif type(value) == "userdata" then | ||
| 279 | self:add_f("%s%s = %s\r\n", prefix, name, safe_tostring(value)) | ||
| 280 | elseif type(value) == "nil" then | ||
| 281 | self:add_f("%s%s = nil\r\n", prefix, name) | ||
| 282 | elseif type(value) == "table" then | ||
| 283 | if m_known_tables[value] then | ||
| 284 | self:add_f("%s%s = %s\r\n", prefix, name, m_known_tables[value]) | ||
| 285 | elseif m_user_known_tables[value] then | ||
| 286 | self:add_f("%s%s = %s\r\n", prefix, name, m_user_known_tables[value]) | ||
| 287 | else | ||
| 288 | local txt = "{" | ||
| 289 | for k,v in pairs(value) do | ||
| 290 | txt = txt..safe_tostring(k)..":"..safe_tostring(v) | ||
| 291 | if #txt > _M.max_tb_output_len then | ||
| 292 | txt = txt.." (more...)" | ||
| 293 | break | ||
| 294 | end | ||
| 295 | if next(value, k) then txt = txt..", " end | ||
| 296 | end | ||
| 297 | self:add_f("%s%s = %s %s\r\n", prefix, name, safe_tostring(value), txt.."}") | ||
| 298 | end | ||
| 299 | elseif type(value) == "function" then | ||
| 300 | local info = self.getinfo(value, "nS") | ||
| 301 | local fun_name = info.name or m_known_functions[value] or m_user_known_functions[value] | ||
| 302 | if info.what == "C" then | ||
| 303 | self:add_f("%s%s = C %s\r\n", prefix, name, (fun_name and ("function: " .. fun_name) or tostring(value))) | ||
| 304 | else | ||
| 305 | local source = info.short_src | ||
| 306 | if source:sub(2,7) == "string" then | ||
| 307 | source = source:sub(9) | ||
| 308 | end | ||
| 309 | --for k,v in pairs(info) do print(k,v) end | ||
| 310 | fun_name = fun_name or GuessFunctionName(info) | ||
| 311 | self:add_f("%s%s = Lua function '%s' (defined at line %d of chunk %s)\r\n", prefix, name, fun_name, info.linedefined, source) | ||
| 312 | end | ||
| 313 | elseif type(value) == "thread" then | ||
| 314 | self:add_f("%sthread %q = %s\r\n", prefix, name, tostring(value)) | ||
| 315 | end | ||
| 316 | i = i + 1 | ||
| 317 | name, value = self.getlocal(level, i) | ||
| 318 | end | ||
| 319 | end | ||
| 320 | |||
| 321 | local function getMoonLineNumber(fname, line) | ||
| 322 | local moonCompiled = require("moonp").moon_compiled | ||
| 323 | local source = moonCompiled["@"..fname] | ||
| 324 | local file = io.open(fname) | ||
| 325 | if not source and file then | ||
| 326 | local codes = file:read("*a") | ||
| 327 | file:close() | ||
| 328 | local moonFile = codes:match("^%s*--%s*%[moon%]:%s*([^\n]*)") | ||
| 329 | if moonFile then | ||
| 330 | fname = moonFile:gsub("^%s*(.-)%s*$", "%1") | ||
| 331 | source = codes | ||
| 332 | end | ||
| 333 | end | ||
| 334 | if source then | ||
| 335 | local i, target = 1, tonumber(line) | ||
| 336 | for lineCode in source:gmatch("([^\n]*)\n") do | ||
| 337 | if i == target then | ||
| 338 | local num = lineCode:match("--%s*(%d*)%s*$") | ||
| 339 | if num then | ||
| 340 | return fname, num | ||
| 341 | end | ||
| 342 | break | ||
| 343 | end | ||
| 344 | i = i + 1 | ||
| 345 | end | ||
| 346 | end | ||
| 347 | return fname, line | ||
| 348 | end | ||
| 349 | |||
| 350 | --- | ||
| 351 | -- Public: | ||
| 352 | -- Collects a detailed stack trace, dumping locals, resolving function names when they're not available, etc. | ||
| 353 | -- This function is suitable to be used as an error handler with pcall or xpcall | ||
| 354 | -- | ||
| 355 | -- @param thread An optional thread whose stack is to be inspected (defaul is the current thread) | ||
| 356 | -- @param message An optional error string or object. | ||
| 357 | -- @param level An optional number telling at which level to start the traceback (default is 1) | ||
| 358 | -- | ||
| 359 | -- Returns a string with the stack trace and a string with the original error. | ||
| 360 | -- | ||
| 361 | function _M.stacktrace(thread, message, level) | ||
| 362 | if type(thread) ~= "thread" then | ||
| 363 | -- shift parameters left | ||
| 364 | thread, message, level = nil, thread, message | ||
| 365 | end | ||
| 366 | |||
| 367 | thread = thread or coroutine.running() | ||
| 368 | |||
| 369 | level = level or 1 | ||
| 370 | |||
| 371 | local dumper = Dumper.new(thread) | ||
| 372 | |||
| 373 | local original_error | ||
| 374 | |||
| 375 | if type(message) == "table" then | ||
| 376 | dumper:add("an error object {\r\n") | ||
| 377 | local first = true | ||
| 378 | for k,v in pairs(message) do | ||
| 379 | if first then | ||
| 380 | dumper:add(" ") | ||
| 381 | first = false | ||
| 382 | else | ||
| 383 | dumper:add(",\r\n ") | ||
| 384 | end | ||
| 385 | dumper:add(safe_tostring(k)) | ||
| 386 | dumper:add(": ") | ||
| 387 | dumper:add(safe_tostring(v)) | ||
| 388 | end | ||
| 389 | dumper:add("\r\n}") | ||
| 390 | original_error = dumper:concat_lines() | ||
| 391 | elseif type(message) == "string" then | ||
| 392 | original_error = message | ||
| 393 | local fname, line, msg = message:match('(.+):(%d+): (.*)$') | ||
| 394 | local nfname, nline, nmsg = fname:match('(.+):(%d+): (.*)$') | ||
| 395 | if nfname then | ||
| 396 | fname = nmsg | ||
| 397 | end | ||
| 398 | if fname then | ||
| 399 | fname = fname:gsub("%[string \"", "") | ||
| 400 | fname = fname:gsub("\"%]", "") | ||
| 401 | fname = fname:gsub("^%s*(.-)%s*$", "%1") | ||
| 402 | local extension = fname:match("%.([^%.\\/]*)$") | ||
| 403 | if not extension then | ||
| 404 | local fext = fname .. ".lua" | ||
| 405 | local file = io.open(fext) | ||
| 406 | if file then | ||
| 407 | file:close() | ||
| 408 | fname = fext | ||
| 409 | else | ||
| 410 | fext = fname .. ".moon" | ||
| 411 | file = io.open(fext) | ||
| 412 | if file then | ||
| 413 | file:close() | ||
| 414 | fname = fext | ||
| 415 | end | ||
| 416 | end | ||
| 417 | end | ||
| 418 | fname, line = getMoonLineNumber(fname, line) | ||
| 419 | if _M.simplified then | ||
| 420 | message = table.concat({ | ||
| 421 | "'", fname, "':", | ||
| 422 | line, ": ", msg}) | ||
| 423 | else | ||
| 424 | message = table.concat({ | ||
| 425 | "[string \"", fname, "\"]:", | ||
| 426 | line, ": ", msg}) | ||
| 427 | end | ||
| 428 | end | ||
| 429 | dumper:add(message) | ||
| 430 | end | ||
| 431 | |||
| 432 | dumper:add("\r\n") | ||
| 433 | dumper:add[[ | ||
| 434 | Stack Traceback | ||
| 435 | =============== | ||
| 436 | ]] | ||
| 437 | --print(error_message) | ||
| 438 | |||
| 439 | local level_to_show = 1 | ||
| 440 | if dumper.dumping_same_thread then level = level + 1 end | ||
| 441 | |||
| 442 | local info = dumper.getinfo(level, "nSlf") | ||
| 443 | while info do | ||
| 444 | if info.source and info.source:sub(1,1) == "@" then | ||
| 445 | info.source = info.source:sub(2) | ||
| 446 | elseif info.what == "main" or info.what == "Lua" then | ||
| 447 | info.source = info.source | ||
| 448 | end | ||
| 449 | local fname = info.source | ||
| 450 | local extension = fname:match("%.([^%.\\/]*)$") | ||
| 451 | if not extension then | ||
| 452 | local fext = fname .. ".lua" | ||
| 453 | local file = io.open(fext) | ||
| 454 | if file then | ||
| 455 | file:close() | ||
| 456 | fname = fext | ||
| 457 | else | ||
| 458 | fext = fname .. ".moon" | ||
| 459 | file = io.open(fext) | ||
| 460 | if file then | ||
| 461 | file:close() | ||
| 462 | fname = fext | ||
| 463 | end | ||
| 464 | end | ||
| 465 | end | ||
| 466 | info.source, info.currentline = getMoonLineNumber(fname, info.currentline) | ||
| 467 | if info.what == "main" then | ||
| 468 | if _M.simplified then | ||
| 469 | dumper:add_f("(%d) '%s':%d\r\n", level_to_show, info.source, info.currentline) | ||
| 470 | else | ||
| 471 | dumper:add_f("(%d) main chunk of file '%s' at line %d\r\n", level_to_show, info.source, info.currentline) | ||
| 472 | end | ||
| 473 | elseif info.what == "C" then | ||
| 474 | --print(info.namewhat, info.name) | ||
| 475 | --for k,v in pairs(info) do print(k,v, type(v)) end | ||
| 476 | local function_name = m_user_known_functions[info.func] or m_known_functions[info.func] or info.name or tostring(info.func) | ||
| 477 | dumper:add_f("(%d) %s C function '%s'\r\n", level_to_show, info.namewhat, function_name) | ||
| 478 | --dumper:add_f("%s%s = C %s\r\n", prefix, name, (m_known_functions[value] and ("function: " .. m_known_functions[value]) or tostring(value))) | ||
| 479 | elseif info.what == "tail" then | ||
| 480 | --print("tail") | ||
| 481 | --for k,v in pairs(info) do print(k,v, type(v)) end--print(info.namewhat, info.name) | ||
| 482 | dumper:add_f("(%d) tail call\r\n", level_to_show) | ||
| 483 | dumper:DumpLocals(level) | ||
| 484 | elseif info.what == "Lua" then | ||
| 485 | local source = info.source | ||
| 486 | local function_name = m_user_known_functions[info.func] or m_known_functions[info.func] or info.name | ||
| 487 | if source:sub(2, 7) == "string" then | ||
| 488 | source = source:sub(10,-3) | ||
| 489 | end | ||
| 490 | local was_guessed = false | ||
| 491 | if not function_name or function_name == "?" then | ||
| 492 | --for k,v in pairs(info) do print(k,v, type(v)) end | ||
| 493 | function_name = GuessFunctionName(info) | ||
| 494 | was_guessed = true | ||
| 495 | end | ||
| 496 | -- test if we have a file name | ||
| 497 | local function_type = (info.namewhat == "") and "function" or info.namewhat | ||
| 498 | if info.source and info.source:sub(1, 1) == "@" then | ||
| 499 | if _M.simplified then | ||
| 500 | dumper:add_f("(%d) '%s':%d%s\r\n", level_to_show, info.source:sub(2), info.currentline, was_guessed and " (guess)" or "") | ||
| 501 | else | ||
| 502 | dumper:add_f("(%d) Lua %s '%s' at file '%s':%d%s\r\n", level_to_show, function_type, function_name, info.source:sub(2), info.currentline, was_guessed and " (best guess)" or "") | ||
| 503 | end | ||
| 504 | elseif info.source and info.source:sub(1,1) == '#' then | ||
| 505 | if _M.simplified then | ||
| 506 | dumper:add_f("(%d) '%s':%d%s\r\n", level_to_show, info.source:sub(2), info.currentline, was_guessed and " (guess)" or "") | ||
| 507 | else | ||
| 508 | dumper:add_f("(%d) Lua %s '%s' at template '%s':%d%s\r\n", level_to_show, function_type, function_name, info.source:sub(2), info.currentline, was_guessed and " (best guess)" or "") | ||
| 509 | end | ||
| 510 | else | ||
| 511 | if _M.simplified then | ||
| 512 | dumper:add_f("(%d) '%s':%d\r\n", level_to_show, source, info.currentline) | ||
| 513 | else | ||
| 514 | dumper:add_f("(%d) Lua %s '%s' at chunk '%s':%d\r\n", level_to_show, function_type, function_name, source, info.currentline) | ||
| 515 | end | ||
| 516 | end | ||
| 517 | dumper:DumpLocals(level) | ||
| 518 | else | ||
| 519 | dumper:add_f("(%d) unknown frame %s\r\n", level_to_show, info.what) | ||
| 520 | end | ||
| 521 | |||
| 522 | level = level + 1 | ||
| 523 | level_to_show = level_to_show + 1 | ||
| 524 | info = dumper.getinfo(level, "nSlf") | ||
| 525 | end | ||
| 526 | |||
| 527 | return dumper:concat_lines(), original_error | ||
| 528 | end | ||
| 529 | |||
| 530 | -- | ||
| 531 | -- Adds a table to the list of known tables | ||
| 532 | function _M.add_known_table(tab, description) | ||
| 533 | if m_known_tables[tab] then | ||
| 534 | error("Cannot override an already known table") | ||
| 535 | end | ||
| 536 | m_user_known_tables[tab] = description | ||
| 537 | end | ||
| 538 | |||
| 539 | -- | ||
| 540 | -- Adds a function to the list of known functions | ||
| 541 | function _M.add_known_function(fun, description) | ||
| 542 | if m_known_functions[fun] then | ||
| 543 | error("Cannot override an already known function") | ||
| 544 | end | ||
| 545 | m_user_known_functions[fun] = description | ||
| 546 | end | ||
| 547 | |||
| 548 | return _M | ||
| 549 | |||
| 550 | )lua_codes"; | ||
diff --git a/src/moonc.cpp b/src/moonp.cpp index d04ce47..a6c99df 100644 --- a/src/moonc.cpp +++ b/src/moonp.cpp | |||
| @@ -13,9 +13,11 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI | |||
| 13 | #include "MoonP/moon_compiler.h" | 13 | #include "MoonP/moon_compiler.h" |
| 14 | #include "MoonP/moon_parser.h" | 14 | #include "MoonP/moon_parser.h" |
| 15 | 15 | ||
| 16 | #ifndef LIBMOONP | ||
| 17 | |||
| 16 | int main(int narg, const char** args) { | 18 | int main(int narg, const char** args) { |
| 17 | const char* help = | 19 | const char* help = |
| 18 | "Usage: moonc [options|files] ...\n\n" | 20 | "Usage: moonp [options|files] ...\n\n" |
| 19 | " -h Print this message\n" | 21 | " -h Print this message\n" |
| 20 | " -t path Specify where to place compiled files\n" | 22 | " -t path Specify where to place compiled files\n" |
| 21 | " -o file Write output to file\n" | 23 | " -o file Write output to file\n" |
| @@ -82,7 +84,7 @@ int main(int narg, const char** args) { | |||
| 82 | std::cout << "Error: -o can not be used with multiple input files.\n"; | 84 | std::cout << "Error: -o can not be used with multiple input files.\n"; |
| 83 | std::cout << help; | 85 | std::cout << help; |
| 84 | } | 86 | } |
| 85 | if (targetPath.back() != '/' && targetPath.back() != '\\') { | 87 | if (!targetPath.empty() && targetPath.back() != '/' && targetPath.back() != '\\') { |
| 86 | targetPath.append("/"); | 88 | targetPath.append("/"); |
| 87 | } | 89 | } |
| 88 | std::list<std::future<std::result_of_t<std::decay_t<int()>()>>> results; | 90 | std::list<std::future<std::result_of_t<std::decay_t<int()>()>>> results; |
| @@ -121,14 +123,9 @@ int main(int narg, const char** args) { | |||
| 121 | } else { | 123 | } else { |
| 122 | std::string targetFile; | 124 | std::string targetFile; |
| 123 | if (resultFile.empty()) { | 125 | if (resultFile.empty()) { |
| 124 | std::string ext; | ||
| 125 | targetFile = file; | 126 | targetFile = file; |
| 126 | size_t pos = file.rfind('.'); | 127 | size_t pos = file.rfind('.'); |
| 127 | if (pos != std::string::npos) { | 128 | if (pos != std::string::npos) { |
| 128 | ext = file.substr(pos + 1); | ||
| 129 | for (size_t i = 0; i < ext.length(); i++) { | ||
| 130 | ext[i] = static_cast<char>(tolower(ext[i])); | ||
| 131 | } | ||
| 132 | targetFile = file.substr(0, pos) + ".lua"; | 129 | targetFile = file.substr(0, pos) + ".lua"; |
| 133 | } | 130 | } |
| 134 | if (!targetPath.empty()) { | 131 | if (!targetPath.empty()) { |
| @@ -177,3 +174,120 @@ int main(int narg, const char** args) { | |||
| 177 | return ret; | 174 | return ret; |
| 178 | } | 175 | } |
| 179 | 176 | ||
| 177 | #else | ||
| 178 | |||
| 179 | extern "C" { | ||
| 180 | |||
| 181 | #include "lua.h" | ||
| 182 | #include "lauxlib.h" | ||
| 183 | |||
| 184 | static const char moonplusCodes[] = | ||
| 185 | #include "MoonPlus.h" | ||
| 186 | |||
| 187 | static int init_moonplus(lua_State* L) { | ||
| 188 | MoonP::MoonConfig config; | ||
| 189 | std::string s(moonplusCodes, sizeof(moonplusCodes) / sizeof(moonplusCodes[0]) - 1); | ||
| 190 | std::string codes, err; | ||
| 191 | MoonP::GlobalVars globals; | ||
| 192 | std::tie(codes, err, globals) = MoonP::moonCompile(s, config); | ||
| 193 | if (codes.empty()) { | ||
| 194 | luaL_error(L, "fail to compile moonplus init codes.\n%s", err.c_str()); | ||
| 195 | } | ||
| 196 | int top = lua_gettop(L); | ||
| 197 | if (luaL_loadbuffer(L, codes.c_str(), codes.size(), "=(moonplus)") != 0) { | ||
| 198 | luaL_error(L, "fail to init moonplus module."); | ||
| 199 | } else { | ||
| 200 | lua_call(L, 0, 0); | ||
| 201 | } | ||
| 202 | lua_settop(L, top); | ||
| 203 | return 0; | ||
| 204 | } | ||
| 205 | |||
| 206 | static const char stpCodes[] = | ||
| 207 | #include "StackTracePlus.h" | ||
| 208 | |||
| 209 | static int init_stacktraceplus(lua_State* L) { | ||
| 210 | if (luaL_loadbuffer(L, stpCodes, sizeof(stpCodes) / sizeof(stpCodes[0]) - 1, "=(stacktraceplus)") != 0) { | ||
| 211 | luaL_error(L, "fail to init stacktraceplus module."); | ||
| 212 | } else { | ||
| 213 | lua_call(L, 0, 1); | ||
| 214 | } | ||
| 215 | return 1; | ||
| 216 | } | ||
| 217 | |||
| 218 | static int moontolua(lua_State* L) { | ||
| 219 | size_t size = 0; | ||
| 220 | const char* input = luaL_checklstring(L, 1, &size); | ||
| 221 | MoonP::MoonConfig config; | ||
| 222 | if (lua_gettop(L) == 2) { | ||
| 223 | lua_pushstring(L, "lint_global"); | ||
| 224 | lua_gettable(L, -2); | ||
| 225 | if (!lua_isnil(L, -1)) { | ||
| 226 | config.lintGlobalVariable = lua_toboolean(L, -1) != 0; | ||
| 227 | } | ||
| 228 | lua_pop(L, 1); | ||
| 229 | lua_pushstring(L, "implicit_return_root"); | ||
| 230 | lua_gettable(L, -2); | ||
| 231 | if (!lua_isnil(L, -1)) { | ||
| 232 | config.implicitReturnRoot = lua_toboolean(L, -1) != 0; | ||
| 233 | } | ||
| 234 | lua_pop(L, 1); | ||
| 235 | lua_pushstring(L, "reserve_line_number"); | ||
| 236 | lua_gettable(L, -2); | ||
| 237 | if (!lua_isnil(L, -1)) { | ||
| 238 | config.reserveLineNumber = lua_toboolean(L, -1) != 0; | ||
| 239 | } | ||
| 240 | lua_pop(L, 1); | ||
| 241 | } | ||
| 242 | std::string s(input, size); | ||
| 243 | std::string codes, err; | ||
| 244 | MoonP::GlobalVars globals; | ||
| 245 | std::tie(codes, err, globals) = MoonP::moonCompile(s, config); | ||
| 246 | if (codes.empty()) { | ||
| 247 | lua_pushnil(L); | ||
| 248 | } else { | ||
| 249 | lua_pushlstring(L, codes.c_str(), codes.size()); | ||
| 250 | } | ||
| 251 | if (err.empty()) { | ||
| 252 | lua_pushnil(L); | ||
| 253 | } else { | ||
| 254 | lua_pushlstring(L, err.c_str(), err.size()); | ||
| 255 | } | ||
| 256 | if (globals && !globals->empty()) { | ||
| 257 | lua_createtable(L, static_cast<int>(globals->size()), 0); | ||
| 258 | int i = 1; | ||
| 259 | for (const auto& var : *globals) { | ||
| 260 | lua_createtable(L, 3, 0); | ||
| 261 | lua_pushlstring(L, var.name.c_str(), var.name.size()); | ||
| 262 | lua_rawseti(L, -2, 1); | ||
| 263 | lua_pushinteger(L, var.line); | ||
| 264 | lua_rawseti(L, -2, 2); | ||
| 265 | lua_pushinteger(L, var.col); | ||
| 266 | lua_rawseti(L, -2, 3); | ||
| 267 | lua_rawseti(L, -2, i); | ||
| 268 | i++; | ||
| 269 | } | ||
| 270 | } else { | ||
| 271 | lua_pushnil(L); | ||
| 272 | } | ||
| 273 | return 3; | ||
| 274 | } | ||
| 275 | |||
| 276 | int luaopen_moonp(lua_State* L) { | ||
| 277 | lua_getglobal(L, "package"); | ||
| 278 | lua_getfield(L, -1, "loaded"); | ||
| 279 | lua_createtable(L, 0, 0); | ||
| 280 | lua_pushcfunction(L, moontolua); | ||
| 281 | lua_setfield(L, -2, "to_lua"); | ||
| 282 | lua_pushcfunction(L, init_stacktraceplus); | ||
| 283 | lua_setfield(L, -2, "load_stacktraceplus"); | ||
| 284 | lua_setfield(L, -2, "moonp"); | ||
| 285 | lua_pop(L, 2); | ||
| 286 | init_moonplus(L); | ||
| 287 | return 0; | ||
| 288 | } | ||
| 289 | |||
| 290 | } // extern "C" | ||
| 291 | |||
| 292 | #endif // LIBMOONP | ||
| 293 | |||
