• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

z00m128 / sjasmplus / 26066527214

18 May 2026 11:30PM UTC coverage: 97.312% (-0.4%) from 97.744%
26066527214

push

github

ped7g
CI: switch coveralls upload to their github action

9991 of 10267 relevant lines covered (97.31%)

134377.41 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

98.78
/sjasm/lua_sjasm.cpp
1
/*
2

3
  SjASMPlus Z80 Cross Compiler - modified - lua scripting module
4

5
  Copyright (c) 2006 Sjoerd Mastijn (original SW)
6
  Copyright (c) 2022 Peter Ped Helcmanovsky (lua scripting module)
7

8
  This software is provided 'as-is', without any express or implied warranty.
9
  In no event will the authors be held liable for any damages arising from the
10
  use of this software.
11

12
  Permission is granted to anyone to use this software for any purpose,
13
  including commercial applications, and to alter it and redistribute it freely,
14
  subject to the following restrictions:
15

16
  1. The origin of this software must not be misrepresented; you must not claim
17
         that you wrote the original software. If you use this software in a product,
18
         an acknowledgment in the product documentation would be appreciated but is
19
         not required.
20

21
  2. Altered source versions must be plainly marked as such, and must not be
22
         misrepresented as being the original software.
23

24
  3. This notice may not be removed or altered from any source distribution.
25

26
*/
27

28
// lua_sjasm.cpp
29

30
#include "sjdefs.h"
31

32
#ifdef USE_LUA
33

34
#ifdef USE_LUA_SO_LIB
35
        // OS Lua is most likely built as C, so fallback to lua.hpp include and treat it as C library
36
        #include "lua.hpp"
37
#else
38
        // bundled Lua is built as C++ library to avoid memory leaks from longjmp usage in C Lua
39
        // breaking destructor calls on C++ object instances
40
        // see https://github.com/vinniefalco/LuaBridge/issues/323 for one example
41
        #include "lauxlib.h"
42
        #include "lua.h"
43
        #include "lualib.h"
44
#endif
45

46
#include "LuaBridge/LuaBridge.h"
47

48
static lua_State *LUA = nullptr;
49

50
static const char* lua_err_prefix = "[LUA] ";
51

52
// extra lua script inserting interface (sj.something) entry functions
53
// for functions with optional arguments, like error and warning
54
// (as LuaBridge2.6 doesn't offer that type of binding as far as I can tell)
55
// Sidestepping LuaBridge write-protection by "rawset" the end point into it
56
static const char* binding_script_name = "lua_sjasm.cpp";
57
static constexpr int binding_script_line = __LINE__;
58
static const std::string lua_impl_init_bindings_script = R"BINDING_LUA(
59
rawset(sj,"error",function(m,v)sj.error_i(m or "no message",v)end)
60
rawset(sj,"warning",function(m,v)sj.warning_i(m or "no message",v)end)
61
rawset(sj,"insert_define",function(n,v)return sj.insert_define_i(n,v)end)
62
rawset(sj,"exit",function(e)return sj.exit_i(e or 1)end)
63
rawset(sj,"set_device",function(i,t)return sj.set_device_i(i or "NONE",t or 0)end)
64
rawset(sj,"get_page_at",function(a)return sj.get_page_at_i(a or sj.current_address)end)
65
rawset(zx,"trdimage_create",function(n,l)return zx.trdimage_create_i(n,l)end)
66
rawset(zx,"trdimage_add_file",function(t,f,s,l,a,r)return zx.trdimage_add_file_i(t,f,s,l,a or -1,r or false)end)
67
)BINDING_LUA";
68

69
static void lua_impl_fatalError(lua_State *L) {
×
70
        Error((char *)lua_tostring(L, -1), NULL, FATAL);
×
71
}
72

73
static std::vector<TextFilePos> scripts_origin;
74

75
static char internal_script_name[LINEMAX];
76

