aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLi Jin <dragon-fly@qq.com>2020-02-07 17:29:34 +0800
committerLi Jin <dragon-fly@qq.com>2020-02-07 17:29:34 +0800
commitc241ea241e8e9c152f6eb14f163b2ae39749f7bf (patch)
tree2fd05ca6866ea60ca778fb6ff31c7ec429e138c4
parent2e50c15bfe67d4709880a0377d37fca191be2f3e (diff)
downloadyuescript-c241ea241e8e9c152f6eb14f163b2ae39749f7bf.tar.gz
yuescript-c241ea241e8e9c152f6eb14f163b2ae39749f7bf.tar.bz2
yuescript-c241ea241e8e9c152f6eb14f163b2ae39749f7bf.zip
releasing moonplus as a lib.
-rw-r--r--.gitignore1
-rw-r--r--CMakeLists.txt36
-rw-r--r--LICENSE2
-rw-r--r--makefile3
-rw-r--r--moonplus-0.1-1.rockspec27
-rw-r--r--spec/inputs/plus.moon6
-rw-r--r--spec/inputs/syntax.moon8
-rw-r--r--src/MoonP/moon_compiler.cpp33
-rw-r--r--src/MoonPlus.h125
-rw-r--r--src/StackTracePlus.h550
-rw-r--r--src/moonp.cpp (renamed from src/moonc.cpp)128
11 files changed, 896 insertions, 23 deletions
diff --git a/.gitignore b/.gitignore
index 5f6a200..d3acf45 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
1moonp 1moonp
2build 2build
3build.luarocks
3spec/outputs 4spec/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 @@
1project ( moonp CXX )
2cmake_minimum_required ( VERSION 3.1 )
3
4MESSAGE(STATUS "Lua: using information from luarocks")
5
6MESSAGE(STATUS "LUA_LIBDIR: " ${LUA_INCDIR}/../../lib)
7MESSAGE(STATUS "LUA_INCDIR: " ${LUA_INCDIR})
8MESSAGE(STATUS "LUA: " ${LUA})
9
10SET(LUA_EXECUTABLE "${LUA}")
11SET(LUA_INCLUDE_DIR "${LUA_INCDIR}")
12
13GET_FILENAME_COMPONENT(LUA_EXEC_NAME ${LUA} NAME_WE)
14IF(LUA_EXEC_NAME STREQUAL "luajit")
15 FIND_LIBRARY(LUA_LIBRARIES
16 NAMES luajit libluajit
17 PATHS ${LUA_INCDIR}/../../lib
18 NO_DEFAULT_PATH)
19ELSEIF(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)
24ENDIF()
25MESSAGE(STATUS "Lua library: ${LUA_LIBRARIES}")
26
27enable_language( CXX )
28find_package( Lua REQUIRED )
29include_directories( src ${LUA_INCLUDE_DIR} )
30add_definitions( -std=c++17 -O3 -DLIBMOONP )
31
32add_library( moonp MODULE src/MoonP/ast.cpp src/MoonP/parser.cpp src/MoonP/moon_parser.cpp src/MoonP/moon_compiler.cpp src/moonp.cpp)
33set_target_properties( moonp PROPERTIES PREFIX "")
34target_link_libraries( moonp ${LUA_LIBRARIES} )
35
36install(CODE "")
diff --git a/LICENSE b/LICENSE
index 5b87539..6c2fbc6 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,6 +1,6 @@
1MIT License 1MIT License
2 2
3Copyright (c) 2017 Li Jin 3Copyright (c) 2020 Li Jin
4 4
5Permission is hereby granted, free of charge, to any person obtaining a copy 5Permission is hereby granted, free of charge, to any person obtaining a copy
6of this software and associated documentation files (the "Software"), to deal 6of this software and associated documentation files (the "Software"), to deal
diff --git a/makefile b/makefile
index fd2d718..14a7752 100644
--- a/makefile
+++ b/makefile
@@ -206,7 +206,10 @@ clean:
206.PHONY: test 206.PHONY: test
207test: release 207test: 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
212all: $(BIN_PATH)/$(BIN_NAME) 215all: $(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 @@
1package = "MoonPlus"
2version = "0.1-1"
3source = {
4 url = "git+https://github.com/pigpigyyy/MoonPlus"
5}
6description = {
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}
13dependencies = {
14 "lua >= 5.1",
15}
16build = {
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
12message = message\gsub "<%->", fcolor if fcolor = message\match "<%w*>" 12message = message\gsub "<%->", fcolor if fcolor = message\match "<%w*>"
13 13
14func val if val = getvalue! 14valA = func! if func = getfunc!
15
16valB = 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",
242another hello, one, 242another 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
247another hello, one, two, three, four,
248 yeah: man
249 okay: yeah
250
251another hello, one, two, three, four, yeah: man
252 okay: yeah
253
246-- 254--
247a += 3 - 5 255a += 3 - 5
248a *= 3 + 5 256a *= 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 @@
1R"moonscript_codes(
2import "moonp"
3import concat, insert, remove from table
4unpack = unpack or table.unpack
5lua = :loadstring, :load
6
7local *
8
9dirsep = "/"
10
11moonp.moon_compiled = {}
12
13split = (str, delim) ->
14 return {} if str == ""
15 str ..= delim
16 [m for m in str\gmatch("(.-)"..delim)]
17
18get_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
27create_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
34moon_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
55loadstring = (...) ->
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
67loadfile = (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
75dofile = (...) ->
76 f = assert loadfile ...
77 f!
78
79insert_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
90remove_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
100moon_require = (name)->
101 insert_loader!
102 xpcall (-> require name), (err)->
103 msg = moonp.stp.stacktrace err, 2
104 print msg
105
106setmetatable 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
120moonp[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 @@
1R"lua_codes(
2--[[
3The MIT License
4
5Copyright (c) 2010 Ignacio Burgueño
6
7Permission is hereby granted, free of charge, to any person obtaining a copy
8of this software and associated documentation files (the "Software"), to deal
9in the Software without restriction, including without limitation the rights
10to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11copies of the Software, and to permit persons to whom the Software is
12furnished to do so, subject to the following conditions:
13
14The above copyright notice and this permission notice shall be included in
15all copies or substantial portions of the Software.
16
17THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23THE SOFTWARE.]]
24
25-- tables
26local _G = _G
27local string, io, debug, coroutine = string, io, debug, coroutine
28
29-- functions
30local tostring, require = tostring, require
31local next, assert = next, assert
32local pcall, type, pairs, ipairs = pcall, type, pairs, ipairs
33local error = error
34
35assert(debug, "debug table must be available at this point")
36
37local string_gmatch = string.gmatch
38local string_sub = string.sub
39local table_concat = table.concat
40
41local _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
48local m_known_tables = { [_G] = "_G (global table)" }
49local 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
54end
55
56add_known_module("string", "string module")
57add_known_module("io", "io module")
58add_known_module("os", "os module")
59add_known_module("table", "table module")
60add_known_module("math", "math module")
61add_known_module("package", "package module")
62add_known_module("debug", "debug module")
63add_known_module("coroutine", "coroutine module")
64
65-- lua5.2
66add_known_module("bit32", "bit32 module")
67-- luajit
68add_known_module("bit", "bit module")
69add_known_module("jit", "jit module")
70-- lua5.3
71if _VERSION >= "Lua 5.3" then
72 add_known_module("utf8", "utf8 module")
73end
74
75
76local m_user_known_tables = {}
77
78local m_known_functions = {}
79for _, 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
118end
119
120local m_user_known_functions = {}
121
122local 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
125end
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
130local 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)"
153end
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
159local 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
202end
203
204---
205-- Dumper instances are used to analyze stacks and collect its information.
206--
207local Dumper = {}
208
209Dumper.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
237end
238
239-- helpers for collecting strings to be used when assembling the final trace
240function Dumper:add (text)
241 self.lines[#self.lines + 1] = text
242end
243function Dumper:add_f (fmt, ...)
244 self:add(fmt:format(...))
245end
246function Dumper:concat_lines ()
247 return table_concat(self.lines)
248end
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--
256function 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
319end
320
321local 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
348end
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--
361function _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[[
434Stack 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
528end
529
530--
531-- Adds a table to the list of known tables
532function _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
537end
538
539--
540-- Adds a function to the list of known functions
541function _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
546end
547
548return _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
16int main(int narg, const char** args) { 18int 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
179extern "C" {
180
181#include "lua.h"
182#include "lauxlib.h"
183
184static const char moonplusCodes[] =
185#include "MoonPlus.h"
186
187static 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
206static const char stpCodes[] =
207#include "StackTracePlus.h"
208
209static 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
218static 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
276int 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