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

z00m128 / sjasmplus / 1498

19 Apr 2025 01:57PM UTC coverage: 96.506% (+0.002%) from 96.504%
1498

push

cirrus-ci

ped7g
docs: refresh lua bindings

9612 of 9960 relevant lines covered (96.51%)

170160.37 hits per line

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

99.13
/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) {
872✔
78
        SPRINTF1(internal_script_name, LINEMAX, "script %u", uint32_t(scripts_origin.size()));
872✔
79
        scripts_origin.push_back(srcPos);
872✔
80
        return internal_script_name;
872✔
81
}
82

83
static bool isInlinedScript(TextFilePos & errorPos, const char* script_name) {
203,978✔
84
        // return false when the script is external (real file name)
85
        if (script_name != strstr(script_name, "[string \"script ")) return false;
203,978✔
86
        // inlined script, find it's origin and add line number to that
87
        int scriptNumber = atoi(script_name + 16);
203,610✔
88
        if (scriptNumber < 0 || int(scripts_origin.size()) <= scriptNumber) return false;
203,610✔
89
        errorPos = scripts_origin.at(scriptNumber);
203,610✔
90
        return true;
203,610✔
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
95
static int addLuaSourcePositions() {
203,346✔
96
        // find all *known* inlined/standalone script names and line numbers on the lua stack
97
        assert(LUA);
203,346✔
98
        lua_Debug ar;
99
        source_positions_t luaPosTemp;
203,346✔
100
        luaPosTemp.reserve(16);
203,346✔
101
        int level = 1;                        // level 0 is "C" space, ignore that always
203,346✔
102
        while (lua_getstack(LUA, level, &ar)) {                        // as long lua stack level are available
407,311✔
103
                if (!lua_getinfo(LUA, "Sl", &ar)) break;        // not enough info about current level
203,965✔
104

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

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

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

126
        return luaPosTemp.end() - stop;
406,692✔
127
}
203,346✔
128

129
static void removeLuaSourcePositions(int to_remove) {
203,346✔
130
        while (to_remove--) sourcePosStack.pop_back();        // restore sourcePosStack to original state
407,210✔
131
}
203,346✔
132

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

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

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

153
        // advance beyond filename and line in LuaError pointer
154
        LuaError = colon2Pos + 1;
13✔
155
        while (White(*LuaError)) ++LuaError;
26✔
156
        return luaErrorPos;
13✔
157
}
158

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

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

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

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

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

207
        removeLuaSourcePositions(positionsAdded);
387✔
208
        return parseResult ? val : 0;
774✔
209
}
210

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

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

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

224
        // restore the original line
225
        STRCPY(line, LINEMAX, oldLine);
3,299✔
226
        eolComment = oldEolComment;
3,299✔
227
        free(oldLine);
3,299✔
228

229
        removeLuaSourcePositions(positionsAdded);
3,299✔
230
}
3,299✔
231

232
static void lua_sj_parse_line(const char *str) {
260✔
233
        parse_line(str, true);
260✔
234
}
260✔
235

236
static void lua_sj_parse_code(const char *str) {
3,039✔
237
        parse_line(str, false);
3,039✔
238
}
3,039✔
239

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

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

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

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

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

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

283
static void lua_sj_shellexec(const char *command) {
1✔
284
        int positionsAdded = addLuaSourcePositions();        // add known script positions to sourcePosStack vector
1✔
285
        LuaShellExec(command);
1✔
286
        removeLuaSourcePositions(positionsAdded);
1✔
287
}
1✔
288

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

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

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

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

336
static void lua_sj_add_byte(int byte) {
199,096✔
337
        int positionsAdded = addLuaSourcePositions();        // add known script positions to sourcePosStack vector
199,096✔
338
        EmitByte(byte);
199,096✔
339
        removeLuaSourcePositions(positionsAdded);
199,096✔
340
}
199,096✔
341

342
static void lua_sj_add_word(int word) {
40✔
343
        int positionsAdded = addLuaSourcePositions();        // add known script positions to sourcePosStack vector
40✔
344
        EmitWord(word);
40✔
345
        removeLuaSourcePositions(positionsAdded);
40✔
346
}
40✔
347

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