77
static const char* lua_impl_get_script_name(const TextFilePos & srcPos) {
876✔
78
        SPRINTF1(internal_script_name, LINEMAX, "script %u", uint32_t(scripts_origin.size()));
876✔
79
        scripts_origin.push_back(srcPos);
876✔
80
        return internal_script_name;
876✔
81
}
82

83
static bool isInlinedScript(TextFilePos & errorPos, const char* script_name) {
203,987✔
84
        // return false when the script is external (real file name)
85
        if (script_name != strstr(script_name, "[string \"script ")) return false;
203,987✔
86
        // inlined script, find it's origin and add line number to that
87
        int scriptNumber = atoi(script_name + 16);
203,619✔
88
        if (scriptNumber < 0 || int(scripts_origin.size()) <= scriptNumber) return false;
203,619✔
89
        errorPos = scripts_origin.at(scriptNumber);
203,619✔
90
        return true;
91
}
92

93
// adds current source position in lua script + full stack depth onto sourcePosStack
94
// = makes calls to Error/Warning API to display more precise error lines in lua scripts
203,352✔
95
static int addLuaSourcePositions() {
96
        // count each request for Lua source position as internal new line to reset SUPPRESS type error messages
203,352✔
97
        ++CompiledCurrentLine;        // otherwise single SUPPRESS error does apply for whole lua block
98
        // find all *known* inlined/standalone script names and line numbers on the lua stack
99
        assert(LUA);
203,352✔
100
        lua_Debug ar;
203,352✔
101
        source_positions_t luaPosTemp;
203,352✔
102
        luaPosTemp.reserve(16);
407,323✔
103
        int level = 1;                        // level 0 is "C" space, ignore that always
104
        while (lua_getstack(LUA, level, &ar)) {                        // as long lua stack level are available
105
                if (!lua_getinfo(LUA, "Sl", &ar)) break;        // not enough info about current level
106

107
                //assert(level || !strcmp("[C]", ar.short_src));        //TODO: verify if ever upgrading to newer lua
203,971✔
108
                //if (!strcmp("[C]", ar.short_src)) { ++level; continue; }
203,971✔
109

203,605✔
110
                TextFilePos levelErrorPos;
111
                if (isInlinedScript(levelErrorPos, ar.short_src)) {
366✔
112
                        levelErrorPos.line += ar.currentline;
366✔
113
                } else {
366✔
114
                        fullpath_ref_t archivedFname = GetInputFile(delim_string_t(ar.short_src, DT_COUNT));
115
                        levelErrorPos.newFile(archivedFname.str.c_str());
203,971✔
116
                        levelErrorPos.line = ar.currentline;
203,971✔
117
                }
118
                luaPosTemp.push_back(levelErrorPos);
119
                ++level;
120
        }
203,352✔
121

203,352✔
122
        // add all lua positions in reversed order to sourcePosStack (hide binding script if anything else is available)
203,352✔
123
        bool hide_binding = (2 <= luaPosTemp.size()) && !strcmp(binding_script_name, luaPosTemp[0].filename);
407,222✔
124
        source_positions_t::iterator stop = hide_binding ? luaPosTemp.begin() + 1 : luaPosTemp.begin();
125
        source_positions_t::iterator i = luaPosTemp.end();
406,704✔
126
        while (i-- != stop) sourcePosStack.push_back(*i);
203,352✔
127

128
        return luaPosTemp.end() - stop;
203,352✔
129
}
407,222✔
130

203,352✔
131
static void removeLuaSourcePositions(int to_remove) {
132
        while (to_remove--) sourcePosStack.pop_back();        // restore sourcePosStack to original state
133
}
19✔
134

