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.
-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 | |||