summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--compiler.c546
-rw-r--r--include/ucode/program.h2
-rw-r--r--tests/custom/04_modules/01_export_variable_declaration29
-rw-r--r--tests/custom/04_modules/02_export_function_declaration22
-rw-r--r--tests/custom/04_modules/03_export_list27
-rw-r--r--tests/custom/04_modules/04_export_rename28
-rw-r--r--tests/custom/04_modules/05_export_default38
-rw-r--r--tests/custom/04_modules/06_export_errors89
-rw-r--r--tests/custom/04_modules/07_import_default99
-rw-r--r--tests/custom/04_modules/08_import_list105
-rw-r--r--tests/custom/04_modules/09_import_wildcard73
-rw-r--r--tests/custom/04_modules/10_import_none18
-rw-r--r--tests/custom/04_modules/11_import_many_exec_once28
-rw-r--r--tests/custom/04_modules/12_import_immutability52
-rw-r--r--tests/custom/04_modules/13_import_liveness29
-rw-r--r--tests/custom/99_bugs/01_try_catch_stack_mismatch (renamed from tests/custom/04_bugs/01_try_catch_stack_mismatch)0
-rw-r--r--tests/custom/99_bugs/02_array_pop_use_after_free (renamed from tests/custom/04_bugs/02_array_pop_use_after_free)0
-rw-r--r--tests/custom/99_bugs/03_switch_fallthrough_miscompilation (renamed from tests/custom/04_bugs/03_switch_fallthrough_miscompilation)0
-rw-r--r--tests/custom/99_bugs/04_property_set_abort (renamed from tests/custom/04_bugs/04_property_set_abort)0
-rw-r--r--tests/custom/99_bugs/05_duplicate_resource_type (renamed from tests/custom/04_bugs/05_duplicate_resource_type)0
-rw-r--r--tests/custom/99_bugs/06_lexer_escape_at_boundary (renamed from tests/custom/04_bugs/06_lexer_escape_at_boundary)0
-rw-r--r--tests/custom/99_bugs/07_lexer_overlong_lines (renamed from tests/custom/04_bugs/07_lexer_overlong_lines)0
-rw-r--r--tests/custom/99_bugs/08_compiler_arrow_fn_expressions (renamed from tests/custom/04_bugs/08_compiler_arrow_fn_expressions)0
-rw-r--r--tests/custom/99_bugs/09_reject_invalid_array_indexes (renamed from tests/custom/04_bugs/09_reject_invalid_array_indexes)0
-rw-r--r--tests/custom/99_bugs/10_break_stack_mismatch (renamed from tests/custom/04_bugs/10_break_stack_mismatch)0
-rw-r--r--tests/custom/99_bugs/11_switch_stack_mismatch (renamed from tests/custom/04_bugs/11_switch_stack_mismatch)0
-rw-r--r--tests/custom/99_bugs/12_altblock_stack_mismatch (renamed from tests/custom/04_bugs/12_altblock_stack_mismatch)0
-rw-r--r--tests/custom/99_bugs/13_split_by_string_leading_trailing (renamed from tests/custom/04_bugs/13_split_by_string_leading_trailing)0
-rw-r--r--tests/custom/99_bugs/14_incomplete_expression_at_eof (renamed from tests/custom/04_bugs/14_incomplete_expression_at_eof)0
-rw-r--r--tests/custom/99_bugs/15_segfault_on_prefix_increment (renamed from tests/custom/04_bugs/15_segfault_on_prefix_increment)0
-rw-r--r--tests/custom/99_bugs/16_hang_on_regexp_at_eof (renamed from tests/custom/04_bugs/16_hang_on_regexp_at_eof)0
-rw-r--r--tests/custom/99_bugs/17_hang_on_unclosed_expression_block (renamed from tests/custom/04_bugs/17_hang_on_unclosed_expression_block)0
-rw-r--r--tests/custom/99_bugs/18_hang_on_line_comments_at_eof (renamed from tests/custom/04_bugs/18_hang_on_line_comments_at_eof)0
-rw-r--r--tests/custom/99_bugs/19_truncated_format_string (renamed from tests/custom/04_bugs/19_truncated_format_string)0
-rw-r--r--tests/custom/99_bugs/20_use_strict_stack_mismatch (renamed from tests/custom/04_bugs/20_use_strict_stack_mismatch)0
-rw-r--r--tests/custom/99_bugs/21_compiler_parenthesized_prop_keyword (renamed from tests/custom/04_bugs/21_compiler_parenthesized_prop_keyword)0
-rw-r--r--tests/custom/99_bugs/22_compiler_break_continue_scoping (renamed from tests/custom/04_bugs/22_compiler_break_continue_scoping)0
-rw-r--r--tests/custom/99_bugs/23_compiler_parenthesized_division (renamed from tests/custom/04_bugs/23_compiler_parenthesized_division)0
-rw-r--r--tests/custom/99_bugs/24_compiler_local_for_loop_declaration (renamed from tests/custom/04_bugs/24_compiler_local_for_loop_declaration)0
-rw-r--r--tests/custom/99_bugs/25_lexer_shifted_offsets (renamed from tests/custom/04_bugs/25_lexer_shifted_offsets)0
-rw-r--r--tests/custom/99_bugs/26_compiler_jmp_to_zero (renamed from tests/custom/04_bugs/26_compiler_jmp_to_zero)0
-rw-r--r--tests/custom/99_bugs/27_invalid_sparse_array_set (renamed from tests/custom/04_bugs/27_invalid_sparse_array_set)0
-rw-r--r--tests/custom/99_bugs/28_null_equality (renamed from tests/custom/04_bugs/28_null_equality)0
-rw-r--r--tests/custom/99_bugs/29_empty_string_as_number (renamed from tests/custom/04_bugs/29_empty_string_as_number)0
-rw-r--r--tests/custom/99_bugs/30_nan_strict_equality (renamed from tests/custom/04_bugs/30_nan_strict_equality)0
-rw-r--r--tests/custom/99_bugs/31_vallist_8bit_shortstrings (renamed from tests/custom/04_bugs/31_vallist_8bit_shortstrings)0
-rw-r--r--tests/custom/99_bugs/32_compiler_switch_patchlist_corruption (renamed from tests/custom/04_bugs/32_compiler_switch_patchlist_corruption)0
-rw-r--r--tests/custom/99_bugs/33_vm_computed_prop_decl_crash (renamed from tests/custom/04_bugs/33_vm_computed_prop_decl_crash)0
-rw-r--r--tests/custom/99_bugs/34_dirname_off_by_one (renamed from tests/custom/04_bugs/34_dirname_off_by_one)0
-rw-r--r--tests/custom/99_bugs/35_vm_callframe_double_free (renamed from tests/custom/04_bugs/35_vm_callframe_double_free)0
-rw-r--r--tests/custom/99_bugs/36_vm_nested_call_return (renamed from tests/custom/04_bugs/36_vm_nested_call_return)0
-rw-r--r--tests/custom/99_bugs/37_compiler_unexpected_unary_op (renamed from tests/custom/04_bugs/37_compiler_unexpected_unary_op)0
-rw-r--r--tests/custom/99_bugs/38_index_segfault (renamed from tests/custom/04_bugs/38_index_segfault)0
-rw-r--r--tests/custom/99_bugs/39_compiler_switch_continue_mismatch (renamed from tests/custom/04_bugs/39_compiler_switch_continue_mismatch)0
-rw-r--r--tests/custom/99_bugs/40_lexer_bug_on_lstrip_off (renamed from tests/custom/04_bugs/40_lexer_bug_on_lstrip_off)0
55 files changed, 1175 insertions, 10 deletions
diff --git a/compiler.c b/compiler.c
index 70d15d6..4878be1 100644
--- a/compiler.c
+++ b/compiler.c
@@ -497,12 +497,23 @@ uc_compiler_set_u32(uc_compiler_t *compiler, size_t off, uint32_t n)
}
static size_t
-uc_compiler_emit_constant(uc_compiler_t *compiler, size_t srcpos, uc_value_t *val)
+uc_compiler_emit_constant_index(uc_compiler_t *compiler, size_t srcpos, uc_value_t *val)
{
size_t cidx = uc_program_add_constant(compiler->program, val);
+ uc_compiler_emit_u32(compiler, srcpos, cidx);
+
+ return cidx;
+}
+
+static size_t
+uc_compiler_emit_constant(uc_compiler_t *compiler, size_t srcpos, uc_value_t *val)
+{
+ size_t cidx;
+
uc_compiler_emit_insn(compiler, srcpos, I_LOAD);
- uc_compiler_emit_u32(compiler, 0, cidx);
+
+ cidx = uc_compiler_emit_constant_index(compiler, srcpos, val);
return cidx;
}
@@ -2924,13 +2935,516 @@ uc_compiler_compile_statement(uc_compiler_t *compiler)
}
static void
-uc_compiler_compile_declaration(uc_compiler_t *compiler)
+uc_compiler_export_add(uc_compiler_t *compiler, uc_value_t *name, ssize_t slot)
+{
+ uc_source_t *source = uc_compiler_current_source(compiler);
+
+ if (!uc_source_export_add(source, name)) {
+ if (name)
+ uc_compiler_syntax_error(compiler, compiler->parser->prev.pos,
+ "Duplicate export '%s' for module '%s'", ucv_string_get(name), source->filename);
+ else
+ uc_compiler_syntax_error(compiler, compiler->parser->prev.pos,
+ "Duplicate default export for module '%s'", source->filename);
+ }
+ else {
+ uc_compiler_emit_insn(compiler, 0, I_EXPORT);
+ uc_compiler_emit_u32(compiler, 0, slot);
+ }
+}
+
+static void
+uc_compiler_compile_exportlist(uc_compiler_t *compiler)
+{
+ uc_value_t *label, *name;
+ bool constant;
+ ssize_t slot;
+
+ /* parse export symbols */
+ do {
+ uc_compiler_parse_consume(compiler, TK_LABEL);
+
+ label = ucv_get(compiler->parser->prev.uv);
+ name = NULL;
+
+ slot = uc_compiler_resolve_local(compiler, label, &constant);
+
+ if (slot == -1) {
+ uc_compiler_syntax_error(compiler, compiler->parser->prev.pos,
+ "Attempt to export undeclared or non-local variable '%s'",
+ ucv_string_get(label));
+ }
+
+ if (uc_compiler_parse_match(compiler, TK_AS)) {
+ if (uc_compiler_parse_match(compiler, TK_LABEL) || uc_compiler_parse_match(compiler, TK_STRING)) {
+ name = ucv_get(compiler->parser->prev.uv);
+ }
+ else if (!uc_compiler_parse_match(compiler, TK_DEFAULT)) {
+ uc_compiler_syntax_error(compiler, compiler->parser->curr.pos,
+ "Unexpected token\nExpecting Label, String or 'default'");
+ }
+ }
+ else {
+ name = ucv_get(label);
+ }
+
+ uc_compiler_export_add(compiler, name, slot);
+
+ ucv_put(label);
+ ucv_put(name);
+
+ if (uc_compiler_parse_match(compiler, TK_RBRACE))
+ break;
+ }
+ while (uc_compiler_parse_match(compiler, TK_COMMA));
+
+ uc_compiler_parse_consume(compiler, TK_SCOL);
+}
+
+static void
+uc_compiler_compile_export(uc_compiler_t *compiler)
+{
+ uc_locals_t *locals = &compiler->locals;
+ size_t off = locals->count;
+ uc_value_t *name;
+ ssize_t slot;
+
+ if (compiler->program->sources.count == 1 || compiler->scope_depth) {
+ uc_compiler_syntax_error(compiler, compiler->parser->prev.pos,
+ "Exports may only appear at top level of a module");
+
+ return;
+ }
+
+ if (uc_compiler_parse_match(compiler, TK_LBRACE)) {
+ uc_compiler_compile_exportlist(compiler);
+
+ return;
+ }
+
+ if (uc_compiler_parse_match(compiler, TK_LOCAL))
+ uc_compiler_compile_declexpr(compiler, false);
+ else if (uc_compiler_parse_match(compiler, TK_CONST))
+ uc_compiler_compile_declexpr(compiler, true);
+ else if (uc_compiler_parse_match(compiler, TK_FUNC))
+ uc_compiler_compile_funcdecl(compiler);
+ else if (uc_compiler_parse_match(compiler, TK_DEFAULT))
+ uc_compiler_compile_expression(compiler);
+ else
+ uc_compiler_syntax_error(compiler, compiler->parser->curr.pos,
+ "Unexpected token\nExpecting 'let', 'const', 'function', 'default' or '{'");
+
+ if (off == locals->count) {
+ name = ucv_string_new("(module default export)");
+ slot = uc_compiler_declare_local(compiler, name, true);
+ ucv_put(name);
+
+ if (slot != -1)
+ uc_compiler_syntax_error(compiler, compiler->parser->prev.pos,
+ "Duplicate default export statement");
+ else
+ uc_compiler_export_add(compiler, NULL, compiler->locals.count - 1);
+ }
+ else {
+ for (; off < locals->count; off++)
+ uc_compiler_export_add(compiler, locals->entries[off].name, off);
+ }
+
+ uc_compiler_parse_consume(compiler, TK_SCOL);
+}
+
+static uc_program_t *
+uc_compile_from_source(uc_parse_config_t *config, uc_source_t *source, uc_program_t *prog, char **errp);
+
+static bool
+uc_compiler_compile_module_source(uc_compiler_t *compiler, uc_source_t *source, uc_value_t *imports, char **errp)
+{
+ uc_parse_config_t config = {
+ .raw_mode = true,
+ .strict_declarations = true,
+ .module_search_path = compiler->parser->lex.config->module_search_path
+ };
+
+ size_t i, load_idx = 0, n_imports = 0;
+ bool loaded = false;
+ uc_value_t *import;
+ ssize_t slot;
+
+ uc_program_function_foreach(compiler->program, fn) {
+ if (uc_program_function_source(fn) == source) {
+ loaded = true;
+ break;
+ }
+ }
+
+ if (!loaded) {
+ load_idx = uc_program_function_id(compiler->program,
+ uc_program_function_last(compiler->program)) + 1;
+
+ if (!uc_compile_from_source(&config, source, compiler->program, errp))
+ return false;
+
+ /* emit load, call & pop instructions */
+ uc_compiler_emit_insn(compiler, compiler->parser->prev.pos, I_CLFN);
+ uc_compiler_emit_u32(compiler, 0, load_idx);
+
+ uc_compiler_emit_insn(compiler, 0, I_CALL);
+ uc_compiler_emit_u32(compiler, 0, 0);
+
+ uc_compiler_emit_insn(compiler, 0, I_POP);
+ }
+
+ /* count imports, handle wildcard imports */
+ for (i = 0; i < ucv_array_length(imports); i++) {
+ if (ucv_boolean_get(ucv_array_get(imports, i))) {
+ /* find index of first module export */
+ slot = uc_program_export_lookup(compiler->program, source, source->exports.entries[0]);
+
+ if (slot > 0xffff || source->exports.count > 0xffff) {
+ uc_compiler_syntax_error(compiler, compiler->parser->prev.pos,
+ "Too many module exports");
+ }
+
+ /* emit import instruction... */
+ uc_compiler_emit_insn(compiler, 0, I_IMPORT);
+ uc_compiler_emit_u32(compiler, 0, source->exports.count | (0xffff << 16));
+
+ /* ... followed by first module export offset ... */
+ uc_compiler_emit_u16(compiler, 0, slot);
+
+ /* ... and constant indexes for all exported names */
+ for (load_idx = 0; load_idx < source->exports.count; load_idx++) {
+ if (source->exports.entries[load_idx])
+ import = ucv_get(source->exports.entries[load_idx]);
+ else
+ import = ucv_string_new("default");
+
+ uc_compiler_emit_constant_index(compiler, 0, import);
+ ucv_put(import);
+ }
+
+ }
+ else {
+ n_imports++;
+ }
+ }
+
+ /* 0xffff is reserved for wildcard import */
+ if (n_imports > 0xfffe)
+ uc_compiler_syntax_error(compiler, 0, "Too many imports");
+
+ /* emit non-wilcard import instructions */
+ for (i = 0; i < ucv_array_length(imports); i++) {
+ import = ucv_array_get(imports, i);
+
+ if (!ucv_boolean_get(import)) {
+ slot = uc_program_export_lookup(compiler->program, source, import);
+
+ if (slot == -1) {
+ if (import)
+ uc_compiler_syntax_error(compiler, compiler->parser->prev.pos,
+ "Module %s does not export '%s'", source->filename, ucv_string_get(import));
+ else
+ uc_compiler_syntax_error(compiler, compiler->parser->prev.pos,
+ "Module %s has no default export", source->filename);
+ }
+ else if (slot > 0xffff) {
+ uc_compiler_syntax_error(compiler, compiler->parser->prev.pos,
+ "Too many module exports");
+ }
+ else {
+ uc_compiler_emit_insn(compiler, 0, I_IMPORT);
+ uc_compiler_emit_u32(compiler, 0, slot | ((compiler->upvals.count - n_imports + i) << 16));
+ }
+ }
+ }
+
+ return true;
+}
+
+static char *
+uc_compiler_canonicalize_path(const char *path, const char *basedir)
+{
+ char *p, *resolved;
+
+ if (*path == '/')
+ xasprintf(&p, "%s", path);
+ else if (basedir)
+ xasprintf(&p, "%s/%s", basedir, path);
+ else
+ xasprintf(&p, "./%s", path);
+
+ resolved = realpath(p, NULL);
+
+ free(p);
+
+ return resolved;
+}
+
+static char *
+uc_compiler_expand_module_path(const char *name, const char *basedir, const char *template)
+{
+ int namelen, prefixlen;
+ char *path, *p;
+
+ p = strchr(template, '*');
+
+ if (!p)
+ return NULL;
+
+ prefixlen = p - template;
+ namelen = strlen(name);
+
+ xasprintf(&path, "%.*s%.*s%s", prefixlen, template, namelen, name, p + 1);
+
+ for (p = path + prefixlen; namelen > 0; namelen--, p++)
+ if (*p == '.')
+ *p = '/';
+
+ p = uc_compiler_canonicalize_path(path, basedir);
+
+ free(path);
+
+ return p;
+}
+
+static char *
+uc_compiler_resolve_module_path(uc_compiler_t *compiler, const char *name)
+{
+ uc_search_path_t *search = &compiler->parser->lex.config->module_search_path;
+ uc_source_t *source = uc_compiler_current_source(compiler);
+ char *path = NULL;
+ size_t i;
+
+ if (strchr(name, '/'))
+ return uc_compiler_canonicalize_path(name, source->runpath);
+
+ for (i = 0; i < search->count && !path; i++)
+ path = uc_compiler_expand_module_path(name, source->runpath, search->entries[i]);
+
+ return path;
+}
+
+static uc_source_t *
+uc_compiler_acquire_source(uc_compiler_t *compiler, const char *path)
+{
+ size_t i;
+
+ for (i = 0; i < compiler->program->sources.count; i++)
+ if (!strcmp(compiler->program->sources.entries[i]->filename, path))
+ return uc_source_get(compiler->program->sources.entries[i]);
+
+ return uc_source_new_file(path);
+}
+
+static bool
+uc_compiler_compile_module(uc_compiler_t *compiler, const char *name, uc_value_t *imports)
+{
+ uc_source_t *source;
+ char *path, *err;
+ bool res;
+
+ if (!name)
+ return false;
+
+ path = uc_compiler_resolve_module_path(compiler, name);
+
+ if (path) {
+ source = uc_compiler_acquire_source(compiler, path);
+
+ if (source) {
+ err = NULL;
+ res = uc_compiler_compile_module_source(compiler, source, imports, &err);
+
+ if (!res)
+ uc_compiler_syntax_error(compiler, compiler->parser->curr.pos,
+ "Unable to compile module '%s':\n%s", source->filename, err);
+
+ free(err);
+ }
+ else {
+ uc_compiler_syntax_error(compiler, compiler->parser->curr.pos,
+ "Unable to open module '%s': %s",
+ path, strerror(errno));
+
+ res = false;
+ }
+ }
+ else {
+ uc_compiler_syntax_error(compiler, compiler->parser->curr.pos,
+ "Unable to resolve path for module '%s'", name);
+
+ return false;
+ }
+
+ uc_source_put(source);
+ free(path);
+
+ return res;
+}
+
+static void
+uc_compiler_import_add(uc_compiler_t *compiler, uc_value_t *name)
+{
+ bool constant;
+ ssize_t slot;
+
+ slot = uc_compiler_resolve_local(compiler, name, &constant);
+
+ if (slot != -1) {
+ uc_compiler_syntax_error(compiler, compiler->parser->prev.pos,
+ "Import name '%s' is already declared as local variable",
+ ucv_string_get(name));
+
+ return;
+ }
+
+ slot = uc_compiler_resolve_upval(compiler, name, &constant);
+
+ if (slot != -1) {
+ uc_compiler_syntax_error(compiler, compiler->parser->prev.pos,
+ "Import name '%s' is already used",
+ ucv_string_get(name));
+
+ return;
+ }
+
+ uc_compiler_add_upval(compiler, (2 << 14) + compiler->upvals.count, false, name, true);
+}
+
+static void
+uc_compiler_compile_importlist(uc_compiler_t *compiler, uc_value_t *namelist)
{
+ uc_value_t *label, *name;
+ /* parse export symbols */
+ do {
+ name = NULL;
+ label = NULL;
+
+ if (uc_compiler_parse_match(compiler, TK_DEFAULT)) {
+ uc_compiler_parse_consume(compiler, TK_AS);
+ uc_compiler_parse_consume(compiler, TK_LABEL);
+
+ label = ucv_get(compiler->parser->prev.uv);
+ }
+ else if (uc_compiler_parse_match(compiler, TK_STRING)) {
+ name = ucv_get(compiler->parser->prev.uv);
+
+ uc_compiler_parse_consume(compiler, TK_AS);
+ uc_compiler_parse_consume(compiler, TK_LABEL);
+
+ label = ucv_get(compiler->parser->prev.uv);
+ }
+ else if (uc_compiler_parse_match(compiler, TK_LABEL)) {
+ name = ucv_get(compiler->parser->prev.uv);
+
+ if (uc_compiler_parse_match(compiler, TK_AS)) {
+ uc_compiler_parse_consume(compiler, TK_LABEL);
+
+ label = ucv_get(compiler->parser->prev.uv);
+ }
+ else {
+ label = ucv_get(name);
+ }
+ }
+ else {
+ uc_compiler_syntax_error(compiler, compiler->parser->curr.pos,
+ "Unexpected token\nExpecting Label, String or 'default'");
+ }
+
+ uc_compiler_import_add(compiler, label);
+ ucv_array_push(namelist, name);
+ ucv_put(label);
+
+ if (uc_compiler_parse_match(compiler, TK_RBRACE))
+ break;
+ }
+ while (uc_compiler_parse_match(compiler, TK_COMMA));
+}
+
+static void
+uc_compiler_compile_import(uc_compiler_t *compiler)
+{
+ uc_value_t *namelist = ucv_array_new(NULL);
+
+ if (compiler->scope_depth) {
+ uc_compiler_syntax_error(compiler, compiler->parser->prev.pos,
+ "Imports may only appear at top level");
+
+ return;
+ }
+
+ /* import { ... } from */
+ if (uc_compiler_parse_match(compiler, TK_LBRACE)) {
+ uc_compiler_compile_importlist(compiler, namelist);
+ uc_compiler_parse_consume(compiler, TK_FROM);
+ }
+
+ /* import * as name from */
+ else if (uc_compiler_parse_match(compiler, TK_MUL)) {
+ uc_compiler_parse_consume(compiler, TK_AS);
+ uc_compiler_parse_consume(compiler, TK_LABEL);
+
+ uc_compiler_declare_local(compiler, compiler->parser->prev.uv, true);
+ uc_compiler_initialize_local(compiler);
+ ucv_array_push(namelist, ucv_boolean_new(true));
+
+ uc_compiler_parse_consume(compiler, TK_FROM);
+ }
+
+ /* import defaultExport [, ... ] from */
+ else if (uc_compiler_parse_match(compiler, TK_LABEL)) {
+ uc_compiler_import_add(compiler, compiler->parser->prev.uv);
+ ucv_array_push(namelist, NULL);
+
+ /* import defaultExport, ... from */
+ if (uc_compiler_parse_match(compiler, TK_COMMA)) {
+ /* import defaultExport, { ... } from */
+ if (uc_compiler_parse_match(compiler, TK_LBRACE)) {
+ uc_compiler_compile_importlist(compiler, namelist);
+ }
+
+ /* import defaultExport, * as name from */
+ else if (uc_compiler_parse_match(compiler, TK_MUL)) {
+ uc_compiler_parse_consume(compiler, TK_AS);
+ uc_compiler_parse_consume(compiler, TK_LABEL);
+
+ uc_compiler_declare_local(compiler, compiler->parser->prev.uv, true);
+ uc_compiler_initialize_local(compiler);
+ ucv_array_push(namelist, ucv_boolean_new(true));
+ }
+
+ /* error */
+ else {
+ uc_compiler_syntax_error(compiler, compiler->parser->curr.pos,
+ "Unexpected token\nExpecting '{' or '*'");
+ }
+ }
+
+ uc_compiler_parse_consume(compiler, TK_FROM);
+ }
+
+ uc_compiler_parse_consume(compiler, TK_STRING);
+
+ uc_compiler_compile_module(compiler, ucv_string_get(compiler->parser->prev.uv), namelist);
+
+ uc_compiler_parse_consume(compiler, TK_SCOL);
+
+ ucv_put(namelist);
+}
+
+static void
+uc_compiler_compile_declaration(uc_compiler_t *compiler)
+{
if (uc_compiler_parse_match(compiler, TK_LOCAL))
uc_compiler_compile_local(compiler);
else if (uc_compiler_parse_match(compiler, TK_CONST))
uc_compiler_compile_const(compiler);
+ else if (uc_compiler_parse_match(compiler, TK_EXPORT))
+ uc_compiler_compile_export(compiler);
+ else if (uc_compiler_parse_match(compiler, TK_IMPORT))
+ uc_compiler_compile_import(compiler);
else
uc_compiler_compile_statement(compiler);
@@ -2942,7 +3456,7 @@ uc_compiler_compile_declaration(uc_compiler_t *compiler)
static uc_program_t *
-uc_compile_from_source(uc_parse_config_t *config, uc_source_t *source, char **errp)
+uc_compile_from_source(uc_parse_config_t *config, uc_source_t *source, uc_program_t *prog, char **errp)
{
#ifdef NO_COMPILE
if (errp)
@@ -2953,13 +3467,21 @@ uc_compile_from_source(uc_parse_config_t *config, uc_source_t *source, char **er
uc_exprstack_t expr = { .token = TK_EOF };
uc_parser_t parser = { .config = config };
uc_compiler_t compiler = { .parser = &parser, .exprstack = &expr };
- uc_program_t *prog;
+ uc_program_t *progptr;
uc_function_t *fn;
+ const char *name;
- prog = uc_program_new();
+ if (!prog) {
+ progptr = uc_program_new();
+ name = "main";
+ }
+ else {
+ progptr = prog;
+ name = "module";
+ }
uc_lexer_init(&parser.lex, config, source);
- uc_compiler_init(&compiler, "main", source, 0, prog,
+ uc_compiler_init(&compiler, name, source, 0, progptr,
config && config->strict_declarations);
uc_compiler_parse_advance(&compiler);
@@ -2980,12 +3502,13 @@ uc_compile_from_source(uc_parse_config_t *config, uc_source_t *source, char **er
uc_lexer_free(&parser.lex);
if (!fn) {
- ucv_put(&prog->header);
+ if (progptr != prog)
+ ucv_put(&progptr->header);
return NULL;
}
- return prog;
+ return progptr;
#endif
}
@@ -3011,9 +3534,12 @@ uc_compile(uc_parse_config_t *config, uc_source_t *source, char **errp)
{
uc_program_t *prog = NULL;
+ if (!config)
+ config = &uc_default_parse_config;
+
switch (uc_source_type_test(source)) {
case UC_SOURCE_TYPE_PLAIN:
- prog = uc_compile_from_source(config, source, errp);
+ prog = uc_compile_from_source(config, source, NULL, errp);
break;
case UC_SOURCE_TYPE_PRECOMPILED:
diff --git a/include/ucode/program.h b/include/ucode/program.h
index 4fb4a9b..c350fea 100644
--- a/include/ucode/program.h
+++ b/include/ucode/program.h
@@ -46,6 +46,8 @@ uc_program_put(uc_program_t *prog) {
fn = fn##_tmp, \
fn##_tmp = (uc_function_t *)fn##_tmp->progref.prev)
+#define uc_program_function_last(prog) (uc_function_t *)prog->functions.next
+
uc_function_t *uc_program_function_new(uc_program_t *, const char *, uc_source_t *, size_t);
size_t uc_program_function_id(uc_program_t *, uc_function_t *);
uc_function_t *uc_program_function_load(uc_program_t *, size_t);
diff --git a/tests/custom/04_modules/01_export_variable_declaration b/tests/custom/04_modules/01_export_variable_declaration
new file mode 100644
index 0000000..19a1c11
--- /dev/null
+++ b/tests/custom/04_modules/01_export_variable_declaration
@@ -0,0 +1,29 @@
+Variable declarations can be prepended with `export` to automatically
+export each variable using the same name as the variable itself.
+
+Updates to the variable after the export are reflected properly in
+the including scope.
+
+-- File test-var-decl.uc --
+export let a, b, c;
+export let d = 4, e = 5, f = 6;
+export const g = 7, h = 8, i = 9;
+
+a = 1;
+b = 2;
+c = 3;
+-- End --
+
+-- Testcase --
+import { a, b, c, d, e, f, g, h, i } from "./files/test-var-decl.uc";
+
+print([ a, b, c, d, e, f, g, h, i ], "\n");
+-- End --
+
+-- Args --
+-R
+-- End --
+
+-- Expect stdout --
+[ 1, 2, 3, 4, 5, 6, 7, 8, 9 ]
+-- End --
diff --git a/tests/custom/04_modules/02_export_function_declaration b/tests/custom/04_modules/02_export_function_declaration
new file mode 100644
index 0000000..4067da9
--- /dev/null
+++ b/tests/custom/04_modules/02_export_function_declaration
@@ -0,0 +1,22 @@
+A named function declaration can be prepended with `export` to
+automatically export the function.
+
+-- File test-func-decl.uc --
+export function func() {
+ print("Hello, world!\n");
+};
+-- End --
+
+-- Testcase --
+import { func } from "./files/test-func-decl.uc";
+
+func();
+-- End --
+
+-- Args --
+-R
+-- End --
+
+-- Expect stdout --
+Hello, world!
+-- End --
diff --git a/tests/custom/04_modules/03_export_list b/tests/custom/04_modules/03_export_list
new file mode 100644
index 0000000..8f93f08
--- /dev/null
+++ b/tests/custom/04_modules/03_export_list
@@ -0,0 +1,27 @@
+Already declared local variables and functions may be exported using the
+curly brace export list syntax.
+
+-- File test-var-decl.uc --
+let testvar = 123;
+const testconst = "Test";
+
+function testfunc() {
+ print("Hello, world!\n");
+}
+
+export { testvar, testconst, testfunc };
+-- End --
+
+-- Testcase --
+import { testvar, testconst, testfunc } from "./files/test-var-decl.uc";
+
+print([ testvar, testconst, testfunc ], "\n");
+-- End --
+
+-- Args --
+-R
+-- End --
+
+-- Expect stdout --
+[ 123, "Test", "function testfunc() { ... }" ]
+-- End --
diff --git a/tests/custom/04_modules/04_export_rename b/tests/custom/04_modules/04_export_rename
new file mode 100644
index 0000000..49057fd
--- /dev/null
+++ b/tests/custom/04_modules/04_export_rename
@@ -0,0 +1,28 @@
+By using the `as` keyword, exports may be renamed when using the export
+list syntax. It is also possible to specify string aliases which are not
+valid variable names, in this case a rename on import is mandatory.
+
+-- File test.uc --
+let testvar = 123;
+const testconst = "Test";
+
+function testfunc() {
+ print("Hello, world!\n");
+}
+
+export { testvar as modvar, testconst as 'define', testfunc as "module-function" };
+-- End --
+
+-- Testcase --
+import { modvar, define, "module-function" as func } from "./files/test.uc";
+
+print([ modvar, define, func ], "\n");
+-- End --
+
+-- Args --
+-R
+-- End --
+
+-- Expect stdout --
+[ 123, "Test", "function testfunc() { ... }" ]
+-- End --
diff --git a/tests/custom/04_modules/05_export_default b/tests/custom/04_modules/05_export_default
new file mode 100644
index 0000000..a4c8a43
--- /dev/null
+++ b/tests/custom/04_modules/05_export_default
@@ -0,0 +1,38 @@
+The `export default` statement can be used to declare a default export
+value for a module. The value for `export default` can be an arbitrary
+expression, it must not refer to a local variable.
+
+When using the export list syntax, the alias "default" can be used to
+designate the default export.
+
+-- File test-default-expr.uc --
+export default 7 * 21;
+-- End --
+
+-- File test-default-func.uc --
+export default function() {
+ return "Hello, world!";
+};
+-- End --
+
+-- File test-default-alias.uc --
+let a = 1, b = 2, c = 3;
+
+export { a, b as default, c };
+-- End --
+
+-- Testcase --
+import def1 from "./files/test-default-expr.uc";
+import def2 from "./files/test-default-func.uc";
+import def3 from "./files/test-default-alias.uc";
+
+print([ def1, def2(), def3 ], "\n");
+-- End --
+
+-- Args --
+-R
+-- End --
+
+-- Expect stdout --
+[ 147, "Hello, world!", 2 ]
+-- End --
diff --git a/tests/custom/04_modules/06_export_errors b/tests/custom/04_modules/06_export_errors
new file mode 100644
index 0000000..c02a547
--- /dev/null
+++ b/tests/custom/04_modules/06_export_errors
@@ -0,0 +1,89 @@
+Export statements are only allowed at the toplevel of a module.
+
+-- Testcase --
+export let x = 1;
+-- End --
+
+-- Args --
+-R
+-- End --
+
+-- Expect stderr --
+Syntax error: Exports may only appear at top level of a module
+
+ `export let x = 1;`
+ ^-- Near here
+
+
+-- End --
+
+
+Export statements are not allowed within functions or nested blocks.
+
+-- Testcase --
+import "./files/test.uc";
+-- End --
+
+-- File test.uc --
+{
+ export let x = 1;
+}
+-- End --
+
+-- Args --
+-R
+-- End --
+
+-- Expect stderr --
+Syntax error: Unable to compile module './files/test.uc':
+Syntax error: Exports may only appear at top level of a module
+In line 2, byte 2:
+
+ ` export let x = 1;`
+ ^-- Near here
+
+
+
+In line 1, byte 25:
+
+ `import "./files/test.uc";`
+ Near here --------------^
+
+
+-- End --
+
+
+Duplicate export names should result in an error.
+
+-- Testcase --
+import "./files/test-duplicate.uc";
+-- End --
+
+-- File test-duplicate.uc --
+let x = 1, y = 2;
+
+export { x };
+export { y as x };
+-- End --
+
+-- Args --
+-R
+-- End --
+
+-- Expect stderr --
+Syntax error: Unable to compile module './files/test-duplicate.uc':
+Syntax error: Duplicate export 'x' for module './files/test-duplicate.uc'
+In line 4, byte 15:
+
+ `export { y as x };`
+ Near here ----^
+
+
+
+In line 1, byte 35:
+
+ `import "./files/test-duplicate.uc";`
+ Near here ------------------------^
+
+
+-- End --
diff --git a/tests/custom/04_modules/07_import_default b/tests/custom/04_modules/07_import_default
new file mode 100644
index 0000000..7190a22
--- /dev/null
+++ b/tests/custom/04_modules/07_import_default
@@ -0,0 +1,99 @@
+An `import` statement with a sole label will import the modules default
+export and bind it to a local variable named after the label.
+
+-- Testcase --
+import defVal from "./files/test1.uc";
+
+print(defVal, "\n");
+-- End --
+
+-- File test1.uc --
+export default "This is the default export";
+-- End --
+
+-- Args --
+-R
+-- End --
+
+-- Expect stdout --
+This is the default export
+-- End --
+
+
+Attemping to import a default export from a module without default
+export will raise an error.
+
+-- Testcase --
+import defVal from "./files/test2.uc";
+
+print(defVal, "\n");
+-- End --
+
+-- File test2.uc --
+export const x = "This is a non-default export";
+-- End --
+
+-- Args --
+-R
+-- End --
+
+-- Expect stderr --
+Syntax error: Module ./files/test2.uc has no default export
+In line 1, byte 20:
+
+ `import defVal from "./files/test2.uc";`
+ Near here ---------^
+
+
+-- End --
+
+
+In import statements usign the list syntax, the `default` keyword can be
+used to refer to default exports.
+
+-- Testcase --
+import { default as defVal } from "./files/test3.uc";
+
+print(defVal, "\n");
+-- End --
+
+-- File test3.uc --
+export default "This is the default export";
+-- End --
+
+-- Args --
+-R
+-- End --
+
+-- Expect stdout --
+This is the default export
+-- End --
+
+
+When using the default keyword within the list syntax, the `as` keyword is
+mandatory to assign a non-reserved keyword as name.
+
+-- Testcase --
+import { default } from "./files/test4.uc";
+
+print(defVal, "\n");
+-- End --
+
+-- File test4.uc --
+export default "This is the default export";
+-- End --
+
+-- Args --
+-R
+-- End --
+
+-- Expect stderr --
+Syntax error: Unexpected token
+Expecting 'as'
+In line 1, byte 18:
+
+ `import { default } from "./files/test4.uc";`
+ Near here -------^
+
+
+-- End --
diff --git a/tests/custom/04_modules/08_import_list b/tests/custom/04_modules/08_import_list
new file mode 100644
index 0000000..1a4f116
--- /dev/null
+++ b/tests/custom/04_modules/08_import_list
@@ -0,0 +1,105 @@
+An `import` statement followed by a curly brace enclosed list of names
+will import the corresponding exports from the module.
+
+-- Testcase --
+import { a, b, c } from "./files/test1.uc";
+
+print([ a, b, c ], "\n");
+-- End --
+
+-- File test1.uc --
+export const a = 1, b = 2, c = 3;
+-- End --
+
+-- Args --
+-R
+-- End --
+
+-- Expect stdout --
+[ 1, 2, 3 ]
+-- End --
+
+
+Attemping to import a not exported name will raise an error.
+
+-- Testcase --
+import y from "./files/test2.uc";
+
+print(y, "\n");
+-- End --
+
+-- File test2.uc --
+export const x = "This is a test";
+-- End --
+
+-- Args --
+-R
+-- End --
+
+-- Expect stderr --
+Syntax error: Module ./files/test2.uc has no default export
+In line 1, byte 15:
+
+ `import y from "./files/test2.uc";`
+ Near here ----^
+
+
+-- End --
+
+
+Imports may be renamed to assign an alternative local name to the
+exported module symbols. Renaming is also required for string export
+names which are no valid variable identifiers.
+
+-- Testcase --
+import { a as var1, bool as var2, "my function" as var3 } from "./files/test3.uc";
+
+print([ var1, var2, var3 ], "\n");
+-- End --
+
+-- File test3.uc --
+const a = "A string";
+
+let b = 123;
+
+function c() {
+ return "A function"
+}
+
+export {
+ a,
+ b as bool,
+ c as "my function"
+};
+-- End --
+
+-- Args --
+-R
+-- End --
+
+-- Expect stdout --
+[ "A string", 123, "function c() { ... }" ]
+-- End --
+
+
+A list expression may follow a default import expression in an `import`
+statment.
+
+-- Testcase --
+import defVal, { a as x, b as y, c as z } from "./files/test4.uc";
+
+print([defVal, x, y, z], "\n");
+-- End --
+
+-- File test4.uc --
+export const a = 1, b = 2, c = 3;
+export default a + b + c;
+-- End --
+
+-- Args --
+-R
+-- End --
+
+-- Expect stdout --
+[ 6, 1, 2, 3 ]
+-- End --
diff --git a/tests/custom/04_modules/09_import_wildcard b/tests/custom/04_modules/09_import_wildcard
new file mode 100644
index 0000000..aa3dc82
--- /dev/null
+++ b/tests/custom/04_modules/09_import_wildcard
@@ -0,0 +1,73 @@
+By specifying `*` instead of a label or an import list after an `import`
+keyword, all of the modules exports are aggregated into an object whose
+keys and values refer to the exported names and their corresponding
+values respectively.
+
+-- Testcase --
+import * as mod from "./files/test1.uc";
+
+print(mod, "\n");
+-- End --
+
+-- File test1.uc --
+export const a = 1, b = 2, c = 3;
+export default a + b + c;
+-- End --
+
+-- Args --
+-R
+-- End --
+
+-- Expect stdout --
+{ "a": 1, "b": 2, "c": 3, "default": 6 }
+-- End --
+
+
+When using the wildcard import syntax, assigning a name using the `as`
+expression is mandatory.
+
+-- Testcase --
+import * from "./files/test2.uc";
+-- End --
+
+-- File test2.uc --
+export const x = "This is a test";
+-- End --
+
+-- Args --
+-R
+-- End --
+
+-- Expect stderr --
+Syntax error: Unexpected token
+Expecting 'as'
+In line 1, byte 10:
+
+ `import * from "./files/test2.uc";`
+ ^-- Near here
+
+
+-- End --
+
+
+A wildcard expression may follow a default import expression in an `import`
+statment.
+
+-- Testcase --
+import defVal, * as mod from "./files/test3.uc";
+
+print([defVal, mod], "\n");
+-- End --
+
+-- File test3.uc --
+export const a = 1, b = 2, c = 3;
+export default a + b + c;
+-- End --
+
+-- Args --
+-R
+-- End --
+
+-- Expect stdout --
+[ 6, { "a": 1, "b": 2, "c": 3, "default": 6 } ]
+-- End --
diff --git a/tests/custom/04_modules/10_import_none b/tests/custom/04_modules/10_import_none
new file mode 100644
index 0000000..be30106
--- /dev/null
+++ b/tests/custom/04_modules/10_import_none
@@ -0,0 +1,18 @@
+An `import` statement may omit a default name, wildcard expression or name
+lsit entirely to execute a module code solely for its side effects.
+
+-- Testcase --
+import "./files/test.uc";
+-- End --
+
+-- File test.uc --
+print("This is the test module running\n");
+-- End --
+
+-- Args --
+-R
+-- End --
+
+-- Expect stdout --
+This is the test module running
+-- End --
diff --git a/tests/custom/04_modules/11_import_many_exec_once b/tests/custom/04_modules/11_import_many_exec_once
new file mode 100644
index 0000000..f469c7f
--- /dev/null
+++ b/tests/custom/04_modules/11_import_many_exec_once
@@ -0,0 +1,28 @@
+When multiple imports refer to the same module, the module will only be
+executed once. The equivalence of module paths is tested after canonicalizing
+the requested path.
+
+-- Testcase --
+import { counter as counter1 } from "./files/test/example.uc";
+import { counter as counter2 } from "files/test/example.uc";
+import { counter as counter3 } from "test.example";
+
+print([ counter1, counter2, counter3 ], "\n");
+-- End --
+
+-- File test/example.uc --
+print("This is the test module running\n");
+
+export let counter = 0;
+
+counter++;
+-- End --
+
+-- Args --
+-R -L ./files
+-- End --
+
+-- Expect stdout --
+This is the test module running
+[ 1, 1, 1 ]
+-- End --
diff --git a/tests/custom/04_modules/12_import_immutability b/tests/custom/04_modules/12_import_immutability
new file mode 100644
index 0000000..37c0bc6
--- /dev/null
+++ b/tests/custom/04_modules/12_import_immutability
@@ -0,0 +1,52 @@
+Module imports are read-only bindings to the exported module variables.
+
+-- Testcase --
+import { a } from "./files/test.uc";
+
+a = 2;
+-- End --
+
+-- File test.uc --
+export let a = 1;
+-- End --
+
+-- Args --
+-R
+-- End --
+
+-- Expect stderr --
+Syntax error: Invalid assignment to constant 'a'
+In line 3, byte 5:
+
+ `a = 2;`
+ ^-- Near here
+
+
+-- End --
+
+
+Aggregated module objects are read-only as well.
+
+-- Testcase --
+import * as mod from "./files/test.uc";
+
+mod.a = 2;
+-- End --
+
+-- File test.uc --
+export let a = 1;
+-- End --
+
+-- Args --
+-R
+-- End --
+
+-- Expect stderr --
+Type error: object value is immutable
+In line 3, byte 9:
+
+ `mod.a = 2;`
+ ^-- Near here
+
+
+-- End --
diff --git a/tests/custom/04_modules/13_import_liveness b/tests/custom/04_modules/13_import_liveness
new file mode 100644
index 0000000..ca7ff35
--- /dev/null
+++ b/tests/custom/04_modules/13_import_liveness
@@ -0,0 +1,29 @@
+Imported bindings to exported module variables are live, they'll reflect
+every change to the exported variable values.
+
+-- Testcase --
+import { counter, count } from "./files/test.uc";
+
+print(counter, "\n");
+count();
+print(counter, "\n");
+-- End --
+
+-- File test.uc --
+let counter = 1;
+
+function count() {
+ counter++;
+}
+
+export { counter, count };
+-- End --
+
+-- Args --
+-R
+-- End --
+
+-- Expect stdout --
+1
+2
+-- End --
diff --git a/tests/custom/04_bugs/01_try_catch_stack_mismatch b/tests/custom/99_bugs/01_try_catch_stack_mismatch
index f6e5a0a..f6e5a0a 100644
--- a/tests/custom/04_bugs/01_try_catch_stack_mismatch
+++ b/tests/custom/99_bugs/01_try_catch_stack_mismatch
diff --git a/tests/custom/04_bugs/02_array_pop_use_after_free b/tests/custom/99_bugs/02_array_pop_use_after_free
index 22f63ff..22f63ff 100644
--- a/tests/custom/04_bugs/02_array_pop_use_after_free
+++ b/tests/custom/99_bugs/02_array_pop_use_after_free
diff --git a/tests/custom/04_bugs/03_switch_fallthrough_miscompilation b/tests/custom/99_bugs/03_switch_fallthrough_miscompilation
index 3e6410e..3e6410e 100644
--- a/tests/custom/04_bugs/03_switch_fallthrough_miscompilation
+++ b/tests/custom/99_bugs/03_switch_fallthrough_miscompilation
diff --git a/tests/custom/04_bugs/04_property_set_abort b/tests/custom/99_bugs/04_property_set_abort
index 8af477f..8af477f 100644
--- a/tests/custom/04_bugs/04_property_set_abort
+++ b/tests/custom/99_bugs/04_property_set_abort
diff --git a/tests/custom/04_bugs/05_duplicate_resource_type b/tests/custom/99_bugs/05_duplicate_resource_type
index 6d8d8f5..6d8d8f5 100644
--- a/tests/custom/04_bugs/05_duplicate_resource_type
+++ b/tests/custom/99_bugs/05_duplicate_resource_type
diff --git a/tests/custom/04_bugs/06_lexer_escape_at_boundary b/tests/custom/99_bugs/06_lexer_escape_at_boundary
index e80b0a1..e80b0a1 100644
--- a/tests/custom/04_bugs/06_lexer_escape_at_boundary
+++ b/tests/custom/99_bugs/06_lexer_escape_at_boundary
diff --git a/tests/custom/04_bugs/07_lexer_overlong_lines b/tests/custom/99_bugs/07_lexer_overlong_lines
index d2dd3be..d2dd3be 100644
--- a/tests/custom/04_bugs/07_lexer_overlong_lines
+++ b/tests/custom/99_bugs/07_lexer_overlong_lines
diff --git a/tests/custom/04_bugs/08_compiler_arrow_fn_expressions b/tests/custom/99_bugs/08_compiler_arrow_fn_expressions
index 5cd8960..5cd8960 100644
--- a/tests/custom/04_bugs/08_compiler_arrow_fn_expressions
+++ b/tests/custom/99_bugs/08_compiler_arrow_fn_expressions
diff --git a/tests/custom/04_bugs/09_reject_invalid_array_indexes b/tests/custom/99_bugs/09_reject_invalid_array_indexes
index a7e5272..a7e5272 100644
--- a/tests/custom/04_bugs/09_reject_invalid_array_indexes
+++ b/tests/custom/99_bugs/09_reject_invalid_array_indexes
diff --git a/tests/custom/04_bugs/10_break_stack_mismatch b/tests/custom/99_bugs/10_break_stack_mismatch
index c9c82c5..c9c82c5 100644
--- a/tests/custom/04_bugs/10_break_stack_mismatch
+++ b/tests/custom/99_bugs/10_break_stack_mismatch
diff --git a/tests/custom/04_bugs/11_switch_stack_mismatch b/tests/custom/99_bugs/11_switch_stack_mismatch
index 0cf82f0..0cf82f0 100644
--- a/tests/custom/04_bugs/11_switch_stack_mismatch
+++ b/tests/custom/99_bugs/11_switch_stack_mismatch
diff --git a/tests/custom/04_bugs/12_altblock_stack_mismatch b/tests/custom/99_bugs/12_altblock_stack_mismatch
index e350660..e350660 100644
--- a/tests/custom/04_bugs/12_altblock_stack_mismatch
+++ b/tests/custom/99_bugs/12_altblock_stack_mismatch
diff --git a/tests/custom/04_bugs/13_split_by_string_leading_trailing b/tests/custom/99_bugs/13_split_by_string_leading_trailing
index 10a6062..10a6062 100644
--- a/tests/custom/04_bugs/13_split_by_string_leading_trailing
+++ b/tests/custom/99_bugs/13_split_by_string_leading_trailing
diff --git a/tests/custom/04_bugs/14_incomplete_expression_at_eof b/tests/custom/99_bugs/14_incomplete_expression_at_eof
index 474e87c..474e87c 100644
--- a/tests/custom/04_bugs/14_incomplete_expression_at_eof
+++ b/tests/custom/99_bugs/14_incomplete_expression_at_eof
diff --git a/tests/custom/04_bugs/15_segfault_on_prefix_increment b/tests/custom/99_bugs/15_segfault_on_prefix_increment
index 280b680..280b680 100644
--- a/tests/custom/04_bugs/15_segfault_on_prefix_increment
+++ b/tests/custom/99_bugs/15_segfault_on_prefix_increment
diff --git a/tests/custom/04_bugs/16_hang_on_regexp_at_eof b/tests/custom/99_bugs/16_hang_on_regexp_at_eof
index d8702ca..d8702ca 100644
--- a/tests/custom/04_bugs/16_hang_on_regexp_at_eof
+++ b/tests/custom/99_bugs/16_hang_on_regexp_at_eof
diff --git a/tests/custom/04_bugs/17_hang_on_unclosed_expression_block b/tests/custom/99_bugs/17_hang_on_unclosed_expression_block
index 29553ab..29553ab 100644
--- a/tests/custom/04_bugs/17_hang_on_unclosed_expression_block
+++ b/tests/custom/99_bugs/17_hang_on_unclosed_expression_block
diff --git a/tests/custom/04_bugs/18_hang_on_line_comments_at_eof b/tests/custom/99_bugs/18_hang_on_line_comments_at_eof
index 5fc811e..5fc811e 100644
--- a/tests/custom/04_bugs/18_hang_on_line_comments_at_eof
+++ b/tests/custom/99_bugs/18_hang_on_line_comments_at_eof
diff --git a/tests/custom/04_bugs/19_truncated_format_string b/tests/custom/99_bugs/19_truncated_format_string
index ead0fdb..ead0fdb 100644
--- a/tests/custom/04_bugs/19_truncated_format_string
+++ b/tests/custom/99_bugs/19_truncated_format_string
diff --git a/tests/custom/04_bugs/20_use_strict_stack_mismatch b/tests/custom/99_bugs/20_use_strict_stack_mismatch
index 7294d23..7294d23 100644
--- a/tests/custom/04_bugs/20_use_strict_stack_mismatch
+++ b/tests/custom/99_bugs/20_use_strict_stack_mismatch
diff --git a/tests/custom/04_bugs/21_compiler_parenthesized_prop_keyword b/tests/custom/99_bugs/21_compiler_parenthesized_prop_keyword
index 472b2af..472b2af 100644
--- a/tests/custom/04_bugs/21_compiler_parenthesized_prop_keyword
+++ b/tests/custom/99_bugs/21_compiler_parenthesized_prop_keyword
diff --git a/tests/custom/04_bugs/22_compiler_break_continue_scoping b/tests/custom/99_bugs/22_compiler_break_continue_scoping
index 461b144..461b144 100644
--- a/tests/custom/04_bugs/22_compiler_break_continue_scoping
+++ b/tests/custom/99_bugs/22_compiler_break_continue_scoping
diff --git a/tests/custom/04_bugs/23_compiler_parenthesized_division b/tests/custom/99_bugs/23_compiler_parenthesized_division
index a70703f..a70703f 100644
--- a/tests/custom/04_bugs/23_compiler_parenthesized_division
+++ b/tests/custom/99_bugs/23_compiler_parenthesized_division
diff --git a/tests/custom/04_bugs/24_compiler_local_for_loop_declaration b/tests/custom/99_bugs/24_compiler_local_for_loop_declaration
index aafde55..aafde55 100644
--- a/tests/custom/04_bugs/24_compiler_local_for_loop_declaration
+++ b/tests/custom/99_bugs/24_compiler_local_for_loop_declaration
diff --git a/tests/custom/04_bugs/25_lexer_shifted_offsets b/tests/custom/99_bugs/25_lexer_shifted_offsets
index db10121..db10121 100644
--- a/tests/custom/04_bugs/25_lexer_shifted_offsets
+++ b/tests/custom/99_bugs/25_lexer_shifted_offsets
diff --git a/tests/custom/04_bugs/26_compiler_jmp_to_zero b/tests/custom/99_bugs/26_compiler_jmp_to_zero
index e7e0127..e7e0127 100644
--- a/tests/custom/04_bugs/26_compiler_jmp_to_zero
+++ b/tests/custom/99_bugs/26_compiler_jmp_to_zero
diff --git a/tests/custom/04_bugs/27_invalid_sparse_array_set b/tests/custom/99_bugs/27_invalid_sparse_array_set
index 4c47039..4c47039 100644
--- a/tests/custom/04_bugs/27_invalid_sparse_array_set
+++ b/tests/custom/99_bugs/27_invalid_sparse_array_set
diff --git a/tests/custom/04_bugs/28_null_equality b/tests/custom/99_bugs/28_null_equality
index b71a3b1..b71a3b1 100644
--- a/tests/custom/04_bugs/28_null_equality
+++ b/tests/custom/99_bugs/28_null_equality
diff --git a/tests/custom/04_bugs/29_empty_string_as_number b/tests/custom/99_bugs/29_empty_string_as_number
index 51a93b2..51a93b2 100644
--- a/tests/custom/04_bugs/29_empty_string_as_number
+++ b/tests/custom/99_bugs/29_empty_string_as_number
diff --git a/tests/custom/04_bugs/30_nan_strict_equality b/tests/custom/99_bugs/30_nan_strict_equality
index 4ec32e2..4ec32e2 100644
--- a/tests/custom/04_bugs/30_nan_strict_equality
+++ b/tests/custom/99_bugs/30_nan_strict_equality
diff --git a/tests/custom/04_bugs/31_vallist_8bit_shortstrings b/tests/custom/99_bugs/31_vallist_8bit_shortstrings
index 9d02f42..9d02f42 100644
--- a/tests/custom/04_bugs/31_vallist_8bit_shortstrings
+++ b/tests/custom/99_bugs/31_vallist_8bit_shortstrings
diff --git a/tests/custom/04_bugs/32_compiler_switch_patchlist_corruption b/tests/custom/99_bugs/32_compiler_switch_patchlist_corruption
index d256de5..d256de5 100644
--- a/tests/custom/04_bugs/32_compiler_switch_patchlist_corruption
+++ b/tests/custom/99_bugs/32_compiler_switch_patchlist_corruption
diff --git a/tests/custom/04_bugs/33_vm_computed_prop_decl_crash b/tests/custom/99_bugs/33_vm_computed_prop_decl_crash
index 60b276c..60b276c 100644
--- a/tests/custom/04_bugs/33_vm_computed_prop_decl_crash
+++ b/tests/custom/99_bugs/33_vm_computed_prop_decl_crash
diff --git a/tests/custom/04_bugs/34_dirname_off_by_one b/tests/custom/99_bugs/34_dirname_off_by_one
index 34ef7c7..34ef7c7 100644
--- a/tests/custom/04_bugs/34_dirname_off_by_one
+++ b/tests/custom/99_bugs/34_dirname_off_by_one
diff --git a/tests/custom/04_bugs/35_vm_callframe_double_free b/tests/custom/99_bugs/35_vm_callframe_double_free
index bb816eb..bb816eb 100644
--- a/tests/custom/04_bugs/35_vm_callframe_double_free
+++ b/tests/custom/99_bugs/35_vm_callframe_double_free
diff --git a/tests/custom/04_bugs/36_vm_nested_call_return b/tests/custom/99_bugs/36_vm_nested_call_return
index 6a52b78..6a52b78 100644
--- a/tests/custom/04_bugs/36_vm_nested_call_return
+++ b/tests/custom/99_bugs/36_vm_nested_call_return
diff --git a/tests/custom/04_bugs/37_compiler_unexpected_unary_op b/tests/custom/99_bugs/37_compiler_unexpected_unary_op
index e652319..e652319 100644
--- a/tests/custom/04_bugs/37_compiler_unexpected_unary_op
+++ b/tests/custom/99_bugs/37_compiler_unexpected_unary_op
diff --git a/tests/custom/04_bugs/38_index_segfault b/tests/custom/99_bugs/38_index_segfault
index e29b99f..e29b99f 100644
--- a/tests/custom/04_bugs/38_index_segfault
+++ b/tests/custom/99_bugs/38_index_segfault
diff --git a/tests/custom/04_bugs/39_compiler_switch_continue_mismatch b/tests/custom/99_bugs/39_compiler_switch_continue_mismatch
index c9b9ec6..c9b9ec6 100644
--- a/tests/custom/04_bugs/39_compiler_switch_continue_mismatch
+++ b/tests/custom/99_bugs/39_compiler_switch_continue_mismatch
diff --git a/tests/custom/04_bugs/40_lexer_bug_on_lstrip_off b/tests/custom/99_bugs/40_lexer_bug_on_lstrip_off
index dc4f8dd..dc4f8dd 100644
--- a/tests/custom/04_bugs/40_lexer_bug_on_lstrip_off
+++ b/tests/custom/99_bugs/40_lexer_bug_on_lstrip_off