19✔
135
// skips file+line_number info (but will adjust global LuaStartPos data for Error output)
19✔
136
static TextFilePos lua_impl_splitLuaErrorMessage(const char*& LuaError) {
137
        TextFilePos luaErrorPos("?");
19✔
138
        if (!sourcePosStack.empty()) luaErrorPos = sourcePosStack.back();
19✔
139
        if (nullptr == LuaError) return luaErrorPos;
19✔
140

141
        const char* colonPos = strchr(LuaError, ':');
16✔
142
        const char* colon2Pos = nullptr != colonPos ? strchr(colonPos+1, ':') : nullptr;
16✔
143
        if (nullptr == colonPos || nullptr == colon2Pos) return luaErrorPos;        // error, format not recognized
14✔
144

145
        int lineNumber = atoi(colonPos + 1);
146
        if (isInlinedScript(luaErrorPos, LuaError)) {
4✔
147
                luaErrorPos.line += lineNumber;
2✔
148
        } else {
2✔
149
                // standalone script, use file name and line number as is (if provided by lua error)
150
                fullpath_ref_t archFname = GetInputFile(delim_string_t(std::string(LuaError, colonPos), DT_COUNT));
151
                luaErrorPos.newFile(archFname.str.c_str());
152
                luaErrorPos.line = lineNumber;
16✔
153
        }
32✔
154

16✔
155
        // advance beyond filename and line in LuaError pointer
156
        LuaError = colon2Pos + 1;
157
        while (White(*LuaError)) ++LuaError;
19✔
158
        return luaErrorPos;
19✔
159
}
19✔
160

19✔
161
static void lua_impl_showLoadError(const EStatus type) {
19✔
162
        const char *msgp = lua_tostring(LUA, -1);
19✔
163
        sourcePosStack.push_back(lua_impl_splitLuaErrorMessage(msgp));
19✔
164
        Error(msgp, nullptr, type);
165
        sourcePosStack.pop_back();
387✔
166
        lua_pop(LUA, 1);
387✔
167
}
168

169
static aint lua_sj_calc(const char *str) {
387✔
170
        int positionsAdded = addLuaSourcePositions();        // add known script positions to sourcePosStack vector
387✔
171

387✔
172
        // substitute defines + macro_args in the `str` first (preserve original global variables)
387✔
173
        char* const oldSubstitutedLine = substitutedLine;
387✔
174
        const int oldComlin = comlin;
3✔
175
        comlin = 0;
176
        char* tmp = nullptr, * tmp2 = nullptr;
387✔
177
        if (sline[0]) {
62✔
178
                tmp = STRDUP(sline);
179
                if (nullptr == tmp) ErrorOOM();
180
        }
181
        if (sline2[0]) {
387✔
182
                tmp2 = STRDUP(sline2);
387✔
183
                if (nullptr == tmp2) ErrorOOM();
184
        }
185
        // non-const copy of input string for ReplaceDefine argument
186
        //TODO: v2.x, rewrite whole parser of sjasmplus to start with const input to avoid such copies
387✔
187
        char* luaInput = STRDUP(str ? str : "");
387✔
188
        char* substitutedStr = ReplaceDefine(luaInput);
189

190
        // evaluate the expression
387✔
191
        aint val;
387✔
192
        int parseResult = ParseExpression(substitutedStr, val);
3✔
193
        free(luaInput);
3✔
194

195
        // restore any global values affected by substitution
387✔
196
        sline[0] = 0;
387✔
197
        if (tmp) {
62✔
198
                STRCPY(sline, LINEMAX2, tmp);
62✔
199
                free(tmp);
200
        }
387✔
201
        sline2[0] = 0;
387✔
202
        if (tmp2) {
203
                STRCPY(sline2, LINEMAX2, tmp2);
387✔
204
                free(tmp2);
774✔
205
        }
206
        substitutedLine = oldSubstitutedLine;
207
        comlin = oldComlin;
3,302✔
208

3,302✔
209
        removeLuaSourcePositions(positionsAdded);
210
        return parseResult ? val : 0;
211
}
3,302✔
212