355
static unsigned int lua_sj_get_word(unsigned int address) {
10✔
356
        int positionsAdded = addLuaSourcePositions();        // add known script positions to sourcePosStack vector
10✔
357
        auto result = MemGetWord(address);
10✔
358
        removeLuaSourcePositions(positionsAdded);
10✔
359
        return result;
10✔
360
}
361

362
static const char* lua_sj_get_modules(void) {
27✔
363
        return ModuleName;
27✔
364
}
365

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

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

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

391
static aint lua_get_current_address() {
30✔
392
        return CurAddress;
30✔
393
}
394

395
static void lua_impl_init() {
64✔
396
        assert(nullptr == LUA);
64✔
397

398
        scripts_origin.reserve(64);
64✔
399

400
        // initialise Lua (regular Lua, without sjasmplus bindings/extensions)
401
        LUA = luaL_newstate();
64✔
402
        lua_atpanic(LUA, (lua_CFunction)lua_impl_fatalError);
64✔
403

404
        // for manual testing of lua_atpanic handler
405
//         { lua_error(LUA); }
406
//         { lua_pushstring(LUA, "testing at panic handler"); lua_error(LUA); }
407

408
        luaL_openlibs(LUA);
64✔
409

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

452
                TextFilePos binding_script_pos(binding_script_name, binding_script_line);
64✔
453
                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))
64✔
454
                        || lua_pcall(LUA, 0, LUA_MULTRET, 0)) {
64✔
455
                        lua_impl_showLoadError(FATAL);                                // unreachable (I hope) // manual testing: damage binding script
×
456
                }
457
}
64✔
458

459
void dirENDLUA() {
3✔
460
        Error("[ENDLUA] End of lua script without script");
3✔
461
}
3✔
462

463
void dirLUA() {
1,068✔
464
        // lazy init of Lua scripting upon first hit of LUA directive
465
        if (nullptr == LUA) lua_impl_init();
1,068✔
466
        assert(LUA);
1,068✔
467

468
        constexpr size_t luaBufferSize = 32768;
1,068✔
469
        char* id, * buff = nullptr, * bp = nullptr;
1,068✔
470

471
        int passToExec = LASTPASS;
1,068✔
472
        if ((id = GetID(lp)) && strlen(id) > 0) {
1,068✔
473
                if (cmphstr(id, "pass1")) {
918✔
474
                        passToExec = 1;
75✔
475
                } else if (cmphstr(id, "pass2")) {
843✔
476
                        passToExec = 2;
36✔
477
                } else if (cmphstr(id, "pass3")) {
807✔
478
                        passToExec = LASTPASS;
126✔
479
                } else if (cmphstr(id, "allpass")) {
681✔
480
                        passToExec = -1;
678✔
481
                } else {
482
                        Error("[LUA] Syntax error", id);
3✔
483
                }
484
        }
485

486
        const EStatus errorType = (1 == passToExec || 2 == passToExec) ? EARLY : PASS3;
1,068✔
487
        const bool execute = (-1 == passToExec) || (passToExec == pass);
1,068✔
488
        // remember warning suppression also from block start
489
        bool showWarning = !suppressedById(W_LUA_MC_PASS);
1,068✔
490

491
        assert(!sourcePosStack.empty());
1,068✔
492
        TextFilePos luaStartPos = sourcePosStack.back();        // position of LUA directive (not ENDLUA)
1,068✔
493
        if (execute) {
1,068✔
494
                buff = new char[luaBufferSize];
808✔
495
                bp = buff;
808✔
496
        }
497
        ListFile();
1,068✔
498

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

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

543
        ++CompiledCurrentLine;
1,067✔
544
        substitutedLine = line;                // override substituted list line for ENDLUA
1,067✔
545
}
1,067✔
546

547
void dirINCLUDELUA() {
51✔
548
        // lazy init of Lua scripting upon first hit of INCLUDELUA directive
549
        if (nullptr == LUA) lua_impl_init();
51✔
550
        assert(LUA);
51✔
551

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

569
#endif //USE_LUA
570

571
////////////////////////////////////////////////////////////////////////////////////////////
572
// close LUA engine and release everything related
573
void lua_impl_close() {
512✔
574
        #ifdef USE_LUA
575
                // if Lua was used and initialised, release everything
576
                if (LUA) lua_close(LUA);
512✔
577
        #endif //USE_LUA
578

579
        // do nothing when Lua is compile-time disabled
580
}
512✔
581

582
//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