Port the duskscript language into the engine as src/dusk/script + duskscriptcc

Brings over the duskscript VM and compiler from dusk-vm: the runtime
(core/vm) lives under src/dusk/script, gated by DUSK_SCRIPT and wired into
the engine lifecycle with print()/consolePrint() routed to the console; the
standalone duskscriptcc compiler stays a lean host tool (linux/knulli only).

Collapses int/fixed/bignum into a single float-backed SCRIPT_VALUE_NUMBER
throughout the lexer, compiler, bytecode format and VM, removing the old
int16-rollover fast paths and the type-conversion builtins they needed.

Fixes a real big-endian bug in the bytecode's 32-bit number reader (found
while adding GameCube/Wii endian support via util/endian.h) and converts
src/dusk/script and duskscriptcc to dusk's memory/string/assert utilities,
which surfaced two more latent bugs: a conservative GC stack scan reading
its own local variable's address, and an uninitialized path buffer in
scriptReadScript when the caller path has no directory component.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
This commit is contained in:
YourWishesandClaude Sonnet 5 committed 2026-10-14 12:59:46 -05:00
1 parent 3948607b1e
commit c1d6a97293
323 files changed
+18389

No files matched your search

+61
View File
@@ -0,0 +1,61 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "dusktest.h"
#include "script/script.h"
#include "console/console.h"
static void test_scriptRunsAndPrintsToConsole(void **state) {
scripterror_t error;
scriptresult_t result;
consoleInit();
assert_int_equal(errorIsOk(scriptInit()), true);
result = scriptRuntimeRunFile(SCRIPT.runtime, DUSKSCRIPT_TEST_FIXTURE, true, &error);
assert_int_equal(result, SCRIPT_OK);
// print() reaches the console through scriptPrintHandler.
assert_string_equal(CONSOLE.line[CONSOLE_HISTORY_MAX - 2], "print hello");
// consolePrint(text), the native, writes directly.
assert_string_equal(CONSOLE.line[CONSOLE_HISTORY_MAX - 1], "consolePrint hello");
assert_int_equal(errorIsOk(scriptDispose()), true);
consoleDispose();
}
static void test_scriptNumbersAreAllFloats(void **state) {
scripterror_t error;
scriptresult_t result;
// In the order fixture_numbers.ds prints them.
const char_t *expected[] = {
"5", "3.75", "2.5", "false", "10", "20", "1", "16", "3", "3.5"
};
const int32_t count = (int32_t)(sizeof(expected) / sizeof(expected[0]));
int32_t i;
consoleInit();
assert_int_equal(errorIsOk(scriptInit()), true);
result = scriptRuntimeRunFile(SCRIPT.runtime, DUSKSCRIPT_TEST_NUMBERS, true, &error);
assert_int_equal(result, SCRIPT_OK);
for(i = 0; i < count; i++) {
assert_string_equal(CONSOLE.line[CONSOLE_HISTORY_MAX - count + i], expected[i]);
}
assert_int_equal(errorIsOk(scriptDispose()), true);
consoleDispose();
}
int main(void) {
const struct CMUnitTest tests[] = {
cmocka_unit_test(test_scriptRunsAndPrintsToConsole),
cmocka_unit_test(test_scriptNumbersAreAllFloats),
};
return cmocka_run_group_tests(tests, NULL, NULL);
}