3,302✔
213
static void parse_line(const char* str, bool parseLabels) {
214
        int positionsAdded = addLuaSourcePositions();        // add known script positions to sourcePosStack vector
215

3,302✔
216
        // preserve current actual line which will be parsed next
3,302✔
217
        char *oldLine = STRDUP(line);
3,302✔
218
        if (nullptr == oldLine) ErrorOOM();
219
        char *oldEolComment = eolComment;
220

3,302✔
221
        // inject new line from Lua call and assemble it
3,302✔
222
        STRCPY(line, LINEMAX, str ? str : "");
3,302✔
223
        eolComment = nullptr;
224
        ParseLineSafe(parseLabels);
3,302✔
225

3,302✔
226
        // restore the original line
227
        STRCPY(line, LINEMAX, oldLine);
260✔
228
        eolComment = oldEolComment;
260✔
229
        free(oldLine);
260✔
230

231
        removeLuaSourcePositions(positionsAdded);
3,042✔
232
}
3,042✔
233

3,042✔
234
static void lua_sj_parse_line(const char *str) {
235
        parse_line(str, true);
20✔
236
}
20✔
237

20✔
238
static void lua_sj_parse_code(const char *str) {
20✔
239
        parse_line(str, false);
20✔
240
}
241

39✔
242
static void lua_sj_error(const char* message, const char* value = nullptr) {
39✔
243
        int positionsAdded = addLuaSourcePositions();        // add known script positions to sourcePosStack vector
39✔
244
        Error(message, value, ALL);
39✔
245
        removeLuaSourcePositions(positionsAdded);
39✔
246
}
247

207✔
248
static void lua_sj_warning(const char* message, const char* value = nullptr) {
207✔
249
        int positionsAdded = addLuaSourcePositions();                        // add known script positions to sourcePosStack vector
207✔
250
        Warning(message, value, W_ALL);
251
        removeLuaSourcePositions(positionsAdded);
252
}
26✔
253

254
static const char* lua_sj_get_define(const char* name, bool macro_args = false) {
26✔
255
        const char* macro_res = (macro_args && macrolabp) ? MacroDefineTable.getverv(name) : nullptr;
26✔
256
        return macro_res ? macro_res : DefineTable.Get(name);
26✔
257
}
14✔
258

259
static bool lua_sj_insert_define(const char* name, const char* value) {
260
        // wrapper to resolve member-function call (without std::function wrapping lambda, just to KISS)
296✔
261
        char* lua_name = const_cast<char*>(name);                //TODO v2.x avoid const_cast like this
296✔
262
        char* id = name ? GetID(lua_name) : nullptr;
263
        if (nullptr == id) return false;
294✔
264
        return DefineTable.Replace(id, value ? value : "");
294✔
265
}
294✔
266

294✔
267
static int lua_sj_get_label(const char *name) {
294✔
268
        if (nullptr == name) return -1;
269
        aint val;
270
        int positionsAdded = addLuaSourcePositions();        // add known script positions to sourcePosStack vector
91✔
271
        char* n = const_cast<char*>(name);        //TODO try to get rid of const_cast, LuaBridge requires const char* to understand it as lua string
91✔
272
        if (!GetLabelValue(n, val)) val = -1;
273
        removeLuaSourcePositions(positionsAdded);
91✔
274
        return val;
91✔
275
}
91✔
276

79✔
277
static bool lua_sj_insert_label(const char *name, int address) {
91✔
278
        int positionsAdded = addLuaSourcePositions();        // add known script positions to sourcePosStack vector
279
        bool isLocal;
1✔
280
        std::unique_ptr<char[]> fullName(ValidateLabel(isLocal, name, true, false));
1✔
281
        removeLuaSourcePositions(positionsAdded);
1✔
282
        if (nullptr == fullName.get()) return false;
1✔
283
        return LabelTable.Insert(fullName.get(), address, (isLocal ? LABEL_IS_LOCAL : 0));
1✔
284
}
285

5✔
286
static void lua_sj_shellexec(const char *command) {
5✔
287
        int positionsAdded = addLuaSourcePositions();        // add known script positions to sourcePosStack vector
5✔
288
        LuaShellExec(command);
5✔
289
        removeLuaSourcePositions(positionsAdded);
5✔
290
}
5✔
291

292
static bool lua_sj_set_page(aint n) {
293
        int positionsAdded = addLuaSourcePositions();        // add known script positions to sourcePosStack vector
10✔
294
        if (!DeviceID) Warning("sj.set_page: only allowed in real device emulation mode (See DEVICE)");
10✔
295
        bool result = DeviceID && dirPageImpl("sj.set_page", n);
10✔
296
        removeLuaSourcePositions(positionsAdded);
10✔
297
        return result;
1✔
298
}
299

9✔
300
static bool lua_sj_set_slot(aint n) {
9✔
301
        int positionsAdded = addLuaSourcePositions();        // add known script positions to sourcePosStack vector
9✔
302
        bool result = false;
303
        if (!DeviceID) {
2✔
304
                Warning("sj.set_slot: only allowed in real device emulation mode (See DEVICE)");
2✔
305
        } else {
306
                n = Device->SlotNumberFromPreciseAddress(n);
307
                result = Device->SetSlot(n);
10✔
308
                if (!result) {
10✔
309
                        char buf[LINEMAX];
310
                        SPRINTF1(buf, LINEMAX, "sj.set_slot: Slot number must be in range 0..%u", Device->SlotsCount - 1);
311
                        Error(buf);
9✔
312
                }
9✔
313
        }
9✔
314
        removeLuaSourcePositions(positionsAdded);
9✔
315
        return result;
9✔
316
}
9✔
317

318
static int32_t lua_sj_get_page_at(int32_t addr) {
319
        int positionsAdded = addLuaSourcePositions();        // add known script positions to sourcePosStack vector
31✔
320
        if (!DeviceID) Warning("sj.get_page_at: only allowed in real device emulation mode (See DEVICE)");
31✔
321
        int32_t result = DeviceID ? Device->GetPageOfA16(addr) : LABEL_PAGE_ROM;
322
        removeLuaSourcePositions(positionsAdded);
31✔
323
        return result;
12✔
324
}
325

326
static bool lua_sj_set_device(const char* id, const aint ramtop = 0) {
31✔
327
        int positionsAdded = addLuaSourcePositions();        // add known script positions to sourcePosStack vector
31✔
328
        // refresh source position of first DEVICE directive (to make global-device detection work correctly)
31✔
329
        if (1 == ++deviceDirectivesCount) {
330
                assert(!sourcePosStack.empty());
331
                globalDeviceSourcePos = sourcePosStack.back();
199,096✔
332
        }
199,096✔
333
        // check for nullptr id??
199,096✔
334
        bool result = SetDevice(id, ramtop);
199,096✔
335
        removeLuaSourcePositions(positionsAdded);
199,096✔
336
        return result;
337
}
43✔
338

43✔
339
static void lua_sj_add_byte(int byte) {
43✔
340
        int positionsAdded = addLuaSourcePositions();        // add known script positions to sourcePosStack vector
43✔
341
        EmitByte(byte);
43✔
342
        removeLuaSourcePositions(positionsAdded);
343
}
11✔
344

11✔
345
static void lua_sj_add_word(int word) {
11✔
346
        int positionsAdded = addLuaSourcePositions();        // add known script positions to sourcePosStack vector
11✔
347
        EmitWord(word);
11✔
348
        removeLuaSourcePositions(positionsAdded);
349
}
350

10✔
351
static unsigned char lua_sj_get_byte(unsigned int address) {
10✔
352
        int positionsAdded = addLuaSourcePositions();        // add known script positions to sourcePosStack vector
10✔
353
        auto result = MemGetByte(address);
10✔
354
        removeLuaSourcePositions(positionsAdded);
10✔
355
        return result;
356
}
357

27✔
358
static unsigned int lua_sj_get_word(unsigned int address) {
27✔
359
        int positionsAdded = addLuaSourcePositions();        // add known script positions to sourcePosStack vector
360
        auto result = MemGetWord(address);
361
        removeLuaSourcePositions(positionsAdded);
1✔
362
        return result;
363
}
1✔
364

1✔
365
static const char* lua_sj_get_modules(void) {
1✔
366
        return ModuleName;
1✔
367
}
1✔
368

369
static bool lua_zx_trdimage_create(const char* trdname, const char* label = nullptr) {
370
        // setup label to truncated 8 char array padded with spaces
1✔
371
        char l8[9] = "        ";
1✔
372
        char* l8_ptr = l8;
1✔
373
        while (label && *label && (l8_ptr - l8) < 8) *l8_ptr++ = *label++;
1✔
374
        int positionsAdded = addLuaSourcePositions();        // add known script positions to sourcePosStack vector
375
        bool result = TRD_SaveEmpty(trdname ? trdname : "", l8);
376
        removeLuaSourcePositions(positionsAdded);
1✔
377
        return result;
1✔
378
}
1✔
379

1✔
380
bool lua_zx_trdimage_add_file(const char* trd, const char* file, int start, int length, int autostart = -1, bool replace = false) {
381
        int positionsAdded = addLuaSourcePositions();        // add known script positions to sourcePosStack vector
382
        bool result = nullptr != trd && nullptr != file && TRD_AddFile(trd, file, start, length, autostart, replace, false);
30✔
383
        removeLuaSourcePositions(positionsAdded);
30✔
384
        return result;
385
}
386

65✔
387
static bool lua_zx_save_snapshot_sna(const char* fname, word start) {
388
        int positionsAdded = addLuaSourcePositions();        // add known script positions to sourcePosStack vector
65✔
389
        bool result = SaveSNA_ZX(fname ? fname : "", start);
390
        removeLuaSourcePositions(positionsAdded);
391
        return result;
65✔
392
}
65✔
393

394
static aint lua_get_current_address() {
395
        return CurAddress;
396
}
397

398
static void lua_impl_init() {
65✔
399
        assert(nullptr == LUA);
400

401
        scripts_origin.reserve(64);
130✔
402

65✔
403
        // initialise Lua (regular Lua, without sjasmplus bindings/extensions)
65✔
404
        LUA = luaL_newstate();
65✔
405
        lua_atpanic(LUA, (lua_CFunction)lua_impl_fatalError);
130✔
406

65✔
407
        // for manual testing of lua_atpanic handler
65✔
408
//         { lua_error(LUA); }
65✔
409
//         { lua_pushstring(LUA, "testing at panic handler"); lua_error(LUA); }
65✔
410

411
        luaL_openlibs(LUA);
65✔
412

65✔
413
        // initialise sjasmplus bindings/extensions
65✔
414
        luabridge::getGlobalNamespace(LUA)
65✔
415
                .addFunction("_c", lua_sj_calc)
65✔
416
                .addFunction("_pl", lua_sj_parse_line)
65✔
417
                .addFunction("_pc", lua_sj_parse_code)
418
                .beginNamespace("sj")
65✔
419
                        .addProperty("current_address", lua_get_current_address, dirOrgOnlyAddr)
65✔
420
                        .addProperty("warning_count", &WarningCount, false)        // read-only
65✔
421
                        .addProperty("error_count", &ErrorCount, false)        // read-only
65✔
422
                        .addProperty("pass", &pass, false)        // read-only
65✔
423
                        // internal functions which are lua-wrapped to enable optional arguments
65✔
424
                        .addFunction("error_i", lua_sj_error)
65✔
425
                        .addFunction("warning_i", lua_sj_warning)
65✔
426
                        .addFunction("insert_define_i", lua_sj_insert_define)
65✔
427
                        .addFunction("exit_i", ExitASM)
65✔
428
                        .addFunction("set_device_i", lua_sj_set_device)
65✔
429
                        .addFunction("get_page_at_i", lua_sj_get_page_at)
65✔
430
                        // remaining public functions with all arguments mandatory (boolean args seems to default to false?)
65✔
431
                        .addFunction("get_define", lua_sj_get_define)
65✔
432
                        .addFunction("get_label", lua_sj_get_label)
65✔
433
                        .addFunction("insert_label", lua_sj_insert_label)
434
                        .addFunction("shellexec", lua_sj_shellexec)
65✔
435
                        .addFunction("calc", lua_sj_calc)
130✔
436
                        .addFunction("parse_line", lua_sj_parse_line)
130✔
437
                        .addFunction("parse_code", lua_sj_parse_code)
65✔
438
                        .addFunction("add_byte", lua_sj_add_byte)
65✔
439
                        .addFunction("add_word", lua_sj_add_word)
65✔
440
                        .addFunction("get_byte", lua_sj_get_byte)
65✔
441
                        .addFunction("get_word", lua_sj_get_word)
442
                        .addFunction("get_device", GetDeviceName)                // no error/warning, can be called directly
65✔
443
                        .addFunction("get_modules", lua_sj_get_modules)
65✔
444
                        .addFunction("set_page", lua_sj_set_page)
×
445
                        .addFunction("set_slot", lua_sj_set_slot)
446
                        // MMU API will be not added, it is too dynamic, and _pc("MMU ...") works
65✔
447
                        .addFunction("file_exists", FileExistsCstr)
448
                .endNamespace()
3✔
449
                .beginNamespace("zx")
3✔
450
                        .addFunction("trdimage_create_i", lua_zx_trdimage_create)
3✔
451
                        .addFunction("trdimage_add_file_i", lua_zx_trdimage_add_file)
452
                        .addFunction("save_snapshot_sna", lua_zx_save_snapshot_sna)
1,071✔
453
                .endNamespace();
454

1,071✔
455
                TextFilePos binding_script_pos(binding_script_name, binding_script_line);
456
                if (luaL_loadbuffer(LUA, lua_impl_init_bindings_script.c_str(), lua_impl_init_bindings_script.size(), lua_impl_get_script_name(binding_script_pos))
1,071✔
457
                        || lua_pcall(LUA, 0, LUA_MULTRET, 0)) {
1,071✔
458
                        lua_impl_showLoadError(FATAL);                                // unreachable (I hope) // manual testing: damage binding script
459
                }
1,071✔
460
}
1,071✔
461

921✔
462
void dirENDLUA() {
75✔
463
        Error("[ENDLUA] End of lua script without script");
846✔
464
}
36✔
465

810✔
466
void dirLUA() {
126✔
467
        // lazy init of Lua scripting upon first hit of LUA directive
684✔
468
        if (nullptr == LUA) lua_impl_init();
681✔
469
        assert(LUA);
470

3✔
471
        constexpr size_t luaBufferSize = 32768;
472
        char* id, * buff = nullptr, * bp = nullptr;
473

474
        int passToExec = LASTPASS;
1,071✔
475
        if ((id = GetID(lp)) && strlen(id) > 0) {
1,071✔
476
                if (cmphstr(id, "pass1")) {
477
                        passToExec = 1;
1,071✔
478
                } else if (cmphstr(id, "pass2")) {
479
                        passToExec = 2;
1,071✔
480
                } else if (cmphstr(id, "pass3")) {
1,071✔
481
                        passToExec = LASTPASS;
811✔
482
                } else if (cmphstr(id, "allpass")) {
811✔
483
                        passToExec = -1;
484
                } else {
1,071✔
485
                        Error("[LUA] Syntax error", id);
486
                }
487
        }
4,302✔
488

3✔
489
        const EStatus errorType = (1 == passToExec || 2 == passToExec) ? EARLY : PASS3;
3✔
490
        const bool execute = (-1 == passToExec) || (passToExec == pass);
491
        // remember warning suppression also from block start
4,299✔
492
        bool showWarning = !suppressedById(W_LUA_MC_PASS);
4,299✔
493

4,299✔
494
        assert(!sourcePosStack.empty());
4,299✔
495
        TextFilePos luaStartPos = sourcePosStack.back();        // position of LUA directive (not ENDLUA)
4,299✔
496
        if (execute) {
3,225✔
497
                buff = new char[luaBufferSize];
×
498
                bp = buff;
499
        }
3,225✔
500
        ListFile();
3,225✔
501

3,225✔
502
        while (1) {
503
                if (!ReadLine(false)) {
4,299✔
504
                        Error("[LUA] Unexpected end of lua script");
1,068✔
505
                        break;
1,068✔
506
                }
1,068✔
507
                lp = line;
1,068✔
508
                SkipBlanks(lp);
509
                const int isEndLua = cmphstr(lp, "endlua");
1,068✔
510
                const size_t lineLen = isEndLua ? (lp - 6 - line) : strlen(line);
1,068✔
511
                if (execute) {
512
                        if (luaBufferSize < (bp - buff) + lineLen + 4) {
3,231✔
513
                                ErrorInt("[LUA] Maximum byte-size of Lua script is", luaBufferSize-4, FATAL);
3,231✔
514
                        }
515
                        memcpy(bp, line, lineLen);
1,071✔
516
                        bp += lineLen;
811✔
517
                        *bp++ = '\n';
811✔
518
                }
811✔
519
                if (isEndLua) {                // eat also any trailing eol-type of comment
811✔
520
                        skipEmitMessagePos = sourcePosStack.back();
18✔
521
                        ++CompiledCurrentLine;
522
                        lp = ReplaceDefine(lp);                // skip any empty substitutions and comments
810✔
523
                        substitutedLine = line;                // override substituted listing for ENDLUA
810✔
524
                        // take into account also warning suppression used at end of block
810✔
525
                        showWarning = showWarning && !suppressedById(W_LUA_MC_PASS);
30✔
526
                        break;
30✔
527
                }
528
                ListFile(true);
529
        }
530

1,070✔
531
        if (execute) {
1,070✔
532
                extraErrorWarningPrefix = lua_err_prefix;
1,070✔
533
                *bp = 0;
534
                DidEmitByte();                        // reset the flag before running lua script
51✔
535
                if (luaL_loadbuffer(LUA, buff, bp-buff, lua_impl_get_script_name(luaStartPos)) || lua_pcall(LUA, 0, LUA_MULTRET, 0)) {
536
                        lua_impl_showLoadError(errorType);
51✔
537
                }
538
                extraErrorWarningPrefix = nullptr;
51✔
539
                delete[] buff;
34✔
540
                if (DidEmitByte() && (-1 != passToExec) && showWarning) {
34✔
541
                        const EWStatus warningType = (1 == passToExec || 2 == passToExec) ? W_EARLY : W_PASS3;
542
                        WarningById(W_LUA_MC_PASS, nullptr, warningType);
17✔
543
                }
17✔
544
        }
3✔
545

546
        ++CompiledCurrentLine;
14✔
547
        substitutedLine = line;                // override substituted list line for ENDLUA
548
}
14✔
549

1✔
550
void dirINCLUDELUA() {
551
        // lazy init of Lua scripting upon first hit of INCLUDELUA directive
14✔
552
        if (nullptr == LUA) lua_impl_init();
553
        assert(LUA);
554

555
        if (1 != pass) {
556
                SkipToEol(lp);                // skip till EOL (colon), to avoid parsing file name
557
                return;
558
        }
559
        fullpath_ref_t file_in = GetInputFile(lp);
537✔
560
        if (!FileExists(file_in.full)) {
561
                Error("[INCLUDELUA] File doesn't exist", file_in.str.c_str(), EARLY);
562
        } else {
537✔
563
                extraErrorWarningPrefix = lua_err_prefix;
564
                // Use relative-to-launch-dir filename for open to get reasonable source-pos-string in errors
565
                if (luaL_dofile(LUA, file_in.full.lexically_proximate(LaunchDirectory).string().c_str())) {
566
                        lua_impl_showLoadError(EARLY);
537✔
567
                }
568
                extraErrorWarningPrefix = nullptr;
569
        }
570
}
571

572
#endif //USE_LUA
573

574
////////////////////////////////////////////////////////////////////////////////////////////
575
// close LUA engine and release everything related
576
void lua_impl_close() {
577
        #ifdef USE_LUA
578
                // if Lua was used and initialised, release everything
579
                if (LUA) lua_close(LUA);
580
        #endif //USE_LUA
581

582
        // do nothing when Lua is compile-time disabled
583
}
584

585
//eof lua_sjasm.cpp
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc