From 2d7a4842030d5b158ee98eaaf96cb5a47aef2cfb Mon Sep 17 00:00:00 2001 From: Raumberg Date: Sun, 15 Feb 2026 21:05:34 +0300 Subject: [PATCH 01/19] tests: test cases cleanup, fully migrated to lv_assert --- Makefile | 38 +++++++++++++++++++-------- tests/test_arithmetic.lv | 2 -- tests/test_block_lambdas.lv | 2 -- tests/test_cast.lv | 17 ++++++------ tests/test_classes.lv | 2 -- tests/test_collections.lv | 2 -- tests/test_comptime.lv | 2 -- tests/test_control_flow.lv | 2 -- tests/test_cpp_block.lv | 2 -- tests/test_enum_methods.lv | 11 ++++---- tests/test_enums.lv | 2 -- tests/test_exceptions.lv | 2 -- tests/test_extern.lv | 42 +++++------------------------- tests/test_extern_import.lv | 12 ++------- tests/test_fail_return_type.lv | 7 +++++ tests/test_fail_type_mismatch.lv | 4 +++ tests/test_fail_undefined_fn.lv | 4 +++ tests/test_fail_undefined_var.lv | 4 +++ tests/test_fail_void_return.lv | 7 +++++ tests/test_functions.lv | 2 -- tests/test_generics.lv | 2 -- tests/test_imports.lv | 2 -- tests/test_lambdas.lv | 2 -- tests/test_modules.lv | 2 -- tests/test_operator.lv | 4 +-- tests/test_refs.lv | 4 +-- tests/test_std_fs.lv | 12 --------- tests/test_std_math.lv | 1 - tests/test_std_os.lv | 6 ----- tests/test_stdlib.lv | 2 -- tests/test_strings.lv | 2 -- tests/test_structs.lv | 2 -- tests/test_types.lv | 44 ++++++++++++++------------------ 33 files changed, 97 insertions(+), 154 deletions(-) create mode 100644 tests/test_fail_return_type.lv create mode 100644 tests/test_fail_type_mismatch.lv create mode 100644 tests/test_fail_undefined_fn.lv create mode 100644 tests/test_fail_undefined_var.lv create mode 100644 tests/test_fail_void_return.lv diff --git a/Makefile b/Makefile index 1912811..2c0592a 100644 --- a/Makefile +++ b/Makefile @@ -77,17 +77,33 @@ test: continue; \ fi; \ fi; \ - /tmp/lavina_next compile $$f 2>/dev/null && \ - $$dir/$$name 2>/dev/null; \ - if [ $$? -eq 0 ]; then \ - echo " PASS $$name"; \ - passed=$$((passed + 1)); \ - else \ - echo " FAIL $$name"; \ - failed=$$((failed + 1)); \ - errors="$$errors $$name"; \ - fi; \ - rm -f $$dir/$$name $$dir/$$name.cpp; \ + case "$$name" in \ + test_fail_*) \ + /tmp/lavina_next compile $$f 2>/dev/null; \ + if [ $$? -ne 0 ]; then \ + echo " PASS $$name (expected compile failure)"; \ + passed=$$((passed + 1)); \ + else \ + echo " FAIL $$name (should not compile)"; \ + failed=$$((failed + 1)); \ + errors="$$errors $$name"; \ + rm -f $$dir/$$name $$dir/$$name.cpp; \ + fi \ + ;; \ + *) \ + /tmp/lavina_next compile $$f 2>/dev/null && \ + $$dir/$$name 2>/dev/null; \ + if [ $$? -eq 0 ]; then \ + echo " PASS $$name"; \ + passed=$$((passed + 1)); \ + else \ + echo " FAIL $$name"; \ + failed=$$((failed + 1)); \ + errors="$$errors $$name"; \ + fi; \ + rm -f $$dir/$$name $$dir/$$name.cpp; \ + ;; \ + esac; \ done; \ echo ""; \ if [ $$skipped -gt 0 ]; then \ diff --git a/tests/test_arithmetic.lv b/tests/test_arithmetic.lv index d846484..427cc18 100644 --- a/tests/test_arithmetic.lv +++ b/tests/test_arithmetic.lv @@ -41,5 +41,3 @@ void fn main(): lv_assert(!(true && false), "&& false") lv_assert(true || false, "|| true") lv_assert(!(false || false), "|| false") - - print("test_arithmetic: ALL PASSED") diff --git a/tests/test_block_lambdas.lv b/tests/test_block_lambdas.lv index 0e9dcd4..311cc7f 100644 --- a/tests/test_block_lambdas.lv +++ b/tests/test_block_lambdas.lv @@ -39,5 +39,3 @@ void fn main(): auto add_base = (int x): return x + base lv_assert(add_base(42) == 142, "block lambda closure") - - print("All block lambda tests passed!") diff --git a/tests/test_cast.lv b/tests/test_cast.lv index 6ce6384..0905658 100644 --- a/tests/test_cast.lv +++ b/tests/test_cast.lv @@ -3,37 +3,37 @@ void fn test_int_to_float(): int x = 42 float y = x as float - print("int->float: " + y) + lv_assert(y > 41.99 and y < 42.01, "int->float cast") void fn test_float_to_int(): float x = 3.14 int y = x as int - print("float->int: " + y) + lv_assert(y == 3, "float->int cast") void fn test_int_sizes(): int x = 100 int32 y = x as int32 int64 z = y as int64 - print("int->int32: " + y) - print("int32->int64: " + z) + lv_assert(y == 100, "int->int32 cast") + lv_assert(z == 100, "int32->int64 cast") void fn test_float_sizes(): float x = 2.718 float32 y = x as float32 float64 z = y as float64 - print("float->float32: " + y) - print("float32->float64: " + z) + lv_assert(y > 2.71 and y < 2.72, "float->float32 cast") + lv_assert(z > 2.71 and z < 2.72, "float32->float64 cast") void fn test_chained(): int x = 42 int32 y = x as float as int32 - print("chained: " + y) + lv_assert(y == 42, "chained cast int->float->int32") void fn test_expr_cast(): int a = 10 int b = 20 float c = (a + b) as float - print("expr cast: " + c) + lv_assert(c > 29.99 and c < 30.01, "expr cast (10+20) as float") void fn main(): test_int_to_float() @@ -42,4 +42,3 @@ void fn main(): test_float_sizes() test_chained() test_expr_cast() - print("All cast tests passed!") diff --git a/tests/test_classes.lv b/tests/test_classes.lv index 6f48636..afc48ae 100644 --- a/tests/test_classes.lv +++ b/tests/test_classes.lv @@ -28,5 +28,3 @@ void fn main(): auto c2 = Counter(100) lv_assert(c2.get() == 100, "second instance") lv_assert(c.get() == 10, "first instance unchanged") - - print("test_classes: ALL PASSED") diff --git a/tests/test_collections.lv b/tests/test_collections.lv index 9196e41..621da9c 100644 --- a/tests/test_collections.lv +++ b/tests/test_collections.lv @@ -34,5 +34,3 @@ void fn main(): lv_assert(s.len() == 2, "set dedup") lv_assert(s.contains("a"), "set contains a") lv_assert(!s.contains("c"), "set not contains c") - - print("test_collections: ALL PASSED") diff --git a/tests/test_comptime.lv b/tests/test_comptime.lv index 1b2d133..756c456 100644 --- a/tests/test_comptime.lv +++ b/tests/test_comptime.lv @@ -27,5 +27,3 @@ void fn main(): lv_assert(MAX_SIZE == 1024, "comptime int var") lv_assert(TAU > 6.28, "comptime float var") lv_assert(TAU < 6.29, "comptime float var upper") - - print("test_comptime: ALL PASSED") diff --git a/tests/test_control_flow.lv b/tests/test_control_flow.lv index 7d7a146..ad4ac43 100644 --- a/tests/test_control_flow.lv +++ b/tests/test_control_flow.lv @@ -42,5 +42,3 @@ void fn main(): continue even_sum += j lv_assert(even_sum == 30, "continue skips odds") - - print("test_control_flow: ALL PASSED") diff --git a/tests/test_cpp_block.lv b/tests/test_cpp_block.lv index 58f5054..aeb9232 100644 --- a/tests/test_cpp_block.lv +++ b/tests/test_cpp_block.lv @@ -3,6 +3,4 @@ void fn main(): cpp { x = 42LL; } - print(x) lv_assert(x == 42, "cpp block should set x to 42") - print("cpp block test passed") diff --git a/tests/test_enum_methods.lv b/tests/test_enum_methods.lv index 0f0f80e..5257bb3 100644 --- a/tests/test_enum_methods.lv +++ b/tests/test_enum_methods.lv @@ -19,8 +19,9 @@ enum Shape: void fn main(): auto c = Shape::Circle(5.0) auto s = Shape::Square(3.0) - print(c.area()) - print(s.area()) - print(c) - print(s) - print("Shape: ${c}") + lv_assert(c.area() > 78.53 and c.area() < 78.55, "circle area") + lv_assert(s.area() > 8.99 and s.area() < 9.01, "square area") + lv_assert(c.to_string().contains("Circle"), "circle to_string") + lv_assert(s.to_string().contains("Square"), "square to_string") + string interp = "Shape: ${c}" + lv_assert(interp.contains("Circle"), "string interpolation with enum") diff --git a/tests/test_enums.lv b/tests/test_enums.lv index 98beb67..47ad6e0 100644 --- a/tests/test_enums.lv +++ b/tests/test_enums.lv @@ -12,5 +12,3 @@ void fn main(): lv_assert(r1["_tag"] == "Ok", "Ok tag") lv_assert(r2["_tag"] == "Error", "Error tag") lv_assert(r3["_tag"] == "Loading", "Loading tag") - - print("test_enums: ALL PASSED") diff --git a/tests/test_exceptions.lv b/tests/test_exceptions.lv index 2027f16..3a5b2af 100644 --- a/tests/test_exceptions.lv +++ b/tests/test_exceptions.lv @@ -38,5 +38,3 @@ void fn main(): outer_ran = true lv_assert(inner_ran, "inner catch ran") lv_assert(outer_ran, "outer catch ran") - - print("test_exceptions: ALL PASSED") diff --git a/tests/test_extern.lv b/tests/test_extern.lv index fba63dc..9f72ae7 100644 --- a/tests/test_extern.lv +++ b/tests/test_extern.lv @@ -30,55 +30,27 @@ void fn main(): // Test 1: basic extern float val = 16.0 float result = sqrt(val) - if result > 3.99 and result < 4.01: - print("PASS: sqrt(16) = 4.0") - else: - print("FAIL: sqrt(16) = " + to_string(result)) - exit(1) + lv_assert(result > 3.99 and result < 4.01, "sqrt(16) should be ~4.0") float neg = fabs(-42.5) - if neg > 42.49 and neg < 42.51: - print("PASS: fabs(-42.5) = 42.5") - else: - print("FAIL: fabs(-42.5) = " + to_string(neg)) - exit(1) + lv_assert(neg > 42.49 and neg < 42.51, "fabs(-42.5) should be ~42.5") // Test 2: duplicate extern block float c = ceil_fn(2.3) - if c > 2.99 and c < 3.01: - print("PASS: ceil(2.3) = 3.0") - else: - print("FAIL: ceil(2.3) = " + to_string(c)) - exit(1) + lv_assert(c > 2.99 and c < 3.01, "ceil(2.3) should be ~3.0") // Test 3: extern with import float f = floor_fn(2.7) - if f > 1.99 and f < 2.01: - print("PASS: floor(2.7) = 2.0") - else: - print("FAIL: floor(2.7) = " + to_string(f)) - exit(1) + lv_assert(f > 1.99 and f < 2.01, "floor(2.7) should be ~2.0") // Test 4: extern with link float l = log_fn(1.0) - if l > -0.01 and l < 0.01: - print("PASS: log(1.0) = 0.0") - else: - print("FAIL: log(1.0) = " + to_string(l)) - exit(1) + lv_assert(l > -0.01 and l < 0.01, "log(1.0) should be ~0.0") // Test 5: extern with import + link float s = sin_fn(0.0) - if s > -0.01 and s < 0.01: - print("PASS: sin(0.0) = 0.0") - else: - print("FAIL: sin(0.0) = " + to_string(s)) - exit(1) + lv_assert(s > -0.01 and s < 0.01, "sin(0.0) should be ~0.0") // Test 6: extern with link + import (reversed order) float co = cos_fn(0.0) - if co > 0.99 and co < 1.01: - print("PASS: cos(0.0) = 1.0") - else: - print("FAIL: cos(0.0) = " + to_string(co)) - exit(1) + lv_assert(co > 0.99 and co < 1.01, "cos(0.0) should be ~1.0") diff --git a/tests/test_extern_import.lv b/tests/test_extern_import.lv index 6c718a2..7e15b0c 100644 --- a/tests/test_extern_import.lv +++ b/tests/test_extern_import.lv @@ -8,15 +8,7 @@ void fn main(): int32 b = 4 int32 sum = ext_add(a, b) - if sum == 7: - print("PASS: ext_add(3, 4) = 7") - else: - print("FAIL: ext_add(3, 4) = " + to_string(sum)) - exit(1) + lv_assert(sum == 7, "ext_add(3, 4) should be 7") int32 product = ext_mul(a, b) - if product == 12: - print("PASS: ext_mul(3, 4) = 12") - else: - print("FAIL: ext_mul(3, 4) = " + to_string(product)) - exit(1) + lv_assert(product == 12, "ext_mul(3, 4) should be 12") diff --git a/tests/test_fail_return_type.lv b/tests/test_fail_return_type.lv new file mode 100644 index 0000000..cd4b817 --- /dev/null +++ b/tests/test_fail_return_type.lv @@ -0,0 +1,7 @@ +// Negative test: returning wrong type should fail compilation + +int fn get_number(): + return "not a number" + +void fn main(): + int x = get_number() diff --git a/tests/test_fail_type_mismatch.lv b/tests/test_fail_type_mismatch.lv new file mode 100644 index 0000000..c91e39e --- /dev/null +++ b/tests/test_fail_type_mismatch.lv @@ -0,0 +1,4 @@ +// Negative test: assigning string to int should fail compilation + +void fn main(): + int x = "hello" diff --git a/tests/test_fail_undefined_fn.lv b/tests/test_fail_undefined_fn.lv new file mode 100644 index 0000000..27546e8 --- /dev/null +++ b/tests/test_fail_undefined_fn.lv @@ -0,0 +1,4 @@ +// Negative test: calling undefined function should fail compilation + +void fn main(): + int result = nonexistent_function(42) diff --git a/tests/test_fail_undefined_var.lv b/tests/test_fail_undefined_var.lv new file mode 100644 index 0000000..51be6d9 --- /dev/null +++ b/tests/test_fail_undefined_var.lv @@ -0,0 +1,4 @@ +// Negative test: using undefined variable should fail compilation + +void fn main(): + int x = y + 1 diff --git a/tests/test_fail_void_return.lv b/tests/test_fail_void_return.lv new file mode 100644 index 0000000..993869d --- /dev/null +++ b/tests/test_fail_void_return.lv @@ -0,0 +1,7 @@ +// Negative test: returning value from void function should fail compilation + +void fn do_something(): + return 42 + +void fn main(): + do_something() diff --git a/tests/test_functions.lv b/tests/test_functions.lv index 2fca642..e15e189 100644 --- a/tests/test_functions.lv +++ b/tests/test_functions.lv @@ -27,5 +27,3 @@ void fn main(): lv_assert(fib(0) == 0, "fib 0") lv_assert(fib(1) == 1, "fib 1") lv_assert(fib(10) == 55, "fib 10") - - print("test_functions: ALL PASSED") diff --git a/tests/test_generics.lv b/tests/test_generics.lv index 8e43dc8..fc3db35 100644 --- a/tests/test_generics.lv +++ b/tests/test_generics.lv @@ -236,5 +236,3 @@ void fn main(): lv_assert(r._tag == "Leaf", "tree right is leaf") _: lv_assert(false, "should be node") - - print("All generics tests passed!") diff --git a/tests/test_imports.lv b/tests/test_imports.lv index b52bf9a..9c8c13c 100644 --- a/tests/test_imports.lv +++ b/tests/test_imports.lv @@ -3,5 +3,3 @@ import helpers::math_helper void fn main(): lv_assert(add(3, 4) == 7, "imported add") lv_assert(multiply(5, 6) == 30, "imported multiply") - - print("test_imports: ALL PASSED") diff --git a/tests/test_lambdas.lv b/tests/test_lambdas.lv index b1fc8c7..c426cc2 100644 --- a/tests/test_lambdas.lv +++ b/tests/test_lambdas.lv @@ -29,5 +29,3 @@ void fn main(): int offset = 10 auto shifted = (int x) => x + offset lv_assert(shifted(5) == 15, "lambda closure") - - print("All lambda tests passed!") diff --git a/tests/test_modules.lv b/tests/test_modules.lv index 1b13130..e44fc8d 100644 --- a/tests/test_modules.lv +++ b/tests/test_modules.lv @@ -17,5 +17,3 @@ void fn main(): // Full namespace access lv_assert(helpers_greeter::greet("Test") == "Hello, Test!", "full namespace greet") - - print("test_modules: ALL PASSED") diff --git a/tests/test_operator.lv b/tests/test_operator.lv index e876db2..601dbe5 100644 --- a/tests/test_operator.lv +++ b/tests/test_operator.lv @@ -15,10 +15,8 @@ void fn main(): Vec2 a = Vec2(1.0, 2.0) Vec2 b = Vec2(3.0, 4.0) Vec2 c = a + b - print(c) lv_assert(c.x == 4.0, "x should be 4.0") lv_assert(c.y == 6.0, "y should be 6.0") lv_assert(a == a, "a should equal a") string s = "point: ${c}" - print(s) - print("All operator tests passed!") + lv_assert(s.contains("4.0") and s.contains("6.0"), "string interpolation of Vec2") diff --git a/tests/test_refs.lv b/tests/test_refs.lv index 3d15243..036bf24 100644 --- a/tests/test_refs.lv +++ b/tests/test_refs.lv @@ -4,7 +4,7 @@ void fn modify_vec(ref! vector[int] v): v.push(42) void fn consume_string(string s): - print("consumed: " + s) + lv_assert(s.len() > 0, "consumed string not empty") void fn main(): // Test 1: ref parameter modifies caller's variable @@ -40,5 +40,3 @@ void fn main(): lv_assert(vals[0] == 11, "for ref! mutate") lv_assert(vals[1] == 21, "for ref! mutate 2") lv_assert(vals[2] == 31, "for ref! mutate 3") - - print("All ref/own tests passed!") diff --git a/tests/test_std_fs.lv b/tests/test_std_fs.lv index b95764b..8a32c02 100644 --- a/tests/test_std_fs.lv +++ b/tests/test_std_fs.lv @@ -4,48 +4,40 @@ void fn test_write_and_read(): fs::write("/tmp/lv_test_fs.txt", "hello lavina") string content = fs::read("/tmp/lv_test_fs.txt") lv_assert(content == "hello lavina", "write/read failed") - print("PASS: write and read") void fn test_exists(): lv_assert(fs::exists("/tmp/lv_test_fs.txt"), "exists failed") lv_assert(not fs::exists("/tmp/lv_nonexistent_xyz.txt"), "not exists failed") - print("PASS: exists") void fn test_append(): fs::append("/tmp/lv_test_fs.txt", "\nline2") string content = fs::read("/tmp/lv_test_fs.txt") lv_assert(content == "hello lavina\nline2", "append failed") - print("PASS: append") void fn test_read_lines(): vector[string] lines = fs::read_lines("/tmp/lv_test_fs.txt") lv_assert(lines.len() == 2, "read_lines count failed") lv_assert(lines[0] == "hello lavina", "read_lines[0] failed") lv_assert(lines[1] == "line2", "read_lines[1] failed") - print("PASS: read_lines") void fn test_size(): int s = fs::size("/tmp/lv_test_fs.txt") lv_assert(s > 0, "size failed") - print("PASS: size = " + s) void fn test_copy(): fs::copy("/tmp/lv_test_fs.txt", "/tmp/lv_test_fs_copy.txt") string content = fs::read("/tmp/lv_test_fs_copy.txt") lv_assert(content == "hello lavina\nline2", "copy failed") - print("PASS: copy") void fn test_rename(): fs::rename("/tmp/lv_test_fs_copy.txt", "/tmp/lv_test_fs_moved.txt") lv_assert(fs::exists("/tmp/lv_test_fs_moved.txt"), "rename dest failed") lv_assert(not fs::exists("/tmp/lv_test_fs_copy.txt"), "rename src still exists") - print("PASS: rename") void fn test_mkdir_and_is_dir(): fs::mkdir("/tmp/lv_test_dir") lv_assert(fs::exists("/tmp/lv_test_dir"), "mkdir failed") lv_assert(fs::is_dir("/tmp/lv_test_dir"), "is_dir failed") - print("PASS: mkdir and is_dir") void fn test_list_dir(): fs::write("/tmp/lv_test_dir/a.txt", "a") @@ -54,19 +46,16 @@ void fn test_list_dir(): lv_assert(entries.len() == 2, "list_dir count failed") lv_assert(entries[0] == "a.txt", "list_dir[0] failed") lv_assert(entries[1] == "b.txt", "list_dir[1] failed") - print("PASS: list_dir") void fn test_basename_dirname(): string b = fs::basename("/tmp/lv_test_fs.txt") string d = fs::dirname("/tmp/lv_test_fs.txt") lv_assert(b == "lv_test_fs.txt", "basename failed: " + b) lv_assert(d == "/tmp", "dirname failed: " + d) - print("PASS: basename = " + b + ", dirname = " + d) void fn test_absolute(): string abs = fs::absolute("/tmp/lv_test_fs.txt") lv_assert(abs.starts_with("/"), "absolute failed: " + abs) - print("PASS: absolute = " + abs) void fn cleanup(): fs::remove("/tmp/lv_test_fs.txt") @@ -88,4 +77,3 @@ void fn main(): test_basename_dirname() test_absolute() cleanup() - print("All std::fs tests passed!") diff --git a/tests/test_std_math.lv b/tests/test_std_math.lv index b4cc2eb..81e902d 100644 --- a/tests/test_std_math.lv +++ b/tests/test_std_math.lv @@ -71,5 +71,4 @@ int fn main(): lv_assert(rf >= 0.0, "random_float >= 0") lv_assert(rf <= 1.0, "random_float <= 1") - print("All std::math tests passed!") return 0 diff --git a/tests/test_std_os.lv b/tests/test_std_os.lv index 2dd4a2d..31ca518 100644 --- a/tests/test_std_os.lv +++ b/tests/test_std_os.lv @@ -3,31 +3,26 @@ import std::os void fn test_args(): vector[string] a = os::args() lv_assert(a.len() >= 1, "args should have at least 1 element") - print("PASS: args, count = " + a.len()) void fn test_env(): string home = os::env("HOME") lv_assert(home.len() > 0, "HOME should not be empty") string empty = os::env("LV_NONEXISTENT_VAR_XYZ") lv_assert(empty == "", "nonexistent env should be empty") - print("PASS: env, HOME = " + home) void fn test_exec(): int result = os::exec("echo hello > /dev/null") lv_assert(result == 0, "exec echo failed") - print("PASS: exec") void fn test_cwd(): string dir = os::cwd() lv_assert(dir.len() > 0, "cwd should not be empty") - print("PASS: cwd = " + dir) void fn test_clock_and_sleep(): int t1 = os::time_ms() os::sleep(20) int t2 = os::time_ms() lv_assert(t2 >= t1 + 10, "clock/sleep failed") - print("PASS: clock/sleep, delta = " + (t2 - t1) + "ms") void fn main(): test_args() @@ -35,4 +30,3 @@ void fn main(): test_exec() test_cwd() test_clock_and_sleep() - print("All std::os tests passed!") diff --git a/tests/test_stdlib.lv b/tests/test_stdlib.lv index 1341ac9..23759f5 100644 --- a/tests/test_stdlib.lv +++ b/tests/test_stdlib.lv @@ -71,5 +71,3 @@ void fn main(): string cwd = __os_cwd() lv_assert(cwd.len() > 0, "os_cwd") - - print("test_stdlib: ALL PASSED") diff --git a/tests/test_strings.lv b/tests/test_strings.lv index d0b85ab..babce1b 100644 --- a/tests/test_strings.lv +++ b/tests/test_strings.lv @@ -27,5 +27,3 @@ void fn main(): int x = 42 string msg = "value is " + x lv_assert(msg.contains("42"), "int to string concat") - - print("test_strings: ALL PASSED") diff --git a/tests/test_structs.lv b/tests/test_structs.lv index a3f4c0f..890dbc1 100644 --- a/tests/test_structs.lv +++ b/tests/test_structs.lv @@ -72,5 +72,3 @@ void fn main(): lv_assert(canvas.bounds.area() == 50.0, "canvas bounds area") lv_assert(canvas.circles[0].center.x == 5.0, "canvas first circle x") lv_assert(canvas.circles[1].radius == 1.0, "canvas second circle radius") - - print("test_structs: ALL PASSED") diff --git a/tests/test_types.lv b/tests/test_types.lv index 126cdc8..4cc4a27 100644 --- a/tests/test_types.lv +++ b/tests/test_types.lv @@ -5,32 +5,32 @@ void fn test_int_types(): int16 b = 1000 int32 c = 100000 int64 d = 9999999 - print("int8: " + a) - print("int16: " + b) - print("int32: " + c) - print("int64: " + d) + lv_assert(a == 42, "int8 value") + lv_assert(b == 1000, "int16 value") + lv_assert(c == 100000, "int32 value") + lv_assert(d == 9999999, "int64 value") void fn test_float_types(): float32 x = 3.14 float64 y = 2.718 - print("float32: " + x) - print("float64: " + y) + lv_assert(x > 3.13 and x < 3.15, "float32 value") + lv_assert(y > 2.71 and y < 2.72, "float64 value") void fn test_usize(): usize idx = 0 vector[string] items = ["a", "b", "c"] - print("usize idx: " + idx) + lv_assert(idx == 0, "usize value") + lv_assert(items.len() == 3, "usize vector len") void fn test_cross_type(): int32 a = 10 int b = a float32 c = 1.5 float d = c - print("int32->int: " + b) - print("float32->float: " + d) + lv_assert(b == 10, "int32->int promotion") + lv_assert(d > 1.49 and d < 1.51, "float32->float promotion") -void fn test_print_all_types(): - // print() with each type directly +void fn test_type_values(): int8 a = 8 int16 b = 16 int32 c = 32 @@ -38,18 +38,13 @@ void fn test_print_all_types(): float32 e = 3.2 float64 f = 6.4 usize g = 99 - print(a) - print(b) - print(c) - print(d) - print(e) - print(f) - print(g) - print(true) - // println - println(42) - println("hello") - println() + lv_assert(a == 8, "int8 val") + lv_assert(b == 16, "int16 val") + lv_assert(c == 32, "int32 val") + lv_assert(d == 64, "int64 val") + lv_assert(e > 3.1 and e < 3.3, "float32 val") + lv_assert(f > 6.3 and f < 6.5, "float64 val") + lv_assert(g == 99, "usize val") void fn test_to_string_all_types(): lv_assert(to_string(42) == "42", "to_string int") @@ -66,6 +61,5 @@ void fn main(): test_float_types() test_usize() test_cross_type() - test_print_all_types() + test_type_values() test_to_string_all_types() - print("All type tests passed!") From 81cc1ab5050c757770c4822b41b7632398fb3c79 Mon Sep 17 00:00:00 2001 From: Raumberg Date: Sun, 15 Feb 2026 21:35:50 +0300 Subject: [PATCH 02/19] std: added collections.lv, added new functions in hashset.h, vector.h --- runtime/liblavina/hashset.h | 30 ++++++ runtime/liblavina/vector.h | 91 ++++++++++++++++ runtime/std/collections.lv | 48 +++++++++ tests/test_collections.lv | 149 +++++++++++++++++++++++++-- tests/test_fail_map_to_vec.lv | 5 + tests/test_fail_set_wrong_type.lv | 5 + tests/test_fail_vec_type_mismatch.lv | 5 + tests/test_std_collections.lv | 141 +++++++++++++++++++++++++ 8 files changed, 466 insertions(+), 8 deletions(-) create mode 100644 runtime/std/collections.lv create mode 100644 tests/test_fail_map_to_vec.lv create mode 100644 tests/test_fail_set_wrong_type.lv create mode 100644 tests/test_fail_vec_type_mismatch.lv create mode 100644 tests/test_std_collections.lv diff --git a/runtime/liblavina/hashset.h b/runtime/liblavina/hashset.h index 1979739..ad3a461 100644 --- a/runtime/liblavina/hashset.h +++ b/runtime/liblavina/hashset.h @@ -10,3 +10,33 @@ template void lv_remove(std::unordered_set& s, const T& val) { s.erase(val); } + +// --- collections module functions --- + +// Set union +template +std::unordered_set __lv_col_set_union(const std::unordered_set& a, const std::unordered_set& b) { + std::unordered_set result = a; + for (const auto& x : b) result.insert(x); + return result; +} + +// Set intersection +template +std::unordered_set __lv_col_set_intersection(const std::unordered_set& a, const std::unordered_set& b) { + std::unordered_set result; + for (const auto& x : a) { + if (b.count(x) > 0) result.insert(x); + } + return result; +} + +// Set difference (a - b) +template +std::unordered_set __lv_col_set_difference(const std::unordered_set& a, const std::unordered_set& b) { + std::unordered_set result; + for (const auto& x : a) { + if (b.count(x) == 0) result.insert(x); + } + return result; +} diff --git a/runtime/liblavina/vector.h b/runtime/liblavina/vector.h index 4d725e8..052b263 100644 --- a/runtime/liblavina/vector.h +++ b/runtime/liblavina/vector.h @@ -83,3 +83,94 @@ std::vector lv_flatten(const std::vector>& v) { result.push_back(x); return result; } + +// --- collections module functions --- + +// Range [start, end) +inline std::vector __lv_col_range(int64_t start, int64_t end) { + std::vector result; + for (int64_t i = start; i < end; i++) result.push_back(i); + return result; +} + +// Range with step +inline std::vector __lv_col_range_step(int64_t start, int64_t end, int64_t step) { + std::vector result; + if (step > 0) { + for (int64_t i = start; i < end; i += step) result.push_back(i); + } else if (step < 0) { + for (int64_t i = start; i > end; i += step) result.push_back(i); + } + return result; +} + +// Take first n elements +template +std::vector __lv_col_take(const std::vector& v, int64_t n) { + if (n <= 0) return {}; + if (n >= static_cast(v.size())) return v; + return std::vector(v.begin(), v.begin() + n); +} + +// Drop first n elements +template +std::vector __lv_col_drop(const std::vector& v, int64_t n) { + if (n <= 0) return v; + if (n >= static_cast(v.size())) return {}; + return std::vector(v.begin() + n, v.end()); +} + +// Map: apply f to each element, return new vector +template +auto __lv_col_map(const std::vector& v, F f) { + using R = decltype(f(v[0])); + std::vector result; + result.reserve(v.size()); + for (const auto& x : v) result.push_back(f(x)); + return result; +} + +// Filter: return elements where f(x) is true +template +std::vector __lv_col_filter(const std::vector& v, F f) { + std::vector result; + for (const auto& x : v) { + if (f(x)) result.push_back(x); + } + return result; +} + +// Reduce: fold left with initial value +template +A __lv_col_reduce(const std::vector& v, F f, A init) { + A acc = init; + for (const auto& x : v) acc = f(acc, x); + return acc; +} + +// For each: apply f to each element (void) +template +void __lv_col_for_each(const std::vector& v, F f) { + for (const auto& x : v) f(x); +} + +// Zip: combine two vectors into vector of pairs +template +std::vector> __lv_col_zip(const std::vector& a, const std::vector& b) { + std::vector> result; + size_t len = std::min(a.size(), b.size()); + result.reserve(len); + for (size_t i = 0; i < len; i++) result.push_back({a[i], b[i]}); + return result; +} + +// Enumerate: pair each element with its index +template +std::vector> __lv_col_enumerate(const std::vector& v) { + std::vector> result; + result.reserve(v.size()); + for (size_t i = 0; i < v.size(); i++) { + result.push_back({static_cast(i), v[i]}); + } + return result; +} diff --git a/runtime/std/collections.lv b/runtime/std/collections.lv new file mode 100644 index 0000000..1e54910 --- /dev/null +++ b/runtime/std/collections.lv @@ -0,0 +1,48 @@ +// std::collections — higher-order functions, utilities, and set operations + +// Higher-order functions + +public auto fn map(auto v, auto f): + return __lv_col_map(v, f) + +public auto fn filter(auto v, auto f): + return __lv_col_filter(v, f) + +public auto fn reduce(auto v, auto f, auto init): + return __lv_col_reduce(v, f, init) + +public void fn for_each(auto v, auto f): + __lv_col_for_each(v, f) + +// Utilities + +public auto fn zip(auto a, auto b): + return __lv_col_zip(a, b) + +public auto fn take(auto v, int n): + return __lv_col_take(v, n) + +public auto fn drop(auto v, int n): + return __lv_col_drop(v, n) + +public auto fn enumerate(auto v): + return __lv_col_enumerate(v) + +// Generators + +public vector[int] fn range(int start, int end): + return __lv_col_range(start, end) + +public vector[int] fn range_step(int start, int end, int step): + return __lv_col_range_step(start, end, step) + +// Set operations + +public auto fn set_union(auto a, auto b): + return __lv_col_set_union(a, b) + +public auto fn set_intersection(auto a, auto b): + return __lv_col_set_intersection(a, b) + +public auto fn set_difference(auto a, auto b): + return __lv_col_set_difference(a, b) diff --git a/tests/test_collections.lv b/tests/test_collections.lv index 621da9c..b705bda 100644 --- a/tests/test_collections.lv +++ b/tests/test_collections.lv @@ -1,32 +1,148 @@ void fn main(): - // Vector basics + // ── Vector basics ───────────────────────────────── vector[int] v = [1, 2, 3] lv_assert(len(v) == 3, "vector length") lv_assert(v[0] == 1, "vector index 0") lv_assert(v[2] == 3, "vector index 2") - // Vector push - v.push_back(4) - lv_assert(len(v) == 4, "after push_back") + // push / pop + v.push(4) + lv_assert(len(v) == 4, "after push") lv_assert(v[3] == 4, "pushed element") - // Vector pop auto last = v.pop() lv_assert(last == 4, "popped value") lv_assert(len(v) == 3, "after pop") - // Vector contains + // contains lv_assert(v.contains(2), "contains 2") lv_assert(!v.contains(99), "not contains 99") - // Hashmap + // is_empty + lv_assert(!v.is_empty(), "vector not empty") + vector[int] empty_v = [] + lv_assert(empty_v.is_empty(), "empty vector is_empty") + + // remove by index + vector[int] rv = [10, 20, 30, 40] + rv.remove(1) + lv_assert(rv.len() == 3, "vector remove length") + lv_assert(rv[0] == 10, "vector remove [0]") + lv_assert(rv[1] == 30, "vector remove [1] shifted") + + // reverse + vector[int] rev = [1, 2, 3] + rev.reverse() + lv_assert(rev[0] == 3, "reverse [0]") + lv_assert(rev[2] == 1, "reverse [2]") + + // join + vector[string] words = ["hello", "world"] + lv_assert(words.join(" ") == "hello world", "join strings") + + vector[int] digits = [1, 2, 3] + lv_assert(digits.join("-") == "1-2-3", "join ints") + lv_assert(digits.join("") == "123", "join no sep") + + // clear + vector[int] cv = [1, 2, 3] + cv.clear() + lv_assert(cv.len() == 0, "vector clear") + lv_assert(cv.is_empty(), "vector clear is_empty") + + // sort + vector[int] unsorted = [5, 1, 4, 2, 3] + unsorted.sort() + lv_assert(unsorted[0] == 1, "sort [0]") + lv_assert(unsorted[4] == 5, "sort [4]") + + // indexOf + vector[int] idx_v = [10, 20, 30, 20] + lv_assert(idx_v.indexOf(20) == 1, "indexOf found") + lv_assert(idx_v.indexOf(99) == -1, "indexOf missing") + + // unique + vector[int] dup = [1, 2, 2, 3, 1] + auto uniq = dup.unique() + lv_assert(uniq.len() == 3, "unique length") + lv_assert(uniq[0] == 1, "unique preserves order [0]") + lv_assert(uniq[1] == 2, "unique preserves order [1]") + lv_assert(uniq[2] == 3, "unique preserves order [2]") + + // slice + vector[int] sl_v = [10, 20, 30, 40, 50] + auto sl = sl_v.slice(1, 4) + lv_assert(sl.len() == 3, "slice length") + lv_assert(sl[0] == 20, "slice [0]") + lv_assert(sl[2] == 40, "slice [2]") + + // slice edge cases + auto sl_empty = sl_v.slice(3, 3) + lv_assert(sl_empty.len() == 0, "slice empty range") + auto sl_clamp = sl_v.slice(0, 100) + lv_assert(sl_clamp.len() == 5, "slice clamped end") + + // flatten + vector[vector[int]] nested = [[1, 2], [3], [4, 5, 6]] + auto flat = nested.flatten() + lv_assert(flat.len() == 6, "flatten length") + lv_assert(flat[0] == 1, "flatten [0]") + lv_assert(flat[5] == 6, "flatten [5]") + + // flatten empty inner + vector[vector[int]] nested2 = [[], [1], []] + auto flat2 = nested2.flatten() + lv_assert(flat2.len() == 1, "flatten with empties") + + // ── Hashmap ─────────────────────────────────────── hashmap[string, int] m = {"x": 10, "y": 20} lv_assert(m["x"] == 10, "map lookup x") lv_assert(m["y"] == 20, "map lookup y") + + // insert m["z"] = 30 lv_assert(m["z"] == 30, "map insert z") + lv_assert(m.len() == 3, "map length after insert") + + // has + lv_assert(m.has("x"), "map has x") + lv_assert(!m.has("w"), "map not has w") + + // overwrite + m["x"] = 100 + lv_assert(m["x"] == 100, "map overwrite") + lv_assert(m.len() == 3, "map length unchanged after overwrite") + + // keys / values + hashmap[string, int] m2 = {"a": 1, "b": 2} + auto ks = m2.keys() + lv_assert(ks.len() == 2, "keys length") + // keys order is unspecified, check both exist + lv_assert(ks.contains("a"), "keys contains a") + lv_assert(ks.contains("b"), "keys contains b") + + auto vs = m2.values() + lv_assert(vs.len() == 2, "values length") + lv_assert(vs.contains(1), "values contains 1") + lv_assert(vs.contains(2), "values contains 2") - // Hashset + // remove + m2.remove("a") + lv_assert(m2.len() == 1, "map remove length") + lv_assert(!m2.has("a"), "map remove deleted") + lv_assert(m2.has("b"), "map remove kept") + + // is_empty + lv_assert(!m2.is_empty(), "map not empty") + m2.remove("b") + lv_assert(m2.is_empty(), "map empty after remove all") + + // clear + hashmap[string, int] m3 = {"x": 1, "y": 2} + m3.clear() + lv_assert(m3.len() == 0, "map clear") + + // ── Hashset ─────────────────────────────────────── hashset[string] s = [] s.add("a") s.add("b") @@ -34,3 +150,20 @@ void fn main(): lv_assert(s.len() == 2, "set dedup") lv_assert(s.contains("a"), "set contains a") lv_assert(!s.contains("c"), "set not contains c") + + // remove + s.remove("a") + lv_assert(s.len() == 1, "set remove length") + lv_assert(!s.contains("a"), "set remove deleted") + lv_assert(s.contains("b"), "set remove kept") + + // is_empty / clear + lv_assert(!s.is_empty(), "set not empty") + s.clear() + lv_assert(s.is_empty(), "set empty after clear") + lv_assert(s.len() == 0, "set clear length") + + // add after clear + s.add("x") + lv_assert(s.len() == 1, "set add after clear") + lv_assert(s.contains("x"), "set contains after clear") diff --git a/tests/test_fail_map_to_vec.lv b/tests/test_fail_map_to_vec.lv new file mode 100644 index 0000000..b66e375 --- /dev/null +++ b/tests/test_fail_map_to_vec.lv @@ -0,0 +1,5 @@ +// Negative test: assigning hashmap to vector should fail + +void fn main(): + hashmap[string, int] m = {"x": 1} + vector[int] v = m diff --git a/tests/test_fail_set_wrong_type.lv b/tests/test_fail_set_wrong_type.lv new file mode 100644 index 0000000..d13c8de --- /dev/null +++ b/tests/test_fail_set_wrong_type.lv @@ -0,0 +1,5 @@ +// Negative test: assigning hashset to vector should fail + +void fn main(): + hashset[int] s = [] + vector[int] v = s diff --git a/tests/test_fail_vec_type_mismatch.lv b/tests/test_fail_vec_type_mismatch.lv new file mode 100644 index 0000000..534de6f --- /dev/null +++ b/tests/test_fail_vec_type_mismatch.lv @@ -0,0 +1,5 @@ +// Negative test: assigning vector[int] to vector[string] should fail + +void fn main(): + vector[int] nums = [1, 2, 3] + vector[string] strs = nums diff --git a/tests/test_std_collections.lv b/tests/test_std_collections.lv new file mode 100644 index 0000000..6d9bd88 --- /dev/null +++ b/tests/test_std_collections.lv @@ -0,0 +1,141 @@ +import std::collections + +void fn main(): + // --- map --- + vector[int] nums = [1, 2, 3, 4, 5] + auto doubled = collections::map(nums, (int x) => x * 2) + lv_assert(len(doubled) == 5, "map length") + lv_assert(doubled[0] == 2, "map [0]") + lv_assert(doubled[4] == 10, "map [4]") + + // map with type transform + auto strs = collections::map(nums, (int x) => to_string(x)) + lv_assert(strs[0] == "1", "map to_string [0]") + lv_assert(strs[2] == "3", "map to_string [2]") + + // map empty vector + vector[int] empty = [] + auto mapped_empty = collections::map(empty, (int x) => x * 2) + lv_assert(len(mapped_empty) == 0, "map empty") + + // --- filter --- + auto evens = collections::filter(nums, (int x) => x % 2 == 0) + lv_assert(len(evens) == 2, "filter length") + lv_assert(evens[0] == 2, "filter [0]") + lv_assert(evens[1] == 4, "filter [1]") + + // filter none match + auto none = collections::filter(nums, (int x) => x > 100) + lv_assert(len(none) == 0, "filter none match") + + // --- reduce --- + int sum = collections::reduce(nums, (int acc, int x) => acc + x, 0) + lv_assert(sum == 15, "reduce sum") + + int product = collections::reduce(nums, (int acc, int x) => acc * x, 1) + lv_assert(product == 120, "reduce product") + + // --- for_each --- + int total = 0 + collections::for_each(nums, (int x): + total += x + ) + lv_assert(total == 15, "for_each sum") + + // --- zip --- + vector[string] names = ["a", "b", "c"] + vector[int] vals = [1, 2, 3] + auto zipped = collections::zip(names, vals) + lv_assert(len(zipped) == 3, "zip length") + lv_assert(zipped[0].first == "a", "zip [0].first") + lv_assert(zipped[0].second == 1, "zip [0].second") + lv_assert(zipped[2].first == "c", "zip [2].first") + + // zip different lengths — truncates to shorter + vector[int] short_v = [10, 20] + auto zipped2 = collections::zip(nums, short_v) + lv_assert(len(zipped2) == 2, "zip truncate length") + lv_assert(zipped2[1].first == 2, "zip truncate [1].first") + + // --- take --- + auto first3 = collections::take(nums, 3) + lv_assert(len(first3) == 3, "take length") + lv_assert(first3[0] == 1, "take [0]") + lv_assert(first3[2] == 3, "take [2]") + + // take overflow + auto take_all = collections::take(nums, 100) + lv_assert(len(take_all) == 5, "take overflow") + + // --- drop --- + auto last2 = collections::drop(nums, 3) + lv_assert(len(last2) == 2, "drop length") + lv_assert(last2[0] == 4, "drop [0]") + lv_assert(last2[1] == 5, "drop [1]") + + // drop overflow + auto drop_all = collections::drop(nums, 100) + lv_assert(len(drop_all) == 0, "drop overflow") + + // --- enumerate --- + vector[string] words = ["hello", "world"] + auto indexed = collections::enumerate(words) + lv_assert(len(indexed) == 2, "enumerate length") + lv_assert(indexed[0].first == 0, "enumerate [0].first") + lv_assert(indexed[0].second == "hello", "enumerate [0].second") + lv_assert(indexed[1].first == 1, "enumerate [1].first") + + // --- range --- + auto r = collections::range(0, 5) + lv_assert(len(r) == 5, "range length") + lv_assert(r[0] == 0, "range [0]") + lv_assert(r[4] == 4, "range [4]") + + // range empty + auto r_empty = collections::range(5, 5) + lv_assert(len(r_empty) == 0, "range empty") + + // --- range_step --- + auto r_step = collections::range_step(0, 10, 3) + lv_assert(len(r_step) == 4, "range_step length") + lv_assert(r_step[0] == 0, "range_step [0]") + lv_assert(r_step[1] == 3, "range_step [1]") + lv_assert(r_step[3] == 9, "range_step [3]") + + // range_step negative + auto r_neg = collections::range_step(10, 0, -2) + lv_assert(len(r_neg) == 5, "range_step neg length") + lv_assert(r_neg[0] == 10, "range_step neg [0]") + lv_assert(r_neg[4] == 2, "range_step neg [4]") + + // --- set operations --- + hashset[int] sa = [] + sa.add(1) + sa.add(2) + sa.add(3) + + hashset[int] sb = [] + sb.add(2) + sb.add(3) + sb.add(4) + + auto u = collections::set_union(sa, sb) + lv_assert(u.len() == 4, "set_union size") + lv_assert(u.contains(1), "set_union contains 1") + lv_assert(u.contains(4), "set_union contains 4") + + auto inter = collections::set_intersection(sa, sb) + lv_assert(inter.len() == 2, "set_intersection size") + lv_assert(inter.contains(2), "set_intersection contains 2") + lv_assert(inter.contains(3), "set_intersection contains 3") + lv_assert(!inter.contains(1), "set_intersection not 1") + + auto diff = collections::set_difference(sa, sb) + lv_assert(diff.len() == 1, "set_difference size") + lv_assert(diff.contains(1), "set_difference contains 1") + lv_assert(!diff.contains(2), "set_difference not 2") + + // --- chaining: map -> filter -> reduce --- + auto result = collections::reduce(collections::filter(collections::map(nums, (int x) => x * x), (int x) => x > 5), (int acc, int x) => acc + x, 0) + // nums = [1,2,3,4,5] -> map(x*x) = [1,4,9,16,25] -> filter(>5) = [9,16,25] -> reduce(+) = 50 + lv_assert(result == 50, "chained map->filter->reduce") From ed51b3f8fc92a1c77d84388fc51bf90da83aa2e9 Mon Sep 17 00:00:00 2001 From: Raumberg Date: Sun, 15 Feb 2026 22:15:25 +0300 Subject: [PATCH 03/19] feat: 'extend' keyword, basic Lavina type methods extensions --- editors/nvim/syntax/lavina.vim | 4 +- .../vscode/syntaxes/lavina.tmLanguage.json | 4 +- runtime/std/collections.lv | 47 +- src/ast.lv | 1 + src/codegen.lv | 118 ++ src/parser.lv | 29 + src/scanner.lv | 3 + stages/stage-latest.cpp | 1268 ++++++++++------- tests/test_extend.lv | 36 + tests/test_std_collections.lv | 50 +- 10 files changed, 1052 insertions(+), 508 deletions(-) create mode 100644 tests/test_extend.lv diff --git a/editors/nvim/syntax/lavina.vim b/editors/nvim/syntax/lavina.vim index d436f68..930b7f7 100644 --- a/editors/nvim/syntax/lavina.vim +++ b/editors/nvim/syntax/lavina.vim @@ -27,7 +27,7 @@ syn keyword lavinaLogical and or not syn keyword lavinaKeyword fn constructor public private static inline const let comptime namespace ref own cpp extern link type operator " Storage -syn keyword lavinaStorage class struct enum +syn keyword lavinaStorage class struct enum extend " Types syn keyword lavinaType int float string bool void auto dynamic vector hashmap hashset null int8 int16 int32 int64 float32 float64 usize cstring ptr @@ -39,7 +39,7 @@ syn keyword lavinaThis this " Functions (name after fn) syn match lavinaFuncDef "\" diff --git a/editors/vscode/syntaxes/lavina.tmLanguage.json b/editors/vscode/syntaxes/lavina.tmLanguage.json index 2b7817f..bf5c456 100644 --- a/editors/vscode/syntaxes/lavina.tmLanguage.json +++ b/editors/vscode/syntaxes/lavina.tmLanguage.json @@ -74,7 +74,7 @@ ] }, "storage": { - "match": "\\b(class|struct|enum)\\b", + "match": "\\b(class|struct|enum|extend)\\b", "name": "storage.type.lavina" }, "types": { @@ -129,7 +129,7 @@ ] }, "classes": { - "match": "\\b(class|struct)\\s+([A-Z]\\w*)", + "match": "\\b(class|struct|extend)\\s+([A-Za-z]\\w*)", "captures": { "1": { "name": "storage.type.lavina" }, "2": { "name": "entity.name.type.class.lavina" } diff --git a/runtime/std/collections.lv b/runtime/std/collections.lv index 1e54910..4be7cba 100644 --- a/runtime/std/collections.lv +++ b/runtime/std/collections.lv @@ -1,6 +1,45 @@ // std::collections — higher-order functions, utilities, and set operations -// Higher-order functions +// ── Vector extensions ──────────────────────────────────── + +extend vector: + auto fn map(auto f): + return __lv_col_map(this, f) + + auto fn filter(auto f): + return __lv_col_filter(this, f) + + auto fn reduce(auto f, auto init): + return __lv_col_reduce(this, f, init) + + void fn for_each(auto f): + __lv_col_for_each(this, f) + + auto fn zip(auto other): + return __lv_col_zip(this, other) + + auto fn take(int n): + return __lv_col_take(this, n) + + auto fn drop(int n): + return __lv_col_drop(this, n) + + auto fn enumerate(): + return __lv_col_enumerate(this) + +// ── Hashset extensions ─────────────────────────────────── + +extend hashset: + auto fn union_with(auto other): + return __lv_col_set_union(this, other) + + auto fn intersect(auto other): + return __lv_col_set_intersection(this, other) + + auto fn difference(auto other): + return __lv_col_set_difference(this, other) + +// ── Free functions (generators, standalone utilities) ──── public auto fn map(auto v, auto f): return __lv_col_map(v, f) @@ -14,8 +53,6 @@ public auto fn reduce(auto v, auto f, auto init): public void fn for_each(auto v, auto f): __lv_col_for_each(v, f) -// Utilities - public auto fn zip(auto a, auto b): return __lv_col_zip(a, b) @@ -28,16 +65,12 @@ public auto fn drop(auto v, int n): public auto fn enumerate(auto v): return __lv_col_enumerate(v) -// Generators - public vector[int] fn range(int start, int end): return __lv_col_range(start, end) public vector[int] fn range_step(int start, int end, int step): return __lv_col_range_step(start, end, step) -// Set operations - public auto fn set_union(auto a, auto b): return __lv_col_set_union(a, b) diff --git a/src/ast.lv b/src/ast.lv index 0f58728..2a92408 100644 --- a/src/ast.lv +++ b/src/ast.lv @@ -113,3 +113,4 @@ enum Stmt: Pass(Token keyword) CppBlock(string code) Extern(string header, string import_path, string link_lib, vector[ExternType] types, vector[ExternFn] functions) + Extend(Token target_type, vector[Stmt] methods, string visibility) diff --git a/src/codegen.lv b/src/codegen.lv index 6aa2df5..d100ab9 100644 --- a/src/codegen.lv +++ b/src/codegen.lv @@ -25,6 +25,8 @@ class CppCodegen: vector[string] module_aliases vector[vector[Stmt]] module_stmts vector[vector[Stmt]] lambda_blocks + hashmap[string, vector[Stmt]] extend_methods + bool in_extend constructor(): this.output = "" @@ -46,6 +48,8 @@ class CppCodegen: this.module_aliases = [] this.module_stmts = [] this.lambda_blocks = [] + this.extend_methods = {} + this.in_extend = false void fn set_modules(ref vector[string] short_names, ref vector[string] full_names, ref vector[string] aliases, ref vector[vector[Stmt]] stmts): this.module_short_names = short_names @@ -367,6 +371,12 @@ class CppCodegen: entries.push("{${this.emit_expr(keys[i], m)}, ${this.emit_expr(values[i], m)}}") return "{{${entries.join(", ")}}}" Get(object, name): + if this.in_extend: + match object: + This(kw): + return "self.${name.lexeme}" + _: + return "${this.emit_expr(object, m)}.${name.lexeme}" if m: match object: This(kw): @@ -375,6 +385,12 @@ class CppCodegen: return "${this.emit_expr(object, m)}.${name.lexeme}" return "${this.emit_expr(object, m)}.${name.lexeme}" Set(object, name, value): + if this.in_extend: + match object: + This(kw): + return "self.${name.lexeme} = ${this.emit_expr(value, m)}" + _: + return "${this.emit_expr(object, m)}.${name.lexeme} = ${this.emit_expr(value, m)}" if m: match object: This(kw): @@ -391,6 +407,8 @@ class CppCodegen: return "${this.emit_expr(object, m)}::${name.lexeme}" return "${this.emit_expr(object, m)}::${name.lexeme}" This(keyword): + if this.in_extend: + return "self" return "(*this)" Cast(expr, target_type): string ex = this.emit_expr(expr, m) @@ -443,6 +461,9 @@ class CppCodegen: string remapped = this.try_remap_method(obj, name.lexeme, arg_strs) if remapped != "": return remapped + string ext_call = this.try_extend_method(object, name.lexeme, obj, arg_strs) + if ext_call != "": + return ext_call return "${obj}.${name.lexeme}(${arg_strs.join(", ")})" StaticGet(object, name): string obj = this.emit_expr(object, in_method) @@ -552,6 +573,65 @@ class CppCodegen: return "lv_flatten(${obj})" return "" + string fn get_type_category(ref Expr object): + match object: + Variable(tok): + if this.var_types.has(tok.lexeme): + TypeNode t = this.var_types[tok.lexeme] + match t: + Array(inner): + return "vector" + HashMap(k, v): + return "hashmap" + HashSet(inner): + return "hashset" + Str(): + return "string" + Custom(name, type_args): + return name + _: + return "" + _: + pass + return "" + + string fn try_extend_method(ref Expr object, ref string method, ref string obj, ref vector[string] args): + string type_cat = this.get_type_category(object) + if type_cat != "" and this.extend_methods.has(type_cat): + vector[Stmt] methods = this.extend_methods[type_cat] + for ref ext_m in methods: + match ext_m: + Function(mname, mparams, mret, mbody, is_inline, comptime_mode, is_static, vis, tp): + if mname.lexeme == method: + if args.len() > 0: + return "__ext_${type_cat}_${method}(${obj}, ${args.join(", ")})" + return "__ext_${type_cat}_${method}(${obj})" + _: + pass + return "" + + void fn emit_extend_method(ref string type_key, ref Stmt method): + match method: + Function(mname, mparams, mret, mbody, is_inline, comptime_mode, is_static, vis, tp): + vector[string] saved_dyn = this.dynamic_vars + string ret_type = this.emit_type(mret) + string param_str = this.emit_params(mparams, true) + string all_params = "auto& self" + if param_str != "": + all_params = "auto& self, ${param_str}" + this.output +="${this.indent()}${ret_type} __ext_${type_key}_${mname.lexeme}(${all_params}) {\n" + this.indent_level += 1 + bool saved_extend = this.in_extend + this.in_extend = true + for ref st in mbody: + this.emit_stmt(st, false) + this.in_extend = saved_extend + this.indent_level -= 1 + this.output +="${this.indent()}}\n\n" + this.dynamic_vars = saved_dyn + _: + pass + string fn emit_params(ref vector[Param] params, bool track_dynamic): vector[string] strs = [] for ref p in params: @@ -722,6 +802,8 @@ class CppCodegen: if ef.name != ef.cpp_name: this.extern_fn_names[ef.name] = ef.cpp_name this.extern_fn_params[ef.name] = ef.params + Extend(target, methods, visibility): + pass _: pass @@ -1287,6 +1369,31 @@ class CppCodegen: _: pass + // Collect extend declarations from modules and main + for mi in 0..this.module_stmts.len(): + for ref stmt in this.module_stmts[mi]: + match stmt: + Extend(target, methods, visibility): + string key = target.lexeme + if not this.extend_methods.has(key): + vector[Stmt] empty = [] + this.extend_methods[key] = empty + for ref m in methods: + this.extend_methods[key].push(m) + _: + pass + for ref stmt in stmts: + match stmt: + Extend(target, methods, visibility): + string key = target.lexeme + if not this.extend_methods.has(key): + vector[Stmt] empty = [] + this.extend_methods[key] = empty + for ref m in methods: + this.extend_methods[key].push(m) + _: + pass + this.declarations = "#include \"lavina.h\"\n" for ref inc in this.extern_includes: this.declarations +="${inc}\n" @@ -1296,6 +1403,15 @@ class CppCodegen: for mi in 0..this.module_stmts.len(): this.emit_module(mi) + // Emit extension methods as free functions + auto ext_keys = this.extend_methods.keys() + for ref ek in ext_keys: + vector[Stmt] methods = this.extend_methods[ek] + for ref m in methods: + this.emit_extend_method(ek, m) + this.declarations += this.output + this.output = "" + for ref stmt in stmts: match stmt: Function(name, params, return_type, body, is_inline, comptime_mode, is_static, visibility, type_params): @@ -1316,6 +1432,8 @@ class CppCodegen: this.output = "" Extern(header, import_path, link_lib, types, functions): pass + Extend(target, methods, visibility): + pass _: this.emit_stmt(stmt, false) this.declarations += this.output diff --git a/src/parser.lv b/src/parser.lv index 25be0a4..25f2816 100644 --- a/src/parser.lv +++ b/src/parser.lv @@ -885,6 +885,32 @@ class Parser: this.consume(TK_DEDENT, "Expect dedent to end enum body.") return Stmt::Enum(name, variants, methods, visibility, type_params) + Stmt fn extend_declaration(string visibility): + if not this.match_any([TK_IDENTIFIER, TK_VECTOR, TK_HASHMAP, TK_HASHSET, TK_STRING_TYPE]): + throw "Expect type name after 'extend'." + auto target = this.previous() + this.consume(TK_COLON, "Expect ':' after extend target.") + this.match_any([TK_NEWLINE]) + this.consume(TK_INDENT, "Expect indentation to start extend body.") + vector[Stmt] methods = [] + auto old = this.in_class_body + this.in_class_body = true + while not this.check(TK_DEDENT) and not this.is_at_end(): + if this.match_any([TK_NEWLINE]): + pass + elif this.is_function_start(): + int method_ct = 0 + if this.match_any([TK_COMPTIME_STRICT]): + method_ct = 2 + elif this.match_any([TK_COMPTIME]): + method_ct = 1 + methods.push(this.function_declaration("public", false, method_ct)) + else: + throw "Only method declarations allowed in extend block" + this.consume(TK_DEDENT, "Expect dedent to end extend body.") + this.in_class_body = old + return Stmt::Extend(target, methods, visibility) + Stmt fn import_statement(): vector[Token] path = [] path.push(this.consume(TK_IDENTIFIER, "Expect module name.")) @@ -947,6 +973,9 @@ class Parser: if this.match_any([TK_ENUM]): return this.enum_declaration(visibility) + if this.match_any([TK_EXTEND]): + return this.extend_declaration(visibility) + if this.match_any([TK_TRY]): return this.try_statement() diff --git a/src/scanner.lv b/src/scanner.lv index ee5ab31..ceb83f2 100644 --- a/src/scanner.lv +++ b/src/scanner.lv @@ -76,6 +76,7 @@ const string TK_STATIC = "Static" const string TK_CLASS = "Class" const string TK_STRUCT = "Struct" const string TK_ENUM = "Enum" +const string TK_EXTEND = "Extend" const string TK_THIS = "This" const string TK_TRY = "Try" const string TK_CATCH = "Catch" @@ -244,6 +245,8 @@ string fn lookup_keyword(ref string w): return TK_LINK elif w == "operator": return TK_OPERATOR + elif w == "extend": + return TK_EXTEND return "" // ── Helper functions ──────────────────────────────────────────── diff --git a/stages/stage-latest.cpp b/stages/stage-latest.cpp index aef4fc6..6d93c43 100644 --- a/stages/stage-latest.cpp +++ b/stages/stage-latest.cpp @@ -71,6 +71,7 @@ const std::string TK_STATIC = std::string("Static"); const std::string TK_CLASS = std::string("Class"); const std::string TK_STRUCT = std::string("Struct"); const std::string TK_ENUM = std::string("Enum"); +const std::string TK_EXTEND = std::string("Extend"); const std::string TK_THIS = std::string("This"); const std::string TK_TRY = std::string("Try"); const std::string TK_CATCH = std::string("Catch"); @@ -363,6 +364,11 @@ std::string lookup_keyword(const std::string& w) { if ((w == std::string("operator"))) { return TK_OPERATOR; } + else { + if ((w == std::string("extend"))) { + return TK_EXTEND; + } + } } } } @@ -1152,9 +1158,10 @@ struct Stmt { struct Pass { Token keyword; }; struct CppBlock { std::string code; }; struct Extern { std::string header; std::string import_path; std::string link_lib; std::vector types; std::vector functions; }; + struct Extend { Token target_type; std::vector methods; std::string visibility; }; std::string _tag; - std::variant _data; + std::variant _data; static Stmt make_None() { return {"None", None{}}; } static Stmt make_ExprStmt(Expr expr) { return {"ExprStmt", ExprStmt{expr}}; } @@ -1178,6 +1185,7 @@ struct Stmt { static Stmt make_Pass(Token keyword) { return {"Pass", Pass{keyword}}; } static Stmt make_CppBlock(std::string code) { return {"CppBlock", CppBlock{code}}; } static Stmt make_Extern(std::string header, std::string import_path, std::string link_lib, std::vector types, std::vector functions) { return {"Extern", Extern{header, import_path, link_lib, types, functions}}; } + static Stmt make_Extend(Token target_type, std::vector methods, std::string visibility) { return {"Extend", Extend{target_type, methods, visibility}}; } std::string operator[](const std::string& key) const { if (key == "_tag") return _tag; @@ -1209,6 +1217,8 @@ struct CppCodegen { std::vector module_aliases; std::vector> module_stmts; std::vector> lambda_blocks; + std::unordered_map> extend_methods; + bool in_extend; CppCodegen() { this->output = std::string(""); @@ -1230,6 +1240,8 @@ struct CppCodegen { this->module_aliases = {}; this->module_stmts = {}; this->lambda_blocks = {}; + this->extend_methods = {{}}; + this->in_extend = false; } void set_modules(const std::vector& short_names, const std::vector& full_names, const std::vector& aliases, const std::vector>& stmts) { @@ -1824,12 +1836,25 @@ struct CppCodegen { auto& _v = std::get::Get>(_match_15._data); auto& object = *_v.object; auto& name = _v.name; - if (m) { + if (this->in_extend) { { const auto& _match_16 = object; if (std::holds_alternative::This>(_match_16._data)) { auto& _v = std::get::This>(_match_16._data); auto& kw = _v.keyword; + return ((std::string("self.") + (name.lexeme)) + std::string("")); + } + else { + return ((((std::string("") + ((*this).emit_expr(object, m))) + std::string(".")) + (name.lexeme)) + std::string("")); + } + } + } + if (m) { + { + const auto& _match_17 = object; + if (std::holds_alternative::This>(_match_17._data)) { + auto& _v = std::get::This>(_match_17._data); + auto& kw = _v.keyword; return ((std::string("this->") + (name.lexeme)) + std::string("")); } else { @@ -1844,11 +1869,24 @@ struct CppCodegen { auto& object = *_v.object; auto& name = _v.name; auto& value = *_v.value; + if (this->in_extend) { + { + const auto& _match_18 = object; + if (std::holds_alternative::This>(_match_18._data)) { + auto& _v = std::get::This>(_match_18._data); + auto& kw = _v.keyword; + return ((((std::string("self.") + (name.lexeme)) + std::string(" = ")) + ((*this).emit_expr(value, m))) + std::string("")); + } + else { + return ((((((std::string("") + ((*this).emit_expr(object, m))) + std::string(".")) + (name.lexeme)) + std::string(" = ")) + ((*this).emit_expr(value, m))) + std::string("")); + } + } + } if (m) { { - const auto& _match_17 = object; - if (std::holds_alternative::This>(_match_17._data)) { - auto& _v = std::get::This>(_match_17._data); + const auto& _match_19 = object; + if (std::holds_alternative::This>(_match_19._data)) { + auto& _v = std::get::This>(_match_19._data); auto& kw = _v.keyword; return ((((std::string("this->") + (name.lexeme)) + std::string(" = ")) + ((*this).emit_expr(value, m))) + std::string("")); } @@ -1865,9 +1903,9 @@ struct CppCodegen { auto& name = _v.name; if (m) { { - const auto& _match_18 = object; - if (std::holds_alternative::This>(_match_18._data)) { - auto& _v = std::get::This>(_match_18._data); + const auto& _match_20 = object; + if (std::holds_alternative::This>(_match_20._data)) { + auto& _v = std::get::This>(_match_20._data); auto& kw = _v.keyword; return ((std::string("(*this)::") + (name.lexeme)) + std::string("")); } @@ -1881,6 +1919,9 @@ struct CppCodegen { else if (std::holds_alternative::This>(_match_15._data)) { auto& _v = std::get::This>(_match_15._data); auto& keyword = _v.keyword; + if (this->in_extend) { + return std::string("self"); + } return std::string("(*this)"); } else if (std::holds_alternative::Cast>(_match_15._data)) { @@ -1956,14 +1997,14 @@ struct CppCodegen { bool is_dynamic_expression(const Expr& e) { { - const auto& _match_19 = e; - if (std::holds_alternative::Variable>(_match_19._data)) { - auto& _v = std::get::Variable>(_match_19._data); + const auto& _match_21 = e; + if (std::holds_alternative::Variable>(_match_21._data)) { + auto& _v = std::get::Variable>(_match_21._data); auto& name = _v.name; return (*this).is_dynamic_var(name.lexeme); } - else if (std::holds_alternative::Index>(_match_19._data)) { - auto& _v = std::get::Index>(_match_19._data); + else if (std::holds_alternative::Index>(_match_21._data)) { + auto& _v = std::get::Index>(_match_21._data); auto& object = *_v.object; auto& bracket = _v.bracket; auto& index = *_v.index; @@ -1977,9 +2018,9 @@ struct CppCodegen { std::string emit_call_expr(const Expr& callee, const std::vector& args, bool in_method) { { - const auto& _match_20 = callee; - if (std::holds_alternative::Get>(_match_20._data)) { - auto& _v = std::get::Get>(_match_20._data); + const auto& _match_22 = callee; + if (std::holds_alternative::Get>(_match_22._data)) { + auto& _v = std::get::Get>(_match_22._data); auto& object = *_v.object; auto& name = _v.name; std::string obj = (*this).emit_expr(object, in_method); @@ -1991,10 +2032,14 @@ struct CppCodegen { if ((remapped != std::string(""))) { return remapped; } + std::string ext_call = (*this).try_extend_method(object, name.lexeme, obj, arg_strs); + if ((ext_call != std::string(""))) { + return ext_call; + } return ((((((std::string("") + (obj)) + std::string(".")) + (name.lexeme)) + std::string("(")) + (lv_join(arg_strs, std::string(", ")))) + std::string(")")); } - else if (std::holds_alternative::StaticGet>(_match_20._data)) { - auto& _v = std::get::StaticGet>(_match_20._data); + else if (std::holds_alternative::StaticGet>(_match_22._data)) { + auto& _v = std::get::StaticGet>(_match_22._data); auto& object = *_v.object; auto& name = _v.name; std::string obj = (*this).emit_expr(object, in_method); @@ -2003,9 +2048,9 @@ struct CppCodegen { arg_strs.push_back((*this).emit_expr(a, in_method)); } { - const auto& _match_21 = object; - if (std::holds_alternative::Variable>(_match_21._data)) { - auto& _v = std::get::Variable>(_match_21._data); + const auto& _match_23 = object; + if (std::holds_alternative::Variable>(_match_23._data)) { + auto& _v = std::get::Variable>(_match_23._data); auto& tok = _v.name; if ((*this).is_known_enum(tok.lexeme)) { return ((((((std::string("") + (obj)) + std::string("::make_")) + (name.lexeme)) + std::string("(")) + (lv_join(arg_strs, std::string(", ")))) + std::string(")")); @@ -2017,8 +2062,8 @@ struct CppCodegen { } return ((((((std::string("") + (obj)) + std::string("::")) + (name.lexeme)) + std::string("(")) + (lv_join(arg_strs, std::string(", ")))) + std::string(")")); } - else if (std::holds_alternative::Variable>(_match_20._data)) { - auto& _v = std::get::Variable>(_match_20._data); + else if (std::holds_alternative::Variable>(_match_22._data)) { + auto& _v = std::get::Variable>(_match_22._data); auto& tok = _v.name; std::vector arg_strs = {}; for (const auto& a : args) { @@ -2220,6 +2265,127 @@ struct CppCodegen { return std::string(""); } + std::string get_type_category(const Expr& object) { + { + const auto& _match_24 = object; + if (std::holds_alternative::Variable>(_match_24._data)) { + auto& _v = std::get::Variable>(_match_24._data); + auto& tok = _v.name; + if ((this->var_types.count(tok.lexeme) > 0)) { + TypeNode t = this->var_types[tok.lexeme]; + { + const auto& _match_25 = t; + if (std::holds_alternative::Array>(_match_25._data)) { + auto& _v = std::get::Array>(_match_25._data); + auto& inner = *_v.inner; + return std::string("vector"); + } + else if (std::holds_alternative::HashMap>(_match_25._data)) { + auto& _v = std::get::HashMap>(_match_25._data); + auto& k = *_v.key_type; + auto& v = *_v.value_type; + return std::string("hashmap"); + } + else if (std::holds_alternative::HashSet>(_match_25._data)) { + auto& _v = std::get::HashSet>(_match_25._data); + auto& inner = *_v.inner; + return std::string("hashset"); + } + else if (std::holds_alternative::Str>(_match_25._data)) { + return std::string("string"); + } + else if (std::holds_alternative::Custom>(_match_25._data)) { + auto& _v = std::get::Custom>(_match_25._data); + auto& name = _v.name; + auto& type_args = _v.type_args; + return name; + } + else { + return std::string(""); + } + } + } + } + else { + /* pass */ + } + } + return std::string(""); + } + + std::string try_extend_method(const Expr& object, const std::string& method, const std::string& obj, const std::vector& args) { + std::string type_cat = (*this).get_type_category(object); + if ((type_cat != std::string("")) && (this->extend_methods.count(type_cat) > 0)) { + std::vector methods = this->extend_methods[type_cat]; + for (const auto& ext_m : methods) { + { + const auto& _match_26 = ext_m; + if (std::holds_alternative::Function>(_match_26._data)) { + auto& _v = std::get::Function>(_match_26._data); + auto& mname = _v.name; + auto& mparams = _v.params; + auto& mret = _v.return_type; + auto& mbody = _v.body; + auto& is_inline = _v.is_inline; + auto& comptime_mode = _v.comptime_mode; + auto& is_static = _v.is_static; + auto& vis = _v.visibility; + auto& tp = _v.type_params; + if ((mname.lexeme == method)) { + if ((static_cast(args.size()) > INT64_C(0))) { + return ((((((((std::string("__ext_") + (type_cat)) + std::string("_")) + (method)) + std::string("(")) + (obj)) + std::string(", ")) + (lv_join(args, std::string(", ")))) + std::string(")")); + } + return ((((((std::string("__ext_") + (type_cat)) + std::string("_")) + (method)) + std::string("(")) + (obj)) + std::string(")")); + } + } + else { + /* pass */ + } + } + } + } + return std::string(""); + } + + void emit_extend_method(const std::string& type_key, const Stmt& method) { + { + const auto& _match_27 = method; + if (std::holds_alternative::Function>(_match_27._data)) { + auto& _v = std::get::Function>(_match_27._data); + auto& mname = _v.name; + auto& mparams = _v.params; + auto& mret = _v.return_type; + auto& mbody = _v.body; + auto& is_inline = _v.is_inline; + auto& comptime_mode = _v.comptime_mode; + auto& is_static = _v.is_static; + auto& vis = _v.visibility; + auto& tp = _v.type_params; + std::vector saved_dyn = this->dynamic_vars; + std::string ret_type = (*this).emit_type(mret); + std::string param_str = (*this).emit_params(mparams, true); + std::string all_params = std::string("auto& self"); + if ((param_str != std::string(""))) { + all_params = ((std::string("auto& self, ") + (param_str)) + std::string("")); + } + this->output = (this->output + ((((((((((std::string("") + ((*this).indent())) + std::string("")) + (ret_type)) + std::string(" __ext_")) + (type_key)) + std::string("_")) + (mname.lexeme)) + std::string("(")) + (all_params)) + std::string(") {\n"))); + this->indent_level = (this->indent_level + INT64_C(1)); + bool saved_extend = this->in_extend; + this->in_extend = true; + for (const auto& st : mbody) { + (*this).emit_stmt(st, false); + } + this->in_extend = saved_extend; + this->indent_level = (this->indent_level - INT64_C(1)); + this->output = (this->output + ((std::string("") + ((*this).indent())) + std::string("}\n\n"))); + this->dynamic_vars = saved_dyn; + } + else { + /* pass */ + } + } + } + std::string emit_params(const std::vector& params, bool track_dynamic) { std::vector strs = {}; for (const auto& p : params) { @@ -2237,8 +2403,8 @@ struct CppCodegen { this->var_types[p.name.lexeme] = p.param_type; if (track_dynamic) { { - const auto& _match_22 = p.param_type; - if (std::holds_alternative::Dynamic>(_match_22._data)) { + const auto& _match_28 = p.param_type; + if (std::holds_alternative::Dynamic>(_match_28._data)) { (*this).add_dynamic_var(p.name.lexeme); } else { @@ -2273,17 +2439,17 @@ struct CppCodegen { void emit_stmt(const Stmt& s, bool m) { { - const auto& _match_23 = s; - if (_match_23._tag == "None") { + const auto& _match_29 = s; + if (_match_29._tag == "None") { /* pass */ } - else if (std::holds_alternative::ExprStmt>(_match_23._data)) { - auto& _v = std::get::ExprStmt>(_match_23._data); + else if (std::holds_alternative::ExprStmt>(_match_29._data)) { + auto& _v = std::get::ExprStmt>(_match_29._data); auto& expr = _v.expr; this->output = (this->output + ((((std::string("") + ((*this).indent())) + std::string("")) + ((*this).emit_expr(expr, m))) + std::string(";\n"))); } - else if (std::holds_alternative::Let>(_match_23._data)) { - auto& _v = std::get::Let>(_match_23._data); + else if (std::holds_alternative::Let>(_match_29._data)) { + auto& _v = std::get::Let>(_match_29._data); auto& name = _v.name; auto& var_type = _v.var_type; auto& initializer = _v.initializer; @@ -2292,8 +2458,8 @@ struct CppCodegen { auto& is_mut = _v.is_mut; (*this).emit_let(name, var_type, initializer, m, is_ref, is_mut); } - else if (std::holds_alternative::Const>(_match_23._data)) { - auto& _v = std::get::Const>(_match_23._data); + else if (std::holds_alternative::Const>(_match_29._data)) { + auto& _v = std::get::Const>(_match_29._data); auto& name = _v.name; auto& const_type = _v.const_type; auto& value = _v.value; @@ -2307,13 +2473,13 @@ struct CppCodegen { } this->output = (this->output + ((((((((((std::string("") + ((*this).indent())) + std::string("")) + (prefix)) + std::string("")) + (cpp_type)) + std::string(" ")) + (name.lexeme)) + std::string(" = ")) + (val)) + std::string(";\n"))); } - else if (std::holds_alternative::Return>(_match_23._data)) { - auto& _v = std::get::Return>(_match_23._data); + else if (std::holds_alternative::Return>(_match_29._data)) { + auto& _v = std::get::Return>(_match_29._data); auto& keyword = _v.keyword; auto& value = _v.value; { - const auto& _match_24 = value; - if (_match_24._tag == "None") { + const auto& _match_30 = value; + if (_match_30._tag == "None") { this->output = (this->output + ((std::string("") + ((*this).indent())) + std::string("return;\n"))); } else { @@ -2321,16 +2487,16 @@ struct CppCodegen { } } } - else if (std::holds_alternative::If>(_match_23._data)) { - auto& _v = std::get::If>(_match_23._data); + else if (std::holds_alternative::If>(_match_29._data)) { + auto& _v = std::get::If>(_match_29._data); auto& condition = _v.condition; auto& then_branch = *_v.then_branch; auto& else_branch = *_v.else_branch; this->output = (this->output + ((((std::string("") + ((*this).indent())) + std::string("if (")) + ((*this).emit_expr(condition, m))) + std::string(") "))); (*this).emit_block_or_stmt(then_branch, m); { - const auto& _match_25 = else_branch; - if (_match_25._tag == "None") { + const auto& _match_31 = else_branch; + if (_match_31._tag == "None") { /* pass */ } else { @@ -2339,24 +2505,24 @@ struct CppCodegen { } } } - else if (std::holds_alternative::While>(_match_23._data)) { - auto& _v = std::get::While>(_match_23._data); + else if (std::holds_alternative::While>(_match_29._data)) { + auto& _v = std::get::While>(_match_29._data); auto& condition = _v.condition; auto& body = *_v.body; this->output = (this->output + ((((std::string("") + ((*this).indent())) + std::string("while (")) + ((*this).emit_expr(condition, m))) + std::string(") "))); (*this).emit_block_or_stmt(body, m); } - else if (std::holds_alternative::For>(_match_23._data)) { - auto& _v = std::get::For>(_match_23._data); + else if (std::holds_alternative::For>(_match_29._data)) { + auto& _v = std::get::For>(_match_29._data); auto& item_name = _v.item_name; auto& collection = _v.collection; auto& body = *_v.body; auto& is_ref = _v.is_ref; auto& is_mut = _v.is_mut; { - const auto& _match_26 = collection; - if (std::holds_alternative::Range>(_match_26._data)) { - auto& _v = std::get::Range>(_match_26._data); + const auto& _match_32 = collection; + if (std::holds_alternative::Range>(_match_32._data)) { + auto& _v = std::get::Range>(_match_32._data); auto& start = *_v.start; auto& end = *_v.end; this->output = (this->output + ((((((((((((std::string("") + ((*this).indent())) + std::string("for (int64_t ")) + (item_name.lexeme)) + std::string(" = ")) + ((*this).emit_expr(start, m))) + std::string("; ")) + (item_name.lexeme)) + std::string(" < ")) + ((*this).emit_expr(end, m))) + std::string("; ")) + (item_name.lexeme)) + std::string("++) "))); @@ -2364,9 +2530,9 @@ struct CppCodegen { } else { { - const auto& _match_27 = collection; - if (std::holds_alternative::Variable>(_match_27._data)) { - auto& _v = std::get::Variable>(_match_27._data); + const auto& _match_33 = collection; + if (std::holds_alternative::Variable>(_match_33._data)) { + auto& _v = std::get::Variable>(_match_33._data); auto& tok = _v.name; if ((*this).is_dynamic_var(tok.lexeme)) { (*this).add_dynamic_var(item_name.lexeme); @@ -2390,8 +2556,8 @@ struct CppCodegen { } } } - else if (std::holds_alternative::Block>(_match_23._data)) { - auto& _v = std::get::Block>(_match_23._data); + else if (std::holds_alternative::Block>(_match_29._data)) { + auto& _v = std::get::Block>(_match_29._data); auto& statements = _v.statements; this->output = (this->output + ((std::string("") + ((*this).indent())) + std::string("{\n"))); this->indent_level = (this->indent_level + INT64_C(1)); @@ -2401,8 +2567,8 @@ struct CppCodegen { this->indent_level = (this->indent_level - INT64_C(1)); this->output = (this->output + ((std::string("") + ((*this).indent())) + std::string("}\n"))); } - else if (std::holds_alternative::Try>(_match_23._data)) { - auto& _v = std::get::Try>(_match_23._data); + else if (std::holds_alternative::Try>(_match_29._data)) { + auto& _v = std::get::Try>(_match_29._data); auto& try_body = *_v.try_body; auto& catch_body = *_v.catch_body; auto& exception_name = _v.exception_name; @@ -2415,8 +2581,8 @@ struct CppCodegen { this->output = (this->output + ((((std::string("") + ((*this).indent())) + std::string(" catch (const std::exception& ")) + (exc_name)) + std::string(") "))); (*this).emit_block_or_stmt(catch_body, m); } - else if (std::holds_alternative::Function>(_match_23._data)) { - auto& _v = std::get::Function>(_match_23._data); + else if (std::holds_alternative::Function>(_match_29._data)) { + auto& _v = std::get::Function>(_match_29._data); auto& name = _v.name; auto& params = _v.params; auto& return_type = _v.return_type; @@ -2428,24 +2594,24 @@ struct CppCodegen { auto& type_params = _v.type_params; (*this).emit_function(name, params, return_type, body, type_params, comptime_mode); } - else if (std::holds_alternative::Class>(_match_23._data)) { - auto& _v = std::get::Class>(_match_23._data); + else if (std::holds_alternative::Class>(_match_29._data)) { + auto& _v = std::get::Class>(_match_29._data); auto& name = _v.name; auto& body = _v.body; auto& visibility = _v.visibility; std::vector empty_tp = {}; (*this).emit_class(name, body, empty_tp); } - else if (std::holds_alternative::Struct>(_match_23._data)) { - auto& _v = std::get::Struct>(_match_23._data); + else if (std::holds_alternative::Struct>(_match_29._data)) { + auto& _v = std::get::Struct>(_match_29._data); auto& name = _v.name; auto& body = _v.body; auto& visibility = _v.visibility; auto& type_params = _v.type_params; (*this).emit_class(name, body, type_params); } - else if (std::holds_alternative::Enum>(_match_23._data)) { - auto& _v = std::get::Enum>(_match_23._data); + else if (std::holds_alternative::Enum>(_match_29._data)) { + auto& _v = std::get::Enum>(_match_29._data); auto& name = _v.name; auto& variants = _v.variants; auto& methods = _v.methods; @@ -2453,43 +2619,43 @@ struct CppCodegen { auto& type_params = _v.type_params; (*this).emit_enum(name, variants, methods, type_params); } - else if (std::holds_alternative::Match>(_match_23._data)) { - auto& _v = std::get::Match>(_match_23._data); + else if (std::holds_alternative::Match>(_match_29._data)) { + auto& _v = std::get::Match>(_match_29._data); auto& expr = _v.expr; auto& arm_patterns = _v.arm_patterns; auto& arm_bodies = _v.arm_bodies; (*this).emit_match_impl(expr, arm_patterns, arm_bodies, m); } - else if (std::holds_alternative::Namespace>(_match_23._data)) { - auto& _v = std::get::Namespace>(_match_23._data); + else if (std::holds_alternative::Namespace>(_match_29._data)) { + auto& _v = std::get::Namespace>(_match_29._data); auto& name = _v.name; auto& body = _v.body; auto& visibility = _v.visibility; /* pass */ } - else if (std::holds_alternative::Import>(_match_23._data)) { - auto& _v = std::get::Import>(_match_23._data); + else if (std::holds_alternative::Import>(_match_29._data)) { + auto& _v = std::get::Import>(_match_29._data); auto& path = _v.path; auto& alias = _v.alias; /* pass */ } - else if (std::holds_alternative::Break>(_match_23._data)) { - auto& _v = std::get::Break>(_match_23._data); + else if (std::holds_alternative::Break>(_match_29._data)) { + auto& _v = std::get::Break>(_match_29._data); auto& keyword = _v.keyword; this->output = (this->output + ((std::string("") + ((*this).indent())) + std::string("break;\n"))); } - else if (std::holds_alternative::Continue>(_match_23._data)) { - auto& _v = std::get::Continue>(_match_23._data); + else if (std::holds_alternative::Continue>(_match_29._data)) { + auto& _v = std::get::Continue>(_match_29._data); auto& keyword = _v.keyword; this->output = (this->output + ((std::string("") + ((*this).indent())) + std::string("continue;\n"))); } - else if (std::holds_alternative::Pass>(_match_23._data)) { - auto& _v = std::get::Pass>(_match_23._data); + else if (std::holds_alternative::Pass>(_match_29._data)) { + auto& _v = std::get::Pass>(_match_29._data); auto& keyword = _v.keyword; this->output = (this->output + ((std::string("") + ((*this).indent())) + std::string("/* pass */\n"))); } - else if (std::holds_alternative::CppBlock>(_match_23._data)) { - auto& _v = std::get::CppBlock>(_match_23._data); + else if (std::holds_alternative::CppBlock>(_match_29._data)) { + auto& _v = std::get::CppBlock>(_match_29._data); auto& code = _v.code; auto lines = lv_split(code, std::string("\n")); for (const auto& line : lines) { @@ -2499,8 +2665,8 @@ struct CppCodegen { } } } - else if (std::holds_alternative::Extern>(_match_23._data)) { - auto& _v = std::get::Extern>(_match_23._data); + else if (std::holds_alternative::Extern>(_match_29._data)) { + auto& _v = std::get::Extern>(_match_29._data); auto& header = _v.header; auto& import_path = _v.import_path; auto& link_lib = _v.link_lib; @@ -2557,6 +2723,13 @@ struct CppCodegen { this->extern_fn_params[ef.name] = ef.params; } } + else if (std::holds_alternative::Extend>(_match_29._data)) { + auto& _v = std::get::Extend>(_match_29._data); + auto& target = _v.target_type; + auto& methods = _v.methods; + auto& visibility = _v.visibility; + /* pass */ + } else { /* pass */ } @@ -2579,17 +2752,17 @@ struct CppCodegen { } std::string init_str = std::string(""); { - const auto& _match_28 = initializer; - if (_match_28._tag == "None") { + const auto& _match_34 = initializer; + if (_match_34._tag == "None") { init_str = (*this).default_init(cpp_type); } else { std::string val = (*this).emit_expr(initializer, in_method); bool is_ptr = false; { - const auto& _match_29 = var_type; - if (std::holds_alternative::Ptr>(_match_29._data)) { - auto& _v = std::get::Ptr>(_match_29._data); + const auto& _match_35 = var_type; + if (std::holds_alternative::Ptr>(_match_35._data)) { + auto& _v = std::get::Ptr>(_match_35._data); auto& inner = *_v.inner; is_ptr = true; } @@ -2608,9 +2781,9 @@ struct CppCodegen { void emit_block_or_stmt(const Stmt& s, bool m) { { - const auto& _match_30 = s; - if (std::holds_alternative::Block>(_match_30._data)) { - auto& _v = std::get::Block>(_match_30._data); + const auto& _match_36 = s; + if (std::holds_alternative::Block>(_match_36._data)) { + auto& _v = std::get::Block>(_match_36._data); auto& stmts = _v.statements; this->output = (this->output + std::string("{\n")); this->indent_level = (this->indent_level + INT64_C(1)); @@ -2680,27 +2853,27 @@ struct CppCodegen { std::vector remaining_body = {}; for (const auto& st : init_body) { { - const auto& _match_31 = st; - if (std::holds_alternative::ExprStmt>(_match_31._data)) { - auto& _v = std::get::ExprStmt>(_match_31._data); + const auto& _match_37 = st; + if (std::holds_alternative::ExprStmt>(_match_37._data)) { + auto& _v = std::get::ExprStmt>(_match_37._data); auto& expr = _v.expr; bool handled = false; { - const auto& _match_32 = expr; - if (std::holds_alternative::Set>(_match_32._data)) { - auto& _v = std::get::Set>(_match_32._data); + const auto& _match_38 = expr; + if (std::holds_alternative::Set>(_match_38._data)) { + auto& _v = std::get::Set>(_match_38._data); auto& object = *_v.object; auto& prop = _v.name; auto& value = *_v.value; { - const auto& _match_33 = object; - if (std::holds_alternative::This>(_match_33._data)) { - auto& _v = std::get::This>(_match_33._data); + const auto& _match_39 = object; + if (std::holds_alternative::This>(_match_39._data)) { + auto& _v = std::get::This>(_match_39._data); auto& kw = _v.keyword; { - const auto& _match_34 = value; - if (std::holds_alternative::Variable>(_match_34._data)) { - auto& _v = std::get::Variable>(_match_34._data); + const auto& _match_40 = value; + if (std::holds_alternative::Variable>(_match_40._data)) { + auto& _v = std::get::Variable>(_match_40._data); auto& tok = _v.name; init_list.push_back(((((std::string("") + (prop.lexeme)) + std::string("(")) + (tok.lexeme)) + std::string(")"))); handled = true; @@ -2747,9 +2920,9 @@ struct CppCodegen { void emit_class_method(const Token& class_name, const Stmt& method) { { - const auto& _match_35 = method; - if (std::holds_alternative::Function>(_match_35._data)) { - auto& _v = std::get::Function>(_match_35._data); + const auto& _match_41 = method; + if (std::holds_alternative::Function>(_match_41._data)) { + auto& _v = std::get::Function>(_match_41._data); auto& mname = _v.name; auto& mparams = _v.params; auto& mret = _v.return_type; @@ -2805,9 +2978,9 @@ struct CppCodegen { std::vector let_field_types = {}; for (const auto& st : body) { { - const auto& _match_36 = st; - if (std::holds_alternative::Function>(_match_36._data)) { - auto& _v = std::get::Function>(_match_36._data); + const auto& _match_42 = st; + if (std::holds_alternative::Function>(_match_42._data)) { + auto& _v = std::get::Function>(_match_42._data); auto& fname = _v.name; auto& params = _v.params; auto& return_type = _v.return_type; @@ -2826,8 +2999,8 @@ struct CppCodegen { methods.push_back(st); } } - else if (std::holds_alternative::Let>(_match_36._data)) { - auto& _v = std::get::Let>(_match_36._data); + else if (std::holds_alternative::Let>(_match_42._data)) { + auto& _v = std::get::Let>(_match_42._data); auto& fname = _v.name; auto& var_type = _v.var_type; auto& init = _v.initializer; @@ -2854,21 +3027,21 @@ struct CppCodegen { std::vector seen = {}; for (const auto& st : init_body) { { - const auto& _match_37 = st; - if (std::holds_alternative::ExprStmt>(_match_37._data)) { - auto& _v = std::get::ExprStmt>(_match_37._data); + const auto& _match_43 = st; + if (std::holds_alternative::ExprStmt>(_match_43._data)) { + auto& _v = std::get::ExprStmt>(_match_43._data); auto& expr = _v.expr; { - const auto& _match_38 = expr; - if (std::holds_alternative::Set>(_match_38._data)) { - auto& _v = std::get::Set>(_match_38._data); + const auto& _match_44 = expr; + if (std::holds_alternative::Set>(_match_44._data)) { + auto& _v = std::get::Set>(_match_44._data); auto& object = *_v.object; auto& prop = _v.name; auto& value = *_v.value; { - const auto& _match_39 = object; - if (std::holds_alternative::This>(_match_39._data)) { - auto& _v = std::get::This>(_match_39._data); + const auto& _match_45 = object; + if (std::holds_alternative::This>(_match_45._data)) { + auto& _v = std::get::This>(_match_45._data); auto& kw = _v.keyword; bool already = false; for (const auto& s : seen) { @@ -2913,9 +3086,9 @@ struct CppCodegen { bool has_to_string = false; for (const auto& m : methods) { { - const auto& _match_40 = m; - if (std::holds_alternative::Function>(_match_40._data)) { - auto& _v = std::get::Function>(_match_40._data); + const auto& _match_46 = m; + if (std::holds_alternative::Function>(_match_46._data)) { + auto& _v = std::get::Function>(_match_46._data); auto& mname = _v.name; auto& mparams = _v.params; auto& mret = _v.return_type; @@ -2949,9 +3122,9 @@ struct CppCodegen { std::string infer_expr_type(const Expr& e, const std::vector& param_names, const std::vector& param_types) { { - const auto& _match_41 = e; - if (std::holds_alternative::Literal>(_match_41._data)) { - auto& _v = std::get::Literal>(_match_41._data); + const auto& _match_47 = e; + if (std::holds_alternative::Literal>(_match_47._data)) { + auto& _v = std::get::Literal>(_match_47._data); auto& kind = _v.kind; auto& value = _v.value; if ((kind == std::string("int"))) { @@ -2979,8 +3152,8 @@ struct CppCodegen { } return std::string("std::any"); } - else if (std::holds_alternative::Variable>(_match_41._data)) { - auto& _v = std::get::Variable>(_match_41._data); + else if (std::holds_alternative::Variable>(_match_47._data)) { + auto& _v = std::get::Variable>(_match_47._data); auto& tok = _v.name; for (int64_t i = INT64_C(0); i < static_cast(param_names.size()); i++) { if ((param_names[i] == tok.lexeme)) { @@ -2992,8 +3165,8 @@ struct CppCodegen { } return std::string("std::any"); } - else if (std::holds_alternative::Binary>(_match_41._data)) { - auto& _v = std::get::Binary>(_match_41._data); + else if (std::holds_alternative::Binary>(_match_47._data)) { + auto& _v = std::get::Binary>(_match_47._data); auto& left = *_v.left; auto& op = _v.op; auto& right = *_v.right; @@ -3007,26 +3180,26 @@ struct CppCodegen { } return std::string("std::any"); } - else if (std::holds_alternative::Unary>(_match_41._data)) { - auto& _v = std::get::Unary>(_match_41._data); + else if (std::holds_alternative::Unary>(_match_47._data)) { + auto& _v = std::get::Unary>(_match_47._data); auto& op = _v.op; auto& right = *_v.right; return (*this).infer_expr_type(right, param_names, param_types); } - else if (std::holds_alternative::Grouping>(_match_41._data)) { - auto& _v = std::get::Grouping>(_match_41._data); + else if (std::holds_alternative::Grouping>(_match_47._data)) { + auto& _v = std::get::Grouping>(_match_47._data); auto& inner = *_v.inner; return (*this).infer_expr_type(inner, param_names, param_types); } - else if (std::holds_alternative::Call>(_match_41._data)) { - auto& _v = std::get::Call>(_match_41._data); + else if (std::holds_alternative::Call>(_match_47._data)) { + auto& _v = std::get::Call>(_match_47._data); auto& callee = *_v.callee; auto& paren = _v.paren; auto& args = _v.args; { - const auto& _match_42 = callee; - if (std::holds_alternative::Variable>(_match_42._data)) { - auto& _v = std::get::Variable>(_match_42._data); + const auto& _match_48 = callee; + if (std::holds_alternative::Variable>(_match_48._data)) { + auto& _v = std::get::Variable>(_match_48._data); auto& tok = _v.name; if ((*this).is_known_enum(tok.lexeme)) { return tok.lexeme; @@ -3038,14 +3211,14 @@ struct CppCodegen { } return tok.lexeme; } - else if (std::holds_alternative::StaticGet>(_match_42._data)) { - auto& _v = std::get::StaticGet>(_match_42._data); + else if (std::holds_alternative::StaticGet>(_match_48._data)) { + auto& _v = std::get::StaticGet>(_match_48._data); auto& object = *_v.object; auto& name = _v.name; { - const auto& _match_43 = object; - if (std::holds_alternative::Variable>(_match_43._data)) { - auto& _v = std::get::Variable>(_match_43._data); + const auto& _match_49 = object; + if (std::holds_alternative::Variable>(_match_49._data)) { + auto& _v = std::get::Variable>(_match_49._data); auto& tok = _v.name; if ((*this).is_known_enum(tok.lexeme)) { return tok.lexeme; @@ -3062,8 +3235,8 @@ struct CppCodegen { } } } - else if (std::holds_alternative::Vector>(_match_41._data)) { - auto& _v = std::get::Vector>(_match_41._data); + else if (std::holds_alternative::Vector>(_match_47._data)) { + auto& _v = std::get::Vector>(_match_47._data); auto& elements = _v.elements; if ((static_cast(elements.size()) > INT64_C(0))) { std::string inner = (*this).infer_expr_type(elements[INT64_C(0)], param_names, param_types); @@ -3073,8 +3246,8 @@ struct CppCodegen { } return std::string("std::vector"); } - else if (std::holds_alternative::Map>(_match_41._data)) { - auto& _v = std::get::Map>(_match_41._data); + else if (std::holds_alternative::Map>(_match_47._data)) { + auto& _v = std::get::Map>(_match_47._data); auto& keys = _v.keys; auto& values = _v.values; std::string kt = std::string("std::any"); @@ -3085,8 +3258,8 @@ struct CppCodegen { } return ((((std::string("std::unordered_map<") + (kt)) + std::string(", ")) + (vt)) + std::string(">")); } - else if (std::holds_alternative::Cast>(_match_41._data)) { - auto& _v = std::get::Cast>(_match_41._data); + else if (std::holds_alternative::Cast>(_match_47._data)) { + auto& _v = std::get::Cast>(_match_47._data); auto& expr = *_v.expr; auto& target_type = _v.target_type; return (*this).emit_type(target_type); @@ -3105,9 +3278,9 @@ struct CppCodegen { std::string cpp_type = (*this).emit_type(v.types[fi]); std::string fname = v.field_names[fi]; { - const auto& _match_44 = v.types[fi]; - if (std::holds_alternative::Custom>(_match_44._data)) { - auto& _v = std::get::Custom>(_match_44._data); + const auto& _match_50 = v.types[fi]; + if (std::holds_alternative::Custom>(_match_50._data)) { + auto& _v = std::get::Custom>(_match_50._data); auto& n = _v.name; auto& _ = _v.type_args; if ((n == enum_name)) { @@ -3150,9 +3323,9 @@ struct CppCodegen { std::string fname = v.field_names[fi]; bool is_self_ref = false; { - const auto& _match_45 = v.types[fi]; - if (std::holds_alternative::Custom>(_match_45._data)) { - auto& _v = std::get::Custom>(_match_45._data); + const auto& _match_51 = v.types[fi]; + if (std::holds_alternative::Custom>(_match_51._data)) { + auto& _v = std::get::Custom>(_match_51._data); auto& n = _v.name; auto& _ = _v.type_args; if ((n == enum_name)) { @@ -3206,9 +3379,9 @@ struct CppCodegen { for (const auto& v : variants) { for (const auto& ft : v.types) { { - const auto& _match_46 = ft; - if (std::holds_alternative::Custom>(_match_46._data)) { - auto& _v = std::get::Custom>(_match_46._data); + const auto& _match_52 = ft; + if (std::holds_alternative::Custom>(_match_52._data)) { + auto& _v = std::get::Custom>(_match_52._data); auto& n = _v.name; auto& _ = _v.type_args; if ((n == enum_name)) { @@ -3239,9 +3412,9 @@ struct CppCodegen { bool has_to_string = false; for (const auto& m : methods) { { - const auto& _match_47 = m; - if (std::holds_alternative::Function>(_match_47._data)) { - auto& _v = std::get::Function>(_match_47._data); + const auto& _match_53 = m; + if (std::holds_alternative::Function>(_match_53._data)) { + auto& _v = std::get::Function>(_match_53._data); auto& mname = _v.name; auto& mparams = _v.params; auto& mret = _v.return_type; @@ -3310,9 +3483,9 @@ struct CppCodegen { bool is_self_ref = false; if ((bi < static_cast(vinfo.types.size()))) { { - const auto& _match_48 = vinfo.types[bi]; - if (std::holds_alternative::Custom>(_match_48._data)) { - auto& _v = std::get::Custom>(_match_48._data); + const auto& _match_54 = vinfo.types[bi]; + if (std::holds_alternative::Custom>(_match_54._data)) { + auto& _v = std::get::Custom>(_match_54._data); auto& n = _v.name; auto& _ = _v.type_args; if ((n == ename)) { @@ -3345,9 +3518,9 @@ struct CppCodegen { void emit_arm_body(const Stmt& arm_body, bool in_method) { { - const auto& _match_49 = arm_body; - if (std::holds_alternative::Block>(_match_49._data)) { - auto& _v = std::get::Block>(_match_49._data); + const auto& _match_55 = arm_body; + if (std::holds_alternative::Block>(_match_55._data)) { + auto& _v = std::get::Block>(_match_55._data); auto& stmts = _v.statements; for (const auto& st : stmts) { (*this).emit_stmt(st, in_method); @@ -3361,9 +3534,9 @@ struct CppCodegen { void emit_using_if_public(const std::string& ns, const Stmt& stmt) { { - const auto& _match_50 = stmt; - if (std::holds_alternative::Function>(_match_50._data)) { - auto& _v = std::get::Function>(_match_50._data); + const auto& _match_56 = stmt; + if (std::holds_alternative::Function>(_match_56._data)) { + auto& _v = std::get::Function>(_match_56._data); auto& name = _v.name; auto& params = _v.params; auto& return_type = _v.return_type; @@ -3377,8 +3550,8 @@ struct CppCodegen { this->output = (this->output + ((((std::string("using ") + (ns)) + std::string("::")) + (name.lexeme)) + std::string(";\n"))); } } - else if (std::holds_alternative::Class>(_match_50._data)) { - auto& _v = std::get::Class>(_match_50._data); + else if (std::holds_alternative::Class>(_match_56._data)) { + auto& _v = std::get::Class>(_match_56._data); auto& name = _v.name; auto& body = _v.body; auto& visibility = _v.visibility; @@ -3386,8 +3559,8 @@ struct CppCodegen { this->output = (this->output + ((((std::string("using ") + (ns)) + std::string("::")) + (name.lexeme)) + std::string(";\n"))); } } - else if (std::holds_alternative::Struct>(_match_50._data)) { - auto& _v = std::get::Struct>(_match_50._data); + else if (std::holds_alternative::Struct>(_match_56._data)) { + auto& _v = std::get::Struct>(_match_56._data); auto& name = _v.name; auto& body = _v.body; auto& visibility = _v.visibility; @@ -3396,8 +3569,8 @@ struct CppCodegen { this->output = (this->output + ((((std::string("using ") + (ns)) + std::string("::")) + (name.lexeme)) + std::string(";\n"))); } } - else if (std::holds_alternative::Enum>(_match_50._data)) { - auto& _v = std::get::Enum>(_match_50._data); + else if (std::holds_alternative::Enum>(_match_56._data)) { + auto& _v = std::get::Enum>(_match_56._data); auto& name = _v.name; auto& variants = _v.variants; auto& methods = _v.methods; @@ -3407,8 +3580,8 @@ struct CppCodegen { this->output = (this->output + ((((std::string("using ") + (ns)) + std::string("::")) + (name.lexeme)) + std::string(";\n"))); } } - else if (std::holds_alternative::Const>(_match_50._data)) { - auto& _v = std::get::Const>(_match_50._data); + else if (std::holds_alternative::Const>(_match_56._data)) { + auto& _v = std::get::Const>(_match_56._data); auto& name = _v.name; auto& const_type = _v.const_type; auto& value = _v.value; @@ -3418,8 +3591,8 @@ struct CppCodegen { this->output = (this->output + ((((std::string("using ") + (ns)) + std::string("::")) + (name.lexeme)) + std::string(";\n"))); } } - else if (std::holds_alternative::Let>(_match_50._data)) { - auto& _v = std::get::Let>(_match_50._data); + else if (std::holds_alternative::Let>(_match_56._data)) { + auto& _v = std::get::Let>(_match_56._data); auto& name = _v.name; auto& var_type = _v.var_type; auto& initializer = _v.initializer; @@ -3444,9 +3617,9 @@ struct CppCodegen { this->indent_level = INT64_C(1); for (const auto& stmt : this->module_stmts[index]) { { - const auto& _match_51 = stmt; - if (std::holds_alternative::Function>(_match_51._data)) { - auto& _v = std::get::Function>(_match_51._data); + const auto& _match_57 = stmt; + if (std::holds_alternative::Function>(_match_57._data)) { + auto& _v = std::get::Function>(_match_57._data); auto& name = _v.name; auto& params = _v.params; auto& return_type = _v.return_type; @@ -3463,8 +3636,8 @@ struct CppCodegen { (*this).emit_stmt(stmt, false); } } - else if (std::holds_alternative::Extern>(_match_51._data)) { - auto& _v = std::get::Extern>(_match_51._data); + else if (std::holds_alternative::Extern>(_match_57._data)) { + auto& _v = std::get::Extern>(_match_57._data); auto& header = _v.header; auto& import_path = _v.import_path; auto& link_lib = _v.link_lib; @@ -3499,9 +3672,9 @@ struct CppCodegen { for (int64_t mi = INT64_C(0); mi < static_cast(this->module_stmts.size()); mi++) { for (const auto& stmt : this->module_stmts[mi]) { { - const auto& _match_52 = stmt; - if (std::holds_alternative::Extern>(_match_52._data)) { - auto& _v = std::get::Extern>(_match_52._data); + const auto& _match_58 = stmt; + if (std::holds_alternative::Extern>(_match_58._data)) { + auto& _v = std::get::Extern>(_match_58._data); auto& header = _v.header; auto& import_path = _v.import_path; auto& link_lib = _v.link_lib; @@ -3518,9 +3691,9 @@ struct CppCodegen { } for (const auto& stmt : stmts) { { - const auto& _match_53 = stmt; - if (std::holds_alternative::Extern>(_match_53._data)) { - auto& _v = std::get::Extern>(_match_53._data); + const auto& _match_59 = stmt; + if (std::holds_alternative::Extern>(_match_59._data)) { + auto& _v = std::get::Extern>(_match_59._data); auto& header = _v.header; auto& import_path = _v.import_path; auto& link_lib = _v.link_lib; @@ -3534,6 +3707,52 @@ struct CppCodegen { } } } + for (int64_t mi = INT64_C(0); mi < static_cast(this->module_stmts.size()); mi++) { + for (const auto& stmt : this->module_stmts[mi]) { + { + const auto& _match_60 = stmt; + if (std::holds_alternative::Extend>(_match_60._data)) { + auto& _v = std::get::Extend>(_match_60._data); + auto& target = _v.target_type; + auto& methods = _v.methods; + auto& visibility = _v.visibility; + std::string key = target.lexeme; + if ((!(this->extend_methods.count(key) > 0))) { + std::vector empty = {}; + this->extend_methods[key] = empty; + } + for (const auto& m : methods) { + this->extend_methods[key].push_back(m); + } + } + else { + /* pass */ + } + } + } + } + for (const auto& stmt : stmts) { + { + const auto& _match_61 = stmt; + if (std::holds_alternative::Extend>(_match_61._data)) { + auto& _v = std::get::Extend>(_match_61._data); + auto& target = _v.target_type; + auto& methods = _v.methods; + auto& visibility = _v.visibility; + std::string key = target.lexeme; + if ((!(this->extend_methods.count(key) > 0))) { + std::vector empty = {}; + this->extend_methods[key] = empty; + } + for (const auto& m : methods) { + this->extend_methods[key].push_back(m); + } + } + else { + /* pass */ + } + } + } this->declarations = std::string("#include \"lavina.h\"\n"); for (const auto& inc : this->extern_includes) { this->declarations = (this->declarations + ((std::string("") + (inc)) + std::string("\n"))); @@ -3542,11 +3761,20 @@ struct CppCodegen { for (int64_t mi = INT64_C(0); mi < static_cast(this->module_stmts.size()); mi++) { (*this).emit_module(mi); } + auto ext_keys = lv_keys(this->extend_methods); + for (const auto& ek : ext_keys) { + std::vector methods = this->extend_methods[ek]; + for (const auto& m : methods) { + (*this).emit_extend_method(ek, m); + } + this->declarations = (this->declarations + this->output); + this->output = std::string(""); + } for (const auto& stmt : stmts) { { - const auto& _match_54 = stmt; - if (std::holds_alternative::Function>(_match_54._data)) { - auto& _v = std::get::Function>(_match_54._data); + const auto& _match_62 = stmt; + if (std::holds_alternative::Function>(_match_62._data)) { + auto& _v = std::get::Function>(_match_62._data); auto& name = _v.name; auto& params = _v.params; auto& return_type = _v.return_type; @@ -3575,8 +3803,8 @@ struct CppCodegen { this->output = std::string(""); } } - else if (std::holds_alternative::Extern>(_match_54._data)) { - auto& _v = std::get::Extern>(_match_54._data); + else if (std::holds_alternative::Extern>(_match_62._data)) { + auto& _v = std::get::Extern>(_match_62._data); auto& header = _v.header; auto& import_path = _v.import_path; auto& link_lib = _v.link_lib; @@ -3584,6 +3812,13 @@ struct CppCodegen { auto& functions = _v.functions; /* pass */ } + else if (std::holds_alternative::Extend>(_match_62._data)) { + auto& _v = std::get::Extend>(_match_62._data); + auto& target = _v.target_type; + auto& methods = _v.methods; + auto& visibility = _v.visibility; + /* pass */ + } else { (*this).emit_stmt(stmt, false); this->declarations = (this->declarations + this->output); @@ -3718,27 +3953,27 @@ struct Checker { bool types_compatible(const TypeNode& expected, const TypeNode& actual) { { - const auto& _match_55 = actual; - if (std::holds_alternative::Array>(_match_55._data)) { - auto& _v = std::get::Array>(_match_55._data); + const auto& _match_63 = actual; + if (std::holds_alternative::Array>(_match_63._data)) { + auto& _v = std::get::Array>(_match_63._data); auto& a_inner = *_v.inner; { - const auto& _match_56 = a_inner; - if (std::holds_alternative::Auto>(_match_56._data)) { + const auto& _match_64 = a_inner; + if (std::holds_alternative::Auto>(_match_64._data)) { { - const auto& _match_57 = expected; - if (std::holds_alternative::Array>(_match_57._data)) { - auto& _v = std::get::Array>(_match_57._data); + const auto& _match_65 = expected; + if (std::holds_alternative::Array>(_match_65._data)) { + auto& _v = std::get::Array>(_match_65._data); auto& e_inner = *_v.inner; return true; } - else if (std::holds_alternative::HashSet>(_match_57._data)) { - auto& _v = std::get::HashSet>(_match_57._data); + else if (std::holds_alternative::HashSet>(_match_65._data)) { + auto& _v = std::get::HashSet>(_match_65._data); auto& e_inner = *_v.inner; return true; } - else if (std::holds_alternative::HashMap>(_match_57._data)) { - auto& _v = std::get::HashMap>(_match_57._data); + else if (std::holds_alternative::HashMap>(_match_65._data)) { + auto& _v = std::get::HashMap>(_match_65._data); auto& ek = *_v.key_type; auto& ev = *_v.value_type; return true; @@ -3758,14 +3993,14 @@ struct Checker { } } { - const auto& _match_58 = expected; - if (std::holds_alternative::Array>(_match_58._data)) { - auto& _v = std::get::Array>(_match_58._data); + const auto& _match_66 = expected; + if (std::holds_alternative::Array>(_match_66._data)) { + auto& _v = std::get::Array>(_match_66._data); auto& e_inner = *_v.inner; { - const auto& _match_59 = actual; - if (std::holds_alternative::Array>(_match_59._data)) { - auto& _v = std::get::Array>(_match_59._data); + const auto& _match_67 = actual; + if (std::holds_alternative::Array>(_match_67._data)) { + auto& _v = std::get::Array>(_match_67._data); auto& a_inner = *_v.inner; return (*this).types_compatible(e_inner, a_inner); } @@ -3779,14 +4014,14 @@ struct Checker { } } { - const auto& _match_60 = expected; - if (std::holds_alternative::Auto>(_match_60._data)) { + const auto& _match_68 = expected; + if (std::holds_alternative::Auto>(_match_68._data)) { return true; } - else if (_match_60._tag == "None") { + else if (_match_68._tag == "None") { return true; } - else if (std::holds_alternative::Dynamic>(_match_60._data)) { + else if (std::holds_alternative::Dynamic>(_match_68._data)) { return true; } else { @@ -3794,14 +4029,14 @@ struct Checker { } } { - const auto& _match_61 = actual; - if (std::holds_alternative::Auto>(_match_61._data)) { + const auto& _match_69 = actual; + if (std::holds_alternative::Auto>(_match_69._data)) { return true; } - else if (_match_61._tag == "None") { + else if (_match_69._tag == "None") { return true; } - else if (std::holds_alternative::Dynamic>(_match_61._data)) { + else if (std::holds_alternative::Dynamic>(_match_69._data)) { return true; } else { @@ -3816,20 +4051,20 @@ struct Checker { bool e_is_int = false; bool a_is_int = false; { - const auto& _match_62 = expected; - if (std::holds_alternative::Int>(_match_62._data)) { + const auto& _match_70 = expected; + if (std::holds_alternative::Int>(_match_70._data)) { e_is_int = true; } - else if (std::holds_alternative::Int8>(_match_62._data)) { + else if (std::holds_alternative::Int8>(_match_70._data)) { e_is_int = true; } - else if (std::holds_alternative::Int16>(_match_62._data)) { + else if (std::holds_alternative::Int16>(_match_70._data)) { e_is_int = true; } - else if (std::holds_alternative::Int32>(_match_62._data)) { + else if (std::holds_alternative::Int32>(_match_70._data)) { e_is_int = true; } - else if (std::holds_alternative::USize>(_match_62._data)) { + else if (std::holds_alternative::USize>(_match_70._data)) { e_is_int = true; } else { @@ -3837,20 +4072,20 @@ struct Checker { } } { - const auto& _match_63 = actual; - if (std::holds_alternative::Int>(_match_63._data)) { + const auto& _match_71 = actual; + if (std::holds_alternative::Int>(_match_71._data)) { a_is_int = true; } - else if (std::holds_alternative::Int8>(_match_63._data)) { + else if (std::holds_alternative::Int8>(_match_71._data)) { a_is_int = true; } - else if (std::holds_alternative::Int16>(_match_63._data)) { + else if (std::holds_alternative::Int16>(_match_71._data)) { a_is_int = true; } - else if (std::holds_alternative::Int32>(_match_63._data)) { + else if (std::holds_alternative::Int32>(_match_71._data)) { a_is_int = true; } - else if (std::holds_alternative::USize>(_match_63._data)) { + else if (std::holds_alternative::USize>(_match_71._data)) { a_is_int = true; } else { @@ -3863,11 +4098,11 @@ struct Checker { bool e_is_float = false; bool a_is_float = false; { - const auto& _match_64 = expected; - if (std::holds_alternative::Float>(_match_64._data)) { + const auto& _match_72 = expected; + if (std::holds_alternative::Float>(_match_72._data)) { e_is_float = true; } - else if (std::holds_alternative::Float32>(_match_64._data)) { + else if (std::holds_alternative::Float32>(_match_72._data)) { e_is_float = true; } else { @@ -3875,11 +4110,11 @@ struct Checker { } } { - const auto& _match_65 = actual; - if (std::holds_alternative::Float>(_match_65._data)) { + const auto& _match_73 = actual; + if (std::holds_alternative::Float>(_match_73._data)) { a_is_float = true; } - else if (std::holds_alternative::Float32>(_match_65._data)) { + else if (std::holds_alternative::Float32>(_match_73._data)) { a_is_float = true; } else { @@ -3893,11 +4128,11 @@ struct Checker { return true; } { - const auto& _match_66 = expected; - if (std::holds_alternative::CString>(_match_66._data)) { + const auto& _match_74 = expected; + if (std::holds_alternative::CString>(_match_74._data)) { { - const auto& _match_67 = actual; - if (std::holds_alternative::Str>(_match_67._data)) { + const auto& _match_75 = actual; + if (std::holds_alternative::Str>(_match_75._data)) { return true; } else { @@ -3905,10 +4140,10 @@ struct Checker { } } } - else if (std::holds_alternative::Str>(_match_66._data)) { + else if (std::holds_alternative::Str>(_match_74._data)) { { - const auto& _match_68 = actual; - if (std::holds_alternative::CString>(_match_68._data)) { + const auto& _match_76 = actual; + if (std::holds_alternative::CString>(_match_76._data)) { return true; } else { @@ -3921,13 +4156,13 @@ struct Checker { } } { - const auto& _match_69 = expected; - if (std::holds_alternative::Nullable>(_match_69._data)) { - auto& _v = std::get::Nullable>(_match_69._data); + const auto& _match_77 = expected; + if (std::holds_alternative::Nullable>(_match_77._data)) { + auto& _v = std::get::Nullable>(_match_77._data); auto& inner = *_v.inner; { - const auto& _match_70 = actual; - if (std::holds_alternative::NullType>(_match_70._data)) { + const auto& _match_78 = actual; + if (std::holds_alternative::NullType>(_match_78._data)) { return true; } else { @@ -3940,13 +4175,13 @@ struct Checker { } } { - const auto& _match_71 = expected; - if (std::holds_alternative::Ptr>(_match_71._data)) { - auto& _v = std::get::Ptr>(_match_71._data); + const auto& _match_79 = expected; + if (std::holds_alternative::Ptr>(_match_79._data)) { + auto& _v = std::get::Ptr>(_match_79._data); auto& inner = *_v.inner; { - const auto& _match_72 = actual; - if (std::holds_alternative::NullType>(_match_72._data)) { + const auto& _match_80 = actual; + if (std::holds_alternative::NullType>(_match_80._data)) { return true; } else { @@ -3963,78 +4198,78 @@ struct Checker { std::string type_name(const TypeNode& t) { { - const auto& _match_73 = t; - if (std::holds_alternative::Int>(_match_73._data)) { + const auto& _match_81 = t; + if (std::holds_alternative::Int>(_match_81._data)) { return std::string("int"); } - else if (std::holds_alternative::Float>(_match_73._data)) { + else if (std::holds_alternative::Float>(_match_81._data)) { return std::string("float"); } - else if (std::holds_alternative::Str>(_match_73._data)) { + else if (std::holds_alternative::Str>(_match_81._data)) { return std::string("string"); } - else if (std::holds_alternative::Bool>(_match_73._data)) { + else if (std::holds_alternative::Bool>(_match_81._data)) { return std::string("bool"); } - else if (std::holds_alternative::Void>(_match_73._data)) { + else if (std::holds_alternative::Void>(_match_81._data)) { return std::string("void"); } - else if (std::holds_alternative::Auto>(_match_73._data)) { + else if (std::holds_alternative::Auto>(_match_81._data)) { return std::string("auto"); } - else if (std::holds_alternative::Dynamic>(_match_73._data)) { + else if (std::holds_alternative::Dynamic>(_match_81._data)) { return std::string("dynamic"); } - else if (std::holds_alternative::NullType>(_match_73._data)) { + else if (std::holds_alternative::NullType>(_match_81._data)) { return std::string("null"); } - else if (std::holds_alternative::Custom>(_match_73._data)) { - auto& _v = std::get::Custom>(_match_73._data); + else if (std::holds_alternative::Custom>(_match_81._data)) { + auto& _v = std::get::Custom>(_match_81._data); auto& name = _v.name; auto& _ = _v.type_args; return name; } - else if (std::holds_alternative::Array>(_match_73._data)) { - auto& _v = std::get::Array>(_match_73._data); + else if (std::holds_alternative::Array>(_match_81._data)) { + auto& _v = std::get::Array>(_match_81._data); auto& inner = *_v.inner; return ((std::string("vector[") + ((*this).type_name(inner))) + std::string("]")); } - else if (std::holds_alternative::HashSet>(_match_73._data)) { - auto& _v = std::get::HashSet>(_match_73._data); + else if (std::holds_alternative::HashSet>(_match_81._data)) { + auto& _v = std::get::HashSet>(_match_81._data); auto& inner = *_v.inner; return ((std::string("set[") + ((*this).type_name(inner))) + std::string("]")); } - else if (std::holds_alternative::HashMap>(_match_73._data)) { - auto& _v = std::get::HashMap>(_match_73._data); + else if (std::holds_alternative::HashMap>(_match_81._data)) { + auto& _v = std::get::HashMap>(_match_81._data); auto& k = *_v.key_type; auto& v = *_v.value_type; return ((((std::string("map[") + ((*this).type_name(k))) + std::string(", ")) + ((*this).type_name(v))) + std::string("]")); } - else if (std::holds_alternative::Nullable>(_match_73._data)) { - auto& _v = std::get::Nullable>(_match_73._data); + else if (std::holds_alternative::Nullable>(_match_81._data)) { + auto& _v = std::get::Nullable>(_match_81._data); auto& inner = *_v.inner; return ((std::string("") + ((*this).type_name(inner))) + std::string("?")); } - else if (std::holds_alternative::Int8>(_match_73._data)) { + else if (std::holds_alternative::Int8>(_match_81._data)) { return std::string("int8"); } - else if (std::holds_alternative::Int16>(_match_73._data)) { + else if (std::holds_alternative::Int16>(_match_81._data)) { return std::string("int16"); } - else if (std::holds_alternative::Int32>(_match_73._data)) { + else if (std::holds_alternative::Int32>(_match_81._data)) { return std::string("int32"); } - else if (std::holds_alternative::Float32>(_match_73._data)) { + else if (std::holds_alternative::Float32>(_match_81._data)) { return std::string("float32"); } - else if (std::holds_alternative::USize>(_match_73._data)) { + else if (std::holds_alternative::USize>(_match_81._data)) { return std::string("usize"); } - else if (std::holds_alternative::CString>(_match_73._data)) { + else if (std::holds_alternative::CString>(_match_81._data)) { return std::string("cstring"); } - else if (std::holds_alternative::Ptr>(_match_73._data)) { - auto& _v = std::get::Ptr>(_match_73._data); + else if (std::holds_alternative::Ptr>(_match_81._data)) { + auto& _v = std::get::Ptr>(_match_81._data); auto& inner = *_v.inner; return ((std::string("ptr[") + ((*this).type_name(inner))) + std::string("]")); } @@ -4046,12 +4281,12 @@ struct Checker { TypeNode infer_type(const Expr& e) { { - const auto& _match_74 = e; - if (_match_74._tag == "None") { + const auto& _match_82 = e; + if (_match_82._tag == "None") { return TypeNode::make_None(); } - else if (std::holds_alternative::Literal>(_match_74._data)) { - auto& _v = std::get::Literal>(_match_74._data); + else if (std::holds_alternative::Literal>(_match_82._data)) { + auto& _v = std::get::Literal>(_match_82._data); auto& kind = _v.kind; auto& value = _v.value; if ((kind == std::string("int"))) { @@ -4079,14 +4314,14 @@ struct Checker { } return TypeNode::make_Auto(); } - else if (std::holds_alternative::Variable>(_match_74._data)) { - auto& _v = std::get::Variable>(_match_74._data); + else if (std::holds_alternative::Variable>(_match_82._data)) { + auto& _v = std::get::Variable>(_match_82._data); auto& name = _v.name; auto sym = (*this).lookup(name.lexeme); return sym.sym_type; } - else if (std::holds_alternative::Binary>(_match_74._data)) { - auto& _v = std::get::Binary>(_match_74._data); + else if (std::holds_alternative::Binary>(_match_82._data)) { + auto& _v = std::get::Binary>(_match_82._data); auto& left = *_v.left; auto& op = _v.op; auto& right = *_v.right; @@ -4096,8 +4331,8 @@ struct Checker { return TypeNode::make_Bool(); } { - const auto& _match_75 = lt; - if (std::holds_alternative::Str>(_match_75._data)) { + const auto& _match_83 = lt; + if (std::holds_alternative::Str>(_match_83._data)) { return TypeNode::make_Str(); } else { @@ -4105,8 +4340,8 @@ struct Checker { } } { - const auto& _match_76 = lt; - if (std::holds_alternative::Float>(_match_76._data)) { + const auto& _match_84 = lt; + if (std::holds_alternative::Float>(_match_84._data)) { return TypeNode::make_Float(); } else { @@ -4114,8 +4349,8 @@ struct Checker { } } { - const auto& _match_77 = rt; - if (std::holds_alternative::Float>(_match_77._data)) { + const auto& _match_85 = rt; + if (std::holds_alternative::Float>(_match_85._data)) { return TypeNode::make_Float(); } else { @@ -4123,8 +4358,8 @@ struct Checker { } } { - const auto& _match_78 = lt; - if (std::holds_alternative::Int>(_match_78._data)) { + const auto& _match_86 = lt; + if (std::holds_alternative::Int>(_match_86._data)) { return TypeNode::make_Int(); } else { @@ -4133,8 +4368,8 @@ struct Checker { } return TypeNode::make_Auto(); } - else if (std::holds_alternative::Unary>(_match_74._data)) { - auto& _v = std::get::Unary>(_match_74._data); + else if (std::holds_alternative::Unary>(_match_82._data)) { + auto& _v = std::get::Unary>(_match_82._data); auto& op = _v.op; auto& right = *_v.right; if ((op.token_type == TK_BANG) || (op.token_type == TK_NOT)) { @@ -4142,35 +4377,35 @@ struct Checker { } return (*this).infer_type(right); } - else if (std::holds_alternative::Logical>(_match_74._data)) { - auto& _v = std::get::Logical>(_match_74._data); + else if (std::holds_alternative::Logical>(_match_82._data)) { + auto& _v = std::get::Logical>(_match_82._data); auto& left = *_v.left; auto& op = _v.op; auto& right = *_v.right; return TypeNode::make_Bool(); } - else if (std::holds_alternative::Call>(_match_74._data)) { - auto& _v = std::get::Call>(_match_74._data); + else if (std::holds_alternative::Call>(_match_82._data)) { + auto& _v = std::get::Call>(_match_82._data); auto& callee = *_v.callee; auto& paren = _v.paren; auto& args = _v.args; return (*this).infer_call_type(callee); } - else if (std::holds_alternative::Grouping>(_match_74._data)) { - auto& _v = std::get::Grouping>(_match_74._data); + else if (std::holds_alternative::Grouping>(_match_82._data)) { + auto& _v = std::get::Grouping>(_match_82._data); auto& inner = *_v.inner; return (*this).infer_type(inner); } - else if (std::holds_alternative::Index>(_match_74._data)) { - auto& _v = std::get::Index>(_match_74._data); + else if (std::holds_alternative::Index>(_match_82._data)) { + auto& _v = std::get::Index>(_match_82._data); auto& object = *_v.object; auto& bracket = _v.bracket; auto& index = *_v.index; auto ot = (*this).infer_type(object); { - const auto& _match_79 = ot; - if (std::holds_alternative::Array>(_match_79._data)) { - auto& _v = std::get::Array>(_match_79._data); + const auto& _match_87 = ot; + if (std::holds_alternative::Array>(_match_87._data)) { + auto& _v = std::get::Array>(_match_87._data); auto& inner = *_v.inner; return inner; } @@ -4179,8 +4414,8 @@ struct Checker { } } } - else if (std::holds_alternative::Vector>(_match_74._data)) { - auto& _v = std::get::Vector>(_match_74._data); + else if (std::holds_alternative::Vector>(_match_82._data)) { + auto& _v = std::get::Vector>(_match_82._data); auto& elements = _v.elements; if ((static_cast(elements.size()) > INT64_C(0))) { auto inner = (*this).infer_type(elements[INT64_C(0)]); @@ -4188,24 +4423,24 @@ struct Checker { } return TypeNode::make_Array(TypeNode::make_Auto()); } - else if (std::holds_alternative::Cast>(_match_74._data)) { - auto& _v = std::get::Cast>(_match_74._data); + else if (std::holds_alternative::Cast>(_match_82._data)) { + auto& _v = std::get::Cast>(_match_82._data); auto& expr = *_v.expr; auto& target_type = _v.target_type; return target_type; } - else if (std::holds_alternative::This>(_match_74._data)) { - auto& _v = std::get::This>(_match_74._data); + else if (std::holds_alternative::This>(_match_82._data)) { + auto& _v = std::get::This>(_match_82._data); auto& kw = _v.keyword; return TypeNode::make_Custom(this->current_class_name, {}); } - else if (std::holds_alternative::Own>(_match_74._data)) { - auto& _v = std::get::Own>(_match_74._data); + else if (std::holds_alternative::Own>(_match_82._data)) { + auto& _v = std::get::Own>(_match_82._data); auto& expr = *_v.expr; return (*this).infer_type(expr); } - else if (std::holds_alternative::AddressOf>(_match_74._data)) { - auto& _v = std::get::AddressOf>(_match_74._data); + else if (std::holds_alternative::AddressOf>(_match_82._data)) { + auto& _v = std::get::AddressOf>(_match_82._data); auto& expr = *_v.expr; return TypeNode::make_Ptr((*this).infer_type(expr)); } @@ -4217,9 +4452,9 @@ struct Checker { TypeNode infer_call_type(const Expr& callee) { { - const auto& _match_80 = callee; - if (std::holds_alternative::Variable>(_match_80._data)) { - auto& _v = std::get::Variable>(_match_80._data); + const auto& _match_88 = callee; + if (std::holds_alternative::Variable>(_match_88._data)) { + auto& _v = std::get::Variable>(_match_88._data); auto& name = _v.name; if ((this->known_funcs.count(name.lexeme) > 0)) { ExternFn fi = this->known_funcs[name.lexeme]; @@ -4233,20 +4468,20 @@ struct Checker { } return TypeNode::make_Auto(); } - else if (std::holds_alternative::Get>(_match_80._data)) { - auto& _v = std::get::Get>(_match_80._data); + else if (std::holds_alternative::Get>(_match_88._data)) { + auto& _v = std::get::Get>(_match_88._data); auto& object = *_v.object; auto& name = _v.name; return TypeNode::make_Auto(); } - else if (std::holds_alternative::StaticGet>(_match_80._data)) { - auto& _v = std::get::StaticGet>(_match_80._data); + else if (std::holds_alternative::StaticGet>(_match_88._data)) { + auto& _v = std::get::StaticGet>(_match_88._data); auto& object = *_v.object; auto& name = _v.name; { - const auto& _match_81 = object; - if (std::holds_alternative::Variable>(_match_81._data)) { - auto& _v = std::get::Variable>(_match_81._data); + const auto& _match_89 = object; + if (std::holds_alternative::Variable>(_match_89._data)) { + auto& _v = std::get::Variable>(_match_89._data); auto& obj_name = _v.name; if ((this->known_enums.count(obj_name.lexeme) > 0)) { return TypeNode::make_Custom(obj_name.lexeme, {}); @@ -4299,9 +4534,9 @@ struct Checker { void collect_decl(const Stmt& s) { { - const auto& _match_82 = s; - if (std::holds_alternative::Function>(_match_82._data)) { - auto& _v = std::get::Function>(_match_82._data); + const auto& _match_90 = s; + if (std::holds_alternative::Function>(_match_90._data)) { + auto& _v = std::get::Function>(_match_90._data); auto& name = _v.name; auto& params = _v.params; auto& return_type = _v.return_type; @@ -4313,15 +4548,15 @@ struct Checker { auto& type_params = _v.type_params; this->known_funcs[name.lexeme] = ExternFn(name.lexeme, name.lexeme, return_type, params); } - else if (std::holds_alternative::Class>(_match_82._data)) { - auto& _v = std::get::Class>(_match_82._data); + else if (std::holds_alternative::Class>(_match_90._data)) { + auto& _v = std::get::Class>(_match_90._data); auto& name = _v.name; auto& body = _v.body; auto& visibility = _v.visibility; this->known_classes[name.lexeme] = body; } - else if (std::holds_alternative::Enum>(_match_82._data)) { - auto& _v = std::get::Enum>(_match_82._data); + else if (std::holds_alternative::Enum>(_match_90._data)) { + auto& _v = std::get::Enum>(_match_90._data); auto& name = _v.name; auto& variants = _v.variants; auto& methods = _v.methods; @@ -4329,8 +4564,8 @@ struct Checker { auto& enum_tp = _v.type_params; this->known_enums[name.lexeme] = variants; } - else if (std::holds_alternative::Const>(_match_82._data)) { - auto& _v = std::get::Const>(_match_82._data); + else if (std::holds_alternative::Const>(_match_90._data)) { + auto& _v = std::get::Const>(_match_90._data); auto& name = _v.name; auto& const_type = _v.const_type; auto& value = _v.value; @@ -4338,16 +4573,16 @@ struct Checker { auto& comptime_mode = _v.comptime_mode; (*this).declare(name.lexeme, const_type, std::string("const"), false, false, name); } - else if (std::holds_alternative::Struct>(_match_82._data)) { - auto& _v = std::get::Struct>(_match_82._data); + else if (std::holds_alternative::Struct>(_match_90._data)) { + auto& _v = std::get::Struct>(_match_90._data); auto& name = _v.name; auto& body = _v.body; auto& visibility = _v.visibility; auto& struct_tp = _v.type_params; this->known_classes[name.lexeme] = body; } - else if (std::holds_alternative::Namespace>(_match_82._data)) { - auto& _v = std::get::Namespace>(_match_82._data); + else if (std::holds_alternative::Namespace>(_match_90._data)) { + auto& _v = std::get::Namespace>(_match_90._data); auto& name = _v.name; auto& body = _v.body; auto& visibility = _v.visibility; @@ -4355,8 +4590,8 @@ struct Checker { (*this).collect_decl(ns_stmt); } } - else if (std::holds_alternative::Extern>(_match_82._data)) { - auto& _v = std::get::Extern>(_match_82._data); + else if (std::holds_alternative::Extern>(_match_90._data)) { + auto& _v = std::get::Extern>(_match_90._data); auto& header = _v.header; auto& import_path = _v.import_path; auto& link_lib = _v.link_lib; @@ -4378,14 +4613,14 @@ struct Checker { void check_stmt(const Stmt& s) { { - const auto& _match_83 = s; - if (std::holds_alternative::ExprStmt>(_match_83._data)) { - auto& _v = std::get::ExprStmt>(_match_83._data); + const auto& _match_91 = s; + if (std::holds_alternative::ExprStmt>(_match_91._data)) { + auto& _v = std::get::ExprStmt>(_match_91._data); auto& expr = _v.expr; (*this).check_expr(expr); } - else if (std::holds_alternative::Let>(_match_83._data)) { - auto& _v = std::get::Let>(_match_83._data); + else if (std::holds_alternative::Let>(_match_91._data)) { + auto& _v = std::get::Let>(_match_91._data); auto& name = _v.name; auto& var_type = _v.var_type; auto& initializer = _v.initializer; @@ -4395,11 +4630,11 @@ struct Checker { (*this).check_expr(initializer); auto init_type = (*this).infer_type(initializer); { - const auto& _match_84 = var_type; - if (std::holds_alternative::Auto>(_match_84._data)) { + const auto& _match_92 = var_type; + if (std::holds_alternative::Auto>(_match_92._data)) { /* pass */ } - else if (_match_84._tag == "None") { + else if (_match_92._tag == "None") { /* pass */ } else { @@ -4410,8 +4645,8 @@ struct Checker { } (*this).declare(name.lexeme, var_type, std::string("var"), is_ref, true, name); } - else if (std::holds_alternative::Const>(_match_83._data)) { - auto& _v = std::get::Const>(_match_83._data); + else if (std::holds_alternative::Const>(_match_91._data)) { + auto& _v = std::get::Const>(_match_91._data); auto& name = _v.name; auto& const_type = _v.const_type; auto& value = _v.value; @@ -4419,20 +4654,20 @@ struct Checker { auto& comptime_mode = _v.comptime_mode; (*this).check_expr(value); } - else if (std::holds_alternative::Return>(_match_83._data)) { - auto& _v = std::get::Return>(_match_83._data); + else if (std::holds_alternative::Return>(_match_91._data)) { + auto& _v = std::get::Return>(_match_91._data); auto& keyword = _v.keyword; auto& value = _v.value; (*this).check_expr(value); { - const auto& _match_85 = this->current_return_type; - if (_match_85._tag == "None") { + const auto& _match_93 = this->current_return_type; + if (_match_93._tag == "None") { /* pass */ } - else if (std::holds_alternative::Void>(_match_85._data)) { + else if (std::holds_alternative::Void>(_match_93._data)) { { - const auto& _match_86 = value; - if (_match_86._tag == "None") { + const auto& _match_94 = value; + if (_match_94._tag == "None") { /* pass */ } else { @@ -4448,8 +4683,8 @@ struct Checker { } } } - else if (std::holds_alternative::If>(_match_83._data)) { - auto& _v = std::get::If>(_match_83._data); + else if (std::holds_alternative::If>(_match_91._data)) { + auto& _v = std::get::If>(_match_91._data); auto& condition = _v.condition; auto& then_branch = *_v.then_branch; auto& else_branch = *_v.else_branch; @@ -4457,15 +4692,15 @@ struct Checker { (*this).check_stmt(then_branch); (*this).check_stmt(else_branch); } - else if (std::holds_alternative::While>(_match_83._data)) { - auto& _v = std::get::While>(_match_83._data); + else if (std::holds_alternative::While>(_match_91._data)) { + auto& _v = std::get::While>(_match_91._data); auto& condition = _v.condition; auto& body = *_v.body; (*this).check_expr(condition); (*this).check_stmt(body); } - else if (std::holds_alternative::For>(_match_83._data)) { - auto& _v = std::get::For>(_match_83._data); + else if (std::holds_alternative::For>(_match_91._data)) { + auto& _v = std::get::For>(_match_91._data); auto& item_name = _v.item_name; auto& collection = _v.collection; auto& body = *_v.body; @@ -4476,9 +4711,9 @@ struct Checker { auto coll_type = (*this).infer_type(collection); auto item_type = TypeNode::make_Auto(); { - const auto& _match_87 = coll_type; - if (std::holds_alternative::Array>(_match_87._data)) { - auto& _v = std::get::Array>(_match_87._data); + const auto& _match_95 = coll_type; + if (std::holds_alternative::Array>(_match_95._data)) { + auto& _v = std::get::Array>(_match_95._data); auto& inner = *_v.inner; item_type = inner; } @@ -4490,8 +4725,8 @@ struct Checker { (*this).check_stmt(body); (*this).pop_scope(); } - else if (std::holds_alternative::Block>(_match_83._data)) { - auto& _v = std::get::Block>(_match_83._data); + else if (std::holds_alternative::Block>(_match_91._data)) { + auto& _v = std::get::Block>(_match_91._data); auto& statements = _v.statements; (*this).push_scope(); for (const auto& st : statements) { @@ -4499,8 +4734,8 @@ struct Checker { } (*this).pop_scope(); } - else if (std::holds_alternative::Function>(_match_83._data)) { - auto& _v = std::get::Function>(_match_83._data); + else if (std::holds_alternative::Function>(_match_91._data)) { + auto& _v = std::get::Function>(_match_91._data); auto& name = _v.name; auto& params = _v.params; auto& return_type = _v.return_type; @@ -4512,23 +4747,23 @@ struct Checker { auto& type_params = _v.type_params; (*this).check_function(name, params, return_type, body); } - else if (std::holds_alternative::Class>(_match_83._data)) { - auto& _v = std::get::Class>(_match_83._data); + else if (std::holds_alternative::Class>(_match_91._data)) { + auto& _v = std::get::Class>(_match_91._data); auto& name = _v.name; auto& body = _v.body; auto& visibility = _v.visibility; (*this).check_class(name, body); } - else if (std::holds_alternative::Struct>(_match_83._data)) { - auto& _v = std::get::Struct>(_match_83._data); + else if (std::holds_alternative::Struct>(_match_91._data)) { + auto& _v = std::get::Struct>(_match_91._data); auto& name = _v.name; auto& body = _v.body; auto& visibility = _v.visibility; auto& struct_tp = _v.type_params; /* pass */ } - else if (std::holds_alternative::Enum>(_match_83._data)) { - auto& _v = std::get::Enum>(_match_83._data); + else if (std::holds_alternative::Enum>(_match_91._data)) { + auto& _v = std::get::Enum>(_match_91._data); auto& name = _v.name; auto& variants = _v.variants; auto& methods = _v.methods; @@ -4536,16 +4771,16 @@ struct Checker { auto& enum_tp = _v.type_params; /* pass */ } - else if (std::holds_alternative::Match>(_match_83._data)) { - auto& _v = std::get::Match>(_match_83._data); + else if (std::holds_alternative::Match>(_match_91._data)) { + auto& _v = std::get::Match>(_match_91._data); auto& expr = _v.expr; auto& arm_patterns = _v.arm_patterns; auto& arm_bodies = _v.arm_bodies; (*this).check_expr(expr); (*this).check_match(expr, arm_patterns, arm_bodies); } - else if (std::holds_alternative::Try>(_match_83._data)) { - auto& _v = std::get::Try>(_match_83._data); + else if (std::holds_alternative::Try>(_match_91._data)) { + auto& _v = std::get::Try>(_match_91._data); auto& try_body = *_v.try_body; auto& catch_body = *_v.catch_body; auto& exception_name = _v.exception_name; @@ -4559,8 +4794,8 @@ struct Checker { (*this).check_stmt(catch_body); (*this).pop_scope(); } - else if (std::holds_alternative::Namespace>(_match_83._data)) { - auto& _v = std::get::Namespace>(_match_83._data); + else if (std::holds_alternative::Namespace>(_match_91._data)) { + auto& _v = std::get::Namespace>(_match_91._data); auto& name = _v.name; auto& body = _v.body; auto& visibility = _v.visibility; @@ -4573,34 +4808,34 @@ struct Checker { } (*this).pop_scope(); } - else if (std::holds_alternative::Import>(_match_83._data)) { - auto& _v = std::get::Import>(_match_83._data); + else if (std::holds_alternative::Import>(_match_91._data)) { + auto& _v = std::get::Import>(_match_91._data); auto& path = _v.path; auto& alias = _v.alias; /* pass */ } - else if (std::holds_alternative::Break>(_match_83._data)) { - auto& _v = std::get::Break>(_match_83._data); + else if (std::holds_alternative::Break>(_match_91._data)) { + auto& _v = std::get::Break>(_match_91._data); auto& keyword = _v.keyword; /* pass */ } - else if (std::holds_alternative::Continue>(_match_83._data)) { - auto& _v = std::get::Continue>(_match_83._data); + else if (std::holds_alternative::Continue>(_match_91._data)) { + auto& _v = std::get::Continue>(_match_91._data); auto& keyword = _v.keyword; /* pass */ } - else if (std::holds_alternative::Pass>(_match_83._data)) { - auto& _v = std::get::Pass>(_match_83._data); + else if (std::holds_alternative::Pass>(_match_91._data)) { + auto& _v = std::get::Pass>(_match_91._data); auto& keyword = _v.keyword; /* pass */ } - else if (std::holds_alternative::CppBlock>(_match_83._data)) { - auto& _v = std::get::CppBlock>(_match_83._data); + else if (std::holds_alternative::CppBlock>(_match_91._data)) { + auto& _v = std::get::CppBlock>(_match_91._data); auto& code = _v.code; /* pass */ } - else if (std::holds_alternative::Extern>(_match_83._data)) { - auto& _v = std::get::Extern>(_match_83._data); + else if (std::holds_alternative::Extern>(_match_91._data)) { + auto& _v = std::get::Extern>(_match_91._data); auto& header = _v.header; auto& import_path = _v.import_path; auto& link_lib = _v.link_lib; @@ -4637,9 +4872,9 @@ struct Checker { (*this).declare(std::string("this"), TypeNode::make_Custom(name.lexeme, {}), std::string("var"), false, false, name); for (const auto& s : body) { { - const auto& _match_88 = s; - if (std::holds_alternative::Function>(_match_88._data)) { - auto& _v = std::get::Function>(_match_88._data); + const auto& _match_96 = s; + if (std::holds_alternative::Function>(_match_96._data)) { + auto& _v = std::get::Function>(_match_96._data); auto& fname = _v.name; auto& params = _v.params; auto& return_type = _v.return_type; @@ -4651,8 +4886,8 @@ struct Checker { auto& fn_tp = _v.type_params; (*this).check_function(fname, params, return_type, fbody); } - else if (std::holds_alternative::Let>(_match_88._data)) { - auto& _v = std::get::Let>(_match_88._data); + else if (std::holds_alternative::Let>(_match_96._data)) { + auto& _v = std::get::Let>(_match_96._data); auto& lname = _v.name; auto& var_type = _v.var_type; auto& initializer = _v.initializer; @@ -4673,46 +4908,46 @@ struct Checker { void check_expr(const Expr& e) { { - const auto& _match_89 = e; - if (_match_89._tag == "None") { + const auto& _match_97 = e; + if (_match_97._tag == "None") { /* pass */ } - else if (std::holds_alternative::Variable>(_match_89._data)) { - auto& _v = std::get::Variable>(_match_89._data); + else if (std::holds_alternative::Variable>(_match_97._data)) { + auto& _v = std::get::Variable>(_match_97._data); auto& name = _v.name; if ((!(*this).resolve(name.lexeme))) { (*this).error(((std::string("Undefined variable '") + (name.lexeme)) + std::string("'")), name); } } - else if (std::holds_alternative::Binary>(_match_89._data)) { - auto& _v = std::get::Binary>(_match_89._data); + else if (std::holds_alternative::Binary>(_match_97._data)) { + auto& _v = std::get::Binary>(_match_97._data); auto& left = *_v.left; auto& op = _v.op; auto& right = *_v.right; (*this).check_expr(left); (*this).check_expr(right); } - else if (std::holds_alternative::Unary>(_match_89._data)) { - auto& _v = std::get::Unary>(_match_89._data); + else if (std::holds_alternative::Unary>(_match_97._data)) { + auto& _v = std::get::Unary>(_match_97._data); auto& op = _v.op; auto& right = *_v.right; (*this).check_expr(right); } - else if (std::holds_alternative::Logical>(_match_89._data)) { - auto& _v = std::get::Logical>(_match_89._data); + else if (std::holds_alternative::Logical>(_match_97._data)) { + auto& _v = std::get::Logical>(_match_97._data); auto& left = *_v.left; auto& op = _v.op; auto& right = *_v.right; (*this).check_expr(left); (*this).check_expr(right); } - else if (std::holds_alternative::Grouping>(_match_89._data)) { - auto& _v = std::get::Grouping>(_match_89._data); + else if (std::holds_alternative::Grouping>(_match_97._data)) { + auto& _v = std::get::Grouping>(_match_97._data); auto& inner = *_v.inner; (*this).check_expr(inner); } - else if (std::holds_alternative::Call>(_match_89._data)) { - auto& _v = std::get::Call>(_match_89._data); + else if (std::holds_alternative::Call>(_match_97._data)) { + auto& _v = std::get::Call>(_match_97._data); auto& callee = *_v.callee; auto& paren = _v.paren; auto& args = _v.args; @@ -4722,8 +4957,8 @@ struct Checker { } (*this).check_call_args(callee, args, paren); } - else if (std::holds_alternative::Assign>(_match_89._data)) { - auto& _v = std::get::Assign>(_match_89._data); + else if (std::holds_alternative::Assign>(_match_97._data)) { + auto& _v = std::get::Assign>(_match_97._data); auto& name = _v.name; auto& value = *_v.value; (*this).check_expr(value); @@ -4737,16 +4972,16 @@ struct Checker { } } } - else if (std::holds_alternative::Index>(_match_89._data)) { - auto& _v = std::get::Index>(_match_89._data); + else if (std::holds_alternative::Index>(_match_97._data)) { + auto& _v = std::get::Index>(_match_97._data); auto& object = *_v.object; auto& bracket = _v.bracket; auto& index = *_v.index; (*this).check_expr(object); (*this).check_expr(index); } - else if (std::holds_alternative::IndexSet>(_match_89._data)) { - auto& _v = std::get::IndexSet>(_match_89._data); + else if (std::holds_alternative::IndexSet>(_match_97._data)) { + auto& _v = std::get::IndexSet>(_match_97._data); auto& object = *_v.object; auto& bracket = _v.bracket; auto& index = *_v.index; @@ -4755,15 +4990,15 @@ struct Checker { (*this).check_expr(index); (*this).check_expr(value); } - else if (std::holds_alternative::Vector>(_match_89._data)) { - auto& _v = std::get::Vector>(_match_89._data); + else if (std::holds_alternative::Vector>(_match_97._data)) { + auto& _v = std::get::Vector>(_match_97._data); auto& elements = _v.elements; for (const auto& el : elements) { (*this).check_expr(el); } } - else if (std::holds_alternative::Map>(_match_89._data)) { - auto& _v = std::get::Map>(_match_89._data); + else if (std::holds_alternative::Map>(_match_97._data)) { + auto& _v = std::get::Map>(_match_97._data); auto& keys = _v.keys; auto& values = _v.values; for (const auto& k : keys) { @@ -4773,28 +5008,28 @@ struct Checker { (*this).check_expr(v); } } - else if (std::holds_alternative::Get>(_match_89._data)) { - auto& _v = std::get::Get>(_match_89._data); + else if (std::holds_alternative::Get>(_match_97._data)) { + auto& _v = std::get::Get>(_match_97._data); auto& object = *_v.object; auto& name = _v.name; (*this).check_expr(object); } - else if (std::holds_alternative::Set>(_match_89._data)) { - auto& _v = std::get::Set>(_match_89._data); + else if (std::holds_alternative::Set>(_match_97._data)) { + auto& _v = std::get::Set>(_match_97._data); auto& object = *_v.object; auto& name = _v.name; auto& value = *_v.value; (*this).check_expr(object); (*this).check_expr(value); } - else if (std::holds_alternative::StaticGet>(_match_89._data)) { - auto& _v = std::get::StaticGet>(_match_89._data); + else if (std::holds_alternative::StaticGet>(_match_97._data)) { + auto& _v = std::get::StaticGet>(_match_97._data); auto& object = *_v.object; auto& name = _v.name; { - const auto& _match_90 = object; - if (std::holds_alternative::Variable>(_match_90._data)) { - auto& _v = std::get::Variable>(_match_90._data); + const auto& _match_98 = object; + if (std::holds_alternative::Variable>(_match_98._data)) { + auto& _v = std::get::Variable>(_match_98._data); auto& tok = _v.name; /* pass */ } @@ -4803,26 +5038,26 @@ struct Checker { } } } - else if (std::holds_alternative::Cast>(_match_89._data)) { - auto& _v = std::get::Cast>(_match_89._data); + else if (std::holds_alternative::Cast>(_match_97._data)) { + auto& _v = std::get::Cast>(_match_97._data); auto& expr = *_v.expr; auto& target_type = _v.target_type; (*this).check_expr(expr); } - else if (std::holds_alternative::Throw>(_match_89._data)) { - auto& _v = std::get::Throw>(_match_89._data); + else if (std::holds_alternative::Throw>(_match_97._data)) { + auto& _v = std::get::Throw>(_match_97._data); auto& expr = *_v.expr; (*this).check_expr(expr); } - else if (std::holds_alternative::Range>(_match_89._data)) { - auto& _v = std::get::Range>(_match_89._data); + else if (std::holds_alternative::Range>(_match_97._data)) { + auto& _v = std::get::Range>(_match_97._data); auto& start = *_v.start; auto& end = *_v.end; (*this).check_expr(start); (*this).check_expr(end); } - else if (std::holds_alternative::Lambda>(_match_89._data)) { - auto& _v = std::get::Lambda>(_match_89._data); + else if (std::holds_alternative::Lambda>(_match_97._data)) { + auto& _v = std::get::Lambda>(_match_97._data); auto& params = _v.params; auto& body = *_v.body; (*this).push_scope(); @@ -4832,8 +5067,8 @@ struct Checker { (*this).check_expr(body); (*this).pop_scope(); } - else if (std::holds_alternative::BlockLambda>(_match_89._data)) { - auto& _v = std::get::BlockLambda>(_match_89._data); + else if (std::holds_alternative::BlockLambda>(_match_97._data)) { + auto& _v = std::get::BlockLambda>(_match_97._data); auto& params = _v.params; auto& body_id = _v.body_id; (*this).push_scope(); @@ -4842,13 +5077,13 @@ struct Checker { } (*this).pop_scope(); } - else if (std::holds_alternative::Own>(_match_89._data)) { - auto& _v = std::get::Own>(_match_89._data); + else if (std::holds_alternative::Own>(_match_97._data)) { + auto& _v = std::get::Own>(_match_97._data); auto& expr = *_v.expr; (*this).check_expr(expr); } - else if (std::holds_alternative::AddressOf>(_match_89._data)) { - auto& _v = std::get::AddressOf>(_match_89._data); + else if (std::holds_alternative::AddressOf>(_match_97._data)) { + auto& _v = std::get::AddressOf>(_match_97._data); auto& expr = *_v.expr; (*this).check_expr(expr); } @@ -4860,9 +5095,9 @@ struct Checker { void check_call_args(const Expr& callee, const std::vector& args, const Token& paren) { { - const auto& _match_91 = callee; - if (std::holds_alternative::Variable>(_match_91._data)) { - auto& _v = std::get::Variable>(_match_91._data); + const auto& _match_99 = callee; + if (std::holds_alternative::Variable>(_match_99._data)) { + auto& _v = std::get::Variable>(_match_99._data); auto& name = _v.name; if ((this->known_funcs.count(name.lexeme) > 0)) { ExternFn fi = this->known_funcs[name.lexeme]; @@ -5271,27 +5506,27 @@ struct Parser { std::string type_to_string(const TypeNode& t) { { - const auto& _match_92 = t; - if (std::holds_alternative::Int>(_match_92._data)) { + const auto& _match_100 = t; + if (std::holds_alternative::Int>(_match_100._data)) { return std::string("int64_t"); } - else if (std::holds_alternative::Float>(_match_92._data)) { + else if (std::holds_alternative::Float>(_match_100._data)) { return std::string("double"); } - else if (std::holds_alternative::Str>(_match_92._data)) { + else if (std::holds_alternative::Str>(_match_100._data)) { return std::string("std::string"); } - else if (std::holds_alternative::Bool>(_match_92._data)) { + else if (std::holds_alternative::Bool>(_match_100._data)) { return std::string("bool"); } - else if (std::holds_alternative::Void>(_match_92._data)) { + else if (std::holds_alternative::Void>(_match_100._data)) { return std::string("void"); } - else if (std::holds_alternative::Auto>(_match_92._data)) { + else if (std::holds_alternative::Auto>(_match_100._data)) { return std::string("auto"); } - else if (std::holds_alternative::Custom>(_match_92._data)) { - auto& _v = std::get::Custom>(_match_92._data); + else if (std::holds_alternative::Custom>(_match_100._data)) { + auto& _v = std::get::Custom>(_match_100._data); auto& name = _v.name; auto& type_args = _v.type_args; if ((static_cast(type_args.size()) > INT64_C(0))) { @@ -5303,31 +5538,31 @@ struct Parser { } return name; } - else if (std::holds_alternative::Array>(_match_92._data)) { - auto& _v = std::get::Array>(_match_92._data); + else if (std::holds_alternative::Array>(_match_100._data)) { + auto& _v = std::get::Array>(_match_100._data); auto& inner = *_v.inner; return ((std::string("std::vector<") + ((*this).type_to_string(inner))) + std::string(">")); } - else if (std::holds_alternative::Int8>(_match_92._data)) { + else if (std::holds_alternative::Int8>(_match_100._data)) { return std::string("int8_t"); } - else if (std::holds_alternative::Int16>(_match_92._data)) { + else if (std::holds_alternative::Int16>(_match_100._data)) { return std::string("int16_t"); } - else if (std::holds_alternative::Int32>(_match_92._data)) { + else if (std::holds_alternative::Int32>(_match_100._data)) { return std::string("int32_t"); } - else if (std::holds_alternative::Float32>(_match_92._data)) { + else if (std::holds_alternative::Float32>(_match_100._data)) { return std::string("float"); } - else if (std::holds_alternative::USize>(_match_92._data)) { + else if (std::holds_alternative::USize>(_match_100._data)) { return std::string("size_t"); } - else if (std::holds_alternative::CString>(_match_92._data)) { + else if (std::holds_alternative::CString>(_match_100._data)) { return std::string("const char*"); } - else if (std::holds_alternative::Ptr>(_match_92._data)) { - auto& _v = std::get::Ptr>(_match_92._data); + else if (std::holds_alternative::Ptr>(_match_100._data)) { + auto& _v = std::get::Ptr>(_match_100._data); auto& inner = *_v.inner; return ((std::string("") + ((*this).type_to_string(inner))) + std::string("*")); } @@ -5346,20 +5581,20 @@ struct Parser { if ((*this).match_any(std::vector{TK_EQUAL})) { Expr value = (*this).assignment(); { - const auto& _match_93 = expr; - if (std::holds_alternative::Variable>(_match_93._data)) { - auto& _v = std::get::Variable>(_match_93._data); + const auto& _match_101 = expr; + if (std::holds_alternative::Variable>(_match_101._data)) { + auto& _v = std::get::Variable>(_match_101._data); auto& name = _v.name; return Expr::make_Assign(name, value); } - else if (std::holds_alternative::Get>(_match_93._data)) { - auto& _v = std::get::Get>(_match_93._data); + else if (std::holds_alternative::Get>(_match_101._data)) { + auto& _v = std::get::Get>(_match_101._data); auto& object = *_v.object; auto& name = _v.name; return Expr::make_Set(object, name, value); } - else if (std::holds_alternative::Index>(_match_93._data)) { - auto& _v = std::get::Index>(_match_93._data); + else if (std::holds_alternative::Index>(_match_101._data)) { + auto& _v = std::get::Index>(_match_101._data); auto& object = *_v.object; auto& bracket = _v.bracket; auto& index = *_v.index; @@ -5388,22 +5623,22 @@ struct Parser { } auto op_token = Token(base_type, base_lexeme, compound_op.line, compound_op.col); { - const auto& _match_94 = expr; - if (std::holds_alternative::Variable>(_match_94._data)) { - auto& _v = std::get::Variable>(_match_94._data); + const auto& _match_102 = expr; + if (std::holds_alternative::Variable>(_match_102._data)) { + auto& _v = std::get::Variable>(_match_102._data); auto& name = _v.name; Expr bin = Expr::make_Binary(Expr::make_Variable(name), op_token, rhs); return Expr::make_Assign(name, bin); } - else if (std::holds_alternative::Get>(_match_94._data)) { - auto& _v = std::get::Get>(_match_94._data); + else if (std::holds_alternative::Get>(_match_102._data)) { + auto& _v = std::get::Get>(_match_102._data); auto& object = *_v.object; auto& name = _v.name; Expr bin = Expr::make_Binary(Expr::make_Get(object, name), op_token, rhs); return Expr::make_Set(object, name, bin); } - else if (std::holds_alternative::Index>(_match_94._data)) { - auto& _v = std::get::Index>(_match_94._data); + else if (std::holds_alternative::Index>(_match_102._data)) { + auto& _v = std::get::Index>(_match_102._data); auto& object = *_v.object; auto& bracket = _v.bracket; auto& index = *_v.index; @@ -5499,9 +5734,9 @@ struct Parser { Expr call() { Expr expr = (*this).primary(); { - const auto& _match_95 = expr; - if (std::holds_alternative::Variable>(_match_95._data)) { - auto& _v = std::get::Variable>(_match_95._data); + const auto& _match_103 = expr; + if (std::holds_alternative::Variable>(_match_103._data)) { + auto& _v = std::get::Variable>(_match_103._data); auto& tok = _v.name; if ((*this).check(TK_LEFT_BRACKET)) { int64_t save_pos = this->current; @@ -5580,9 +5815,9 @@ struct Parser { Expr finish_call(const Expr& callee) { { - const auto& _match_96 = callee; - if (std::holds_alternative::Variable>(_match_96._data)) { - auto& _v = std::get::Variable>(_match_96._data); + const auto& _match_104 = callee; + if (std::holds_alternative::Variable>(_match_104._data)) { + auto& _v = std::get::Variable>(_match_104._data); auto& name = _v.name; if ((name.lexeme == std::string("cast"))) { Expr expr = (*this).expression(); @@ -6092,11 +6327,11 @@ struct Parser { std::vector old_fnames = {}; bool is_unit_type = false; { - const auto& _match_97 = vtype; - if (std::holds_alternative::NullType>(_match_97._data)) { + const auto& _match_105 = vtype; + if (std::holds_alternative::NullType>(_match_105._data)) { is_unit_type = true; } - else if (std::holds_alternative::Void>(_match_97._data)) { + else if (std::holds_alternative::Void>(_match_105._data)) { is_unit_type = true; } else { @@ -6118,6 +6353,44 @@ struct Parser { return Stmt::make_Enum(name, variants, methods, visibility, type_params); } + Stmt extend_declaration(std::string visibility) { + if ((!(*this).match_any(std::vector{TK_IDENTIFIER, TK_VECTOR, TK_HASHMAP, TK_HASHSET, TK_STRING_TYPE}))) { + throw std::runtime_error(std::string("Expect type name after 'extend'.")); + } + auto target = (*this).previous(); + (*this).consume(TK_COLON, std::string("Expect ':' after extend target.")); + (*this).match_any(std::vector{TK_NEWLINE}); + (*this).consume(TK_INDENT, std::string("Expect indentation to start extend body.")); + std::vector methods = {}; + auto old = this->in_class_body; + this->in_class_body = true; + while ((!(*this).check(TK_DEDENT)) && (!(*this).is_at_end())) { + if ((*this).match_any(std::vector{TK_NEWLINE})) { + /* pass */ + } + else { + if ((*this).is_function_start()) { + int64_t method_ct = INT64_C(0); + if ((*this).match_any(std::vector{TK_COMPTIME_STRICT})) { + method_ct = INT64_C(2); + } + else { + if ((*this).match_any(std::vector{TK_COMPTIME})) { + method_ct = INT64_C(1); + } + } + methods.push_back((*this).function_declaration(std::string("public"), false, method_ct)); + } + else { + throw std::runtime_error(std::string("Only method declarations allowed in extend block")); + } + } + } + (*this).consume(TK_DEDENT, std::string("Expect dedent to end extend body.")); + this->in_class_body = old; + return Stmt::make_Extend(target, methods, visibility); + } + Stmt import_statement() { std::vector path = {}; path.push_back((*this).consume(TK_IDENTIFIER, std::string("Expect module name."))); @@ -6187,6 +6460,9 @@ struct Parser { if ((*this).match_any(std::vector{TK_ENUM})) { return (*this).enum_declaration(visibility); } + if ((*this).match_any(std::vector{TK_EXTEND})) { + return (*this).extend_declaration(visibility); + } if ((*this).match_any(std::vector{TK_TRY})) { return (*this).try_statement(); } diff --git a/tests/test_extend.lv b/tests/test_extend.lv new file mode 100644 index 0000000..2c73498 --- /dev/null +++ b/tests/test_extend.lv @@ -0,0 +1,36 @@ +extend vector: + auto fn double_all(): + return __lv_col_map(this, (auto x) => x * 2) + + int fn sum(): + int result = 0 + for x in this: + result += x + return result + + auto fn square(): + return __lv_col_map(this, (auto x) => x * x) + +extend string: + string fn shout(): + return this + "!" + +void fn main(): + vector[int] nums = [1, 2, 3, 4, 5] + + // Test extend vector: double_all + auto d = nums.double_all() + lv_assert(d[0] == 2, "double_all [0]") + lv_assert(d[4] == 10, "double_all [4]") + + // Test extend vector: sum + lv_assert(nums.sum() == 15, "sum") + + // Test extend vector: square + auto sq = nums.square() + lv_assert(sq[0] == 1, "square [0]") + lv_assert(sq[2] == 9, "square [2]") + + // Test extend string: shout + string greeting = "hello" + lv_assert(greeting.shout() == "hello!", "shout") diff --git a/tests/test_std_collections.lv b/tests/test_std_collections.lv index 6d9bd88..c6aa867 100644 --- a/tests/test_std_collections.lv +++ b/tests/test_std_collections.lv @@ -135,7 +135,55 @@ void fn main(): lv_assert(diff.contains(1), "set_difference contains 1") lv_assert(!diff.contains(2), "set_difference not 2") - // --- chaining: map -> filter -> reduce --- + // --- chaining: map -> filter -> reduce (free functions) --- auto result = collections::reduce(collections::filter(collections::map(nums, (int x) => x * x), (int x) => x > 5), (int acc, int x) => acc + x, 0) // nums = [1,2,3,4,5] -> map(x*x) = [1,4,9,16,25] -> filter(>5) = [9,16,25] -> reduce(+) = 50 lv_assert(result == 50, "chained map->filter->reduce") + + // ── Dot-notation via extend ────────────────────────── + auto dot_doubled = nums.map((int x) => x * 2) + lv_assert(dot_doubled[0] == 2, "dot map [0]") + lv_assert(dot_doubled[4] == 10, "dot map [4]") + + auto dot_evens = nums.filter((int x) => x % 2 == 0) + lv_assert(dot_evens.len() == 2, "dot filter length") + lv_assert(dot_evens[0] == 2, "dot filter [0]") + + int dot_sum = nums.reduce((int acc, int x) => acc + x, 0) + lv_assert(dot_sum == 15, "dot reduce") + + int dot_total = 0 + nums.for_each((int x): + dot_total += x + ) + lv_assert(dot_total == 15, "dot for_each") + + auto dot_zipped = names.zip(vals) + lv_assert(dot_zipped.len() == 3, "dot zip length") + lv_assert(dot_zipped[0].first == "a", "dot zip [0].first") + + auto dot_first3 = nums.take(3) + lv_assert(dot_first3.len() == 3, "dot take") + + auto dot_last2 = nums.drop(3) + lv_assert(dot_last2.len() == 2, "dot drop") + + auto dot_indexed = words.enumerate() + lv_assert(dot_indexed[0].first == 0, "dot enumerate [0].first") + lv_assert(dot_indexed[0].second == "hello", "dot enumerate [0].second") + + // dot-notation hashset operations + auto dot_u = sa.union_with(sb) + lv_assert(dot_u.len() == 4, "dot union_with") + + auto dot_inter = sa.intersect(sb) + lv_assert(dot_inter.len() == 2, "dot intersect") + + auto dot_diff = sa.difference(sb) + lv_assert(dot_diff.len() == 1, "dot difference") + + // dot-notation chaining via typed intermediate variables + vector[int] step1 = nums.map((int x) => x * x) + vector[int] step2 = step1.filter((int x) => x > 5) + int chained = step2.reduce((int acc, int x) => acc + x, 0) + lv_assert(chained == 50, "dot chained map->filter->reduce") From 68636eb731fc5a15676e000f0915cc4fd9e285ff Mon Sep 17 00:00:00 2001 From: Raumberg Date: Sun, 15 Feb 2026 22:27:53 +0300 Subject: [PATCH 04/19] docs: 'extend' keyword functionality in README and DOCUMENTATION --- DOCUMENTATION.md | 75 ++++++++++++++++++++++++++++++++++++++++++++++-- README.md | 9 +++++- 2 files changed, 81 insertions(+), 3 deletions(-) diff --git a/DOCUMENTATION.md b/DOCUMENTATION.md index a482eeb..527c66e 100644 --- a/DOCUMENTATION.md +++ b/DOCUMENTATION.md @@ -21,7 +21,8 @@ 17. [Compile-Time Evaluation](#compile-time-evaluation) 18. [FFI (Foreign Function Interface)](#ffi-foreign-function-interface) 19. [Standard Library](#standard-library) -20. [Compiler and Bootstrap](#compiler-and-bootstrap) +20. [Extension Methods](#extension-methods) +21. [Compiler and Bootstrap](#compiler-and-bootstrap) --- @@ -961,6 +962,76 @@ The C++ code has access to all variables in scope. The `return 0` after the `cpp | `random(min, max)` | Random integer in range. | | `random_float()` | Random float in [0, 1). | +### Standard Library Modules + +The standard library is organized into importable modules: + +```lavina +import std::fs // fs::read(), fs::write(), ... +import std::os // os::args(), os::exec(), ... +import std::math // math::PI, math::sqrt(), ... +import std::collections // vector/hashset dot-methods + free functions +``` + +**std::collections** provides higher-order functions both as free functions and as dot-notation methods via `extend`: + +```lavina +import std::collections + +vector[int] nums = [1, 2, 3, 4, 5] + +// Dot-notation (via extend) +auto doubled = nums.map((int x) => x * 2) +auto evens = nums.filter((int x) => x % 2 == 0) +int sum = nums.reduce((int acc, int x) => acc + x, 0) + +// Free function style +auto doubled2 = collections::map(nums, (int x) => x * 2) + +// Generators (free functions only) +auto r = collections::range(0, 10) +``` + +Available dot-methods on vectors: `map`, `filter`, `reduce`, `for_each`, `zip`, `take`, `drop`, `enumerate`. +Available dot-methods on hashsets: `union_with`, `intersect`, `difference`. + +--- + +## Extension Methods + +The `extend` keyword adds methods to existing types: + +```lavina +extend vector: + auto fn double_all(): + return __lv_col_map(this, (auto x) => x * 2) + + int fn sum(): + int result = 0 + for x in this: + result += x + return result + +extend string: + string fn shout(): + return this + "!" +``` + +Usage: + +```lavina +vector[int] nums = [1, 2, 3] +auto d = nums.double_all() // [2, 4, 6] +int s = nums.sum() // 6 + +string greeting = "hello" +print(greeting.shout()) // hello! +``` + +Extend methods work on: `vector`, `hashmap`, `hashset`, `string`, and custom types (structs/enums by name). + +Use `this` inside extend methods to refer to the object. Built-in methods (`.push()`, `.sort()`, etc.) take priority over extend methods. + --- ## Compiler and Bootstrap @@ -1008,7 +1079,7 @@ The compiler maintains C++ snapshots in `stages/`. Each snapshot is a complete s make bootstrap # Build from latest stage, verify fixed point make snapshot # Save new stage after codegen changes make evolve # Handle codegen output format changes (2-pass) -make test # Run test suite (22 tests) +make test # Run test suite (37 tests) ``` **Fixed point verification**: The bootstrap compiles `src/main.lv` twice — once with the previous stage and once with the newly built compiler. The outputs must be identical, proving the compiler correctly compiles itself. diff --git a/README.md b/README.md index aa5bf89..bc6d9c4 100644 --- a/README.md +++ b/README.md @@ -79,7 +79,7 @@ sudo cp -r lib/* /usr/local/lib/ git clone https://github.com/Raumberg/lavina.git cd lavina make bootstrap # compile compiler from saved C++ snapshot -make test # run the test suite (27 tests) +make test # run the test suite (37 tests) make build # optimized binary → build/ make install # install to /usr/local/ (or: make install PREFIX=~/.local) ``` @@ -114,6 +114,7 @@ Lavina ships with a standard library: import std::fs import std::os import std::math +import std::collections void fn main(): // File I/O @@ -126,6 +127,11 @@ void fn main(): // Math print("pi = ${math::PI}") print("sqrt(2) = ${math::sqrt(2.0)}") + + // Collections — dot-notation via extend + vector[int] nums = [1, 2, 3, 4, 5] + auto doubled = nums.map((int x) => x * 2) + auto evens = nums.filter((int x) => x % 2 == 0) ``` ## Features @@ -135,6 +141,7 @@ void fn main(): - **Generics** — `[T, U]` on functions, structs, and enums - **Enums / sum types** with named fields and pattern matching - **Enum methods** — methods defined inside enum bodies +- **Extension methods** — `extend vector:` adds dot-notation methods to built-in types - **Operator overloading** — `Type operator + (params):` - **References and ownership** — `ref`, `ref!`, `own` - **Block lambdas** — `(params):` with indented body From eac5e0c7883d284a0caeac1d4a6ffdf4dc5b950d Mon Sep 17 00:00:00 2001 From: Raumberg Date: Sun, 15 Feb 2026 23:08:50 +0300 Subject: [PATCH 05/19] qol: added multilining support, referenced to PEP documentation (Python multilining) --- src/parser.lv | 12 +++++ src/scanner.lv | 19 +++++++- stages/stage-latest.cpp | 37 ++++++++++++++- tests/test_block_lambdas.lv | 90 +++++++++++++++++++++++++++++++++++++ tests/test_lambdas.lv | 53 ++++++++++++++++++++++ tests/test_multiline.lv | 61 +++++++++++++++++++++++++ 6 files changed, 268 insertions(+), 4 deletions(-) create mode 100644 tests/test_multiline.lv diff --git a/src/parser.lv b/src/parser.lv index 25f2816..a25a68c 100644 --- a/src/parser.lv +++ b/src/parser.lv @@ -460,12 +460,17 @@ class Parser: pass vector[Expr] args = [] + this.skip_formatting() if not this.check(TK_RIGHT_PAREN): this.match_any([TK_REF, TK_REF_MUT]) args.push(this.expression()) + this.skip_formatting() while this.match_any([TK_COMMA]): + this.skip_formatting() this.match_any([TK_REF, TK_REF_MUT]) args.push(this.expression()) + this.skip_formatting() + this.skip_formatting() auto paren = this.consume(TK_RIGHT_PAREN, "Expect ')' after arguments.") return Expr::Call(callee, paren, args) @@ -489,7 +494,9 @@ class Parser: if this.match_any([TK_LEFT_PAREN]): if this.try_parse_lambda(): return this.parse_lambda() + this.skip_formatting() Expr expr = this.expression() + this.skip_formatting() this.consume(TK_RIGHT_PAREN, "Expect ')' after expression.") return Expr::Grouping(expr) if this.match_any([TK_LEFT_BRACKET]): @@ -539,18 +546,23 @@ class Parser: vector[Param] fn parse_param_list(): vector[Param] params = [] + this.skip_formatting() if not this.check(TK_RIGHT_PAREN): bool p_mut = this.match_any([TK_REF_MUT]) bool p_ref = p_mut or this.match_any([TK_REF]) TypeNode param_type = this.parse_type() auto param_name = this.consume(TK_IDENTIFIER, "Expect parameter name.") params.push(Param(param_name, param_type, p_ref, p_mut)) + this.skip_formatting() while this.match_any([TK_COMMA]): + this.skip_formatting() p_mut = this.match_any([TK_REF_MUT]) p_ref = p_mut or this.match_any([TK_REF]) param_type = this.parse_type() param_name = this.consume(TK_IDENTIFIER, "Expect parameter name.") params.push(Param(param_name, param_type, p_ref, p_mut)) + this.skip_formatting() + this.skip_formatting() return params // ── Lambda parsing ──────────────────────────────────── diff --git a/src/scanner.lv b/src/scanner.lv index ceb83f2..f6db235 100644 --- a/src/scanner.lv +++ b/src/scanner.lv @@ -288,6 +288,7 @@ class Scanner: bool at_line_start bool in_string_interp int interp_brace_depth + int bracket_depth constructor(string source): this.source = source @@ -303,6 +304,7 @@ class Scanner: this.at_line_start = true this.in_string_interp = false this.interp_brace_depth = 0 + this.bracket_depth = 0 bool fn is_at_end(): return this.current >= this.source.len() @@ -349,6 +351,11 @@ class Scanner: // ── Indentation handling ──────────────────────────────────── void fn handle_indentation(): + if this.bracket_depth > 0: + while not this.is_at_end() and (this.peek() == " " or this.peek() == "\t"): + this.advance() + this.at_line_start = false + return int indent = 0 while not this.is_at_end() and (this.peek() == " " or this.peek() == "\t"): auto c = this.advance() @@ -489,10 +496,14 @@ class Scanner: elif c == ")": this.add_simple_token(TK_RIGHT_PAREN) elif c == "[": + this.bracket_depth += 1 this.add_simple_token(TK_LEFT_BRACKET) elif c == "]": + if this.bracket_depth > 0: + this.bracket_depth -= 1 this.add_simple_token(TK_RIGHT_BRACKET) elif c == "{": + this.bracket_depth += 1 if this.in_string_interp: this.interp_brace_depth += 1 this.add_simple_token(TK_LEFT_BRACE) @@ -505,6 +516,8 @@ class Scanner: this.add_token(TK_PLUS, "+") this.scan_string() return + if this.bracket_depth > 0: + this.bracket_depth -= 1 this.add_simple_token(TK_RIGHT_BRACE) elif c == ",": this.add_simple_token(TK_COMMA) @@ -584,10 +597,12 @@ class Scanner: elif c == " " or c == "\r" or c == "\t": pass elif c == "\n": - this.add_token(TK_NEWLINE, "\n") + if this.bracket_depth == 0: + this.add_token(TK_NEWLINE, "\n") this.line += 1 this.column = 1 - this.at_line_start = true + if this.bracket_depth == 0: + this.at_line_start = true elif c == "\"": this.scan_string() elif is_digit(c): diff --git a/stages/stage-latest.cpp b/stages/stage-latest.cpp index 6d93c43..7b205e1 100644 --- a/stages/stage-latest.cpp +++ b/stages/stage-latest.cpp @@ -479,6 +479,7 @@ struct Scanner { bool at_line_start; bool in_string_interp; int64_t interp_brace_depth; + int64_t bracket_depth; Scanner(std::string source) : source(source) { @@ -494,6 +495,7 @@ struct Scanner { this->at_line_start = true; this->in_string_interp = false; this->interp_brace_depth = INT64_C(0); + this->bracket_depth = INT64_C(0); } bool is_at_end() { @@ -551,6 +553,13 @@ struct Scanner { } void handle_indentation() { + if ((this->bracket_depth > INT64_C(0))) { + while ((!(*this).is_at_end()) && (((*this).peek() == std::string(" ")) || ((*this).peek() == std::string("\t")))) { + (*this).advance(); + } + this->at_line_start = false; + return; + } int64_t indent = INT64_C(0); while ((!(*this).is_at_end()) && (((*this).peek() == std::string(" ")) || ((*this).peek() == std::string("\t")))) { auto c = (*this).advance(); @@ -737,14 +746,19 @@ struct Scanner { } else { if ((c == std::string("["))) { + this->bracket_depth = (this->bracket_depth + INT64_C(1)); (*this).add_simple_token(TK_LEFT_BRACKET); } else { if ((c == std::string("]"))) { + if ((this->bracket_depth > INT64_C(0))) { + this->bracket_depth = (this->bracket_depth - INT64_C(1)); + } (*this).add_simple_token(TK_RIGHT_BRACKET); } else { if ((c == std::string("{"))) { + this->bracket_depth = (this->bracket_depth + INT64_C(1)); if (this->in_string_interp) { this->interp_brace_depth = (this->interp_brace_depth + INT64_C(1)); } @@ -762,6 +776,9 @@ struct Scanner { return; } } + if ((this->bracket_depth > INT64_C(0))) { + this->bracket_depth = (this->bracket_depth - INT64_C(1)); + } (*this).add_simple_token(TK_RIGHT_BRACE); } else { @@ -910,10 +927,14 @@ struct Scanner { } else { if ((c == std::string("\n"))) { - (*this).add_token(TK_NEWLINE, std::string("\n")); + if ((this->bracket_depth == INT64_C(0))) { + (*this).add_token(TK_NEWLINE, std::string("\n")); + } this->line = (this->line + INT64_C(1)); this->column = INT64_C(1); - this->at_line_start = true; + if ((this->bracket_depth == INT64_C(0))) { + this->at_line_start = true; + } } else { if ((c == std::string("\""))) { @@ -5832,14 +5853,19 @@ struct Parser { } } std::vector args = {}; + (*this).skip_formatting(); if ((!(*this).check(TK_RIGHT_PAREN))) { (*this).match_any(std::vector{TK_REF, TK_REF_MUT}); args.push_back((*this).expression()); + (*this).skip_formatting(); while ((*this).match_any(std::vector{TK_COMMA})) { + (*this).skip_formatting(); (*this).match_any(std::vector{TK_REF, TK_REF_MUT}); args.push_back((*this).expression()); + (*this).skip_formatting(); } } + (*this).skip_formatting(); auto paren = (*this).consume(TK_RIGHT_PAREN, std::string("Expect ')' after arguments.")); return Expr::make_Call(callee, paren, args); } @@ -5873,7 +5899,9 @@ struct Parser { if ((*this).try_parse_lambda()) { return (*this).parse_lambda(); } + (*this).skip_formatting(); Expr expr = (*this).expression(); + (*this).skip_formatting(); (*this).consume(TK_RIGHT_PAREN, std::string("Expect ')' after expression.")); return Expr::make_Grouping(expr); } @@ -5930,20 +5958,25 @@ struct Parser { std::vector parse_param_list() { std::vector params = {}; + (*this).skip_formatting(); if ((!(*this).check(TK_RIGHT_PAREN))) { bool p_mut = (*this).match_any(std::vector{TK_REF_MUT}); bool p_ref = p_mut || (*this).match_any(std::vector{TK_REF}); TypeNode param_type = (*this).parse_type(); auto param_name = (*this).consume(TK_IDENTIFIER, std::string("Expect parameter name.")); params.push_back(Param(param_name, param_type, p_ref, p_mut)); + (*this).skip_formatting(); while ((*this).match_any(std::vector{TK_COMMA})) { + (*this).skip_formatting(); p_mut = (*this).match_any(std::vector{TK_REF_MUT}); p_ref = p_mut || (*this).match_any(std::vector{TK_REF}); param_type = (*this).parse_type(); param_name = (*this).consume(TK_IDENTIFIER, std::string("Expect parameter name.")); params.push_back(Param(param_name, param_type, p_ref, p_mut)); + (*this).skip_formatting(); } } + (*this).skip_formatting(); return params; } diff --git a/tests/test_block_lambdas.lv b/tests/test_block_lambdas.lv index 311cc7f..d7cb821 100644 --- a/tests/test_block_lambdas.lv +++ b/tests/test_block_lambdas.lv @@ -6,6 +6,13 @@ int fn apply(int x, auto f): void fn run_callback(auto f): f() +int fn apply2(int x, int y, auto f): + return f(x, y) + +void fn for_each_int(vector[int] v, auto f): + for x in v: + f(x) + void fn main(): // Block lambda with return auto double_it = (int x): @@ -39,3 +46,86 @@ void fn main(): auto add_base = (int x): return x + base lv_assert(add_base(42) == 142, "block lambda closure") + + // Block lambda mutating closure + int sum = 0 + auto add_to_sum = (int x): + sum += x + add_to_sum(10) + add_to_sum(20) + add_to_sum(30) + lv_assert(sum == 60, "block lambda mutating closure") + + // Block lambda with local variables + auto complex_calc = (int x): + int a = x * 2 + int b = a + 3 + int c = b * b + return c - a + lv_assert(complex_calc(5) == 159, "block lambda local vars") + + // Block lambda with if/else + auto abs_val = (int x): + if x < 0: + return 0 - x + return x + lv_assert(abs_val(-5) == 5, "block lambda if negative") + lv_assert(abs_val(3) == 3, "block lambda if positive") + + // Block lambda with loop + auto factorial = (int n): + int result = 1 + int i = 1 + while i <= n: + result *= i + i += 1 + return result + lv_assert(factorial(5) == 120, "block lambda with loop") + + // Block lambda as argument in multiline call + lv_assert( + apply( + 10, + (int x): + return x * x + 1 + ) == 101, + "block lambda multiline arg" + ) + + // Two-param block lambda as argument + lv_assert(apply2(3, 4, (int x, int y): + int sum = x + y + int product = x * y + return sum + product + ) == 19, "2-param block lambda as arg") + + // Block lambda with for_each pattern + int total = 0 + vector[int] nums = [1, 2, 3, 4, 5] + for_each_int(nums, (int x): + total += x + ) + lv_assert(total == 15, "block lambda for_each") + + // Void block lambda as callback + int flag = 0 + run_callback((): + flag = 42 + ) + lv_assert(flag == 42, "void block lambda callback") + + // Nested block lambda calls + auto outer = (int x): + auto inner = (int y): + return y * 2 + return inner(x) + 1 + lv_assert(outer(5) == 11, "nested block lambdas") + + // Block lambda capturing multiple variables + int a = 10 + int b = 20 + string prefix = "result" + auto multi_capture = (int x): + int val = a + b + x + return val + lv_assert(multi_capture(30) == 60, "block lambda multi capture") diff --git a/tests/test_lambdas.lv b/tests/test_lambdas.lv index c426cc2..a73f395 100644 --- a/tests/test_lambdas.lv +++ b/tests/test_lambdas.lv @@ -4,6 +4,9 @@ int fn apply(auto f, int x): int fn apply2(auto f, int x, int y): return f(x, y) +auto fn transform(auto f, auto g, int x): + return g(f(x)) + void fn main(): // Basic lambda call auto dbl = (int x) => x * 2 @@ -29,3 +32,53 @@ void fn main(): int offset = 10 auto shifted = (int x) => x + offset lv_assert(shifted(5) == 15, "lambda closure") + + // Lambda returning bool + auto is_even = (int x) => x % 2 == 0 + lv_assert(is_even(4), "lambda bool true") + lv_assert(!is_even(3), "lambda bool false") + + // Lambda with string + auto greet_name = (string name) => "hello " + name + lv_assert(greet_name("world") == "hello world", "lambda string concat") + + // Chained lambda calls + auto inc = (int x) => x + 1 + lv_assert(inc(inc(inc(0))) == 3, "chained lambda calls") + + // Lambda composition via higher-order + lv_assert(transform((int x) => x * 2, (int x) => x + 1, 5) == 11, "lambda composition") + + // Lambda in multiline function call + lv_assert( + apply( + (int x) => x * x, + 7 + ) == 49, + "lambda in multiline call" + ) + + // Multiple lambdas in multiline call + lv_assert( + transform( + (int x) => x + 10, + (int x) => x * 2, + 5 + ) == 30, + "multiple lambdas multiline" + ) + + // Lambda inside vector literal + vector[int] nums = [1, 2, 3, 4, 5] + auto double_fn = (int x) => x * 2 + + // Lambda with negative result + auto negate = (int x) => 0 - x + lv_assert(negate(42) == -42, "lambda negate") + + // Lambda capturing mutable variable + int total = 0 + auto accumulate = (int x) => total + x + lv_assert(accumulate(5) == 5, "lambda capture before mutation") + total = 100 + lv_assert(accumulate(5) == 105, "lambda capture after mutation") diff --git a/tests/test_multiline.lv b/tests/test_multiline.lv new file mode 100644 index 0000000..1d47281 --- /dev/null +++ b/tests/test_multiline.lv @@ -0,0 +1,61 @@ +// Multiline function declaration +int fn add_three( + int a, + int b, + int c +): + return a + b + c + +void fn main(): + // Multiline vector literal + vector[int] nums = [ + 1, 2, 3, + 4, 5, 6 + ] + lv_assert(len(nums) == 6, "multiline vector length") + lv_assert(nums[0] == 1, "multiline vector [0]") + lv_assert(nums[5] == 6, "multiline vector [5]") + + // Multiline vector single element per line + vector[string] words = [ + "hello", + "world" + ] + lv_assert(len(words) == 2, "multiline vector strings") + + // Multiline function call + lv_assert( + nums[0] == 1, + "multiline function call" + ) + + // Multiline function declaration + lv_assert(add_three(1, 2, 3) == 6, "multiline fn decl") + lv_assert( + add_three( + 10,20,30 + ) == 60, + "multiline nested call" + ) + + // Multiline hashmap + hashmap[string, int] scores = { + "alice": 100, + "bob": 200, + "charlie": 300 + } + lv_assert(scores["alice"] == 100, "multiline hashmap") + lv_assert(scores.len() == 3, "multiline hashmap len") + + // Nested multiline + vector[vector[int]] matrix = [ + [1, 2, 3], + [4, 5, 6], + [7, 8, 9] + ] + lv_assert(matrix[0][0] == 1, "nested [0][0]") + lv_assert(matrix[2][2] == 9, "nested [2][2]") + + // Single-line still works + vector[int] simple = [1, 2, 3] + lv_assert(len(simple) == 3, "single-line vector") From 99643c7f6ef480f80a16353adb484c3a69cc3ffa Mon Sep 17 00:00:00 2001 From: Raumberg Date: Sun, 15 Feb 2026 23:21:38 +0300 Subject: [PATCH 06/19] src: small refactoring using new multilining support --- src/codegen.lv | 118 ++++++++++---------- src/parser.lv | 48 ++------ stages/stage-latest.cpp | 237 ++++++++++++++-------------------------- 3 files changed, 144 insertions(+), 259 deletions(-) diff --git a/src/codegen.lv b/src/codegen.lv index d100ab9..ca3e607 100644 --- a/src/codegen.lv +++ b/src/codegen.lv @@ -126,6 +126,12 @@ class CppCodegen: tps.push("typename ${tp}") return "${this.indent()}template<${tps.join(", ")}>\n" + void fn push_unique(ref! vector[string] vec, string value): + for ref existing in vec: + if existing == value: + return + vec.push(value) + string fn wrap_convert(string expr, ref TypeNode from, ref TypeNode expected): match from: Str(): @@ -503,74 +509,75 @@ class CppCodegen: return "${func}(${arg_strs.join(", ")})" string fn try_remap_method(ref string obj, ref string method, ref vector[string] args): + string a = args.join(", ") if method == "len": return "static_cast(${obj}.size())" elif method == "is_empty": return "${obj}.empty()" elif method == "contains": - return "lv_contains(${obj}, ${args.join(", ")})" + return "lv_contains(${obj}, ${a})" + elif method == "has": + return "(${obj}.count(${a}) > 0)" + elif method == "push": + return "${obj}.push_back(${a})" + elif method == "pop": + return "lv_pop(${obj})" elif method == "add": - return "${obj}.insert(${args.join(", ")})" + return "${obj}.insert(${a})" + elif method == "remove": + return "lv_remove(${obj}, ${a})" + elif method == "clear": + return "${obj}.clear()" + elif method == "sort": + return "lv_sort(${obj})" + elif method == "reverse": + return "lv_reverse(${obj})" + elif method == "unique": + return "lv_unique(${obj})" + elif method == "flatten": + return "lv_flatten(${obj})" + elif method == "slice": + return "lv_slice(${obj}, ${a})" + elif method == "keys": + return "lv_keys(${obj})" + elif method == "values": + return "lv_values(${obj})" elif method == "upper": return "lv_upper(${obj})" elif method == "lower": return "lv_lower(${obj})" elif method == "trim": return "lv_trim(${obj})" + elif method == "pad_left": + return "lv_pad_left(${obj}, ${a})" + elif method == "pad_right": + return "lv_pad_right(${obj}, ${a})" + elif method == "repeat": + return "lv_repeat(${obj}, ${a})" elif method == "replace": - return "lv_replace(${obj}, ${args.join(", ")})" + return "lv_replace(${obj}, ${a})" + elif method == "starts_with": + return "${obj}.starts_with(${a})" + elif method == "ends_with": + return "${obj}.ends_with(${a})" + elif method == "charAt": + return "std::string(1, ${obj}[${a}])" + elif method == "join": + if args.len() > 0: + return "lv_join(${obj}, ${args[0]})" + return "lv_join(${obj}, std::string(\"\"))" elif method == "split": if args.len() > 0: return "lv_split(${obj}, ${args[0]})" return "lv_split(${obj}, std::string(\" \"))" - elif method == "starts_with": - return "${obj}.starts_with(${args.join(", ")})" - elif method == "ends_with": - return "${obj}.ends_with(${args.join(", ")})" elif method == "indexOf": if args.len() >= 2: return "lv_index_of(${obj}, ${args[0]}, ${args[1]})" - return "lv_index_of(${obj}, ${args.join(", ")})" - elif method == "charAt": - return "std::string(1, ${obj}[${args.join(", ")}])" + return "lv_index_of(${obj}, ${a})" elif method == "substring": if args.len() >= 2: return "${obj}.substr(${args[0]}, (${args[1]}) - (${args[0]}))" - return "${obj}.substr(${args.join(", ")})" - elif method == "push": - return "${obj}.push_back(${args.join(", ")})" - elif method == "pop": - return "lv_pop(${obj})" - elif method == "clear": - return "${obj}.clear()" - elif method == "remove": - return "lv_remove(${obj}, ${args.join(", ")})" - elif method == "join": - if args.len() > 0: - return "lv_join(${obj}, ${args[0]})" - return "lv_join(${obj}, std::string(\"\"))" - elif method == "reverse": - return "lv_reverse(${obj})" - elif method == "keys": - return "lv_keys(${obj})" - elif method == "values": - return "lv_values(${obj})" - elif method == "has": - return "(${obj}.count(${args.join(", ")}) > 0)" - elif method == "repeat": - return "lv_repeat(${obj}, ${args.join(", ")})" - elif method == "pad_left": - return "lv_pad_left(${obj}, ${args.join(", ")})" - elif method == "pad_right": - return "lv_pad_right(${obj}, ${args.join(", ")})" - elif method == "sort": - return "lv_sort(${obj})" - elif method == "unique": - return "lv_unique(${obj})" - elif method == "slice": - return "lv_slice(${obj}, ${args.join(", ")})" - elif method == "flatten": - return "lv_flatten(${obj})" + return "${obj}.substr(${a})" return "" string fn get_type_category(ref Expr object): @@ -775,26 +782,11 @@ class CppCodegen: inc_line = "#include \"${header}\"" else: inc_line = "#include <${header}>" - bool already = false - for ref existing in this.extern_includes: - if existing == inc_line: - already = true - if not already: - this.extern_includes.push(inc_line) + this.push_unique(this.extern_includes, inc_line) if link_lib != "": - bool link_already = false - for ref existing_lib in this.extern_link_libs: - if existing_lib == link_lib: - link_already = true - if not link_already: - this.extern_link_libs.push(link_lib) + this.push_unique(this.extern_link_libs, link_lib) if import_path != "": - bool path_already = false - for ref existing_path in this.extern_import_paths: - if existing_path == import_path: - path_already = true - if not path_already: - this.extern_import_paths.push(import_path) + this.push_unique(this.extern_import_paths, import_path) for ref et in types: if et.lavina_name != et.cpp_name: this.extern_type_names[et.lavina_name] = et.cpp_name diff --git a/src/parser.lv b/src/parser.lv index a25a68c..33c9f10 100644 --- a/src/parser.lv +++ b/src/parser.lv @@ -64,47 +64,13 @@ class Parser: bool fn is_type_at_pos(int pos): auto t = this.peek_at(pos).token_type - if t == TK_INT_TYPE: - return true - if t == TK_FLOAT_TYPE: - return true - if t == TK_STRING_TYPE: - return true - if t == TK_BOOL: - return true - if t == TK_VOID: - return true - if t == TK_AUTO: - return true - if t == TK_DYNAMIC: - return true - if t == TK_VECTOR: - return true - if t == TK_HASHMAP: - return true - if t == TK_HASHSET: - return true - if t == TK_IDENTIFIER: - return true - if t == TK_INT8: - return true - if t == TK_INT16: - return true - if t == TK_INT32: - return true - if t == TK_INT64: - return true - if t == TK_FLOAT32: - return true - if t == TK_FLOAT64: - return true - if t == TK_USIZE: - return true - if t == TK_CSTRING: - return true - if t == TK_PTR: - return true - return false + vector[string] type_tokens = [ + TK_INT_TYPE, TK_FLOAT_TYPE, TK_STRING_TYPE, TK_BOOL, TK_VOID, + TK_AUTO, TK_DYNAMIC, TK_VECTOR, TK_HASHMAP, TK_HASHSET, + TK_IDENTIFIER, TK_INT8, TK_INT16, TK_INT32, TK_INT64, + TK_FLOAT32, TK_FLOAT64, TK_USIZE, TK_CSTRING, TK_PTR + ] + return type_tokens.contains(t) int fn skip_type_tokens(int pos): auto tt = this.peek_at(pos).token_type diff --git a/stages/stage-latest.cpp b/stages/stage-latest.cpp index 7b205e1..0217487 100644 --- a/stages/stage-latest.cpp +++ b/stages/stage-latest.cpp @@ -1370,6 +1370,15 @@ struct CppCodegen { return ((((std::string("") + ((*this).indent())) + std::string("template<")) + (lv_join(tps, std::string(", ")))) + std::string(">\n")); } + void push_unique(std::vector& vec, std::string value) { + for (const auto& existing : vec) { + if ((existing == value)) { + return; + } + } + vec.push_back(value); + } + std::string wrap_convert(std::string expr, const TypeNode& from, const TypeNode& expected) { { const auto& _match_1 = from; @@ -2123,6 +2132,7 @@ struct CppCodegen { } std::string try_remap_method(const std::string& obj, const std::string& method, const std::vector& args) { + std::string a = lv_join(args, std::string(", ")); if ((method == std::string("len"))) { return ((std::string("static_cast(") + (obj)) + std::string(".size())")); } @@ -2132,127 +2142,127 @@ struct CppCodegen { } else { if ((method == std::string("contains"))) { - return ((((std::string("lv_contains(") + (obj)) + std::string(", ")) + (lv_join(args, std::string(", ")))) + std::string(")")); + return ((((std::string("lv_contains(") + (obj)) + std::string(", ")) + (a)) + std::string(")")); } else { - if ((method == std::string("add"))) { - return ((((std::string("") + (obj)) + std::string(".insert(")) + (lv_join(args, std::string(", ")))) + std::string(")")); + if ((method == std::string("has"))) { + return ((((std::string("(") + (obj)) + std::string(".count(")) + (a)) + std::string(") > 0)")); } else { - if ((method == std::string("upper"))) { - return ((std::string("lv_upper(") + (obj)) + std::string(")")); + if ((method == std::string("push"))) { + return ((((std::string("") + (obj)) + std::string(".push_back(")) + (a)) + std::string(")")); } else { - if ((method == std::string("lower"))) { - return ((std::string("lv_lower(") + (obj)) + std::string(")")); + if ((method == std::string("pop"))) { + return ((std::string("lv_pop(") + (obj)) + std::string(")")); } else { - if ((method == std::string("trim"))) { - return ((std::string("lv_trim(") + (obj)) + std::string(")")); + if ((method == std::string("add"))) { + return ((((std::string("") + (obj)) + std::string(".insert(")) + (a)) + std::string(")")); } else { - if ((method == std::string("replace"))) { - return ((((std::string("lv_replace(") + (obj)) + std::string(", ")) + (lv_join(args, std::string(", ")))) + std::string(")")); + if ((method == std::string("remove"))) { + return ((((std::string("lv_remove(") + (obj)) + std::string(", ")) + (a)) + std::string(")")); } else { - if ((method == std::string("split"))) { - if ((static_cast(args.size()) > INT64_C(0))) { - return ((((std::string("lv_split(") + (obj)) + std::string(", ")) + (args[INT64_C(0)])) + std::string(")")); - } - return ((std::string("lv_split(") + (obj)) + std::string(", std::string(\" \"))")); + if ((method == std::string("clear"))) { + return ((std::string("") + (obj)) + std::string(".clear()")); } else { - if ((method == std::string("starts_with"))) { - return ((((std::string("") + (obj)) + std::string(".starts_with(")) + (lv_join(args, std::string(", ")))) + std::string(")")); + if ((method == std::string("sort"))) { + return ((std::string("lv_sort(") + (obj)) + std::string(")")); } else { - if ((method == std::string("ends_with"))) { - return ((((std::string("") + (obj)) + std::string(".ends_with(")) + (lv_join(args, std::string(", ")))) + std::string(")")); + if ((method == std::string("reverse"))) { + return ((std::string("lv_reverse(") + (obj)) + std::string(")")); } else { - if ((method == std::string("indexOf"))) { - if ((static_cast(args.size()) >= INT64_C(2))) { - return ((((((std::string("lv_index_of(") + (obj)) + std::string(", ")) + (args[INT64_C(0)])) + std::string(", ")) + (args[INT64_C(1)])) + std::string(")")); - } - return ((((std::string("lv_index_of(") + (obj)) + std::string(", ")) + (lv_join(args, std::string(", ")))) + std::string(")")); + if ((method == std::string("unique"))) { + return ((std::string("lv_unique(") + (obj)) + std::string(")")); } else { - if ((method == std::string("charAt"))) { - return ((((std::string("std::string(1, ") + (obj)) + std::string("[")) + (lv_join(args, std::string(", ")))) + std::string("])")); + if ((method == std::string("flatten"))) { + return ((std::string("lv_flatten(") + (obj)) + std::string(")")); } else { - if ((method == std::string("substring"))) { - if ((static_cast(args.size()) >= INT64_C(2))) { - return ((((((((std::string("") + (obj)) + std::string(".substr(")) + (args[INT64_C(0)])) + std::string(", (")) + (args[INT64_C(1)])) + std::string(") - (")) + (args[INT64_C(0)])) + std::string("))")); - } - return ((((std::string("") + (obj)) + std::string(".substr(")) + (lv_join(args, std::string(", ")))) + std::string(")")); + if ((method == std::string("slice"))) { + return ((((std::string("lv_slice(") + (obj)) + std::string(", ")) + (a)) + std::string(")")); } else { - if ((method == std::string("push"))) { - return ((((std::string("") + (obj)) + std::string(".push_back(")) + (lv_join(args, std::string(", ")))) + std::string(")")); + if ((method == std::string("keys"))) { + return ((std::string("lv_keys(") + (obj)) + std::string(")")); } else { - if ((method == std::string("pop"))) { - return ((std::string("lv_pop(") + (obj)) + std::string(")")); + if ((method == std::string("values"))) { + return ((std::string("lv_values(") + (obj)) + std::string(")")); } else { - if ((method == std::string("clear"))) { - return ((std::string("") + (obj)) + std::string(".clear()")); + if ((method == std::string("upper"))) { + return ((std::string("lv_upper(") + (obj)) + std::string(")")); } else { - if ((method == std::string("remove"))) { - return ((((std::string("lv_remove(") + (obj)) + std::string(", ")) + (lv_join(args, std::string(", ")))) + std::string(")")); + if ((method == std::string("lower"))) { + return ((std::string("lv_lower(") + (obj)) + std::string(")")); } else { - if ((method == std::string("join"))) { - if ((static_cast(args.size()) > INT64_C(0))) { - return ((((std::string("lv_join(") + (obj)) + std::string(", ")) + (args[INT64_C(0)])) + std::string(")")); - } - return ((std::string("lv_join(") + (obj)) + std::string(", std::string(\"\"))")); + if ((method == std::string("trim"))) { + return ((std::string("lv_trim(") + (obj)) + std::string(")")); } else { - if ((method == std::string("reverse"))) { - return ((std::string("lv_reverse(") + (obj)) + std::string(")")); + if ((method == std::string("pad_left"))) { + return ((((std::string("lv_pad_left(") + (obj)) + std::string(", ")) + (a)) + std::string(")")); } else { - if ((method == std::string("keys"))) { - return ((std::string("lv_keys(") + (obj)) + std::string(")")); + if ((method == std::string("pad_right"))) { + return ((((std::string("lv_pad_right(") + (obj)) + std::string(", ")) + (a)) + std::string(")")); } else { - if ((method == std::string("values"))) { - return ((std::string("lv_values(") + (obj)) + std::string(")")); + if ((method == std::string("repeat"))) { + return ((((std::string("lv_repeat(") + (obj)) + std::string(", ")) + (a)) + std::string(")")); } else { - if ((method == std::string("has"))) { - return ((((std::string("(") + (obj)) + std::string(".count(")) + (lv_join(args, std::string(", ")))) + std::string(") > 0)")); + if ((method == std::string("replace"))) { + return ((((std::string("lv_replace(") + (obj)) + std::string(", ")) + (a)) + std::string(")")); } else { - if ((method == std::string("repeat"))) { - return ((((std::string("lv_repeat(") + (obj)) + std::string(", ")) + (lv_join(args, std::string(", ")))) + std::string(")")); + if ((method == std::string("starts_with"))) { + return ((((std::string("") + (obj)) + std::string(".starts_with(")) + (a)) + std::string(")")); } else { - if ((method == std::string("pad_left"))) { - return ((((std::string("lv_pad_left(") + (obj)) + std::string(", ")) + (lv_join(args, std::string(", ")))) + std::string(")")); + if ((method == std::string("ends_with"))) { + return ((((std::string("") + (obj)) + std::string(".ends_with(")) + (a)) + std::string(")")); } else { - if ((method == std::string("pad_right"))) { - return ((((std::string("lv_pad_right(") + (obj)) + std::string(", ")) + (lv_join(args, std::string(", ")))) + std::string(")")); + if ((method == std::string("charAt"))) { + return ((((std::string("std::string(1, ") + (obj)) + std::string("[")) + (a)) + std::string("])")); } else { - if ((method == std::string("sort"))) { - return ((std::string("lv_sort(") + (obj)) + std::string(")")); + if ((method == std::string("join"))) { + if ((static_cast(args.size()) > INT64_C(0))) { + return ((((std::string("lv_join(") + (obj)) + std::string(", ")) + (args[INT64_C(0)])) + std::string(")")); + } + return ((std::string("lv_join(") + (obj)) + std::string(", std::string(\"\"))")); } else { - if ((method == std::string("unique"))) { - return ((std::string("lv_unique(") + (obj)) + std::string(")")); + if ((method == std::string("split"))) { + if ((static_cast(args.size()) > INT64_C(0))) { + return ((((std::string("lv_split(") + (obj)) + std::string(", ")) + (args[INT64_C(0)])) + std::string(")")); + } + return ((std::string("lv_split(") + (obj)) + std::string(", std::string(\" \"))")); } else { - if ((method == std::string("slice"))) { - return ((((std::string("lv_slice(") + (obj)) + std::string(", ")) + (lv_join(args, std::string(", ")))) + std::string(")")); + if ((method == std::string("indexOf"))) { + if ((static_cast(args.size()) >= INT64_C(2))) { + return ((((((std::string("lv_index_of(") + (obj)) + std::string(", ")) + (args[INT64_C(0)])) + std::string(", ")) + (args[INT64_C(1)])) + std::string(")")); + } + return ((((std::string("lv_index_of(") + (obj)) + std::string(", ")) + (a)) + std::string(")")); } else { - if ((method == std::string("flatten"))) { - return ((std::string("lv_flatten(") + (obj)) + std::string(")")); + if ((method == std::string("substring"))) { + if ((static_cast(args.size()) >= INT64_C(2))) { + return ((((((((std::string("") + (obj)) + std::string(".substr(")) + (args[INT64_C(0)])) + std::string(", (")) + (args[INT64_C(1)])) + std::string(") - (")) + (args[INT64_C(0)])) + std::string("))")); + } + return ((((std::string("") + (obj)) + std::string(".substr(")) + (a)) + std::string(")")); } } } @@ -2701,36 +2711,12 @@ struct CppCodegen { else { inc_line = ((std::string("#include <") + (header)) + std::string(">")); } - bool already = false; - for (const auto& existing : this->extern_includes) { - if ((existing == inc_line)) { - already = true; - } - } - if ((!already)) { - this->extern_includes.push_back(inc_line); - } + (*this).push_unique(this->extern_includes, inc_line); if ((link_lib != std::string(""))) { - bool link_already = false; - for (const auto& existing_lib : this->extern_link_libs) { - if ((existing_lib == link_lib)) { - link_already = true; - } - } - if ((!link_already)) { - this->extern_link_libs.push_back(link_lib); - } + (*this).push_unique(this->extern_link_libs, link_lib); } if ((import_path != std::string(""))) { - bool path_already = false; - for (const auto& existing_path : this->extern_import_paths) { - if ((existing_path == import_path)) { - path_already = true; - } - } - if ((!path_already)) { - this->extern_import_paths.push_back(import_path); - } + (*this).push_unique(this->extern_import_paths, import_path); } for (const auto& et : types) { if ((et.lavina_name != et.cpp_name)) { @@ -5239,67 +5225,8 @@ struct Parser { bool is_type_at_pos(int64_t pos) { auto t = (*this).peek_at(pos).token_type; - if ((t == TK_INT_TYPE)) { - return true; - } - if ((t == TK_FLOAT_TYPE)) { - return true; - } - if ((t == TK_STRING_TYPE)) { - return true; - } - if ((t == TK_BOOL)) { - return true; - } - if ((t == TK_VOID)) { - return true; - } - if ((t == TK_AUTO)) { - return true; - } - if ((t == TK_DYNAMIC)) { - return true; - } - if ((t == TK_VECTOR)) { - return true; - } - if ((t == TK_HASHMAP)) { - return true; - } - if ((t == TK_HASHSET)) { - return true; - } - if ((t == TK_IDENTIFIER)) { - return true; - } - if ((t == TK_INT8)) { - return true; - } - if ((t == TK_INT16)) { - return true; - } - if ((t == TK_INT32)) { - return true; - } - if ((t == TK_INT64)) { - return true; - } - if ((t == TK_FLOAT32)) { - return true; - } - if ((t == TK_FLOAT64)) { - return true; - } - if ((t == TK_USIZE)) { - return true; - } - if ((t == TK_CSTRING)) { - return true; - } - if ((t == TK_PTR)) { - return true; - } - return false; + std::vector type_tokens = std::vector{TK_INT_TYPE, TK_FLOAT_TYPE, TK_STRING_TYPE, TK_BOOL, TK_VOID, TK_AUTO, TK_DYNAMIC, TK_VECTOR, TK_HASHMAP, TK_HASHSET, TK_IDENTIFIER, TK_INT8, TK_INT16, TK_INT32, TK_INT64, TK_FLOAT32, TK_FLOAT64, TK_USIZE, TK_CSTRING, TK_PTR}; + return lv_contains(type_tokens, t); } int64_t skip_type_tokens(int64_t pos) { From 8419622442fba9cb3911b4125ffd00bf1cfe9038 Mon Sep 17 00:00:00 2001 From: Raumberg Date: Sun, 15 Feb 2026 23:27:29 +0300 Subject: [PATCH 07/19] examples: delete wrong class example --- examples/class.lv | 16 ---------------- 1 file changed, 16 deletions(-) delete mode 100644 examples/class.lv diff --git a/examples/class.lv b/examples/class.lv deleted file mode 100644 index 0ccd658..0000000 --- a/examples/class.lv +++ /dev/null @@ -1,16 +0,0 @@ -class Player: - void fn __init__(string name): - this.name = name - this.health = 100 - - void fn heal(int amount): - this.health = this.health + amount - - void fn info(): - print("Player: ${this.name}, HP: ${this.health}" - -void fn main(): - auto p = Player("Alina") - p.info() - p.heal(20) - p.info() From b2386f20f98e9f0fd6dc6f1f631eedf6dcc2ffef Mon Sep 17 00:00:00 2001 From: Raumberg Date: Tue, 17 Feb 2026 13:43:02 +0300 Subject: [PATCH 08/19] std: adding std::net, udp/tcp --- Makefile | 2 +- runtime/lavina.h | 1 + runtime/liblavina/core.h | 7 ++ runtime/liblavina/net.h | 228 +++++++++++++++++++++++++++++++++++++++ runtime/std/net.lv | 89 +++++++++++++++ src/parser.lv | 28 +++-- tests/test_std_net.lv | 112 +++++++++++++++++++ 7 files changed, 457 insertions(+), 10 deletions(-) create mode 100644 runtime/liblavina/net.h create mode 100644 runtime/std/net.lv create mode 100644 tests/test_std_net.lv diff --git a/Makefile b/Makefile index 2c0592a..6ace2c3 100644 --- a/Makefile +++ b/Makefile @@ -56,7 +56,7 @@ evolve: $(BOOTSTRAP_SRC) # ── Run test suite ─────────────────────────────────────────── -SKIP_WINDOWS_TESTS = test_std_fs test_std_os test_stdlib +SKIP_WINDOWS_TESTS = test_std_fs test_std_os test_std_net test_stdlib test: @if [ ! -f /tmp/lavina_next ]; then echo "Run 'make bootstrap' first"; exit 1; fi diff --git a/runtime/lavina.h b/runtime/lavina.h index 76eb837..b5402aa 100644 --- a/runtime/lavina.h +++ b/runtime/lavina.h @@ -11,3 +11,4 @@ #include "liblavina/os.h" #include "liblavina/util.h" #include "liblavina/math.h" +#include "liblavina/net.h" diff --git a/runtime/liblavina/core.h b/runtime/liblavina/core.h index 27c2c3a..301e23a 100644 --- a/runtime/liblavina/core.h +++ b/runtime/liblavina/core.h @@ -24,10 +24,17 @@ #include #include #include +#include +#include +#include +#include #endif #if defined(__APPLE__) #include #endif #if defined(_WIN32) #include +#include +#include +#pragma comment(lib, "ws2_32.lib") #endif diff --git a/runtime/liblavina/net.h b/runtime/liblavina/net.h new file mode 100644 index 0000000..fd125a1 --- /dev/null +++ b/runtime/liblavina/net.h @@ -0,0 +1,228 @@ +#pragma once +#include "core.h" + +// ── Cross-platform socket compatibility ──────────────────────── +#if defined(_WIN32) +using socket_t = SOCKET; +#define LV_INVALID_SOCKET INVALID_SOCKET +#define LV_SOCKET_ERROR SOCKET_ERROR + +struct _LvWinsockInit { + _LvWinsockInit() { + WSADATA wsa; + WSAStartup(MAKEWORD(2, 2), &wsa); + } + ~_LvWinsockInit() { WSACleanup(); } +}; +static _LvWinsockInit _lv_winsock_init; + +inline void _lv_close_socket(socket_t s) { closesocket(s); } +#else +using socket_t = int; +#define LV_INVALID_SOCKET (-1) +#define LV_SOCKET_ERROR (-1) + +inline void _lv_close_socket(socket_t s) { close(s); } +#endif + +// ── TCP ──────────────────────────────────────────────────────── + +// Create a TCP server socket: bind + listen on host:port +// Returns socket fd (as int64_t for Lavina compatibility) +inline int64_t __net_tcp_listen(const std::string& host, int64_t port) { + struct addrinfo hints{}, *res; + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_PASSIVE; + + std::string port_str = std::to_string(port); + const char* node = host.empty() ? nullptr : host.c_str(); + int rc = getaddrinfo(node, port_str.c_str(), &hints, &res); + if (rc != 0) throw std::runtime_error("tcp_listen: getaddrinfo failed: " + std::string(gai_strerror(rc))); + + socket_t fd = socket(res->ai_family, res->ai_socktype, res->ai_protocol); + if (fd == LV_INVALID_SOCKET) { + freeaddrinfo(res); + throw std::runtime_error("tcp_listen: socket() failed"); + } + + int opt = 1; + setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast(&opt), sizeof(opt)); + + if (bind(fd, res->ai_addr, static_cast(res->ai_addrlen)) == LV_SOCKET_ERROR) { + _lv_close_socket(fd); + freeaddrinfo(res); + throw std::runtime_error("tcp_listen: bind() failed on port " + port_str); + } + freeaddrinfo(res); + + if (listen(fd, SOMAXCONN) == LV_SOCKET_ERROR) { + _lv_close_socket(fd); + throw std::runtime_error("tcp_listen: listen() failed"); + } + + return static_cast(fd); +} + +// Accept a connection on a listening socket +// Returns new client socket fd +inline int64_t __net_tcp_accept(int64_t server_fd) { + struct sockaddr_storage addr; + socklen_t addrlen = sizeof(addr); + socket_t client = accept(static_cast(server_fd), + reinterpret_cast(&addr), &addrlen); + if (client == LV_INVALID_SOCKET) { + throw std::runtime_error("tcp_accept: accept() failed"); + } + return static_cast(client); +} + +// Connect to a remote TCP server +// Returns socket fd +inline int64_t __net_tcp_connect(const std::string& host, int64_t port) { + struct addrinfo hints{}, *res; + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + + std::string port_str = std::to_string(port); + int rc = getaddrinfo(host.c_str(), port_str.c_str(), &hints, &res); + if (rc != 0) throw std::runtime_error("tcp_connect: getaddrinfo failed: " + std::string(gai_strerror(rc))); + + socket_t fd = socket(res->ai_family, res->ai_socktype, res->ai_protocol); + if (fd == LV_INVALID_SOCKET) { + freeaddrinfo(res); + throw std::runtime_error("tcp_connect: socket() failed"); + } + + if (connect(fd, res->ai_addr, static_cast(res->ai_addrlen)) == LV_SOCKET_ERROR) { + _lv_close_socket(fd); + freeaddrinfo(res); + throw std::runtime_error("tcp_connect: connect() failed to " + host + ":" + port_str); + } + freeaddrinfo(res); + + return static_cast(fd); +} + +// Send data on a connected TCP socket +// Returns number of bytes sent +inline int64_t __net_tcp_send(int64_t fd, const std::string& data) { + auto sent = send(static_cast(fd), data.c_str(), + static_cast(data.size()), 0); + if (sent == LV_SOCKET_ERROR) { + throw std::runtime_error("tcp_send: send() failed"); + } + return static_cast(sent); +} + +// Receive data from a connected TCP socket +// Returns received data as string +inline std::string __net_tcp_recv(int64_t fd, int64_t max_bytes) { + std::vector buf(static_cast(max_bytes)); + auto received = recv(static_cast(fd), buf.data(), + static_cast(buf.size()), 0); + if (received == LV_SOCKET_ERROR) { + throw std::runtime_error("tcp_recv: recv() failed"); + } + if (received == 0) return ""; + return std::string(buf.data(), static_cast(received)); +} + +// Close a TCP socket +inline void __net_tcp_close(int64_t fd) { + _lv_close_socket(static_cast(fd)); +} + +// ── UDP ──────────────────────────────────────────────────────── + +// Create and bind a UDP socket on host:port +// Returns socket fd +inline int64_t __net_udp_create(const std::string& host, int64_t port) { + struct addrinfo hints{}, *res; + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_DGRAM; + hints.ai_flags = AI_PASSIVE; + + std::string port_str = std::to_string(port); + const char* node = host.empty() ? nullptr : host.c_str(); + int rc = getaddrinfo(node, port_str.c_str(), &hints, &res); + if (rc != 0) throw std::runtime_error("udp_create: getaddrinfo failed: " + std::string(gai_strerror(rc))); + + socket_t fd = socket(res->ai_family, res->ai_socktype, res->ai_protocol); + if (fd == LV_INVALID_SOCKET) { + freeaddrinfo(res); + throw std::runtime_error("udp_create: socket() failed"); + } + + if (bind(fd, res->ai_addr, static_cast(res->ai_addrlen)) == LV_SOCKET_ERROR) { + _lv_close_socket(fd); + freeaddrinfo(res); + throw std::runtime_error("udp_create: bind() failed on port " + port_str); + } + freeaddrinfo(res); + + return static_cast(fd); +} + +// Send data to a specific host:port via UDP +// Returns number of bytes sent +inline int64_t __net_udp_send(int64_t fd, const std::string& data, + const std::string& host, int64_t port) { + struct addrinfo hints{}, *res; + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_DGRAM; + + std::string port_str = std::to_string(port); + int rc = getaddrinfo(host.c_str(), port_str.c_str(), &hints, &res); + if (rc != 0) throw std::runtime_error("udp_send: getaddrinfo failed: " + std::string(gai_strerror(rc))); + + auto sent = sendto(static_cast(fd), data.c_str(), + static_cast(data.size()), 0, + res->ai_addr, static_cast(res->ai_addrlen)); + freeaddrinfo(res); + + if (sent == LV_SOCKET_ERROR) { + throw std::runtime_error("udp_send: sendto() failed"); + } + return static_cast(sent); +} + +// Receive data from a UDP socket +// Returns received data as string +inline std::string __net_udp_recv(int64_t fd, int64_t max_bytes) { + std::vector buf(static_cast(max_bytes)); + struct sockaddr_storage src_addr; + socklen_t addrlen = sizeof(src_addr); + + auto received = recvfrom(static_cast(fd), buf.data(), + static_cast(buf.size()), 0, + reinterpret_cast(&src_addr), &addrlen); + if (received == LV_SOCKET_ERROR) { + throw std::runtime_error("udp_recv: recvfrom() failed"); + } + return std::string(buf.data(), static_cast(received)); +} + +// Close a UDP socket +inline void __net_udp_close(int64_t fd) { + _lv_close_socket(static_cast(fd)); +} + +// ── Utility ──────────────────────────────────────────────────── + +// DNS resolution: hostname -> IP address string +inline std::string __net_resolve(const std::string& hostname) { + struct addrinfo hints{}, *res; + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + + int rc = getaddrinfo(hostname.c_str(), nullptr, &hints, &res); + if (rc != 0) throw std::runtime_error("resolve: getaddrinfo failed: " + std::string(gai_strerror(rc))); + + char ip[INET_ADDRSTRLEN]; + struct sockaddr_in* addr = reinterpret_cast(res->ai_addr); + inet_ntop(AF_INET, &addr->sin_addr, ip, sizeof(ip)); + freeaddrinfo(res); + + return std::string(ip); +} diff --git a/runtime/std/net.lv b/runtime/std/net.lv new file mode 100644 index 0000000..d1fe39e --- /dev/null +++ b/runtime/std/net.lv @@ -0,0 +1,89 @@ +// std::net — TCP/UDP networking + +// ── TCP ────────────────────────────────────────────────────── + +public int fn tcp_listen(string host, int port): + return __net_tcp_listen(host, port) + +public int fn tcp_accept(int server_fd): + return __net_tcp_accept(server_fd) + +public int fn tcp_connect(string host, int port): + return __net_tcp_connect(host, port) + +public int fn tcp_send(int fd, string data): + return __net_tcp_send(fd, data) + +public string fn tcp_recv(int fd, int max_bytes): + return __net_tcp_recv(fd, max_bytes) + +public void fn tcp_close(int fd): + __net_tcp_close(fd) + +// ── UDP ────────────────────────────────────────────────────── + +public int fn udp_create(string host, int port): + return __net_udp_create(host, port) + +public int fn udp_send(int fd, string data, string host, int port): + return __net_udp_send(fd, data, host, port) + +public string fn udp_recv(int fd, int max_bytes): + return __net_udp_recv(fd, max_bytes) + +public void fn udp_close(int fd): + __net_udp_close(fd) + +// ── Utility ────────────────────────────────────────────────── + +public string fn resolve(string hostname): + return __net_resolve(hostname) + +// ── High-level types ───────────────────────────────────────── + +public struct TcpStream: + int fd + + constructor(int fd): + this.fd = fd + + int fn send(string data): + return __net_tcp_send(this.fd, data) + + string fn recv(int max_bytes): + return __net_tcp_recv(this.fd, max_bytes) + + void fn close(): + __net_tcp_close(this.fd) + +public struct TcpListener: + int fd + + constructor(string host, int port): + this.fd = __net_tcp_listen(host, port) + + TcpStream fn accept(): + int client_fd = __net_tcp_accept(this.fd) + return TcpStream(client_fd) + + void fn close(): + __net_tcp_close(this.fd) + +public struct UdpSocket: + int fd + + constructor(string host, int port): + this.fd = __net_udp_create(host, port) + + int fn send(string data, string host, int port): + return __net_udp_send(this.fd, data, host, port) + + string fn recv(int max_bytes): + return __net_udp_recv(this.fd, max_bytes) + + void fn close(): + __net_udp_close(this.fd) + +public TcpStream fn connect(string host, int port): + int fd = __net_tcp_connect(host, port) + return TcpStream(fd) diff --git a/src/parser.lv b/src/parser.lv index 33c9f10..de81c38 100644 --- a/src/parser.lv +++ b/src/parser.lv @@ -94,16 +94,21 @@ class Parser: pos = this.skip_type_tokens(pos) pos += 1 return pos - // Check for Identifier[Type, Type, ...] (generic type args) - if tt == TK_IDENTIFIER and this.peek_at(pos + 1).token_type == TK_LEFT_BRACKET: - // Could be generic type args — skip Ident [ Type , Type ] - int try_pos = pos + 2 - try_pos = this.skip_type_tokens(try_pos) - while this.peek_at(try_pos).token_type == TK_COMMA: - try_pos += 1 + // Skip module-qualified identifiers: module::Type + if tt == TK_IDENTIFIER: + int id_pos = pos + 1 + while this.peek_at(id_pos).token_type == TK_DOUBLE_COLON and this.peek_at(id_pos + 1).token_type == TK_IDENTIFIER: + id_pos += 2 + // Check for generic type args: Ident[Type, Type, ...] + if this.peek_at(id_pos).token_type == TK_LEFT_BRACKET: + int try_pos = id_pos + 1 try_pos = this.skip_type_tokens(try_pos) - if this.peek_at(try_pos).token_type == TK_RIGHT_BRACKET: - return try_pos + 1 + while this.peek_at(try_pos).token_type == TK_COMMA: + try_pos += 1 + try_pos = this.skip_type_tokens(try_pos) + if this.peek_at(try_pos).token_type == TK_RIGHT_BRACKET: + return try_pos + 1 + return id_pos return pos + 1 // Note: checks for comptime at offset 0 because this is also called from @@ -187,6 +192,11 @@ class Parser: t = TypeNode::Ptr(inner) elif this.match_any([TK_IDENTIFIER]): string custom_name = this.previous().lexeme + // Handle module-qualified types: module::Type + while this.check(TK_DOUBLE_COLON): + this.advance() + auto part = this.consume(TK_IDENTIFIER, "Expect type name after '::'.") + custom_name = custom_name + "::" + part.lexeme vector[TypeNode] type_args = [] if this.check(TK_LEFT_BRACKET): // Peek ahead to see if this is a type arg list (Type, Type]) vs index diff --git a/tests/test_std_net.lv b/tests/test_std_net.lv new file mode 100644 index 0000000..eb3e2ec --- /dev/null +++ b/tests/test_std_net.lv @@ -0,0 +1,112 @@ +import std::net + +void fn test_tcp(): + // Start a TCP server on loopback + int server = net::tcp_listen("127.0.0.1", 19876) + lv_assert(server >= 0, "tcp_listen should return valid fd") + + // Connect from client side (completes TCP handshake) + int client = net::tcp_connect("127.0.0.1", 19876) + lv_assert(client >= 0, "tcp_connect should return valid fd") + + // Accept the pending connection on server side + int accepted = net::tcp_accept(server) + lv_assert(accepted >= 0, "tcp_accept should return valid fd") + + // Send data from client -> server + string msg = "hello lavina" + int sent = net::tcp_send(client, msg) + lv_assert(sent == msg.len(), "tcp_send should send all bytes") + + // Receive data on server side + string received = net::tcp_recv(accepted, 1024) + lv_assert(received == msg, "tcp_recv should match sent data") + + // Send response back + string reply = "pong" + net::tcp_send(accepted, reply) + string got_reply = net::tcp_recv(client, 1024) + lv_assert(got_reply == reply, "client should receive reply") + + // Cleanup + net::tcp_close(client) + net::tcp_close(accepted) + net::tcp_close(server) + print("PASS: TCP send/recv") + +void fn test_udp(): + // Create two UDP sockets on loopback + int sock_a = net::udp_create("127.0.0.1", 19877) + lv_assert(sock_a >= 0, "udp_create A should return valid fd") + + int sock_b = net::udp_create("127.0.0.1", 19878) + lv_assert(sock_b >= 0, "udp_create B should return valid fd") + + // Send from A -> B + string msg = "udp hello" + int sent = net::udp_send(sock_a, msg, "127.0.0.1", 19878) + lv_assert(sent == msg.len(), "udp_send should send all bytes") + + // Receive on B + string received = net::udp_recv(sock_b, 1024) + lv_assert(received == msg, "udp_recv should match sent data") + + // Cleanup + net::udp_close(sock_a) + net::udp_close(sock_b) + print("PASS: UDP send/recv") + +void fn test_resolve(): + string ip = net::resolve("localhost") + lv_assert(ip == "127.0.0.1", "resolve localhost should return 127.0.0.1") + print("PASS: DNS resolve") + +void fn test_tcp_stream(): + // High-level TCP: TcpListener + TcpStream (explicit types) + net::TcpListener listener = net::TcpListener("127.0.0.1", 19879) + + // Connect via factory function + net::TcpStream client = net::connect("127.0.0.1", 19879) + + // Accept returns TcpStream + net::TcpStream accepted = listener.accept() + + // Send/recv via methods + string msg = "high-level hello" + client.send(msg) + string received = accepted.recv(1024) + lv_assert(received == msg, "TcpStream recv should match sent data") + + // Reply + accepted.send("hi back") + string reply = client.recv(1024) + lv_assert(reply == "hi back", "TcpStream reply should match") + + // Cleanup + client.close() + accepted.close() + listener.close() + print("PASS: TcpListener/TcpStream") + +void fn test_udp_socket(): + // High-level UDP: UdpSocket (explicit types) + net::UdpSocket sock_a = net::UdpSocket("127.0.0.1", 19880) + net::UdpSocket sock_b = net::UdpSocket("127.0.0.1", 19881) + + // Send from A -> B via methods + string msg = "udp struct hello" + sock_a.send(msg, "127.0.0.1", 19881) + string received = sock_b.recv(1024) + lv_assert(received == msg, "UdpSocket recv should match sent data") + + sock_a.close() + sock_b.close() + print("PASS: UdpSocket") + +void fn main(): + test_tcp() + test_udp() + test_resolve() + test_tcp_stream() + test_udp_socket() + print("All std::net tests passed!") From 27c030754197a7056f475d46a77f27f86e98e4b1 Mon Sep 17 00:00:00 2001 From: Raumberg Date: Tue, 17 Feb 2026 14:06:39 +0300 Subject: [PATCH 09/19] std: threading. omg. plus a huge test --- Makefile | 2 +- runtime/lavina.h | 1 + runtime/liblavina/thread.h | 191 ++++++++++++++ runtime/std/thread.lv | 56 +++++ tests/test_std_thread.lv | 499 +++++++++++++++++++++++++++++++++++++ 5 files changed, 748 insertions(+), 1 deletion(-) create mode 100644 runtime/liblavina/thread.h create mode 100644 runtime/std/thread.lv create mode 100644 tests/test_std_thread.lv diff --git a/Makefile b/Makefile index 6ace2c3..68c520c 100644 --- a/Makefile +++ b/Makefile @@ -56,7 +56,7 @@ evolve: $(BOOTSTRAP_SRC) # ── Run test suite ─────────────────────────────────────────── -SKIP_WINDOWS_TESTS = test_std_fs test_std_os test_std_net test_stdlib +SKIP_WINDOWS_TESTS = test_std_fs test_std_os test_std_net test_std_thread test_stdlib test: @if [ ! -f /tmp/lavina_next ]; then echo "Run 'make bootstrap' first"; exit 1; fi diff --git a/runtime/lavina.h b/runtime/lavina.h index b5402aa..09e91a0 100644 --- a/runtime/lavina.h +++ b/runtime/lavina.h @@ -12,3 +12,4 @@ #include "liblavina/util.h" #include "liblavina/math.h" #include "liblavina/net.h" +#include "liblavina/thread.h" diff --git a/runtime/liblavina/thread.h b/runtime/liblavina/thread.h new file mode 100644 index 0000000..d46c060 --- /dev/null +++ b/runtime/liblavina/thread.h @@ -0,0 +1,191 @@ +#pragma once +#include "core.h" +#include +#include +#include +#include +#include +#include + +// ── Thread registry ──────────────────────────────────────────── + +static std::unordered_map> _lv_threads; +static std::atomic _lv_thread_counter{0}; +static std::mutex _lv_thread_registry_mutex; + +// Spawn a new thread running f(). +// Caller MUST call __thread_wait() before the captured scope exits. +inline int64_t __thread_spawn(auto f) { + int64_t id = ++_lv_thread_counter; + auto task = std::make_shared>(std::move(f)); + std::lock_guard lock(_lv_thread_registry_mutex); + _lv_threads[id] = std::make_unique([task]() { (*task)(); }); + return id; +} + +// Wait for thread to complete. +// Named "wait" because "join" conflicts with try_remap_method in codegen. +inline void __thread_wait(int64_t id) { + std::unique_ptr t; + { + std::lock_guard lock(_lv_thread_registry_mutex); + auto it = _lv_threads.find(id); + if (it == _lv_threads.end()) return; + t = std::move(it->second); + _lv_threads.erase(it); + } + if (t && t->joinable()) { + t->join(); + } +} + +// Detach thread (let it run independently). +inline void __thread_detach(int64_t id) { + std::lock_guard lock(_lv_thread_registry_mutex); + auto it = _lv_threads.find(id); + if (it == _lv_threads.end()) return; + if (it->second && it->second->joinable()) { + it->second->detach(); + } + _lv_threads.erase(it); +} + +// Sleep current thread for ms milliseconds. +inline void __thread_sleep(int64_t ms) { + std::this_thread::sleep_for(std::chrono::milliseconds(ms)); +} + +// Get current thread ID as int64_t. +inline int64_t __thread_current_id() { + return static_cast( + std::hash{}(std::this_thread::get_id()) + ); +} + +// ── Mutex registry ───────────────────────────────────────────── + +static std::unordered_map> _lv_mutexes; +static std::atomic _lv_mutex_counter{0}; +static std::mutex _lv_mutex_registry_mutex; + +inline int64_t __mutex_create() { + int64_t id = ++_lv_mutex_counter; + std::lock_guard lock(_lv_mutex_registry_mutex); + _lv_mutexes[id] = std::make_unique(); + return id; +} + +inline void __mutex_lock(int64_t id) { + std::mutex* m = nullptr; + { + std::lock_guard lock(_lv_mutex_registry_mutex); + auto it = _lv_mutexes.find(id); + if (it == _lv_mutexes.end()) return; + m = it->second.get(); + } + m->lock(); +} + +inline void __mutex_unlock(int64_t id) { + std::mutex* m = nullptr; + { + std::lock_guard lock(_lv_mutex_registry_mutex); + auto it = _lv_mutexes.find(id); + if (it == _lv_mutexes.end()) return; + m = it->second.get(); + } + m->unlock(); +} + +inline void __mutex_destroy(int64_t id) { + std::lock_guard lock(_lv_mutex_registry_mutex); + _lv_mutexes.erase(id); +} + +// ── Thread pool ──────────────────────────────────────────────── + +class _LvThreadPool { +public: + std::vector workers; + std::queue> tasks; + std::mutex queue_mutex; + std::condition_variable condition; + bool stop; + + _LvThreadPool(int64_t n) : stop(false) { + for (int64_t i = 0; i < n; ++i) { + workers.emplace_back([this] { + while (true) { + std::function task; + { + std::unique_lock lock(this->queue_mutex); + this->condition.wait(lock, [this] { + return this->stop || !this->tasks.empty(); + }); + if (this->stop && this->tasks.empty()) return; + task = std::move(this->tasks.front()); + this->tasks.pop(); + } + task(); + } + }); + } + } + + void submit_task(std::function f) { + { + std::lock_guard lock(queue_mutex); + tasks.push(std::move(f)); + } + condition.notify_one(); + } + + void shutdown() { + { + std::lock_guard lock(queue_mutex); + stop = true; + } + condition.notify_all(); + for (auto& w : workers) { + if (w.joinable()) w.join(); + } + } + + ~_LvThreadPool() { + if (!stop) shutdown(); + } +}; + +static std::unordered_map> _lv_pools; +static std::atomic _lv_pool_counter{0}; +static std::mutex _lv_pool_registry_mutex; + +inline int64_t __pool_create(int64_t n) { + int64_t id = ++_lv_pool_counter; + std::lock_guard lock(_lv_pool_registry_mutex); + _lv_pools[id] = std::make_unique<_LvThreadPool>(n); + return id; +} + +inline void __pool_submit(int64_t id, auto f) { + _LvThreadPool* pool = nullptr; + { + std::lock_guard lock(_lv_pool_registry_mutex); + auto it = _lv_pools.find(id); + if (it == _lv_pools.end()) return; + pool = it->second.get(); + } + pool->submit_task(std::function(std::move(f))); +} + +inline void __pool_shutdown(int64_t id) { + std::unique_ptr<_LvThreadPool> pool; + { + std::lock_guard lock(_lv_pool_registry_mutex); + auto it = _lv_pools.find(id); + if (it == _lv_pools.end()) return; + pool = std::move(it->second); + _lv_pools.erase(it); + } + pool->shutdown(); +} diff --git a/runtime/std/thread.lv b/runtime/std/thread.lv new file mode 100644 index 0000000..f0aba8d --- /dev/null +++ b/runtime/std/thread.lv @@ -0,0 +1,56 @@ +// std::thread — threading, mutexes, and thread pools + +// ── Thread ─────────────────────────────────────────────────── + +public struct Thread: + int id + + constructor(int id): + this.id = id + + void fn wait(): + __thread_wait(this.id) + + void fn detach(): + __thread_detach(this.id) + +public Thread fn spawn(auto f): + int id = __thread_spawn(f) + return Thread(id) + +public void fn sleep(int ms): + __thread_sleep(ms) + +public int fn current_id(): + return __thread_current_id() + +// ── Mutex ──────────────────────────────────────────────────── + +public struct Mutex: + int id + + constructor(): + this.id = __mutex_create() + + void fn lock(): + __mutex_lock(this.id) + + void fn unlock(): + __mutex_unlock(this.id) + + void fn destroy(): + __mutex_destroy(this.id) + +// ── Pool ───────────────────────────────────────────────────── + +public struct Pool: + int id + + constructor(int size): + this.id = __pool_create(size) + + void fn submit(auto f): + __pool_submit(this.id, f) + + void fn shutdown(): + __pool_shutdown(this.id) diff --git a/tests/test_std_thread.lv b/tests/test_std_thread.lv new file mode 100644 index 0000000..2dc4064 --- /dev/null +++ b/tests/test_std_thread.lv @@ -0,0 +1,499 @@ +import std::thread + +// ── Basic spawn/wait ───────────────────────────────────────── + +void fn test_spawn_and_wait(): + int result = 0 + thread::Thread t = thread::spawn((): + result = 42 + ) + t.wait() + lv_assert(result == 42, "thread should modify shared state") + print("PASS: spawn and wait") + +void fn test_spawn_multiple_sequential(): + int a = 0 + int b = 0 + int c = 0 + thread::Thread t1 = thread::spawn((): + a = 1 + ) + t1.wait() + thread::Thread t2 = thread::spawn((): + b = a + 1 + ) + t2.wait() + thread::Thread t3 = thread::spawn((): + c = b + 1 + ) + t3.wait() + lv_assert(a == 1, "sequential thread a") + lv_assert(b == 2, "sequential thread b") + lv_assert(c == 3, "sequential thread c") + print("PASS: spawn multiple sequential") + +void fn test_spawn_many_concurrent(): + int count = 0 + thread::Mutex mtx = thread::Mutex() + vector[thread::Thread] threads = [] + for i in 0..20: + thread::Thread t = thread::spawn((): + mtx.lock() + count += 1 + mtx.unlock() + ) + threads.push(t) + for t in threads: + t.wait() + mtx.destroy() + lv_assert(count == 20, "20 concurrent threads should all complete") + print("PASS: spawn many concurrent") + +void fn test_thread_with_computation(): + int result = 0 + thread::Thread t = thread::spawn((): + int sum = 0 + for i in 0..100: + sum += i + result = sum + ) + t.wait() + lv_assert(result == 4950, "thread should compute sum 0..99 = 4950") + print("PASS: thread with computation") + +void fn test_thread_string_capture(): + string msg = "" + thread::Thread t = thread::spawn((): + msg = "hello from thread" + ) + t.wait() + lv_assert(msg == "hello from thread", "thread should modify string") + print("PASS: thread string capture") + +void fn test_thread_vector_capture(): + vector[int] nums = [] + thread::Mutex mtx = thread::Mutex() + thread::Thread t = thread::spawn((): + for i in 0..5: + mtx.lock() + nums.push(i) + mtx.unlock() + ) + t.wait() + mtx.destroy() + lv_assert(nums.len() == 5, "thread should push 5 elements") + lv_assert(nums[0] == 0, "first element should be 0") + lv_assert(nums[4] == 4, "last element should be 4") + print("PASS: thread vector capture") + +// ── Mutex ──────────────────────────────────────────────────── + +void fn test_mutex_basic(): + int counter = 0 + thread::Mutex mtx = thread::Mutex() + thread::Thread t1 = thread::spawn((): + for i in 0..1000: + mtx.lock() + counter += 1 + mtx.unlock() + ) + thread::Thread t2 = thread::spawn((): + for i in 0..1000: + mtx.lock() + counter += 1 + mtx.unlock() + ) + t1.wait() + t2.wait() + mtx.destroy() + lv_assert(counter == 2000, "mutex should protect counter") + print("PASS: mutex basic") + +void fn test_mutex_four_threads(): + int counter = 0 + thread::Mutex mtx = thread::Mutex() + thread::Thread t1 = thread::spawn((): + for i in 0..500: + mtx.lock() + counter += 1 + mtx.unlock() + ) + thread::Thread t2 = thread::spawn((): + for i in 0..500: + mtx.lock() + counter += 1 + mtx.unlock() + ) + thread::Thread t3 = thread::spawn((): + for i in 0..500: + mtx.lock() + counter += 1 + mtx.unlock() + ) + thread::Thread t4 = thread::spawn((): + for i in 0..500: + mtx.lock() + counter += 1 + mtx.unlock() + ) + t1.wait() + t2.wait() + t3.wait() + t4.wait() + mtx.destroy() + lv_assert(counter == 2000, "4 threads x 500 = 2000") + print("PASS: mutex four threads") + +void fn test_mutex_protects_vector(): + vector[int] results = [] + thread::Mutex mtx = thread::Mutex() + thread::Thread t1 = thread::spawn((): + for i in 0..50: + mtx.lock() + results.push(1) + mtx.unlock() + ) + thread::Thread t2 = thread::spawn((): + for i in 0..50: + mtx.lock() + results.push(2) + mtx.unlock() + ) + t1.wait() + t2.wait() + mtx.destroy() + lv_assert(results.len() == 100, "mutex should protect vector, got 100 elements") + // Count 1s and 2s + int ones = 0 + int twos = 0 + for v in results: + if v == 1: + ones += 1 + if v == 2: + twos += 1 + lv_assert(ones == 50, "should have 50 ones") + lv_assert(twos == 50, "should have 50 twos") + print("PASS: mutex protects vector") + +void fn test_multiple_mutexes(): + int a = 0 + int b = 0 + thread::Mutex mtx_a = thread::Mutex() + thread::Mutex mtx_b = thread::Mutex() + thread::Thread t1 = thread::spawn((): + for i in 0..500: + mtx_a.lock() + a += 1 + mtx_a.unlock() + ) + thread::Thread t2 = thread::spawn((): + for i in 0..500: + mtx_b.lock() + b += 1 + mtx_b.unlock() + ) + t1.wait() + t2.wait() + mtx_a.destroy() + mtx_b.destroy() + lv_assert(a == 500, "mutex_a should protect a") + lv_assert(b == 500, "mutex_b should protect b") + print("PASS: multiple mutexes") + +// ── Sleep / timing ─────────────────────────────────────────── + +void fn test_sleep(): + int t1 = __os_clock() + thread::sleep(50) + int t2 = __os_clock() + lv_assert(t2 - t1 >= 30, "sleep should wait at least ~30ms") + print("PASS: sleep") + +void fn test_sleep_in_thread(): + int done = 0 + int t1 = __os_clock() + thread::Thread t = thread::spawn((): + thread::sleep(50) + done = 1 + ) + t.wait() + int t2 = __os_clock() + lv_assert(done == 1, "thread should complete after sleep") + lv_assert(t2 - t1 >= 30, "thread sleep should take time") + print("PASS: sleep in thread") + +// ── Thread identity ────────────────────────────────────────── + +void fn test_current_id(): + int main_id = thread::current_id() + int child_id = 0 + thread::Thread t = thread::spawn((): + child_id = thread::current_id() + ) + t.wait() + lv_assert(main_id != child_id, "different threads should have different IDs") + lv_assert(main_id != 0, "thread ID should not be zero") + print("PASS: current_id") + +void fn test_unique_thread_ids(): + int id1 = 0 + int id2 = 0 + int id3 = 0 + thread::Thread t1 = thread::spawn((): + id1 = thread::current_id() + ) + thread::Thread t2 = thread::spawn((): + id2 = thread::current_id() + ) + thread::Thread t3 = thread::spawn((): + id3 = thread::current_id() + ) + t1.wait() + t2.wait() + t3.wait() + lv_assert(id1 != id2, "thread IDs 1 and 2 should differ") + lv_assert(id2 != id3, "thread IDs 2 and 3 should differ") + lv_assert(id1 != id3, "thread IDs 1 and 3 should differ") + print("PASS: unique thread IDs") + +// ── Thread pool ────────────────────────────────────────────── + +void fn test_pool_basic(): + int count = 0 + thread::Mutex mtx = thread::Mutex() + thread::Pool pool = thread::Pool(4) + for i in 0..10: + pool.submit((): + mtx.lock() + count += 1 + mtx.unlock() + ) + pool.shutdown() + mtx.destroy() + lv_assert(count == 10, "pool should complete all 10 tasks") + print("PASS: pool basic") + +void fn test_pool_many_tasks(): + int count = 0 + thread::Mutex mtx = thread::Mutex() + thread::Pool pool = thread::Pool(4) + for i in 0..100: + pool.submit((): + mtx.lock() + count += 1 + mtx.unlock() + ) + pool.shutdown() + mtx.destroy() + lv_assert(count == 100, "pool should complete all 100 tasks") + print("PASS: pool many tasks") + +void fn test_pool_single_worker(): + int count = 0 + thread::Mutex mtx = thread::Mutex() + thread::Pool pool = thread::Pool(1) + for i in 0..20: + pool.submit((): + mtx.lock() + count += 1 + mtx.unlock() + ) + pool.shutdown() + mtx.destroy() + lv_assert(count == 20, "single-worker pool should complete all tasks") + print("PASS: pool single worker") + +void fn test_pool_heavy_computation(): + int total = 0 + thread::Mutex mtx = thread::Mutex() + thread::Pool pool = thread::Pool(4) + for i in 0..8: + pool.submit((): + // Simulate work + int sum = 0 + for j in 0..1000: + sum += 1 + mtx.lock() + total += sum + mtx.unlock() + ) + pool.shutdown() + mtx.destroy() + lv_assert(total == 8000, "pool heavy computation: 8 tasks x 1000 = 8000") + print("PASS: pool heavy computation") + +void fn test_pool_with_sleep(): + int count = 0 + thread::Mutex mtx = thread::Mutex() + thread::Pool pool = thread::Pool(4) + for i in 0..4: + pool.submit((): + thread::sleep(20) + mtx.lock() + count += 1 + mtx.unlock() + ) + pool.shutdown() + mtx.destroy() + lv_assert(count == 4, "pool tasks with sleep should all complete") + print("PASS: pool with sleep") + +void fn test_pool_many_workers(): + int count = 0 + thread::Mutex mtx = thread::Mutex() + thread::Pool pool = thread::Pool(8) + for i in 0..50: + pool.submit((): + mtx.lock() + count += 1 + mtx.unlock() + ) + pool.shutdown() + mtx.destroy() + lv_assert(count == 50, "8-worker pool should complete 50 tasks") + print("PASS: pool many workers") + +// ── Detach ─────────────────────────────────────────────────── + +void fn test_detach(): + thread::Thread t = thread::spawn((): + thread::sleep(10) + ) + t.detach() + thread::sleep(50) + print("PASS: detach") + +// ── Stress / edge cases ────────────────────────────────────── + +void fn test_rapid_spawn_wait(): + int count = 0 + for i in 0..50: + thread::Thread t = thread::spawn((): + count += 1 + ) + t.wait() + lv_assert(count == 50, "50 rapid spawn/wait cycles") + print("PASS: rapid spawn/wait") + +void fn test_nested_thread_spawn(): + int result = 0 + thread::Thread outer = thread::spawn((): + thread::Thread inner = thread::spawn((): + result = 99 + ) + inner.wait() + ) + outer.wait() + lv_assert(result == 99, "nested thread should set result") + print("PASS: nested thread spawn") + +void fn test_thread_modifies_multiple_vars(): + int x = 0 + int y = 0 + string s = "" + float f = 0.0 + thread::Thread t = thread::spawn((): + x = 10 + y = 20 + s = "done" + f = 3.14 + ) + t.wait() + lv_assert(x == 10, "thread should set x") + lv_assert(y == 20, "thread should set y") + lv_assert(s == "done", "thread should set s") + lv_assert(f > 3.0, "thread should set f") + print("PASS: thread modifies multiple vars") + +void fn test_pool_sequential_reuse(): + // Create, use, shutdown, create again + int count1 = 0 + int count2 = 0 + thread::Mutex mtx = thread::Mutex() + + thread::Pool pool1 = thread::Pool(2) + for i in 0..5: + pool1.submit((): + mtx.lock() + count1 += 1 + mtx.unlock() + ) + pool1.shutdown() + + thread::Pool pool2 = thread::Pool(2) + for i in 0..5: + pool2.submit((): + mtx.lock() + count2 += 1 + mtx.unlock() + ) + pool2.shutdown() + mtx.destroy() + + lv_assert(count1 == 5, "first pool should complete 5 tasks") + lv_assert(count2 == 5, "second pool should complete 5 tasks") + print("PASS: pool sequential reuse") + +void fn test_mutex_contention(): + // High contention: many threads fighting for one mutex + int counter = 0 + thread::Mutex mtx = thread::Mutex() + vector[thread::Thread] threads = [] + for i in 0..10: + thread::Thread t = thread::spawn((): + for j in 0..200: + mtx.lock() + counter += 1 + mtx.unlock() + ) + threads.push(t) + for t in threads: + t.wait() + mtx.destroy() + lv_assert(counter == 2000, "10 threads x 200 = 2000 under contention") + print("PASS: mutex contention") + +// ── Main ───────────────────────────────────────────────────── + +void fn main(): + // Basic spawn/wait + test_spawn_and_wait() + test_spawn_multiple_sequential() + test_spawn_many_concurrent() + test_thread_with_computation() + test_thread_string_capture() + test_thread_vector_capture() + + // Mutex + test_mutex_basic() + test_mutex_four_threads() + test_mutex_protects_vector() + test_multiple_mutexes() + + // Sleep / timing + test_sleep() + test_sleep_in_thread() + + // Thread identity + test_current_id() + test_unique_thread_ids() + + // Thread pool + test_pool_basic() + test_pool_many_tasks() + test_pool_single_worker() + test_pool_heavy_computation() + test_pool_with_sleep() + test_pool_many_workers() + + // Detach + test_detach() + + // Stress / edge cases + test_rapid_spawn_wait() + test_nested_thread_spawn() + test_thread_modifies_multiple_vars() + test_pool_sequential_reuse() + test_mutex_contention() + + println("All std::thread tests passed!") From 736e6cb8a87d20d9e80d207fe57bc6232dd4a669 Mon Sep 17 00:00:00 2001 From: Raumberg Date: Tue, 17 Feb 2026 15:19:41 +0300 Subject: [PATCH 10/19] std, src: 'bytes' type, bytes operations, bytes extension --- editors/nvim/syntax/lavina.vim | 4 +- .../vscode/syntaxes/lavina.tmLanguage.json | 6 +- runtime/lavina.h | 1 + runtime/liblavina/bytes.h | 198 +++++++++++++++++ runtime/std/bytes.lv | 70 ++++++ src/ast.lv | 1 + src/checker.lv | 2 + src/codegen.lv | 4 + src/parser.lv | 23 +- src/scanner.lv | 20 ++ stages/stage-latest.cpp | 185 +++++++++++----- tests/test_bytes.lv | 200 ++++++++++++++++++ tests/test_std_bytes.lv | 149 +++++++++++++ 13 files changed, 801 insertions(+), 62 deletions(-) create mode 100644 runtime/liblavina/bytes.h create mode 100644 runtime/std/bytes.lv create mode 100644 tests/test_bytes.lv create mode 100644 tests/test_std_bytes.lv diff --git a/editors/nvim/syntax/lavina.vim b/editors/nvim/syntax/lavina.vim index 930b7f7..8b1ecd5 100644 --- a/editors/nvim/syntax/lavina.vim +++ b/editors/nvim/syntax/lavina.vim @@ -12,6 +12,7 @@ syn region lavinaInterp start='\${' end='}' contained contains=TOP " Numbers syn match lavinaFloat "\<\d\+\.\d\+\>" +syn match lavinaHex "\<0[xX][0-9a-fA-F]\+\>" syn match lavinaInt "\<\d\+\>" " Control flow @@ -30,7 +31,7 @@ syn keyword lavinaKeyword fn constructor public private static inline const let syn keyword lavinaStorage class struct enum extend " Types -syn keyword lavinaType int float string bool void auto dynamic vector hashmap hashset null int8 int16 int32 int64 float32 float64 usize cstring ptr +syn keyword lavinaType int float string bool void auto dynamic vector hashmap hashset bytes null int8 int16 int32 int64 float32 float64 usize cstring ptr " Constants syn keyword lavinaConstant true false null @@ -55,6 +56,7 @@ hi def link lavinaString String hi def link lavinaEscape SpecialChar hi def link lavinaInterp Special hi def link lavinaFloat Float +hi def link lavinaHex Number hi def link lavinaInt Number hi def link lavinaControl Conditional hi def link lavinaImport Include diff --git a/editors/vscode/syntaxes/lavina.tmLanguage.json b/editors/vscode/syntaxes/lavina.tmLanguage.json index bf5c456..199de56 100644 --- a/editors/vscode/syntaxes/lavina.tmLanguage.json +++ b/editors/vscode/syntaxes/lavina.tmLanguage.json @@ -51,6 +51,10 @@ "name": "constant.numeric.float.lavina", "match": "\\b\\d+\\.\\d+\\b" }, + { + "name": "constant.numeric.hex.lavina", + "match": "\\b0[xX][0-9a-fA-F]+\\b" + }, { "name": "constant.numeric.integer.lavina", "match": "\\b\\d+\\b" @@ -78,7 +82,7 @@ "name": "storage.type.lavina" }, "types": { - "match": "\\b(int|float|string|bool|void|auto|dynamic|vector|hashmap|hashset|null|int8|int16|int32|int64|float32|float64|usize|cstring|ptr)\\b", + "match": "\\b(int|float|string|bool|void|auto|dynamic|vector|hashmap|hashset|bytes|null|int8|int16|int32|int64|float32|float64|usize|cstring|ptr)\\b", "name": "support.type.lavina" }, "constants": { diff --git a/runtime/lavina.h b/runtime/lavina.h index 09e91a0..86ef361 100644 --- a/runtime/lavina.h +++ b/runtime/lavina.h @@ -13,3 +13,4 @@ #include "liblavina/math.h" #include "liblavina/net.h" #include "liblavina/thread.h" +#include "liblavina/bytes.h" diff --git a/runtime/liblavina/bytes.h b/runtime/liblavina/bytes.h new file mode 100644 index 0000000..389e5d7 --- /dev/null +++ b/runtime/liblavina/bytes.h @@ -0,0 +1,198 @@ +#pragma once +#include "core.h" + +using lv_bytes = std::vector; + +// ── Construction ──────────────────────────────────────────── + +inline lv_bytes __bytes_create(int64_t size) { + return lv_bytes(static_cast(size), 0); +} + +inline lv_bytes __bytes_from_string(const std::string& s) { + return lv_bytes(s.begin(), s.end()); +} + +inline std::string __bytes_to_string(const lv_bytes& b) { + return std::string(b.begin(), b.end()); +} + +// ── Hex encoding/decoding ─────────────────────────────────── + +inline std::string __bytes_to_hex(const lv_bytes& b) { + static const char hex_chars[] = "0123456789abcdef"; + std::string result; + result.reserve(b.size() * 2); + for (uint8_t byte : b) { + result.push_back(hex_chars[byte >> 4]); + result.push_back(hex_chars[byte & 0x0F]); + } + return result; +} + +inline lv_bytes __bytes_from_hex(const std::string& hex) { + if (hex.size() % 2 != 0) { + throw std::runtime_error("from_hex: odd-length hex string"); + } + lv_bytes result; + result.reserve(hex.size() / 2); + for (size_t i = 0; i < hex.size(); i += 2) { + uint8_t hi = 0, lo = 0; + char c1 = hex[i], c2 = hex[i + 1]; + if (c1 >= '0' && c1 <= '9') hi = c1 - '0'; + else if (c1 >= 'a' && c1 <= 'f') hi = c1 - 'a' + 10; + else if (c1 >= 'A' && c1 <= 'F') hi = c1 - 'A' + 10; + else throw std::runtime_error("from_hex: invalid hex char"); + if (c2 >= '0' && c2 <= '9') lo = c2 - '0'; + else if (c2 >= 'a' && c2 <= 'f') lo = c2 - 'a' + 10; + else if (c2 >= 'A' && c2 <= 'F') lo = c2 - 'A' + 10; + else throw std::runtime_error("from_hex: invalid hex char"); + result.push_back((hi << 4) | lo); + } + return result; +} + +// ── Byte-level access ─────────────────────────────────────── + +inline int64_t __bytes_get(const lv_bytes& b, int64_t idx) { + if (idx < 0 || idx >= static_cast(b.size())) { + throw std::runtime_error("bytes: index out of range"); + } + return static_cast(b[static_cast(idx)]); +} + +inline void __bytes_set(lv_bytes& b, int64_t idx, int64_t val) { + if (idx < 0 || idx >= static_cast(b.size())) { + throw std::runtime_error("bytes: index out of range"); + } + b[static_cast(idx)] = static_cast(val & 0xFF); +} + +// ── Multi-byte read/write (big-endian) ────────────────────── + +inline int64_t __bytes_read_u16_be(const lv_bytes& b, int64_t offset) { + size_t o = static_cast(offset); + if (o + 2 > b.size()) throw std::runtime_error("bytes: read_u16_be out of range"); + return static_cast((static_cast(b[o]) << 8) | b[o + 1]); +} + +inline void __bytes_write_u16_be(lv_bytes& b, int64_t offset, int64_t val) { + size_t o = static_cast(offset); + if (o + 2 > b.size()) throw std::runtime_error("bytes: write_u16_be out of range"); + uint16_t v = static_cast(val); + b[o] = static_cast(v >> 8); + b[o + 1] = static_cast(v & 0xFF); +} + +inline int64_t __bytes_read_u32_be(const lv_bytes& b, int64_t offset) { + size_t o = static_cast(offset); + if (o + 4 > b.size()) throw std::runtime_error("bytes: read_u32_be out of range"); + return static_cast( + (static_cast(b[o]) << 24) | + (static_cast(b[o + 1]) << 16) | + (static_cast(b[o + 2]) << 8) | + b[o + 3] + ); +} + +inline void __bytes_write_u32_be(lv_bytes& b, int64_t offset, int64_t val) { + size_t o = static_cast(offset); + if (o + 4 > b.size()) throw std::runtime_error("bytes: write_u32_be out of range"); + uint32_t v = static_cast(val); + b[o] = static_cast(v >> 24); + b[o + 1] = static_cast((v >> 16) & 0xFF); + b[o + 2] = static_cast((v >> 8) & 0xFF); + b[o + 3] = static_cast(v & 0xFF); +} + +// ── Multi-byte read/write (little-endian) ─────────────────── + +inline int64_t __bytes_read_u16_le(const lv_bytes& b, int64_t offset) { + size_t o = static_cast(offset); + if (o + 2 > b.size()) throw std::runtime_error("bytes: read_u16_le out of range"); + return static_cast(b[o] | (static_cast(b[o + 1]) << 8)); +} + +inline void __bytes_write_u16_le(lv_bytes& b, int64_t offset, int64_t val) { + size_t o = static_cast(offset); + if (o + 2 > b.size()) throw std::runtime_error("bytes: write_u16_le out of range"); + uint16_t v = static_cast(val); + b[o] = static_cast(v & 0xFF); + b[o + 1] = static_cast(v >> 8); +} + +inline int64_t __bytes_read_u32_le(const lv_bytes& b, int64_t offset) { + size_t o = static_cast(offset); + if (o + 4 > b.size()) throw std::runtime_error("bytes: read_u32_le out of range"); + return static_cast( + b[o] | + (static_cast(b[o + 1]) << 8) | + (static_cast(b[o + 2]) << 16) | + (static_cast(b[o + 3]) << 24) + ); +} + +inline void __bytes_write_u32_le(lv_bytes& b, int64_t offset, int64_t val) { + size_t o = static_cast(offset); + if (o + 4 > b.size()) throw std::runtime_error("bytes: write_u32_le out of range"); + uint32_t v = static_cast(val); + b[o] = static_cast(v & 0xFF); + b[o + 1] = static_cast((v >> 8) & 0xFF); + b[o + 2] = static_cast((v >> 16) & 0xFF); + b[o + 3] = static_cast(v >> 24); +} + +// ── Slice / Concat / Compare ──────────────────────────────── + +inline lv_bytes __bytes_slice(const lv_bytes& b, int64_t start, int64_t end) { + if (start < 0) start = 0; + if (end > static_cast(b.size())) end = b.size(); + if (start >= end) return {}; + return lv_bytes(b.begin() + start, b.begin() + end); +} + +inline lv_bytes __bytes_concat(const lv_bytes& a, const lv_bytes& b) { + lv_bytes result; + result.reserve(a.size() + b.size()); + result.insert(result.end(), a.begin(), a.end()); + result.insert(result.end(), b.begin(), b.end()); + return result; +} + +inline bool __bytes_equals(const lv_bytes& a, const lv_bytes& b) { + return a == b; +} + +inline void __bytes_fill(lv_bytes& b, int64_t val) { + std::fill(b.begin(), b.end(), static_cast(val & 0xFF)); +} + +// ── Print support ─────────────────────────────────────────── + +inline std::string to_string(const lv_bytes& b) { + return "bytes[" + std::to_string(b.size()) + "]"; +} + +inline void print(const lv_bytes& b) { + std::cout << "bytes[" << b.size() << "]("; + static const char hx[] = "0123456789abcdef"; + for (size_t i = 0; i < b.size() && i < 32; i++) { + if (i > 0) std::cout << " "; + std::cout << hx[b[i] >> 4] << hx[b[i] & 0x0F]; + } + if (b.size() > 32) std::cout << "..."; + std::cout << ")"; +} + +inline void println(const lv_bytes& b) { + print(b); + std::cout << std::endl; +} + +inline std::string operator+(const std::string& s, const lv_bytes& b) { + return s + to_string(b); +} + +inline std::string operator+(const lv_bytes& b, const std::string& s) { + return to_string(b) + s; +} diff --git a/runtime/std/bytes.lv b/runtime/std/bytes.lv new file mode 100644 index 0000000..b2bab53 --- /dev/null +++ b/runtime/std/bytes.lv @@ -0,0 +1,70 @@ +// std::bytes — byte buffer operations + +// ── Extension methods ─────────────────────────────────────── +// Methods available directly on bytes values: buf.get(0), buf.set(0, 0xFF), etc. +// Note: .len(), .push(), .clear(), .slice() already work via try_remap_method. + +extend bytes: + int fn get(int idx): + return __bytes_get(this, idx) + + void fn set(int idx, int val): + __bytes_set(this, idx, val) + + string fn to_string(): + return __bytes_to_string(this) + + string fn to_hex(): + return __bytes_to_hex(this) + + void fn fill(int val): + __bytes_fill(this, val) + + bool fn equals(bytes other): + return __bytes_equals(this, other) + + bytes fn concat(bytes other): + return __bytes_concat(this, other) + + // Big-endian + int fn read_u16_be(int offset): + return __bytes_read_u16_be(this, offset) + + void fn write_u16_be(int offset, int val): + __bytes_write_u16_be(this, offset, val) + + int fn read_u32_be(int offset): + return __bytes_read_u32_be(this, offset) + + void fn write_u32_be(int offset, int val): + __bytes_write_u32_be(this, offset, val) + + // Little-endian + int fn read_u16_le(int offset): + return __bytes_read_u16_le(this, offset) + + void fn write_u16_le(int offset, int val): + __bytes_write_u16_le(this, offset, val) + + int fn read_u32_le(int offset): + return __bytes_read_u32_le(this, offset) + + void fn write_u32_le(int offset, int val): + __bytes_write_u32_le(this, offset, val) + +// ── Free functions (construction + utilities) ─────────────── + +public bytes fn create(int size): + return __bytes_create(size) + +public bytes fn from_string(string s): + return __bytes_from_string(s) + +public bytes fn from_hex(string hex): + return __bytes_from_hex(hex) + +public bytes fn concat(bytes a, bytes b): + return __bytes_concat(a, b) + +public bool fn equals(bytes a, bytes b): + return __bytes_equals(a, b) diff --git a/src/ast.lv b/src/ast.lv index 2a92408..ea82921 100644 --- a/src/ast.lv +++ b/src/ast.lv @@ -28,6 +28,7 @@ enum TypeNode: USize CString Ptr(TypeNode inner) + Bytes // ── Helper structs ─────────────────────────────────────────── diff --git a/src/checker.lv b/src/checker.lv index 539f281..aa18ee5 100644 --- a/src/checker.lv +++ b/src/checker.lv @@ -300,6 +300,8 @@ class Checker: return "cstring" Ptr(inner): return "ptr[${this.type_name(inner)}]" + Bytes(): + return "bytes" _: return "unknown" diff --git a/src/codegen.lv b/src/codegen.lv index ca3e607..1925f4c 100644 --- a/src/codegen.lv +++ b/src/codegen.lv @@ -271,6 +271,8 @@ class CppCodegen: return "const char*" Ptr(inner): return "${this.emit_type(inner)}*" + Bytes(): + return "std::vector" _: return "auto" @@ -594,6 +596,8 @@ class CppCodegen: return "hashset" Str(): return "string" + Bytes(): + return "bytes" Custom(name, type_args): return name _: diff --git a/src/parser.lv b/src/parser.lv index de81c38..510116a 100644 --- a/src/parser.lv +++ b/src/parser.lv @@ -68,7 +68,7 @@ class Parser: TK_INT_TYPE, TK_FLOAT_TYPE, TK_STRING_TYPE, TK_BOOL, TK_VOID, TK_AUTO, TK_DYNAMIC, TK_VECTOR, TK_HASHMAP, TK_HASHSET, TK_IDENTIFIER, TK_INT8, TK_INT16, TK_INT32, TK_INT64, - TK_FLOAT32, TK_FLOAT64, TK_USIZE, TK_CSTRING, TK_PTR + TK_FLOAT32, TK_FLOAT64, TK_USIZE, TK_CSTRING, TK_PTR, TK_BYTES ] return type_tokens.contains(t) @@ -185,6 +185,8 @@ class Parser: t = TypeNode::USize() elif this.match_any([TK_CSTRING]): t = TypeNode::CString() + elif this.match_any([TK_BYTES]): + t = TypeNode::Bytes() elif this.match_any([TK_PTR]): this.consume(TK_LEFT_BRACKET, "Expect '[' after 'ptr'.") TypeNode inner = this.parse_type() @@ -263,6 +265,8 @@ class Parser: return "const char*" Ptr(inner): return "${this.type_to_string(inner)}*" + Bytes(): + return "std::vector" _: return "auto" @@ -465,6 +469,9 @@ class Parser: return Expr::Literal("string", this.previous().lexeme) if this.match_any([TK_IDENTIFIER]): return Expr::Variable(this.previous()) + if this.check(TK_BYTES) and this.peek_at(1).token_type == TK_DOUBLE_COLON: + auto tok = this.advance() + return Expr::Variable(Token(TK_IDENTIFIER, tok.lexeme, tok.line, tok.col)) if this.match_any([TK_THIS]): return Expr::This(this.previous()) if this.match_any([TK_LEFT_PAREN]): @@ -874,7 +881,7 @@ class Parser: return Stmt::Enum(name, variants, methods, visibility, type_params) Stmt fn extend_declaration(string visibility): - if not this.match_any([TK_IDENTIFIER, TK_VECTOR, TK_HASHMAP, TK_HASHSET, TK_STRING_TYPE]): + if not this.match_any([TK_IDENTIFIER, TK_VECTOR, TK_HASHMAP, TK_HASHSET, TK_STRING_TYPE, TK_BYTES]): throw "Expect type name after 'extend'." auto target = this.previous() this.consume(TK_COLON, "Expect ':' after extend target.") @@ -899,11 +906,19 @@ class Parser: this.in_class_body = old return Stmt::Extend(target, methods, visibility) + Token fn consume_module_name(): + if this.match_any([TK_IDENTIFIER]): + return this.previous() + if this.match_any([TK_BYTES]): + auto tok = this.previous() + return Token(TK_IDENTIFIER, tok.lexeme, tok.line, tok.col) + throw "Expect module name. Got ${this.peek().token_type} at ${this.peek().line}:${this.peek().col}" + Stmt fn import_statement(): vector[Token] path = [] - path.push(this.consume(TK_IDENTIFIER, "Expect module name.")) + path.push(this.consume_module_name()) while this.match_any([TK_DOUBLE_COLON]): - path.push(this.consume(TK_IDENTIFIER, "Expect module name.")) + path.push(this.consume_module_name()) string alias = "" if this.match_any([TK_AS]): diff --git a/src/scanner.lv b/src/scanner.lv index f6db235..bfbdad9 100644 --- a/src/scanner.lv +++ b/src/scanner.lv @@ -100,6 +100,7 @@ const string TK_FLOAT64 = "Float64" const string TK_USIZE = "USize" const string TK_CSTRING = "CString" const string TK_PTR = "Ptr" +const string TK_BYTES = "Bytes" const string TK_CPP = "Cpp" const string TK_AMPERSAND = "Ampersand" const string TK_EXTERN = "Extern" @@ -237,6 +238,8 @@ string fn lookup_keyword(ref string w): return TK_CSTRING elif w == "ptr": return TK_PTR + elif w == "bytes": + return TK_BYTES elif w == "cpp": return TK_CPP elif w == "extern": @@ -265,6 +268,15 @@ bool fn is_digit(auto c): return true return false +bool fn is_hex_digit(auto c): + if is_digit(c): + return true + if c >= "a" and c <= "f": + return true + if c >= "A" and c <= "F": + return true + return false + bool fn is_alnum(auto c): if is_alpha(c): return true @@ -412,6 +424,14 @@ class Scanner: this.add_token(TK_STRING, value) void fn scan_number(): + // Check for hex literal: 0x... + if this.source.charAt(this.start) == "0" and (this.peek() == "x" or this.peek() == "X"): + this.advance() + while is_hex_digit(this.peek()): + this.advance() + this.add_simple_token(TK_INT) + return + while is_digit(this.peek()): this.advance() diff --git a/stages/stage-latest.cpp b/stages/stage-latest.cpp index 0217487..6ca8a59 100644 --- a/stages/stage-latest.cpp +++ b/stages/stage-latest.cpp @@ -95,6 +95,7 @@ const std::string TK_FLOAT64 = std::string("Float64"); const std::string TK_USIZE = std::string("USize"); const std::string TK_CSTRING = std::string("CString"); const std::string TK_PTR = std::string("Ptr"); +const std::string TK_BYTES = std::string("Bytes"); const std::string TK_CPP = std::string("Cpp"); const std::string TK_AMPERSAND = std::string("Ampersand"); const std::string TK_EXTERN = std::string("Extern"); @@ -349,24 +350,29 @@ std::string lookup_keyword(const std::string& w) { return TK_PTR; } else { - if ((w == std::string("cpp"))) { - return TK_CPP; + if ((w == std::string("bytes"))) { + return TK_BYTES; } else { - if ((w == std::string("extern"))) { - return TK_EXTERN; + if ((w == std::string("cpp"))) { + return TK_CPP; } else { - if ((w == std::string("link"))) { - return TK_LINK; + if ((w == std::string("extern"))) { + return TK_EXTERN; } else { - if ((w == std::string("operator"))) { - return TK_OPERATOR; + if ((w == std::string("link"))) { + return TK_LINK; } else { - if ((w == std::string("extend"))) { - return TK_EXTEND; + if ((w == std::string("operator"))) { + return TK_OPERATOR; + } + else { + if ((w == std::string("extend"))) { + return TK_EXTEND; + } } } } @@ -455,6 +461,19 @@ bool is_digit(auto c) { return false; } +bool is_hex_digit(auto c) { + if (is_digit(c)) { + return true; + } + if ((c >= std::string("a")) && (c <= std::string("f"))) { + return true; + } + if ((c >= std::string("A")) && (c <= std::string("F"))) { + return true; + } + return false; +} + bool is_alnum(auto c) { if (is_alpha(c)) { return true; @@ -630,6 +649,14 @@ struct Scanner { } void scan_number() { + if ((std::string(1, this->source[this->start]) == std::string("0")) && (((*this).peek() == std::string("x")) || ((*this).peek() == std::string("X")))) { + (*this).advance(); + while (is_hex_digit((*this).peek())) { + (*this).advance(); + } + (*this).add_simple_token(TK_INT); + return; + } while (is_digit((*this).peek())) { (*this).advance(); } @@ -1019,9 +1046,10 @@ struct TypeNode { struct USize {}; struct CString {}; struct Ptr { std::shared_ptr inner; }; + struct Bytes {}; std::string _tag; - std::variant _data; + std::variant _data; static TypeNode make_None() { return {"None", None{}}; } static TypeNode make_Int() { return {"Int", Int{}}; } @@ -1044,6 +1072,7 @@ struct TypeNode { static TypeNode make_USize() { return {"USize", USize{}}; } static TypeNode make_CString() { return {"CString", CString{}}; } static TypeNode make_Ptr(TypeNode inner) { return {"Ptr", Ptr{std::make_shared(std::move(inner))}}; } + static TypeNode make_Bytes() { return {"Bytes", Bytes{}}; } std::string operator[](const std::string& key) const { if (key == "_tag") return _tag; @@ -1629,6 +1658,9 @@ struct CppCodegen { auto& inner = *_v.inner; return ((std::string("") + ((*this).emit_type(inner))) + std::string("*")); } + else if (std::holds_alternative::Bytes>(_match_14._data)) { + return std::string("std::vector"); + } else { return std::string("auto"); } @@ -2325,6 +2357,9 @@ struct CppCodegen { else if (std::holds_alternative::Str>(_match_25._data)) { return std::string("string"); } + else if (std::holds_alternative::Bytes>(_match_25._data)) { + return std::string("bytes"); + } else if (std::holds_alternative::Custom>(_match_25._data)) { auto& _v = std::get::Custom>(_match_25._data); auto& name = _v.name; @@ -4280,6 +4315,9 @@ struct Checker { auto& inner = *_v.inner; return ((std::string("ptr[") + ((*this).type_name(inner))) + std::string("]")); } + else if (std::holds_alternative::Bytes>(_match_81._data)) { + return std::string("bytes"); + } else { return std::string("unknown"); } @@ -5225,7 +5263,7 @@ struct Parser { bool is_type_at_pos(int64_t pos) { auto t = (*this).peek_at(pos).token_type; - std::vector type_tokens = std::vector{TK_INT_TYPE, TK_FLOAT_TYPE, TK_STRING_TYPE, TK_BOOL, TK_VOID, TK_AUTO, TK_DYNAMIC, TK_VECTOR, TK_HASHMAP, TK_HASHSET, TK_IDENTIFIER, TK_INT8, TK_INT16, TK_INT32, TK_INT64, TK_FLOAT32, TK_FLOAT64, TK_USIZE, TK_CSTRING, TK_PTR}; + std::vector type_tokens = std::vector{TK_INT_TYPE, TK_FLOAT_TYPE, TK_STRING_TYPE, TK_BOOL, TK_VOID, TK_AUTO, TK_DYNAMIC, TK_VECTOR, TK_HASHMAP, TK_HASHSET, TK_IDENTIFIER, TK_INT8, TK_INT16, TK_INT32, TK_INT64, TK_FLOAT32, TK_FLOAT64, TK_USIZE, TK_CSTRING, TK_PTR, TK_BYTES}; return lv_contains(type_tokens, t); } @@ -5256,16 +5294,23 @@ struct Parser { return pos; } } - if ((tt == TK_IDENTIFIER) && ((*this).peek_at((pos + INT64_C(1))).token_type == TK_LEFT_BRACKET)) { - int64_t try_pos = (pos + INT64_C(2)); - try_pos = (*this).skip_type_tokens(try_pos); - while (((*this).peek_at(try_pos).token_type == TK_COMMA)) { - try_pos = (try_pos + INT64_C(1)); - try_pos = (*this).skip_type_tokens(try_pos); + if ((tt == TK_IDENTIFIER)) { + int64_t id_pos = (pos + INT64_C(1)); + while (((*this).peek_at(id_pos).token_type == TK_DOUBLE_COLON) && ((*this).peek_at((id_pos + INT64_C(1))).token_type == TK_IDENTIFIER)) { + id_pos = (id_pos + INT64_C(2)); } - if (((*this).peek_at(try_pos).token_type == TK_RIGHT_BRACKET)) { - return (try_pos + INT64_C(1)); + if (((*this).peek_at(id_pos).token_type == TK_LEFT_BRACKET)) { + int64_t try_pos = (id_pos + INT64_C(1)); + try_pos = (*this).skip_type_tokens(try_pos); + while (((*this).peek_at(try_pos).token_type == TK_COMMA)) { + try_pos = (try_pos + INT64_C(1)); + try_pos = (*this).skip_type_tokens(try_pos); + } + if (((*this).peek_at(try_pos).token_type == TK_RIGHT_BRACKET)) { + return (try_pos + INT64_C(1)); + } } + return id_pos; } return (pos + INT64_C(1)); } @@ -5385,46 +5430,56 @@ struct Parser { t = TypeNode::make_CString(); } else { - if ((*this).match_any(std::vector{TK_PTR})) { - (*this).consume(TK_LEFT_BRACKET, std::string("Expect '[' after 'ptr'.")); - TypeNode inner = (*this).parse_type(); - (*this).consume(TK_RIGHT_BRACKET, std::string("Expect ']' after ptr type.")); - t = TypeNode::make_Ptr(inner); + if ((*this).match_any(std::vector{TK_BYTES})) { + t = TypeNode::make_Bytes(); } else { - if ((*this).match_any(std::vector{TK_IDENTIFIER})) { - std::string custom_name = (*this).previous().lexeme; - std::vector type_args = {}; - if ((*this).check(TK_LEFT_BRACKET)) { - int64_t save_pos = this->current; - (*this).advance(); - bool is_type_args = true; - try { - TypeNode first_arg = (*this).parse_type(); - type_args.push_back(first_arg); - while ((*this).match_any(std::vector{TK_COMMA})) { - type_args.push_back((*this).parse_type()); + if ((*this).match_any(std::vector{TK_PTR})) { + (*this).consume(TK_LEFT_BRACKET, std::string("Expect '[' after 'ptr'.")); + TypeNode inner = (*this).parse_type(); + (*this).consume(TK_RIGHT_BRACKET, std::string("Expect ']' after ptr type.")); + t = TypeNode::make_Ptr(inner); + } + else { + if ((*this).match_any(std::vector{TK_IDENTIFIER})) { + std::string custom_name = (*this).previous().lexeme; + while ((*this).check(TK_DOUBLE_COLON)) { + (*this).advance(); + auto part = (*this).consume(TK_IDENTIFIER, std::string("Expect type name after '::'.")); + custom_name = ((custom_name + std::string("::")) + part.lexeme); + } + std::vector type_args = {}; + if ((*this).check(TK_LEFT_BRACKET)) { + int64_t save_pos = this->current; + (*this).advance(); + bool is_type_args = true; + try { + TypeNode first_arg = (*this).parse_type(); + type_args.push_back(first_arg); + while ((*this).match_any(std::vector{TK_COMMA})) { + type_args.push_back((*this).parse_type()); + } + if ((!(*this).check(TK_RIGHT_BRACKET))) { + is_type_args = false; + } } - if ((!(*this).check(TK_RIGHT_BRACKET))) { + catch (const std::exception& e) { is_type_args = false; } + if (is_type_args) { + (*this).consume(TK_RIGHT_BRACKET, std::string("Expect ']' after type arguments.")); + } + else { + type_args = {}; + this->current = save_pos; + } } - catch (const std::exception& e) { - is_type_args = false; - } - if (is_type_args) { - (*this).consume(TK_RIGHT_BRACKET, std::string("Expect ']' after type arguments.")); - } - else { - type_args = {}; - this->current = save_pos; - } + t = TypeNode::make_Custom(custom_name, type_args); + } + else { + auto tok = (*this).peek(); + throw std::runtime_error(((((((std::string("Expect type. Got ") + (tok.token_type)) + std::string(" at ")) + (tok.line)) + std::string(":")) + (tok.col)) + std::string(""))); } - t = TypeNode::make_Custom(custom_name, type_args); - } - else { - auto tok = (*this).peek(); - throw std::runtime_error(((((((std::string("Expect type. Got ") + (tok.token_type)) + std::string(" at ")) + (tok.line)) + std::string(":")) + (tok.col)) + std::string(""))); } } } @@ -5514,6 +5569,9 @@ struct Parser { auto& inner = *_v.inner; return ((std::string("") + ((*this).type_to_string(inner))) + std::string("*")); } + else if (std::holds_alternative::Bytes>(_match_100._data)) { + return std::string("std::vector"); + } else { return std::string("auto"); } @@ -5819,6 +5877,10 @@ struct Parser { if ((*this).match_any(std::vector{TK_IDENTIFIER})) { return Expr::make_Variable((*this).previous()); } + if ((*this).check(TK_BYTES) && ((*this).peek_at(INT64_C(1)).token_type == TK_DOUBLE_COLON)) { + auto tok = (*this).advance(); + return Expr::make_Variable(Token(TK_IDENTIFIER, tok.lexeme, tok.line, tok.col)); + } if ((*this).match_any(std::vector{TK_THIS})) { return Expr::make_This((*this).previous()); } @@ -6314,7 +6376,7 @@ struct Parser { } Stmt extend_declaration(std::string visibility) { - if ((!(*this).match_any(std::vector{TK_IDENTIFIER, TK_VECTOR, TK_HASHMAP, TK_HASHSET, TK_STRING_TYPE}))) { + if ((!(*this).match_any(std::vector{TK_IDENTIFIER, TK_VECTOR, TK_HASHMAP, TK_HASHSET, TK_STRING_TYPE, TK_BYTES}))) { throw std::runtime_error(std::string("Expect type name after 'extend'.")); } auto target = (*this).previous(); @@ -6351,11 +6413,22 @@ struct Parser { return Stmt::make_Extend(target, methods, visibility); } + Token consume_module_name() { + if ((*this).match_any(std::vector{TK_IDENTIFIER})) { + return (*this).previous(); + } + if ((*this).match_any(std::vector{TK_BYTES})) { + auto tok = (*this).previous(); + return Token(TK_IDENTIFIER, tok.lexeme, tok.line, tok.col); + } + throw std::runtime_error(((((((std::string("Expect module name. Got ") + ((*this).peek().token_type)) + std::string(" at ")) + ((*this).peek().line)) + std::string(":")) + ((*this).peek().col)) + std::string(""))); + } + Stmt import_statement() { std::vector path = {}; - path.push_back((*this).consume(TK_IDENTIFIER, std::string("Expect module name."))); + path.push_back((*this).consume_module_name()); while ((*this).match_any(std::vector{TK_DOUBLE_COLON})) { - path.push_back((*this).consume(TK_IDENTIFIER, std::string("Expect module name."))); + path.push_back((*this).consume_module_name()); } std::string alias = std::string(""); if ((*this).match_any(std::vector{TK_AS})) { diff --git a/tests/test_bytes.lv b/tests/test_bytes.lv new file mode 100644 index 0000000..66f918f --- /dev/null +++ b/tests/test_bytes.lv @@ -0,0 +1,200 @@ +// Test: bytes type + hex literals + std::bytes API +import std::bytes + +void fn test_hex_literals(): + int a = 0xFF + lv_assert(a == 255, "0xFF == 255") + int b = 0x10 + lv_assert(b == 16, "0x10 == 16") + int c = 0xDEAD + lv_assert(c == 57005, "0xDEAD == 57005") + int d = 0x0 + lv_assert(d == 0, "0x0 == 0") + print("PASS: hex literals") + +void fn test_create(): + bytes buf = bytes::create(4) + lv_assert(buf.len() == 4, "create size 4") + bytes empty = bytes::create(0) + lv_assert(empty.len() == 0, "create size 0") + print("PASS: create") + +void fn test_get_set(): + bytes buf = bytes::create(4) + buf.set(0, 0x48) + buf.set(1, 0x65) + buf.set(2, 0x6C) + buf.set(3, 0x6F) + lv_assert(buf.get(0) == 0x48, "get byte 0") + lv_assert(buf.get(1) == 0x65, "get byte 1") + lv_assert(buf.get(2) == 0x6C, "get byte 2") + lv_assert(buf.get(3) == 0x6F, "get byte 3") + print("PASS: get/set") + +void fn test_from_string(): + bytes hello = bytes::from_string("Hello") + lv_assert(hello.len() == 5, "from_string length") + lv_assert(hello.get(0) == 0x48, "H = 0x48") + lv_assert(hello.get(4) == 0x6F, "o = 0x6F") + print("PASS: from_string") + +void fn test_to_string(): + bytes hello = bytes::from_string("Hello") + string back = hello.to_string() + lv_assert(back == "Hello", "to_string roundtrip") + print("PASS: to_string") + +void fn test_hex_encode(): + bytes hello = bytes::from_string("Hello") + string hex = hello.to_hex() + lv_assert(hex == "48656c6c6f", "to_hex") + print("PASS: hex encode") + +void fn test_hex_decode(): + bytes decoded = bytes::from_hex("48656c6c6f") + lv_assert(decoded.len() == 5, "from_hex length") + string back = decoded.to_string() + lv_assert(back == "Hello", "from_hex roundtrip") + print("PASS: hex decode") + +void fn test_push(): + bytes buf = bytes::create(0) + buf.push(0x41) + buf.push(0x42) + buf.push(0x43) + lv_assert(buf.len() == 3, "push length") + lv_assert(buf.get(0) == 0x41, "push byte 0") + lv_assert(buf.get(2) == 0x43, "push byte 2") + print("PASS: push") + +void fn test_slice(): + bytes hello = bytes::from_string("Hello World") + bytes sub = hello.slice(0, 5) + lv_assert(sub.len() == 5, "slice length") + lv_assert(sub.to_string() == "Hello", "slice content") + bytes sub2 = hello.slice(6, 11) + lv_assert(sub2.to_string() == "World", "slice content 2") + print("PASS: slice") + +void fn test_concat(): + bytes a = bytes::from_string("AB") + bytes b = bytes::from_string("CD") + bytes combined = a.concat(b) + lv_assert(combined.len() == 4, "concat length") + lv_assert(combined.to_string() == "ABCD", "concat content") + print("PASS: concat") + +void fn test_equals(): + bytes a = bytes::from_string("test") + bytes b = bytes::from_string("test") + bytes c = bytes::from_string("other") + lv_assert(a.equals(b) == true, "equal bytes") + lv_assert(a.equals(c) == false, "not equal bytes") + print("PASS: equals") + +void fn test_fill(): + bytes buf = bytes::create(4) + buf.fill(0xFF) + lv_assert(buf.get(0) == 255, "fill byte 0") + lv_assert(buf.get(3) == 255, "fill byte 3") + print("PASS: fill") + +void fn test_u16_be(): + bytes buf = bytes::create(4) + buf.write_u16_be(0, 0x0102) + lv_assert(buf.read_u16_be(0) == 258, "u16 be roundtrip") + buf.write_u16_be(2, 0x01BB) + lv_assert(buf.read_u16_be(2) == 443, "u16 be port 443") + // Verify byte order + lv_assert(buf.get(0) == 0x01, "u16 be high byte") + lv_assert(buf.get(1) == 0x02, "u16 be low byte") + print("PASS: u16 big-endian") + +void fn test_u32_be(): + bytes buf = bytes::create(4) + buf.write_u32_be(0, 0x01020304) + lv_assert(buf.read_u32_be(0) == 16909060, "u32 be roundtrip") + lv_assert(buf.get(0) == 0x01, "u32 be byte 0") + lv_assert(buf.get(1) == 0x02, "u32 be byte 1") + lv_assert(buf.get(2) == 0x03, "u32 be byte 2") + lv_assert(buf.get(3) == 0x04, "u32 be byte 3") + print("PASS: u32 big-endian") + +void fn test_u16_le(): + bytes buf = bytes::create(2) + buf.write_u16_le(0, 0x0102) + lv_assert(buf.read_u16_le(0) == 258, "u16 le roundtrip") + // Little-endian: low byte first + lv_assert(buf.get(0) == 0x02, "u16 le low byte first") + lv_assert(buf.get(1) == 0x01, "u16 le high byte second") + print("PASS: u16 little-endian") + +void fn test_u32_le(): + bytes buf = bytes::create(4) + buf.write_u32_le(0, 0x01020304) + lv_assert(buf.read_u32_le(0) == 16909060, "u32 le roundtrip") + lv_assert(buf.get(0) == 0x04, "u32 le byte 0 (lowest)") + lv_assert(buf.get(3) == 0x01, "u32 le byte 3 (highest)") + print("PASS: u32 little-endian") + +int fn get_len(bytes b): + return b.len() + +bytes fn make_bytes(): + bytes b = bytes::create(3) + b.set(0, 1) + b.set(1, 2) + b.set(2, 3) + return b + +void fn test_bytes_as_param(): + bytes data = bytes::from_string("param test") + int length = get_len(data) + lv_assert(length == 10, "bytes param passing") + print("PASS: bytes as param") + +void fn test_bytes_return(): + bytes result = make_bytes() + lv_assert(result.len() == 3, "bytes return") + lv_assert(result.get(0) == 1, "return byte 0") + print("PASS: bytes return") + +void fn test_clear(): + bytes buf = bytes::from_string("hello") + lv_assert(buf.len() == 5, "before clear") + buf.clear() + lv_assert(buf.len() == 0, "after clear") + print("PASS: clear") + +void fn test_free_functions(): + bytes a = bytes::from_string("AB") + bytes b = bytes::from_string("AB") + bytes c = bytes::from_string("XY") + lv_assert(bytes::equals(a, b) == true, "free equals true") + lv_assert(bytes::equals(a, c) == false, "free equals false") + bytes combined = bytes::concat(a, c) + lv_assert(combined.to_string() == "ABXY", "free concat") + print("PASS: free functions") + +void fn main(): + test_hex_literals() + test_create() + test_get_set() + test_from_string() + test_to_string() + test_hex_encode() + test_hex_decode() + test_push() + test_slice() + test_concat() + test_equals() + test_fill() + test_u16_be() + test_u32_be() + test_u16_le() + test_u32_le() + test_bytes_as_param() + test_bytes_return() + test_clear() + test_free_functions() + println("All bytes tests passed!") diff --git a/tests/test_std_bytes.lv b/tests/test_std_bytes.lv new file mode 100644 index 0000000..45877c8 --- /dev/null +++ b/tests/test_std_bytes.lv @@ -0,0 +1,149 @@ +import std::bytes + +void fn test_create(): + bytes buf = bytes::create(4) + lv_assert(buf.len() == 4, "create size 4") + bytes empty = bytes::create(0) + lv_assert(empty.len() == 0, "create size 0") + print("PASS: create") + +void fn test_from_to_string(): + bytes hello = bytes::from_string("Hello") + lv_assert(hello.len() == 5, "from_string length") + string back = hello.to_string() + lv_assert(back == "Hello", "to_string roundtrip") + print("PASS: from/to string") + +void fn test_hex_encoding(): + bytes hello = bytes::from_string("Hello") + string hex = hello.to_hex() + lv_assert(hex == "48656c6c6f", "to_hex") + bytes decoded = bytes::from_hex("48656c6c6f") + string back = decoded.to_string() + lv_assert(back == "Hello", "from_hex roundtrip") + print("PASS: hex encoding") + +void fn test_get_set(): + bytes buf = bytes::create(3) + buf.set(0, 0x41) + buf.set(1, 0x42) + buf.set(2, 0x43) + lv_assert(buf.get(0) == 0x41, "get byte 0") + lv_assert(buf.get(1) == 0x42, "get byte 1") + lv_assert(buf.get(2) == 0x43, "get byte 2") + print("PASS: get/set") + +void fn test_push_len_clear(): + bytes buf = bytes::create(0) + buf.push(0x01) + buf.push(0x02) + buf.push(0x03) + lv_assert(buf.len() == 3, "push length") + buf.clear() + lv_assert(buf.len() == 0, "after clear") + print("PASS: push/len/clear") + +void fn test_u16_be(): + bytes buf = bytes::create(2) + buf.write_u16_be(0, 0x01BB) + lv_assert(buf.read_u16_be(0) == 443, "u16 be port 443") + lv_assert(buf.get(0) == 0x01, "u16 be high byte") + lv_assert(buf.get(1) == 0xBB, "u16 be low byte") + print("PASS: u16 big-endian") + +void fn test_u32_be(): + bytes buf = bytes::create(4) + buf.write_u32_be(0, 0x01020304) + lv_assert(buf.read_u32_be(0) == 16909060, "u32 be roundtrip") + lv_assert(buf.get(0) == 0x01, "u32 be byte 0") + lv_assert(buf.get(3) == 0x04, "u32 be byte 3") + print("PASS: u32 big-endian") + +void fn test_u16_le(): + bytes buf = bytes::create(2) + buf.write_u16_le(0, 0x0102) + lv_assert(buf.read_u16_le(0) == 258, "u16 le roundtrip") + lv_assert(buf.get(0) == 0x02, "u16 le low byte first") + lv_assert(buf.get(1) == 0x01, "u16 le high byte second") + print("PASS: u16 little-endian") + +void fn test_u32_le(): + bytes buf = bytes::create(4) + buf.write_u32_le(0, 0x01020304) + lv_assert(buf.read_u32_le(0) == 16909060, "u32 le roundtrip") + lv_assert(buf.get(0) == 0x04, "u32 le byte 0 (lowest)") + lv_assert(buf.get(3) == 0x01, "u32 le byte 3 (highest)") + print("PASS: u32 little-endian") + +void fn test_slice(): + bytes hello = bytes::from_string("Hello World") + bytes sub = hello.slice(0, 5) + lv_assert(sub.len() == 5, "slice length") + lv_assert(sub.to_string() == "Hello", "slice content") + bytes sub2 = hello.slice(6, 11) + lv_assert(sub2.to_string() == "World", "slice content 2") + print("PASS: slice") + +void fn test_concat(): + bytes a = bytes::from_string("AB") + bytes b = bytes::from_string("CD") + bytes combined = a.concat(b) + lv_assert(combined.len() == 4, "concat length") + lv_assert(combined.to_string() == "ABCD", "concat content") + print("PASS: concat") + +void fn test_equals(): + bytes a = bytes::from_string("test") + bytes b = bytes::from_string("test") + bytes c = bytes::from_string("other") + lv_assert(a.equals(b) == true, "equal bytes") + lv_assert(a.equals(c) == false, "not equal bytes") + print("PASS: equals") + +void fn test_fill(): + bytes buf = bytes::create(4) + buf.fill(0xFF) + lv_assert(buf.get(0) == 255, "fill byte 0") + lv_assert(buf.get(3) == 255, "fill byte 3") + print("PASS: fill") + +void fn test_combined_workflow(): + // Simulate building a simple binary packet: [u16 length][payload] + bytes payload = bytes::from_string("Hi") + bytes header = bytes::create(2) + header.write_u16_be(0, payload.len()) + bytes packet = header.concat(payload) + lv_assert(packet.len() == 4, "packet length") + int plen = packet.read_u16_be(0) + lv_assert(plen == 2, "header stores payload length") + bytes extracted = packet.slice(2, 4) + lv_assert(extracted.to_string() == "Hi", "extracted payload") + print("PASS: combined workflow") + +void fn test_free_functions(): + bytes a = bytes::from_string("AB") + bytes b = bytes::from_string("AB") + bytes c = bytes::from_string("XY") + lv_assert(bytes::equals(a, b) == true, "free equals true") + lv_assert(bytes::equals(a, c) == false, "free equals false") + bytes combined = bytes::concat(a, c) + lv_assert(combined.to_string() == "ABXY", "free concat") + print("PASS: free functions") + +void fn main(): + test_create() + test_from_to_string() + test_hex_encoding() + test_get_set() + test_push_len_clear() + test_u16_be() + test_u32_be() + test_u16_le() + test_u32_le() + test_slice() + test_concat() + test_equals() + test_fill() + test_combined_workflow() + test_free_functions() + println("All std::bytes tests passed!") From 8a601c873f3829c894bb19403b939f9ad47cd6b6 Mon Sep 17 00:00:00 2001 From: Raumberg Date: Tue, 17 Feb 2026 15:25:13 +0300 Subject: [PATCH 11/19] docs: updated documentation with latest changes (std thread, net, bytes) --- DOCUMENTATION.md | 357 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 346 insertions(+), 11 deletions(-) diff --git a/DOCUMENTATION.md b/DOCUMENTATION.md index 527c66e..28683ba 100644 --- a/DOCUMENTATION.md +++ b/DOCUMENTATION.md @@ -14,15 +14,18 @@ 10. [References and Ownership](#references-and-ownership) 11. [Lambdas](#lambdas) 12. [Collections](#collections) -13. [Strings](#strings) -14. [Error Handling](#error-handling) -15. [Imports and Modules](#imports-and-modules) -16. [Operator Overloading](#operator-overloading) -17. [Compile-Time Evaluation](#compile-time-evaluation) -18. [FFI (Foreign Function Interface)](#ffi-foreign-function-interface) -19. [Standard Library](#standard-library) -20. [Extension Methods](#extension-methods) -21. [Compiler and Bootstrap](#compiler-and-bootstrap) +13. [Bytes](#bytes) +14. [Strings](#strings) +15. [Error Handling](#error-handling) +16. [Imports and Modules](#imports-and-modules) +17. [Operator Overloading](#operator-overloading) +18. [Compile-Time Evaluation](#compile-time-evaluation) +19. [FFI (Foreign Function Interface)](#ffi-foreign-function-interface) +20. [Standard Library](#standard-library) +21. [Networking](#networking) +22. [Threading](#threading) +23. [Extension Methods](#extension-methods) +24. [Compiler and Bootstrap](#compiler-and-bootstrap) --- @@ -42,6 +45,15 @@ Comments use `//`: int x = 42 // inline comment ``` +### Number Literals + +```lavina +int x = 42 // decimal +int y = 0xFF // hexadecimal (255) +int port = 0x01BB // hex (443) +float pi = 3.14 // float +``` + Every program needs a `main()` function as its entry point. It can return `int` or `void`. --- @@ -66,6 +78,12 @@ Every program needs a `main()` function as its entry point. It can return `int` | `hashmap[K, V]` | Hash map | `std::unordered_map` | | `hashset[T]` | Hash set | `std::unordered_set` | +### Binary Types + +| Type | Description | C++ mapping | +|------|-------------|-------------| +| `bytes` | Byte buffer | `std::vector` | + ### Special Types | Type | Description | @@ -618,6 +636,90 @@ s.len() // 2 --- +## Bytes + +The `bytes` type is a built-in byte buffer backed by `std::vector`. It is designed for binary protocol work (network headers, UUID encoding, crypto buffers, etc.). + +### Creating Bytes + +```lavina +import std::bytes + +bytes buf = bytes::create(64) // zero-filled buffer of 64 bytes +bytes hello = bytes::from_string("Hello") // from UTF-8 string +bytes raw = bytes::from_hex("48656c6c6f") // from hex string +``` + +### Byte-Level Access + +```lavina +buf.set(0, 0xFF) // set byte at index +int val = buf.get(0) // get byte at index (returns int) +buf.fill(0x00) // fill all bytes with value +``` + +### Conversions + +```lavina +string s = buf.to_string() // bytes → string +string hex = buf.to_hex() // bytes → hex string ("48656c6c6f") +``` + +### Binary Read/Write + +Read and write multi-byte integers in big-endian or little-endian byte order: + +```lavina +bytes pkt = bytes::create(8) + +// Big-endian (network byte order) +pkt.write_u16_be(0, 0x01BB) // write u16 at offset 0 +int port = pkt.read_u16_be(0) // 443 +pkt.write_u32_be(2, 0x01020304) // write u32 at offset 2 +int val = pkt.read_u32_be(2) // 16909060 + +// Little-endian +pkt.write_u16_le(0, 0x0102) +pkt.write_u32_le(2, 0x01020304) +``` + +### Operations + +```lavina +// Inherited from vector +buf.push(0x42) // append byte +buf.len() // length +buf.clear() // clear + +// Slice and concat +bytes sub = buf.slice(0, 5) // [start, end) +bytes combined = a.concat(b) // concatenate two buffers +bool eq = a.equals(b) // byte-by-byte comparison + +// Free function alternatives +bytes c = bytes::concat(a, b) +bool e = bytes::equals(a, b) +``` + +### Example: Binary Packet + +```lavina +import std::bytes + +// Build a packet: [u16 length][payload] +bytes payload = bytes::from_string("Hi") +bytes header = bytes::create(2) +header.write_u16_be(0, payload.len()) +bytes packet = header.concat(payload) + +// Parse it back +int plen = packet.read_u16_be(0) // 2 +bytes extracted = packet.slice(2, 4) +string msg = extracted.to_string() // "Hi" +``` + +--- + ## Strings ### Basics @@ -971,6 +1073,9 @@ import std::fs // fs::read(), fs::write(), ... import std::os // os::args(), os::exec(), ... import std::math // math::PI, math::sqrt(), ... import std::collections // vector/hashset dot-methods + free functions +import std::bytes // bytes::create(), buf.get(), buf.write_u16_be(), ... +import std::net // net::tcp_listen(), TcpStream, UdpSocket, ... +import std::thread // thread::spawn(), Mutex, Pool, ... ``` **std::collections** provides higher-order functions both as free functions and as dot-notation methods via `extend`: @@ -995,6 +1100,236 @@ auto r = collections::range(0, 10) Available dot-methods on vectors: `map`, `filter`, `reduce`, `for_each`, `zip`, `take`, `drop`, `enumerate`. Available dot-methods on hashsets: `union_with`, `intersect`, `difference`. +**std::bytes** provides byte buffer operations for binary protocols, with both dot-notation methods (via `extend bytes`) and free functions: + +```lavina +import std::bytes + +// Construction +bytes buf = bytes::create(64) +bytes hello = bytes::from_string("Hello") +bytes decoded = bytes::from_hex("48656c6c6f") + +// Dot-methods (via extend) +buf.set(0, 0xFF) // set byte +int val = buf.get(0) // get byte +buf.fill(0x00) // fill all bytes +string s = buf.to_string() // convert to string +string hex = buf.to_hex() // hex-encode + +// Binary read/write (big-endian and little-endian) +buf.write_u16_be(0, 0x01BB) // write u16 big-endian +int port = buf.read_u16_be(0) // read u16 big-endian (443) +buf.write_u32_le(0, 0x01020304) // write u32 little-endian + +// Inherited vector methods +buf.push(0x42) // append byte +buf.len() // buffer length +buf.clear() // clear buffer + +// Slice, concat, equals +bytes sub = buf.slice(0, 5) // slice [start, end) +bytes combined = a.concat(b) // concatenate +bool eq = a.equals(b) // compare + +// Free functions +bytes c = bytes::concat(a, b) +bool e = bytes::equals(a, b) +``` + +**std::net** provides TCP/UDP networking and DNS resolution: + +```lavina +import std::net + +// High-level API with structs +net::TcpListener server = net::TcpListener("127.0.0.1", 8080) +net::TcpStream client = net::connect("127.0.0.1", 8080) +net::TcpStream accepted = server.accept() +client.send("hello") +string data = accepted.recv(1024) +client.close() +accepted.close() +server.close() + +net::UdpSocket udp = net::UdpSocket("0.0.0.0", 9000) +udp.send("message", "127.0.0.1", 9001) +string msg = udp.recv(1024) +udp.close() + +// DNS +string ip = net::resolve("example.com") + +// Low-level API (file descriptor based) +int fd = net::tcp_listen("0.0.0.0", 8080) +int conn = net::tcp_accept(fd) +net::tcp_send(conn, "hello") +string reply = net::tcp_recv(conn, 1024) +net::tcp_close(conn) +``` + +**std::thread** provides OS threads, mutexes, and thread pools: + +```lavina +import std::thread + +// Spawn threads +int result = 0 +thread::Thread t = thread::spawn((): + result = 42 +) +t.wait() // join thread + +// Mutex for synchronization +thread::Mutex mtx = thread::Mutex() +mtx.lock() +// ... critical section ... +mtx.unlock() +mtx.destroy() + +// Thread pool +thread::Pool pool = thread::Pool(4) // 4 workers +for i in 0..100: + pool.submit((): + // task runs on a worker thread + pass + ) +pool.shutdown() // wait for all tasks to finish + +// Utilities +thread::sleep(100) // sleep 100ms +int id = thread::current_id() // current thread ID +``` + +--- + +## Networking + +The `std::net` module provides TCP and UDP networking plus DNS resolution, using POSIX sockets (no external dependencies). + +### TCP + +```lavina +import std::net + +// Server: listen, accept, send/recv +net::TcpListener server = net::TcpListener("127.0.0.1", 8080) +net::TcpStream client_conn = server.accept() +string data = client_conn.recv(1024) +client_conn.send("echo: ${data}") +client_conn.close() +server.close() + +// Client: connect, send/recv +net::TcpStream client = net::connect("127.0.0.1", 8080) +client.send("hello") +string response = client.recv(1024) +client.close() +``` + +### UDP + +```lavina +import std::net + +net::UdpSocket sock = net::UdpSocket("0.0.0.0", 9000) +sock.send("ping", "127.0.0.1", 9001) +string msg = sock.recv(1024) +sock.close() +``` + +### DNS + +```lavina +import std::net + +string ip = net::resolve("example.com") // "93.184.216.34" +``` + +### Low-Level API + +For direct file descriptor control: + +| Function | Description | +|----------|-------------| +| `net::tcp_listen(host, port)` | Bind and listen, returns fd | +| `net::tcp_accept(fd)` | Accept connection, returns fd | +| `net::tcp_connect(host, port)` | Connect, returns fd | +| `net::tcp_send(fd, data)` | Send string over TCP | +| `net::tcp_recv(fd, max)` | Receive string from TCP | +| `net::tcp_close(fd)` | Close TCP socket | +| `net::udp_create(host, port)` | Create and bind UDP socket | +| `net::udp_send(fd, data, host, port)` | Send UDP datagram | +| `net::udp_recv(fd, max)` | Receive UDP datagram | +| `net::udp_close(fd)` | Close UDP socket | + +--- + +## Threading + +The `std::thread` module provides OS threads, mutexes, and thread pools. + +### Spawning Threads + +```lavina +import std::thread + +int result = 0 +thread::Thread t = thread::spawn((): + result = 42 +) +t.wait() // blocks until thread completes +// result is now 42 +``` + +Lambdas capture variables by reference (`[&]` in C++), so threads can modify shared state. Always call `.wait()` before the captured variables go out of scope. + +```lavina +t.detach() // let thread run independently (don't wait) +``` + +### Mutex + +```lavina +import std::thread + +thread::Mutex mtx = thread::Mutex() +int counter = 0 + +for i in 0..10: + thread::spawn((): + mtx.lock() + counter += 1 + mtx.unlock() + ) + +// ... wait for threads ... +mtx.destroy() +``` + +### Thread Pool + +```lavina +import std::thread + +thread::Pool pool = thread::Pool(4) // 4 worker threads + +for i in 0..100: + pool.submit((): + // runs on a worker thread + pass + ) + +pool.shutdown() // waits for all submitted tasks to finish +``` + +### Utilities + +| Function | Description | +|----------|-------------| +| `thread::sleep(ms)` | Sleep current thread for `ms` milliseconds | +| `thread::current_id()` | Get current thread's ID (int) | + --- ## Extension Methods @@ -1028,7 +1363,7 @@ string greeting = "hello" print(greeting.shout()) // hello! ``` -Extend methods work on: `vector`, `hashmap`, `hashset`, `string`, and custom types (structs/enums by name). +Extend methods work on: `vector`, `hashmap`, `hashset`, `string`, `bytes`, and custom types (structs/enums by name). Use `this` inside extend methods to refer to the object. Built-in methods (`.push()`, `.sort()`, etc.) take priority over extend methods. @@ -1079,7 +1414,7 @@ The compiler maintains C++ snapshots in `stages/`. Each snapshot is a complete s make bootstrap # Build from latest stage, verify fixed point make snapshot # Save new stage after codegen changes make evolve # Handle codegen output format changes (2-pass) -make test # Run test suite (37 tests) +make test # Run test suite (42 tests) ``` **Fixed point verification**: The bootstrap compiles `src/main.lv` twice — once with the previous stage and once with the newly built compiler. The outputs must be identical, proving the compiler correctly compiles itself. From 675b6ea1d2610d0ebb9b04d7c13947b1818debae Mon Sep 17 00:00:00 2001 From: Raumberg Date: Tue, 17 Feb 2026 15:59:23 +0300 Subject: [PATCH 12/19] examples: fully functional VLESS protocol --- examples/complex/vless/test_vless.lv | 272 +++++++++++++++++++++++++++ examples/complex/vless/vless.lv | 235 +++++++++++++++++++++++ runtime/lavina.h | 3 +- runtime/liblavina/net.h | 46 +++++ runtime/liblavina/uuid.h | 56 ++++++ runtime/std/net.lv | 18 ++ runtime/std/uuid.lv | 13 ++ tests/test_uuid.lv | 63 +++++++ 8 files changed, 705 insertions(+), 1 deletion(-) create mode 100644 examples/complex/vless/test_vless.lv create mode 100644 examples/complex/vless/vless.lv create mode 100644 runtime/liblavina/uuid.h create mode 100644 runtime/std/uuid.lv create mode 100644 tests/test_uuid.lv diff --git a/examples/complex/vless/test_vless.lv b/examples/complex/vless/test_vless.lv new file mode 100644 index 0000000..2e254ce --- /dev/null +++ b/examples/complex/vless/test_vless.lv @@ -0,0 +1,272 @@ +// End-to-end test for VLESS proxy +// 1. Start an echo TCP server on port 19876 +// 2. Start the VLESS handler on port 19877 +// 3. Connect to VLESS, send a VLESS request targeting the echo server +// 4. Verify data round-trips correctly + +import std::bytes +import std::net +import std::uuid +import std::thread +import std::os + +// ── Constants ──────────────────────────────────────────────── + +comptime int VLESS_VERSION = 0 +comptime int CMD_TCP = 1 +comptime int ADDR_IPV4 = 1 +comptime int ADDR_DOMAIN = 2 + +// ── Parsed VLESS request ──────────────────────────────────── + +struct VlessRequest: + int command + string address + int port + int payload_offset + bytes client_uuid + + constructor(): + this.command = 0 + this.address = "" + this.port = 0 + this.payload_offset = 0 + this.client_uuid = bytes::create(0) + +// ── Parse / build ─────────────────────────────────────────── + +VlessRequest fn parse_request(bytes data): + if data.len() < 23: + throw "VLESS: request too short" + VlessRequest req = VlessRequest() + int offset = 0 + int version = data.get(offset) + offset = offset + 1 + req.client_uuid = data.slice(offset, offset + 16) + offset = offset + 16 + int addons_len = data.get(offset) + offset = offset + 1 + offset = offset + addons_len + req.command = data.get(offset) + offset = offset + 1 + req.port = data.read_u16_be(offset) + offset = offset + 2 + int addr_type = data.get(offset) + offset = offset + 1 + if addr_type == ADDR_IPV4: + int a = data.get(offset) + int b = data.get(offset + 1) + int c = data.get(offset + 2) + int d = data.get(offset + 3) + req.address = "${a}.${b}.${c}.${d}" + offset = offset + 4 + elif addr_type == ADDR_DOMAIN: + int domain_len = data.get(offset) + offset = offset + 1 + bytes domain_bytes = data.slice(offset, offset + domain_len) + req.address = domain_bytes.to_string() + offset = offset + domain_len + else: + throw "VLESS: unknown address type" + req.payload_offset = offset + return req + +bytes fn build_response(): + bytes resp = bytes::create(2) + resp.set(0, 0) + resp.set(1, 0) + return resp + +void fn relay(int from_fd, int to_fd): + while true: + bytes chunk = net::tcp_recv_bytes(from_fd, 8192) + if chunk.len() == 0: + break + net::tcp_send_bytes(to_fd, chunk) + +void fn handle_vless_client(int client_fd, bytes auth_uuid): + bytes data = net::tcp_recv_bytes(client_fd, 65536) + if data.len() == 0: + net::tcp_close(client_fd) + return + VlessRequest req = VlessRequest() + try: + req = parse_request(data) + catch: + println(" VLESS: parse failed") + net::tcp_close(client_fd) + return + if uuid::equals(req.client_uuid, auth_uuid) == false: + println(" VLESS: auth failed") + net::tcp_close(client_fd) + return + if req.command != CMD_TCP: + println(" VLESS: bad command") + net::tcp_close(client_fd) + return + int remote_fd = 0 + try: + remote_fd = net::tcp_connect(req.address, req.port) + catch: + println(" VLESS: connect failed to ${req.address}:${req.port}") + net::tcp_close(client_fd) + return + bytes resp_header = build_response() + net::tcp_send_bytes(client_fd, resp_header) + if req.payload_offset < data.len(): + bytes initial_payload = data.slice(req.payload_offset, data.len()) + net::tcp_send_bytes(remote_fd, initial_payload) + thread::Thread t = thread::spawn((): + try: + relay(remote_fd, client_fd) + catch: + pass + ) + try: + relay(client_fd, remote_fd) + catch: + pass + t.wait() + net::tcp_close(remote_fd) + net::tcp_close(client_fd) + +// ── Build a VLESS request ──────────────────────────────────── + +bytes fn build_vless_request(bytes auth_uuid, string host, int port, bytes payload): + bytes domain_bytes = bytes::from_string(host) + int header_len = 1 + 16 + 1 + 1 + 2 + 1 + 1 + domain_bytes.len() + payload.len() + bytes req = bytes::create(header_len) + int offset = 0 + + // Version + req.set(offset, VLESS_VERSION) + offset = offset + 1 + + // UUID (copy 16 bytes) + int i = 0 + while i < 16: + req.set(offset + i, auth_uuid.get(i)) + i = i + 1 + offset = offset + 16 + + // Addons length = 0 + req.set(offset, 0) + offset = offset + 1 + + // Command = TCP + req.set(offset, CMD_TCP) + offset = offset + 1 + + // Port (big-endian) + req.write_u16_be(offset, port) + offset = offset + 2 + + // Address type = domain + req.set(offset, ADDR_DOMAIN) + offset = offset + 1 + + // Domain length + domain + req.set(offset, domain_bytes.len()) + offset = offset + 1 + i = 0 + while i < domain_bytes.len(): + req.set(offset + i, domain_bytes.get(i)) + i = i + 1 + offset = offset + domain_bytes.len() + + // Payload + i = 0 + while i < payload.len(): + req.set(offset + i, payload.get(i)) + i = i + 1 + + return req + +// ── Tests ──────────────────────────────────────────────────── + +void fn test_request_builder(): + bytes test_uuid = uuid::parse("550e8400-e29b-41d4-a716-446655440000") + bytes payload = bytes::from_string("Hello") + bytes req = build_vless_request(test_uuid, "127.0.0.1", 80, payload) + + lv_assert(req.get(0) == 0, "version is 0") + lv_assert(req.get(1) == 0x55, "uuid byte 0") + lv_assert(req.get(17) == 0, "addons len = 0") + lv_assert(req.get(18) == CMD_TCP, "command = TCP") + lv_assert(req.read_u16_be(19) == 80, "port = 80") + lv_assert(req.get(21) == ADDR_DOMAIN, "addr type = domain") + lv_assert(req.get(22) == 9, "domain len = 9") + println("PASS: request builder") + +void fn test_parse_roundtrip(): + bytes test_uuid = uuid::parse("550e8400-e29b-41d4-a716-446655440000") + bytes payload = bytes::from_string("Hello") + bytes raw = build_vless_request(test_uuid, "example.com", 443, payload) + + VlessRequest req = parse_request(raw) + lv_assert(req.command == CMD_TCP, "parsed command = TCP") + lv_assert(req.port == 443, "parsed port = 443") + lv_assert(req.address == "example.com", "parsed address") + lv_assert(uuid::equals(req.client_uuid, test_uuid), "parsed uuid matches") + + bytes parsed_payload = raw.slice(req.payload_offset, raw.len()) + lv_assert(parsed_payload.to_string() == "Hello", "parsed payload") + println("PASS: parse roundtrip") + +void fn test_end_to_end(): + bytes auth_uuid = uuid::parse("550e8400-e29b-41d4-a716-446655440000") + int echo_port = 19876 + int vless_port = 19877 + + // 1. Start echo server + thread::Thread echo_thread = thread::spawn((): + int srv = net::tcp_listen("127.0.0.1", echo_port) + int cli = net::tcp_accept(srv) + bytes data = net::tcp_recv_bytes(cli, 4096) + net::tcp_send_bytes(cli, data) + net::tcp_close(cli) + net::tcp_close(srv) + ) + + thread::sleep(100) + + // 2. Start VLESS server (single connection for test) + thread::Thread vless_thread = thread::spawn((): + int srv = net::tcp_listen("127.0.0.1", vless_port) + int cli = net::tcp_accept(srv) + handle_vless_client(cli, auth_uuid) + net::tcp_close(srv) + ) + + thread::sleep(100) + + // 3. Connect to VLESS server and send request + int fd = net::tcp_connect("127.0.0.1", vless_port) + + bytes payload = bytes::from_string("Hello VLESS!") + bytes req = build_vless_request(auth_uuid, "127.0.0.1", echo_port, payload) + net::tcp_send_bytes(fd, req) + + // 4. Read VLESS response + bytes resp = net::tcp_recv_bytes(fd, 4096) + lv_assert(resp.len() >= 2, "got response") + lv_assert(resp.get(0) == 0, "response version = 0") + lv_assert(resp.get(1) == 0, "response addons_len = 0") + + bytes echo_data = resp.slice(2, resp.len()) + if echo_data.len() == 0: + echo_data = net::tcp_recv_bytes(fd, 4096) + + lv_assert(echo_data.to_string() == "Hello VLESS!", "echo data matches") + + net::tcp_close(fd) + vless_thread.wait() + echo_thread.wait() + + println("PASS: end-to-end proxy") + +void fn main(): + test_request_builder() + test_parse_roundtrip() + test_end_to_end() + println("All VLESS tests passed!") diff --git a/examples/complex/vless/vless.lv b/examples/complex/vless/vless.lv new file mode 100644 index 0000000..4e81cca --- /dev/null +++ b/examples/complex/vless/vless.lv @@ -0,0 +1,235 @@ +// VLESS proxy server — Phase 1 (plaintext) +// Protocol: https://xtls.github.io/development/protocols/vless.html +// +// VLESS Request: +// [1B version][16B UUID][1B addons_len][addons...] +// [1B command][2B port BE][1B addr_type][addr...][payload...] +// +// VLESS Response: +// [1B version=0][1B addons_len=0][payload...] +// +// Address types: 1=IPv4(4B), 2=domain(1B len + string), 3=IPv6(16B) +// Commands: 1=TCP, 2=UDP (only TCP supported in Phase 1) + +import std::bytes +import std::net +import std::uuid +import std::thread +import std::os + +// ── Constants ──────────────────────────────────────────────── + +comptime int VLESS_VERSION = 0 +comptime int CMD_TCP = 1 +comptime int CMD_UDP = 2 +comptime int ADDR_IPV4 = 1 +comptime int ADDR_DOMAIN = 2 +comptime int ADDR_IPV6 = 3 + +// ── Parsed VLESS request ──────────────────────────────────── + +struct VlessRequest: + int command + string address + int port + int payload_offset + bytes client_uuid + + constructor(): + this.command = 0 + this.address = "" + this.port = 0 + this.payload_offset = 0 + this.client_uuid = bytes::create(0) + +// ── Parse VLESS request ───────────────────────────────────── + +VlessRequest fn parse_request(bytes data): + // Minimum: 1(ver) + 16(uuid) + 1(addons_len) + 1(cmd) + 2(port) + 1(addr_type) + 1(addr) = 23 + if data.len() < 23: + throw "VLESS: request too short" + + VlessRequest req = VlessRequest() + int offset = 0 + + // Version + int version = data.get(offset) + offset = offset + 1 + + // UUID (16 bytes) + req.client_uuid = data.slice(offset, offset + 16) + offset = offset + 16 + + // Addons + int addons_len = data.get(offset) + offset = offset + 1 + offset = offset + addons_len + + // Command + req.command = data.get(offset) + offset = offset + 1 + + // Port (big-endian u16) + req.port = data.read_u16_be(offset) + offset = offset + 2 + + // Address type + int addr_type = data.get(offset) + offset = offset + 1 + + if addr_type == ADDR_IPV4: + // 4 bytes → "a.b.c.d" + int a = data.get(offset) + int b = data.get(offset + 1) + int c = data.get(offset + 2) + int d = data.get(offset + 3) + req.address = "${a}.${b}.${c}.${d}" + offset = offset + 4 + elif addr_type == ADDR_DOMAIN: + int domain_len = data.get(offset) + offset = offset + 1 + bytes domain_bytes = data.slice(offset, offset + domain_len) + req.address = domain_bytes.to_string() + offset = offset + domain_len + elif addr_type == ADDR_IPV6: + bytes ipv6 = data.slice(offset, offset + 16) + req.address = ipv6.to_hex() + offset = offset + 16 + else: + throw "VLESS: unknown address type ${addr_type}" + + req.payload_offset = offset + return req + +// ── Build VLESS response ──────────────────────────────────── + +bytes fn build_response(): + // Response header: [version=0][addons_len=0] + bytes resp = bytes::create(2) + resp.set(0, 0) + resp.set(1, 0) + return resp + +// ── TCP relay ─────────────────────────────────────────────── + +void fn relay(int from_fd, int to_fd): + // Forward data from one fd to another until connection closes + while true: + bytes chunk = net::tcp_recv_bytes(from_fd, 8192) + if chunk.len() == 0: + break + net::tcp_send_bytes(to_fd, chunk) + +// ── Handle one VLESS connection ───────────────────────────── + +void fn handle_client(int client_fd, bytes auth_uuid): + // Read initial data (VLESS header + first payload chunk) + bytes data = net::tcp_recv_bytes(client_fd, 65536) + if data.len() == 0: + net::tcp_close(client_fd) + return + + // Parse VLESS request + VlessRequest req = VlessRequest() + try: + req = parse_request(data) + catch: + println("VLESS: failed to parse request") + net::tcp_close(client_fd) + return + + // Authenticate UUID + if uuid::equals(req.client_uuid, auth_uuid) == false: + println("VLESS: auth failed — invalid UUID") + net::tcp_close(client_fd) + return + + // Only TCP supported + if req.command != CMD_TCP: + println("VLESS: unsupported command ${req.command}") + net::tcp_close(client_fd) + return + + string dest_host = req.address + int dest_port = req.port + + println("[VLESS] ${dest_host}:${dest_port}") + + // Connect to destination + int remote_fd = 0 + try: + remote_fd = net::tcp_connect(dest_host, dest_port) + catch: + println("VLESS: failed to connect to ${dest_host}:${dest_port}") + net::tcp_close(client_fd) + return + + // Send VLESS response header + bytes resp_header = build_response() + net::tcp_send_bytes(client_fd, resp_header) + + // Send initial payload (data after VLESS header) to remote + if req.payload_offset < data.len(): + bytes initial_payload = data.slice(req.payload_offset, data.len()) + net::tcp_send_bytes(remote_fd, initial_payload) + + // Bidirectional relay using two threads + // Thread 1: remote → client + thread::Thread t = thread::spawn((): + try: + relay(remote_fd, client_fd) + catch: + pass + ) + + // Main: client → remote + try: + relay(client_fd, remote_fd) + catch: + pass + + // Cleanup + t.wait() + net::tcp_close(remote_fd) + net::tcp_close(client_fd) + +// ── Main ──────────────────────────────────────────────────── + +void fn main(): + vector[string] args = os::args() + + // Default config + string listen_host = "0.0.0.0" + int listen_port = 1080 + string uuid_str = "" + + // Parse args: vless [host:port] + if args.len() < 2: + println("Usage: vless [listen_host] [listen_port]") + println("Example: vless 550e8400-e29b-41d4-a716-446655440000") + println(" vless 550e8400-e29b-41d4-a716-446655440000 0.0.0.0 443") + os::exit(1) + + uuid_str = args.at(1) + + if args.len() >= 3: + listen_host = args.at(2) + if args.len() >= 4: + listen_port = __str_to_int(args.at(3)) + + // Parse auth UUID + bytes auth_uuid = uuid::parse(uuid_str) + println("VLESS server starting on ${listen_host}:${listen_port}") + println("Auth UUID: ${uuid_str}") + + // Listen + int server_fd = net::tcp_listen(listen_host, listen_port) + + // Accept loop + while true: + int client_fd = net::tcp_accept(server_fd) + // Handle each connection in a new thread + thread::Thread t = thread::spawn((): + handle_client(client_fd, auth_uuid) + ) + t.detach() diff --git a/runtime/lavina.h b/runtime/lavina.h index 86ef361..2a60eb5 100644 --- a/runtime/lavina.h +++ b/runtime/lavina.h @@ -11,6 +11,7 @@ #include "liblavina/os.h" #include "liblavina/util.h" #include "liblavina/math.h" +#include "liblavina/bytes.h" +#include "liblavina/uuid.h" #include "liblavina/net.h" #include "liblavina/thread.h" -#include "liblavina/bytes.h" diff --git a/runtime/liblavina/net.h b/runtime/liblavina/net.h index fd125a1..814bb36 100644 --- a/runtime/liblavina/net.h +++ b/runtime/liblavina/net.h @@ -128,6 +128,52 @@ inline std::string __net_tcp_recv(int64_t fd, int64_t max_bytes) { return std::string(buf.data(), static_cast(received)); } +// Send raw bytes on a connected TCP socket +// Returns number of bytes sent +inline int64_t __net_tcp_send_bytes(int64_t fd, const lv_bytes& data) { + auto sent = send(static_cast(fd), + reinterpret_cast(data.data()), + static_cast(data.size()), 0); + if (sent == LV_SOCKET_ERROR) { + throw std::runtime_error("tcp_send_bytes: send() failed"); + } + return static_cast(sent); +} + +// Receive raw bytes from a connected TCP socket +// Returns received data as bytes (empty on connection close) +inline lv_bytes __net_tcp_recv_bytes(int64_t fd, int64_t max_bytes) { + lv_bytes buf(static_cast(max_bytes)); + auto received = recv(static_cast(fd), + reinterpret_cast(buf.data()), + static_cast(buf.size()), 0); + if (received == LV_SOCKET_ERROR) { + throw std::runtime_error("tcp_recv_bytes: recv() failed"); + } + buf.resize(static_cast(received)); + return buf; +} + +// Receive exactly N bytes (blocks until all received or connection closes) +// Returns bytes received (may be shorter if connection closed early) +inline lv_bytes __net_tcp_recv_exact(int64_t fd, int64_t n) { + lv_bytes result; + result.reserve(static_cast(n)); + size_t remaining = static_cast(n); + while (remaining > 0) { + char tmp[4096]; + int chunk = static_cast(remaining < 4096 ? remaining : 4096); + auto received = recv(static_cast(fd), tmp, chunk, 0); + if (received == LV_SOCKET_ERROR) { + throw std::runtime_error("tcp_recv_exact: recv() failed"); + } + if (received == 0) break; // connection closed + result.insert(result.end(), tmp, tmp + received); + remaining -= static_cast(received); + } + return result; +} + // Close a TCP socket inline void __net_tcp_close(int64_t fd) { _lv_close_socket(static_cast(fd)); diff --git a/runtime/liblavina/uuid.h b/runtime/liblavina/uuid.h new file mode 100644 index 0000000..b7f6506 --- /dev/null +++ b/runtime/liblavina/uuid.h @@ -0,0 +1,56 @@ +#pragma once +#include "core.h" +#include "bytes.h" +#include + +// ── UUID v4 support ───────────────────────────────────────── +// UUID is represented as 16 bytes (lv_bytes). +// String format: "550e8400-e29b-41d4-a716-446655440000" + +// Parse UUID string (with or without dashes) → 16 bytes +inline lv_bytes __uuid_parse(const std::string& s) { + // Strip dashes + std::string hex; + hex.reserve(32); + for (char c : s) { + if (c != '-') hex.push_back(c); + } + if (hex.size() != 32) { + throw std::runtime_error("uuid_parse: invalid UUID string (expected 32 hex chars, got " + std::to_string(hex.size()) + ")"); + } + return __bytes_from_hex(hex); +} + +// Format 16 bytes → UUID string with dashes +inline std::string __uuid_to_string(const lv_bytes& b) { + if (b.size() != 16) { + throw std::runtime_error("uuid_to_string: expected 16 bytes, got " + std::to_string(b.size())); + } + std::string hex = __bytes_to_hex(b); + // Insert dashes: 8-4-4-4-12 + return hex.substr(0, 8) + "-" + hex.substr(8, 4) + "-" + + hex.substr(12, 4) + "-" + hex.substr(16, 4) + "-" + + hex.substr(20, 12); +} + +// Generate random UUID v4 +inline lv_bytes __uuid_random() { + static thread_local std::mt19937 rng(std::random_device{}()); + std::uniform_int_distribution dist(0, 255); + + lv_bytes uuid(16); + for (int i = 0; i < 16; i++) { + uuid[i] = static_cast(dist(rng)); + } + // Set version 4: byte 6 high nibble = 0100 + uuid[6] = (uuid[6] & 0x0F) | 0x40; + // Set variant 1: byte 8 high bits = 10xx + uuid[8] = (uuid[8] & 0x3F) | 0x80; + return uuid; +} + +// Compare two UUIDs +inline bool __uuid_equals(const lv_bytes& a, const lv_bytes& b) { + if (a.size() != 16 || b.size() != 16) return false; + return a == b; +} diff --git a/runtime/std/net.lv b/runtime/std/net.lv index d1fe39e..37d48d7 100644 --- a/runtime/std/net.lv +++ b/runtime/std/net.lv @@ -20,6 +20,15 @@ public string fn tcp_recv(int fd, int max_bytes): public void fn tcp_close(int fd): __net_tcp_close(fd) +public int fn tcp_send_bytes(int fd, bytes data): + return __net_tcp_send_bytes(fd, data) + +public bytes fn tcp_recv_bytes(int fd, int max_bytes): + return __net_tcp_recv_bytes(fd, max_bytes) + +public bytes fn tcp_recv_exact(int fd, int n): + return __net_tcp_recv_exact(fd, n) + // ── UDP ────────────────────────────────────────────────────── public int fn udp_create(string host, int port): @@ -53,6 +62,15 @@ public struct TcpStream: string fn recv(int max_bytes): return __net_tcp_recv(this.fd, max_bytes) + int fn send_bytes(bytes data): + return __net_tcp_send_bytes(this.fd, data) + + bytes fn recv_bytes(int max_bytes): + return __net_tcp_recv_bytes(this.fd, max_bytes) + + bytes fn recv_exact(int n): + return __net_tcp_recv_exact(this.fd, n) + void fn close(): __net_tcp_close(this.fd) diff --git a/runtime/std/uuid.lv b/runtime/std/uuid.lv new file mode 100644 index 0000000..5f5698a --- /dev/null +++ b/runtime/std/uuid.lv @@ -0,0 +1,13 @@ +// std::uuid — UUID v4 generation and parsing + +public bytes fn parse(string s): + return __uuid_parse(s) + +public string fn to_string(bytes uuid_bytes): + return __uuid_to_string(uuid_bytes) + +public bytes fn random(): + return __uuid_random() + +public bool fn equals(bytes a, bytes b): + return __uuid_equals(a, b) diff --git a/tests/test_uuid.lv b/tests/test_uuid.lv new file mode 100644 index 0000000..7fc2718 --- /dev/null +++ b/tests/test_uuid.lv @@ -0,0 +1,63 @@ +import std::uuid +import std::bytes + +void fn test_parse(): + bytes id = uuid::parse("550e8400-e29b-41d4-a716-446655440000") + lv_assert(id.len() == 16, "uuid is 16 bytes") + lv_assert(id.get(0) == 0x55, "byte 0") + lv_assert(id.get(1) == 0x0e, "byte 1") + lv_assert(id.get(15) == 0x00, "byte 15") + print("PASS: parse") + +void fn test_parse_no_dashes(): + bytes id = uuid::parse("550e8400e29b41d4a716446655440000") + lv_assert(id.len() == 16, "no-dash uuid is 16 bytes") + lv_assert(id.get(0) == 0x55, "no-dash byte 0") + print("PASS: parse no dashes") + +void fn test_to_string(): + bytes id = uuid::parse("550e8400-e29b-41d4-a716-446655440000") + string s = uuid::to_string(id) + lv_assert(s == "550e8400-e29b-41d4-a716-446655440000", "to_string roundtrip") + print("PASS: to_string") + +void fn test_random(): + bytes id1 = uuid::random() + bytes id2 = uuid::random() + lv_assert(id1.len() == 16, "random uuid is 16 bytes") + lv_assert(id2.len() == 16, "random uuid2 is 16 bytes") + // Version nibble (byte 6 high nibble) must be 4 + int version_byte = id1.get(6) + int version = version_byte / 16 + lv_assert(version == 4, "version is 4") + // Variant (byte 8 high 2 bits) must be 10 + int variant_byte = id1.get(8) + int variant = variant_byte / 64 + lv_assert(variant == 2, "variant is 10xx") + // Two random UUIDs should be different + lv_assert(uuid::equals(id1, id2) == false, "random uuids differ") + print("PASS: random") + +void fn test_equals(): + bytes a = uuid::parse("550e8400-e29b-41d4-a716-446655440000") + bytes b = uuid::parse("550e8400-e29b-41d4-a716-446655440000") + bytes c = uuid::parse("00000000-0000-0000-0000-000000000000") + lv_assert(uuid::equals(a, b) == true, "same uuids equal") + lv_assert(uuid::equals(a, c) == false, "different uuids not equal") + print("PASS: equals") + +void fn test_roundtrip(): + bytes id = uuid::random() + string s = uuid::to_string(id) + bytes id2 = uuid::parse(s) + lv_assert(uuid::equals(id, id2) == true, "random->string->parse roundtrip") + print("PASS: roundtrip") + +void fn main(): + test_parse() + test_parse_no_dashes() + test_to_string() + test_random() + test_equals() + test_roundtrip() + println("All UUID tests passed!") From 82f5410fd81ef2fd1df82e415404924890c33c31 Mon Sep 17 00:00:00 2001 From: Raumberg Date: Tue, 17 Feb 2026 16:21:10 +0300 Subject: [PATCH 13/19] src: improved casting, improved memory management in vless.lv --- examples/complex/vless/test_vless.lv | 4 +- examples/complex/vless/vless.lv | 6 +- runtime/std/bytes.lv | 12 +- runtime/std/net.lv | 8 +- runtime/std/uuid.lv | 6 +- src/codegen.lv | 96 ++ stages/stage-latest.cpp | 1209 +++++++++++++++----------- tests/test_cast.lv | 75 ++ 8 files changed, 878 insertions(+), 538 deletions(-) diff --git a/examples/complex/vless/test_vless.lv b/examples/complex/vless/test_vless.lv index 2e254ce..81727e6 100644 --- a/examples/complex/vless/test_vless.lv +++ b/examples/complex/vless/test_vless.lv @@ -35,7 +35,7 @@ struct VlessRequest: // ── Parse / build ─────────────────────────────────────────── -VlessRequest fn parse_request(bytes data): +VlessRequest fn parse_request(ref bytes data): if data.len() < 23: throw "VLESS: request too short" VlessRequest req = VlessRequest() @@ -84,7 +84,7 @@ void fn relay(int from_fd, int to_fd): break net::tcp_send_bytes(to_fd, chunk) -void fn handle_vless_client(int client_fd, bytes auth_uuid): +void fn handle_vless_client(int client_fd, ref bytes auth_uuid): bytes data = net::tcp_recv_bytes(client_fd, 65536) if data.len() == 0: net::tcp_close(client_fd) diff --git a/examples/complex/vless/vless.lv b/examples/complex/vless/vless.lv index 4e81cca..3445909 100644 --- a/examples/complex/vless/vless.lv +++ b/examples/complex/vless/vless.lv @@ -44,7 +44,7 @@ struct VlessRequest: // ── Parse VLESS request ───────────────────────────────────── -VlessRequest fn parse_request(bytes data): +VlessRequest fn parse_request(ref bytes data): // Minimum: 1(ver) + 16(uuid) + 1(addons_len) + 1(cmd) + 2(port) + 1(addr_type) + 1(addr) = 23 if data.len() < 23: throw "VLESS: request too short" @@ -122,7 +122,7 @@ void fn relay(int from_fd, int to_fd): // ── Handle one VLESS connection ───────────────────────────── -void fn handle_client(int client_fd, bytes auth_uuid): +void fn handle_client(int client_fd, ref bytes auth_uuid): // Read initial data (VLESS header + first payload chunk) bytes data = net::tcp_recv_bytes(client_fd, 65536) if data.len() == 0: @@ -215,7 +215,7 @@ void fn main(): if args.len() >= 3: listen_host = args.at(2) if args.len() >= 4: - listen_port = __str_to_int(args.at(3)) + listen_port = args.at(3) as int // Parse auth UUID bytes auth_uuid = uuid::parse(uuid_str) diff --git a/runtime/std/bytes.lv b/runtime/std/bytes.lv index b2bab53..c3c33ee 100644 --- a/runtime/std/bytes.lv +++ b/runtime/std/bytes.lv @@ -20,10 +20,10 @@ extend bytes: void fn fill(int val): __bytes_fill(this, val) - bool fn equals(bytes other): + bool fn equals(ref bytes other): return __bytes_equals(this, other) - bytes fn concat(bytes other): + bytes fn concat(ref bytes other): return __bytes_concat(this, other) // Big-endian @@ -57,14 +57,14 @@ extend bytes: public bytes fn create(int size): return __bytes_create(size) -public bytes fn from_string(string s): +public bytes fn from_string(ref string s): return __bytes_from_string(s) -public bytes fn from_hex(string hex): +public bytes fn from_hex(ref string hex): return __bytes_from_hex(hex) -public bytes fn concat(bytes a, bytes b): +public bytes fn concat(ref bytes a, ref bytes b): return __bytes_concat(a, b) -public bool fn equals(bytes a, bytes b): +public bool fn equals(ref bytes a, ref bytes b): return __bytes_equals(a, b) diff --git a/runtime/std/net.lv b/runtime/std/net.lv index 37d48d7..f5c877a 100644 --- a/runtime/std/net.lv +++ b/runtime/std/net.lv @@ -11,7 +11,7 @@ public int fn tcp_accept(int server_fd): public int fn tcp_connect(string host, int port): return __net_tcp_connect(host, port) -public int fn tcp_send(int fd, string data): +public int fn tcp_send(int fd, ref string data): return __net_tcp_send(fd, data) public string fn tcp_recv(int fd, int max_bytes): @@ -20,7 +20,7 @@ public string fn tcp_recv(int fd, int max_bytes): public void fn tcp_close(int fd): __net_tcp_close(fd) -public int fn tcp_send_bytes(int fd, bytes data): +public int fn tcp_send_bytes(int fd, ref bytes data): return __net_tcp_send_bytes(fd, data) public bytes fn tcp_recv_bytes(int fd, int max_bytes): @@ -56,13 +56,13 @@ public struct TcpStream: constructor(int fd): this.fd = fd - int fn send(string data): + int fn send(ref string data): return __net_tcp_send(this.fd, data) string fn recv(int max_bytes): return __net_tcp_recv(this.fd, max_bytes) - int fn send_bytes(bytes data): + int fn send_bytes(ref bytes data): return __net_tcp_send_bytes(this.fd, data) bytes fn recv_bytes(int max_bytes): diff --git a/runtime/std/uuid.lv b/runtime/std/uuid.lv index 5f5698a..9bfab77 100644 --- a/runtime/std/uuid.lv +++ b/runtime/std/uuid.lv @@ -1,13 +1,13 @@ // std::uuid — UUID v4 generation and parsing -public bytes fn parse(string s): +public bytes fn parse(ref string s): return __uuid_parse(s) -public string fn to_string(bytes uuid_bytes): +public string fn to_string(ref bytes uuid_bytes): return __uuid_to_string(uuid_bytes) public bytes fn random(): return __uuid_random() -public bool fn equals(bytes a, bytes b): +public bool fn equals(ref bytes a, ref bytes b): return __uuid_equals(a, b) diff --git a/src/codegen.lv b/src/codegen.lv index 1925f4c..f28ce8f 100644 --- a/src/codegen.lv +++ b/src/codegen.lv @@ -423,6 +423,39 @@ class CppCodegen: string t = this.emit_type(target_type) if this.is_dynamic_expression(expr): return "std::any_cast<${t}>(${ex})" + TypeNode src = this.infer_source_type(expr) + // string -> int/float + if this.is_string_type(src): + if this.is_integer_type(target_type): + match target_type: + Int(): + return "__str_to_int(${ex})" + _: + return "static_cast<${t}>(__str_to_int(${ex}))" + if this.is_float_type(target_type): + match target_type: + Float(): + return "__str_to_float(${ex})" + _: + return "static_cast<${t}>(__str_to_float(${ex}))" + if this.is_bytes_type(target_type): + return "__bytes_from_string(${ex})" + // numeric/bool -> string + if this.is_string_type(target_type): + if this.is_integer_type(src) or this.is_float_type(src): + return "to_string(${ex})" + match src: + Bool(): + return "to_string(${ex})" + _: + pass + // bytes -> string + if this.is_bytes_type(src) and this.is_string_type(target_type): + return "__bytes_to_string(${ex})" + // string -> bytes + if this.is_string_type(src) and this.is_bytes_type(target_type): + return "__bytes_from_string(${ex})" + // bytes <-> string (already handled above but keep for safety) return "static_cast<${t}>(${ex})" Throw(expr): return "throw std::runtime_error(${this.emit_expr(expr, m)})" @@ -606,6 +639,69 @@ class CppCodegen: pass return "" + TypeNode fn infer_source_type(ref Expr e): + match e: + Literal(kind, value): + if kind == "int": + return TypeNode::Int() + elif kind == "float": + return TypeNode::Float() + elif kind == "string": + return TypeNode::Str() + elif kind == "bool": + return TypeNode::Bool() + return TypeNode::Auto() + Variable(tok): + if this.var_types.has(tok.lexeme): + return this.var_types[tok.lexeme] + return TypeNode::Auto() + Grouping(inner): + return this.infer_source_type(inner) + Cast(expr, target_type): + return target_type + Unary(op, right): + return this.infer_source_type(right) + _: + return TypeNode::Auto() + + bool fn is_integer_type(ref TypeNode t): + match t: + Int(): + return true + Int8(): + return true + Int16(): + return true + Int32(): + return true + USize(): + return true + _: + return false + + bool fn is_float_type(ref TypeNode t): + match t: + Float(): + return true + Float32(): + return true + _: + return false + + bool fn is_string_type(ref TypeNode t): + match t: + Str(): + return true + _: + return false + + bool fn is_bytes_type(ref TypeNode t): + match t: + Bytes(): + return true + _: + return false + string fn try_extend_method(ref Expr object, ref string method, ref string obj, ref vector[string] args): string type_cat = this.get_type_category(object) if type_cat != "" and this.extend_methods.has(type_cat): diff --git a/stages/stage-latest.cpp b/stages/stage-latest.cpp index 6ca8a59..c1b4478 100644 --- a/stages/stage-latest.cpp +++ b/stages/stage-latest.cpp @@ -1995,6 +1995,54 @@ struct CppCodegen { if ((*this).is_dynamic_expression(expr)) { return ((((std::string("std::any_cast<") + (t)) + std::string(">(")) + (ex)) + std::string(")")); } + TypeNode src = (*this).infer_source_type(expr); + if ((*this).is_string_type(src)) { + if ((*this).is_integer_type(target_type)) { + { + const auto& _match_21 = target_type; + if (std::holds_alternative::Int>(_match_21._data)) { + return ((std::string("__str_to_int(") + (ex)) + std::string(")")); + } + else { + return ((((std::string("static_cast<") + (t)) + std::string(">(__str_to_int(")) + (ex)) + std::string("))")); + } + } + } + if ((*this).is_float_type(target_type)) { + { + const auto& _match_22 = target_type; + if (std::holds_alternative::Float>(_match_22._data)) { + return ((std::string("__str_to_float(") + (ex)) + std::string(")")); + } + else { + return ((((std::string("static_cast<") + (t)) + std::string(">(__str_to_float(")) + (ex)) + std::string("))")); + } + } + } + if ((*this).is_bytes_type(target_type)) { + return ((std::string("__bytes_from_string(") + (ex)) + std::string(")")); + } + } + if ((*this).is_string_type(target_type)) { + if ((*this).is_integer_type(src) || (*this).is_float_type(src)) { + return ((std::string("to_string(") + (ex)) + std::string(")")); + } + { + const auto& _match_23 = src; + if (std::holds_alternative::Bool>(_match_23._data)) { + return ((std::string("to_string(") + (ex)) + std::string(")")); + } + else { + /* pass */ + } + } + } + if ((*this).is_bytes_type(src) && (*this).is_string_type(target_type)) { + return ((std::string("__bytes_to_string(") + (ex)) + std::string(")")); + } + if ((*this).is_string_type(src) && (*this).is_bytes_type(target_type)) { + return ((std::string("__bytes_from_string(") + (ex)) + std::string(")")); + } return ((((std::string("static_cast<") + (t)) + std::string(">(")) + (ex)) + std::string(")")); } else if (std::holds_alternative::Throw>(_match_15._data)) { @@ -2059,14 +2107,14 @@ struct CppCodegen { bool is_dynamic_expression(const Expr& e) { { - const auto& _match_21 = e; - if (std::holds_alternative::Variable>(_match_21._data)) { - auto& _v = std::get::Variable>(_match_21._data); + const auto& _match_24 = e; + if (std::holds_alternative::Variable>(_match_24._data)) { + auto& _v = std::get::Variable>(_match_24._data); auto& name = _v.name; return (*this).is_dynamic_var(name.lexeme); } - else if (std::holds_alternative::Index>(_match_21._data)) { - auto& _v = std::get::Index>(_match_21._data); + else if (std::holds_alternative::Index>(_match_24._data)) { + auto& _v = std::get::Index>(_match_24._data); auto& object = *_v.object; auto& bracket = _v.bracket; auto& index = *_v.index; @@ -2080,9 +2128,9 @@ struct CppCodegen { std::string emit_call_expr(const Expr& callee, const std::vector& args, bool in_method) { { - const auto& _match_22 = callee; - if (std::holds_alternative::Get>(_match_22._data)) { - auto& _v = std::get::Get>(_match_22._data); + const auto& _match_25 = callee; + if (std::holds_alternative::Get>(_match_25._data)) { + auto& _v = std::get::Get>(_match_25._data); auto& object = *_v.object; auto& name = _v.name; std::string obj = (*this).emit_expr(object, in_method); @@ -2100,8 +2148,8 @@ struct CppCodegen { } return ((((((std::string("") + (obj)) + std::string(".")) + (name.lexeme)) + std::string("(")) + (lv_join(arg_strs, std::string(", ")))) + std::string(")")); } - else if (std::holds_alternative::StaticGet>(_match_22._data)) { - auto& _v = std::get::StaticGet>(_match_22._data); + else if (std::holds_alternative::StaticGet>(_match_25._data)) { + auto& _v = std::get::StaticGet>(_match_25._data); auto& object = *_v.object; auto& name = _v.name; std::string obj = (*this).emit_expr(object, in_method); @@ -2110,9 +2158,9 @@ struct CppCodegen { arg_strs.push_back((*this).emit_expr(a, in_method)); } { - const auto& _match_23 = object; - if (std::holds_alternative::Variable>(_match_23._data)) { - auto& _v = std::get::Variable>(_match_23._data); + const auto& _match_26 = object; + if (std::holds_alternative::Variable>(_match_26._data)) { + auto& _v = std::get::Variable>(_match_26._data); auto& tok = _v.name; if ((*this).is_known_enum(tok.lexeme)) { return ((((((std::string("") + (obj)) + std::string("::make_")) + (name.lexeme)) + std::string("(")) + (lv_join(arg_strs, std::string(", ")))) + std::string(")")); @@ -2124,8 +2172,8 @@ struct CppCodegen { } return ((((((std::string("") + (obj)) + std::string("::")) + (name.lexeme)) + std::string("(")) + (lv_join(arg_strs, std::string(", ")))) + std::string(")")); } - else if (std::holds_alternative::Variable>(_match_22._data)) { - auto& _v = std::get::Variable>(_match_22._data); + else if (std::holds_alternative::Variable>(_match_25._data)) { + auto& _v = std::get::Variable>(_match_25._data); auto& tok = _v.name; std::vector arg_strs = {}; for (const auto& a : args) { @@ -2330,38 +2378,38 @@ struct CppCodegen { std::string get_type_category(const Expr& object) { { - const auto& _match_24 = object; - if (std::holds_alternative::Variable>(_match_24._data)) { - auto& _v = std::get::Variable>(_match_24._data); + const auto& _match_27 = object; + if (std::holds_alternative::Variable>(_match_27._data)) { + auto& _v = std::get::Variable>(_match_27._data); auto& tok = _v.name; if ((this->var_types.count(tok.lexeme) > 0)) { TypeNode t = this->var_types[tok.lexeme]; { - const auto& _match_25 = t; - if (std::holds_alternative::Array>(_match_25._data)) { - auto& _v = std::get::Array>(_match_25._data); + const auto& _match_28 = t; + if (std::holds_alternative::Array>(_match_28._data)) { + auto& _v = std::get::Array>(_match_28._data); auto& inner = *_v.inner; return std::string("vector"); } - else if (std::holds_alternative::HashMap>(_match_25._data)) { - auto& _v = std::get::HashMap>(_match_25._data); + else if (std::holds_alternative::HashMap>(_match_28._data)) { + auto& _v = std::get::HashMap>(_match_28._data); auto& k = *_v.key_type; auto& v = *_v.value_type; return std::string("hashmap"); } - else if (std::holds_alternative::HashSet>(_match_25._data)) { - auto& _v = std::get::HashSet>(_match_25._data); + else if (std::holds_alternative::HashSet>(_match_28._data)) { + auto& _v = std::get::HashSet>(_match_28._data); auto& inner = *_v.inner; return std::string("hashset"); } - else if (std::holds_alternative::Str>(_match_25._data)) { + else if (std::holds_alternative::Str>(_match_28._data)) { return std::string("string"); } - else if (std::holds_alternative::Bytes>(_match_25._data)) { + else if (std::holds_alternative::Bytes>(_match_28._data)) { return std::string("bytes"); } - else if (std::holds_alternative::Custom>(_match_25._data)) { - auto& _v = std::get::Custom>(_match_25._data); + else if (std::holds_alternative::Custom>(_match_28._data)) { + auto& _v = std::get::Custom>(_match_28._data); auto& name = _v.name; auto& type_args = _v.type_args; return name; @@ -2379,15 +2427,136 @@ struct CppCodegen { return std::string(""); } + TypeNode infer_source_type(const Expr& e) { + { + const auto& _match_29 = e; + if (std::holds_alternative::Literal>(_match_29._data)) { + auto& _v = std::get::Literal>(_match_29._data); + auto& kind = _v.kind; + auto& value = _v.value; + if ((kind == std::string("int"))) { + return TypeNode::make_Int(); + } + else { + if ((kind == std::string("float"))) { + return TypeNode::make_Float(); + } + else { + if ((kind == std::string("string"))) { + return TypeNode::make_Str(); + } + else { + if ((kind == std::string("bool"))) { + return TypeNode::make_Bool(); + } + } + } + } + return TypeNode::make_Auto(); + } + else if (std::holds_alternative::Variable>(_match_29._data)) { + auto& _v = std::get::Variable>(_match_29._data); + auto& tok = _v.name; + if ((this->var_types.count(tok.lexeme) > 0)) { + return this->var_types[tok.lexeme]; + } + return TypeNode::make_Auto(); + } + else if (std::holds_alternative::Grouping>(_match_29._data)) { + auto& _v = std::get::Grouping>(_match_29._data); + auto& inner = *_v.inner; + return (*this).infer_source_type(inner); + } + else if (std::holds_alternative::Cast>(_match_29._data)) { + auto& _v = std::get::Cast>(_match_29._data); + auto& expr = *_v.expr; + auto& target_type = _v.target_type; + return target_type; + } + else if (std::holds_alternative::Unary>(_match_29._data)) { + auto& _v = std::get::Unary>(_match_29._data); + auto& op = _v.op; + auto& right = *_v.right; + return (*this).infer_source_type(right); + } + else { + return TypeNode::make_Auto(); + } + } + } + + bool is_integer_type(const TypeNode& t) { + { + const auto& _match_30 = t; + if (std::holds_alternative::Int>(_match_30._data)) { + return true; + } + else if (std::holds_alternative::Int8>(_match_30._data)) { + return true; + } + else if (std::holds_alternative::Int16>(_match_30._data)) { + return true; + } + else if (std::holds_alternative::Int32>(_match_30._data)) { + return true; + } + else if (std::holds_alternative::USize>(_match_30._data)) { + return true; + } + else { + return false; + } + } + } + + bool is_float_type(const TypeNode& t) { + { + const auto& _match_31 = t; + if (std::holds_alternative::Float>(_match_31._data)) { + return true; + } + else if (std::holds_alternative::Float32>(_match_31._data)) { + return true; + } + else { + return false; + } + } + } + + bool is_string_type(const TypeNode& t) { + { + const auto& _match_32 = t; + if (std::holds_alternative::Str>(_match_32._data)) { + return true; + } + else { + return false; + } + } + } + + bool is_bytes_type(const TypeNode& t) { + { + const auto& _match_33 = t; + if (std::holds_alternative::Bytes>(_match_33._data)) { + return true; + } + else { + return false; + } + } + } + std::string try_extend_method(const Expr& object, const std::string& method, const std::string& obj, const std::vector& args) { std::string type_cat = (*this).get_type_category(object); if ((type_cat != std::string("")) && (this->extend_methods.count(type_cat) > 0)) { std::vector methods = this->extend_methods[type_cat]; for (const auto& ext_m : methods) { { - const auto& _match_26 = ext_m; - if (std::holds_alternative::Function>(_match_26._data)) { - auto& _v = std::get::Function>(_match_26._data); + const auto& _match_34 = ext_m; + if (std::holds_alternative::Function>(_match_34._data)) { + auto& _v = std::get::Function>(_match_34._data); auto& mname = _v.name; auto& mparams = _v.params; auto& mret = _v.return_type; @@ -2415,9 +2584,9 @@ struct CppCodegen { void emit_extend_method(const std::string& type_key, const Stmt& method) { { - const auto& _match_27 = method; - if (std::holds_alternative::Function>(_match_27._data)) { - auto& _v = std::get::Function>(_match_27._data); + const auto& _match_35 = method; + if (std::holds_alternative::Function>(_match_35._data)) { + auto& _v = std::get::Function>(_match_35._data); auto& mname = _v.name; auto& mparams = _v.params; auto& mret = _v.return_type; @@ -2469,8 +2638,8 @@ struct CppCodegen { this->var_types[p.name.lexeme] = p.param_type; if (track_dynamic) { { - const auto& _match_28 = p.param_type; - if (std::holds_alternative::Dynamic>(_match_28._data)) { + const auto& _match_36 = p.param_type; + if (std::holds_alternative::Dynamic>(_match_36._data)) { (*this).add_dynamic_var(p.name.lexeme); } else { @@ -2505,17 +2674,17 @@ struct CppCodegen { void emit_stmt(const Stmt& s, bool m) { { - const auto& _match_29 = s; - if (_match_29._tag == "None") { + const auto& _match_37 = s; + if (_match_37._tag == "None") { /* pass */ } - else if (std::holds_alternative::ExprStmt>(_match_29._data)) { - auto& _v = std::get::ExprStmt>(_match_29._data); + else if (std::holds_alternative::ExprStmt>(_match_37._data)) { + auto& _v = std::get::ExprStmt>(_match_37._data); auto& expr = _v.expr; this->output = (this->output + ((((std::string("") + ((*this).indent())) + std::string("")) + ((*this).emit_expr(expr, m))) + std::string(";\n"))); } - else if (std::holds_alternative::Let>(_match_29._data)) { - auto& _v = std::get::Let>(_match_29._data); + else if (std::holds_alternative::Let>(_match_37._data)) { + auto& _v = std::get::Let>(_match_37._data); auto& name = _v.name; auto& var_type = _v.var_type; auto& initializer = _v.initializer; @@ -2524,8 +2693,8 @@ struct CppCodegen { auto& is_mut = _v.is_mut; (*this).emit_let(name, var_type, initializer, m, is_ref, is_mut); } - else if (std::holds_alternative::Const>(_match_29._data)) { - auto& _v = std::get::Const>(_match_29._data); + else if (std::holds_alternative::Const>(_match_37._data)) { + auto& _v = std::get::Const>(_match_37._data); auto& name = _v.name; auto& const_type = _v.const_type; auto& value = _v.value; @@ -2539,13 +2708,13 @@ struct CppCodegen { } this->output = (this->output + ((((((((((std::string("") + ((*this).indent())) + std::string("")) + (prefix)) + std::string("")) + (cpp_type)) + std::string(" ")) + (name.lexeme)) + std::string(" = ")) + (val)) + std::string(";\n"))); } - else if (std::holds_alternative::Return>(_match_29._data)) { - auto& _v = std::get::Return>(_match_29._data); + else if (std::holds_alternative::Return>(_match_37._data)) { + auto& _v = std::get::Return>(_match_37._data); auto& keyword = _v.keyword; auto& value = _v.value; { - const auto& _match_30 = value; - if (_match_30._tag == "None") { + const auto& _match_38 = value; + if (_match_38._tag == "None") { this->output = (this->output + ((std::string("") + ((*this).indent())) + std::string("return;\n"))); } else { @@ -2553,16 +2722,16 @@ struct CppCodegen { } } } - else if (std::holds_alternative::If>(_match_29._data)) { - auto& _v = std::get::If>(_match_29._data); + else if (std::holds_alternative::If>(_match_37._data)) { + auto& _v = std::get::If>(_match_37._data); auto& condition = _v.condition; auto& then_branch = *_v.then_branch; auto& else_branch = *_v.else_branch; this->output = (this->output + ((((std::string("") + ((*this).indent())) + std::string("if (")) + ((*this).emit_expr(condition, m))) + std::string(") "))); (*this).emit_block_or_stmt(then_branch, m); { - const auto& _match_31 = else_branch; - if (_match_31._tag == "None") { + const auto& _match_39 = else_branch; + if (_match_39._tag == "None") { /* pass */ } else { @@ -2571,24 +2740,24 @@ struct CppCodegen { } } } - else if (std::holds_alternative::While>(_match_29._data)) { - auto& _v = std::get::While>(_match_29._data); + else if (std::holds_alternative::While>(_match_37._data)) { + auto& _v = std::get::While>(_match_37._data); auto& condition = _v.condition; auto& body = *_v.body; this->output = (this->output + ((((std::string("") + ((*this).indent())) + std::string("while (")) + ((*this).emit_expr(condition, m))) + std::string(") "))); (*this).emit_block_or_stmt(body, m); } - else if (std::holds_alternative::For>(_match_29._data)) { - auto& _v = std::get::For>(_match_29._data); + else if (std::holds_alternative::For>(_match_37._data)) { + auto& _v = std::get::For>(_match_37._data); auto& item_name = _v.item_name; auto& collection = _v.collection; auto& body = *_v.body; auto& is_ref = _v.is_ref; auto& is_mut = _v.is_mut; { - const auto& _match_32 = collection; - if (std::holds_alternative::Range>(_match_32._data)) { - auto& _v = std::get::Range>(_match_32._data); + const auto& _match_40 = collection; + if (std::holds_alternative::Range>(_match_40._data)) { + auto& _v = std::get::Range>(_match_40._data); auto& start = *_v.start; auto& end = *_v.end; this->output = (this->output + ((((((((((((std::string("") + ((*this).indent())) + std::string("for (int64_t ")) + (item_name.lexeme)) + std::string(" = ")) + ((*this).emit_expr(start, m))) + std::string("; ")) + (item_name.lexeme)) + std::string(" < ")) + ((*this).emit_expr(end, m))) + std::string("; ")) + (item_name.lexeme)) + std::string("++) "))); @@ -2596,9 +2765,9 @@ struct CppCodegen { } else { { - const auto& _match_33 = collection; - if (std::holds_alternative::Variable>(_match_33._data)) { - auto& _v = std::get::Variable>(_match_33._data); + const auto& _match_41 = collection; + if (std::holds_alternative::Variable>(_match_41._data)) { + auto& _v = std::get::Variable>(_match_41._data); auto& tok = _v.name; if ((*this).is_dynamic_var(tok.lexeme)) { (*this).add_dynamic_var(item_name.lexeme); @@ -2622,8 +2791,8 @@ struct CppCodegen { } } } - else if (std::holds_alternative::Block>(_match_29._data)) { - auto& _v = std::get::Block>(_match_29._data); + else if (std::holds_alternative::Block>(_match_37._data)) { + auto& _v = std::get::Block>(_match_37._data); auto& statements = _v.statements; this->output = (this->output + ((std::string("") + ((*this).indent())) + std::string("{\n"))); this->indent_level = (this->indent_level + INT64_C(1)); @@ -2633,8 +2802,8 @@ struct CppCodegen { this->indent_level = (this->indent_level - INT64_C(1)); this->output = (this->output + ((std::string("") + ((*this).indent())) + std::string("}\n"))); } - else if (std::holds_alternative::Try>(_match_29._data)) { - auto& _v = std::get::Try>(_match_29._data); + else if (std::holds_alternative::Try>(_match_37._data)) { + auto& _v = std::get::Try>(_match_37._data); auto& try_body = *_v.try_body; auto& catch_body = *_v.catch_body; auto& exception_name = _v.exception_name; @@ -2647,8 +2816,8 @@ struct CppCodegen { this->output = (this->output + ((((std::string("") + ((*this).indent())) + std::string(" catch (const std::exception& ")) + (exc_name)) + std::string(") "))); (*this).emit_block_or_stmt(catch_body, m); } - else if (std::holds_alternative::Function>(_match_29._data)) { - auto& _v = std::get::Function>(_match_29._data); + else if (std::holds_alternative::Function>(_match_37._data)) { + auto& _v = std::get::Function>(_match_37._data); auto& name = _v.name; auto& params = _v.params; auto& return_type = _v.return_type; @@ -2660,24 +2829,24 @@ struct CppCodegen { auto& type_params = _v.type_params; (*this).emit_function(name, params, return_type, body, type_params, comptime_mode); } - else if (std::holds_alternative::Class>(_match_29._data)) { - auto& _v = std::get::Class>(_match_29._data); + else if (std::holds_alternative::Class>(_match_37._data)) { + auto& _v = std::get::Class>(_match_37._data); auto& name = _v.name; auto& body = _v.body; auto& visibility = _v.visibility; std::vector empty_tp = {}; (*this).emit_class(name, body, empty_tp); } - else if (std::holds_alternative::Struct>(_match_29._data)) { - auto& _v = std::get::Struct>(_match_29._data); + else if (std::holds_alternative::Struct>(_match_37._data)) { + auto& _v = std::get::Struct>(_match_37._data); auto& name = _v.name; auto& body = _v.body; auto& visibility = _v.visibility; auto& type_params = _v.type_params; (*this).emit_class(name, body, type_params); } - else if (std::holds_alternative::Enum>(_match_29._data)) { - auto& _v = std::get::Enum>(_match_29._data); + else if (std::holds_alternative::Enum>(_match_37._data)) { + auto& _v = std::get::Enum>(_match_37._data); auto& name = _v.name; auto& variants = _v.variants; auto& methods = _v.methods; @@ -2685,43 +2854,43 @@ struct CppCodegen { auto& type_params = _v.type_params; (*this).emit_enum(name, variants, methods, type_params); } - else if (std::holds_alternative::Match>(_match_29._data)) { - auto& _v = std::get::Match>(_match_29._data); + else if (std::holds_alternative::Match>(_match_37._data)) { + auto& _v = std::get::Match>(_match_37._data); auto& expr = _v.expr; auto& arm_patterns = _v.arm_patterns; auto& arm_bodies = _v.arm_bodies; (*this).emit_match_impl(expr, arm_patterns, arm_bodies, m); } - else if (std::holds_alternative::Namespace>(_match_29._data)) { - auto& _v = std::get::Namespace>(_match_29._data); + else if (std::holds_alternative::Namespace>(_match_37._data)) { + auto& _v = std::get::Namespace>(_match_37._data); auto& name = _v.name; auto& body = _v.body; auto& visibility = _v.visibility; /* pass */ } - else if (std::holds_alternative::Import>(_match_29._data)) { - auto& _v = std::get::Import>(_match_29._data); + else if (std::holds_alternative::Import>(_match_37._data)) { + auto& _v = std::get::Import>(_match_37._data); auto& path = _v.path; auto& alias = _v.alias; /* pass */ } - else if (std::holds_alternative::Break>(_match_29._data)) { - auto& _v = std::get::Break>(_match_29._data); + else if (std::holds_alternative::Break>(_match_37._data)) { + auto& _v = std::get::Break>(_match_37._data); auto& keyword = _v.keyword; this->output = (this->output + ((std::string("") + ((*this).indent())) + std::string("break;\n"))); } - else if (std::holds_alternative::Continue>(_match_29._data)) { - auto& _v = std::get::Continue>(_match_29._data); + else if (std::holds_alternative::Continue>(_match_37._data)) { + auto& _v = std::get::Continue>(_match_37._data); auto& keyword = _v.keyword; this->output = (this->output + ((std::string("") + ((*this).indent())) + std::string("continue;\n"))); } - else if (std::holds_alternative::Pass>(_match_29._data)) { - auto& _v = std::get::Pass>(_match_29._data); + else if (std::holds_alternative::Pass>(_match_37._data)) { + auto& _v = std::get::Pass>(_match_37._data); auto& keyword = _v.keyword; this->output = (this->output + ((std::string("") + ((*this).indent())) + std::string("/* pass */\n"))); } - else if (std::holds_alternative::CppBlock>(_match_29._data)) { - auto& _v = std::get::CppBlock>(_match_29._data); + else if (std::holds_alternative::CppBlock>(_match_37._data)) { + auto& _v = std::get::CppBlock>(_match_37._data); auto& code = _v.code; auto lines = lv_split(code, std::string("\n")); for (const auto& line : lines) { @@ -2731,8 +2900,8 @@ struct CppCodegen { } } } - else if (std::holds_alternative::Extern>(_match_29._data)) { - auto& _v = std::get::Extern>(_match_29._data); + else if (std::holds_alternative::Extern>(_match_37._data)) { + auto& _v = std::get::Extern>(_match_37._data); auto& header = _v.header; auto& import_path = _v.import_path; auto& link_lib = _v.link_lib; @@ -2765,8 +2934,8 @@ struct CppCodegen { this->extern_fn_params[ef.name] = ef.params; } } - else if (std::holds_alternative::Extend>(_match_29._data)) { - auto& _v = std::get::Extend>(_match_29._data); + else if (std::holds_alternative::Extend>(_match_37._data)) { + auto& _v = std::get::Extend>(_match_37._data); auto& target = _v.target_type; auto& methods = _v.methods; auto& visibility = _v.visibility; @@ -2794,17 +2963,17 @@ struct CppCodegen { } std::string init_str = std::string(""); { - const auto& _match_34 = initializer; - if (_match_34._tag == "None") { + const auto& _match_42 = initializer; + if (_match_42._tag == "None") { init_str = (*this).default_init(cpp_type); } else { std::string val = (*this).emit_expr(initializer, in_method); bool is_ptr = false; { - const auto& _match_35 = var_type; - if (std::holds_alternative::Ptr>(_match_35._data)) { - auto& _v = std::get::Ptr>(_match_35._data); + const auto& _match_43 = var_type; + if (std::holds_alternative::Ptr>(_match_43._data)) { + auto& _v = std::get::Ptr>(_match_43._data); auto& inner = *_v.inner; is_ptr = true; } @@ -2823,9 +2992,9 @@ struct CppCodegen { void emit_block_or_stmt(const Stmt& s, bool m) { { - const auto& _match_36 = s; - if (std::holds_alternative::Block>(_match_36._data)) { - auto& _v = std::get::Block>(_match_36._data); + const auto& _match_44 = s; + if (std::holds_alternative::Block>(_match_44._data)) { + auto& _v = std::get::Block>(_match_44._data); auto& stmts = _v.statements; this->output = (this->output + std::string("{\n")); this->indent_level = (this->indent_level + INT64_C(1)); @@ -2895,27 +3064,27 @@ struct CppCodegen { std::vector remaining_body = {}; for (const auto& st : init_body) { { - const auto& _match_37 = st; - if (std::holds_alternative::ExprStmt>(_match_37._data)) { - auto& _v = std::get::ExprStmt>(_match_37._data); + const auto& _match_45 = st; + if (std::holds_alternative::ExprStmt>(_match_45._data)) { + auto& _v = std::get::ExprStmt>(_match_45._data); auto& expr = _v.expr; bool handled = false; { - const auto& _match_38 = expr; - if (std::holds_alternative::Set>(_match_38._data)) { - auto& _v = std::get::Set>(_match_38._data); + const auto& _match_46 = expr; + if (std::holds_alternative::Set>(_match_46._data)) { + auto& _v = std::get::Set>(_match_46._data); auto& object = *_v.object; auto& prop = _v.name; auto& value = *_v.value; { - const auto& _match_39 = object; - if (std::holds_alternative::This>(_match_39._data)) { - auto& _v = std::get::This>(_match_39._data); + const auto& _match_47 = object; + if (std::holds_alternative::This>(_match_47._data)) { + auto& _v = std::get::This>(_match_47._data); auto& kw = _v.keyword; { - const auto& _match_40 = value; - if (std::holds_alternative::Variable>(_match_40._data)) { - auto& _v = std::get::Variable>(_match_40._data); + const auto& _match_48 = value; + if (std::holds_alternative::Variable>(_match_48._data)) { + auto& _v = std::get::Variable>(_match_48._data); auto& tok = _v.name; init_list.push_back(((((std::string("") + (prop.lexeme)) + std::string("(")) + (tok.lexeme)) + std::string(")"))); handled = true; @@ -2962,9 +3131,9 @@ struct CppCodegen { void emit_class_method(const Token& class_name, const Stmt& method) { { - const auto& _match_41 = method; - if (std::holds_alternative::Function>(_match_41._data)) { - auto& _v = std::get::Function>(_match_41._data); + const auto& _match_49 = method; + if (std::holds_alternative::Function>(_match_49._data)) { + auto& _v = std::get::Function>(_match_49._data); auto& mname = _v.name; auto& mparams = _v.params; auto& mret = _v.return_type; @@ -3020,9 +3189,9 @@ struct CppCodegen { std::vector let_field_types = {}; for (const auto& st : body) { { - const auto& _match_42 = st; - if (std::holds_alternative::Function>(_match_42._data)) { - auto& _v = std::get::Function>(_match_42._data); + const auto& _match_50 = st; + if (std::holds_alternative::Function>(_match_50._data)) { + auto& _v = std::get::Function>(_match_50._data); auto& fname = _v.name; auto& params = _v.params; auto& return_type = _v.return_type; @@ -3041,8 +3210,8 @@ struct CppCodegen { methods.push_back(st); } } - else if (std::holds_alternative::Let>(_match_42._data)) { - auto& _v = std::get::Let>(_match_42._data); + else if (std::holds_alternative::Let>(_match_50._data)) { + auto& _v = std::get::Let>(_match_50._data); auto& fname = _v.name; auto& var_type = _v.var_type; auto& init = _v.initializer; @@ -3069,21 +3238,21 @@ struct CppCodegen { std::vector seen = {}; for (const auto& st : init_body) { { - const auto& _match_43 = st; - if (std::holds_alternative::ExprStmt>(_match_43._data)) { - auto& _v = std::get::ExprStmt>(_match_43._data); + const auto& _match_51 = st; + if (std::holds_alternative::ExprStmt>(_match_51._data)) { + auto& _v = std::get::ExprStmt>(_match_51._data); auto& expr = _v.expr; { - const auto& _match_44 = expr; - if (std::holds_alternative::Set>(_match_44._data)) { - auto& _v = std::get::Set>(_match_44._data); + const auto& _match_52 = expr; + if (std::holds_alternative::Set>(_match_52._data)) { + auto& _v = std::get::Set>(_match_52._data); auto& object = *_v.object; auto& prop = _v.name; auto& value = *_v.value; { - const auto& _match_45 = object; - if (std::holds_alternative::This>(_match_45._data)) { - auto& _v = std::get::This>(_match_45._data); + const auto& _match_53 = object; + if (std::holds_alternative::This>(_match_53._data)) { + auto& _v = std::get::This>(_match_53._data); auto& kw = _v.keyword; bool already = false; for (const auto& s : seen) { @@ -3128,9 +3297,9 @@ struct CppCodegen { bool has_to_string = false; for (const auto& m : methods) { { - const auto& _match_46 = m; - if (std::holds_alternative::Function>(_match_46._data)) { - auto& _v = std::get::Function>(_match_46._data); + const auto& _match_54 = m; + if (std::holds_alternative::Function>(_match_54._data)) { + auto& _v = std::get::Function>(_match_54._data); auto& mname = _v.name; auto& mparams = _v.params; auto& mret = _v.return_type; @@ -3164,9 +3333,9 @@ struct CppCodegen { std::string infer_expr_type(const Expr& e, const std::vector& param_names, const std::vector& param_types) { { - const auto& _match_47 = e; - if (std::holds_alternative::Literal>(_match_47._data)) { - auto& _v = std::get::Literal>(_match_47._data); + const auto& _match_55 = e; + if (std::holds_alternative::Literal>(_match_55._data)) { + auto& _v = std::get::Literal>(_match_55._data); auto& kind = _v.kind; auto& value = _v.value; if ((kind == std::string("int"))) { @@ -3194,8 +3363,8 @@ struct CppCodegen { } return std::string("std::any"); } - else if (std::holds_alternative::Variable>(_match_47._data)) { - auto& _v = std::get::Variable>(_match_47._data); + else if (std::holds_alternative::Variable>(_match_55._data)) { + auto& _v = std::get::Variable>(_match_55._data); auto& tok = _v.name; for (int64_t i = INT64_C(0); i < static_cast(param_names.size()); i++) { if ((param_names[i] == tok.lexeme)) { @@ -3207,8 +3376,8 @@ struct CppCodegen { } return std::string("std::any"); } - else if (std::holds_alternative::Binary>(_match_47._data)) { - auto& _v = std::get::Binary>(_match_47._data); + else if (std::holds_alternative::Binary>(_match_55._data)) { + auto& _v = std::get::Binary>(_match_55._data); auto& left = *_v.left; auto& op = _v.op; auto& right = *_v.right; @@ -3222,26 +3391,26 @@ struct CppCodegen { } return std::string("std::any"); } - else if (std::holds_alternative::Unary>(_match_47._data)) { - auto& _v = std::get::Unary>(_match_47._data); + else if (std::holds_alternative::Unary>(_match_55._data)) { + auto& _v = std::get::Unary>(_match_55._data); auto& op = _v.op; auto& right = *_v.right; return (*this).infer_expr_type(right, param_names, param_types); } - else if (std::holds_alternative::Grouping>(_match_47._data)) { - auto& _v = std::get::Grouping>(_match_47._data); + else if (std::holds_alternative::Grouping>(_match_55._data)) { + auto& _v = std::get::Grouping>(_match_55._data); auto& inner = *_v.inner; return (*this).infer_expr_type(inner, param_names, param_types); } - else if (std::holds_alternative::Call>(_match_47._data)) { - auto& _v = std::get::Call>(_match_47._data); + else if (std::holds_alternative::Call>(_match_55._data)) { + auto& _v = std::get::Call>(_match_55._data); auto& callee = *_v.callee; auto& paren = _v.paren; auto& args = _v.args; { - const auto& _match_48 = callee; - if (std::holds_alternative::Variable>(_match_48._data)) { - auto& _v = std::get::Variable>(_match_48._data); + const auto& _match_56 = callee; + if (std::holds_alternative::Variable>(_match_56._data)) { + auto& _v = std::get::Variable>(_match_56._data); auto& tok = _v.name; if ((*this).is_known_enum(tok.lexeme)) { return tok.lexeme; @@ -3253,14 +3422,14 @@ struct CppCodegen { } return tok.lexeme; } - else if (std::holds_alternative::StaticGet>(_match_48._data)) { - auto& _v = std::get::StaticGet>(_match_48._data); + else if (std::holds_alternative::StaticGet>(_match_56._data)) { + auto& _v = std::get::StaticGet>(_match_56._data); auto& object = *_v.object; auto& name = _v.name; { - const auto& _match_49 = object; - if (std::holds_alternative::Variable>(_match_49._data)) { - auto& _v = std::get::Variable>(_match_49._data); + const auto& _match_57 = object; + if (std::holds_alternative::Variable>(_match_57._data)) { + auto& _v = std::get::Variable>(_match_57._data); auto& tok = _v.name; if ((*this).is_known_enum(tok.lexeme)) { return tok.lexeme; @@ -3277,8 +3446,8 @@ struct CppCodegen { } } } - else if (std::holds_alternative::Vector>(_match_47._data)) { - auto& _v = std::get::Vector>(_match_47._data); + else if (std::holds_alternative::Vector>(_match_55._data)) { + auto& _v = std::get::Vector>(_match_55._data); auto& elements = _v.elements; if ((static_cast(elements.size()) > INT64_C(0))) { std::string inner = (*this).infer_expr_type(elements[INT64_C(0)], param_names, param_types); @@ -3288,8 +3457,8 @@ struct CppCodegen { } return std::string("std::vector"); } - else if (std::holds_alternative::Map>(_match_47._data)) { - auto& _v = std::get::Map>(_match_47._data); + else if (std::holds_alternative::Map>(_match_55._data)) { + auto& _v = std::get::Map>(_match_55._data); auto& keys = _v.keys; auto& values = _v.values; std::string kt = std::string("std::any"); @@ -3300,8 +3469,8 @@ struct CppCodegen { } return ((((std::string("std::unordered_map<") + (kt)) + std::string(", ")) + (vt)) + std::string(">")); } - else if (std::holds_alternative::Cast>(_match_47._data)) { - auto& _v = std::get::Cast>(_match_47._data); + else if (std::holds_alternative::Cast>(_match_55._data)) { + auto& _v = std::get::Cast>(_match_55._data); auto& expr = *_v.expr; auto& target_type = _v.target_type; return (*this).emit_type(target_type); @@ -3320,9 +3489,9 @@ struct CppCodegen { std::string cpp_type = (*this).emit_type(v.types[fi]); std::string fname = v.field_names[fi]; { - const auto& _match_50 = v.types[fi]; - if (std::holds_alternative::Custom>(_match_50._data)) { - auto& _v = std::get::Custom>(_match_50._data); + const auto& _match_58 = v.types[fi]; + if (std::holds_alternative::Custom>(_match_58._data)) { + auto& _v = std::get::Custom>(_match_58._data); auto& n = _v.name; auto& _ = _v.type_args; if ((n == enum_name)) { @@ -3365,9 +3534,9 @@ struct CppCodegen { std::string fname = v.field_names[fi]; bool is_self_ref = false; { - const auto& _match_51 = v.types[fi]; - if (std::holds_alternative::Custom>(_match_51._data)) { - auto& _v = std::get::Custom>(_match_51._data); + const auto& _match_59 = v.types[fi]; + if (std::holds_alternative::Custom>(_match_59._data)) { + auto& _v = std::get::Custom>(_match_59._data); auto& n = _v.name; auto& _ = _v.type_args; if ((n == enum_name)) { @@ -3421,9 +3590,9 @@ struct CppCodegen { for (const auto& v : variants) { for (const auto& ft : v.types) { { - const auto& _match_52 = ft; - if (std::holds_alternative::Custom>(_match_52._data)) { - auto& _v = std::get::Custom>(_match_52._data); + const auto& _match_60 = ft; + if (std::holds_alternative::Custom>(_match_60._data)) { + auto& _v = std::get::Custom>(_match_60._data); auto& n = _v.name; auto& _ = _v.type_args; if ((n == enum_name)) { @@ -3454,9 +3623,9 @@ struct CppCodegen { bool has_to_string = false; for (const auto& m : methods) { { - const auto& _match_53 = m; - if (std::holds_alternative::Function>(_match_53._data)) { - auto& _v = std::get::Function>(_match_53._data); + const auto& _match_61 = m; + if (std::holds_alternative::Function>(_match_61._data)) { + auto& _v = std::get::Function>(_match_61._data); auto& mname = _v.name; auto& mparams = _v.params; auto& mret = _v.return_type; @@ -3525,9 +3694,9 @@ struct CppCodegen { bool is_self_ref = false; if ((bi < static_cast(vinfo.types.size()))) { { - const auto& _match_54 = vinfo.types[bi]; - if (std::holds_alternative::Custom>(_match_54._data)) { - auto& _v = std::get::Custom>(_match_54._data); + const auto& _match_62 = vinfo.types[bi]; + if (std::holds_alternative::Custom>(_match_62._data)) { + auto& _v = std::get::Custom>(_match_62._data); auto& n = _v.name; auto& _ = _v.type_args; if ((n == ename)) { @@ -3560,9 +3729,9 @@ struct CppCodegen { void emit_arm_body(const Stmt& arm_body, bool in_method) { { - const auto& _match_55 = arm_body; - if (std::holds_alternative::Block>(_match_55._data)) { - auto& _v = std::get::Block>(_match_55._data); + const auto& _match_63 = arm_body; + if (std::holds_alternative::Block>(_match_63._data)) { + auto& _v = std::get::Block>(_match_63._data); auto& stmts = _v.statements; for (const auto& st : stmts) { (*this).emit_stmt(st, in_method); @@ -3576,9 +3745,9 @@ struct CppCodegen { void emit_using_if_public(const std::string& ns, const Stmt& stmt) { { - const auto& _match_56 = stmt; - if (std::holds_alternative::Function>(_match_56._data)) { - auto& _v = std::get::Function>(_match_56._data); + const auto& _match_64 = stmt; + if (std::holds_alternative::Function>(_match_64._data)) { + auto& _v = std::get::Function>(_match_64._data); auto& name = _v.name; auto& params = _v.params; auto& return_type = _v.return_type; @@ -3592,8 +3761,8 @@ struct CppCodegen { this->output = (this->output + ((((std::string("using ") + (ns)) + std::string("::")) + (name.lexeme)) + std::string(";\n"))); } } - else if (std::holds_alternative::Class>(_match_56._data)) { - auto& _v = std::get::Class>(_match_56._data); + else if (std::holds_alternative::Class>(_match_64._data)) { + auto& _v = std::get::Class>(_match_64._data); auto& name = _v.name; auto& body = _v.body; auto& visibility = _v.visibility; @@ -3601,8 +3770,8 @@ struct CppCodegen { this->output = (this->output + ((((std::string("using ") + (ns)) + std::string("::")) + (name.lexeme)) + std::string(";\n"))); } } - else if (std::holds_alternative::Struct>(_match_56._data)) { - auto& _v = std::get::Struct>(_match_56._data); + else if (std::holds_alternative::Struct>(_match_64._data)) { + auto& _v = std::get::Struct>(_match_64._data); auto& name = _v.name; auto& body = _v.body; auto& visibility = _v.visibility; @@ -3611,8 +3780,8 @@ struct CppCodegen { this->output = (this->output + ((((std::string("using ") + (ns)) + std::string("::")) + (name.lexeme)) + std::string(";\n"))); } } - else if (std::holds_alternative::Enum>(_match_56._data)) { - auto& _v = std::get::Enum>(_match_56._data); + else if (std::holds_alternative::Enum>(_match_64._data)) { + auto& _v = std::get::Enum>(_match_64._data); auto& name = _v.name; auto& variants = _v.variants; auto& methods = _v.methods; @@ -3622,8 +3791,8 @@ struct CppCodegen { this->output = (this->output + ((((std::string("using ") + (ns)) + std::string("::")) + (name.lexeme)) + std::string(";\n"))); } } - else if (std::holds_alternative::Const>(_match_56._data)) { - auto& _v = std::get::Const>(_match_56._data); + else if (std::holds_alternative::Const>(_match_64._data)) { + auto& _v = std::get::Const>(_match_64._data); auto& name = _v.name; auto& const_type = _v.const_type; auto& value = _v.value; @@ -3633,8 +3802,8 @@ struct CppCodegen { this->output = (this->output + ((((std::string("using ") + (ns)) + std::string("::")) + (name.lexeme)) + std::string(";\n"))); } } - else if (std::holds_alternative::Let>(_match_56._data)) { - auto& _v = std::get::Let>(_match_56._data); + else if (std::holds_alternative::Let>(_match_64._data)) { + auto& _v = std::get::Let>(_match_64._data); auto& name = _v.name; auto& var_type = _v.var_type; auto& initializer = _v.initializer; @@ -3659,9 +3828,9 @@ struct CppCodegen { this->indent_level = INT64_C(1); for (const auto& stmt : this->module_stmts[index]) { { - const auto& _match_57 = stmt; - if (std::holds_alternative::Function>(_match_57._data)) { - auto& _v = std::get::Function>(_match_57._data); + const auto& _match_65 = stmt; + if (std::holds_alternative::Function>(_match_65._data)) { + auto& _v = std::get::Function>(_match_65._data); auto& name = _v.name; auto& params = _v.params; auto& return_type = _v.return_type; @@ -3678,8 +3847,8 @@ struct CppCodegen { (*this).emit_stmt(stmt, false); } } - else if (std::holds_alternative::Extern>(_match_57._data)) { - auto& _v = std::get::Extern>(_match_57._data); + else if (std::holds_alternative::Extern>(_match_65._data)) { + auto& _v = std::get::Extern>(_match_65._data); auto& header = _v.header; auto& import_path = _v.import_path; auto& link_lib = _v.link_lib; @@ -3714,9 +3883,9 @@ struct CppCodegen { for (int64_t mi = INT64_C(0); mi < static_cast(this->module_stmts.size()); mi++) { for (const auto& stmt : this->module_stmts[mi]) { { - const auto& _match_58 = stmt; - if (std::holds_alternative::Extern>(_match_58._data)) { - auto& _v = std::get::Extern>(_match_58._data); + const auto& _match_66 = stmt; + if (std::holds_alternative::Extern>(_match_66._data)) { + auto& _v = std::get::Extern>(_match_66._data); auto& header = _v.header; auto& import_path = _v.import_path; auto& link_lib = _v.link_lib; @@ -3733,9 +3902,9 @@ struct CppCodegen { } for (const auto& stmt : stmts) { { - const auto& _match_59 = stmt; - if (std::holds_alternative::Extern>(_match_59._data)) { - auto& _v = std::get::Extern>(_match_59._data); + const auto& _match_67 = stmt; + if (std::holds_alternative::Extern>(_match_67._data)) { + auto& _v = std::get::Extern>(_match_67._data); auto& header = _v.header; auto& import_path = _v.import_path; auto& link_lib = _v.link_lib; @@ -3752,9 +3921,9 @@ struct CppCodegen { for (int64_t mi = INT64_C(0); mi < static_cast(this->module_stmts.size()); mi++) { for (const auto& stmt : this->module_stmts[mi]) { { - const auto& _match_60 = stmt; - if (std::holds_alternative::Extend>(_match_60._data)) { - auto& _v = std::get::Extend>(_match_60._data); + const auto& _match_68 = stmt; + if (std::holds_alternative::Extend>(_match_68._data)) { + auto& _v = std::get::Extend>(_match_68._data); auto& target = _v.target_type; auto& methods = _v.methods; auto& visibility = _v.visibility; @@ -3775,9 +3944,9 @@ struct CppCodegen { } for (const auto& stmt : stmts) { { - const auto& _match_61 = stmt; - if (std::holds_alternative::Extend>(_match_61._data)) { - auto& _v = std::get::Extend>(_match_61._data); + const auto& _match_69 = stmt; + if (std::holds_alternative::Extend>(_match_69._data)) { + auto& _v = std::get::Extend>(_match_69._data); auto& target = _v.target_type; auto& methods = _v.methods; auto& visibility = _v.visibility; @@ -3814,9 +3983,9 @@ struct CppCodegen { } for (const auto& stmt : stmts) { { - const auto& _match_62 = stmt; - if (std::holds_alternative::Function>(_match_62._data)) { - auto& _v = std::get::Function>(_match_62._data); + const auto& _match_70 = stmt; + if (std::holds_alternative::Function>(_match_70._data)) { + auto& _v = std::get::Function>(_match_70._data); auto& name = _v.name; auto& params = _v.params; auto& return_type = _v.return_type; @@ -3845,8 +4014,8 @@ struct CppCodegen { this->output = std::string(""); } } - else if (std::holds_alternative::Extern>(_match_62._data)) { - auto& _v = std::get::Extern>(_match_62._data); + else if (std::holds_alternative::Extern>(_match_70._data)) { + auto& _v = std::get::Extern>(_match_70._data); auto& header = _v.header; auto& import_path = _v.import_path; auto& link_lib = _v.link_lib; @@ -3854,8 +4023,8 @@ struct CppCodegen { auto& functions = _v.functions; /* pass */ } - else if (std::holds_alternative::Extend>(_match_62._data)) { - auto& _v = std::get::Extend>(_match_62._data); + else if (std::holds_alternative::Extend>(_match_70._data)) { + auto& _v = std::get::Extend>(_match_70._data); auto& target = _v.target_type; auto& methods = _v.methods; auto& visibility = _v.visibility; @@ -3995,27 +4164,27 @@ struct Checker { bool types_compatible(const TypeNode& expected, const TypeNode& actual) { { - const auto& _match_63 = actual; - if (std::holds_alternative::Array>(_match_63._data)) { - auto& _v = std::get::Array>(_match_63._data); + const auto& _match_71 = actual; + if (std::holds_alternative::Array>(_match_71._data)) { + auto& _v = std::get::Array>(_match_71._data); auto& a_inner = *_v.inner; { - const auto& _match_64 = a_inner; - if (std::holds_alternative::Auto>(_match_64._data)) { + const auto& _match_72 = a_inner; + if (std::holds_alternative::Auto>(_match_72._data)) { { - const auto& _match_65 = expected; - if (std::holds_alternative::Array>(_match_65._data)) { - auto& _v = std::get::Array>(_match_65._data); + const auto& _match_73 = expected; + if (std::holds_alternative::Array>(_match_73._data)) { + auto& _v = std::get::Array>(_match_73._data); auto& e_inner = *_v.inner; return true; } - else if (std::holds_alternative::HashSet>(_match_65._data)) { - auto& _v = std::get::HashSet>(_match_65._data); + else if (std::holds_alternative::HashSet>(_match_73._data)) { + auto& _v = std::get::HashSet>(_match_73._data); auto& e_inner = *_v.inner; return true; } - else if (std::holds_alternative::HashMap>(_match_65._data)) { - auto& _v = std::get::HashMap>(_match_65._data); + else if (std::holds_alternative::HashMap>(_match_73._data)) { + auto& _v = std::get::HashMap>(_match_73._data); auto& ek = *_v.key_type; auto& ev = *_v.value_type; return true; @@ -4035,14 +4204,14 @@ struct Checker { } } { - const auto& _match_66 = expected; - if (std::holds_alternative::Array>(_match_66._data)) { - auto& _v = std::get::Array>(_match_66._data); + const auto& _match_74 = expected; + if (std::holds_alternative::Array>(_match_74._data)) { + auto& _v = std::get::Array>(_match_74._data); auto& e_inner = *_v.inner; { - const auto& _match_67 = actual; - if (std::holds_alternative::Array>(_match_67._data)) { - auto& _v = std::get::Array>(_match_67._data); + const auto& _match_75 = actual; + if (std::holds_alternative::Array>(_match_75._data)) { + auto& _v = std::get::Array>(_match_75._data); auto& a_inner = *_v.inner; return (*this).types_compatible(e_inner, a_inner); } @@ -4056,14 +4225,14 @@ struct Checker { } } { - const auto& _match_68 = expected; - if (std::holds_alternative::Auto>(_match_68._data)) { + const auto& _match_76 = expected; + if (std::holds_alternative::Auto>(_match_76._data)) { return true; } - else if (_match_68._tag == "None") { + else if (_match_76._tag == "None") { return true; } - else if (std::holds_alternative::Dynamic>(_match_68._data)) { + else if (std::holds_alternative::Dynamic>(_match_76._data)) { return true; } else { @@ -4071,14 +4240,14 @@ struct Checker { } } { - const auto& _match_69 = actual; - if (std::holds_alternative::Auto>(_match_69._data)) { + const auto& _match_77 = actual; + if (std::holds_alternative::Auto>(_match_77._data)) { return true; } - else if (_match_69._tag == "None") { + else if (_match_77._tag == "None") { return true; } - else if (std::holds_alternative::Dynamic>(_match_69._data)) { + else if (std::holds_alternative::Dynamic>(_match_77._data)) { return true; } else { @@ -4093,20 +4262,20 @@ struct Checker { bool e_is_int = false; bool a_is_int = false; { - const auto& _match_70 = expected; - if (std::holds_alternative::Int>(_match_70._data)) { + const auto& _match_78 = expected; + if (std::holds_alternative::Int>(_match_78._data)) { e_is_int = true; } - else if (std::holds_alternative::Int8>(_match_70._data)) { + else if (std::holds_alternative::Int8>(_match_78._data)) { e_is_int = true; } - else if (std::holds_alternative::Int16>(_match_70._data)) { + else if (std::holds_alternative::Int16>(_match_78._data)) { e_is_int = true; } - else if (std::holds_alternative::Int32>(_match_70._data)) { + else if (std::holds_alternative::Int32>(_match_78._data)) { e_is_int = true; } - else if (std::holds_alternative::USize>(_match_70._data)) { + else if (std::holds_alternative::USize>(_match_78._data)) { e_is_int = true; } else { @@ -4114,20 +4283,20 @@ struct Checker { } } { - const auto& _match_71 = actual; - if (std::holds_alternative::Int>(_match_71._data)) { + const auto& _match_79 = actual; + if (std::holds_alternative::Int>(_match_79._data)) { a_is_int = true; } - else if (std::holds_alternative::Int8>(_match_71._data)) { + else if (std::holds_alternative::Int8>(_match_79._data)) { a_is_int = true; } - else if (std::holds_alternative::Int16>(_match_71._data)) { + else if (std::holds_alternative::Int16>(_match_79._data)) { a_is_int = true; } - else if (std::holds_alternative::Int32>(_match_71._data)) { + else if (std::holds_alternative::Int32>(_match_79._data)) { a_is_int = true; } - else if (std::holds_alternative::USize>(_match_71._data)) { + else if (std::holds_alternative::USize>(_match_79._data)) { a_is_int = true; } else { @@ -4140,11 +4309,11 @@ struct Checker { bool e_is_float = false; bool a_is_float = false; { - const auto& _match_72 = expected; - if (std::holds_alternative::Float>(_match_72._data)) { + const auto& _match_80 = expected; + if (std::holds_alternative::Float>(_match_80._data)) { e_is_float = true; } - else if (std::holds_alternative::Float32>(_match_72._data)) { + else if (std::holds_alternative::Float32>(_match_80._data)) { e_is_float = true; } else { @@ -4152,11 +4321,11 @@ struct Checker { } } { - const auto& _match_73 = actual; - if (std::holds_alternative::Float>(_match_73._data)) { + const auto& _match_81 = actual; + if (std::holds_alternative::Float>(_match_81._data)) { a_is_float = true; } - else if (std::holds_alternative::Float32>(_match_73._data)) { + else if (std::holds_alternative::Float32>(_match_81._data)) { a_is_float = true; } else { @@ -4170,11 +4339,11 @@ struct Checker { return true; } { - const auto& _match_74 = expected; - if (std::holds_alternative::CString>(_match_74._data)) { + const auto& _match_82 = expected; + if (std::holds_alternative::CString>(_match_82._data)) { { - const auto& _match_75 = actual; - if (std::holds_alternative::Str>(_match_75._data)) { + const auto& _match_83 = actual; + if (std::holds_alternative::Str>(_match_83._data)) { return true; } else { @@ -4182,10 +4351,10 @@ struct Checker { } } } - else if (std::holds_alternative::Str>(_match_74._data)) { + else if (std::holds_alternative::Str>(_match_82._data)) { { - const auto& _match_76 = actual; - if (std::holds_alternative::CString>(_match_76._data)) { + const auto& _match_84 = actual; + if (std::holds_alternative::CString>(_match_84._data)) { return true; } else { @@ -4198,13 +4367,13 @@ struct Checker { } } { - const auto& _match_77 = expected; - if (std::holds_alternative::Nullable>(_match_77._data)) { - auto& _v = std::get::Nullable>(_match_77._data); + const auto& _match_85 = expected; + if (std::holds_alternative::Nullable>(_match_85._data)) { + auto& _v = std::get::Nullable>(_match_85._data); auto& inner = *_v.inner; { - const auto& _match_78 = actual; - if (std::holds_alternative::NullType>(_match_78._data)) { + const auto& _match_86 = actual; + if (std::holds_alternative::NullType>(_match_86._data)) { return true; } else { @@ -4217,13 +4386,13 @@ struct Checker { } } { - const auto& _match_79 = expected; - if (std::holds_alternative::Ptr>(_match_79._data)) { - auto& _v = std::get::Ptr>(_match_79._data); + const auto& _match_87 = expected; + if (std::holds_alternative::Ptr>(_match_87._data)) { + auto& _v = std::get::Ptr>(_match_87._data); auto& inner = *_v.inner; { - const auto& _match_80 = actual; - if (std::holds_alternative::NullType>(_match_80._data)) { + const auto& _match_88 = actual; + if (std::holds_alternative::NullType>(_match_88._data)) { return true; } else { @@ -4240,82 +4409,82 @@ struct Checker { std::string type_name(const TypeNode& t) { { - const auto& _match_81 = t; - if (std::holds_alternative::Int>(_match_81._data)) { + const auto& _match_89 = t; + if (std::holds_alternative::Int>(_match_89._data)) { return std::string("int"); } - else if (std::holds_alternative::Float>(_match_81._data)) { + else if (std::holds_alternative::Float>(_match_89._data)) { return std::string("float"); } - else if (std::holds_alternative::Str>(_match_81._data)) { + else if (std::holds_alternative::Str>(_match_89._data)) { return std::string("string"); } - else if (std::holds_alternative::Bool>(_match_81._data)) { + else if (std::holds_alternative::Bool>(_match_89._data)) { return std::string("bool"); } - else if (std::holds_alternative::Void>(_match_81._data)) { + else if (std::holds_alternative::Void>(_match_89._data)) { return std::string("void"); } - else if (std::holds_alternative::Auto>(_match_81._data)) { + else if (std::holds_alternative::Auto>(_match_89._data)) { return std::string("auto"); } - else if (std::holds_alternative::Dynamic>(_match_81._data)) { + else if (std::holds_alternative::Dynamic>(_match_89._data)) { return std::string("dynamic"); } - else if (std::holds_alternative::NullType>(_match_81._data)) { + else if (std::holds_alternative::NullType>(_match_89._data)) { return std::string("null"); } - else if (std::holds_alternative::Custom>(_match_81._data)) { - auto& _v = std::get::Custom>(_match_81._data); + else if (std::holds_alternative::Custom>(_match_89._data)) { + auto& _v = std::get::Custom>(_match_89._data); auto& name = _v.name; auto& _ = _v.type_args; return name; } - else if (std::holds_alternative::Array>(_match_81._data)) { - auto& _v = std::get::Array>(_match_81._data); + else if (std::holds_alternative::Array>(_match_89._data)) { + auto& _v = std::get::Array>(_match_89._data); auto& inner = *_v.inner; return ((std::string("vector[") + ((*this).type_name(inner))) + std::string("]")); } - else if (std::holds_alternative::HashSet>(_match_81._data)) { - auto& _v = std::get::HashSet>(_match_81._data); + else if (std::holds_alternative::HashSet>(_match_89._data)) { + auto& _v = std::get::HashSet>(_match_89._data); auto& inner = *_v.inner; return ((std::string("set[") + ((*this).type_name(inner))) + std::string("]")); } - else if (std::holds_alternative::HashMap>(_match_81._data)) { - auto& _v = std::get::HashMap>(_match_81._data); + else if (std::holds_alternative::HashMap>(_match_89._data)) { + auto& _v = std::get::HashMap>(_match_89._data); auto& k = *_v.key_type; auto& v = *_v.value_type; return ((((std::string("map[") + ((*this).type_name(k))) + std::string(", ")) + ((*this).type_name(v))) + std::string("]")); } - else if (std::holds_alternative::Nullable>(_match_81._data)) { - auto& _v = std::get::Nullable>(_match_81._data); + else if (std::holds_alternative::Nullable>(_match_89._data)) { + auto& _v = std::get::Nullable>(_match_89._data); auto& inner = *_v.inner; return ((std::string("") + ((*this).type_name(inner))) + std::string("?")); } - else if (std::holds_alternative::Int8>(_match_81._data)) { + else if (std::holds_alternative::Int8>(_match_89._data)) { return std::string("int8"); } - else if (std::holds_alternative::Int16>(_match_81._data)) { + else if (std::holds_alternative::Int16>(_match_89._data)) { return std::string("int16"); } - else if (std::holds_alternative::Int32>(_match_81._data)) { + else if (std::holds_alternative::Int32>(_match_89._data)) { return std::string("int32"); } - else if (std::holds_alternative::Float32>(_match_81._data)) { + else if (std::holds_alternative::Float32>(_match_89._data)) { return std::string("float32"); } - else if (std::holds_alternative::USize>(_match_81._data)) { + else if (std::holds_alternative::USize>(_match_89._data)) { return std::string("usize"); } - else if (std::holds_alternative::CString>(_match_81._data)) { + else if (std::holds_alternative::CString>(_match_89._data)) { return std::string("cstring"); } - else if (std::holds_alternative::Ptr>(_match_81._data)) { - auto& _v = std::get::Ptr>(_match_81._data); + else if (std::holds_alternative::Ptr>(_match_89._data)) { + auto& _v = std::get::Ptr>(_match_89._data); auto& inner = *_v.inner; return ((std::string("ptr[") + ((*this).type_name(inner))) + std::string("]")); } - else if (std::holds_alternative::Bytes>(_match_81._data)) { + else if (std::holds_alternative::Bytes>(_match_89._data)) { return std::string("bytes"); } else { @@ -4326,12 +4495,12 @@ struct Checker { TypeNode infer_type(const Expr& e) { { - const auto& _match_82 = e; - if (_match_82._tag == "None") { + const auto& _match_90 = e; + if (_match_90._tag == "None") { return TypeNode::make_None(); } - else if (std::holds_alternative::Literal>(_match_82._data)) { - auto& _v = std::get::Literal>(_match_82._data); + else if (std::holds_alternative::Literal>(_match_90._data)) { + auto& _v = std::get::Literal>(_match_90._data); auto& kind = _v.kind; auto& value = _v.value; if ((kind == std::string("int"))) { @@ -4359,14 +4528,14 @@ struct Checker { } return TypeNode::make_Auto(); } - else if (std::holds_alternative::Variable>(_match_82._data)) { - auto& _v = std::get::Variable>(_match_82._data); + else if (std::holds_alternative::Variable>(_match_90._data)) { + auto& _v = std::get::Variable>(_match_90._data); auto& name = _v.name; auto sym = (*this).lookup(name.lexeme); return sym.sym_type; } - else if (std::holds_alternative::Binary>(_match_82._data)) { - auto& _v = std::get::Binary>(_match_82._data); + else if (std::holds_alternative::Binary>(_match_90._data)) { + auto& _v = std::get::Binary>(_match_90._data); auto& left = *_v.left; auto& op = _v.op; auto& right = *_v.right; @@ -4376,8 +4545,8 @@ struct Checker { return TypeNode::make_Bool(); } { - const auto& _match_83 = lt; - if (std::holds_alternative::Str>(_match_83._data)) { + const auto& _match_91 = lt; + if (std::holds_alternative::Str>(_match_91._data)) { return TypeNode::make_Str(); } else { @@ -4385,8 +4554,8 @@ struct Checker { } } { - const auto& _match_84 = lt; - if (std::holds_alternative::Float>(_match_84._data)) { + const auto& _match_92 = lt; + if (std::holds_alternative::Float>(_match_92._data)) { return TypeNode::make_Float(); } else { @@ -4394,8 +4563,8 @@ struct Checker { } } { - const auto& _match_85 = rt; - if (std::holds_alternative::Float>(_match_85._data)) { + const auto& _match_93 = rt; + if (std::holds_alternative::Float>(_match_93._data)) { return TypeNode::make_Float(); } else { @@ -4403,8 +4572,8 @@ struct Checker { } } { - const auto& _match_86 = lt; - if (std::holds_alternative::Int>(_match_86._data)) { + const auto& _match_94 = lt; + if (std::holds_alternative::Int>(_match_94._data)) { return TypeNode::make_Int(); } else { @@ -4413,8 +4582,8 @@ struct Checker { } return TypeNode::make_Auto(); } - else if (std::holds_alternative::Unary>(_match_82._data)) { - auto& _v = std::get::Unary>(_match_82._data); + else if (std::holds_alternative::Unary>(_match_90._data)) { + auto& _v = std::get::Unary>(_match_90._data); auto& op = _v.op; auto& right = *_v.right; if ((op.token_type == TK_BANG) || (op.token_type == TK_NOT)) { @@ -4422,35 +4591,35 @@ struct Checker { } return (*this).infer_type(right); } - else if (std::holds_alternative::Logical>(_match_82._data)) { - auto& _v = std::get::Logical>(_match_82._data); + else if (std::holds_alternative::Logical>(_match_90._data)) { + auto& _v = std::get::Logical>(_match_90._data); auto& left = *_v.left; auto& op = _v.op; auto& right = *_v.right; return TypeNode::make_Bool(); } - else if (std::holds_alternative::Call>(_match_82._data)) { - auto& _v = std::get::Call>(_match_82._data); + else if (std::holds_alternative::Call>(_match_90._data)) { + auto& _v = std::get::Call>(_match_90._data); auto& callee = *_v.callee; auto& paren = _v.paren; auto& args = _v.args; return (*this).infer_call_type(callee); } - else if (std::holds_alternative::Grouping>(_match_82._data)) { - auto& _v = std::get::Grouping>(_match_82._data); + else if (std::holds_alternative::Grouping>(_match_90._data)) { + auto& _v = std::get::Grouping>(_match_90._data); auto& inner = *_v.inner; return (*this).infer_type(inner); } - else if (std::holds_alternative::Index>(_match_82._data)) { - auto& _v = std::get::Index>(_match_82._data); + else if (std::holds_alternative::Index>(_match_90._data)) { + auto& _v = std::get::Index>(_match_90._data); auto& object = *_v.object; auto& bracket = _v.bracket; auto& index = *_v.index; auto ot = (*this).infer_type(object); { - const auto& _match_87 = ot; - if (std::holds_alternative::Array>(_match_87._data)) { - auto& _v = std::get::Array>(_match_87._data); + const auto& _match_95 = ot; + if (std::holds_alternative::Array>(_match_95._data)) { + auto& _v = std::get::Array>(_match_95._data); auto& inner = *_v.inner; return inner; } @@ -4459,8 +4628,8 @@ struct Checker { } } } - else if (std::holds_alternative::Vector>(_match_82._data)) { - auto& _v = std::get::Vector>(_match_82._data); + else if (std::holds_alternative::Vector>(_match_90._data)) { + auto& _v = std::get::Vector>(_match_90._data); auto& elements = _v.elements; if ((static_cast(elements.size()) > INT64_C(0))) { auto inner = (*this).infer_type(elements[INT64_C(0)]); @@ -4468,24 +4637,24 @@ struct Checker { } return TypeNode::make_Array(TypeNode::make_Auto()); } - else if (std::holds_alternative::Cast>(_match_82._data)) { - auto& _v = std::get::Cast>(_match_82._data); + else if (std::holds_alternative::Cast>(_match_90._data)) { + auto& _v = std::get::Cast>(_match_90._data); auto& expr = *_v.expr; auto& target_type = _v.target_type; return target_type; } - else if (std::holds_alternative::This>(_match_82._data)) { - auto& _v = std::get::This>(_match_82._data); + else if (std::holds_alternative::This>(_match_90._data)) { + auto& _v = std::get::This>(_match_90._data); auto& kw = _v.keyword; return TypeNode::make_Custom(this->current_class_name, {}); } - else if (std::holds_alternative::Own>(_match_82._data)) { - auto& _v = std::get::Own>(_match_82._data); + else if (std::holds_alternative::Own>(_match_90._data)) { + auto& _v = std::get::Own>(_match_90._data); auto& expr = *_v.expr; return (*this).infer_type(expr); } - else if (std::holds_alternative::AddressOf>(_match_82._data)) { - auto& _v = std::get::AddressOf>(_match_82._data); + else if (std::holds_alternative::AddressOf>(_match_90._data)) { + auto& _v = std::get::AddressOf>(_match_90._data); auto& expr = *_v.expr; return TypeNode::make_Ptr((*this).infer_type(expr)); } @@ -4497,9 +4666,9 @@ struct Checker { TypeNode infer_call_type(const Expr& callee) { { - const auto& _match_88 = callee; - if (std::holds_alternative::Variable>(_match_88._data)) { - auto& _v = std::get::Variable>(_match_88._data); + const auto& _match_96 = callee; + if (std::holds_alternative::Variable>(_match_96._data)) { + auto& _v = std::get::Variable>(_match_96._data); auto& name = _v.name; if ((this->known_funcs.count(name.lexeme) > 0)) { ExternFn fi = this->known_funcs[name.lexeme]; @@ -4513,20 +4682,20 @@ struct Checker { } return TypeNode::make_Auto(); } - else if (std::holds_alternative::Get>(_match_88._data)) { - auto& _v = std::get::Get>(_match_88._data); + else if (std::holds_alternative::Get>(_match_96._data)) { + auto& _v = std::get::Get>(_match_96._data); auto& object = *_v.object; auto& name = _v.name; return TypeNode::make_Auto(); } - else if (std::holds_alternative::StaticGet>(_match_88._data)) { - auto& _v = std::get::StaticGet>(_match_88._data); + else if (std::holds_alternative::StaticGet>(_match_96._data)) { + auto& _v = std::get::StaticGet>(_match_96._data); auto& object = *_v.object; auto& name = _v.name; { - const auto& _match_89 = object; - if (std::holds_alternative::Variable>(_match_89._data)) { - auto& _v = std::get::Variable>(_match_89._data); + const auto& _match_97 = object; + if (std::holds_alternative::Variable>(_match_97._data)) { + auto& _v = std::get::Variable>(_match_97._data); auto& obj_name = _v.name; if ((this->known_enums.count(obj_name.lexeme) > 0)) { return TypeNode::make_Custom(obj_name.lexeme, {}); @@ -4579,9 +4748,9 @@ struct Checker { void collect_decl(const Stmt& s) { { - const auto& _match_90 = s; - if (std::holds_alternative::Function>(_match_90._data)) { - auto& _v = std::get::Function>(_match_90._data); + const auto& _match_98 = s; + if (std::holds_alternative::Function>(_match_98._data)) { + auto& _v = std::get::Function>(_match_98._data); auto& name = _v.name; auto& params = _v.params; auto& return_type = _v.return_type; @@ -4593,15 +4762,15 @@ struct Checker { auto& type_params = _v.type_params; this->known_funcs[name.lexeme] = ExternFn(name.lexeme, name.lexeme, return_type, params); } - else if (std::holds_alternative::Class>(_match_90._data)) { - auto& _v = std::get::Class>(_match_90._data); + else if (std::holds_alternative::Class>(_match_98._data)) { + auto& _v = std::get::Class>(_match_98._data); auto& name = _v.name; auto& body = _v.body; auto& visibility = _v.visibility; this->known_classes[name.lexeme] = body; } - else if (std::holds_alternative::Enum>(_match_90._data)) { - auto& _v = std::get::Enum>(_match_90._data); + else if (std::holds_alternative::Enum>(_match_98._data)) { + auto& _v = std::get::Enum>(_match_98._data); auto& name = _v.name; auto& variants = _v.variants; auto& methods = _v.methods; @@ -4609,8 +4778,8 @@ struct Checker { auto& enum_tp = _v.type_params; this->known_enums[name.lexeme] = variants; } - else if (std::holds_alternative::Const>(_match_90._data)) { - auto& _v = std::get::Const>(_match_90._data); + else if (std::holds_alternative::Const>(_match_98._data)) { + auto& _v = std::get::Const>(_match_98._data); auto& name = _v.name; auto& const_type = _v.const_type; auto& value = _v.value; @@ -4618,16 +4787,16 @@ struct Checker { auto& comptime_mode = _v.comptime_mode; (*this).declare(name.lexeme, const_type, std::string("const"), false, false, name); } - else if (std::holds_alternative::Struct>(_match_90._data)) { - auto& _v = std::get::Struct>(_match_90._data); + else if (std::holds_alternative::Struct>(_match_98._data)) { + auto& _v = std::get::Struct>(_match_98._data); auto& name = _v.name; auto& body = _v.body; auto& visibility = _v.visibility; auto& struct_tp = _v.type_params; this->known_classes[name.lexeme] = body; } - else if (std::holds_alternative::Namespace>(_match_90._data)) { - auto& _v = std::get::Namespace>(_match_90._data); + else if (std::holds_alternative::Namespace>(_match_98._data)) { + auto& _v = std::get::Namespace>(_match_98._data); auto& name = _v.name; auto& body = _v.body; auto& visibility = _v.visibility; @@ -4635,8 +4804,8 @@ struct Checker { (*this).collect_decl(ns_stmt); } } - else if (std::holds_alternative::Extern>(_match_90._data)) { - auto& _v = std::get::Extern>(_match_90._data); + else if (std::holds_alternative::Extern>(_match_98._data)) { + auto& _v = std::get::Extern>(_match_98._data); auto& header = _v.header; auto& import_path = _v.import_path; auto& link_lib = _v.link_lib; @@ -4658,14 +4827,14 @@ struct Checker { void check_stmt(const Stmt& s) { { - const auto& _match_91 = s; - if (std::holds_alternative::ExprStmt>(_match_91._data)) { - auto& _v = std::get::ExprStmt>(_match_91._data); + const auto& _match_99 = s; + if (std::holds_alternative::ExprStmt>(_match_99._data)) { + auto& _v = std::get::ExprStmt>(_match_99._data); auto& expr = _v.expr; (*this).check_expr(expr); } - else if (std::holds_alternative::Let>(_match_91._data)) { - auto& _v = std::get::Let>(_match_91._data); + else if (std::holds_alternative::Let>(_match_99._data)) { + auto& _v = std::get::Let>(_match_99._data); auto& name = _v.name; auto& var_type = _v.var_type; auto& initializer = _v.initializer; @@ -4675,11 +4844,11 @@ struct Checker { (*this).check_expr(initializer); auto init_type = (*this).infer_type(initializer); { - const auto& _match_92 = var_type; - if (std::holds_alternative::Auto>(_match_92._data)) { + const auto& _match_100 = var_type; + if (std::holds_alternative::Auto>(_match_100._data)) { /* pass */ } - else if (_match_92._tag == "None") { + else if (_match_100._tag == "None") { /* pass */ } else { @@ -4690,8 +4859,8 @@ struct Checker { } (*this).declare(name.lexeme, var_type, std::string("var"), is_ref, true, name); } - else if (std::holds_alternative::Const>(_match_91._data)) { - auto& _v = std::get::Const>(_match_91._data); + else if (std::holds_alternative::Const>(_match_99._data)) { + auto& _v = std::get::Const>(_match_99._data); auto& name = _v.name; auto& const_type = _v.const_type; auto& value = _v.value; @@ -4699,20 +4868,20 @@ struct Checker { auto& comptime_mode = _v.comptime_mode; (*this).check_expr(value); } - else if (std::holds_alternative::Return>(_match_91._data)) { - auto& _v = std::get::Return>(_match_91._data); + else if (std::holds_alternative::Return>(_match_99._data)) { + auto& _v = std::get::Return>(_match_99._data); auto& keyword = _v.keyword; auto& value = _v.value; (*this).check_expr(value); { - const auto& _match_93 = this->current_return_type; - if (_match_93._tag == "None") { + const auto& _match_101 = this->current_return_type; + if (_match_101._tag == "None") { /* pass */ } - else if (std::holds_alternative::Void>(_match_93._data)) { + else if (std::holds_alternative::Void>(_match_101._data)) { { - const auto& _match_94 = value; - if (_match_94._tag == "None") { + const auto& _match_102 = value; + if (_match_102._tag == "None") { /* pass */ } else { @@ -4728,8 +4897,8 @@ struct Checker { } } } - else if (std::holds_alternative::If>(_match_91._data)) { - auto& _v = std::get::If>(_match_91._data); + else if (std::holds_alternative::If>(_match_99._data)) { + auto& _v = std::get::If>(_match_99._data); auto& condition = _v.condition; auto& then_branch = *_v.then_branch; auto& else_branch = *_v.else_branch; @@ -4737,15 +4906,15 @@ struct Checker { (*this).check_stmt(then_branch); (*this).check_stmt(else_branch); } - else if (std::holds_alternative::While>(_match_91._data)) { - auto& _v = std::get::While>(_match_91._data); + else if (std::holds_alternative::While>(_match_99._data)) { + auto& _v = std::get::While>(_match_99._data); auto& condition = _v.condition; auto& body = *_v.body; (*this).check_expr(condition); (*this).check_stmt(body); } - else if (std::holds_alternative::For>(_match_91._data)) { - auto& _v = std::get::For>(_match_91._data); + else if (std::holds_alternative::For>(_match_99._data)) { + auto& _v = std::get::For>(_match_99._data); auto& item_name = _v.item_name; auto& collection = _v.collection; auto& body = *_v.body; @@ -4756,9 +4925,9 @@ struct Checker { auto coll_type = (*this).infer_type(collection); auto item_type = TypeNode::make_Auto(); { - const auto& _match_95 = coll_type; - if (std::holds_alternative::Array>(_match_95._data)) { - auto& _v = std::get::Array>(_match_95._data); + const auto& _match_103 = coll_type; + if (std::holds_alternative::Array>(_match_103._data)) { + auto& _v = std::get::Array>(_match_103._data); auto& inner = *_v.inner; item_type = inner; } @@ -4770,8 +4939,8 @@ struct Checker { (*this).check_stmt(body); (*this).pop_scope(); } - else if (std::holds_alternative::Block>(_match_91._data)) { - auto& _v = std::get::Block>(_match_91._data); + else if (std::holds_alternative::Block>(_match_99._data)) { + auto& _v = std::get::Block>(_match_99._data); auto& statements = _v.statements; (*this).push_scope(); for (const auto& st : statements) { @@ -4779,8 +4948,8 @@ struct Checker { } (*this).pop_scope(); } - else if (std::holds_alternative::Function>(_match_91._data)) { - auto& _v = std::get::Function>(_match_91._data); + else if (std::holds_alternative::Function>(_match_99._data)) { + auto& _v = std::get::Function>(_match_99._data); auto& name = _v.name; auto& params = _v.params; auto& return_type = _v.return_type; @@ -4792,23 +4961,23 @@ struct Checker { auto& type_params = _v.type_params; (*this).check_function(name, params, return_type, body); } - else if (std::holds_alternative::Class>(_match_91._data)) { - auto& _v = std::get::Class>(_match_91._data); + else if (std::holds_alternative::Class>(_match_99._data)) { + auto& _v = std::get::Class>(_match_99._data); auto& name = _v.name; auto& body = _v.body; auto& visibility = _v.visibility; (*this).check_class(name, body); } - else if (std::holds_alternative::Struct>(_match_91._data)) { - auto& _v = std::get::Struct>(_match_91._data); + else if (std::holds_alternative::Struct>(_match_99._data)) { + auto& _v = std::get::Struct>(_match_99._data); auto& name = _v.name; auto& body = _v.body; auto& visibility = _v.visibility; auto& struct_tp = _v.type_params; /* pass */ } - else if (std::holds_alternative::Enum>(_match_91._data)) { - auto& _v = std::get::Enum>(_match_91._data); + else if (std::holds_alternative::Enum>(_match_99._data)) { + auto& _v = std::get::Enum>(_match_99._data); auto& name = _v.name; auto& variants = _v.variants; auto& methods = _v.methods; @@ -4816,16 +4985,16 @@ struct Checker { auto& enum_tp = _v.type_params; /* pass */ } - else if (std::holds_alternative::Match>(_match_91._data)) { - auto& _v = std::get::Match>(_match_91._data); + else if (std::holds_alternative::Match>(_match_99._data)) { + auto& _v = std::get::Match>(_match_99._data); auto& expr = _v.expr; auto& arm_patterns = _v.arm_patterns; auto& arm_bodies = _v.arm_bodies; (*this).check_expr(expr); (*this).check_match(expr, arm_patterns, arm_bodies); } - else if (std::holds_alternative::Try>(_match_91._data)) { - auto& _v = std::get::Try>(_match_91._data); + else if (std::holds_alternative::Try>(_match_99._data)) { + auto& _v = std::get::Try>(_match_99._data); auto& try_body = *_v.try_body; auto& catch_body = *_v.catch_body; auto& exception_name = _v.exception_name; @@ -4839,8 +5008,8 @@ struct Checker { (*this).check_stmt(catch_body); (*this).pop_scope(); } - else if (std::holds_alternative::Namespace>(_match_91._data)) { - auto& _v = std::get::Namespace>(_match_91._data); + else if (std::holds_alternative::Namespace>(_match_99._data)) { + auto& _v = std::get::Namespace>(_match_99._data); auto& name = _v.name; auto& body = _v.body; auto& visibility = _v.visibility; @@ -4853,34 +5022,34 @@ struct Checker { } (*this).pop_scope(); } - else if (std::holds_alternative::Import>(_match_91._data)) { - auto& _v = std::get::Import>(_match_91._data); + else if (std::holds_alternative::Import>(_match_99._data)) { + auto& _v = std::get::Import>(_match_99._data); auto& path = _v.path; auto& alias = _v.alias; /* pass */ } - else if (std::holds_alternative::Break>(_match_91._data)) { - auto& _v = std::get::Break>(_match_91._data); + else if (std::holds_alternative::Break>(_match_99._data)) { + auto& _v = std::get::Break>(_match_99._data); auto& keyword = _v.keyword; /* pass */ } - else if (std::holds_alternative::Continue>(_match_91._data)) { - auto& _v = std::get::Continue>(_match_91._data); + else if (std::holds_alternative::Continue>(_match_99._data)) { + auto& _v = std::get::Continue>(_match_99._data); auto& keyword = _v.keyword; /* pass */ } - else if (std::holds_alternative::Pass>(_match_91._data)) { - auto& _v = std::get::Pass>(_match_91._data); + else if (std::holds_alternative::Pass>(_match_99._data)) { + auto& _v = std::get::Pass>(_match_99._data); auto& keyword = _v.keyword; /* pass */ } - else if (std::holds_alternative::CppBlock>(_match_91._data)) { - auto& _v = std::get::CppBlock>(_match_91._data); + else if (std::holds_alternative::CppBlock>(_match_99._data)) { + auto& _v = std::get::CppBlock>(_match_99._data); auto& code = _v.code; /* pass */ } - else if (std::holds_alternative::Extern>(_match_91._data)) { - auto& _v = std::get::Extern>(_match_91._data); + else if (std::holds_alternative::Extern>(_match_99._data)) { + auto& _v = std::get::Extern>(_match_99._data); auto& header = _v.header; auto& import_path = _v.import_path; auto& link_lib = _v.link_lib; @@ -4917,9 +5086,9 @@ struct Checker { (*this).declare(std::string("this"), TypeNode::make_Custom(name.lexeme, {}), std::string("var"), false, false, name); for (const auto& s : body) { { - const auto& _match_96 = s; - if (std::holds_alternative::Function>(_match_96._data)) { - auto& _v = std::get::Function>(_match_96._data); + const auto& _match_104 = s; + if (std::holds_alternative::Function>(_match_104._data)) { + auto& _v = std::get::Function>(_match_104._data); auto& fname = _v.name; auto& params = _v.params; auto& return_type = _v.return_type; @@ -4931,8 +5100,8 @@ struct Checker { auto& fn_tp = _v.type_params; (*this).check_function(fname, params, return_type, fbody); } - else if (std::holds_alternative::Let>(_match_96._data)) { - auto& _v = std::get::Let>(_match_96._data); + else if (std::holds_alternative::Let>(_match_104._data)) { + auto& _v = std::get::Let>(_match_104._data); auto& lname = _v.name; auto& var_type = _v.var_type; auto& initializer = _v.initializer; @@ -4953,46 +5122,46 @@ struct Checker { void check_expr(const Expr& e) { { - const auto& _match_97 = e; - if (_match_97._tag == "None") { + const auto& _match_105 = e; + if (_match_105._tag == "None") { /* pass */ } - else if (std::holds_alternative::Variable>(_match_97._data)) { - auto& _v = std::get::Variable>(_match_97._data); + else if (std::holds_alternative::Variable>(_match_105._data)) { + auto& _v = std::get::Variable>(_match_105._data); auto& name = _v.name; if ((!(*this).resolve(name.lexeme))) { (*this).error(((std::string("Undefined variable '") + (name.lexeme)) + std::string("'")), name); } } - else if (std::holds_alternative::Binary>(_match_97._data)) { - auto& _v = std::get::Binary>(_match_97._data); + else if (std::holds_alternative::Binary>(_match_105._data)) { + auto& _v = std::get::Binary>(_match_105._data); auto& left = *_v.left; auto& op = _v.op; auto& right = *_v.right; (*this).check_expr(left); (*this).check_expr(right); } - else if (std::holds_alternative::Unary>(_match_97._data)) { - auto& _v = std::get::Unary>(_match_97._data); + else if (std::holds_alternative::Unary>(_match_105._data)) { + auto& _v = std::get::Unary>(_match_105._data); auto& op = _v.op; auto& right = *_v.right; (*this).check_expr(right); } - else if (std::holds_alternative::Logical>(_match_97._data)) { - auto& _v = std::get::Logical>(_match_97._data); + else if (std::holds_alternative::Logical>(_match_105._data)) { + auto& _v = std::get::Logical>(_match_105._data); auto& left = *_v.left; auto& op = _v.op; auto& right = *_v.right; (*this).check_expr(left); (*this).check_expr(right); } - else if (std::holds_alternative::Grouping>(_match_97._data)) { - auto& _v = std::get::Grouping>(_match_97._data); + else if (std::holds_alternative::Grouping>(_match_105._data)) { + auto& _v = std::get::Grouping>(_match_105._data); auto& inner = *_v.inner; (*this).check_expr(inner); } - else if (std::holds_alternative::Call>(_match_97._data)) { - auto& _v = std::get::Call>(_match_97._data); + else if (std::holds_alternative::Call>(_match_105._data)) { + auto& _v = std::get::Call>(_match_105._data); auto& callee = *_v.callee; auto& paren = _v.paren; auto& args = _v.args; @@ -5002,8 +5171,8 @@ struct Checker { } (*this).check_call_args(callee, args, paren); } - else if (std::holds_alternative::Assign>(_match_97._data)) { - auto& _v = std::get::Assign>(_match_97._data); + else if (std::holds_alternative::Assign>(_match_105._data)) { + auto& _v = std::get::Assign>(_match_105._data); auto& name = _v.name; auto& value = *_v.value; (*this).check_expr(value); @@ -5017,16 +5186,16 @@ struct Checker { } } } - else if (std::holds_alternative::Index>(_match_97._data)) { - auto& _v = std::get::Index>(_match_97._data); + else if (std::holds_alternative::Index>(_match_105._data)) { + auto& _v = std::get::Index>(_match_105._data); auto& object = *_v.object; auto& bracket = _v.bracket; auto& index = *_v.index; (*this).check_expr(object); (*this).check_expr(index); } - else if (std::holds_alternative::IndexSet>(_match_97._data)) { - auto& _v = std::get::IndexSet>(_match_97._data); + else if (std::holds_alternative::IndexSet>(_match_105._data)) { + auto& _v = std::get::IndexSet>(_match_105._data); auto& object = *_v.object; auto& bracket = _v.bracket; auto& index = *_v.index; @@ -5035,15 +5204,15 @@ struct Checker { (*this).check_expr(index); (*this).check_expr(value); } - else if (std::holds_alternative::Vector>(_match_97._data)) { - auto& _v = std::get::Vector>(_match_97._data); + else if (std::holds_alternative::Vector>(_match_105._data)) { + auto& _v = std::get::Vector>(_match_105._data); auto& elements = _v.elements; for (const auto& el : elements) { (*this).check_expr(el); } } - else if (std::holds_alternative::Map>(_match_97._data)) { - auto& _v = std::get::Map>(_match_97._data); + else if (std::holds_alternative::Map>(_match_105._data)) { + auto& _v = std::get::Map>(_match_105._data); auto& keys = _v.keys; auto& values = _v.values; for (const auto& k : keys) { @@ -5053,28 +5222,28 @@ struct Checker { (*this).check_expr(v); } } - else if (std::holds_alternative::Get>(_match_97._data)) { - auto& _v = std::get::Get>(_match_97._data); + else if (std::holds_alternative::Get>(_match_105._data)) { + auto& _v = std::get::Get>(_match_105._data); auto& object = *_v.object; auto& name = _v.name; (*this).check_expr(object); } - else if (std::holds_alternative::Set>(_match_97._data)) { - auto& _v = std::get::Set>(_match_97._data); + else if (std::holds_alternative::Set>(_match_105._data)) { + auto& _v = std::get::Set>(_match_105._data); auto& object = *_v.object; auto& name = _v.name; auto& value = *_v.value; (*this).check_expr(object); (*this).check_expr(value); } - else if (std::holds_alternative::StaticGet>(_match_97._data)) { - auto& _v = std::get::StaticGet>(_match_97._data); + else if (std::holds_alternative::StaticGet>(_match_105._data)) { + auto& _v = std::get::StaticGet>(_match_105._data); auto& object = *_v.object; auto& name = _v.name; { - const auto& _match_98 = object; - if (std::holds_alternative::Variable>(_match_98._data)) { - auto& _v = std::get::Variable>(_match_98._data); + const auto& _match_106 = object; + if (std::holds_alternative::Variable>(_match_106._data)) { + auto& _v = std::get::Variable>(_match_106._data); auto& tok = _v.name; /* pass */ } @@ -5083,26 +5252,26 @@ struct Checker { } } } - else if (std::holds_alternative::Cast>(_match_97._data)) { - auto& _v = std::get::Cast>(_match_97._data); + else if (std::holds_alternative::Cast>(_match_105._data)) { + auto& _v = std::get::Cast>(_match_105._data); auto& expr = *_v.expr; auto& target_type = _v.target_type; (*this).check_expr(expr); } - else if (std::holds_alternative::Throw>(_match_97._data)) { - auto& _v = std::get::Throw>(_match_97._data); + else if (std::holds_alternative::Throw>(_match_105._data)) { + auto& _v = std::get::Throw>(_match_105._data); auto& expr = *_v.expr; (*this).check_expr(expr); } - else if (std::holds_alternative::Range>(_match_97._data)) { - auto& _v = std::get::Range>(_match_97._data); + else if (std::holds_alternative::Range>(_match_105._data)) { + auto& _v = std::get::Range>(_match_105._data); auto& start = *_v.start; auto& end = *_v.end; (*this).check_expr(start); (*this).check_expr(end); } - else if (std::holds_alternative::Lambda>(_match_97._data)) { - auto& _v = std::get::Lambda>(_match_97._data); + else if (std::holds_alternative::Lambda>(_match_105._data)) { + auto& _v = std::get::Lambda>(_match_105._data); auto& params = _v.params; auto& body = *_v.body; (*this).push_scope(); @@ -5112,8 +5281,8 @@ struct Checker { (*this).check_expr(body); (*this).pop_scope(); } - else if (std::holds_alternative::BlockLambda>(_match_97._data)) { - auto& _v = std::get::BlockLambda>(_match_97._data); + else if (std::holds_alternative::BlockLambda>(_match_105._data)) { + auto& _v = std::get::BlockLambda>(_match_105._data); auto& params = _v.params; auto& body_id = _v.body_id; (*this).push_scope(); @@ -5122,13 +5291,13 @@ struct Checker { } (*this).pop_scope(); } - else if (std::holds_alternative::Own>(_match_97._data)) { - auto& _v = std::get::Own>(_match_97._data); + else if (std::holds_alternative::Own>(_match_105._data)) { + auto& _v = std::get::Own>(_match_105._data); auto& expr = *_v.expr; (*this).check_expr(expr); } - else if (std::holds_alternative::AddressOf>(_match_97._data)) { - auto& _v = std::get::AddressOf>(_match_97._data); + else if (std::holds_alternative::AddressOf>(_match_105._data)) { + auto& _v = std::get::AddressOf>(_match_105._data); auto& expr = *_v.expr; (*this).check_expr(expr); } @@ -5140,9 +5309,9 @@ struct Checker { void check_call_args(const Expr& callee, const std::vector& args, const Token& paren) { { - const auto& _match_99 = callee; - if (std::holds_alternative::Variable>(_match_99._data)) { - auto& _v = std::get::Variable>(_match_99._data); + const auto& _match_107 = callee; + if (std::holds_alternative::Variable>(_match_107._data)) { + auto& _v = std::get::Variable>(_match_107._data); auto& name = _v.name; if ((this->known_funcs.count(name.lexeme) > 0)) { ExternFn fi = this->known_funcs[name.lexeme]; @@ -5509,27 +5678,27 @@ struct Parser { std::string type_to_string(const TypeNode& t) { { - const auto& _match_100 = t; - if (std::holds_alternative::Int>(_match_100._data)) { + const auto& _match_108 = t; + if (std::holds_alternative::Int>(_match_108._data)) { return std::string("int64_t"); } - else if (std::holds_alternative::Float>(_match_100._data)) { + else if (std::holds_alternative::Float>(_match_108._data)) { return std::string("double"); } - else if (std::holds_alternative::Str>(_match_100._data)) { + else if (std::holds_alternative::Str>(_match_108._data)) { return std::string("std::string"); } - else if (std::holds_alternative::Bool>(_match_100._data)) { + else if (std::holds_alternative::Bool>(_match_108._data)) { return std::string("bool"); } - else if (std::holds_alternative::Void>(_match_100._data)) { + else if (std::holds_alternative::Void>(_match_108._data)) { return std::string("void"); } - else if (std::holds_alternative::Auto>(_match_100._data)) { + else if (std::holds_alternative::Auto>(_match_108._data)) { return std::string("auto"); } - else if (std::holds_alternative::Custom>(_match_100._data)) { - auto& _v = std::get::Custom>(_match_100._data); + else if (std::holds_alternative::Custom>(_match_108._data)) { + auto& _v = std::get::Custom>(_match_108._data); auto& name = _v.name; auto& type_args = _v.type_args; if ((static_cast(type_args.size()) > INT64_C(0))) { @@ -5541,35 +5710,35 @@ struct Parser { } return name; } - else if (std::holds_alternative::Array>(_match_100._data)) { - auto& _v = std::get::Array>(_match_100._data); + else if (std::holds_alternative::Array>(_match_108._data)) { + auto& _v = std::get::Array>(_match_108._data); auto& inner = *_v.inner; return ((std::string("std::vector<") + ((*this).type_to_string(inner))) + std::string(">")); } - else if (std::holds_alternative::Int8>(_match_100._data)) { + else if (std::holds_alternative::Int8>(_match_108._data)) { return std::string("int8_t"); } - else if (std::holds_alternative::Int16>(_match_100._data)) { + else if (std::holds_alternative::Int16>(_match_108._data)) { return std::string("int16_t"); } - else if (std::holds_alternative::Int32>(_match_100._data)) { + else if (std::holds_alternative::Int32>(_match_108._data)) { return std::string("int32_t"); } - else if (std::holds_alternative::Float32>(_match_100._data)) { + else if (std::holds_alternative::Float32>(_match_108._data)) { return std::string("float"); } - else if (std::holds_alternative::USize>(_match_100._data)) { + else if (std::holds_alternative::USize>(_match_108._data)) { return std::string("size_t"); } - else if (std::holds_alternative::CString>(_match_100._data)) { + else if (std::holds_alternative::CString>(_match_108._data)) { return std::string("const char*"); } - else if (std::holds_alternative::Ptr>(_match_100._data)) { - auto& _v = std::get::Ptr>(_match_100._data); + else if (std::holds_alternative::Ptr>(_match_108._data)) { + auto& _v = std::get::Ptr>(_match_108._data); auto& inner = *_v.inner; return ((std::string("") + ((*this).type_to_string(inner))) + std::string("*")); } - else if (std::holds_alternative::Bytes>(_match_100._data)) { + else if (std::holds_alternative::Bytes>(_match_108._data)) { return std::string("std::vector"); } else { @@ -5587,20 +5756,20 @@ struct Parser { if ((*this).match_any(std::vector{TK_EQUAL})) { Expr value = (*this).assignment(); { - const auto& _match_101 = expr; - if (std::holds_alternative::Variable>(_match_101._data)) { - auto& _v = std::get::Variable>(_match_101._data); + const auto& _match_109 = expr; + if (std::holds_alternative::Variable>(_match_109._data)) { + auto& _v = std::get::Variable>(_match_109._data); auto& name = _v.name; return Expr::make_Assign(name, value); } - else if (std::holds_alternative::Get>(_match_101._data)) { - auto& _v = std::get::Get>(_match_101._data); + else if (std::holds_alternative::Get>(_match_109._data)) { + auto& _v = std::get::Get>(_match_109._data); auto& object = *_v.object; auto& name = _v.name; return Expr::make_Set(object, name, value); } - else if (std::holds_alternative::Index>(_match_101._data)) { - auto& _v = std::get::Index>(_match_101._data); + else if (std::holds_alternative::Index>(_match_109._data)) { + auto& _v = std::get::Index>(_match_109._data); auto& object = *_v.object; auto& bracket = _v.bracket; auto& index = *_v.index; @@ -5629,22 +5798,22 @@ struct Parser { } auto op_token = Token(base_type, base_lexeme, compound_op.line, compound_op.col); { - const auto& _match_102 = expr; - if (std::holds_alternative::Variable>(_match_102._data)) { - auto& _v = std::get::Variable>(_match_102._data); + const auto& _match_110 = expr; + if (std::holds_alternative::Variable>(_match_110._data)) { + auto& _v = std::get::Variable>(_match_110._data); auto& name = _v.name; Expr bin = Expr::make_Binary(Expr::make_Variable(name), op_token, rhs); return Expr::make_Assign(name, bin); } - else if (std::holds_alternative::Get>(_match_102._data)) { - auto& _v = std::get::Get>(_match_102._data); + else if (std::holds_alternative::Get>(_match_110._data)) { + auto& _v = std::get::Get>(_match_110._data); auto& object = *_v.object; auto& name = _v.name; Expr bin = Expr::make_Binary(Expr::make_Get(object, name), op_token, rhs); return Expr::make_Set(object, name, bin); } - else if (std::holds_alternative::Index>(_match_102._data)) { - auto& _v = std::get::Index>(_match_102._data); + else if (std::holds_alternative::Index>(_match_110._data)) { + auto& _v = std::get::Index>(_match_110._data); auto& object = *_v.object; auto& bracket = _v.bracket; auto& index = *_v.index; @@ -5740,9 +5909,9 @@ struct Parser { Expr call() { Expr expr = (*this).primary(); { - const auto& _match_103 = expr; - if (std::holds_alternative::Variable>(_match_103._data)) { - auto& _v = std::get::Variable>(_match_103._data); + const auto& _match_111 = expr; + if (std::holds_alternative::Variable>(_match_111._data)) { + auto& _v = std::get::Variable>(_match_111._data); auto& tok = _v.name; if ((*this).check(TK_LEFT_BRACKET)) { int64_t save_pos = this->current; @@ -5821,9 +5990,9 @@ struct Parser { Expr finish_call(const Expr& callee) { { - const auto& _match_104 = callee; - if (std::holds_alternative::Variable>(_match_104._data)) { - auto& _v = std::get::Variable>(_match_104._data); + const auto& _match_112 = callee; + if (std::holds_alternative::Variable>(_match_112._data)) { + auto& _v = std::get::Variable>(_match_112._data); auto& name = _v.name; if ((name.lexeme == std::string("cast"))) { Expr expr = (*this).expression(); @@ -6349,11 +6518,11 @@ struct Parser { std::vector old_fnames = {}; bool is_unit_type = false; { - const auto& _match_105 = vtype; - if (std::holds_alternative::NullType>(_match_105._data)) { + const auto& _match_113 = vtype; + if (std::holds_alternative::NullType>(_match_113._data)) { is_unit_type = true; } - else if (std::holds_alternative::Void>(_match_105._data)) { + else if (std::holds_alternative::Void>(_match_113._data)) { is_unit_type = true; } else { diff --git a/tests/test_cast.lv b/tests/test_cast.lv index 0905658..2f9ac0c 100644 --- a/tests/test_cast.lv +++ b/tests/test_cast.lv @@ -1,5 +1,9 @@ // Test 'as' keyword for type casting +import std::bytes + +// ── Numeric casts (existing) ───────────────────────────────── + void fn test_int_to_float(): int x = 42 float y = x as float @@ -35,10 +39,81 @@ void fn test_expr_cast(): float c = (a + b) as float lv_assert(c > 29.99 and c < 30.01, "expr cast (10+20) as float") +// ── Cross-family casts (new) ───────────────────────────────── + +void fn test_string_to_int(): + string s = "123" + int x = s as int + lv_assert(x == 123, "string->int") + +void fn test_string_to_int32(): + string s = "42" + int32 x = s as int32 + lv_assert(x == 42, "string->int32") + +void fn test_string_to_float(): + string s = "3.14" + float x = s as float + lv_assert(x > 3.13 and x < 3.15, "string->float") + +void fn test_int_to_string(): + int x = 42 + string s = x as string + lv_assert(s == "42", "int->string") + +void fn test_float_to_string(): + float x = 2.5 + string s = x as string + lv_assert(s.len() > 0, "float->string non-empty") + +void fn test_bool_to_string(): + bool b = true + string s = b as string + lv_assert(s == "true", "bool->string") + bool f = false + string s2 = f as string + lv_assert(s2 == "false", "false->string") + +void fn test_string_to_bytes(): + string s = "hello" + bytes b = s as bytes + lv_assert(b.len() == 5, "string->bytes len") + lv_assert(b.get(0) == 0x68, "string->bytes byte 0") + +void fn test_bytes_to_string(): + bytes b = bytes::from_string("world") + string s = b as string + lv_assert(s == "world", "bytes->string") + +void fn test_cross_family_chained(): + int x = 42 + string s = x as string + int y = s as int + lv_assert(y == 42, "int->string->int roundtrip") + +void fn test_literal_cast(): + int x = "99" as int + lv_assert(x == 99, "literal string->int") + string s = 77 as string + lv_assert(s == "77", "literal int->string") + void fn main(): + // Numeric test_int_to_float() test_float_to_int() test_int_sizes() test_float_sizes() test_chained() test_expr_cast() + // Cross-family + test_string_to_int() + test_string_to_int32() + test_string_to_float() + test_int_to_string() + test_float_to_string() + test_bool_to_string() + test_string_to_bytes() + test_bytes_to_string() + test_cross_family_chained() + test_literal_cast() + println("All cast tests passed!") From 67532acc7da53cc729bf93b44513660116032841 Mon Sep 17 00:00:00 2001 From: Raumberg Date: Tue, 17 Feb 2026 16:35:42 +0300 Subject: [PATCH 14/19] src: added trailing comma support --- src/parser.lv | 22 ++++++++++++++++++++++ stages/stage-latest.cpp | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+) diff --git a/src/parser.lv b/src/parser.lv index 510116a..67a0c02 100644 --- a/src/parser.lv +++ b/src/parser.lv @@ -209,6 +209,8 @@ class Parser: TypeNode first_arg = this.parse_type() type_args.push(first_arg) while this.match_any([TK_COMMA]): + if this.check(TK_RIGHT_BRACKET): + break type_args.push(this.parse_type()) if not this.check(TK_RIGHT_BRACKET): is_type_args = false @@ -389,6 +391,8 @@ class Parser: TypeNode first_t = this.parse_type() type_strs.push(this.type_to_string(first_t)) while this.match_any([TK_COMMA]): + if this.check(TK_RIGHT_BRACKET): + break TypeNode next_t = this.parse_type() type_strs.push(this.type_to_string(next_t)) if not this.check(TK_RIGHT_BRACKET): @@ -447,6 +451,8 @@ class Parser: this.skip_formatting() while this.match_any([TK_COMMA]): this.skip_formatting() + if this.check(TK_RIGHT_PAREN): + break this.match_any([TK_REF, TK_REF_MUT]) args.push(this.expression()) this.skip_formatting() @@ -491,6 +497,8 @@ class Parser: this.skip_formatting() while this.match_any([TK_COMMA]): this.skip_formatting() + if this.check(TK_RIGHT_BRACKET): + break elements.push(this.expression()) this.skip_formatting() this.skip_formatting() @@ -512,6 +520,8 @@ class Parser: values.push(value) while this.match_any([TK_COMMA]): this.skip_formatting() + if this.check(TK_RIGHT_BRACE): + break key = this.expression() this.skip_formatting() this.consume(TK_COLON, "Expect ':' after map key.") @@ -539,6 +549,8 @@ class Parser: this.skip_formatting() while this.match_any([TK_COMMA]): this.skip_formatting() + if this.check(TK_RIGHT_PAREN): + break p_mut = this.match_any([TK_REF_MUT]) p_ref = p_mut or this.match_any([TK_REF]) param_type = this.parse_type() @@ -703,6 +715,8 @@ class Parser: if not this.check(TK_RIGHT_PAREN): bindings.push(this.consume(TK_IDENTIFIER, "Expect binding name.").lexeme) while this.match_any([TK_COMMA]): + if this.check(TK_RIGHT_PAREN): + break bindings.push(this.consume(TK_IDENTIFIER, "Expect binding name.").lexeme) this.consume(TK_RIGHT_PAREN, "Expect ')' after bindings.") this.consume(TK_COLON, "Expect ':' after match pattern.") @@ -775,6 +789,8 @@ class Parser: if this.match_any([TK_LEFT_BRACKET]): type_params.push(this.consume(TK_IDENTIFIER, "Expect type parameter name.").lexeme) while this.match_any([TK_COMMA]): + if this.check(TK_RIGHT_BRACKET): + break type_params.push(this.consume(TK_IDENTIFIER, "Expect type parameter name.").lexeme) this.consume(TK_RIGHT_BRACKET, "Expect ']' after type parameters.") @@ -801,6 +817,8 @@ class Parser: if this.match_any([TK_LEFT_BRACKET]): type_params.push(this.consume(TK_IDENTIFIER, "Expect type parameter name.").lexeme) while this.match_any([TK_COMMA]): + if this.check(TK_RIGHT_BRACKET): + break type_params.push(this.consume(TK_IDENTIFIER, "Expect type parameter name.").lexeme) this.consume(TK_RIGHT_BRACKET, "Expect ']' after type parameters.") this.consume(TK_COLON, "Expect ':' after struct name.") @@ -816,6 +834,8 @@ class Parser: if this.match_any([TK_LEFT_BRACKET]): type_params.push(this.consume(TK_IDENTIFIER, "Expect type parameter name.").lexeme) while this.match_any([TK_COMMA]): + if this.check(TK_RIGHT_BRACKET): + break type_params.push(this.consume(TK_IDENTIFIER, "Expect type parameter name.").lexeme) this.consume(TK_RIGHT_BRACKET, "Expect ']' after type parameters.") this.consume(TK_COLON, "Expect ':' after enum name.") @@ -847,6 +867,8 @@ class Parser: fields.push(this.parse_type()) fnames.push(this.consume(TK_IDENTIFIER, "Expect field name.").lexeme) while this.match_any([TK_COMMA]): + if this.check(TK_RIGHT_PAREN): + break fields.push(this.parse_type()) fnames.push(this.consume(TK_IDENTIFIER, "Expect field name.").lexeme) this.consume(TK_RIGHT_PAREN, "Expect ')' after variant fields.") diff --git a/stages/stage-latest.cpp b/stages/stage-latest.cpp index c1b4478..fbe2c56 100644 --- a/stages/stage-latest.cpp +++ b/stages/stage-latest.cpp @@ -5626,6 +5626,9 @@ struct Parser { TypeNode first_arg = (*this).parse_type(); type_args.push_back(first_arg); while ((*this).match_any(std::vector{TK_COMMA})) { + if ((*this).check(TK_RIGHT_BRACKET)) { + break; + } type_args.push_back((*this).parse_type()); } if ((!(*this).check(TK_RIGHT_BRACKET))) { @@ -5922,6 +5925,9 @@ struct Parser { TypeNode first_t = (*this).parse_type(); type_strs.push_back((*this).type_to_string(first_t)); while ((*this).match_any(std::vector{TK_COMMA})) { + if ((*this).check(TK_RIGHT_BRACKET)) { + break; + } TypeNode next_t = (*this).parse_type(); type_strs.push_back((*this).type_to_string(next_t)); } @@ -6014,6 +6020,9 @@ struct Parser { (*this).skip_formatting(); while ((*this).match_any(std::vector{TK_COMMA})) { (*this).skip_formatting(); + if ((*this).check(TK_RIGHT_PAREN)) { + break; + } (*this).match_any(std::vector{TK_REF, TK_REF_MUT}); args.push_back((*this).expression()); (*this).skip_formatting(); @@ -6072,6 +6081,9 @@ struct Parser { (*this).skip_formatting(); while ((*this).match_any(std::vector{TK_COMMA})) { (*this).skip_formatting(); + if ((*this).check(TK_RIGHT_BRACKET)) { + break; + } elements.push_back((*this).expression()); (*this).skip_formatting(); } @@ -6096,6 +6108,9 @@ struct Parser { values.push_back(value); while ((*this).match_any(std::vector{TK_COMMA})) { (*this).skip_formatting(); + if ((*this).check(TK_RIGHT_BRACE)) { + break; + } key = (*this).expression(); (*this).skip_formatting(); (*this).consume(TK_COLON, std::string("Expect ':' after map key.")); @@ -6126,6 +6141,9 @@ struct Parser { (*this).skip_formatting(); while ((*this).match_any(std::vector{TK_COMMA})) { (*this).skip_formatting(); + if ((*this).check(TK_RIGHT_PAREN)) { + break; + } p_mut = (*this).match_any(std::vector{TK_REF_MUT}); p_ref = p_mut || (*this).match_any(std::vector{TK_REF}); param_type = (*this).parse_type(); @@ -6327,6 +6345,9 @@ struct Parser { if ((!(*this).check(TK_RIGHT_PAREN))) { bindings.push_back((*this).consume(TK_IDENTIFIER, std::string("Expect binding name.")).lexeme); while ((*this).match_any(std::vector{TK_COMMA})) { + if ((*this).check(TK_RIGHT_PAREN)) { + break; + } bindings.push_back((*this).consume(TK_IDENTIFIER, std::string("Expect binding name.")).lexeme); } } @@ -6410,6 +6431,9 @@ struct Parser { if ((*this).match_any(std::vector{TK_LEFT_BRACKET})) { type_params.push_back((*this).consume(TK_IDENTIFIER, std::string("Expect type parameter name.")).lexeme); while ((*this).match_any(std::vector{TK_COMMA})) { + if ((*this).check(TK_RIGHT_BRACKET)) { + break; + } type_params.push_back((*this).consume(TK_IDENTIFIER, std::string("Expect type parameter name.")).lexeme); } (*this).consume(TK_RIGHT_BRACKET, std::string("Expect ']' after type parameters.")); @@ -6438,6 +6462,9 @@ struct Parser { if ((*this).match_any(std::vector{TK_LEFT_BRACKET})) { type_params.push_back((*this).consume(TK_IDENTIFIER, std::string("Expect type parameter name.")).lexeme); while ((*this).match_any(std::vector{TK_COMMA})) { + if ((*this).check(TK_RIGHT_BRACKET)) { + break; + } type_params.push_back((*this).consume(TK_IDENTIFIER, std::string("Expect type parameter name.")).lexeme); } (*this).consume(TK_RIGHT_BRACKET, std::string("Expect ']' after type parameters.")); @@ -6456,6 +6483,9 @@ struct Parser { if ((*this).match_any(std::vector{TK_LEFT_BRACKET})) { type_params.push_back((*this).consume(TK_IDENTIFIER, std::string("Expect type parameter name.")).lexeme); while ((*this).match_any(std::vector{TK_COMMA})) { + if ((*this).check(TK_RIGHT_BRACKET)) { + break; + } type_params.push_back((*this).consume(TK_IDENTIFIER, std::string("Expect type parameter name.")).lexeme); } (*this).consume(TK_RIGHT_BRACKET, std::string("Expect ']' after type parameters.")); @@ -6495,6 +6525,9 @@ struct Parser { fields.push_back((*this).parse_type()); fnames.push_back((*this).consume(TK_IDENTIFIER, std::string("Expect field name.")).lexeme); while ((*this).match_any(std::vector{TK_COMMA})) { + if ((*this).check(TK_RIGHT_PAREN)) { + break; + } fields.push_back((*this).parse_type()); fnames.push_back((*this).consume(TK_IDENTIFIER, std::string("Expect field name.")).lexeme); } From 6b7e60573e56981ae0271431124f44e6ef472876 Mon Sep 17 00:00:00 2001 From: Raumberg Date: Tue, 17 Feb 2026 17:15:26 +0300 Subject: [PATCH 15/19] src: default arguments, method chaining --- src/ast.lv | 5 +- src/checker.lv | 51 +- src/codegen.lv | 202 ++++- src/parser.lv | 42 +- stages/stage-latest.cpp | 1445 ++++++++++++++++++++------------- tests/test_extend.lv | 13 + tests/test_named_args.lv | 121 +++ tests/test_std_collections.lv | 10 + tests/test_trailing_comma.lv | 44 + 9 files changed, 1310 insertions(+), 623 deletions(-) create mode 100644 tests/test_named_args.lv create mode 100644 tests/test_trailing_comma.lv diff --git a/src/ast.lv b/src/ast.lv index ea82921..ed7015f 100644 --- a/src/ast.lv +++ b/src/ast.lv @@ -54,7 +54,7 @@ enum Expr: Variable(Token name) Assign(Token name, Expr value) Logical(Expr left, Token op, Expr right) - Call(Expr callee, Token paren, vector[Expr] args) + Call(Expr callee, Token paren, vector[Expr] args, vector[string] arg_names) Index(Expr object, Token bracket, Expr index) IndexSet(Expr object, Token bracket, Expr index, Expr value) Vector(vector[Expr] elements) @@ -82,6 +82,7 @@ struct ExternFn: string cpp_name TypeNode return_type vector[Param] params + vector[Expr] param_defaults // ── Match arm ──────────────────────────────────────────────── @@ -102,7 +103,7 @@ enum Stmt: For(Token item_name, Expr collection, Stmt body, bool is_ref, bool is_mut) Block(vector[Stmt] statements) Try(Stmt try_body, Stmt catch_body, string exception_name) - Function(Token name, vector[Param] params, TypeNode return_type, vector[Stmt] body, bool is_inline, int comptime_mode, bool is_static, string visibility, vector[string] type_params) + Function(Token name, vector[Param] params, TypeNode return_type, vector[Stmt] body, bool is_inline, int comptime_mode, bool is_static, string visibility, vector[string] type_params, vector[Expr] param_defaults) Class(Token name, vector[Stmt] body, string visibility) Struct(Token name, vector[Stmt] body, string visibility, vector[string] type_params) Enum(Token name, vector[EnumVariantNode] variants, vector[Stmt] methods, string visibility, vector[string] type_params) diff --git a/src/checker.lv b/src/checker.lv index aa18ee5..f6c87dc 100644 --- a/src/checker.lv +++ b/src/checker.lv @@ -361,7 +361,7 @@ class Checker: return this.infer_type(right) Logical(left, op, right): return TypeNode::Bool() - Call(callee, paren, args): + Call(callee, paren, args, arg_names): return this.infer_call_type(callee) Grouping(inner): return this.infer_type(inner) @@ -432,10 +432,11 @@ class Checker: // Runtime functions that are always available // Public API: functions users call directly (no __ prefix) // All __ prefixed runtime functions are auto-resolved by resolve() - vector[string] builtins = ["print", "println", "lv_assert", "to_string", "to_int", "to_float", "input", "typeof", "len", "exit", "abs", "cast"] + vector[string] builtins = ["print", "println", "lv_assert", "to_string", "input", "typeof", "len", "exit", "abs", "cast"] vector[Param] empty_params = [] for ref name in builtins: - this.known_funcs[name] = ExternFn(name, name, TypeNode::Auto(), empty_params) + vector[Expr] empty_defaults = [] + this.known_funcs[name] = ExternFn(name, name, TypeNode::Auto(), empty_params, empty_defaults) void fn check(ref vector[Stmt] stmts): this.register_builtins() @@ -450,8 +451,8 @@ class Checker: void fn collect_decl(ref Stmt s): match s: - Function(name, params, return_type, body, is_inline, comptime_mode, is_static, visibility, type_params): - this.known_funcs[name.lexeme] = ExternFn(name.lexeme, name.lexeme, return_type, params) + Function(name, params, return_type, body, is_inline, comptime_mode, is_static, visibility, type_params, param_defaults): + this.known_funcs[name.lexeme] = ExternFn(name.lexeme, name.lexeme, return_type, params, param_defaults) Class(name, body, visibility): this.known_classes[name.lexeme] = body Enum(name, variants, methods, visibility, enum_tp): @@ -460,6 +461,14 @@ class Checker: this.declare(name.lexeme, const_type, "const", false, false, name) Struct(name, body, visibility, struct_tp): this.known_classes[name.lexeme] = body + // Register constructor params for arg count validation + for ref st in body: + match st: + Function(fname, fparams, fret, fbody, fi, fc, fs, fv, ftp, f_defaults): + if fname.lexeme == "constructor": + this.known_funcs[name.lexeme] = ExternFn(name.lexeme, name.lexeme, TypeNode::Custom(name.lexeme, []), fparams, f_defaults) + _: + pass Namespace(name, body, visibility): for ref ns_stmt in body: this.collect_decl(ns_stmt) @@ -532,7 +541,7 @@ class Checker: for ref st in statements: this.check_stmt(st) this.pop_scope() - Function(name, params, return_type, body, is_inline, comptime_mode, is_static, visibility, type_params): + Function(name, params, return_type, body, is_inline, comptime_mode, is_static, visibility, type_params, param_defaults): this.check_function(name, params, return_type, body) Class(name, body, visibility): this.check_class(name, body) @@ -595,7 +604,7 @@ class Checker: this.declare("this", TypeNode::Custom(name.lexeme, []), "var", false, false, name) for ref s in body: match s: - Function(fname, params, return_type, fbody, is_inline, comptime_mode, is_static, visibility, fn_tp): + Function(fname, params, return_type, fbody, is_inline, comptime_mode, is_static, visibility, fn_tp, fn_defaults): this.check_function(fname, params, return_type, fbody) Let(lname, var_type, initializer, visibility, is_ref, is_mut): this.check_expr(initializer) @@ -624,11 +633,11 @@ class Checker: this.check_expr(right) Grouping(inner): this.check_expr(inner) - Call(callee, paren, args): + Call(callee, paren, args, arg_names): this.check_expr(callee) for ref a in args: this.check_expr(a) - this.check_call_args(callee, args, paren) + this.check_call_args(callee, args, arg_names, paren) Assign(name, value): this.check_expr(value) if not this.resolve(name.lexeme): @@ -689,14 +698,32 @@ class Checker: _: pass - void fn check_call_args(ref Expr callee, ref vector[Expr] args, ref Token paren): + void fn check_call_args(ref Expr callee, ref vector[Expr] args, ref vector[string] arg_names, ref Token paren): match callee: Variable(name): if this.known_funcs.has(name.lexeme): ExternFn fi = this.known_funcs[name.lexeme] // Skip arg count check for builtins (registered with 0 params) - if fi.params.len() > 0 and args.len() != fi.params.len(): - this.error("Function '${name.lexeme}' expects ${fi.params.len()} args, got ${args.len()}", paren) + if fi.params.len() == 0: + return + // Count required params (those without defaults) + int required = 0 + int di = 0 + while di < fi.param_defaults.len(): + match fi.param_defaults[di]: + None(): + required = required + 1 + _: + pass + di = di + 1 + // If no defaults info, all params are required + if fi.param_defaults.len() == 0: + required = fi.params.len() + if args.len() < required or args.len() > fi.params.len(): + if required == fi.params.len(): + this.error("Function '${name.lexeme}' expects ${fi.params.len()} args, got ${args.len()}", paren) + else: + this.error("Function '${name.lexeme}' expects ${required}-${fi.params.len()} args, got ${args.len()}", paren) return _: pass diff --git a/src/codegen.lv b/src/codegen.lv index f28ce8f..94336e1 100644 --- a/src/codegen.lv +++ b/src/codegen.lv @@ -27,6 +27,8 @@ class CppCodegen: vector[vector[Stmt]] lambda_blocks hashmap[string, vector[Stmt]] extend_methods bool in_extend + hashmap[string, vector[Param]] fn_params + hashmap[string, vector[Expr]] fn_defaults constructor(): this.output = "" @@ -50,6 +52,8 @@ class CppCodegen: this.lambda_blocks = [] this.extend_methods = {} this.in_extend = false + this.fn_params = {} + this.fn_defaults = {} void fn set_modules(ref vector[string] short_names, ref vector[string] full_names, ref vector[string] aliases, ref vector[vector[Stmt]] stmts): this.module_short_names = short_names @@ -360,8 +364,8 @@ class CppCodegen: if op.token_type == TK_OR: op_str = "||" return "${l} ${op_str} ${r}" - Call(callee, paren, args): - return this.emit_call_expr(callee, args, m) + Call(callee, paren, args, arg_names): + return this.emit_call_expr(callee, args, arg_names, m) Index(object, bracket, index): return "${this.emit_expr(object, m)}[${this.emit_expr(index, m)}]" IndexSet(object, bracket, index, value): @@ -492,7 +496,61 @@ class CppCodegen: _: return false - string fn emit_call_expr(ref Expr callee, ref vector[Expr] args, bool in_method): + // Resolve named arguments: reorder args to match param positions, fill defaults + vector[string] fn resolve_named_args(ref vector[Expr] args, ref vector[string] arg_names, ref vector[Param] params, ref vector[Expr] defaults, bool in_method): + // Check if any named args are present + bool has_named = false + for ref n in arg_names: + if n != "": + has_named = true + // Fast path: all positional, fill missing defaults + if not has_named: + vector[string] result = [] + for ref a in args: + result.push(this.emit_expr(a, in_method)) + // Append defaults for missing trailing args + int i = args.len() + while i < params.len(): + if i < defaults.len(): + result.push(this.emit_expr(defaults[i], in_method)) + i = i + 1 + return result + // Mixed positional + named args + // Step 1: count positional args (those before the first named arg) + int positional_count = 0 + int ai = 0 + while ai < arg_names.len(): + if arg_names[ai] != "": + ai = arg_names.len() + else: + positional_count = positional_count + 1 + ai = ai + 1 + // Step 2: build result for each param position + vector[string] result = [] + int pi = 0 + while pi < params.len(): + if pi < positional_count: + // Covered by positional arg + result.push(this.emit_expr(args[pi], in_method)) + else: + // Look for named arg matching this param + bool found = false + int ni = positional_count + while ni < arg_names.len(): + if arg_names[ni] == params[pi].name.lexeme: + result.push(this.emit_expr(args[ni], in_method)) + found = true + ni = arg_names.len() + else: + ni = ni + 1 + if not found: + // Use default value + if pi < defaults.len(): + result.push(this.emit_expr(defaults[pi], in_method)) + pi = pi + 1 + return result + + string fn emit_call_expr(ref Expr callee, ref vector[Expr] args, ref vector[string] arg_names, bool in_method): match callee: Get(object, name): string obj = this.emit_expr(object, in_method) @@ -519,6 +577,22 @@ class CppCodegen: pass return "${obj}::${name.lexeme}(${arg_strs.join(", ")})" Variable(tok): + // Check if we have param info for named arg / default resolution + if this.fn_params.has(tok.lexeme): + vector[Param] fparams = this.fn_params[tok.lexeme] + vector[Expr] fdefaults = [] + if this.fn_defaults.has(tok.lexeme): + fdefaults = this.fn_defaults[tok.lexeme] + vector[string] arg_strs = this.resolve_named_args(args, arg_names, fparams, fdefaults, in_method) + if this.extern_fn_params.has(tok.lexeme): + vector[Param] eparams = this.extern_fn_params[tok.lexeme] + for i in 0..arg_strs.len(): + if i < eparams.len(): + arg_strs[i] = this.wrap_extern_arg(arg_strs[i], args[i], eparams[i].param_type) + string fn_name = tok.lexeme + if this.extern_fn_names.has(tok.lexeme): + fn_name = this.extern_fn_names[tok.lexeme] + return "${fn_name}(${arg_strs.join(", ")})" vector[string] arg_strs = [] for ref a in args: arg_strs.push(this.emit_expr(a, in_method)) @@ -620,25 +694,81 @@ class CppCodegen: Variable(tok): if this.var_types.has(tok.lexeme): TypeNode t = this.var_types[tok.lexeme] - match t: - Array(inner): - return "vector" - HashMap(k, v): - return "hashmap" - HashSet(inner): - return "hashset" - Str(): - return "string" - Bytes(): - return "bytes" - Custom(name, type_args): - return name - _: - return "" + return this.type_node_to_category(t) + Call(callee, paren, args, arg_names): + // Infer type from method call for chaining: obj.method(...).method2(...) + match callee: + Get(inner_obj, method_name): + string obj_cat = this.get_type_category(inner_obj) + if obj_cat != "": + return this.infer_method_return_category(obj_cat, method_name.lexeme) + _: + pass _: pass return "" + string fn type_node_to_category(ref TypeNode t): + match t: + Array(inner): + return "vector" + HashMap(k, v): + return "hashmap" + HashSet(inner): + return "hashset" + Str(): + return "string" + Bytes(): + return "bytes" + Custom(name, type_args): + return name + _: + return "" + + string fn infer_method_return_category(ref string obj_cat, ref string method): + // Extend methods that preserve type (collection -> same collection) + if obj_cat == "vector": + if method == "map" or method == "filter" or method == "take" or method == "drop": + return "vector" + if method == "zip" or method == "enumerate": + return "vector" + if obj_cat == "hashset": + if method == "union_with" or method == "intersect" or method == "difference": + return "hashset" + // bytes methods that return bytes + if obj_cat == "bytes": + if method == "slice" or method == "concat": + return "bytes" + // string methods that return string + if obj_cat == "string": + if method == "substring" or method == "trim" or method == "to_lower" or method == "to_upper": + return "string" + // Cross-type methods + if obj_cat == "bytes": + if method == "to_string" or method == "to_hex": + return "string" + if obj_cat == "string": + if method == "split": + return "vector" + if obj_cat == "vector": + if method == "join": + return "string" + // Extend method lookup: check the extend block for this type + if this.extend_methods.has(obj_cat): + vector[Stmt] methods = this.extend_methods[obj_cat] + for ref ext_m in methods: + match ext_m: + Function(mname, mparams, mret, mbody, mi, mc, ms, mv, mtp, m_defs): + if mname.lexeme == method: + string ret_cat = this.type_node_to_category(mret) + if ret_cat != "": + return ret_cat + // Auto return type: assume same type as input for collection methods + return obj_cat + _: + pass + return "" + TypeNode fn infer_source_type(ref Expr e): match e: Literal(kind, value): @@ -708,7 +838,7 @@ class CppCodegen: vector[Stmt] methods = this.extend_methods[type_cat] for ref ext_m in methods: match ext_m: - Function(mname, mparams, mret, mbody, is_inline, comptime_mode, is_static, vis, tp): + Function(mname, mparams, mret, mbody, is_inline, comptime_mode, is_static, vis, tp, m_defaults): if mname.lexeme == method: if args.len() > 0: return "__ext_${type_cat}_${method}(${obj}, ${args.join(", ")})" @@ -719,13 +849,13 @@ class CppCodegen: void fn emit_extend_method(ref string type_key, ref Stmt method): match method: - Function(mname, mparams, mret, mbody, is_inline, comptime_mode, is_static, vis, tp): + Function(mname, mparams, mret, mbody, is_inline, comptime_mode, is_static, vis, tp, m_defaults): vector[string] saved_dyn = this.dynamic_vars string ret_type = this.emit_type(mret) string param_str = this.emit_params(mparams, true) - string all_params = "auto& self" + string all_params = "auto&& self" if param_str != "": - all_params = "auto& self, ${param_str}" + all_params = "auto&& self, ${param_str}" this.output +="${this.indent()}${ret_type} __ext_${type_key}_${mname.lexeme}(${all_params}) {\n" this.indent_level += 1 bool saved_extend = this.in_extend @@ -844,8 +974,8 @@ class CppCodegen: exc_name = exception_name this.output +="${this.indent()} catch (const std::exception& ${exc_name}) " this.emit_block_or_stmt(catch_body, m) - Function(name, params, return_type, body, is_inline, comptime_mode, is_static, visibility, type_params): - this.emit_function(name, params, return_type, body, type_params, comptime_mode) + Function(name, params, return_type, body, is_inline, comptime_mode, is_static, visibility, type_params, param_defaults): + this.emit_function(name, params, return_type, body, type_params, comptime_mode, param_defaults) Class(name, body, visibility): vector[string] empty_tp = [] this.emit_class(name, body, empty_tp) @@ -944,7 +1074,9 @@ class CppCodegen: this.indent_level -= 1 this.output +="${this.indent()}}\n" - void fn emit_function(ref Token name, ref vector[Param] params, ref TypeNode return_type, ref vector[Stmt] body, ref vector[string] type_params, int comptime_mode): + void fn emit_function(ref Token name, ref vector[Param] params, ref TypeNode return_type, ref vector[Stmt] body, ref vector[string] type_params, int comptime_mode, ref vector[Expr] defaults): + this.fn_params[name.lexeme] = params + this.fn_defaults[name.lexeme] = defaults vector[string] saved_dynamic = this.dynamic_vars this.output +=this.template_prefix(type_params) string ret_type = this.emit_type(return_type) @@ -1018,7 +1150,7 @@ class CppCodegen: void fn emit_class_method(ref Token class_name, ref Stmt method): match method: - Function(mname, mparams, mret, mbody, is_inline, comptime_mode, is_static, visibility, m_tp): + Function(mname, mparams, mret, mbody, is_inline, comptime_mode, is_static, visibility, m_tp, m_defs2): vector[string] saved_dyn = this.dynamic_vars string ret_type = this.emit_type(mret) string mparam_str = this.emit_params(mparams, true) @@ -1045,6 +1177,7 @@ class CppCodegen: void fn emit_class(ref Token name, ref vector[Stmt] body, ref vector[string] type_params): vector[Stmt] init_body = [] vector[Param] init_params = [] + vector[Expr] init_defaults = [] bool has_init = false vector[Stmt] methods = [] vector[string] let_field_names = [] @@ -1052,11 +1185,12 @@ class CppCodegen: for ref st in body: match st: - Function(fname, params, return_type, fbody, is_inline, comptime_mode, is_static, visibility, fn_tp): + Function(fname, params, return_type, fbody, is_inline, comptime_mode, is_static, visibility, fn_tp, fn_defaults): if fname.lexeme == "constructor": has_init = true init_body = fbody init_params = params + init_defaults = fn_defaults else: methods.push(st) Let(fname, var_type, init, visibility, is_ref, is_mut): @@ -1101,6 +1235,8 @@ class CppCodegen: this.indent_level += 1 this.emit_class_fields(init_field_names, init_field_types, let_field_names, let_field_types) if has_init: + this.fn_params[name.lexeme] = init_params + this.fn_defaults[name.lexeme] = init_defaults this.emit_constructor(name, init_params, init_body) for ref m in methods: this.emit_class_method(name, m) @@ -1111,7 +1247,7 @@ class CppCodegen: bool has_to_string = false for ref m in methods: match m: - Function(mname, mparams, mret, mbody, mi, mc, ms, mv, mtp): + Function(mname, mparams, mret, mbody, mi, mc, ms, mv, mtp, m_defs): if mname.lexeme == "to_string": has_to_string = true _: @@ -1159,7 +1295,7 @@ class CppCodegen: return this.infer_expr_type(right, param_names, param_types) Grouping(inner): return this.infer_expr_type(inner, param_names, param_types) - Call(callee, paren, args): + Call(callee, paren, args, arg_names): match callee: Variable(tok): if this.is_known_enum(tok.lexeme): @@ -1299,7 +1435,7 @@ class CppCodegen: bool has_to_string = false for ref m in methods: match m: - Function(mname, mparams, mret, mbody, mi, mc, ms, mv, mtp): + Function(mname, mparams, mret, mbody, mi, mc, ms, mv, mtp, m_defs): if mname.lexeme == "to_string": has_to_string = true _: @@ -1381,7 +1517,7 @@ class CppCodegen: void fn emit_using_if_public(ref string ns, ref Stmt stmt): match stmt: - Function(name, params, return_type, body, is_inline, comptime_mode, is_static, visibility, type_params): + Function(name, params, return_type, body, is_inline, comptime_mode, is_static, visibility, type_params, param_defaults): if visibility != "private": this.output +="using ${ns}::${name.lexeme};\n" Class(name, body, visibility): @@ -1412,7 +1548,7 @@ class CppCodegen: for ref stmt in this.module_stmts[index]: match stmt: - Function(name, params, return_type, body, is_inline, comptime_mode, is_static, visibility, type_params): + Function(name, params, return_type, body, is_inline, comptime_mode, is_static, visibility, type_params, param_defaults): if name.lexeme == "main": pass else: @@ -1506,7 +1642,7 @@ class CppCodegen: for ref stmt in stmts: match stmt: - Function(name, params, return_type, body, is_inline, comptime_mode, is_static, visibility, type_params): + Function(name, params, return_type, body, is_inline, comptime_mode, is_static, visibility, type_params, param_defaults): if name.lexeme == "main": this.has_main = true this.output +="int main(int argc, char* argv[]) {\n" diff --git a/src/parser.lv b/src/parser.lv index 67a0c02..bf725e0 100644 --- a/src/parser.lv +++ b/src/parser.lv @@ -12,12 +12,14 @@ class Parser: int current bool in_class_body vector[vector[Stmt]] lambda_blocks + vector[Expr] last_param_defaults constructor(vector[Token] tokens): this.tokens = tokens this.current = 0 this.in_class_body = false this.lambda_blocks = [] + this.last_param_defaults = [] // ── Helpers ───────────────────────────────────────────── @@ -444,21 +446,37 @@ class Parser: pass vector[Expr] args = [] + vector[string] arg_names = [] this.skip_formatting() if not this.check(TK_RIGHT_PAREN): this.match_any([TK_REF, TK_REF_MUT]) - args.push(this.expression()) + Expr arg_expr = this.expression() + // Detect named argument: name = value parsed as Assign(name, value) + match arg_expr: + Assign(aname, avalue): + arg_names.push(aname.lexeme) + args.push(avalue) + _: + arg_names.push("") + args.push(arg_expr) this.skip_formatting() while this.match_any([TK_COMMA]): this.skip_formatting() if this.check(TK_RIGHT_PAREN): break this.match_any([TK_REF, TK_REF_MUT]) - args.push(this.expression()) + arg_expr = this.expression() + match arg_expr: + Assign(aname, avalue): + arg_names.push(aname.lexeme) + args.push(avalue) + _: + arg_names.push("") + args.push(arg_expr) this.skip_formatting() this.skip_formatting() auto paren = this.consume(TK_RIGHT_PAREN, "Expect ')' after arguments.") - return Expr::Call(callee, paren, args) + return Expr::Call(callee, paren, args, arg_names) Expr fn primary(): if this.match_any([TK_FALSE]): @@ -539,13 +557,18 @@ class Parser: vector[Param] fn parse_param_list(): vector[Param] params = [] + this.last_param_defaults = [] this.skip_formatting() if not this.check(TK_RIGHT_PAREN): bool p_mut = this.match_any([TK_REF_MUT]) bool p_ref = p_mut or this.match_any([TK_REF]) TypeNode param_type = this.parse_type() auto param_name = this.consume(TK_IDENTIFIER, "Expect parameter name.") + Expr default_val = Expr::None() + if this.match_any([TK_EQUAL]): + default_val = this.expression() params.push(Param(param_name, param_type, p_ref, p_mut)) + this.last_param_defaults.push(default_val) this.skip_formatting() while this.match_any([TK_COMMA]): this.skip_formatting() @@ -555,7 +578,11 @@ class Parser: p_ref = p_mut or this.match_any([TK_REF]) param_type = this.parse_type() param_name = this.consume(TK_IDENTIFIER, "Expect parameter name.") + default_val = Expr::None() + if this.match_any([TK_EQUAL]): + default_val = this.expression() params.push(Param(param_name, param_type, p_ref, p_mut)) + this.last_param_defaults.push(default_val) this.skip_formatting() this.skip_formatting() return params @@ -796,11 +823,12 @@ class Parser: this.consume(TK_LEFT_PAREN, "Expect '(' after function name.") vector[Param] params = this.parse_param_list() + vector[Expr] defaults = this.last_param_defaults this.consume(TK_RIGHT_PAREN, "Expect ')' after parameters.") this.consume(TK_COLON, "Expect ':' before function body.") vector[Stmt] body = this.block() - return Stmt::Function(name, params, return_type, body, is_inline, comptime_mode, is_static, visibility, type_params) + return Stmt::Function(name, params, return_type, body, is_inline, comptime_mode, is_static, visibility, type_params, defaults) Stmt fn class_declaration(string visibility): auto name = this.consume(TK_IDENTIFIER, "Expect class name.") @@ -1019,11 +1047,12 @@ class Parser: auto name_tok = this.advance() this.consume(TK_LEFT_PAREN, "Expect '(' after ${ctor_name}.") vector[Param] params = this.parse_param_list() + vector[Expr] ctor_defaults = this.last_param_defaults this.consume(TK_RIGHT_PAREN, "Expect ')' after parameters.") this.consume(TK_COLON, "Expect ':' before body.") vector[Stmt] body = this.block() vector[string] empty_tp = [] - return Stmt::Function(name_tok, params, TypeNode::Void(), body, false, 0, is_static, visibility, empty_tp) + return Stmt::Function(name_tok, params, TypeNode::Void(), body, false, 0, is_static, visibility, empty_tp, ctor_defaults) if this.match_any([TK_REF_MUT]): return this.var_declaration_with_ref(visibility, true, true) @@ -1089,7 +1118,8 @@ class Parser: string fn_cpp_name = fn_name.lexeme if this.match_any([TK_EQUAL]): fn_cpp_name = this.consume(TK_STRING, "Expect C++ name string.").lexeme - functions.push(ExternFn(fn_name.lexeme, fn_cpp_name, ret_type, params)) + vector[Expr] ext_defaults = this.last_param_defaults + functions.push(ExternFn(fn_name.lexeme, fn_cpp_name, ret_type, params, ext_defaults)) this.match_any([TK_NEWLINE]) this.consume(TK_DEDENT, "Expect dedent to end extern body.") diff --git a/stages/stage-latest.cpp b/stages/stage-latest.cpp index fbe2c56..33556d4 100644 --- a/stages/stage-latest.cpp +++ b/stages/stage-latest.cpp @@ -1109,7 +1109,7 @@ struct Expr { struct Variable { Token name; }; struct Assign { Token name; std::shared_ptr value; }; struct Logical { std::shared_ptr left; Token op; std::shared_ptr right; }; - struct Call { std::shared_ptr callee; Token paren; std::vector args; }; + struct Call { std::shared_ptr callee; Token paren; std::vector args; std::vector arg_names; }; struct Index { std::shared_ptr object; Token bracket; std::shared_ptr index; }; struct IndexSet { std::shared_ptr object; Token bracket; std::shared_ptr index; std::shared_ptr value; }; struct Vector { std::vector elements; }; @@ -1137,7 +1137,7 @@ struct Expr { static Expr make_Variable(Token name) { return {"Variable", Variable{name}}; } static Expr make_Assign(Token name, Expr value) { return {"Assign", Assign{name, std::make_shared(std::move(value))}}; } static Expr make_Logical(Expr left, Token op, Expr right) { return {"Logical", Logical{std::make_shared(std::move(left)), op, std::make_shared(std::move(right))}}; } - static Expr make_Call(Expr callee, Token paren, std::vector args) { return {"Call", Call{std::make_shared(std::move(callee)), paren, args}}; } + static Expr make_Call(Expr callee, Token paren, std::vector args, std::vector arg_names) { return {"Call", Call{std::make_shared(std::move(callee)), paren, args, arg_names}}; } static Expr make_Index(Expr object, Token bracket, Expr index) { return {"Index", Index{std::make_shared(std::move(object)), bracket, std::make_shared(std::move(index))}}; } static Expr make_IndexSet(Expr object, Token bracket, Expr index, Expr value) { return {"IndexSet", IndexSet{std::make_shared(std::move(object)), bracket, std::make_shared(std::move(index)), std::make_shared(std::move(value))}}; } static Expr make_Vector(std::vector elements) { return {"Vector", Vector{elements}}; } @@ -1175,6 +1175,7 @@ struct ExternFn { std::string cpp_name; TypeNode return_type; std::vector params; + std::vector param_defaults; }; @@ -1196,7 +1197,7 @@ struct Stmt { struct For { Token item_name; Expr collection; std::shared_ptr body; bool is_ref; bool is_mut; }; struct Block { std::vector statements; }; struct Try { std::shared_ptr try_body; std::shared_ptr catch_body; std::string exception_name; }; - struct Function { Token name; std::vector params; TypeNode return_type; std::vector body; bool is_inline; int64_t comptime_mode; bool is_static; std::string visibility; std::vector type_params; }; + struct Function { Token name; std::vector params; TypeNode return_type; std::vector body; bool is_inline; int64_t comptime_mode; bool is_static; std::string visibility; std::vector type_params; std::vector param_defaults; }; struct Class { Token name; std::vector body; std::string visibility; }; struct Struct { Token name; std::vector body; std::string visibility; std::vector type_params; }; struct Enum { Token name; std::vector variants; std::vector methods; std::string visibility; std::vector type_params; }; @@ -1223,7 +1224,7 @@ struct Stmt { static Stmt make_For(Token item_name, Expr collection, Stmt body, bool is_ref, bool is_mut) { return {"For", For{item_name, collection, std::make_shared(std::move(body)), is_ref, is_mut}}; } static Stmt make_Block(std::vector statements) { return {"Block", Block{statements}}; } static Stmt make_Try(Stmt try_body, Stmt catch_body, std::string exception_name) { return {"Try", Try{std::make_shared(std::move(try_body)), std::make_shared(std::move(catch_body)), exception_name}}; } - static Stmt make_Function(Token name, std::vector params, TypeNode return_type, std::vector body, bool is_inline, int64_t comptime_mode, bool is_static, std::string visibility, std::vector type_params) { return {"Function", Function{name, params, return_type, body, is_inline, comptime_mode, is_static, visibility, type_params}}; } + static Stmt make_Function(Token name, std::vector params, TypeNode return_type, std::vector body, bool is_inline, int64_t comptime_mode, bool is_static, std::string visibility, std::vector type_params, std::vector param_defaults) { return {"Function", Function{name, params, return_type, body, is_inline, comptime_mode, is_static, visibility, type_params, param_defaults}}; } static Stmt make_Class(Token name, std::vector body, std::string visibility) { return {"Class", Class{name, body, visibility}}; } static Stmt make_Struct(Token name, std::vector body, std::string visibility, std::vector type_params) { return {"Struct", Struct{name, body, visibility, type_params}}; } static Stmt make_Enum(Token name, std::vector variants, std::vector methods, std::string visibility, std::vector type_params) { return {"Enum", Enum{name, variants, methods, visibility, type_params}}; } @@ -1269,6 +1270,8 @@ struct CppCodegen { std::vector> lambda_blocks; std::unordered_map> extend_methods; bool in_extend; + std::unordered_map> fn_params; + std::unordered_map> fn_defaults; CppCodegen() { this->output = std::string(""); @@ -1292,6 +1295,8 @@ struct CppCodegen { this->lambda_blocks = {}; this->extend_methods = {{}}; this->in_extend = false; + this->fn_params = {{}}; + this->fn_defaults = {{}}; } void set_modules(const std::vector& short_names, const std::vector& full_names, const std::vector& aliases, const std::vector>& stmts) { @@ -1855,7 +1860,8 @@ struct CppCodegen { auto& callee = *_v.callee; auto& paren = _v.paren; auto& args = _v.args; - return (*this).emit_call_expr(callee, args, m); + auto& arg_names = _v.arg_names; + return (*this).emit_call_expr(callee, args, arg_names, m); } else if (std::holds_alternative::Index>(_match_15._data)) { auto& _v = std::get::Index>(_match_15._data); @@ -2126,7 +2132,69 @@ struct CppCodegen { } } - std::string emit_call_expr(const Expr& callee, const std::vector& args, bool in_method) { + std::vector resolve_named_args(const std::vector& args, const std::vector& arg_names, const std::vector& params, const std::vector& defaults, bool in_method) { + bool has_named = false; + for (const auto& n : arg_names) { + if ((n != std::string(""))) { + has_named = true; + } + } + if ((!has_named)) { + std::vector result = {}; + for (const auto& a : args) { + result.push_back((*this).emit_expr(a, in_method)); + } + int64_t i = static_cast(args.size()); + while ((i < static_cast(params.size()))) { + if ((i < static_cast(defaults.size()))) { + result.push_back((*this).emit_expr(defaults[i], in_method)); + } + i = (i + INT64_C(1)); + } + return result; + } + int64_t positional_count = INT64_C(0); + int64_t ai = INT64_C(0); + while ((ai < static_cast(arg_names.size()))) { + if ((arg_names[ai] != std::string(""))) { + ai = static_cast(arg_names.size()); + } + else { + positional_count = (positional_count + INT64_C(1)); + ai = (ai + INT64_C(1)); + } + } + std::vector result = {}; + int64_t pi = INT64_C(0); + while ((pi < static_cast(params.size()))) { + if ((pi < positional_count)) { + result.push_back((*this).emit_expr(args[pi], in_method)); + } + else { + bool found = false; + int64_t ni = positional_count; + while ((ni < static_cast(arg_names.size()))) { + if ((arg_names[ni] == params[pi].name.lexeme)) { + result.push_back((*this).emit_expr(args[ni], in_method)); + found = true; + ni = static_cast(arg_names.size()); + } + else { + ni = (ni + INT64_C(1)); + } + } + if ((!found)) { + if ((pi < static_cast(defaults.size()))) { + result.push_back((*this).emit_expr(defaults[pi], in_method)); + } + } + } + pi = (pi + INT64_C(1)); + } + return result; + } + + std::string emit_call_expr(const Expr& callee, const std::vector& args, const std::vector& arg_names, bool in_method) { { const auto& _match_25 = callee; if (std::holds_alternative::Get>(_match_25._data)) { @@ -2175,6 +2243,27 @@ struct CppCodegen { else if (std::holds_alternative::Variable>(_match_25._data)) { auto& _v = std::get::Variable>(_match_25._data); auto& tok = _v.name; + if ((this->fn_params.count(tok.lexeme) > 0)) { + std::vector fparams = this->fn_params[tok.lexeme]; + std::vector fdefaults = {}; + if ((this->fn_defaults.count(tok.lexeme) > 0)) { + fdefaults = this->fn_defaults[tok.lexeme]; + } + std::vector arg_strs = (*this).resolve_named_args(args, arg_names, fparams, fdefaults, in_method); + if ((this->extern_fn_params.count(tok.lexeme) > 0)) { + std::vector eparams = this->extern_fn_params[tok.lexeme]; + for (int64_t i = INT64_C(0); i < static_cast(arg_strs.size()); i++) { + if ((i < static_cast(eparams.size()))) { + arg_strs[i] = (*this).wrap_extern_arg(arg_strs[i], args[i], eparams[i].param_type); + } + } + } + std::string fn_name = tok.lexeme; + if ((this->extern_fn_names.count(tok.lexeme) > 0)) { + fn_name = this->extern_fn_names[tok.lexeme]; + } + return ((((std::string("") + (fn_name)) + std::string("(")) + (lv_join(arg_strs, std::string(", ")))) + std::string(")")); + } std::vector arg_strs = {}; for (const auto& a : args) { arg_strs.push_back((*this).emit_expr(a, in_method)); @@ -2384,40 +2473,29 @@ struct CppCodegen { auto& tok = _v.name; if ((this->var_types.count(tok.lexeme) > 0)) { TypeNode t = this->var_types[tok.lexeme]; - { - const auto& _match_28 = t; - if (std::holds_alternative::Array>(_match_28._data)) { - auto& _v = std::get::Array>(_match_28._data); - auto& inner = *_v.inner; - return std::string("vector"); - } - else if (std::holds_alternative::HashMap>(_match_28._data)) { - auto& _v = std::get::HashMap>(_match_28._data); - auto& k = *_v.key_type; - auto& v = *_v.value_type; - return std::string("hashmap"); - } - else if (std::holds_alternative::HashSet>(_match_28._data)) { - auto& _v = std::get::HashSet>(_match_28._data); - auto& inner = *_v.inner; - return std::string("hashset"); - } - else if (std::holds_alternative::Str>(_match_28._data)) { - return std::string("string"); - } - else if (std::holds_alternative::Bytes>(_match_28._data)) { - return std::string("bytes"); - } - else if (std::holds_alternative::Custom>(_match_28._data)) { - auto& _v = std::get::Custom>(_match_28._data); - auto& name = _v.name; - auto& type_args = _v.type_args; - return name; - } - else { - return std::string(""); + return (*this).type_node_to_category(t); + } + } + else if (std::holds_alternative::Call>(_match_27._data)) { + auto& _v = std::get::Call>(_match_27._data); + auto& callee = *_v.callee; + auto& paren = _v.paren; + auto& args = _v.args; + auto& arg_names = _v.arg_names; + { + const auto& _match_28 = callee; + if (std::holds_alternative::Get>(_match_28._data)) { + auto& _v = std::get::Get>(_match_28._data); + auto& inner_obj = *_v.object; + auto& method_name = _v.name; + std::string obj_cat = (*this).get_type_category(inner_obj); + if ((obj_cat != std::string(""))) { + return (*this).infer_method_return_category(obj_cat, method_name.lexeme); } } + else { + /* pass */ + } } } else { @@ -2427,11 +2505,121 @@ struct CppCodegen { return std::string(""); } + std::string type_node_to_category(const TypeNode& t) { + { + const auto& _match_29 = t; + if (std::holds_alternative::Array>(_match_29._data)) { + auto& _v = std::get::Array>(_match_29._data); + auto& inner = *_v.inner; + return std::string("vector"); + } + else if (std::holds_alternative::HashMap>(_match_29._data)) { + auto& _v = std::get::HashMap>(_match_29._data); + auto& k = *_v.key_type; + auto& v = *_v.value_type; + return std::string("hashmap"); + } + else if (std::holds_alternative::HashSet>(_match_29._data)) { + auto& _v = std::get::HashSet>(_match_29._data); + auto& inner = *_v.inner; + return std::string("hashset"); + } + else if (std::holds_alternative::Str>(_match_29._data)) { + return std::string("string"); + } + else if (std::holds_alternative::Bytes>(_match_29._data)) { + return std::string("bytes"); + } + else if (std::holds_alternative::Custom>(_match_29._data)) { + auto& _v = std::get::Custom>(_match_29._data); + auto& name = _v.name; + auto& type_args = _v.type_args; + return name; + } + else { + return std::string(""); + } + } + } + + std::string infer_method_return_category(const std::string& obj_cat, const std::string& method) { + if ((obj_cat == std::string("vector"))) { + if ((method == std::string("map")) || (method == std::string("filter")) || (method == std::string("take")) || (method == std::string("drop"))) { + return std::string("vector"); + } + if ((method == std::string("zip")) || (method == std::string("enumerate"))) { + return std::string("vector"); + } + } + if ((obj_cat == std::string("hashset"))) { + if ((method == std::string("union_with")) || (method == std::string("intersect")) || (method == std::string("difference"))) { + return std::string("hashset"); + } + } + if ((obj_cat == std::string("bytes"))) { + if ((method == std::string("slice")) || (method == std::string("concat"))) { + return std::string("bytes"); + } + } + if ((obj_cat == std::string("string"))) { + if ((method == std::string("substring")) || (method == std::string("trim")) || (method == std::string("to_lower")) || (method == std::string("to_upper"))) { + return std::string("string"); + } + } + if ((obj_cat == std::string("bytes"))) { + if ((method == std::string("to_string")) || (method == std::string("to_hex"))) { + return std::string("string"); + } + } + if ((obj_cat == std::string("string"))) { + if ((method == std::string("split"))) { + return std::string("vector"); + } + } + if ((obj_cat == std::string("vector"))) { + if ((method == std::string("join"))) { + return std::string("string"); + } + } + if ((this->extend_methods.count(obj_cat) > 0)) { + std::vector methods = this->extend_methods[obj_cat]; + for (const auto& ext_m : methods) { + { + const auto& _match_30 = ext_m; + if (std::holds_alternative::Function>(_match_30._data)) { + auto& _v = std::get::Function>(_match_30._data); + auto& mname = _v.name; + auto& mparams = _v.params; + auto& mret = _v.return_type; + auto& mbody = _v.body; + auto& mi = _v.is_inline; + auto& mc = _v.comptime_mode; + auto& ms = _v.is_static; + auto& mv = _v.visibility; + auto& mtp = _v.type_params; + auto& m_defs = _v.param_defaults; + if ((mname.lexeme == method)) { + std::string ret_cat = (*this).type_node_to_category(mret); + if ((ret_cat != std::string(""))) { + return ret_cat; + } + return obj_cat; + } + } + else { + /* pass */ + } + } + } + } + return std::string(""); + } + TypeNode infer_source_type(const Expr& e) { { - const auto& _match_29 = e; - if (std::holds_alternative::Literal>(_match_29._data)) { - auto& _v = std::get::Literal>(_match_29._data); + const auto& _match_31 = e; + if (std::holds_alternative::Literal>(_match_31._data)) { + auto& _v = std::get::Literal>(_match_31._data); auto& kind = _v.kind; auto& value = _v.value; if ((kind == std::string("int"))) { @@ -2454,27 +2642,27 @@ struct CppCodegen { } return TypeNode::make_Auto(); } - else if (std::holds_alternative::Variable>(_match_29._data)) { - auto& _v = std::get::Variable>(_match_29._data); + else if (std::holds_alternative::Variable>(_match_31._data)) { + auto& _v = std::get::Variable>(_match_31._data); auto& tok = _v.name; if ((this->var_types.count(tok.lexeme) > 0)) { return this->var_types[tok.lexeme]; } return TypeNode::make_Auto(); } - else if (std::holds_alternative::Grouping>(_match_29._data)) { - auto& _v = std::get::Grouping>(_match_29._data); + else if (std::holds_alternative::Grouping>(_match_31._data)) { + auto& _v = std::get::Grouping>(_match_31._data); auto& inner = *_v.inner; return (*this).infer_source_type(inner); } - else if (std::holds_alternative::Cast>(_match_29._data)) { - auto& _v = std::get::Cast>(_match_29._data); + else if (std::holds_alternative::Cast>(_match_31._data)) { + auto& _v = std::get::Cast>(_match_31._data); auto& expr = *_v.expr; auto& target_type = _v.target_type; return target_type; } - else if (std::holds_alternative::Unary>(_match_29._data)) { - auto& _v = std::get::Unary>(_match_29._data); + else if (std::holds_alternative::Unary>(_match_31._data)) { + auto& _v = std::get::Unary>(_match_31._data); auto& op = _v.op; auto& right = *_v.right; return (*this).infer_source_type(right); @@ -2487,20 +2675,20 @@ struct CppCodegen { bool is_integer_type(const TypeNode& t) { { - const auto& _match_30 = t; - if (std::holds_alternative::Int>(_match_30._data)) { + const auto& _match_32 = t; + if (std::holds_alternative::Int>(_match_32._data)) { return true; } - else if (std::holds_alternative::Int8>(_match_30._data)) { + else if (std::holds_alternative::Int8>(_match_32._data)) { return true; } - else if (std::holds_alternative::Int16>(_match_30._data)) { + else if (std::holds_alternative::Int16>(_match_32._data)) { return true; } - else if (std::holds_alternative::Int32>(_match_30._data)) { + else if (std::holds_alternative::Int32>(_match_32._data)) { return true; } - else if (std::holds_alternative::USize>(_match_30._data)) { + else if (std::holds_alternative::USize>(_match_32._data)) { return true; } else { @@ -2511,11 +2699,11 @@ struct CppCodegen { bool is_float_type(const TypeNode& t) { { - const auto& _match_31 = t; - if (std::holds_alternative::Float>(_match_31._data)) { + const auto& _match_33 = t; + if (std::holds_alternative::Float>(_match_33._data)) { return true; } - else if (std::holds_alternative::Float32>(_match_31._data)) { + else if (std::holds_alternative::Float32>(_match_33._data)) { return true; } else { @@ -2526,8 +2714,8 @@ struct CppCodegen { bool is_string_type(const TypeNode& t) { { - const auto& _match_32 = t; - if (std::holds_alternative::Str>(_match_32._data)) { + const auto& _match_34 = t; + if (std::holds_alternative::Str>(_match_34._data)) { return true; } else { @@ -2538,8 +2726,8 @@ struct CppCodegen { bool is_bytes_type(const TypeNode& t) { { - const auto& _match_33 = t; - if (std::holds_alternative::Bytes>(_match_33._data)) { + const auto& _match_35 = t; + if (std::holds_alternative::Bytes>(_match_35._data)) { return true; } else { @@ -2554,9 +2742,9 @@ struct CppCodegen { std::vector methods = this->extend_methods[type_cat]; for (const auto& ext_m : methods) { { - const auto& _match_34 = ext_m; - if (std::holds_alternative::Function>(_match_34._data)) { - auto& _v = std::get::Function>(_match_34._data); + const auto& _match_36 = ext_m; + if (std::holds_alternative::Function>(_match_36._data)) { + auto& _v = std::get::Function>(_match_36._data); auto& mname = _v.name; auto& mparams = _v.params; auto& mret = _v.return_type; @@ -2566,6 +2754,7 @@ struct CppCodegen { auto& is_static = _v.is_static; auto& vis = _v.visibility; auto& tp = _v.type_params; + auto& m_defaults = _v.param_defaults; if ((mname.lexeme == method)) { if ((static_cast(args.size()) > INT64_C(0))) { return ((((((((std::string("__ext_") + (type_cat)) + std::string("_")) + (method)) + std::string("(")) + (obj)) + std::string(", ")) + (lv_join(args, std::string(", ")))) + std::string(")")); @@ -2584,9 +2773,9 @@ struct CppCodegen { void emit_extend_method(const std::string& type_key, const Stmt& method) { { - const auto& _match_35 = method; - if (std::holds_alternative::Function>(_match_35._data)) { - auto& _v = std::get::Function>(_match_35._data); + const auto& _match_37 = method; + if (std::holds_alternative::Function>(_match_37._data)) { + auto& _v = std::get::Function>(_match_37._data); auto& mname = _v.name; auto& mparams = _v.params; auto& mret = _v.return_type; @@ -2596,12 +2785,13 @@ struct CppCodegen { auto& is_static = _v.is_static; auto& vis = _v.visibility; auto& tp = _v.type_params; + auto& m_defaults = _v.param_defaults; std::vector saved_dyn = this->dynamic_vars; std::string ret_type = (*this).emit_type(mret); std::string param_str = (*this).emit_params(mparams, true); - std::string all_params = std::string("auto& self"); + std::string all_params = std::string("auto&& self"); if ((param_str != std::string(""))) { - all_params = ((std::string("auto& self, ") + (param_str)) + std::string("")); + all_params = ((std::string("auto&& self, ") + (param_str)) + std::string("")); } this->output = (this->output + ((((((((((std::string("") + ((*this).indent())) + std::string("")) + (ret_type)) + std::string(" __ext_")) + (type_key)) + std::string("_")) + (mname.lexeme)) + std::string("(")) + (all_params)) + std::string(") {\n"))); this->indent_level = (this->indent_level + INT64_C(1)); @@ -2638,8 +2828,8 @@ struct CppCodegen { this->var_types[p.name.lexeme] = p.param_type; if (track_dynamic) { { - const auto& _match_36 = p.param_type; - if (std::holds_alternative::Dynamic>(_match_36._data)) { + const auto& _match_38 = p.param_type; + if (std::holds_alternative::Dynamic>(_match_38._data)) { (*this).add_dynamic_var(p.name.lexeme); } else { @@ -2674,17 +2864,17 @@ struct CppCodegen { void emit_stmt(const Stmt& s, bool m) { { - const auto& _match_37 = s; - if (_match_37._tag == "None") { + const auto& _match_39 = s; + if (_match_39._tag == "None") { /* pass */ } - else if (std::holds_alternative::ExprStmt>(_match_37._data)) { - auto& _v = std::get::ExprStmt>(_match_37._data); + else if (std::holds_alternative::ExprStmt>(_match_39._data)) { + auto& _v = std::get::ExprStmt>(_match_39._data); auto& expr = _v.expr; this->output = (this->output + ((((std::string("") + ((*this).indent())) + std::string("")) + ((*this).emit_expr(expr, m))) + std::string(";\n"))); } - else if (std::holds_alternative::Let>(_match_37._data)) { - auto& _v = std::get::Let>(_match_37._data); + else if (std::holds_alternative::Let>(_match_39._data)) { + auto& _v = std::get::Let>(_match_39._data); auto& name = _v.name; auto& var_type = _v.var_type; auto& initializer = _v.initializer; @@ -2693,8 +2883,8 @@ struct CppCodegen { auto& is_mut = _v.is_mut; (*this).emit_let(name, var_type, initializer, m, is_ref, is_mut); } - else if (std::holds_alternative::Const>(_match_37._data)) { - auto& _v = std::get::Const>(_match_37._data); + else if (std::holds_alternative::Const>(_match_39._data)) { + auto& _v = std::get::Const>(_match_39._data); auto& name = _v.name; auto& const_type = _v.const_type; auto& value = _v.value; @@ -2708,13 +2898,13 @@ struct CppCodegen { } this->output = (this->output + ((((((((((std::string("") + ((*this).indent())) + std::string("")) + (prefix)) + std::string("")) + (cpp_type)) + std::string(" ")) + (name.lexeme)) + std::string(" = ")) + (val)) + std::string(";\n"))); } - else if (std::holds_alternative::Return>(_match_37._data)) { - auto& _v = std::get::Return>(_match_37._data); + else if (std::holds_alternative::Return>(_match_39._data)) { + auto& _v = std::get::Return>(_match_39._data); auto& keyword = _v.keyword; auto& value = _v.value; { - const auto& _match_38 = value; - if (_match_38._tag == "None") { + const auto& _match_40 = value; + if (_match_40._tag == "None") { this->output = (this->output + ((std::string("") + ((*this).indent())) + std::string("return;\n"))); } else { @@ -2722,16 +2912,16 @@ struct CppCodegen { } } } - else if (std::holds_alternative::If>(_match_37._data)) { - auto& _v = std::get::If>(_match_37._data); + else if (std::holds_alternative::If>(_match_39._data)) { + auto& _v = std::get::If>(_match_39._data); auto& condition = _v.condition; auto& then_branch = *_v.then_branch; auto& else_branch = *_v.else_branch; this->output = (this->output + ((((std::string("") + ((*this).indent())) + std::string("if (")) + ((*this).emit_expr(condition, m))) + std::string(") "))); (*this).emit_block_or_stmt(then_branch, m); { - const auto& _match_39 = else_branch; - if (_match_39._tag == "None") { + const auto& _match_41 = else_branch; + if (_match_41._tag == "None") { /* pass */ } else { @@ -2740,24 +2930,24 @@ struct CppCodegen { } } } - else if (std::holds_alternative::While>(_match_37._data)) { - auto& _v = std::get::While>(_match_37._data); + else if (std::holds_alternative::While>(_match_39._data)) { + auto& _v = std::get::While>(_match_39._data); auto& condition = _v.condition; auto& body = *_v.body; this->output = (this->output + ((((std::string("") + ((*this).indent())) + std::string("while (")) + ((*this).emit_expr(condition, m))) + std::string(") "))); (*this).emit_block_or_stmt(body, m); } - else if (std::holds_alternative::For>(_match_37._data)) { - auto& _v = std::get::For>(_match_37._data); + else if (std::holds_alternative::For>(_match_39._data)) { + auto& _v = std::get::For>(_match_39._data); auto& item_name = _v.item_name; auto& collection = _v.collection; auto& body = *_v.body; auto& is_ref = _v.is_ref; auto& is_mut = _v.is_mut; { - const auto& _match_40 = collection; - if (std::holds_alternative::Range>(_match_40._data)) { - auto& _v = std::get::Range>(_match_40._data); + const auto& _match_42 = collection; + if (std::holds_alternative::Range>(_match_42._data)) { + auto& _v = std::get::Range>(_match_42._data); auto& start = *_v.start; auto& end = *_v.end; this->output = (this->output + ((((((((((((std::string("") + ((*this).indent())) + std::string("for (int64_t ")) + (item_name.lexeme)) + std::string(" = ")) + ((*this).emit_expr(start, m))) + std::string("; ")) + (item_name.lexeme)) + std::string(" < ")) + ((*this).emit_expr(end, m))) + std::string("; ")) + (item_name.lexeme)) + std::string("++) "))); @@ -2765,9 +2955,9 @@ struct CppCodegen { } else { { - const auto& _match_41 = collection; - if (std::holds_alternative::Variable>(_match_41._data)) { - auto& _v = std::get::Variable>(_match_41._data); + const auto& _match_43 = collection; + if (std::holds_alternative::Variable>(_match_43._data)) { + auto& _v = std::get::Variable>(_match_43._data); auto& tok = _v.name; if ((*this).is_dynamic_var(tok.lexeme)) { (*this).add_dynamic_var(item_name.lexeme); @@ -2791,8 +2981,8 @@ struct CppCodegen { } } } - else if (std::holds_alternative::Block>(_match_37._data)) { - auto& _v = std::get::Block>(_match_37._data); + else if (std::holds_alternative::Block>(_match_39._data)) { + auto& _v = std::get::Block>(_match_39._data); auto& statements = _v.statements; this->output = (this->output + ((std::string("") + ((*this).indent())) + std::string("{\n"))); this->indent_level = (this->indent_level + INT64_C(1)); @@ -2802,8 +2992,8 @@ struct CppCodegen { this->indent_level = (this->indent_level - INT64_C(1)); this->output = (this->output + ((std::string("") + ((*this).indent())) + std::string("}\n"))); } - else if (std::holds_alternative::Try>(_match_37._data)) { - auto& _v = std::get::Try>(_match_37._data); + else if (std::holds_alternative::Try>(_match_39._data)) { + auto& _v = std::get::Try>(_match_39._data); auto& try_body = *_v.try_body; auto& catch_body = *_v.catch_body; auto& exception_name = _v.exception_name; @@ -2816,8 +3006,8 @@ struct CppCodegen { this->output = (this->output + ((((std::string("") + ((*this).indent())) + std::string(" catch (const std::exception& ")) + (exc_name)) + std::string(") "))); (*this).emit_block_or_stmt(catch_body, m); } - else if (std::holds_alternative::Function>(_match_37._data)) { - auto& _v = std::get::Function>(_match_37._data); + else if (std::holds_alternative::Function>(_match_39._data)) { + auto& _v = std::get::Function>(_match_39._data); auto& name = _v.name; auto& params = _v.params; auto& return_type = _v.return_type; @@ -2827,26 +3017,27 @@ struct CppCodegen { auto& is_static = _v.is_static; auto& visibility = _v.visibility; auto& type_params = _v.type_params; - (*this).emit_function(name, params, return_type, body, type_params, comptime_mode); + auto& param_defaults = _v.param_defaults; + (*this).emit_function(name, params, return_type, body, type_params, comptime_mode, param_defaults); } - else if (std::holds_alternative::Class>(_match_37._data)) { - auto& _v = std::get::Class>(_match_37._data); + else if (std::holds_alternative::Class>(_match_39._data)) { + auto& _v = std::get::Class>(_match_39._data); auto& name = _v.name; auto& body = _v.body; auto& visibility = _v.visibility; std::vector empty_tp = {}; (*this).emit_class(name, body, empty_tp); } - else if (std::holds_alternative::Struct>(_match_37._data)) { - auto& _v = std::get::Struct>(_match_37._data); + else if (std::holds_alternative::Struct>(_match_39._data)) { + auto& _v = std::get::Struct>(_match_39._data); auto& name = _v.name; auto& body = _v.body; auto& visibility = _v.visibility; auto& type_params = _v.type_params; (*this).emit_class(name, body, type_params); } - else if (std::holds_alternative::Enum>(_match_37._data)) { - auto& _v = std::get::Enum>(_match_37._data); + else if (std::holds_alternative::Enum>(_match_39._data)) { + auto& _v = std::get::Enum>(_match_39._data); auto& name = _v.name; auto& variants = _v.variants; auto& methods = _v.methods; @@ -2854,43 +3045,43 @@ struct CppCodegen { auto& type_params = _v.type_params; (*this).emit_enum(name, variants, methods, type_params); } - else if (std::holds_alternative::Match>(_match_37._data)) { - auto& _v = std::get::Match>(_match_37._data); + else if (std::holds_alternative::Match>(_match_39._data)) { + auto& _v = std::get::Match>(_match_39._data); auto& expr = _v.expr; auto& arm_patterns = _v.arm_patterns; auto& arm_bodies = _v.arm_bodies; (*this).emit_match_impl(expr, arm_patterns, arm_bodies, m); } - else if (std::holds_alternative::Namespace>(_match_37._data)) { - auto& _v = std::get::Namespace>(_match_37._data); + else if (std::holds_alternative::Namespace>(_match_39._data)) { + auto& _v = std::get::Namespace>(_match_39._data); auto& name = _v.name; auto& body = _v.body; auto& visibility = _v.visibility; /* pass */ } - else if (std::holds_alternative::Import>(_match_37._data)) { - auto& _v = std::get::Import>(_match_37._data); + else if (std::holds_alternative::Import>(_match_39._data)) { + auto& _v = std::get::Import>(_match_39._data); auto& path = _v.path; auto& alias = _v.alias; /* pass */ } - else if (std::holds_alternative::Break>(_match_37._data)) { - auto& _v = std::get::Break>(_match_37._data); + else if (std::holds_alternative::Break>(_match_39._data)) { + auto& _v = std::get::Break>(_match_39._data); auto& keyword = _v.keyword; this->output = (this->output + ((std::string("") + ((*this).indent())) + std::string("break;\n"))); } - else if (std::holds_alternative::Continue>(_match_37._data)) { - auto& _v = std::get::Continue>(_match_37._data); + else if (std::holds_alternative::Continue>(_match_39._data)) { + auto& _v = std::get::Continue>(_match_39._data); auto& keyword = _v.keyword; this->output = (this->output + ((std::string("") + ((*this).indent())) + std::string("continue;\n"))); } - else if (std::holds_alternative::Pass>(_match_37._data)) { - auto& _v = std::get::Pass>(_match_37._data); + else if (std::holds_alternative::Pass>(_match_39._data)) { + auto& _v = std::get::Pass>(_match_39._data); auto& keyword = _v.keyword; this->output = (this->output + ((std::string("") + ((*this).indent())) + std::string("/* pass */\n"))); } - else if (std::holds_alternative::CppBlock>(_match_37._data)) { - auto& _v = std::get::CppBlock>(_match_37._data); + else if (std::holds_alternative::CppBlock>(_match_39._data)) { + auto& _v = std::get::CppBlock>(_match_39._data); auto& code = _v.code; auto lines = lv_split(code, std::string("\n")); for (const auto& line : lines) { @@ -2900,8 +3091,8 @@ struct CppCodegen { } } } - else if (std::holds_alternative::Extern>(_match_37._data)) { - auto& _v = std::get::Extern>(_match_37._data); + else if (std::holds_alternative::Extern>(_match_39._data)) { + auto& _v = std::get::Extern>(_match_39._data); auto& header = _v.header; auto& import_path = _v.import_path; auto& link_lib = _v.link_lib; @@ -2934,8 +3125,8 @@ struct CppCodegen { this->extern_fn_params[ef.name] = ef.params; } } - else if (std::holds_alternative::Extend>(_match_37._data)) { - auto& _v = std::get::Extend>(_match_37._data); + else if (std::holds_alternative::Extend>(_match_39._data)) { + auto& _v = std::get::Extend>(_match_39._data); auto& target = _v.target_type; auto& methods = _v.methods; auto& visibility = _v.visibility; @@ -2963,17 +3154,17 @@ struct CppCodegen { } std::string init_str = std::string(""); { - const auto& _match_42 = initializer; - if (_match_42._tag == "None") { + const auto& _match_44 = initializer; + if (_match_44._tag == "None") { init_str = (*this).default_init(cpp_type); } else { std::string val = (*this).emit_expr(initializer, in_method); bool is_ptr = false; { - const auto& _match_43 = var_type; - if (std::holds_alternative::Ptr>(_match_43._data)) { - auto& _v = std::get::Ptr>(_match_43._data); + const auto& _match_45 = var_type; + if (std::holds_alternative::Ptr>(_match_45._data)) { + auto& _v = std::get::Ptr>(_match_45._data); auto& inner = *_v.inner; is_ptr = true; } @@ -2992,9 +3183,9 @@ struct CppCodegen { void emit_block_or_stmt(const Stmt& s, bool m) { { - const auto& _match_44 = s; - if (std::holds_alternative::Block>(_match_44._data)) { - auto& _v = std::get::Block>(_match_44._data); + const auto& _match_46 = s; + if (std::holds_alternative::Block>(_match_46._data)) { + auto& _v = std::get::Block>(_match_46._data); auto& stmts = _v.statements; this->output = (this->output + std::string("{\n")); this->indent_level = (this->indent_level + INT64_C(1)); @@ -3014,7 +3205,9 @@ struct CppCodegen { } } - void emit_function(const Token& name, const std::vector& params, const TypeNode& return_type, const std::vector& body, const std::vector& type_params, int64_t comptime_mode) { + void emit_function(const Token& name, const std::vector& params, const TypeNode& return_type, const std::vector& body, const std::vector& type_params, int64_t comptime_mode, const std::vector& defaults) { + this->fn_params[name.lexeme] = params; + this->fn_defaults[name.lexeme] = defaults; std::vector saved_dynamic = this->dynamic_vars; this->output = (this->output + (*this).template_prefix(type_params)); std::string ret_type = (*this).emit_type(return_type); @@ -3064,27 +3257,27 @@ struct CppCodegen { std::vector remaining_body = {}; for (const auto& st : init_body) { { - const auto& _match_45 = st; - if (std::holds_alternative::ExprStmt>(_match_45._data)) { - auto& _v = std::get::ExprStmt>(_match_45._data); + const auto& _match_47 = st; + if (std::holds_alternative::ExprStmt>(_match_47._data)) { + auto& _v = std::get::ExprStmt>(_match_47._data); auto& expr = _v.expr; bool handled = false; { - const auto& _match_46 = expr; - if (std::holds_alternative::Set>(_match_46._data)) { - auto& _v = std::get::Set>(_match_46._data); + const auto& _match_48 = expr; + if (std::holds_alternative::Set>(_match_48._data)) { + auto& _v = std::get::Set>(_match_48._data); auto& object = *_v.object; auto& prop = _v.name; auto& value = *_v.value; { - const auto& _match_47 = object; - if (std::holds_alternative::This>(_match_47._data)) { - auto& _v = std::get::This>(_match_47._data); + const auto& _match_49 = object; + if (std::holds_alternative::This>(_match_49._data)) { + auto& _v = std::get::This>(_match_49._data); auto& kw = _v.keyword; { - const auto& _match_48 = value; - if (std::holds_alternative::Variable>(_match_48._data)) { - auto& _v = std::get::Variable>(_match_48._data); + const auto& _match_50 = value; + if (std::holds_alternative::Variable>(_match_50._data)) { + auto& _v = std::get::Variable>(_match_50._data); auto& tok = _v.name; init_list.push_back(((((std::string("") + (prop.lexeme)) + std::string("(")) + (tok.lexeme)) + std::string(")"))); handled = true; @@ -3131,9 +3324,9 @@ struct CppCodegen { void emit_class_method(const Token& class_name, const Stmt& method) { { - const auto& _match_49 = method; - if (std::holds_alternative::Function>(_match_49._data)) { - auto& _v = std::get::Function>(_match_49._data); + const auto& _match_51 = method; + if (std::holds_alternative::Function>(_match_51._data)) { + auto& _v = std::get::Function>(_match_51._data); auto& mname = _v.name; auto& mparams = _v.params; auto& mret = _v.return_type; @@ -3143,6 +3336,7 @@ struct CppCodegen { auto& is_static = _v.is_static; auto& visibility = _v.visibility; auto& m_tp = _v.type_params; + auto& m_defs2 = _v.param_defaults; std::vector saved_dyn = this->dynamic_vars; std::string ret_type = (*this).emit_type(mret); std::string mparam_str = (*this).emit_params(mparams, true); @@ -3183,15 +3377,16 @@ struct CppCodegen { void emit_class(const Token& name, const std::vector& body, const std::vector& type_params) { std::vector init_body = {}; std::vector init_params = {}; + std::vector init_defaults = {}; bool has_init = false; std::vector methods = {}; std::vector let_field_names = {}; std::vector let_field_types = {}; for (const auto& st : body) { { - const auto& _match_50 = st; - if (std::holds_alternative::Function>(_match_50._data)) { - auto& _v = std::get::Function>(_match_50._data); + const auto& _match_52 = st; + if (std::holds_alternative::Function>(_match_52._data)) { + auto& _v = std::get::Function>(_match_52._data); auto& fname = _v.name; auto& params = _v.params; auto& return_type = _v.return_type; @@ -3201,17 +3396,19 @@ struct CppCodegen { auto& is_static = _v.is_static; auto& visibility = _v.visibility; auto& fn_tp = _v.type_params; + auto& fn_defaults = _v.param_defaults; if ((fname.lexeme == std::string("constructor"))) { has_init = true; init_body = fbody; init_params = params; + init_defaults = fn_defaults; } else { methods.push_back(st); } } - else if (std::holds_alternative::Let>(_match_50._data)) { - auto& _v = std::get::Let>(_match_50._data); + else if (std::holds_alternative::Let>(_match_52._data)) { + auto& _v = std::get::Let>(_match_52._data); auto& fname = _v.name; auto& var_type = _v.var_type; auto& init = _v.initializer; @@ -3238,21 +3435,21 @@ struct CppCodegen { std::vector seen = {}; for (const auto& st : init_body) { { - const auto& _match_51 = st; - if (std::holds_alternative::ExprStmt>(_match_51._data)) { - auto& _v = std::get::ExprStmt>(_match_51._data); + const auto& _match_53 = st; + if (std::holds_alternative::ExprStmt>(_match_53._data)) { + auto& _v = std::get::ExprStmt>(_match_53._data); auto& expr = _v.expr; { - const auto& _match_52 = expr; - if (std::holds_alternative::Set>(_match_52._data)) { - auto& _v = std::get::Set>(_match_52._data); + const auto& _match_54 = expr; + if (std::holds_alternative::Set>(_match_54._data)) { + auto& _v = std::get::Set>(_match_54._data); auto& object = *_v.object; auto& prop = _v.name; auto& value = *_v.value; { - const auto& _match_53 = object; - if (std::holds_alternative::This>(_match_53._data)) { - auto& _v = std::get::This>(_match_53._data); + const auto& _match_55 = object; + if (std::holds_alternative::This>(_match_55._data)) { + auto& _v = std::get::This>(_match_55._data); auto& kw = _v.keyword; bool already = false; for (const auto& s : seen) { @@ -3287,6 +3484,8 @@ struct CppCodegen { this->indent_level = (this->indent_level + INT64_C(1)); (*this).emit_class_fields(init_field_names, init_field_types, let_field_names, let_field_types); if (has_init) { + this->fn_params[name.lexeme] = init_params; + this->fn_defaults[name.lexeme] = init_defaults; (*this).emit_constructor(name, init_params, init_body); } for (const auto& m : methods) { @@ -3297,9 +3496,9 @@ struct CppCodegen { bool has_to_string = false; for (const auto& m : methods) { { - const auto& _match_54 = m; - if (std::holds_alternative::Function>(_match_54._data)) { - auto& _v = std::get::Function>(_match_54._data); + const auto& _match_56 = m; + if (std::holds_alternative::Function>(_match_56._data)) { + auto& _v = std::get::Function>(_match_56._data); auto& mname = _v.name; auto& mparams = _v.params; auto& mret = _v.return_type; @@ -3309,6 +3508,7 @@ struct CppCodegen { auto& ms = _v.is_static; auto& mv = _v.visibility; auto& mtp = _v.type_params; + auto& m_defs = _v.param_defaults; if ((mname.lexeme == std::string("to_string"))) { has_to_string = true; } @@ -3333,9 +3533,9 @@ struct CppCodegen { std::string infer_expr_type(const Expr& e, const std::vector& param_names, const std::vector& param_types) { { - const auto& _match_55 = e; - if (std::holds_alternative::Literal>(_match_55._data)) { - auto& _v = std::get::Literal>(_match_55._data); + const auto& _match_57 = e; + if (std::holds_alternative::Literal>(_match_57._data)) { + auto& _v = std::get::Literal>(_match_57._data); auto& kind = _v.kind; auto& value = _v.value; if ((kind == std::string("int"))) { @@ -3363,8 +3563,8 @@ struct CppCodegen { } return std::string("std::any"); } - else if (std::holds_alternative::Variable>(_match_55._data)) { - auto& _v = std::get::Variable>(_match_55._data); + else if (std::holds_alternative::Variable>(_match_57._data)) { + auto& _v = std::get::Variable>(_match_57._data); auto& tok = _v.name; for (int64_t i = INT64_C(0); i < static_cast(param_names.size()); i++) { if ((param_names[i] == tok.lexeme)) { @@ -3376,8 +3576,8 @@ struct CppCodegen { } return std::string("std::any"); } - else if (std::holds_alternative::Binary>(_match_55._data)) { - auto& _v = std::get::Binary>(_match_55._data); + else if (std::holds_alternative::Binary>(_match_57._data)) { + auto& _v = std::get::Binary>(_match_57._data); auto& left = *_v.left; auto& op = _v.op; auto& right = *_v.right; @@ -3391,26 +3591,27 @@ struct CppCodegen { } return std::string("std::any"); } - else if (std::holds_alternative::Unary>(_match_55._data)) { - auto& _v = std::get::Unary>(_match_55._data); + else if (std::holds_alternative::Unary>(_match_57._data)) { + auto& _v = std::get::Unary>(_match_57._data); auto& op = _v.op; auto& right = *_v.right; return (*this).infer_expr_type(right, param_names, param_types); } - else if (std::holds_alternative::Grouping>(_match_55._data)) { - auto& _v = std::get::Grouping>(_match_55._data); + else if (std::holds_alternative::Grouping>(_match_57._data)) { + auto& _v = std::get::Grouping>(_match_57._data); auto& inner = *_v.inner; return (*this).infer_expr_type(inner, param_names, param_types); } - else if (std::holds_alternative::Call>(_match_55._data)) { - auto& _v = std::get::Call>(_match_55._data); + else if (std::holds_alternative::Call>(_match_57._data)) { + auto& _v = std::get::Call>(_match_57._data); auto& callee = *_v.callee; auto& paren = _v.paren; auto& args = _v.args; + auto& arg_names = _v.arg_names; { - const auto& _match_56 = callee; - if (std::holds_alternative::Variable>(_match_56._data)) { - auto& _v = std::get::Variable>(_match_56._data); + const auto& _match_58 = callee; + if (std::holds_alternative::Variable>(_match_58._data)) { + auto& _v = std::get::Variable>(_match_58._data); auto& tok = _v.name; if ((*this).is_known_enum(tok.lexeme)) { return tok.lexeme; @@ -3422,14 +3623,14 @@ struct CppCodegen { } return tok.lexeme; } - else if (std::holds_alternative::StaticGet>(_match_56._data)) { - auto& _v = std::get::StaticGet>(_match_56._data); + else if (std::holds_alternative::StaticGet>(_match_58._data)) { + auto& _v = std::get::StaticGet>(_match_58._data); auto& object = *_v.object; auto& name = _v.name; { - const auto& _match_57 = object; - if (std::holds_alternative::Variable>(_match_57._data)) { - auto& _v = std::get::Variable>(_match_57._data); + const auto& _match_59 = object; + if (std::holds_alternative::Variable>(_match_59._data)) { + auto& _v = std::get::Variable>(_match_59._data); auto& tok = _v.name; if ((*this).is_known_enum(tok.lexeme)) { return tok.lexeme; @@ -3446,8 +3647,8 @@ struct CppCodegen { } } } - else if (std::holds_alternative::Vector>(_match_55._data)) { - auto& _v = std::get::Vector>(_match_55._data); + else if (std::holds_alternative::Vector>(_match_57._data)) { + auto& _v = std::get::Vector>(_match_57._data); auto& elements = _v.elements; if ((static_cast(elements.size()) > INT64_C(0))) { std::string inner = (*this).infer_expr_type(elements[INT64_C(0)], param_names, param_types); @@ -3457,8 +3658,8 @@ struct CppCodegen { } return std::string("std::vector"); } - else if (std::holds_alternative::Map>(_match_55._data)) { - auto& _v = std::get::Map>(_match_55._data); + else if (std::holds_alternative::Map>(_match_57._data)) { + auto& _v = std::get::Map>(_match_57._data); auto& keys = _v.keys; auto& values = _v.values; std::string kt = std::string("std::any"); @@ -3469,8 +3670,8 @@ struct CppCodegen { } return ((((std::string("std::unordered_map<") + (kt)) + std::string(", ")) + (vt)) + std::string(">")); } - else if (std::holds_alternative::Cast>(_match_55._data)) { - auto& _v = std::get::Cast>(_match_55._data); + else if (std::holds_alternative::Cast>(_match_57._data)) { + auto& _v = std::get::Cast>(_match_57._data); auto& expr = *_v.expr; auto& target_type = _v.target_type; return (*this).emit_type(target_type); @@ -3489,9 +3690,9 @@ struct CppCodegen { std::string cpp_type = (*this).emit_type(v.types[fi]); std::string fname = v.field_names[fi]; { - const auto& _match_58 = v.types[fi]; - if (std::holds_alternative::Custom>(_match_58._data)) { - auto& _v = std::get::Custom>(_match_58._data); + const auto& _match_60 = v.types[fi]; + if (std::holds_alternative::Custom>(_match_60._data)) { + auto& _v = std::get::Custom>(_match_60._data); auto& n = _v.name; auto& _ = _v.type_args; if ((n == enum_name)) { @@ -3534,9 +3735,9 @@ struct CppCodegen { std::string fname = v.field_names[fi]; bool is_self_ref = false; { - const auto& _match_59 = v.types[fi]; - if (std::holds_alternative::Custom>(_match_59._data)) { - auto& _v = std::get::Custom>(_match_59._data); + const auto& _match_61 = v.types[fi]; + if (std::holds_alternative::Custom>(_match_61._data)) { + auto& _v = std::get::Custom>(_match_61._data); auto& n = _v.name; auto& _ = _v.type_args; if ((n == enum_name)) { @@ -3590,9 +3791,9 @@ struct CppCodegen { for (const auto& v : variants) { for (const auto& ft : v.types) { { - const auto& _match_60 = ft; - if (std::holds_alternative::Custom>(_match_60._data)) { - auto& _v = std::get::Custom>(_match_60._data); + const auto& _match_62 = ft; + if (std::holds_alternative::Custom>(_match_62._data)) { + auto& _v = std::get::Custom>(_match_62._data); auto& n = _v.name; auto& _ = _v.type_args; if ((n == enum_name)) { @@ -3623,9 +3824,9 @@ struct CppCodegen { bool has_to_string = false; for (const auto& m : methods) { { - const auto& _match_61 = m; - if (std::holds_alternative::Function>(_match_61._data)) { - auto& _v = std::get::Function>(_match_61._data); + const auto& _match_63 = m; + if (std::holds_alternative::Function>(_match_63._data)) { + auto& _v = std::get::Function>(_match_63._data); auto& mname = _v.name; auto& mparams = _v.params; auto& mret = _v.return_type; @@ -3635,6 +3836,7 @@ struct CppCodegen { auto& ms = _v.is_static; auto& mv = _v.visibility; auto& mtp = _v.type_params; + auto& m_defs = _v.param_defaults; if ((mname.lexeme == std::string("to_string"))) { has_to_string = true; } @@ -3694,9 +3896,9 @@ struct CppCodegen { bool is_self_ref = false; if ((bi < static_cast(vinfo.types.size()))) { { - const auto& _match_62 = vinfo.types[bi]; - if (std::holds_alternative::Custom>(_match_62._data)) { - auto& _v = std::get::Custom>(_match_62._data); + const auto& _match_64 = vinfo.types[bi]; + if (std::holds_alternative::Custom>(_match_64._data)) { + auto& _v = std::get::Custom>(_match_64._data); auto& n = _v.name; auto& _ = _v.type_args; if ((n == ename)) { @@ -3729,9 +3931,9 @@ struct CppCodegen { void emit_arm_body(const Stmt& arm_body, bool in_method) { { - const auto& _match_63 = arm_body; - if (std::holds_alternative::Block>(_match_63._data)) { - auto& _v = std::get::Block>(_match_63._data); + const auto& _match_65 = arm_body; + if (std::holds_alternative::Block>(_match_65._data)) { + auto& _v = std::get::Block>(_match_65._data); auto& stmts = _v.statements; for (const auto& st : stmts) { (*this).emit_stmt(st, in_method); @@ -3745,9 +3947,9 @@ struct CppCodegen { void emit_using_if_public(const std::string& ns, const Stmt& stmt) { { - const auto& _match_64 = stmt; - if (std::holds_alternative::Function>(_match_64._data)) { - auto& _v = std::get::Function>(_match_64._data); + const auto& _match_66 = stmt; + if (std::holds_alternative::Function>(_match_66._data)) { + auto& _v = std::get::Function>(_match_66._data); auto& name = _v.name; auto& params = _v.params; auto& return_type = _v.return_type; @@ -3757,12 +3959,13 @@ struct CppCodegen { auto& is_static = _v.is_static; auto& visibility = _v.visibility; auto& type_params = _v.type_params; + auto& param_defaults = _v.param_defaults; if ((visibility != std::string("private"))) { this->output = (this->output + ((((std::string("using ") + (ns)) + std::string("::")) + (name.lexeme)) + std::string(";\n"))); } } - else if (std::holds_alternative::Class>(_match_64._data)) { - auto& _v = std::get::Class>(_match_64._data); + else if (std::holds_alternative::Class>(_match_66._data)) { + auto& _v = std::get::Class>(_match_66._data); auto& name = _v.name; auto& body = _v.body; auto& visibility = _v.visibility; @@ -3770,8 +3973,8 @@ struct CppCodegen { this->output = (this->output + ((((std::string("using ") + (ns)) + std::string("::")) + (name.lexeme)) + std::string(";\n"))); } } - else if (std::holds_alternative::Struct>(_match_64._data)) { - auto& _v = std::get::Struct>(_match_64._data); + else if (std::holds_alternative::Struct>(_match_66._data)) { + auto& _v = std::get::Struct>(_match_66._data); auto& name = _v.name; auto& body = _v.body; auto& visibility = _v.visibility; @@ -3780,8 +3983,8 @@ struct CppCodegen { this->output = (this->output + ((((std::string("using ") + (ns)) + std::string("::")) + (name.lexeme)) + std::string(";\n"))); } } - else if (std::holds_alternative::Enum>(_match_64._data)) { - auto& _v = std::get::Enum>(_match_64._data); + else if (std::holds_alternative::Enum>(_match_66._data)) { + auto& _v = std::get::Enum>(_match_66._data); auto& name = _v.name; auto& variants = _v.variants; auto& methods = _v.methods; @@ -3791,8 +3994,8 @@ struct CppCodegen { this->output = (this->output + ((((std::string("using ") + (ns)) + std::string("::")) + (name.lexeme)) + std::string(";\n"))); } } - else if (std::holds_alternative::Const>(_match_64._data)) { - auto& _v = std::get::Const>(_match_64._data); + else if (std::holds_alternative::Const>(_match_66._data)) { + auto& _v = std::get::Const>(_match_66._data); auto& name = _v.name; auto& const_type = _v.const_type; auto& value = _v.value; @@ -3802,8 +4005,8 @@ struct CppCodegen { this->output = (this->output + ((((std::string("using ") + (ns)) + std::string("::")) + (name.lexeme)) + std::string(";\n"))); } } - else if (std::holds_alternative::Let>(_match_64._data)) { - auto& _v = std::get::Let>(_match_64._data); + else if (std::holds_alternative::Let>(_match_66._data)) { + auto& _v = std::get::Let>(_match_66._data); auto& name = _v.name; auto& var_type = _v.var_type; auto& initializer = _v.initializer; @@ -3828,9 +4031,9 @@ struct CppCodegen { this->indent_level = INT64_C(1); for (const auto& stmt : this->module_stmts[index]) { { - const auto& _match_65 = stmt; - if (std::holds_alternative::Function>(_match_65._data)) { - auto& _v = std::get::Function>(_match_65._data); + const auto& _match_67 = stmt; + if (std::holds_alternative::Function>(_match_67._data)) { + auto& _v = std::get::Function>(_match_67._data); auto& name = _v.name; auto& params = _v.params; auto& return_type = _v.return_type; @@ -3840,6 +4043,7 @@ struct CppCodegen { auto& is_static = _v.is_static; auto& visibility = _v.visibility; auto& type_params = _v.type_params; + auto& param_defaults = _v.param_defaults; if ((name.lexeme == std::string("main"))) { /* pass */ } @@ -3847,8 +4051,8 @@ struct CppCodegen { (*this).emit_stmt(stmt, false); } } - else if (std::holds_alternative::Extern>(_match_65._data)) { - auto& _v = std::get::Extern>(_match_65._data); + else if (std::holds_alternative::Extern>(_match_67._data)) { + auto& _v = std::get::Extern>(_match_67._data); auto& header = _v.header; auto& import_path = _v.import_path; auto& link_lib = _v.link_lib; @@ -3883,9 +4087,9 @@ struct CppCodegen { for (int64_t mi = INT64_C(0); mi < static_cast(this->module_stmts.size()); mi++) { for (const auto& stmt : this->module_stmts[mi]) { { - const auto& _match_66 = stmt; - if (std::holds_alternative::Extern>(_match_66._data)) { - auto& _v = std::get::Extern>(_match_66._data); + const auto& _match_68 = stmt; + if (std::holds_alternative::Extern>(_match_68._data)) { + auto& _v = std::get::Extern>(_match_68._data); auto& header = _v.header; auto& import_path = _v.import_path; auto& link_lib = _v.link_lib; @@ -3902,9 +4106,9 @@ struct CppCodegen { } for (const auto& stmt : stmts) { { - const auto& _match_67 = stmt; - if (std::holds_alternative::Extern>(_match_67._data)) { - auto& _v = std::get::Extern>(_match_67._data); + const auto& _match_69 = stmt; + if (std::holds_alternative::Extern>(_match_69._data)) { + auto& _v = std::get::Extern>(_match_69._data); auto& header = _v.header; auto& import_path = _v.import_path; auto& link_lib = _v.link_lib; @@ -3921,9 +4125,9 @@ struct CppCodegen { for (int64_t mi = INT64_C(0); mi < static_cast(this->module_stmts.size()); mi++) { for (const auto& stmt : this->module_stmts[mi]) { { - const auto& _match_68 = stmt; - if (std::holds_alternative::Extend>(_match_68._data)) { - auto& _v = std::get::Extend>(_match_68._data); + const auto& _match_70 = stmt; + if (std::holds_alternative::Extend>(_match_70._data)) { + auto& _v = std::get::Extend>(_match_70._data); auto& target = _v.target_type; auto& methods = _v.methods; auto& visibility = _v.visibility; @@ -3944,9 +4148,9 @@ struct CppCodegen { } for (const auto& stmt : stmts) { { - const auto& _match_69 = stmt; - if (std::holds_alternative::Extend>(_match_69._data)) { - auto& _v = std::get::Extend>(_match_69._data); + const auto& _match_71 = stmt; + if (std::holds_alternative::Extend>(_match_71._data)) { + auto& _v = std::get::Extend>(_match_71._data); auto& target = _v.target_type; auto& methods = _v.methods; auto& visibility = _v.visibility; @@ -3983,9 +4187,9 @@ struct CppCodegen { } for (const auto& stmt : stmts) { { - const auto& _match_70 = stmt; - if (std::holds_alternative::Function>(_match_70._data)) { - auto& _v = std::get::Function>(_match_70._data); + const auto& _match_72 = stmt; + if (std::holds_alternative::Function>(_match_72._data)) { + auto& _v = std::get::Function>(_match_72._data); auto& name = _v.name; auto& params = _v.params; auto& return_type = _v.return_type; @@ -3995,6 +4199,7 @@ struct CppCodegen { auto& is_static = _v.is_static; auto& visibility = _v.visibility; auto& type_params = _v.type_params; + auto& param_defaults = _v.param_defaults; if ((name.lexeme == std::string("main"))) { this->has_main = true; this->output = (this->output + std::string("int main(int argc, char* argv[]) {\n")); @@ -4014,8 +4219,8 @@ struct CppCodegen { this->output = std::string(""); } } - else if (std::holds_alternative::Extern>(_match_70._data)) { - auto& _v = std::get::Extern>(_match_70._data); + else if (std::holds_alternative::Extern>(_match_72._data)) { + auto& _v = std::get::Extern>(_match_72._data); auto& header = _v.header; auto& import_path = _v.import_path; auto& link_lib = _v.link_lib; @@ -4023,8 +4228,8 @@ struct CppCodegen { auto& functions = _v.functions; /* pass */ } - else if (std::holds_alternative::Extend>(_match_70._data)) { - auto& _v = std::get::Extend>(_match_70._data); + else if (std::holds_alternative::Extend>(_match_72._data)) { + auto& _v = std::get::Extend>(_match_72._data); auto& target = _v.target_type; auto& methods = _v.methods; auto& visibility = _v.visibility; @@ -4164,27 +4369,27 @@ struct Checker { bool types_compatible(const TypeNode& expected, const TypeNode& actual) { { - const auto& _match_71 = actual; - if (std::holds_alternative::Array>(_match_71._data)) { - auto& _v = std::get::Array>(_match_71._data); + const auto& _match_73 = actual; + if (std::holds_alternative::Array>(_match_73._data)) { + auto& _v = std::get::Array>(_match_73._data); auto& a_inner = *_v.inner; { - const auto& _match_72 = a_inner; - if (std::holds_alternative::Auto>(_match_72._data)) { + const auto& _match_74 = a_inner; + if (std::holds_alternative::Auto>(_match_74._data)) { { - const auto& _match_73 = expected; - if (std::holds_alternative::Array>(_match_73._data)) { - auto& _v = std::get::Array>(_match_73._data); + const auto& _match_75 = expected; + if (std::holds_alternative::Array>(_match_75._data)) { + auto& _v = std::get::Array>(_match_75._data); auto& e_inner = *_v.inner; return true; } - else if (std::holds_alternative::HashSet>(_match_73._data)) { - auto& _v = std::get::HashSet>(_match_73._data); + else if (std::holds_alternative::HashSet>(_match_75._data)) { + auto& _v = std::get::HashSet>(_match_75._data); auto& e_inner = *_v.inner; return true; } - else if (std::holds_alternative::HashMap>(_match_73._data)) { - auto& _v = std::get::HashMap>(_match_73._data); + else if (std::holds_alternative::HashMap>(_match_75._data)) { + auto& _v = std::get::HashMap>(_match_75._data); auto& ek = *_v.key_type; auto& ev = *_v.value_type; return true; @@ -4204,14 +4409,14 @@ struct Checker { } } { - const auto& _match_74 = expected; - if (std::holds_alternative::Array>(_match_74._data)) { - auto& _v = std::get::Array>(_match_74._data); + const auto& _match_76 = expected; + if (std::holds_alternative::Array>(_match_76._data)) { + auto& _v = std::get::Array>(_match_76._data); auto& e_inner = *_v.inner; { - const auto& _match_75 = actual; - if (std::holds_alternative::Array>(_match_75._data)) { - auto& _v = std::get::Array>(_match_75._data); + const auto& _match_77 = actual; + if (std::holds_alternative::Array>(_match_77._data)) { + auto& _v = std::get::Array>(_match_77._data); auto& a_inner = *_v.inner; return (*this).types_compatible(e_inner, a_inner); } @@ -4225,14 +4430,14 @@ struct Checker { } } { - const auto& _match_76 = expected; - if (std::holds_alternative::Auto>(_match_76._data)) { + const auto& _match_78 = expected; + if (std::holds_alternative::Auto>(_match_78._data)) { return true; } - else if (_match_76._tag == "None") { + else if (_match_78._tag == "None") { return true; } - else if (std::holds_alternative::Dynamic>(_match_76._data)) { + else if (std::holds_alternative::Dynamic>(_match_78._data)) { return true; } else { @@ -4240,14 +4445,14 @@ struct Checker { } } { - const auto& _match_77 = actual; - if (std::holds_alternative::Auto>(_match_77._data)) { + const auto& _match_79 = actual; + if (std::holds_alternative::Auto>(_match_79._data)) { return true; } - else if (_match_77._tag == "None") { + else if (_match_79._tag == "None") { return true; } - else if (std::holds_alternative::Dynamic>(_match_77._data)) { + else if (std::holds_alternative::Dynamic>(_match_79._data)) { return true; } else { @@ -4262,20 +4467,20 @@ struct Checker { bool e_is_int = false; bool a_is_int = false; { - const auto& _match_78 = expected; - if (std::holds_alternative::Int>(_match_78._data)) { + const auto& _match_80 = expected; + if (std::holds_alternative::Int>(_match_80._data)) { e_is_int = true; } - else if (std::holds_alternative::Int8>(_match_78._data)) { + else if (std::holds_alternative::Int8>(_match_80._data)) { e_is_int = true; } - else if (std::holds_alternative::Int16>(_match_78._data)) { + else if (std::holds_alternative::Int16>(_match_80._data)) { e_is_int = true; } - else if (std::holds_alternative::Int32>(_match_78._data)) { + else if (std::holds_alternative::Int32>(_match_80._data)) { e_is_int = true; } - else if (std::holds_alternative::USize>(_match_78._data)) { + else if (std::holds_alternative::USize>(_match_80._data)) { e_is_int = true; } else { @@ -4283,20 +4488,20 @@ struct Checker { } } { - const auto& _match_79 = actual; - if (std::holds_alternative::Int>(_match_79._data)) { + const auto& _match_81 = actual; + if (std::holds_alternative::Int>(_match_81._data)) { a_is_int = true; } - else if (std::holds_alternative::Int8>(_match_79._data)) { + else if (std::holds_alternative::Int8>(_match_81._data)) { a_is_int = true; } - else if (std::holds_alternative::Int16>(_match_79._data)) { + else if (std::holds_alternative::Int16>(_match_81._data)) { a_is_int = true; } - else if (std::holds_alternative::Int32>(_match_79._data)) { + else if (std::holds_alternative::Int32>(_match_81._data)) { a_is_int = true; } - else if (std::holds_alternative::USize>(_match_79._data)) { + else if (std::holds_alternative::USize>(_match_81._data)) { a_is_int = true; } else { @@ -4309,11 +4514,11 @@ struct Checker { bool e_is_float = false; bool a_is_float = false; { - const auto& _match_80 = expected; - if (std::holds_alternative::Float>(_match_80._data)) { + const auto& _match_82 = expected; + if (std::holds_alternative::Float>(_match_82._data)) { e_is_float = true; } - else if (std::holds_alternative::Float32>(_match_80._data)) { + else if (std::holds_alternative::Float32>(_match_82._data)) { e_is_float = true; } else { @@ -4321,11 +4526,11 @@ struct Checker { } } { - const auto& _match_81 = actual; - if (std::holds_alternative::Float>(_match_81._data)) { + const auto& _match_83 = actual; + if (std::holds_alternative::Float>(_match_83._data)) { a_is_float = true; } - else if (std::holds_alternative::Float32>(_match_81._data)) { + else if (std::holds_alternative::Float32>(_match_83._data)) { a_is_float = true; } else { @@ -4339,11 +4544,11 @@ struct Checker { return true; } { - const auto& _match_82 = expected; - if (std::holds_alternative::CString>(_match_82._data)) { + const auto& _match_84 = expected; + if (std::holds_alternative::CString>(_match_84._data)) { { - const auto& _match_83 = actual; - if (std::holds_alternative::Str>(_match_83._data)) { + const auto& _match_85 = actual; + if (std::holds_alternative::Str>(_match_85._data)) { return true; } else { @@ -4351,10 +4556,10 @@ struct Checker { } } } - else if (std::holds_alternative::Str>(_match_82._data)) { + else if (std::holds_alternative::Str>(_match_84._data)) { { - const auto& _match_84 = actual; - if (std::holds_alternative::CString>(_match_84._data)) { + const auto& _match_86 = actual; + if (std::holds_alternative::CString>(_match_86._data)) { return true; } else { @@ -4367,13 +4572,13 @@ struct Checker { } } { - const auto& _match_85 = expected; - if (std::holds_alternative::Nullable>(_match_85._data)) { - auto& _v = std::get::Nullable>(_match_85._data); + const auto& _match_87 = expected; + if (std::holds_alternative::Nullable>(_match_87._data)) { + auto& _v = std::get::Nullable>(_match_87._data); auto& inner = *_v.inner; { - const auto& _match_86 = actual; - if (std::holds_alternative::NullType>(_match_86._data)) { + const auto& _match_88 = actual; + if (std::holds_alternative::NullType>(_match_88._data)) { return true; } else { @@ -4386,13 +4591,13 @@ struct Checker { } } { - const auto& _match_87 = expected; - if (std::holds_alternative::Ptr>(_match_87._data)) { - auto& _v = std::get::Ptr>(_match_87._data); + const auto& _match_89 = expected; + if (std::holds_alternative::Ptr>(_match_89._data)) { + auto& _v = std::get::Ptr>(_match_89._data); auto& inner = *_v.inner; { - const auto& _match_88 = actual; - if (std::holds_alternative::NullType>(_match_88._data)) { + const auto& _match_90 = actual; + if (std::holds_alternative::NullType>(_match_90._data)) { return true; } else { @@ -4409,82 +4614,82 @@ struct Checker { std::string type_name(const TypeNode& t) { { - const auto& _match_89 = t; - if (std::holds_alternative::Int>(_match_89._data)) { + const auto& _match_91 = t; + if (std::holds_alternative::Int>(_match_91._data)) { return std::string("int"); } - else if (std::holds_alternative::Float>(_match_89._data)) { + else if (std::holds_alternative::Float>(_match_91._data)) { return std::string("float"); } - else if (std::holds_alternative::Str>(_match_89._data)) { + else if (std::holds_alternative::Str>(_match_91._data)) { return std::string("string"); } - else if (std::holds_alternative::Bool>(_match_89._data)) { + else if (std::holds_alternative::Bool>(_match_91._data)) { return std::string("bool"); } - else if (std::holds_alternative::Void>(_match_89._data)) { + else if (std::holds_alternative::Void>(_match_91._data)) { return std::string("void"); } - else if (std::holds_alternative::Auto>(_match_89._data)) { + else if (std::holds_alternative::Auto>(_match_91._data)) { return std::string("auto"); } - else if (std::holds_alternative::Dynamic>(_match_89._data)) { + else if (std::holds_alternative::Dynamic>(_match_91._data)) { return std::string("dynamic"); } - else if (std::holds_alternative::NullType>(_match_89._data)) { + else if (std::holds_alternative::NullType>(_match_91._data)) { return std::string("null"); } - else if (std::holds_alternative::Custom>(_match_89._data)) { - auto& _v = std::get::Custom>(_match_89._data); + else if (std::holds_alternative::Custom>(_match_91._data)) { + auto& _v = std::get::Custom>(_match_91._data); auto& name = _v.name; auto& _ = _v.type_args; return name; } - else if (std::holds_alternative::Array>(_match_89._data)) { - auto& _v = std::get::Array>(_match_89._data); + else if (std::holds_alternative::Array>(_match_91._data)) { + auto& _v = std::get::Array>(_match_91._data); auto& inner = *_v.inner; return ((std::string("vector[") + ((*this).type_name(inner))) + std::string("]")); } - else if (std::holds_alternative::HashSet>(_match_89._data)) { - auto& _v = std::get::HashSet>(_match_89._data); + else if (std::holds_alternative::HashSet>(_match_91._data)) { + auto& _v = std::get::HashSet>(_match_91._data); auto& inner = *_v.inner; return ((std::string("set[") + ((*this).type_name(inner))) + std::string("]")); } - else if (std::holds_alternative::HashMap>(_match_89._data)) { - auto& _v = std::get::HashMap>(_match_89._data); + else if (std::holds_alternative::HashMap>(_match_91._data)) { + auto& _v = std::get::HashMap>(_match_91._data); auto& k = *_v.key_type; auto& v = *_v.value_type; return ((((std::string("map[") + ((*this).type_name(k))) + std::string(", ")) + ((*this).type_name(v))) + std::string("]")); } - else if (std::holds_alternative::Nullable>(_match_89._data)) { - auto& _v = std::get::Nullable>(_match_89._data); + else if (std::holds_alternative::Nullable>(_match_91._data)) { + auto& _v = std::get::Nullable>(_match_91._data); auto& inner = *_v.inner; return ((std::string("") + ((*this).type_name(inner))) + std::string("?")); } - else if (std::holds_alternative::Int8>(_match_89._data)) { + else if (std::holds_alternative::Int8>(_match_91._data)) { return std::string("int8"); } - else if (std::holds_alternative::Int16>(_match_89._data)) { + else if (std::holds_alternative::Int16>(_match_91._data)) { return std::string("int16"); } - else if (std::holds_alternative::Int32>(_match_89._data)) { + else if (std::holds_alternative::Int32>(_match_91._data)) { return std::string("int32"); } - else if (std::holds_alternative::Float32>(_match_89._data)) { + else if (std::holds_alternative::Float32>(_match_91._data)) { return std::string("float32"); } - else if (std::holds_alternative::USize>(_match_89._data)) { + else if (std::holds_alternative::USize>(_match_91._data)) { return std::string("usize"); } - else if (std::holds_alternative::CString>(_match_89._data)) { + else if (std::holds_alternative::CString>(_match_91._data)) { return std::string("cstring"); } - else if (std::holds_alternative::Ptr>(_match_89._data)) { - auto& _v = std::get::Ptr>(_match_89._data); + else if (std::holds_alternative::Ptr>(_match_91._data)) { + auto& _v = std::get::Ptr>(_match_91._data); auto& inner = *_v.inner; return ((std::string("ptr[") + ((*this).type_name(inner))) + std::string("]")); } - else if (std::holds_alternative::Bytes>(_match_89._data)) { + else if (std::holds_alternative::Bytes>(_match_91._data)) { return std::string("bytes"); } else { @@ -4495,12 +4700,12 @@ struct Checker { TypeNode infer_type(const Expr& e) { { - const auto& _match_90 = e; - if (_match_90._tag == "None") { + const auto& _match_92 = e; + if (_match_92._tag == "None") { return TypeNode::make_None(); } - else if (std::holds_alternative::Literal>(_match_90._data)) { - auto& _v = std::get::Literal>(_match_90._data); + else if (std::holds_alternative::Literal>(_match_92._data)) { + auto& _v = std::get::Literal>(_match_92._data); auto& kind = _v.kind; auto& value = _v.value; if ((kind == std::string("int"))) { @@ -4528,14 +4733,14 @@ struct Checker { } return TypeNode::make_Auto(); } - else if (std::holds_alternative::Variable>(_match_90._data)) { - auto& _v = std::get::Variable>(_match_90._data); + else if (std::holds_alternative::Variable>(_match_92._data)) { + auto& _v = std::get::Variable>(_match_92._data); auto& name = _v.name; auto sym = (*this).lookup(name.lexeme); return sym.sym_type; } - else if (std::holds_alternative::Binary>(_match_90._data)) { - auto& _v = std::get::Binary>(_match_90._data); + else if (std::holds_alternative::Binary>(_match_92._data)) { + auto& _v = std::get::Binary>(_match_92._data); auto& left = *_v.left; auto& op = _v.op; auto& right = *_v.right; @@ -4545,8 +4750,8 @@ struct Checker { return TypeNode::make_Bool(); } { - const auto& _match_91 = lt; - if (std::holds_alternative::Str>(_match_91._data)) { + const auto& _match_93 = lt; + if (std::holds_alternative::Str>(_match_93._data)) { return TypeNode::make_Str(); } else { @@ -4554,8 +4759,8 @@ struct Checker { } } { - const auto& _match_92 = lt; - if (std::holds_alternative::Float>(_match_92._data)) { + const auto& _match_94 = lt; + if (std::holds_alternative::Float>(_match_94._data)) { return TypeNode::make_Float(); } else { @@ -4563,8 +4768,8 @@ struct Checker { } } { - const auto& _match_93 = rt; - if (std::holds_alternative::Float>(_match_93._data)) { + const auto& _match_95 = rt; + if (std::holds_alternative::Float>(_match_95._data)) { return TypeNode::make_Float(); } else { @@ -4572,8 +4777,8 @@ struct Checker { } } { - const auto& _match_94 = lt; - if (std::holds_alternative::Int>(_match_94._data)) { + const auto& _match_96 = lt; + if (std::holds_alternative::Int>(_match_96._data)) { return TypeNode::make_Int(); } else { @@ -4582,8 +4787,8 @@ struct Checker { } return TypeNode::make_Auto(); } - else if (std::holds_alternative::Unary>(_match_90._data)) { - auto& _v = std::get::Unary>(_match_90._data); + else if (std::holds_alternative::Unary>(_match_92._data)) { + auto& _v = std::get::Unary>(_match_92._data); auto& op = _v.op; auto& right = *_v.right; if ((op.token_type == TK_BANG) || (op.token_type == TK_NOT)) { @@ -4591,35 +4796,36 @@ struct Checker { } return (*this).infer_type(right); } - else if (std::holds_alternative::Logical>(_match_90._data)) { - auto& _v = std::get::Logical>(_match_90._data); + else if (std::holds_alternative::Logical>(_match_92._data)) { + auto& _v = std::get::Logical>(_match_92._data); auto& left = *_v.left; auto& op = _v.op; auto& right = *_v.right; return TypeNode::make_Bool(); } - else if (std::holds_alternative::Call>(_match_90._data)) { - auto& _v = std::get::Call>(_match_90._data); + else if (std::holds_alternative::Call>(_match_92._data)) { + auto& _v = std::get::Call>(_match_92._data); auto& callee = *_v.callee; auto& paren = _v.paren; auto& args = _v.args; + auto& arg_names = _v.arg_names; return (*this).infer_call_type(callee); } - else if (std::holds_alternative::Grouping>(_match_90._data)) { - auto& _v = std::get::Grouping>(_match_90._data); + else if (std::holds_alternative::Grouping>(_match_92._data)) { + auto& _v = std::get::Grouping>(_match_92._data); auto& inner = *_v.inner; return (*this).infer_type(inner); } - else if (std::holds_alternative::Index>(_match_90._data)) { - auto& _v = std::get::Index>(_match_90._data); + else if (std::holds_alternative::Index>(_match_92._data)) { + auto& _v = std::get::Index>(_match_92._data); auto& object = *_v.object; auto& bracket = _v.bracket; auto& index = *_v.index; auto ot = (*this).infer_type(object); { - const auto& _match_95 = ot; - if (std::holds_alternative::Array>(_match_95._data)) { - auto& _v = std::get::Array>(_match_95._data); + const auto& _match_97 = ot; + if (std::holds_alternative::Array>(_match_97._data)) { + auto& _v = std::get::Array>(_match_97._data); auto& inner = *_v.inner; return inner; } @@ -4628,8 +4834,8 @@ struct Checker { } } } - else if (std::holds_alternative::Vector>(_match_90._data)) { - auto& _v = std::get::Vector>(_match_90._data); + else if (std::holds_alternative::Vector>(_match_92._data)) { + auto& _v = std::get::Vector>(_match_92._data); auto& elements = _v.elements; if ((static_cast(elements.size()) > INT64_C(0))) { auto inner = (*this).infer_type(elements[INT64_C(0)]); @@ -4637,24 +4843,24 @@ struct Checker { } return TypeNode::make_Array(TypeNode::make_Auto()); } - else if (std::holds_alternative::Cast>(_match_90._data)) { - auto& _v = std::get::Cast>(_match_90._data); + else if (std::holds_alternative::Cast>(_match_92._data)) { + auto& _v = std::get::Cast>(_match_92._data); auto& expr = *_v.expr; auto& target_type = _v.target_type; return target_type; } - else if (std::holds_alternative::This>(_match_90._data)) { - auto& _v = std::get::This>(_match_90._data); + else if (std::holds_alternative::This>(_match_92._data)) { + auto& _v = std::get::This>(_match_92._data); auto& kw = _v.keyword; return TypeNode::make_Custom(this->current_class_name, {}); } - else if (std::holds_alternative::Own>(_match_90._data)) { - auto& _v = std::get::Own>(_match_90._data); + else if (std::holds_alternative::Own>(_match_92._data)) { + auto& _v = std::get::Own>(_match_92._data); auto& expr = *_v.expr; return (*this).infer_type(expr); } - else if (std::holds_alternative::AddressOf>(_match_90._data)) { - auto& _v = std::get::AddressOf>(_match_90._data); + else if (std::holds_alternative::AddressOf>(_match_92._data)) { + auto& _v = std::get::AddressOf>(_match_92._data); auto& expr = *_v.expr; return TypeNode::make_Ptr((*this).infer_type(expr)); } @@ -4666,9 +4872,9 @@ struct Checker { TypeNode infer_call_type(const Expr& callee) { { - const auto& _match_96 = callee; - if (std::holds_alternative::Variable>(_match_96._data)) { - auto& _v = std::get::Variable>(_match_96._data); + const auto& _match_98 = callee; + if (std::holds_alternative::Variable>(_match_98._data)) { + auto& _v = std::get::Variable>(_match_98._data); auto& name = _v.name; if ((this->known_funcs.count(name.lexeme) > 0)) { ExternFn fi = this->known_funcs[name.lexeme]; @@ -4682,20 +4888,20 @@ struct Checker { } return TypeNode::make_Auto(); } - else if (std::holds_alternative::Get>(_match_96._data)) { - auto& _v = std::get::Get>(_match_96._data); + else if (std::holds_alternative::Get>(_match_98._data)) { + auto& _v = std::get::Get>(_match_98._data); auto& object = *_v.object; auto& name = _v.name; return TypeNode::make_Auto(); } - else if (std::holds_alternative::StaticGet>(_match_96._data)) { - auto& _v = std::get::StaticGet>(_match_96._data); + else if (std::holds_alternative::StaticGet>(_match_98._data)) { + auto& _v = std::get::StaticGet>(_match_98._data); auto& object = *_v.object; auto& name = _v.name; { - const auto& _match_97 = object; - if (std::holds_alternative::Variable>(_match_97._data)) { - auto& _v = std::get::Variable>(_match_97._data); + const auto& _match_99 = object; + if (std::holds_alternative::Variable>(_match_99._data)) { + auto& _v = std::get::Variable>(_match_99._data); auto& obj_name = _v.name; if ((this->known_enums.count(obj_name.lexeme) > 0)) { return TypeNode::make_Custom(obj_name.lexeme, {}); @@ -4727,10 +4933,11 @@ struct Checker { } void register_builtins() { - std::vector builtins = std::vector{std::string("print"), std::string("println"), std::string("lv_assert"), std::string("to_string"), std::string("to_int"), std::string("to_float"), std::string("input"), std::string("typeof"), std::string("len"), std::string("exit"), std::string("abs"), std::string("cast")}; + std::vector builtins = std::vector{std::string("print"), std::string("println"), std::string("lv_assert"), std::string("to_string"), std::string("input"), std::string("typeof"), std::string("len"), std::string("exit"), std::string("abs"), std::string("cast")}; std::vector empty_params = {}; for (const auto& name : builtins) { - this->known_funcs[name] = ExternFn(name, name, TypeNode::make_Auto(), empty_params); + std::vector empty_defaults = {}; + this->known_funcs[name] = ExternFn(name, name, TypeNode::make_Auto(), empty_params, empty_defaults); } } @@ -4748,9 +4955,9 @@ struct Checker { void collect_decl(const Stmt& s) { { - const auto& _match_98 = s; - if (std::holds_alternative::Function>(_match_98._data)) { - auto& _v = std::get::Function>(_match_98._data); + const auto& _match_100 = s; + if (std::holds_alternative::Function>(_match_100._data)) { + auto& _v = std::get::Function>(_match_100._data); auto& name = _v.name; auto& params = _v.params; auto& return_type = _v.return_type; @@ -4760,17 +4967,18 @@ struct Checker { auto& is_static = _v.is_static; auto& visibility = _v.visibility; auto& type_params = _v.type_params; - this->known_funcs[name.lexeme] = ExternFn(name.lexeme, name.lexeme, return_type, params); + auto& param_defaults = _v.param_defaults; + this->known_funcs[name.lexeme] = ExternFn(name.lexeme, name.lexeme, return_type, params, param_defaults); } - else if (std::holds_alternative::Class>(_match_98._data)) { - auto& _v = std::get::Class>(_match_98._data); + else if (std::holds_alternative::Class>(_match_100._data)) { + auto& _v = std::get::Class>(_match_100._data); auto& name = _v.name; auto& body = _v.body; auto& visibility = _v.visibility; this->known_classes[name.lexeme] = body; } - else if (std::holds_alternative::Enum>(_match_98._data)) { - auto& _v = std::get::Enum>(_match_98._data); + else if (std::holds_alternative::Enum>(_match_100._data)) { + auto& _v = std::get::Enum>(_match_100._data); auto& name = _v.name; auto& variants = _v.variants; auto& methods = _v.methods; @@ -4778,8 +4986,8 @@ struct Checker { auto& enum_tp = _v.type_params; this->known_enums[name.lexeme] = variants; } - else if (std::holds_alternative::Const>(_match_98._data)) { - auto& _v = std::get::Const>(_match_98._data); + else if (std::holds_alternative::Const>(_match_100._data)) { + auto& _v = std::get::Const>(_match_100._data); auto& name = _v.name; auto& const_type = _v.const_type; auto& value = _v.value; @@ -4787,16 +4995,40 @@ struct Checker { auto& comptime_mode = _v.comptime_mode; (*this).declare(name.lexeme, const_type, std::string("const"), false, false, name); } - else if (std::holds_alternative::Struct>(_match_98._data)) { - auto& _v = std::get::Struct>(_match_98._data); + else if (std::holds_alternative::Struct>(_match_100._data)) { + auto& _v = std::get::Struct>(_match_100._data); auto& name = _v.name; auto& body = _v.body; auto& visibility = _v.visibility; auto& struct_tp = _v.type_params; this->known_classes[name.lexeme] = body; + for (const auto& st : body) { + { + const auto& _match_101 = st; + if (std::holds_alternative::Function>(_match_101._data)) { + auto& _v = std::get::Function>(_match_101._data); + auto& fname = _v.name; + auto& fparams = _v.params; + auto& fret = _v.return_type; + auto& fbody = _v.body; + auto& fi = _v.is_inline; + auto& fc = _v.comptime_mode; + auto& fs = _v.is_static; + auto& fv = _v.visibility; + auto& ftp = _v.type_params; + auto& f_defaults = _v.param_defaults; + if ((fname.lexeme == std::string("constructor"))) { + this->known_funcs[name.lexeme] = ExternFn(name.lexeme, name.lexeme, TypeNode::make_Custom(name.lexeme, {}), fparams, f_defaults); + } + } + else { + /* pass */ + } + } + } } - else if (std::holds_alternative::Namespace>(_match_98._data)) { - auto& _v = std::get::Namespace>(_match_98._data); + else if (std::holds_alternative::Namespace>(_match_100._data)) { + auto& _v = std::get::Namespace>(_match_100._data); auto& name = _v.name; auto& body = _v.body; auto& visibility = _v.visibility; @@ -4804,8 +5036,8 @@ struct Checker { (*this).collect_decl(ns_stmt); } } - else if (std::holds_alternative::Extern>(_match_98._data)) { - auto& _v = std::get::Extern>(_match_98._data); + else if (std::holds_alternative::Extern>(_match_100._data)) { + auto& _v = std::get::Extern>(_match_100._data); auto& header = _v.header; auto& import_path = _v.import_path; auto& link_lib = _v.link_lib; @@ -4827,14 +5059,14 @@ struct Checker { void check_stmt(const Stmt& s) { { - const auto& _match_99 = s; - if (std::holds_alternative::ExprStmt>(_match_99._data)) { - auto& _v = std::get::ExprStmt>(_match_99._data); + const auto& _match_102 = s; + if (std::holds_alternative::ExprStmt>(_match_102._data)) { + auto& _v = std::get::ExprStmt>(_match_102._data); auto& expr = _v.expr; (*this).check_expr(expr); } - else if (std::holds_alternative::Let>(_match_99._data)) { - auto& _v = std::get::Let>(_match_99._data); + else if (std::holds_alternative::Let>(_match_102._data)) { + auto& _v = std::get::Let>(_match_102._data); auto& name = _v.name; auto& var_type = _v.var_type; auto& initializer = _v.initializer; @@ -4844,11 +5076,11 @@ struct Checker { (*this).check_expr(initializer); auto init_type = (*this).infer_type(initializer); { - const auto& _match_100 = var_type; - if (std::holds_alternative::Auto>(_match_100._data)) { + const auto& _match_103 = var_type; + if (std::holds_alternative::Auto>(_match_103._data)) { /* pass */ } - else if (_match_100._tag == "None") { + else if (_match_103._tag == "None") { /* pass */ } else { @@ -4859,8 +5091,8 @@ struct Checker { } (*this).declare(name.lexeme, var_type, std::string("var"), is_ref, true, name); } - else if (std::holds_alternative::Const>(_match_99._data)) { - auto& _v = std::get::Const>(_match_99._data); + else if (std::holds_alternative::Const>(_match_102._data)) { + auto& _v = std::get::Const>(_match_102._data); auto& name = _v.name; auto& const_type = _v.const_type; auto& value = _v.value; @@ -4868,20 +5100,20 @@ struct Checker { auto& comptime_mode = _v.comptime_mode; (*this).check_expr(value); } - else if (std::holds_alternative::Return>(_match_99._data)) { - auto& _v = std::get::Return>(_match_99._data); + else if (std::holds_alternative::Return>(_match_102._data)) { + auto& _v = std::get::Return>(_match_102._data); auto& keyword = _v.keyword; auto& value = _v.value; (*this).check_expr(value); { - const auto& _match_101 = this->current_return_type; - if (_match_101._tag == "None") { + const auto& _match_104 = this->current_return_type; + if (_match_104._tag == "None") { /* pass */ } - else if (std::holds_alternative::Void>(_match_101._data)) { + else if (std::holds_alternative::Void>(_match_104._data)) { { - const auto& _match_102 = value; - if (_match_102._tag == "None") { + const auto& _match_105 = value; + if (_match_105._tag == "None") { /* pass */ } else { @@ -4897,8 +5129,8 @@ struct Checker { } } } - else if (std::holds_alternative::If>(_match_99._data)) { - auto& _v = std::get::If>(_match_99._data); + else if (std::holds_alternative::If>(_match_102._data)) { + auto& _v = std::get::If>(_match_102._data); auto& condition = _v.condition; auto& then_branch = *_v.then_branch; auto& else_branch = *_v.else_branch; @@ -4906,15 +5138,15 @@ struct Checker { (*this).check_stmt(then_branch); (*this).check_stmt(else_branch); } - else if (std::holds_alternative::While>(_match_99._data)) { - auto& _v = std::get::While>(_match_99._data); + else if (std::holds_alternative::While>(_match_102._data)) { + auto& _v = std::get::While>(_match_102._data); auto& condition = _v.condition; auto& body = *_v.body; (*this).check_expr(condition); (*this).check_stmt(body); } - else if (std::holds_alternative::For>(_match_99._data)) { - auto& _v = std::get::For>(_match_99._data); + else if (std::holds_alternative::For>(_match_102._data)) { + auto& _v = std::get::For>(_match_102._data); auto& item_name = _v.item_name; auto& collection = _v.collection; auto& body = *_v.body; @@ -4925,9 +5157,9 @@ struct Checker { auto coll_type = (*this).infer_type(collection); auto item_type = TypeNode::make_Auto(); { - const auto& _match_103 = coll_type; - if (std::holds_alternative::Array>(_match_103._data)) { - auto& _v = std::get::Array>(_match_103._data); + const auto& _match_106 = coll_type; + if (std::holds_alternative::Array>(_match_106._data)) { + auto& _v = std::get::Array>(_match_106._data); auto& inner = *_v.inner; item_type = inner; } @@ -4939,8 +5171,8 @@ struct Checker { (*this).check_stmt(body); (*this).pop_scope(); } - else if (std::holds_alternative::Block>(_match_99._data)) { - auto& _v = std::get::Block>(_match_99._data); + else if (std::holds_alternative::Block>(_match_102._data)) { + auto& _v = std::get::Block>(_match_102._data); auto& statements = _v.statements; (*this).push_scope(); for (const auto& st : statements) { @@ -4948,8 +5180,8 @@ struct Checker { } (*this).pop_scope(); } - else if (std::holds_alternative::Function>(_match_99._data)) { - auto& _v = std::get::Function>(_match_99._data); + else if (std::holds_alternative::Function>(_match_102._data)) { + auto& _v = std::get::Function>(_match_102._data); auto& name = _v.name; auto& params = _v.params; auto& return_type = _v.return_type; @@ -4959,25 +5191,26 @@ struct Checker { auto& is_static = _v.is_static; auto& visibility = _v.visibility; auto& type_params = _v.type_params; + auto& param_defaults = _v.param_defaults; (*this).check_function(name, params, return_type, body); } - else if (std::holds_alternative::Class>(_match_99._data)) { - auto& _v = std::get::Class>(_match_99._data); + else if (std::holds_alternative::Class>(_match_102._data)) { + auto& _v = std::get::Class>(_match_102._data); auto& name = _v.name; auto& body = _v.body; auto& visibility = _v.visibility; (*this).check_class(name, body); } - else if (std::holds_alternative::Struct>(_match_99._data)) { - auto& _v = std::get::Struct>(_match_99._data); + else if (std::holds_alternative::Struct>(_match_102._data)) { + auto& _v = std::get::Struct>(_match_102._data); auto& name = _v.name; auto& body = _v.body; auto& visibility = _v.visibility; auto& struct_tp = _v.type_params; /* pass */ } - else if (std::holds_alternative::Enum>(_match_99._data)) { - auto& _v = std::get::Enum>(_match_99._data); + else if (std::holds_alternative::Enum>(_match_102._data)) { + auto& _v = std::get::Enum>(_match_102._data); auto& name = _v.name; auto& variants = _v.variants; auto& methods = _v.methods; @@ -4985,16 +5218,16 @@ struct Checker { auto& enum_tp = _v.type_params; /* pass */ } - else if (std::holds_alternative::Match>(_match_99._data)) { - auto& _v = std::get::Match>(_match_99._data); + else if (std::holds_alternative::Match>(_match_102._data)) { + auto& _v = std::get::Match>(_match_102._data); auto& expr = _v.expr; auto& arm_patterns = _v.arm_patterns; auto& arm_bodies = _v.arm_bodies; (*this).check_expr(expr); (*this).check_match(expr, arm_patterns, arm_bodies); } - else if (std::holds_alternative::Try>(_match_99._data)) { - auto& _v = std::get::Try>(_match_99._data); + else if (std::holds_alternative::Try>(_match_102._data)) { + auto& _v = std::get::Try>(_match_102._data); auto& try_body = *_v.try_body; auto& catch_body = *_v.catch_body; auto& exception_name = _v.exception_name; @@ -5008,8 +5241,8 @@ struct Checker { (*this).check_stmt(catch_body); (*this).pop_scope(); } - else if (std::holds_alternative::Namespace>(_match_99._data)) { - auto& _v = std::get::Namespace>(_match_99._data); + else if (std::holds_alternative::Namespace>(_match_102._data)) { + auto& _v = std::get::Namespace>(_match_102._data); auto& name = _v.name; auto& body = _v.body; auto& visibility = _v.visibility; @@ -5022,34 +5255,34 @@ struct Checker { } (*this).pop_scope(); } - else if (std::holds_alternative::Import>(_match_99._data)) { - auto& _v = std::get::Import>(_match_99._data); + else if (std::holds_alternative::Import>(_match_102._data)) { + auto& _v = std::get::Import>(_match_102._data); auto& path = _v.path; auto& alias = _v.alias; /* pass */ } - else if (std::holds_alternative::Break>(_match_99._data)) { - auto& _v = std::get::Break>(_match_99._data); + else if (std::holds_alternative::Break>(_match_102._data)) { + auto& _v = std::get::Break>(_match_102._data); auto& keyword = _v.keyword; /* pass */ } - else if (std::holds_alternative::Continue>(_match_99._data)) { - auto& _v = std::get::Continue>(_match_99._data); + else if (std::holds_alternative::Continue>(_match_102._data)) { + auto& _v = std::get::Continue>(_match_102._data); auto& keyword = _v.keyword; /* pass */ } - else if (std::holds_alternative::Pass>(_match_99._data)) { - auto& _v = std::get::Pass>(_match_99._data); + else if (std::holds_alternative::Pass>(_match_102._data)) { + auto& _v = std::get::Pass>(_match_102._data); auto& keyword = _v.keyword; /* pass */ } - else if (std::holds_alternative::CppBlock>(_match_99._data)) { - auto& _v = std::get::CppBlock>(_match_99._data); + else if (std::holds_alternative::CppBlock>(_match_102._data)) { + auto& _v = std::get::CppBlock>(_match_102._data); auto& code = _v.code; /* pass */ } - else if (std::holds_alternative::Extern>(_match_99._data)) { - auto& _v = std::get::Extern>(_match_99._data); + else if (std::holds_alternative::Extern>(_match_102._data)) { + auto& _v = std::get::Extern>(_match_102._data); auto& header = _v.header; auto& import_path = _v.import_path; auto& link_lib = _v.link_lib; @@ -5086,9 +5319,9 @@ struct Checker { (*this).declare(std::string("this"), TypeNode::make_Custom(name.lexeme, {}), std::string("var"), false, false, name); for (const auto& s : body) { { - const auto& _match_104 = s; - if (std::holds_alternative::Function>(_match_104._data)) { - auto& _v = std::get::Function>(_match_104._data); + const auto& _match_107 = s; + if (std::holds_alternative::Function>(_match_107._data)) { + auto& _v = std::get::Function>(_match_107._data); auto& fname = _v.name; auto& params = _v.params; auto& return_type = _v.return_type; @@ -5098,10 +5331,11 @@ struct Checker { auto& is_static = _v.is_static; auto& visibility = _v.visibility; auto& fn_tp = _v.type_params; + auto& fn_defaults = _v.param_defaults; (*this).check_function(fname, params, return_type, fbody); } - else if (std::holds_alternative::Let>(_match_104._data)) { - auto& _v = std::get::Let>(_match_104._data); + else if (std::holds_alternative::Let>(_match_107._data)) { + auto& _v = std::get::Let>(_match_107._data); auto& lname = _v.name; auto& var_type = _v.var_type; auto& initializer = _v.initializer; @@ -5122,57 +5356,58 @@ struct Checker { void check_expr(const Expr& e) { { - const auto& _match_105 = e; - if (_match_105._tag == "None") { + const auto& _match_108 = e; + if (_match_108._tag == "None") { /* pass */ } - else if (std::holds_alternative::Variable>(_match_105._data)) { - auto& _v = std::get::Variable>(_match_105._data); + else if (std::holds_alternative::Variable>(_match_108._data)) { + auto& _v = std::get::Variable>(_match_108._data); auto& name = _v.name; if ((!(*this).resolve(name.lexeme))) { (*this).error(((std::string("Undefined variable '") + (name.lexeme)) + std::string("'")), name); } } - else if (std::holds_alternative::Binary>(_match_105._data)) { - auto& _v = std::get::Binary>(_match_105._data); + else if (std::holds_alternative::Binary>(_match_108._data)) { + auto& _v = std::get::Binary>(_match_108._data); auto& left = *_v.left; auto& op = _v.op; auto& right = *_v.right; (*this).check_expr(left); (*this).check_expr(right); } - else if (std::holds_alternative::Unary>(_match_105._data)) { - auto& _v = std::get::Unary>(_match_105._data); + else if (std::holds_alternative::Unary>(_match_108._data)) { + auto& _v = std::get::Unary>(_match_108._data); auto& op = _v.op; auto& right = *_v.right; (*this).check_expr(right); } - else if (std::holds_alternative::Logical>(_match_105._data)) { - auto& _v = std::get::Logical>(_match_105._data); + else if (std::holds_alternative::Logical>(_match_108._data)) { + auto& _v = std::get::Logical>(_match_108._data); auto& left = *_v.left; auto& op = _v.op; auto& right = *_v.right; (*this).check_expr(left); (*this).check_expr(right); } - else if (std::holds_alternative::Grouping>(_match_105._data)) { - auto& _v = std::get::Grouping>(_match_105._data); + else if (std::holds_alternative::Grouping>(_match_108._data)) { + auto& _v = std::get::Grouping>(_match_108._data); auto& inner = *_v.inner; (*this).check_expr(inner); } - else if (std::holds_alternative::Call>(_match_105._data)) { - auto& _v = std::get::Call>(_match_105._data); + else if (std::holds_alternative::Call>(_match_108._data)) { + auto& _v = std::get::Call>(_match_108._data); auto& callee = *_v.callee; auto& paren = _v.paren; auto& args = _v.args; + auto& arg_names = _v.arg_names; (*this).check_expr(callee); for (const auto& a : args) { (*this).check_expr(a); } - (*this).check_call_args(callee, args, paren); + (*this).check_call_args(callee, args, arg_names, paren); } - else if (std::holds_alternative::Assign>(_match_105._data)) { - auto& _v = std::get::Assign>(_match_105._data); + else if (std::holds_alternative::Assign>(_match_108._data)) { + auto& _v = std::get::Assign>(_match_108._data); auto& name = _v.name; auto& value = *_v.value; (*this).check_expr(value); @@ -5186,16 +5421,16 @@ struct Checker { } } } - else if (std::holds_alternative::Index>(_match_105._data)) { - auto& _v = std::get::Index>(_match_105._data); + else if (std::holds_alternative::Index>(_match_108._data)) { + auto& _v = std::get::Index>(_match_108._data); auto& object = *_v.object; auto& bracket = _v.bracket; auto& index = *_v.index; (*this).check_expr(object); (*this).check_expr(index); } - else if (std::holds_alternative::IndexSet>(_match_105._data)) { - auto& _v = std::get::IndexSet>(_match_105._data); + else if (std::holds_alternative::IndexSet>(_match_108._data)) { + auto& _v = std::get::IndexSet>(_match_108._data); auto& object = *_v.object; auto& bracket = _v.bracket; auto& index = *_v.index; @@ -5204,15 +5439,15 @@ struct Checker { (*this).check_expr(index); (*this).check_expr(value); } - else if (std::holds_alternative::Vector>(_match_105._data)) { - auto& _v = std::get::Vector>(_match_105._data); + else if (std::holds_alternative::Vector>(_match_108._data)) { + auto& _v = std::get::Vector>(_match_108._data); auto& elements = _v.elements; for (const auto& el : elements) { (*this).check_expr(el); } } - else if (std::holds_alternative::Map>(_match_105._data)) { - auto& _v = std::get::Map>(_match_105._data); + else if (std::holds_alternative::Map>(_match_108._data)) { + auto& _v = std::get::Map>(_match_108._data); auto& keys = _v.keys; auto& values = _v.values; for (const auto& k : keys) { @@ -5222,28 +5457,28 @@ struct Checker { (*this).check_expr(v); } } - else if (std::holds_alternative::Get>(_match_105._data)) { - auto& _v = std::get::Get>(_match_105._data); + else if (std::holds_alternative::Get>(_match_108._data)) { + auto& _v = std::get::Get>(_match_108._data); auto& object = *_v.object; auto& name = _v.name; (*this).check_expr(object); } - else if (std::holds_alternative::Set>(_match_105._data)) { - auto& _v = std::get::Set>(_match_105._data); + else if (std::holds_alternative::Set>(_match_108._data)) { + auto& _v = std::get::Set>(_match_108._data); auto& object = *_v.object; auto& name = _v.name; auto& value = *_v.value; (*this).check_expr(object); (*this).check_expr(value); } - else if (std::holds_alternative::StaticGet>(_match_105._data)) { - auto& _v = std::get::StaticGet>(_match_105._data); + else if (std::holds_alternative::StaticGet>(_match_108._data)) { + auto& _v = std::get::StaticGet>(_match_108._data); auto& object = *_v.object; auto& name = _v.name; { - const auto& _match_106 = object; - if (std::holds_alternative::Variable>(_match_106._data)) { - auto& _v = std::get::Variable>(_match_106._data); + const auto& _match_109 = object; + if (std::holds_alternative::Variable>(_match_109._data)) { + auto& _v = std::get::Variable>(_match_109._data); auto& tok = _v.name; /* pass */ } @@ -5252,26 +5487,26 @@ struct Checker { } } } - else if (std::holds_alternative::Cast>(_match_105._data)) { - auto& _v = std::get::Cast>(_match_105._data); + else if (std::holds_alternative::Cast>(_match_108._data)) { + auto& _v = std::get::Cast>(_match_108._data); auto& expr = *_v.expr; auto& target_type = _v.target_type; (*this).check_expr(expr); } - else if (std::holds_alternative::Throw>(_match_105._data)) { - auto& _v = std::get::Throw>(_match_105._data); + else if (std::holds_alternative::Throw>(_match_108._data)) { + auto& _v = std::get::Throw>(_match_108._data); auto& expr = *_v.expr; (*this).check_expr(expr); } - else if (std::holds_alternative::Range>(_match_105._data)) { - auto& _v = std::get::Range>(_match_105._data); + else if (std::holds_alternative::Range>(_match_108._data)) { + auto& _v = std::get::Range>(_match_108._data); auto& start = *_v.start; auto& end = *_v.end; (*this).check_expr(start); (*this).check_expr(end); } - else if (std::holds_alternative::Lambda>(_match_105._data)) { - auto& _v = std::get::Lambda>(_match_105._data); + else if (std::holds_alternative::Lambda>(_match_108._data)) { + auto& _v = std::get::Lambda>(_match_108._data); auto& params = _v.params; auto& body = *_v.body; (*this).push_scope(); @@ -5281,8 +5516,8 @@ struct Checker { (*this).check_expr(body); (*this).pop_scope(); } - else if (std::holds_alternative::BlockLambda>(_match_105._data)) { - auto& _v = std::get::BlockLambda>(_match_105._data); + else if (std::holds_alternative::BlockLambda>(_match_108._data)) { + auto& _v = std::get::BlockLambda>(_match_108._data); auto& params = _v.params; auto& body_id = _v.body_id; (*this).push_scope(); @@ -5291,13 +5526,13 @@ struct Checker { } (*this).pop_scope(); } - else if (std::holds_alternative::Own>(_match_105._data)) { - auto& _v = std::get::Own>(_match_105._data); + else if (std::holds_alternative::Own>(_match_108._data)) { + auto& _v = std::get::Own>(_match_108._data); auto& expr = *_v.expr; (*this).check_expr(expr); } - else if (std::holds_alternative::AddressOf>(_match_105._data)) { - auto& _v = std::get::AddressOf>(_match_105._data); + else if (std::holds_alternative::AddressOf>(_match_108._data)) { + auto& _v = std::get::AddressOf>(_match_108._data); auto& expr = *_v.expr; (*this).check_expr(expr); } @@ -5307,16 +5542,41 @@ struct Checker { } } - void check_call_args(const Expr& callee, const std::vector& args, const Token& paren) { + void check_call_args(const Expr& callee, const std::vector& args, const std::vector& arg_names, const Token& paren) { { - const auto& _match_107 = callee; - if (std::holds_alternative::Variable>(_match_107._data)) { - auto& _v = std::get::Variable>(_match_107._data); + const auto& _match_110 = callee; + if (std::holds_alternative::Variable>(_match_110._data)) { + auto& _v = std::get::Variable>(_match_110._data); auto& name = _v.name; if ((this->known_funcs.count(name.lexeme) > 0)) { ExternFn fi = this->known_funcs[name.lexeme]; - if ((static_cast(fi.params.size()) > INT64_C(0)) && (static_cast(args.size()) != static_cast(fi.params.size()))) { - (*this).error(((((((std::string("Function '") + (name.lexeme)) + std::string("' expects ")) + (static_cast(fi.params.size()))) + std::string(" args, got ")) + (static_cast(args.size()))) + std::string("")), paren); + if ((static_cast(fi.params.size()) == INT64_C(0))) { + return; + } + int64_t required = INT64_C(0); + int64_t di = INT64_C(0); + while ((di < static_cast(fi.param_defaults.size()))) { + { + const auto& _match_111 = fi.param_defaults[di]; + if (_match_111._tag == "None") { + required = (required + INT64_C(1)); + } + else { + /* pass */ + } + } + di = (di + INT64_C(1)); + } + if ((static_cast(fi.param_defaults.size()) == INT64_C(0))) { + required = static_cast(fi.params.size()); + } + if ((static_cast(args.size()) < required) || (static_cast(args.size()) > static_cast(fi.params.size()))) { + if ((required == static_cast(fi.params.size()))) { + (*this).error(((((((std::string("Function '") + (name.lexeme)) + std::string("' expects ")) + (static_cast(fi.params.size()))) + std::string(" args, got ")) + (static_cast(args.size()))) + std::string("")), paren); + } + else { + (*this).error(((((((((std::string("Function '") + (name.lexeme)) + std::string("' expects ")) + (required)) + std::string("-")) + (static_cast(fi.params.size()))) + std::string(" args, got ")) + (static_cast(args.size()))) + std::string("")), paren); + } } return; } @@ -5365,12 +5625,14 @@ struct Parser { int64_t current; bool in_class_body; std::vector> lambda_blocks; + std::vector last_param_defaults; Parser(std::vector tokens) : tokens(tokens) { this->current = INT64_C(0); this->in_class_body = false; this->lambda_blocks = {}; + this->last_param_defaults = {}; } bool is_at_end() { @@ -5681,27 +5943,27 @@ struct Parser { std::string type_to_string(const TypeNode& t) { { - const auto& _match_108 = t; - if (std::holds_alternative::Int>(_match_108._data)) { + const auto& _match_112 = t; + if (std::holds_alternative::Int>(_match_112._data)) { return std::string("int64_t"); } - else if (std::holds_alternative::Float>(_match_108._data)) { + else if (std::holds_alternative::Float>(_match_112._data)) { return std::string("double"); } - else if (std::holds_alternative::Str>(_match_108._data)) { + else if (std::holds_alternative::Str>(_match_112._data)) { return std::string("std::string"); } - else if (std::holds_alternative::Bool>(_match_108._data)) { + else if (std::holds_alternative::Bool>(_match_112._data)) { return std::string("bool"); } - else if (std::holds_alternative::Void>(_match_108._data)) { + else if (std::holds_alternative::Void>(_match_112._data)) { return std::string("void"); } - else if (std::holds_alternative::Auto>(_match_108._data)) { + else if (std::holds_alternative::Auto>(_match_112._data)) { return std::string("auto"); } - else if (std::holds_alternative::Custom>(_match_108._data)) { - auto& _v = std::get::Custom>(_match_108._data); + else if (std::holds_alternative::Custom>(_match_112._data)) { + auto& _v = std::get::Custom>(_match_112._data); auto& name = _v.name; auto& type_args = _v.type_args; if ((static_cast(type_args.size()) > INT64_C(0))) { @@ -5713,35 +5975,35 @@ struct Parser { } return name; } - else if (std::holds_alternative::Array>(_match_108._data)) { - auto& _v = std::get::Array>(_match_108._data); + else if (std::holds_alternative::Array>(_match_112._data)) { + auto& _v = std::get::Array>(_match_112._data); auto& inner = *_v.inner; return ((std::string("std::vector<") + ((*this).type_to_string(inner))) + std::string(">")); } - else if (std::holds_alternative::Int8>(_match_108._data)) { + else if (std::holds_alternative::Int8>(_match_112._data)) { return std::string("int8_t"); } - else if (std::holds_alternative::Int16>(_match_108._data)) { + else if (std::holds_alternative::Int16>(_match_112._data)) { return std::string("int16_t"); } - else if (std::holds_alternative::Int32>(_match_108._data)) { + else if (std::holds_alternative::Int32>(_match_112._data)) { return std::string("int32_t"); } - else if (std::holds_alternative::Float32>(_match_108._data)) { + else if (std::holds_alternative::Float32>(_match_112._data)) { return std::string("float"); } - else if (std::holds_alternative::USize>(_match_108._data)) { + else if (std::holds_alternative::USize>(_match_112._data)) { return std::string("size_t"); } - else if (std::holds_alternative::CString>(_match_108._data)) { + else if (std::holds_alternative::CString>(_match_112._data)) { return std::string("const char*"); } - else if (std::holds_alternative::Ptr>(_match_108._data)) { - auto& _v = std::get::Ptr>(_match_108._data); + else if (std::holds_alternative::Ptr>(_match_112._data)) { + auto& _v = std::get::Ptr>(_match_112._data); auto& inner = *_v.inner; return ((std::string("") + ((*this).type_to_string(inner))) + std::string("*")); } - else if (std::holds_alternative::Bytes>(_match_108._data)) { + else if (std::holds_alternative::Bytes>(_match_112._data)) { return std::string("std::vector"); } else { @@ -5759,20 +6021,20 @@ struct Parser { if ((*this).match_any(std::vector{TK_EQUAL})) { Expr value = (*this).assignment(); { - const auto& _match_109 = expr; - if (std::holds_alternative::Variable>(_match_109._data)) { - auto& _v = std::get::Variable>(_match_109._data); + const auto& _match_113 = expr; + if (std::holds_alternative::Variable>(_match_113._data)) { + auto& _v = std::get::Variable>(_match_113._data); auto& name = _v.name; return Expr::make_Assign(name, value); } - else if (std::holds_alternative::Get>(_match_109._data)) { - auto& _v = std::get::Get>(_match_109._data); + else if (std::holds_alternative::Get>(_match_113._data)) { + auto& _v = std::get::Get>(_match_113._data); auto& object = *_v.object; auto& name = _v.name; return Expr::make_Set(object, name, value); } - else if (std::holds_alternative::Index>(_match_109._data)) { - auto& _v = std::get::Index>(_match_109._data); + else if (std::holds_alternative::Index>(_match_113._data)) { + auto& _v = std::get::Index>(_match_113._data); auto& object = *_v.object; auto& bracket = _v.bracket; auto& index = *_v.index; @@ -5801,22 +6063,22 @@ struct Parser { } auto op_token = Token(base_type, base_lexeme, compound_op.line, compound_op.col); { - const auto& _match_110 = expr; - if (std::holds_alternative::Variable>(_match_110._data)) { - auto& _v = std::get::Variable>(_match_110._data); + const auto& _match_114 = expr; + if (std::holds_alternative::Variable>(_match_114._data)) { + auto& _v = std::get::Variable>(_match_114._data); auto& name = _v.name; Expr bin = Expr::make_Binary(Expr::make_Variable(name), op_token, rhs); return Expr::make_Assign(name, bin); } - else if (std::holds_alternative::Get>(_match_110._data)) { - auto& _v = std::get::Get>(_match_110._data); + else if (std::holds_alternative::Get>(_match_114._data)) { + auto& _v = std::get::Get>(_match_114._data); auto& object = *_v.object; auto& name = _v.name; Expr bin = Expr::make_Binary(Expr::make_Get(object, name), op_token, rhs); return Expr::make_Set(object, name, bin); } - else if (std::holds_alternative::Index>(_match_110._data)) { - auto& _v = std::get::Index>(_match_110._data); + else if (std::holds_alternative::Index>(_match_114._data)) { + auto& _v = std::get::Index>(_match_114._data); auto& object = *_v.object; auto& bracket = _v.bracket; auto& index = *_v.index; @@ -5912,9 +6174,9 @@ struct Parser { Expr call() { Expr expr = (*this).primary(); { - const auto& _match_111 = expr; - if (std::holds_alternative::Variable>(_match_111._data)) { - auto& _v = std::get::Variable>(_match_111._data); + const auto& _match_115 = expr; + if (std::holds_alternative::Variable>(_match_115._data)) { + auto& _v = std::get::Variable>(_match_115._data); auto& tok = _v.name; if ((*this).check(TK_LEFT_BRACKET)) { int64_t save_pos = this->current; @@ -5996,9 +6258,9 @@ struct Parser { Expr finish_call(const Expr& callee) { { - const auto& _match_112 = callee; - if (std::holds_alternative::Variable>(_match_112._data)) { - auto& _v = std::get::Variable>(_match_112._data); + const auto& _match_116 = callee; + if (std::holds_alternative::Variable>(_match_116._data)) { + auto& _v = std::get::Variable>(_match_116._data); auto& name = _v.name; if ((name.lexeme == std::string("cast"))) { Expr expr = (*this).expression(); @@ -6013,10 +6275,25 @@ struct Parser { } } std::vector args = {}; + std::vector arg_names = {}; (*this).skip_formatting(); if ((!(*this).check(TK_RIGHT_PAREN))) { (*this).match_any(std::vector{TK_REF, TK_REF_MUT}); - args.push_back((*this).expression()); + Expr arg_expr = (*this).expression(); + { + const auto& _match_117 = arg_expr; + if (std::holds_alternative::Assign>(_match_117._data)) { + auto& _v = std::get::Assign>(_match_117._data); + auto& aname = _v.name; + auto& avalue = *_v.value; + arg_names.push_back(aname.lexeme); + args.push_back(avalue); + } + else { + arg_names.push_back(std::string("")); + args.push_back(arg_expr); + } + } (*this).skip_formatting(); while ((*this).match_any(std::vector{TK_COMMA})) { (*this).skip_formatting(); @@ -6024,13 +6301,27 @@ struct Parser { break; } (*this).match_any(std::vector{TK_REF, TK_REF_MUT}); - args.push_back((*this).expression()); + arg_expr = (*this).expression(); + { + const auto& _match_118 = arg_expr; + if (std::holds_alternative::Assign>(_match_118._data)) { + auto& _v = std::get::Assign>(_match_118._data); + auto& aname = _v.name; + auto& avalue = *_v.value; + arg_names.push_back(aname.lexeme); + args.push_back(avalue); + } + else { + arg_names.push_back(std::string("")); + args.push_back(arg_expr); + } + } (*this).skip_formatting(); } } (*this).skip_formatting(); auto paren = (*this).consume(TK_RIGHT_PAREN, std::string("Expect ')' after arguments.")); - return Expr::make_Call(callee, paren, args); + return Expr::make_Call(callee, paren, args, arg_names); } Expr primary() { @@ -6131,13 +6422,19 @@ struct Parser { std::vector parse_param_list() { std::vector params = {}; + this->last_param_defaults = {}; (*this).skip_formatting(); if ((!(*this).check(TK_RIGHT_PAREN))) { bool p_mut = (*this).match_any(std::vector{TK_REF_MUT}); bool p_ref = p_mut || (*this).match_any(std::vector{TK_REF}); TypeNode param_type = (*this).parse_type(); auto param_name = (*this).consume(TK_IDENTIFIER, std::string("Expect parameter name.")); + Expr default_val = Expr::make_None(); + if ((*this).match_any(std::vector{TK_EQUAL})) { + default_val = (*this).expression(); + } params.push_back(Param(param_name, param_type, p_ref, p_mut)); + this->last_param_defaults.push_back(default_val); (*this).skip_formatting(); while ((*this).match_any(std::vector{TK_COMMA})) { (*this).skip_formatting(); @@ -6148,7 +6445,12 @@ struct Parser { p_ref = p_mut || (*this).match_any(std::vector{TK_REF}); param_type = (*this).parse_type(); param_name = (*this).consume(TK_IDENTIFIER, std::string("Expect parameter name.")); + default_val = Expr::make_None(); + if ((*this).match_any(std::vector{TK_EQUAL})) { + default_val = (*this).expression(); + } params.push_back(Param(param_name, param_type, p_ref, p_mut)); + this->last_param_defaults.push_back(default_val); (*this).skip_formatting(); } } @@ -6440,10 +6742,11 @@ struct Parser { } (*this).consume(TK_LEFT_PAREN, std::string("Expect '(' after function name.")); std::vector params = (*this).parse_param_list(); + std::vector defaults = this->last_param_defaults; (*this).consume(TK_RIGHT_PAREN, std::string("Expect ')' after parameters.")); (*this).consume(TK_COLON, std::string("Expect ':' before function body.")); std::vector body = (*this).block(); - return Stmt::make_Function(name, params, return_type, body, is_inline, comptime_mode, is_static, visibility, type_params); + return Stmt::make_Function(name, params, return_type, body, is_inline, comptime_mode, is_static, visibility, type_params, defaults); } Stmt class_declaration(std::string visibility) { @@ -6551,11 +6854,11 @@ struct Parser { std::vector old_fnames = {}; bool is_unit_type = false; { - const auto& _match_113 = vtype; - if (std::holds_alternative::NullType>(_match_113._data)) { + const auto& _match_119 = vtype; + if (std::holds_alternative::NullType>(_match_119._data)) { is_unit_type = true; } - else if (std::holds_alternative::Void>(_match_113._data)) { + else if (std::holds_alternative::Void>(_match_119._data)) { is_unit_type = true; } else { @@ -6719,11 +7022,12 @@ struct Parser { auto name_tok = (*this).advance(); (*this).consume(TK_LEFT_PAREN, ((std::string("Expect '(' after ") + (ctor_name)) + std::string("."))); std::vector params = (*this).parse_param_list(); + std::vector ctor_defaults = this->last_param_defaults; (*this).consume(TK_RIGHT_PAREN, std::string("Expect ')' after parameters.")); (*this).consume(TK_COLON, std::string("Expect ':' before body.")); std::vector body = (*this).block(); std::vector empty_tp = {}; - return Stmt::make_Function(name_tok, params, TypeNode::make_Void(), body, false, INT64_C(0), is_static, visibility, empty_tp); + return Stmt::make_Function(name_tok, params, TypeNode::make_Void(), body, false, INT64_C(0), is_static, visibility, empty_tp, ctor_defaults); } } if ((*this).match_any(std::vector{TK_REF_MUT})) { @@ -6800,7 +7104,8 @@ struct Parser { if ((*this).match_any(std::vector{TK_EQUAL})) { fn_cpp_name = (*this).consume(TK_STRING, std::string("Expect C++ name string.")).lexeme; } - functions.push_back(ExternFn(fn_name.lexeme, fn_cpp_name, ret_type, params)); + std::vector ext_defaults = this->last_param_defaults; + functions.push_back(ExternFn(fn_name.lexeme, fn_cpp_name, ret_type, params, ext_defaults)); (*this).match_any(std::vector{TK_NEWLINE}); } } diff --git a/tests/test_extend.lv b/tests/test_extend.lv index 2c73498..bb58129 100644 --- a/tests/test_extend.lv +++ b/tests/test_extend.lv @@ -34,3 +34,16 @@ void fn main(): // Test extend string: shout string greeting = "hello" lv_assert(greeting.shout() == "hello!", "shout") + + // Test extend chaining: double_all then sum + int chain_sum = nums.double_all().sum() + lv_assert(chain_sum == 30, "chain double_all().sum()") + + // Test extend chaining: square then double_all + auto chain_sq_dbl = nums.square().double_all() + lv_assert(chain_sq_dbl[0] == 2, "chain square().double_all() [0]") + lv_assert(chain_sq_dbl[2] == 18, "chain square().double_all() [2]") + + // Test extend chaining: three levels + int triple_chain = nums.double_all().square().sum() + lv_assert(triple_chain == 220, "chain 3 levels: 2+4+6+8+10 squared = 4+16+36+64+100") diff --git a/tests/test_named_args.lv b/tests/test_named_args.lv new file mode 100644 index 0000000..a6a9615 --- /dev/null +++ b/tests/test_named_args.lv @@ -0,0 +1,121 @@ +// Test named arguments and default parameter values + +// ── Default parameter values ────────────────────────────── + +int fn add(int a, int b = 10): + return a + b + +int fn greet_len(string name, string prefix = "Hello", string suffix = "!"): + string msg = prefix + " " + name + suffix + return msg.len() + +// ── Struct with default constructor args ────────────────── + +struct Point: + int x + int y + int z + + constructor(int x, int y = 0, int z = 0): + this.x = x + this.y = y + this.z = z + + string fn to_string(): + return "(${this.x}, ${this.y}, ${this.z})" + +// ── Named arguments with reordering ────────────────────── + +int fn compute(int a, int b, int c): + return a * 100 + b * 10 + c + +int fn mixed(int x, int y = 5, int z = 10): + return x + y + z + +// ── Tests ──────────────────────────────────────────────── + +void fn test_default_params(): + // All args provided + lv_assert(add(3, 7) == 10, "add(3, 7)") + // Using default + lv_assert(add(5) == 15, "add(5) uses default b=10") + println("PASS: default params") + +void fn test_multiple_defaults(): + // All provided + lv_assert(greet_len("World", "Hi", "?") > 0, "all args") + // One default + lv_assert(greet_len("World", "Hi") > 0, "one default") + // Two defaults + lv_assert(greet_len("World") > 0, "two defaults") + println("PASS: multiple defaults") + +void fn test_struct_defaults(): + // All args + Point p1 = Point(1, 2, 3) + lv_assert(p1.x == 1, "p1.x") + lv_assert(p1.y == 2, "p1.y") + lv_assert(p1.z == 3, "p1.z") + // One default + Point p2 = Point(10, 20) + lv_assert(p2.x == 10, "p2.x") + lv_assert(p2.y == 20, "p2.y") + lv_assert(p2.z == 0, "p2.z default") + // Two defaults + Point p3 = Point(99) + lv_assert(p3.x == 99, "p3.x") + lv_assert(p3.y == 0, "p3.y default") + lv_assert(p3.z == 0, "p3.z default") + println("PASS: struct defaults") + +void fn test_named_args(): + // Named args in order + lv_assert(compute(a = 1, b = 2, c = 3) == 123, "named in order") + // Named args out of order + lv_assert(compute(c = 3, b = 2, a = 1) == 123, "named reordered") + // Named args with different order + lv_assert(compute(b = 5, a = 2, c = 8) == 258, "named shuffled") + println("PASS: named args") + +void fn test_mixed_positional_named(): + // Positional + named + lv_assert(compute(1, c = 3, b = 2) == 123, "positional + named") + // First two positional, last named + lv_assert(compute(4, 5, c = 6) == 456, "two positional + named") + println("PASS: mixed positional + named") + +void fn test_named_with_defaults(): + // Using defaults with named args + lv_assert(mixed(1) == 16, "mixed(1) defaults") + lv_assert(mixed(1, z = 20) == 26, "mixed(1, z=20) skip y") + lv_assert(mixed(1, y = 100) == 111, "mixed(1, y=100) skip z") + lv_assert(mixed(1, z = 0, y = 0) == 1, "mixed named zeros") + println("PASS: named with defaults") + +void fn test_struct_named_args(): + // Named args for struct constructor + Point p = Point(x = 5, y = 10, z = 15) + lv_assert(p.x == 5, "named struct x") + lv_assert(p.y == 10, "named struct y") + lv_assert(p.z == 15, "named struct z") + // Named + defaults + Point p2 = Point(x = 42) + lv_assert(p2.x == 42, "named struct default y/z") + lv_assert(p2.y == 0, "named struct y default") + lv_assert(p2.z == 0, "named struct z default") + // Reordered named for struct + Point p3 = Point(z = 100, x = 1) + lv_assert(p3.x == 1, "reordered struct x") + lv_assert(p3.y == 0, "reordered struct y default") + lv_assert(p3.z == 100, "reordered struct z") + println("PASS: struct named args") + +void fn main(): + test_default_params() + test_multiple_defaults() + test_struct_defaults() + test_named_args() + test_mixed_positional_named() + test_named_with_defaults() + test_struct_named_args() + println("All named args tests passed!") diff --git a/tests/test_std_collections.lv b/tests/test_std_collections.lv index c6aa867..6167241 100644 --- a/tests/test_std_collections.lv +++ b/tests/test_std_collections.lv @@ -187,3 +187,13 @@ void fn main(): vector[int] step2 = step1.filter((int x) => x > 5) int chained = step2.reduce((int acc, int x) => acc + x, 0) lv_assert(chained == 50, "dot chained map->filter->reduce") + + // Direct chaining without intermediate variables + int direct_chain = nums.map((int x) => x * x).filter((int x) => x > 5).reduce((int acc, int x) => acc + x, 0) + lv_assert(direct_chain == 50, "direct chaining map->filter->reduce") + + // Two-level chaining + auto two_chain = nums.map((int x) => x + 10).take(3) + lv_assert(two_chain.len() == 3, "two chain length") + lv_assert(two_chain[0] == 11, "two chain [0]") + lv_assert(two_chain[2] == 13, "two chain [2]") diff --git a/tests/test_trailing_comma.lv b/tests/test_trailing_comma.lv new file mode 100644 index 0000000..195ce84 --- /dev/null +++ b/tests/test_trailing_comma.lv @@ -0,0 +1,44 @@ +// Test trailing commas in all comma-separated list contexts + +int fn add(int a, int b, int c): + return a + b + c + +void fn main(): + // Trailing comma in vector literal + vector[int] nums = [1, 2, 3,] + lv_assert(len(nums) == 3, "vector trailing comma") + + // Trailing comma in multiline vector + vector[int] nums2 = [ + 10, + 20, + 30, + ] + lv_assert(len(nums2) == 3, "multiline vector trailing comma") + lv_assert(nums2[2] == 30, "multiline vector last elem") + + // Trailing comma in function call + lv_assert(add(1, 2, 3,) == 6, "fn call trailing comma") + + // Trailing comma in multiline function call + int result = add( + 10, + 20, + 30, + ) + lv_assert(result == 60, "multiline fn call trailing comma") + + // Trailing comma in hashmap literal + hashmap[string, int] scores = {"alice": 100, "bob": 200,} + lv_assert(scores["alice"] == 100, "hashmap trailing comma") + lv_assert(scores.len() == 2, "hashmap trailing comma len") + + // Trailing comma in multiline hashmap + hashmap[string, int] scores2 = { + "x": 1, + "y": 2, + "z": 3, + } + lv_assert(scores2.len() == 3, "multiline hashmap trailing comma") + + println("All trailing comma tests passed!") From fa7d5c64d31596ce515ad9604174add0735ee262 Mon Sep 17 00:00:00 2001 From: Raumberg Date: Tue, 17 Feb 2026 17:32:30 +0300 Subject: [PATCH 16/19] examples: enchanced examples --- examples/complex/lvg/lvg.lv | 19 +- examples/complex/tree/tree.lv | 17 +- examples/complex/vless/test_vless.lv | 15 +- examples/complex/vless/vless.lv | 15 +- examples/complex/webserver/webserver.lv | 18 +- src/codegen.lv | 40 + stages/stage-latest.cpp | 1108 ++++++++++++----------- 7 files changed, 681 insertions(+), 551 deletions(-) diff --git a/examples/complex/lvg/lvg.lv b/examples/complex/lvg/lvg.lv index 8a165b5..04e6f55 100644 --- a/examples/complex/lvg/lvg.lv +++ b/examples/complex/lvg/lvg.lv @@ -1,6 +1,9 @@ // lvg — lavina grep (colorized) // Usage: lvg [path] [--ext .lv] [-i] [-c] +import std::os +import std::fs + int match_count = 0 int file_count = 0 @@ -76,7 +79,7 @@ string fn highlight(string line, string pattern): string last_file = "" void fn search_file(string path, string pattern): - auto lines = __fs_read_lines(path) + auto lines = fs::read_lines(path) int local_count = 0 int line_num = 1 @@ -100,7 +103,7 @@ void fn search_file(string path, string pattern): string colored_line = highlight(line, pattern) if flag_line_numbers: - print(" ${C_LINE}${__int_to_str(line_num)}${C_DIM}:${C_RESET} ${colored_line}") + print(" ${C_LINE}${to_string(line_num)}${C_DIM}:${C_RESET} ${colored_line}") else: print(" ${colored_line}") line_num += 1 @@ -108,16 +111,16 @@ void fn search_file(string path, string pattern): if local_count > 0: file_count += 1 if flag_count_only: - print("${C_FILE}${path}${C_DIM}:${C_RESET} ${C_BOLD}${__int_to_str(local_count)}${C_RESET} matches") + print("${C_FILE}${path}${C_DIM}:${C_RESET} ${C_BOLD}${to_string(local_count)}${C_RESET} matches") void fn search_dir(string path, string pattern): - auto entries = __fs_listdir(path) + auto entries = fs::list_dir(path) for ref name in entries: if name.substring(0, 1) == ".": continue string full = "${path}/${name}" - if __fs_is_dir(full): + if fs::is_dir(full): search_dir(full, pattern) else: if is_binary_ext(name): @@ -133,7 +136,7 @@ void fn search_dir(string path, string pattern): void fn main(): init_colors() - auto args = __os_args() + auto args = os::args() if args.len() < 2: print("Usage: lvg [path] [--ext .lv] [-i] [-c]") exit(1) @@ -156,10 +159,10 @@ void fn main(): path = args[i] i += 1 - if __fs_is_dir(path): + if fs::is_dir(path): search_dir(path, pattern) else: search_file(path, pattern) print("") - print("${C_SUMMARY}${__int_to_str(match_count)} matches${C_RESET} in ${C_SUMMARY}${__int_to_str(file_count)} files${C_RESET}") + print("${C_SUMMARY}${to_string(match_count)} matches${C_RESET} in ${C_SUMMARY}${to_string(file_count)} files${C_RESET}") diff --git a/examples/complex/tree/tree.lv b/examples/complex/tree/tree.lv index 75b2168..79f6860 100644 --- a/examples/complex/tree/tree.lv +++ b/examples/complex/tree/tree.lv @@ -1,11 +1,14 @@ // tree — recursive directory listing utility // Usage: tree [path] +import std::os +import std::fs + int dir_count = 0 int file_count = 0 -void fn print_tree(string path, string prefix): - auto entries = __fs_listdir(path) +void fn print_tree(string path, string prefix = ""): + auto entries = fs::list_dir(path) int i = 0 int total = entries.len() @@ -23,10 +26,10 @@ void fn print_tree(string path, string prefix): child_prefix = " " string full_path = "${path}/${name}" - if __fs_is_dir(full_path): + if fs::is_dir(full_path): print("${prefix}${connector}${name}/") dir_count += 1 - print_tree(full_path, "${prefix}${child_prefix}") + print_tree(full_path, prefix = "${prefix}${child_prefix}") else: print("${prefix}${connector}${name}") file_count += 1 @@ -35,11 +38,11 @@ void fn print_tree(string path, string prefix): void fn main(): string root = "." - auto args = __os_args() + auto args = os::args() if args.len() > 1: root = args[1] print(root) - print_tree(root, "") + print_tree(root) print("") - print("${__int_to_str(dir_count)} directories, ${__int_to_str(file_count)} files") + print("${dir_count as string} directories, ${file_count as string} files") diff --git a/examples/complex/vless/test_vless.lv b/examples/complex/vless/test_vless.lv index 81727e6..edf7a47 100644 --- a/examples/complex/vless/test_vless.lv +++ b/examples/complex/vless/test_vless.lv @@ -26,11 +26,16 @@ struct VlessRequest: int payload_offset bytes client_uuid - constructor(): - this.command = 0 - this.address = "" - this.port = 0 - this.payload_offset = 0 + constructor( + int command = 0, + string address = "", + int port = 0, + int payload_offset = 0, + ): + this.command = command + this.address = address + this.port = port + this.payload_offset = payload_offset this.client_uuid = bytes::create(0) // ── Parse / build ─────────────────────────────────────────── diff --git a/examples/complex/vless/vless.lv b/examples/complex/vless/vless.lv index 3445909..221c240 100644 --- a/examples/complex/vless/vless.lv +++ b/examples/complex/vless/vless.lv @@ -35,11 +35,16 @@ struct VlessRequest: int payload_offset bytes client_uuid - constructor(): - this.command = 0 - this.address = "" - this.port = 0 - this.payload_offset = 0 + constructor( + int command = 0, + string address = "", + int port = 0, + int payload_offset = 0, + ): + this.command = command + this.address = address + this.port = port + this.payload_offset = payload_offset this.client_uuid = bytes::create(0) // ── Parse VLESS request ───────────────────────────────────── diff --git a/examples/complex/webserver/webserver.lv b/examples/complex/webserver/webserver.lv index 68f5193..0f6afb7 100644 --- a/examples/complex/webserver/webserver.lv +++ b/examples/complex/webserver/webserver.lv @@ -1,3 +1,5 @@ +import std::fs + extern "httplib.h": type Server = "httplib::Server" type Request = "httplib::Request" @@ -10,10 +12,10 @@ struct Todo: string title bool done - constructor(int id, string title): + constructor(int id, string title, bool done = false): this.id = id this.title = title - this.done = false + this.done = done string fn to_string(): string done_str = "false" @@ -54,18 +56,12 @@ string fn parse_json_title(ref string body): return "" return rest.substring(0, end_quote) -int fn str_to_int(string s): - cpp { - return std::stoll(s); - } - return 0 - // ── Main ──────────────────────────────────────────────────── void fn main(): vector[Todo] todos = [] int next_id = 1 - string html = __fs_read("index.html") + string html = fs::read("index.html") Server svr @@ -94,7 +90,7 @@ void fn main(): // Toggle a todo svr.Put("/api/todos/:id/toggle", (ref Request req, ref! Response res): string id_str = req.path_params.at("id") - int id = str_to_int(id_str) + int id = id_str as int for i in 0..todos.len(): if todos[i].id == id: todos[i].done = not todos[i].done @@ -106,7 +102,7 @@ void fn main(): // Delete a todo svr.Delete("/api/todos/:id", (ref Request req, ref! Response res): string id_str = req.path_params.at("id") - int id = str_to_int(id_str) + int id = id_str as int for i in 0..todos.len(): if todos[i].id == id: todos.remove(i) diff --git a/src/codegen.lv b/src/codegen.lv index 94336e1..dc253b1 100644 --- a/src/codegen.lv +++ b/src/codegen.lv @@ -791,6 +791,46 @@ class CppCodegen: return target_type Unary(op, right): return this.infer_source_type(right) + Call(callee, paren, args, arg_names): + // Infer return type from known method calls + match callee: + Get(obj, method_tok): + TypeNode obj_type = this.infer_source_type(obj) + string method = method_tok.lexeme + // Methods that return element type (at, pop, etc.) + if method == "at": + match obj_type: + Array(inner): + return inner + Custom(name, targs): + if targs.len() > 0: + return targs[0] + return TypeNode::Auto() + _: + return TypeNode::Auto() + // String methods that return string + if method == "substring" or method == "trim" or method == "to_string" or method == "join": + return TypeNode::Str() + // len() returns int + if method == "len": + return TypeNode::Int() + return TypeNode::Auto() + _: + return TypeNode::Auto() + return TypeNode::Auto() + Index(obj, bracket, idx): + // vec[i] returns element type + TypeNode obj_type = this.infer_source_type(obj) + match obj_type: + Array(inner): + return inner + Custom(name, targs): + if targs.len() > 0: + return targs[0] + return TypeNode::Auto() + _: + return TypeNode::Auto() + return TypeNode::Auto() _: return TypeNode::Auto() diff --git a/stages/stage-latest.cpp b/stages/stage-latest.cpp index 33556d4..3bc65f2 100644 --- a/stages/stage-latest.cpp +++ b/stages/stage-latest.cpp @@ -2667,6 +2667,84 @@ struct CppCodegen { auto& right = *_v.right; return (*this).infer_source_type(right); } + else if (std::holds_alternative::Call>(_match_31._data)) { + auto& _v = std::get::Call>(_match_31._data); + auto& callee = *_v.callee; + auto& paren = _v.paren; + auto& args = _v.args; + auto& arg_names = _v.arg_names; + { + const auto& _match_32 = callee; + if (std::holds_alternative::Get>(_match_32._data)) { + auto& _v = std::get::Get>(_match_32._data); + auto& obj = *_v.object; + auto& method_tok = _v.name; + TypeNode obj_type = (*this).infer_source_type(obj); + std::string method = method_tok.lexeme; + if ((method == std::string("at"))) { + { + const auto& _match_33 = obj_type; + if (std::holds_alternative::Array>(_match_33._data)) { + auto& _v = std::get::Array>(_match_33._data); + auto& inner = *_v.inner; + return inner; + } + else if (std::holds_alternative::Custom>(_match_33._data)) { + auto& _v = std::get::Custom>(_match_33._data); + auto& name = _v.name; + auto& targs = _v.type_args; + if ((static_cast(targs.size()) > INT64_C(0))) { + return targs[INT64_C(0)]; + } + return TypeNode::make_Auto(); + } + else { + return TypeNode::make_Auto(); + } + } + } + if ((method == std::string("substring")) || (method == std::string("trim")) || (method == std::string("to_string")) || (method == std::string("join"))) { + return TypeNode::make_Str(); + } + if ((method == std::string("len"))) { + return TypeNode::make_Int(); + } + return TypeNode::make_Auto(); + } + else { + return TypeNode::make_Auto(); + } + } + return TypeNode::make_Auto(); + } + else if (std::holds_alternative::Index>(_match_31._data)) { + auto& _v = std::get::Index>(_match_31._data); + auto& obj = *_v.object; + auto& bracket = _v.bracket; + auto& idx = *_v.index; + TypeNode obj_type = (*this).infer_source_type(obj); + { + const auto& _match_34 = obj_type; + if (std::holds_alternative::Array>(_match_34._data)) { + auto& _v = std::get::Array>(_match_34._data); + auto& inner = *_v.inner; + return inner; + } + else if (std::holds_alternative::Custom>(_match_34._data)) { + auto& _v = std::get::Custom>(_match_34._data); + auto& name = _v.name; + auto& targs = _v.type_args; + if ((static_cast(targs.size()) > INT64_C(0))) { + return targs[INT64_C(0)]; + } + return TypeNode::make_Auto(); + } + else { + return TypeNode::make_Auto(); + } + } + return TypeNode::make_Auto(); + } else { return TypeNode::make_Auto(); } @@ -2675,20 +2753,20 @@ struct CppCodegen { bool is_integer_type(const TypeNode& t) { { - const auto& _match_32 = t; - if (std::holds_alternative::Int>(_match_32._data)) { + const auto& _match_35 = t; + if (std::holds_alternative::Int>(_match_35._data)) { return true; } - else if (std::holds_alternative::Int8>(_match_32._data)) { + else if (std::holds_alternative::Int8>(_match_35._data)) { return true; } - else if (std::holds_alternative::Int16>(_match_32._data)) { + else if (std::holds_alternative::Int16>(_match_35._data)) { return true; } - else if (std::holds_alternative::Int32>(_match_32._data)) { + else if (std::holds_alternative::Int32>(_match_35._data)) { return true; } - else if (std::holds_alternative::USize>(_match_32._data)) { + else if (std::holds_alternative::USize>(_match_35._data)) { return true; } else { @@ -2699,11 +2777,11 @@ struct CppCodegen { bool is_float_type(const TypeNode& t) { { - const auto& _match_33 = t; - if (std::holds_alternative::Float>(_match_33._data)) { + const auto& _match_36 = t; + if (std::holds_alternative::Float>(_match_36._data)) { return true; } - else if (std::holds_alternative::Float32>(_match_33._data)) { + else if (std::holds_alternative::Float32>(_match_36._data)) { return true; } else { @@ -2714,8 +2792,8 @@ struct CppCodegen { bool is_string_type(const TypeNode& t) { { - const auto& _match_34 = t; - if (std::holds_alternative::Str>(_match_34._data)) { + const auto& _match_37 = t; + if (std::holds_alternative::Str>(_match_37._data)) { return true; } else { @@ -2726,8 +2804,8 @@ struct CppCodegen { bool is_bytes_type(const TypeNode& t) { { - const auto& _match_35 = t; - if (std::holds_alternative::Bytes>(_match_35._data)) { + const auto& _match_38 = t; + if (std::holds_alternative::Bytes>(_match_38._data)) { return true; } else { @@ -2742,9 +2820,9 @@ struct CppCodegen { std::vector methods = this->extend_methods[type_cat]; for (const auto& ext_m : methods) { { - const auto& _match_36 = ext_m; - if (std::holds_alternative::Function>(_match_36._data)) { - auto& _v = std::get::Function>(_match_36._data); + const auto& _match_39 = ext_m; + if (std::holds_alternative::Function>(_match_39._data)) { + auto& _v = std::get::Function>(_match_39._data); auto& mname = _v.name; auto& mparams = _v.params; auto& mret = _v.return_type; @@ -2773,9 +2851,9 @@ struct CppCodegen { void emit_extend_method(const std::string& type_key, const Stmt& method) { { - const auto& _match_37 = method; - if (std::holds_alternative::Function>(_match_37._data)) { - auto& _v = std::get::Function>(_match_37._data); + const auto& _match_40 = method; + if (std::holds_alternative::Function>(_match_40._data)) { + auto& _v = std::get::Function>(_match_40._data); auto& mname = _v.name; auto& mparams = _v.params; auto& mret = _v.return_type; @@ -2828,8 +2906,8 @@ struct CppCodegen { this->var_types[p.name.lexeme] = p.param_type; if (track_dynamic) { { - const auto& _match_38 = p.param_type; - if (std::holds_alternative::Dynamic>(_match_38._data)) { + const auto& _match_41 = p.param_type; + if (std::holds_alternative::Dynamic>(_match_41._data)) { (*this).add_dynamic_var(p.name.lexeme); } else { @@ -2864,17 +2942,17 @@ struct CppCodegen { void emit_stmt(const Stmt& s, bool m) { { - const auto& _match_39 = s; - if (_match_39._tag == "None") { + const auto& _match_42 = s; + if (_match_42._tag == "None") { /* pass */ } - else if (std::holds_alternative::ExprStmt>(_match_39._data)) { - auto& _v = std::get::ExprStmt>(_match_39._data); + else if (std::holds_alternative::ExprStmt>(_match_42._data)) { + auto& _v = std::get::ExprStmt>(_match_42._data); auto& expr = _v.expr; this->output = (this->output + ((((std::string("") + ((*this).indent())) + std::string("")) + ((*this).emit_expr(expr, m))) + std::string(";\n"))); } - else if (std::holds_alternative::Let>(_match_39._data)) { - auto& _v = std::get::Let>(_match_39._data); + else if (std::holds_alternative::Let>(_match_42._data)) { + auto& _v = std::get::Let>(_match_42._data); auto& name = _v.name; auto& var_type = _v.var_type; auto& initializer = _v.initializer; @@ -2883,8 +2961,8 @@ struct CppCodegen { auto& is_mut = _v.is_mut; (*this).emit_let(name, var_type, initializer, m, is_ref, is_mut); } - else if (std::holds_alternative::Const>(_match_39._data)) { - auto& _v = std::get::Const>(_match_39._data); + else if (std::holds_alternative::Const>(_match_42._data)) { + auto& _v = std::get::Const>(_match_42._data); auto& name = _v.name; auto& const_type = _v.const_type; auto& value = _v.value; @@ -2898,13 +2976,13 @@ struct CppCodegen { } this->output = (this->output + ((((((((((std::string("") + ((*this).indent())) + std::string("")) + (prefix)) + std::string("")) + (cpp_type)) + std::string(" ")) + (name.lexeme)) + std::string(" = ")) + (val)) + std::string(";\n"))); } - else if (std::holds_alternative::Return>(_match_39._data)) { - auto& _v = std::get::Return>(_match_39._data); + else if (std::holds_alternative::Return>(_match_42._data)) { + auto& _v = std::get::Return>(_match_42._data); auto& keyword = _v.keyword; auto& value = _v.value; { - const auto& _match_40 = value; - if (_match_40._tag == "None") { + const auto& _match_43 = value; + if (_match_43._tag == "None") { this->output = (this->output + ((std::string("") + ((*this).indent())) + std::string("return;\n"))); } else { @@ -2912,16 +2990,16 @@ struct CppCodegen { } } } - else if (std::holds_alternative::If>(_match_39._data)) { - auto& _v = std::get::If>(_match_39._data); + else if (std::holds_alternative::If>(_match_42._data)) { + auto& _v = std::get::If>(_match_42._data); auto& condition = _v.condition; auto& then_branch = *_v.then_branch; auto& else_branch = *_v.else_branch; this->output = (this->output + ((((std::string("") + ((*this).indent())) + std::string("if (")) + ((*this).emit_expr(condition, m))) + std::string(") "))); (*this).emit_block_or_stmt(then_branch, m); { - const auto& _match_41 = else_branch; - if (_match_41._tag == "None") { + const auto& _match_44 = else_branch; + if (_match_44._tag == "None") { /* pass */ } else { @@ -2930,24 +3008,24 @@ struct CppCodegen { } } } - else if (std::holds_alternative::While>(_match_39._data)) { - auto& _v = std::get::While>(_match_39._data); + else if (std::holds_alternative::While>(_match_42._data)) { + auto& _v = std::get::While>(_match_42._data); auto& condition = _v.condition; auto& body = *_v.body; this->output = (this->output + ((((std::string("") + ((*this).indent())) + std::string("while (")) + ((*this).emit_expr(condition, m))) + std::string(") "))); (*this).emit_block_or_stmt(body, m); } - else if (std::holds_alternative::For>(_match_39._data)) { - auto& _v = std::get::For>(_match_39._data); + else if (std::holds_alternative::For>(_match_42._data)) { + auto& _v = std::get::For>(_match_42._data); auto& item_name = _v.item_name; auto& collection = _v.collection; auto& body = *_v.body; auto& is_ref = _v.is_ref; auto& is_mut = _v.is_mut; { - const auto& _match_42 = collection; - if (std::holds_alternative::Range>(_match_42._data)) { - auto& _v = std::get::Range>(_match_42._data); + const auto& _match_45 = collection; + if (std::holds_alternative::Range>(_match_45._data)) { + auto& _v = std::get::Range>(_match_45._data); auto& start = *_v.start; auto& end = *_v.end; this->output = (this->output + ((((((((((((std::string("") + ((*this).indent())) + std::string("for (int64_t ")) + (item_name.lexeme)) + std::string(" = ")) + ((*this).emit_expr(start, m))) + std::string("; ")) + (item_name.lexeme)) + std::string(" < ")) + ((*this).emit_expr(end, m))) + std::string("; ")) + (item_name.lexeme)) + std::string("++) "))); @@ -2955,9 +3033,9 @@ struct CppCodegen { } else { { - const auto& _match_43 = collection; - if (std::holds_alternative::Variable>(_match_43._data)) { - auto& _v = std::get::Variable>(_match_43._data); + const auto& _match_46 = collection; + if (std::holds_alternative::Variable>(_match_46._data)) { + auto& _v = std::get::Variable>(_match_46._data); auto& tok = _v.name; if ((*this).is_dynamic_var(tok.lexeme)) { (*this).add_dynamic_var(item_name.lexeme); @@ -2981,8 +3059,8 @@ struct CppCodegen { } } } - else if (std::holds_alternative::Block>(_match_39._data)) { - auto& _v = std::get::Block>(_match_39._data); + else if (std::holds_alternative::Block>(_match_42._data)) { + auto& _v = std::get::Block>(_match_42._data); auto& statements = _v.statements; this->output = (this->output + ((std::string("") + ((*this).indent())) + std::string("{\n"))); this->indent_level = (this->indent_level + INT64_C(1)); @@ -2992,8 +3070,8 @@ struct CppCodegen { this->indent_level = (this->indent_level - INT64_C(1)); this->output = (this->output + ((std::string("") + ((*this).indent())) + std::string("}\n"))); } - else if (std::holds_alternative::Try>(_match_39._data)) { - auto& _v = std::get::Try>(_match_39._data); + else if (std::holds_alternative::Try>(_match_42._data)) { + auto& _v = std::get::Try>(_match_42._data); auto& try_body = *_v.try_body; auto& catch_body = *_v.catch_body; auto& exception_name = _v.exception_name; @@ -3006,8 +3084,8 @@ struct CppCodegen { this->output = (this->output + ((((std::string("") + ((*this).indent())) + std::string(" catch (const std::exception& ")) + (exc_name)) + std::string(") "))); (*this).emit_block_or_stmt(catch_body, m); } - else if (std::holds_alternative::Function>(_match_39._data)) { - auto& _v = std::get::Function>(_match_39._data); + else if (std::holds_alternative::Function>(_match_42._data)) { + auto& _v = std::get::Function>(_match_42._data); auto& name = _v.name; auto& params = _v.params; auto& return_type = _v.return_type; @@ -3020,24 +3098,24 @@ struct CppCodegen { auto& param_defaults = _v.param_defaults; (*this).emit_function(name, params, return_type, body, type_params, comptime_mode, param_defaults); } - else if (std::holds_alternative::Class>(_match_39._data)) { - auto& _v = std::get::Class>(_match_39._data); + else if (std::holds_alternative::Class>(_match_42._data)) { + auto& _v = std::get::Class>(_match_42._data); auto& name = _v.name; auto& body = _v.body; auto& visibility = _v.visibility; std::vector empty_tp = {}; (*this).emit_class(name, body, empty_tp); } - else if (std::holds_alternative::Struct>(_match_39._data)) { - auto& _v = std::get::Struct>(_match_39._data); + else if (std::holds_alternative::Struct>(_match_42._data)) { + auto& _v = std::get::Struct>(_match_42._data); auto& name = _v.name; auto& body = _v.body; auto& visibility = _v.visibility; auto& type_params = _v.type_params; (*this).emit_class(name, body, type_params); } - else if (std::holds_alternative::Enum>(_match_39._data)) { - auto& _v = std::get::Enum>(_match_39._data); + else if (std::holds_alternative::Enum>(_match_42._data)) { + auto& _v = std::get::Enum>(_match_42._data); auto& name = _v.name; auto& variants = _v.variants; auto& methods = _v.methods; @@ -3045,43 +3123,43 @@ struct CppCodegen { auto& type_params = _v.type_params; (*this).emit_enum(name, variants, methods, type_params); } - else if (std::holds_alternative::Match>(_match_39._data)) { - auto& _v = std::get::Match>(_match_39._data); + else if (std::holds_alternative::Match>(_match_42._data)) { + auto& _v = std::get::Match>(_match_42._data); auto& expr = _v.expr; auto& arm_patterns = _v.arm_patterns; auto& arm_bodies = _v.arm_bodies; (*this).emit_match_impl(expr, arm_patterns, arm_bodies, m); } - else if (std::holds_alternative::Namespace>(_match_39._data)) { - auto& _v = std::get::Namespace>(_match_39._data); + else if (std::holds_alternative::Namespace>(_match_42._data)) { + auto& _v = std::get::Namespace>(_match_42._data); auto& name = _v.name; auto& body = _v.body; auto& visibility = _v.visibility; /* pass */ } - else if (std::holds_alternative::Import>(_match_39._data)) { - auto& _v = std::get::Import>(_match_39._data); + else if (std::holds_alternative::Import>(_match_42._data)) { + auto& _v = std::get::Import>(_match_42._data); auto& path = _v.path; auto& alias = _v.alias; /* pass */ } - else if (std::holds_alternative::Break>(_match_39._data)) { - auto& _v = std::get::Break>(_match_39._data); + else if (std::holds_alternative::Break>(_match_42._data)) { + auto& _v = std::get::Break>(_match_42._data); auto& keyword = _v.keyword; this->output = (this->output + ((std::string("") + ((*this).indent())) + std::string("break;\n"))); } - else if (std::holds_alternative::Continue>(_match_39._data)) { - auto& _v = std::get::Continue>(_match_39._data); + else if (std::holds_alternative::Continue>(_match_42._data)) { + auto& _v = std::get::Continue>(_match_42._data); auto& keyword = _v.keyword; this->output = (this->output + ((std::string("") + ((*this).indent())) + std::string("continue;\n"))); } - else if (std::holds_alternative::Pass>(_match_39._data)) { - auto& _v = std::get::Pass>(_match_39._data); + else if (std::holds_alternative::Pass>(_match_42._data)) { + auto& _v = std::get::Pass>(_match_42._data); auto& keyword = _v.keyword; this->output = (this->output + ((std::string("") + ((*this).indent())) + std::string("/* pass */\n"))); } - else if (std::holds_alternative::CppBlock>(_match_39._data)) { - auto& _v = std::get::CppBlock>(_match_39._data); + else if (std::holds_alternative::CppBlock>(_match_42._data)) { + auto& _v = std::get::CppBlock>(_match_42._data); auto& code = _v.code; auto lines = lv_split(code, std::string("\n")); for (const auto& line : lines) { @@ -3091,8 +3169,8 @@ struct CppCodegen { } } } - else if (std::holds_alternative::Extern>(_match_39._data)) { - auto& _v = std::get::Extern>(_match_39._data); + else if (std::holds_alternative::Extern>(_match_42._data)) { + auto& _v = std::get::Extern>(_match_42._data); auto& header = _v.header; auto& import_path = _v.import_path; auto& link_lib = _v.link_lib; @@ -3125,8 +3203,8 @@ struct CppCodegen { this->extern_fn_params[ef.name] = ef.params; } } - else if (std::holds_alternative::Extend>(_match_39._data)) { - auto& _v = std::get::Extend>(_match_39._data); + else if (std::holds_alternative::Extend>(_match_42._data)) { + auto& _v = std::get::Extend>(_match_42._data); auto& target = _v.target_type; auto& methods = _v.methods; auto& visibility = _v.visibility; @@ -3154,17 +3232,17 @@ struct CppCodegen { } std::string init_str = std::string(""); { - const auto& _match_44 = initializer; - if (_match_44._tag == "None") { + const auto& _match_47 = initializer; + if (_match_47._tag == "None") { init_str = (*this).default_init(cpp_type); } else { std::string val = (*this).emit_expr(initializer, in_method); bool is_ptr = false; { - const auto& _match_45 = var_type; - if (std::holds_alternative::Ptr>(_match_45._data)) { - auto& _v = std::get::Ptr>(_match_45._data); + const auto& _match_48 = var_type; + if (std::holds_alternative::Ptr>(_match_48._data)) { + auto& _v = std::get::Ptr>(_match_48._data); auto& inner = *_v.inner; is_ptr = true; } @@ -3183,9 +3261,9 @@ struct CppCodegen { void emit_block_or_stmt(const Stmt& s, bool m) { { - const auto& _match_46 = s; - if (std::holds_alternative::Block>(_match_46._data)) { - auto& _v = std::get::Block>(_match_46._data); + const auto& _match_49 = s; + if (std::holds_alternative::Block>(_match_49._data)) { + auto& _v = std::get::Block>(_match_49._data); auto& stmts = _v.statements; this->output = (this->output + std::string("{\n")); this->indent_level = (this->indent_level + INT64_C(1)); @@ -3257,27 +3335,27 @@ struct CppCodegen { std::vector remaining_body = {}; for (const auto& st : init_body) { { - const auto& _match_47 = st; - if (std::holds_alternative::ExprStmt>(_match_47._data)) { - auto& _v = std::get::ExprStmt>(_match_47._data); + const auto& _match_50 = st; + if (std::holds_alternative::ExprStmt>(_match_50._data)) { + auto& _v = std::get::ExprStmt>(_match_50._data); auto& expr = _v.expr; bool handled = false; { - const auto& _match_48 = expr; - if (std::holds_alternative::Set>(_match_48._data)) { - auto& _v = std::get::Set>(_match_48._data); + const auto& _match_51 = expr; + if (std::holds_alternative::Set>(_match_51._data)) { + auto& _v = std::get::Set>(_match_51._data); auto& object = *_v.object; auto& prop = _v.name; auto& value = *_v.value; { - const auto& _match_49 = object; - if (std::holds_alternative::This>(_match_49._data)) { - auto& _v = std::get::This>(_match_49._data); + const auto& _match_52 = object; + if (std::holds_alternative::This>(_match_52._data)) { + auto& _v = std::get::This>(_match_52._data); auto& kw = _v.keyword; { - const auto& _match_50 = value; - if (std::holds_alternative::Variable>(_match_50._data)) { - auto& _v = std::get::Variable>(_match_50._data); + const auto& _match_53 = value; + if (std::holds_alternative::Variable>(_match_53._data)) { + auto& _v = std::get::Variable>(_match_53._data); auto& tok = _v.name; init_list.push_back(((((std::string("") + (prop.lexeme)) + std::string("(")) + (tok.lexeme)) + std::string(")"))); handled = true; @@ -3324,9 +3402,9 @@ struct CppCodegen { void emit_class_method(const Token& class_name, const Stmt& method) { { - const auto& _match_51 = method; - if (std::holds_alternative::Function>(_match_51._data)) { - auto& _v = std::get::Function>(_match_51._data); + const auto& _match_54 = method; + if (std::holds_alternative::Function>(_match_54._data)) { + auto& _v = std::get::Function>(_match_54._data); auto& mname = _v.name; auto& mparams = _v.params; auto& mret = _v.return_type; @@ -3384,9 +3462,9 @@ struct CppCodegen { std::vector let_field_types = {}; for (const auto& st : body) { { - const auto& _match_52 = st; - if (std::holds_alternative::Function>(_match_52._data)) { - auto& _v = std::get::Function>(_match_52._data); + const auto& _match_55 = st; + if (std::holds_alternative::Function>(_match_55._data)) { + auto& _v = std::get::Function>(_match_55._data); auto& fname = _v.name; auto& params = _v.params; auto& return_type = _v.return_type; @@ -3407,8 +3485,8 @@ struct CppCodegen { methods.push_back(st); } } - else if (std::holds_alternative::Let>(_match_52._data)) { - auto& _v = std::get::Let>(_match_52._data); + else if (std::holds_alternative::Let>(_match_55._data)) { + auto& _v = std::get::Let>(_match_55._data); auto& fname = _v.name; auto& var_type = _v.var_type; auto& init = _v.initializer; @@ -3435,21 +3513,21 @@ struct CppCodegen { std::vector seen = {}; for (const auto& st : init_body) { { - const auto& _match_53 = st; - if (std::holds_alternative::ExprStmt>(_match_53._data)) { - auto& _v = std::get::ExprStmt>(_match_53._data); + const auto& _match_56 = st; + if (std::holds_alternative::ExprStmt>(_match_56._data)) { + auto& _v = std::get::ExprStmt>(_match_56._data); auto& expr = _v.expr; { - const auto& _match_54 = expr; - if (std::holds_alternative::Set>(_match_54._data)) { - auto& _v = std::get::Set>(_match_54._data); + const auto& _match_57 = expr; + if (std::holds_alternative::Set>(_match_57._data)) { + auto& _v = std::get::Set>(_match_57._data); auto& object = *_v.object; auto& prop = _v.name; auto& value = *_v.value; { - const auto& _match_55 = object; - if (std::holds_alternative::This>(_match_55._data)) { - auto& _v = std::get::This>(_match_55._data); + const auto& _match_58 = object; + if (std::holds_alternative::This>(_match_58._data)) { + auto& _v = std::get::This>(_match_58._data); auto& kw = _v.keyword; bool already = false; for (const auto& s : seen) { @@ -3496,9 +3574,9 @@ struct CppCodegen { bool has_to_string = false; for (const auto& m : methods) { { - const auto& _match_56 = m; - if (std::holds_alternative::Function>(_match_56._data)) { - auto& _v = std::get::Function>(_match_56._data); + const auto& _match_59 = m; + if (std::holds_alternative::Function>(_match_59._data)) { + auto& _v = std::get::Function>(_match_59._data); auto& mname = _v.name; auto& mparams = _v.params; auto& mret = _v.return_type; @@ -3533,9 +3611,9 @@ struct CppCodegen { std::string infer_expr_type(const Expr& e, const std::vector& param_names, const std::vector& param_types) { { - const auto& _match_57 = e; - if (std::holds_alternative::Literal>(_match_57._data)) { - auto& _v = std::get::Literal>(_match_57._data); + const auto& _match_60 = e; + if (std::holds_alternative::Literal>(_match_60._data)) { + auto& _v = std::get::Literal>(_match_60._data); auto& kind = _v.kind; auto& value = _v.value; if ((kind == std::string("int"))) { @@ -3563,8 +3641,8 @@ struct CppCodegen { } return std::string("std::any"); } - else if (std::holds_alternative::Variable>(_match_57._data)) { - auto& _v = std::get::Variable>(_match_57._data); + else if (std::holds_alternative::Variable>(_match_60._data)) { + auto& _v = std::get::Variable>(_match_60._data); auto& tok = _v.name; for (int64_t i = INT64_C(0); i < static_cast(param_names.size()); i++) { if ((param_names[i] == tok.lexeme)) { @@ -3576,8 +3654,8 @@ struct CppCodegen { } return std::string("std::any"); } - else if (std::holds_alternative::Binary>(_match_57._data)) { - auto& _v = std::get::Binary>(_match_57._data); + else if (std::holds_alternative::Binary>(_match_60._data)) { + auto& _v = std::get::Binary>(_match_60._data); auto& left = *_v.left; auto& op = _v.op; auto& right = *_v.right; @@ -3591,27 +3669,27 @@ struct CppCodegen { } return std::string("std::any"); } - else if (std::holds_alternative::Unary>(_match_57._data)) { - auto& _v = std::get::Unary>(_match_57._data); + else if (std::holds_alternative::Unary>(_match_60._data)) { + auto& _v = std::get::Unary>(_match_60._data); auto& op = _v.op; auto& right = *_v.right; return (*this).infer_expr_type(right, param_names, param_types); } - else if (std::holds_alternative::Grouping>(_match_57._data)) { - auto& _v = std::get::Grouping>(_match_57._data); + else if (std::holds_alternative::Grouping>(_match_60._data)) { + auto& _v = std::get::Grouping>(_match_60._data); auto& inner = *_v.inner; return (*this).infer_expr_type(inner, param_names, param_types); } - else if (std::holds_alternative::Call>(_match_57._data)) { - auto& _v = std::get::Call>(_match_57._data); + else if (std::holds_alternative::Call>(_match_60._data)) { + auto& _v = std::get::Call>(_match_60._data); auto& callee = *_v.callee; auto& paren = _v.paren; auto& args = _v.args; auto& arg_names = _v.arg_names; { - const auto& _match_58 = callee; - if (std::holds_alternative::Variable>(_match_58._data)) { - auto& _v = std::get::Variable>(_match_58._data); + const auto& _match_61 = callee; + if (std::holds_alternative::Variable>(_match_61._data)) { + auto& _v = std::get::Variable>(_match_61._data); auto& tok = _v.name; if ((*this).is_known_enum(tok.lexeme)) { return tok.lexeme; @@ -3623,14 +3701,14 @@ struct CppCodegen { } return tok.lexeme; } - else if (std::holds_alternative::StaticGet>(_match_58._data)) { - auto& _v = std::get::StaticGet>(_match_58._data); + else if (std::holds_alternative::StaticGet>(_match_61._data)) { + auto& _v = std::get::StaticGet>(_match_61._data); auto& object = *_v.object; auto& name = _v.name; { - const auto& _match_59 = object; - if (std::holds_alternative::Variable>(_match_59._data)) { - auto& _v = std::get::Variable>(_match_59._data); + const auto& _match_62 = object; + if (std::holds_alternative::Variable>(_match_62._data)) { + auto& _v = std::get::Variable>(_match_62._data); auto& tok = _v.name; if ((*this).is_known_enum(tok.lexeme)) { return tok.lexeme; @@ -3647,8 +3725,8 @@ struct CppCodegen { } } } - else if (std::holds_alternative::Vector>(_match_57._data)) { - auto& _v = std::get::Vector>(_match_57._data); + else if (std::holds_alternative::Vector>(_match_60._data)) { + auto& _v = std::get::Vector>(_match_60._data); auto& elements = _v.elements; if ((static_cast(elements.size()) > INT64_C(0))) { std::string inner = (*this).infer_expr_type(elements[INT64_C(0)], param_names, param_types); @@ -3658,8 +3736,8 @@ struct CppCodegen { } return std::string("std::vector"); } - else if (std::holds_alternative::Map>(_match_57._data)) { - auto& _v = std::get::Map>(_match_57._data); + else if (std::holds_alternative::Map>(_match_60._data)) { + auto& _v = std::get::Map>(_match_60._data); auto& keys = _v.keys; auto& values = _v.values; std::string kt = std::string("std::any"); @@ -3670,8 +3748,8 @@ struct CppCodegen { } return ((((std::string("std::unordered_map<") + (kt)) + std::string(", ")) + (vt)) + std::string(">")); } - else if (std::holds_alternative::Cast>(_match_57._data)) { - auto& _v = std::get::Cast>(_match_57._data); + else if (std::holds_alternative::Cast>(_match_60._data)) { + auto& _v = std::get::Cast>(_match_60._data); auto& expr = *_v.expr; auto& target_type = _v.target_type; return (*this).emit_type(target_type); @@ -3690,9 +3768,9 @@ struct CppCodegen { std::string cpp_type = (*this).emit_type(v.types[fi]); std::string fname = v.field_names[fi]; { - const auto& _match_60 = v.types[fi]; - if (std::holds_alternative::Custom>(_match_60._data)) { - auto& _v = std::get::Custom>(_match_60._data); + const auto& _match_63 = v.types[fi]; + if (std::holds_alternative::Custom>(_match_63._data)) { + auto& _v = std::get::Custom>(_match_63._data); auto& n = _v.name; auto& _ = _v.type_args; if ((n == enum_name)) { @@ -3735,9 +3813,9 @@ struct CppCodegen { std::string fname = v.field_names[fi]; bool is_self_ref = false; { - const auto& _match_61 = v.types[fi]; - if (std::holds_alternative::Custom>(_match_61._data)) { - auto& _v = std::get::Custom>(_match_61._data); + const auto& _match_64 = v.types[fi]; + if (std::holds_alternative::Custom>(_match_64._data)) { + auto& _v = std::get::Custom>(_match_64._data); auto& n = _v.name; auto& _ = _v.type_args; if ((n == enum_name)) { @@ -3791,9 +3869,9 @@ struct CppCodegen { for (const auto& v : variants) { for (const auto& ft : v.types) { { - const auto& _match_62 = ft; - if (std::holds_alternative::Custom>(_match_62._data)) { - auto& _v = std::get::Custom>(_match_62._data); + const auto& _match_65 = ft; + if (std::holds_alternative::Custom>(_match_65._data)) { + auto& _v = std::get::Custom>(_match_65._data); auto& n = _v.name; auto& _ = _v.type_args; if ((n == enum_name)) { @@ -3824,9 +3902,9 @@ struct CppCodegen { bool has_to_string = false; for (const auto& m : methods) { { - const auto& _match_63 = m; - if (std::holds_alternative::Function>(_match_63._data)) { - auto& _v = std::get::Function>(_match_63._data); + const auto& _match_66 = m; + if (std::holds_alternative::Function>(_match_66._data)) { + auto& _v = std::get::Function>(_match_66._data); auto& mname = _v.name; auto& mparams = _v.params; auto& mret = _v.return_type; @@ -3896,9 +3974,9 @@ struct CppCodegen { bool is_self_ref = false; if ((bi < static_cast(vinfo.types.size()))) { { - const auto& _match_64 = vinfo.types[bi]; - if (std::holds_alternative::Custom>(_match_64._data)) { - auto& _v = std::get::Custom>(_match_64._data); + const auto& _match_67 = vinfo.types[bi]; + if (std::holds_alternative::Custom>(_match_67._data)) { + auto& _v = std::get::Custom>(_match_67._data); auto& n = _v.name; auto& _ = _v.type_args; if ((n == ename)) { @@ -3931,9 +4009,9 @@ struct CppCodegen { void emit_arm_body(const Stmt& arm_body, bool in_method) { { - const auto& _match_65 = arm_body; - if (std::holds_alternative::Block>(_match_65._data)) { - auto& _v = std::get::Block>(_match_65._data); + const auto& _match_68 = arm_body; + if (std::holds_alternative::Block>(_match_68._data)) { + auto& _v = std::get::Block>(_match_68._data); auto& stmts = _v.statements; for (const auto& st : stmts) { (*this).emit_stmt(st, in_method); @@ -3947,9 +4025,9 @@ struct CppCodegen { void emit_using_if_public(const std::string& ns, const Stmt& stmt) { { - const auto& _match_66 = stmt; - if (std::holds_alternative::Function>(_match_66._data)) { - auto& _v = std::get::Function>(_match_66._data); + const auto& _match_69 = stmt; + if (std::holds_alternative::Function>(_match_69._data)) { + auto& _v = std::get::Function>(_match_69._data); auto& name = _v.name; auto& params = _v.params; auto& return_type = _v.return_type; @@ -3964,8 +4042,8 @@ struct CppCodegen { this->output = (this->output + ((((std::string("using ") + (ns)) + std::string("::")) + (name.lexeme)) + std::string(";\n"))); } } - else if (std::holds_alternative::Class>(_match_66._data)) { - auto& _v = std::get::Class>(_match_66._data); + else if (std::holds_alternative::Class>(_match_69._data)) { + auto& _v = std::get::Class>(_match_69._data); auto& name = _v.name; auto& body = _v.body; auto& visibility = _v.visibility; @@ -3973,8 +4051,8 @@ struct CppCodegen { this->output = (this->output + ((((std::string("using ") + (ns)) + std::string("::")) + (name.lexeme)) + std::string(";\n"))); } } - else if (std::holds_alternative::Struct>(_match_66._data)) { - auto& _v = std::get::Struct>(_match_66._data); + else if (std::holds_alternative::Struct>(_match_69._data)) { + auto& _v = std::get::Struct>(_match_69._data); auto& name = _v.name; auto& body = _v.body; auto& visibility = _v.visibility; @@ -3983,8 +4061,8 @@ struct CppCodegen { this->output = (this->output + ((((std::string("using ") + (ns)) + std::string("::")) + (name.lexeme)) + std::string(";\n"))); } } - else if (std::holds_alternative::Enum>(_match_66._data)) { - auto& _v = std::get::Enum>(_match_66._data); + else if (std::holds_alternative::Enum>(_match_69._data)) { + auto& _v = std::get::Enum>(_match_69._data); auto& name = _v.name; auto& variants = _v.variants; auto& methods = _v.methods; @@ -3994,8 +4072,8 @@ struct CppCodegen { this->output = (this->output + ((((std::string("using ") + (ns)) + std::string("::")) + (name.lexeme)) + std::string(";\n"))); } } - else if (std::holds_alternative::Const>(_match_66._data)) { - auto& _v = std::get::Const>(_match_66._data); + else if (std::holds_alternative::Const>(_match_69._data)) { + auto& _v = std::get::Const>(_match_69._data); auto& name = _v.name; auto& const_type = _v.const_type; auto& value = _v.value; @@ -4005,8 +4083,8 @@ struct CppCodegen { this->output = (this->output + ((((std::string("using ") + (ns)) + std::string("::")) + (name.lexeme)) + std::string(";\n"))); } } - else if (std::holds_alternative::Let>(_match_66._data)) { - auto& _v = std::get::Let>(_match_66._data); + else if (std::holds_alternative::Let>(_match_69._data)) { + auto& _v = std::get::Let>(_match_69._data); auto& name = _v.name; auto& var_type = _v.var_type; auto& initializer = _v.initializer; @@ -4031,9 +4109,9 @@ struct CppCodegen { this->indent_level = INT64_C(1); for (const auto& stmt : this->module_stmts[index]) { { - const auto& _match_67 = stmt; - if (std::holds_alternative::Function>(_match_67._data)) { - auto& _v = std::get::Function>(_match_67._data); + const auto& _match_70 = stmt; + if (std::holds_alternative::Function>(_match_70._data)) { + auto& _v = std::get::Function>(_match_70._data); auto& name = _v.name; auto& params = _v.params; auto& return_type = _v.return_type; @@ -4051,8 +4129,8 @@ struct CppCodegen { (*this).emit_stmt(stmt, false); } } - else if (std::holds_alternative::Extern>(_match_67._data)) { - auto& _v = std::get::Extern>(_match_67._data); + else if (std::holds_alternative::Extern>(_match_70._data)) { + auto& _v = std::get::Extern>(_match_70._data); auto& header = _v.header; auto& import_path = _v.import_path; auto& link_lib = _v.link_lib; @@ -4087,9 +4165,9 @@ struct CppCodegen { for (int64_t mi = INT64_C(0); mi < static_cast(this->module_stmts.size()); mi++) { for (const auto& stmt : this->module_stmts[mi]) { { - const auto& _match_68 = stmt; - if (std::holds_alternative::Extern>(_match_68._data)) { - auto& _v = std::get::Extern>(_match_68._data); + const auto& _match_71 = stmt; + if (std::holds_alternative::Extern>(_match_71._data)) { + auto& _v = std::get::Extern>(_match_71._data); auto& header = _v.header; auto& import_path = _v.import_path; auto& link_lib = _v.link_lib; @@ -4106,9 +4184,9 @@ struct CppCodegen { } for (const auto& stmt : stmts) { { - const auto& _match_69 = stmt; - if (std::holds_alternative::Extern>(_match_69._data)) { - auto& _v = std::get::Extern>(_match_69._data); + const auto& _match_72 = stmt; + if (std::holds_alternative::Extern>(_match_72._data)) { + auto& _v = std::get::Extern>(_match_72._data); auto& header = _v.header; auto& import_path = _v.import_path; auto& link_lib = _v.link_lib; @@ -4125,9 +4203,9 @@ struct CppCodegen { for (int64_t mi = INT64_C(0); mi < static_cast(this->module_stmts.size()); mi++) { for (const auto& stmt : this->module_stmts[mi]) { { - const auto& _match_70 = stmt; - if (std::holds_alternative::Extend>(_match_70._data)) { - auto& _v = std::get::Extend>(_match_70._data); + const auto& _match_73 = stmt; + if (std::holds_alternative::Extend>(_match_73._data)) { + auto& _v = std::get::Extend>(_match_73._data); auto& target = _v.target_type; auto& methods = _v.methods; auto& visibility = _v.visibility; @@ -4148,9 +4226,9 @@ struct CppCodegen { } for (const auto& stmt : stmts) { { - const auto& _match_71 = stmt; - if (std::holds_alternative::Extend>(_match_71._data)) { - auto& _v = std::get::Extend>(_match_71._data); + const auto& _match_74 = stmt; + if (std::holds_alternative::Extend>(_match_74._data)) { + auto& _v = std::get::Extend>(_match_74._data); auto& target = _v.target_type; auto& methods = _v.methods; auto& visibility = _v.visibility; @@ -4187,9 +4265,9 @@ struct CppCodegen { } for (const auto& stmt : stmts) { { - const auto& _match_72 = stmt; - if (std::holds_alternative::Function>(_match_72._data)) { - auto& _v = std::get::Function>(_match_72._data); + const auto& _match_75 = stmt; + if (std::holds_alternative::Function>(_match_75._data)) { + auto& _v = std::get::Function>(_match_75._data); auto& name = _v.name; auto& params = _v.params; auto& return_type = _v.return_type; @@ -4219,8 +4297,8 @@ struct CppCodegen { this->output = std::string(""); } } - else if (std::holds_alternative::Extern>(_match_72._data)) { - auto& _v = std::get::Extern>(_match_72._data); + else if (std::holds_alternative::Extern>(_match_75._data)) { + auto& _v = std::get::Extern>(_match_75._data); auto& header = _v.header; auto& import_path = _v.import_path; auto& link_lib = _v.link_lib; @@ -4228,8 +4306,8 @@ struct CppCodegen { auto& functions = _v.functions; /* pass */ } - else if (std::holds_alternative::Extend>(_match_72._data)) { - auto& _v = std::get::Extend>(_match_72._data); + else if (std::holds_alternative::Extend>(_match_75._data)) { + auto& _v = std::get::Extend>(_match_75._data); auto& target = _v.target_type; auto& methods = _v.methods; auto& visibility = _v.visibility; @@ -4369,27 +4447,27 @@ struct Checker { bool types_compatible(const TypeNode& expected, const TypeNode& actual) { { - const auto& _match_73 = actual; - if (std::holds_alternative::Array>(_match_73._data)) { - auto& _v = std::get::Array>(_match_73._data); + const auto& _match_76 = actual; + if (std::holds_alternative::Array>(_match_76._data)) { + auto& _v = std::get::Array>(_match_76._data); auto& a_inner = *_v.inner; { - const auto& _match_74 = a_inner; - if (std::holds_alternative::Auto>(_match_74._data)) { + const auto& _match_77 = a_inner; + if (std::holds_alternative::Auto>(_match_77._data)) { { - const auto& _match_75 = expected; - if (std::holds_alternative::Array>(_match_75._data)) { - auto& _v = std::get::Array>(_match_75._data); + const auto& _match_78 = expected; + if (std::holds_alternative::Array>(_match_78._data)) { + auto& _v = std::get::Array>(_match_78._data); auto& e_inner = *_v.inner; return true; } - else if (std::holds_alternative::HashSet>(_match_75._data)) { - auto& _v = std::get::HashSet>(_match_75._data); + else if (std::holds_alternative::HashSet>(_match_78._data)) { + auto& _v = std::get::HashSet>(_match_78._data); auto& e_inner = *_v.inner; return true; } - else if (std::holds_alternative::HashMap>(_match_75._data)) { - auto& _v = std::get::HashMap>(_match_75._data); + else if (std::holds_alternative::HashMap>(_match_78._data)) { + auto& _v = std::get::HashMap>(_match_78._data); auto& ek = *_v.key_type; auto& ev = *_v.value_type; return true; @@ -4409,14 +4487,14 @@ struct Checker { } } { - const auto& _match_76 = expected; - if (std::holds_alternative::Array>(_match_76._data)) { - auto& _v = std::get::Array>(_match_76._data); + const auto& _match_79 = expected; + if (std::holds_alternative::Array>(_match_79._data)) { + auto& _v = std::get::Array>(_match_79._data); auto& e_inner = *_v.inner; { - const auto& _match_77 = actual; - if (std::holds_alternative::Array>(_match_77._data)) { - auto& _v = std::get::Array>(_match_77._data); + const auto& _match_80 = actual; + if (std::holds_alternative::Array>(_match_80._data)) { + auto& _v = std::get::Array>(_match_80._data); auto& a_inner = *_v.inner; return (*this).types_compatible(e_inner, a_inner); } @@ -4430,14 +4508,14 @@ struct Checker { } } { - const auto& _match_78 = expected; - if (std::holds_alternative::Auto>(_match_78._data)) { + const auto& _match_81 = expected; + if (std::holds_alternative::Auto>(_match_81._data)) { return true; } - else if (_match_78._tag == "None") { + else if (_match_81._tag == "None") { return true; } - else if (std::holds_alternative::Dynamic>(_match_78._data)) { + else if (std::holds_alternative::Dynamic>(_match_81._data)) { return true; } else { @@ -4445,14 +4523,14 @@ struct Checker { } } { - const auto& _match_79 = actual; - if (std::holds_alternative::Auto>(_match_79._data)) { + const auto& _match_82 = actual; + if (std::holds_alternative::Auto>(_match_82._data)) { return true; } - else if (_match_79._tag == "None") { + else if (_match_82._tag == "None") { return true; } - else if (std::holds_alternative::Dynamic>(_match_79._data)) { + else if (std::holds_alternative::Dynamic>(_match_82._data)) { return true; } else { @@ -4467,20 +4545,20 @@ struct Checker { bool e_is_int = false; bool a_is_int = false; { - const auto& _match_80 = expected; - if (std::holds_alternative::Int>(_match_80._data)) { + const auto& _match_83 = expected; + if (std::holds_alternative::Int>(_match_83._data)) { e_is_int = true; } - else if (std::holds_alternative::Int8>(_match_80._data)) { + else if (std::holds_alternative::Int8>(_match_83._data)) { e_is_int = true; } - else if (std::holds_alternative::Int16>(_match_80._data)) { + else if (std::holds_alternative::Int16>(_match_83._data)) { e_is_int = true; } - else if (std::holds_alternative::Int32>(_match_80._data)) { + else if (std::holds_alternative::Int32>(_match_83._data)) { e_is_int = true; } - else if (std::holds_alternative::USize>(_match_80._data)) { + else if (std::holds_alternative::USize>(_match_83._data)) { e_is_int = true; } else { @@ -4488,20 +4566,20 @@ struct Checker { } } { - const auto& _match_81 = actual; - if (std::holds_alternative::Int>(_match_81._data)) { + const auto& _match_84 = actual; + if (std::holds_alternative::Int>(_match_84._data)) { a_is_int = true; } - else if (std::holds_alternative::Int8>(_match_81._data)) { + else if (std::holds_alternative::Int8>(_match_84._data)) { a_is_int = true; } - else if (std::holds_alternative::Int16>(_match_81._data)) { + else if (std::holds_alternative::Int16>(_match_84._data)) { a_is_int = true; } - else if (std::holds_alternative::Int32>(_match_81._data)) { + else if (std::holds_alternative::Int32>(_match_84._data)) { a_is_int = true; } - else if (std::holds_alternative::USize>(_match_81._data)) { + else if (std::holds_alternative::USize>(_match_84._data)) { a_is_int = true; } else { @@ -4514,11 +4592,11 @@ struct Checker { bool e_is_float = false; bool a_is_float = false; { - const auto& _match_82 = expected; - if (std::holds_alternative::Float>(_match_82._data)) { + const auto& _match_85 = expected; + if (std::holds_alternative::Float>(_match_85._data)) { e_is_float = true; } - else if (std::holds_alternative::Float32>(_match_82._data)) { + else if (std::holds_alternative::Float32>(_match_85._data)) { e_is_float = true; } else { @@ -4526,11 +4604,11 @@ struct Checker { } } { - const auto& _match_83 = actual; - if (std::holds_alternative::Float>(_match_83._data)) { + const auto& _match_86 = actual; + if (std::holds_alternative::Float>(_match_86._data)) { a_is_float = true; } - else if (std::holds_alternative::Float32>(_match_83._data)) { + else if (std::holds_alternative::Float32>(_match_86._data)) { a_is_float = true; } else { @@ -4544,11 +4622,11 @@ struct Checker { return true; } { - const auto& _match_84 = expected; - if (std::holds_alternative::CString>(_match_84._data)) { + const auto& _match_87 = expected; + if (std::holds_alternative::CString>(_match_87._data)) { { - const auto& _match_85 = actual; - if (std::holds_alternative::Str>(_match_85._data)) { + const auto& _match_88 = actual; + if (std::holds_alternative::Str>(_match_88._data)) { return true; } else { @@ -4556,10 +4634,10 @@ struct Checker { } } } - else if (std::holds_alternative::Str>(_match_84._data)) { + else if (std::holds_alternative::Str>(_match_87._data)) { { - const auto& _match_86 = actual; - if (std::holds_alternative::CString>(_match_86._data)) { + const auto& _match_89 = actual; + if (std::holds_alternative::CString>(_match_89._data)) { return true; } else { @@ -4572,13 +4650,13 @@ struct Checker { } } { - const auto& _match_87 = expected; - if (std::holds_alternative::Nullable>(_match_87._data)) { - auto& _v = std::get::Nullable>(_match_87._data); + const auto& _match_90 = expected; + if (std::holds_alternative::Nullable>(_match_90._data)) { + auto& _v = std::get::Nullable>(_match_90._data); auto& inner = *_v.inner; { - const auto& _match_88 = actual; - if (std::holds_alternative::NullType>(_match_88._data)) { + const auto& _match_91 = actual; + if (std::holds_alternative::NullType>(_match_91._data)) { return true; } else { @@ -4591,13 +4669,13 @@ struct Checker { } } { - const auto& _match_89 = expected; - if (std::holds_alternative::Ptr>(_match_89._data)) { - auto& _v = std::get::Ptr>(_match_89._data); + const auto& _match_92 = expected; + if (std::holds_alternative::Ptr>(_match_92._data)) { + auto& _v = std::get::Ptr>(_match_92._data); auto& inner = *_v.inner; { - const auto& _match_90 = actual; - if (std::holds_alternative::NullType>(_match_90._data)) { + const auto& _match_93 = actual; + if (std::holds_alternative::NullType>(_match_93._data)) { return true; } else { @@ -4614,82 +4692,82 @@ struct Checker { std::string type_name(const TypeNode& t) { { - const auto& _match_91 = t; - if (std::holds_alternative::Int>(_match_91._data)) { + const auto& _match_94 = t; + if (std::holds_alternative::Int>(_match_94._data)) { return std::string("int"); } - else if (std::holds_alternative::Float>(_match_91._data)) { + else if (std::holds_alternative::Float>(_match_94._data)) { return std::string("float"); } - else if (std::holds_alternative::Str>(_match_91._data)) { + else if (std::holds_alternative::Str>(_match_94._data)) { return std::string("string"); } - else if (std::holds_alternative::Bool>(_match_91._data)) { + else if (std::holds_alternative::Bool>(_match_94._data)) { return std::string("bool"); } - else if (std::holds_alternative::Void>(_match_91._data)) { + else if (std::holds_alternative::Void>(_match_94._data)) { return std::string("void"); } - else if (std::holds_alternative::Auto>(_match_91._data)) { + else if (std::holds_alternative::Auto>(_match_94._data)) { return std::string("auto"); } - else if (std::holds_alternative::Dynamic>(_match_91._data)) { + else if (std::holds_alternative::Dynamic>(_match_94._data)) { return std::string("dynamic"); } - else if (std::holds_alternative::NullType>(_match_91._data)) { + else if (std::holds_alternative::NullType>(_match_94._data)) { return std::string("null"); } - else if (std::holds_alternative::Custom>(_match_91._data)) { - auto& _v = std::get::Custom>(_match_91._data); + else if (std::holds_alternative::Custom>(_match_94._data)) { + auto& _v = std::get::Custom>(_match_94._data); auto& name = _v.name; auto& _ = _v.type_args; return name; } - else if (std::holds_alternative::Array>(_match_91._data)) { - auto& _v = std::get::Array>(_match_91._data); + else if (std::holds_alternative::Array>(_match_94._data)) { + auto& _v = std::get::Array>(_match_94._data); auto& inner = *_v.inner; return ((std::string("vector[") + ((*this).type_name(inner))) + std::string("]")); } - else if (std::holds_alternative::HashSet>(_match_91._data)) { - auto& _v = std::get::HashSet>(_match_91._data); + else if (std::holds_alternative::HashSet>(_match_94._data)) { + auto& _v = std::get::HashSet>(_match_94._data); auto& inner = *_v.inner; return ((std::string("set[") + ((*this).type_name(inner))) + std::string("]")); } - else if (std::holds_alternative::HashMap>(_match_91._data)) { - auto& _v = std::get::HashMap>(_match_91._data); + else if (std::holds_alternative::HashMap>(_match_94._data)) { + auto& _v = std::get::HashMap>(_match_94._data); auto& k = *_v.key_type; auto& v = *_v.value_type; return ((((std::string("map[") + ((*this).type_name(k))) + std::string(", ")) + ((*this).type_name(v))) + std::string("]")); } - else if (std::holds_alternative::Nullable>(_match_91._data)) { - auto& _v = std::get::Nullable>(_match_91._data); + else if (std::holds_alternative::Nullable>(_match_94._data)) { + auto& _v = std::get::Nullable>(_match_94._data); auto& inner = *_v.inner; return ((std::string("") + ((*this).type_name(inner))) + std::string("?")); } - else if (std::holds_alternative::Int8>(_match_91._data)) { + else if (std::holds_alternative::Int8>(_match_94._data)) { return std::string("int8"); } - else if (std::holds_alternative::Int16>(_match_91._data)) { + else if (std::holds_alternative::Int16>(_match_94._data)) { return std::string("int16"); } - else if (std::holds_alternative::Int32>(_match_91._data)) { + else if (std::holds_alternative::Int32>(_match_94._data)) { return std::string("int32"); } - else if (std::holds_alternative::Float32>(_match_91._data)) { + else if (std::holds_alternative::Float32>(_match_94._data)) { return std::string("float32"); } - else if (std::holds_alternative::USize>(_match_91._data)) { + else if (std::holds_alternative::USize>(_match_94._data)) { return std::string("usize"); } - else if (std::holds_alternative::CString>(_match_91._data)) { + else if (std::holds_alternative::CString>(_match_94._data)) { return std::string("cstring"); } - else if (std::holds_alternative::Ptr>(_match_91._data)) { - auto& _v = std::get::Ptr>(_match_91._data); + else if (std::holds_alternative::Ptr>(_match_94._data)) { + auto& _v = std::get::Ptr>(_match_94._data); auto& inner = *_v.inner; return ((std::string("ptr[") + ((*this).type_name(inner))) + std::string("]")); } - else if (std::holds_alternative::Bytes>(_match_91._data)) { + else if (std::holds_alternative::Bytes>(_match_94._data)) { return std::string("bytes"); } else { @@ -4700,12 +4778,12 @@ struct Checker { TypeNode infer_type(const Expr& e) { { - const auto& _match_92 = e; - if (_match_92._tag == "None") { + const auto& _match_95 = e; + if (_match_95._tag == "None") { return TypeNode::make_None(); } - else if (std::holds_alternative::Literal>(_match_92._data)) { - auto& _v = std::get::Literal>(_match_92._data); + else if (std::holds_alternative::Literal>(_match_95._data)) { + auto& _v = std::get::Literal>(_match_95._data); auto& kind = _v.kind; auto& value = _v.value; if ((kind == std::string("int"))) { @@ -4733,14 +4811,14 @@ struct Checker { } return TypeNode::make_Auto(); } - else if (std::holds_alternative::Variable>(_match_92._data)) { - auto& _v = std::get::Variable>(_match_92._data); + else if (std::holds_alternative::Variable>(_match_95._data)) { + auto& _v = std::get::Variable>(_match_95._data); auto& name = _v.name; auto sym = (*this).lookup(name.lexeme); return sym.sym_type; } - else if (std::holds_alternative::Binary>(_match_92._data)) { - auto& _v = std::get::Binary>(_match_92._data); + else if (std::holds_alternative::Binary>(_match_95._data)) { + auto& _v = std::get::Binary>(_match_95._data); auto& left = *_v.left; auto& op = _v.op; auto& right = *_v.right; @@ -4750,8 +4828,8 @@ struct Checker { return TypeNode::make_Bool(); } { - const auto& _match_93 = lt; - if (std::holds_alternative::Str>(_match_93._data)) { + const auto& _match_96 = lt; + if (std::holds_alternative::Str>(_match_96._data)) { return TypeNode::make_Str(); } else { @@ -4759,8 +4837,8 @@ struct Checker { } } { - const auto& _match_94 = lt; - if (std::holds_alternative::Float>(_match_94._data)) { + const auto& _match_97 = lt; + if (std::holds_alternative::Float>(_match_97._data)) { return TypeNode::make_Float(); } else { @@ -4768,8 +4846,8 @@ struct Checker { } } { - const auto& _match_95 = rt; - if (std::holds_alternative::Float>(_match_95._data)) { + const auto& _match_98 = rt; + if (std::holds_alternative::Float>(_match_98._data)) { return TypeNode::make_Float(); } else { @@ -4777,8 +4855,8 @@ struct Checker { } } { - const auto& _match_96 = lt; - if (std::holds_alternative::Int>(_match_96._data)) { + const auto& _match_99 = lt; + if (std::holds_alternative::Int>(_match_99._data)) { return TypeNode::make_Int(); } else { @@ -4787,8 +4865,8 @@ struct Checker { } return TypeNode::make_Auto(); } - else if (std::holds_alternative::Unary>(_match_92._data)) { - auto& _v = std::get::Unary>(_match_92._data); + else if (std::holds_alternative::Unary>(_match_95._data)) { + auto& _v = std::get::Unary>(_match_95._data); auto& op = _v.op; auto& right = *_v.right; if ((op.token_type == TK_BANG) || (op.token_type == TK_NOT)) { @@ -4796,36 +4874,36 @@ struct Checker { } return (*this).infer_type(right); } - else if (std::holds_alternative::Logical>(_match_92._data)) { - auto& _v = std::get::Logical>(_match_92._data); + else if (std::holds_alternative::Logical>(_match_95._data)) { + auto& _v = std::get::Logical>(_match_95._data); auto& left = *_v.left; auto& op = _v.op; auto& right = *_v.right; return TypeNode::make_Bool(); } - else if (std::holds_alternative::Call>(_match_92._data)) { - auto& _v = std::get::Call>(_match_92._data); + else if (std::holds_alternative::Call>(_match_95._data)) { + auto& _v = std::get::Call>(_match_95._data); auto& callee = *_v.callee; auto& paren = _v.paren; auto& args = _v.args; auto& arg_names = _v.arg_names; return (*this).infer_call_type(callee); } - else if (std::holds_alternative::Grouping>(_match_92._data)) { - auto& _v = std::get::Grouping>(_match_92._data); + else if (std::holds_alternative::Grouping>(_match_95._data)) { + auto& _v = std::get::Grouping>(_match_95._data); auto& inner = *_v.inner; return (*this).infer_type(inner); } - else if (std::holds_alternative::Index>(_match_92._data)) { - auto& _v = std::get::Index>(_match_92._data); + else if (std::holds_alternative::Index>(_match_95._data)) { + auto& _v = std::get::Index>(_match_95._data); auto& object = *_v.object; auto& bracket = _v.bracket; auto& index = *_v.index; auto ot = (*this).infer_type(object); { - const auto& _match_97 = ot; - if (std::holds_alternative::Array>(_match_97._data)) { - auto& _v = std::get::Array>(_match_97._data); + const auto& _match_100 = ot; + if (std::holds_alternative::Array>(_match_100._data)) { + auto& _v = std::get::Array>(_match_100._data); auto& inner = *_v.inner; return inner; } @@ -4834,8 +4912,8 @@ struct Checker { } } } - else if (std::holds_alternative::Vector>(_match_92._data)) { - auto& _v = std::get::Vector>(_match_92._data); + else if (std::holds_alternative::Vector>(_match_95._data)) { + auto& _v = std::get::Vector>(_match_95._data); auto& elements = _v.elements; if ((static_cast(elements.size()) > INT64_C(0))) { auto inner = (*this).infer_type(elements[INT64_C(0)]); @@ -4843,24 +4921,24 @@ struct Checker { } return TypeNode::make_Array(TypeNode::make_Auto()); } - else if (std::holds_alternative::Cast>(_match_92._data)) { - auto& _v = std::get::Cast>(_match_92._data); + else if (std::holds_alternative::Cast>(_match_95._data)) { + auto& _v = std::get::Cast>(_match_95._data); auto& expr = *_v.expr; auto& target_type = _v.target_type; return target_type; } - else if (std::holds_alternative::This>(_match_92._data)) { - auto& _v = std::get::This>(_match_92._data); + else if (std::holds_alternative::This>(_match_95._data)) { + auto& _v = std::get::This>(_match_95._data); auto& kw = _v.keyword; return TypeNode::make_Custom(this->current_class_name, {}); } - else if (std::holds_alternative::Own>(_match_92._data)) { - auto& _v = std::get::Own>(_match_92._data); + else if (std::holds_alternative::Own>(_match_95._data)) { + auto& _v = std::get::Own>(_match_95._data); auto& expr = *_v.expr; return (*this).infer_type(expr); } - else if (std::holds_alternative::AddressOf>(_match_92._data)) { - auto& _v = std::get::AddressOf>(_match_92._data); + else if (std::holds_alternative::AddressOf>(_match_95._data)) { + auto& _v = std::get::AddressOf>(_match_95._data); auto& expr = *_v.expr; return TypeNode::make_Ptr((*this).infer_type(expr)); } @@ -4872,9 +4950,9 @@ struct Checker { TypeNode infer_call_type(const Expr& callee) { { - const auto& _match_98 = callee; - if (std::holds_alternative::Variable>(_match_98._data)) { - auto& _v = std::get::Variable>(_match_98._data); + const auto& _match_101 = callee; + if (std::holds_alternative::Variable>(_match_101._data)) { + auto& _v = std::get::Variable>(_match_101._data); auto& name = _v.name; if ((this->known_funcs.count(name.lexeme) > 0)) { ExternFn fi = this->known_funcs[name.lexeme]; @@ -4888,20 +4966,20 @@ struct Checker { } return TypeNode::make_Auto(); } - else if (std::holds_alternative::Get>(_match_98._data)) { - auto& _v = std::get::Get>(_match_98._data); + else if (std::holds_alternative::Get>(_match_101._data)) { + auto& _v = std::get::Get>(_match_101._data); auto& object = *_v.object; auto& name = _v.name; return TypeNode::make_Auto(); } - else if (std::holds_alternative::StaticGet>(_match_98._data)) { - auto& _v = std::get::StaticGet>(_match_98._data); + else if (std::holds_alternative::StaticGet>(_match_101._data)) { + auto& _v = std::get::StaticGet>(_match_101._data); auto& object = *_v.object; auto& name = _v.name; { - const auto& _match_99 = object; - if (std::holds_alternative::Variable>(_match_99._data)) { - auto& _v = std::get::Variable>(_match_99._data); + const auto& _match_102 = object; + if (std::holds_alternative::Variable>(_match_102._data)) { + auto& _v = std::get::Variable>(_match_102._data); auto& obj_name = _v.name; if ((this->known_enums.count(obj_name.lexeme) > 0)) { return TypeNode::make_Custom(obj_name.lexeme, {}); @@ -4955,9 +5033,9 @@ struct Checker { void collect_decl(const Stmt& s) { { - const auto& _match_100 = s; - if (std::holds_alternative::Function>(_match_100._data)) { - auto& _v = std::get::Function>(_match_100._data); + const auto& _match_103 = s; + if (std::holds_alternative::Function>(_match_103._data)) { + auto& _v = std::get::Function>(_match_103._data); auto& name = _v.name; auto& params = _v.params; auto& return_type = _v.return_type; @@ -4970,15 +5048,15 @@ struct Checker { auto& param_defaults = _v.param_defaults; this->known_funcs[name.lexeme] = ExternFn(name.lexeme, name.lexeme, return_type, params, param_defaults); } - else if (std::holds_alternative::Class>(_match_100._data)) { - auto& _v = std::get::Class>(_match_100._data); + else if (std::holds_alternative::Class>(_match_103._data)) { + auto& _v = std::get::Class>(_match_103._data); auto& name = _v.name; auto& body = _v.body; auto& visibility = _v.visibility; this->known_classes[name.lexeme] = body; } - else if (std::holds_alternative::Enum>(_match_100._data)) { - auto& _v = std::get::Enum>(_match_100._data); + else if (std::holds_alternative::Enum>(_match_103._data)) { + auto& _v = std::get::Enum>(_match_103._data); auto& name = _v.name; auto& variants = _v.variants; auto& methods = _v.methods; @@ -4986,8 +5064,8 @@ struct Checker { auto& enum_tp = _v.type_params; this->known_enums[name.lexeme] = variants; } - else if (std::holds_alternative::Const>(_match_100._data)) { - auto& _v = std::get::Const>(_match_100._data); + else if (std::holds_alternative::Const>(_match_103._data)) { + auto& _v = std::get::Const>(_match_103._data); auto& name = _v.name; auto& const_type = _v.const_type; auto& value = _v.value; @@ -4995,8 +5073,8 @@ struct Checker { auto& comptime_mode = _v.comptime_mode; (*this).declare(name.lexeme, const_type, std::string("const"), false, false, name); } - else if (std::holds_alternative::Struct>(_match_100._data)) { - auto& _v = std::get::Struct>(_match_100._data); + else if (std::holds_alternative::Struct>(_match_103._data)) { + auto& _v = std::get::Struct>(_match_103._data); auto& name = _v.name; auto& body = _v.body; auto& visibility = _v.visibility; @@ -5004,9 +5082,9 @@ struct Checker { this->known_classes[name.lexeme] = body; for (const auto& st : body) { { - const auto& _match_101 = st; - if (std::holds_alternative::Function>(_match_101._data)) { - auto& _v = std::get::Function>(_match_101._data); + const auto& _match_104 = st; + if (std::holds_alternative::Function>(_match_104._data)) { + auto& _v = std::get::Function>(_match_104._data); auto& fname = _v.name; auto& fparams = _v.params; auto& fret = _v.return_type; @@ -5027,8 +5105,8 @@ struct Checker { } } } - else if (std::holds_alternative::Namespace>(_match_100._data)) { - auto& _v = std::get::Namespace>(_match_100._data); + else if (std::holds_alternative::Namespace>(_match_103._data)) { + auto& _v = std::get::Namespace>(_match_103._data); auto& name = _v.name; auto& body = _v.body; auto& visibility = _v.visibility; @@ -5036,8 +5114,8 @@ struct Checker { (*this).collect_decl(ns_stmt); } } - else if (std::holds_alternative::Extern>(_match_100._data)) { - auto& _v = std::get::Extern>(_match_100._data); + else if (std::holds_alternative::Extern>(_match_103._data)) { + auto& _v = std::get::Extern>(_match_103._data); auto& header = _v.header; auto& import_path = _v.import_path; auto& link_lib = _v.link_lib; @@ -5059,14 +5137,14 @@ struct Checker { void check_stmt(const Stmt& s) { { - const auto& _match_102 = s; - if (std::holds_alternative::ExprStmt>(_match_102._data)) { - auto& _v = std::get::ExprStmt>(_match_102._data); + const auto& _match_105 = s; + if (std::holds_alternative::ExprStmt>(_match_105._data)) { + auto& _v = std::get::ExprStmt>(_match_105._data); auto& expr = _v.expr; (*this).check_expr(expr); } - else if (std::holds_alternative::Let>(_match_102._data)) { - auto& _v = std::get::Let>(_match_102._data); + else if (std::holds_alternative::Let>(_match_105._data)) { + auto& _v = std::get::Let>(_match_105._data); auto& name = _v.name; auto& var_type = _v.var_type; auto& initializer = _v.initializer; @@ -5076,11 +5154,11 @@ struct Checker { (*this).check_expr(initializer); auto init_type = (*this).infer_type(initializer); { - const auto& _match_103 = var_type; - if (std::holds_alternative::Auto>(_match_103._data)) { + const auto& _match_106 = var_type; + if (std::holds_alternative::Auto>(_match_106._data)) { /* pass */ } - else if (_match_103._tag == "None") { + else if (_match_106._tag == "None") { /* pass */ } else { @@ -5091,8 +5169,8 @@ struct Checker { } (*this).declare(name.lexeme, var_type, std::string("var"), is_ref, true, name); } - else if (std::holds_alternative::Const>(_match_102._data)) { - auto& _v = std::get::Const>(_match_102._data); + else if (std::holds_alternative::Const>(_match_105._data)) { + auto& _v = std::get::Const>(_match_105._data); auto& name = _v.name; auto& const_type = _v.const_type; auto& value = _v.value; @@ -5100,20 +5178,20 @@ struct Checker { auto& comptime_mode = _v.comptime_mode; (*this).check_expr(value); } - else if (std::holds_alternative::Return>(_match_102._data)) { - auto& _v = std::get::Return>(_match_102._data); + else if (std::holds_alternative::Return>(_match_105._data)) { + auto& _v = std::get::Return>(_match_105._data); auto& keyword = _v.keyword; auto& value = _v.value; (*this).check_expr(value); { - const auto& _match_104 = this->current_return_type; - if (_match_104._tag == "None") { + const auto& _match_107 = this->current_return_type; + if (_match_107._tag == "None") { /* pass */ } - else if (std::holds_alternative::Void>(_match_104._data)) { + else if (std::holds_alternative::Void>(_match_107._data)) { { - const auto& _match_105 = value; - if (_match_105._tag == "None") { + const auto& _match_108 = value; + if (_match_108._tag == "None") { /* pass */ } else { @@ -5129,8 +5207,8 @@ struct Checker { } } } - else if (std::holds_alternative::If>(_match_102._data)) { - auto& _v = std::get::If>(_match_102._data); + else if (std::holds_alternative::If>(_match_105._data)) { + auto& _v = std::get::If>(_match_105._data); auto& condition = _v.condition; auto& then_branch = *_v.then_branch; auto& else_branch = *_v.else_branch; @@ -5138,15 +5216,15 @@ struct Checker { (*this).check_stmt(then_branch); (*this).check_stmt(else_branch); } - else if (std::holds_alternative::While>(_match_102._data)) { - auto& _v = std::get::While>(_match_102._data); + else if (std::holds_alternative::While>(_match_105._data)) { + auto& _v = std::get::While>(_match_105._data); auto& condition = _v.condition; auto& body = *_v.body; (*this).check_expr(condition); (*this).check_stmt(body); } - else if (std::holds_alternative::For>(_match_102._data)) { - auto& _v = std::get::For>(_match_102._data); + else if (std::holds_alternative::For>(_match_105._data)) { + auto& _v = std::get::For>(_match_105._data); auto& item_name = _v.item_name; auto& collection = _v.collection; auto& body = *_v.body; @@ -5157,9 +5235,9 @@ struct Checker { auto coll_type = (*this).infer_type(collection); auto item_type = TypeNode::make_Auto(); { - const auto& _match_106 = coll_type; - if (std::holds_alternative::Array>(_match_106._data)) { - auto& _v = std::get::Array>(_match_106._data); + const auto& _match_109 = coll_type; + if (std::holds_alternative::Array>(_match_109._data)) { + auto& _v = std::get::Array>(_match_109._data); auto& inner = *_v.inner; item_type = inner; } @@ -5171,8 +5249,8 @@ struct Checker { (*this).check_stmt(body); (*this).pop_scope(); } - else if (std::holds_alternative::Block>(_match_102._data)) { - auto& _v = std::get::Block>(_match_102._data); + else if (std::holds_alternative::Block>(_match_105._data)) { + auto& _v = std::get::Block>(_match_105._data); auto& statements = _v.statements; (*this).push_scope(); for (const auto& st : statements) { @@ -5180,8 +5258,8 @@ struct Checker { } (*this).pop_scope(); } - else if (std::holds_alternative::Function>(_match_102._data)) { - auto& _v = std::get::Function>(_match_102._data); + else if (std::holds_alternative::Function>(_match_105._data)) { + auto& _v = std::get::Function>(_match_105._data); auto& name = _v.name; auto& params = _v.params; auto& return_type = _v.return_type; @@ -5194,23 +5272,23 @@ struct Checker { auto& param_defaults = _v.param_defaults; (*this).check_function(name, params, return_type, body); } - else if (std::holds_alternative::Class>(_match_102._data)) { - auto& _v = std::get::Class>(_match_102._data); + else if (std::holds_alternative::Class>(_match_105._data)) { + auto& _v = std::get::Class>(_match_105._data); auto& name = _v.name; auto& body = _v.body; auto& visibility = _v.visibility; (*this).check_class(name, body); } - else if (std::holds_alternative::Struct>(_match_102._data)) { - auto& _v = std::get::Struct>(_match_102._data); + else if (std::holds_alternative::Struct>(_match_105._data)) { + auto& _v = std::get::Struct>(_match_105._data); auto& name = _v.name; auto& body = _v.body; auto& visibility = _v.visibility; auto& struct_tp = _v.type_params; /* pass */ } - else if (std::holds_alternative::Enum>(_match_102._data)) { - auto& _v = std::get::Enum>(_match_102._data); + else if (std::holds_alternative::Enum>(_match_105._data)) { + auto& _v = std::get::Enum>(_match_105._data); auto& name = _v.name; auto& variants = _v.variants; auto& methods = _v.methods; @@ -5218,16 +5296,16 @@ struct Checker { auto& enum_tp = _v.type_params; /* pass */ } - else if (std::holds_alternative::Match>(_match_102._data)) { - auto& _v = std::get::Match>(_match_102._data); + else if (std::holds_alternative::Match>(_match_105._data)) { + auto& _v = std::get::Match>(_match_105._data); auto& expr = _v.expr; auto& arm_patterns = _v.arm_patterns; auto& arm_bodies = _v.arm_bodies; (*this).check_expr(expr); (*this).check_match(expr, arm_patterns, arm_bodies); } - else if (std::holds_alternative::Try>(_match_102._data)) { - auto& _v = std::get::Try>(_match_102._data); + else if (std::holds_alternative::Try>(_match_105._data)) { + auto& _v = std::get::Try>(_match_105._data); auto& try_body = *_v.try_body; auto& catch_body = *_v.catch_body; auto& exception_name = _v.exception_name; @@ -5241,8 +5319,8 @@ struct Checker { (*this).check_stmt(catch_body); (*this).pop_scope(); } - else if (std::holds_alternative::Namespace>(_match_102._data)) { - auto& _v = std::get::Namespace>(_match_102._data); + else if (std::holds_alternative::Namespace>(_match_105._data)) { + auto& _v = std::get::Namespace>(_match_105._data); auto& name = _v.name; auto& body = _v.body; auto& visibility = _v.visibility; @@ -5255,34 +5333,34 @@ struct Checker { } (*this).pop_scope(); } - else if (std::holds_alternative::Import>(_match_102._data)) { - auto& _v = std::get::Import>(_match_102._data); + else if (std::holds_alternative::Import>(_match_105._data)) { + auto& _v = std::get::Import>(_match_105._data); auto& path = _v.path; auto& alias = _v.alias; /* pass */ } - else if (std::holds_alternative::Break>(_match_102._data)) { - auto& _v = std::get::Break>(_match_102._data); + else if (std::holds_alternative::Break>(_match_105._data)) { + auto& _v = std::get::Break>(_match_105._data); auto& keyword = _v.keyword; /* pass */ } - else if (std::holds_alternative::Continue>(_match_102._data)) { - auto& _v = std::get::Continue>(_match_102._data); + else if (std::holds_alternative::Continue>(_match_105._data)) { + auto& _v = std::get::Continue>(_match_105._data); auto& keyword = _v.keyword; /* pass */ } - else if (std::holds_alternative::Pass>(_match_102._data)) { - auto& _v = std::get::Pass>(_match_102._data); + else if (std::holds_alternative::Pass>(_match_105._data)) { + auto& _v = std::get::Pass>(_match_105._data); auto& keyword = _v.keyword; /* pass */ } - else if (std::holds_alternative::CppBlock>(_match_102._data)) { - auto& _v = std::get::CppBlock>(_match_102._data); + else if (std::holds_alternative::CppBlock>(_match_105._data)) { + auto& _v = std::get::CppBlock>(_match_105._data); auto& code = _v.code; /* pass */ } - else if (std::holds_alternative::Extern>(_match_102._data)) { - auto& _v = std::get::Extern>(_match_102._data); + else if (std::holds_alternative::Extern>(_match_105._data)) { + auto& _v = std::get::Extern>(_match_105._data); auto& header = _v.header; auto& import_path = _v.import_path; auto& link_lib = _v.link_lib; @@ -5319,9 +5397,9 @@ struct Checker { (*this).declare(std::string("this"), TypeNode::make_Custom(name.lexeme, {}), std::string("var"), false, false, name); for (const auto& s : body) { { - const auto& _match_107 = s; - if (std::holds_alternative::Function>(_match_107._data)) { - auto& _v = std::get::Function>(_match_107._data); + const auto& _match_110 = s; + if (std::holds_alternative::Function>(_match_110._data)) { + auto& _v = std::get::Function>(_match_110._data); auto& fname = _v.name; auto& params = _v.params; auto& return_type = _v.return_type; @@ -5334,8 +5412,8 @@ struct Checker { auto& fn_defaults = _v.param_defaults; (*this).check_function(fname, params, return_type, fbody); } - else if (std::holds_alternative::Let>(_match_107._data)) { - auto& _v = std::get::Let>(_match_107._data); + else if (std::holds_alternative::Let>(_match_110._data)) { + auto& _v = std::get::Let>(_match_110._data); auto& lname = _v.name; auto& var_type = _v.var_type; auto& initializer = _v.initializer; @@ -5356,46 +5434,46 @@ struct Checker { void check_expr(const Expr& e) { { - const auto& _match_108 = e; - if (_match_108._tag == "None") { + const auto& _match_111 = e; + if (_match_111._tag == "None") { /* pass */ } - else if (std::holds_alternative::Variable>(_match_108._data)) { - auto& _v = std::get::Variable>(_match_108._data); + else if (std::holds_alternative::Variable>(_match_111._data)) { + auto& _v = std::get::Variable>(_match_111._data); auto& name = _v.name; if ((!(*this).resolve(name.lexeme))) { (*this).error(((std::string("Undefined variable '") + (name.lexeme)) + std::string("'")), name); } } - else if (std::holds_alternative::Binary>(_match_108._data)) { - auto& _v = std::get::Binary>(_match_108._data); + else if (std::holds_alternative::Binary>(_match_111._data)) { + auto& _v = std::get::Binary>(_match_111._data); auto& left = *_v.left; auto& op = _v.op; auto& right = *_v.right; (*this).check_expr(left); (*this).check_expr(right); } - else if (std::holds_alternative::Unary>(_match_108._data)) { - auto& _v = std::get::Unary>(_match_108._data); + else if (std::holds_alternative::Unary>(_match_111._data)) { + auto& _v = std::get::Unary>(_match_111._data); auto& op = _v.op; auto& right = *_v.right; (*this).check_expr(right); } - else if (std::holds_alternative::Logical>(_match_108._data)) { - auto& _v = std::get::Logical>(_match_108._data); + else if (std::holds_alternative::Logical>(_match_111._data)) { + auto& _v = std::get::Logical>(_match_111._data); auto& left = *_v.left; auto& op = _v.op; auto& right = *_v.right; (*this).check_expr(left); (*this).check_expr(right); } - else if (std::holds_alternative::Grouping>(_match_108._data)) { - auto& _v = std::get::Grouping>(_match_108._data); + else if (std::holds_alternative::Grouping>(_match_111._data)) { + auto& _v = std::get::Grouping>(_match_111._data); auto& inner = *_v.inner; (*this).check_expr(inner); } - else if (std::holds_alternative::Call>(_match_108._data)) { - auto& _v = std::get::Call>(_match_108._data); + else if (std::holds_alternative::Call>(_match_111._data)) { + auto& _v = std::get::Call>(_match_111._data); auto& callee = *_v.callee; auto& paren = _v.paren; auto& args = _v.args; @@ -5406,8 +5484,8 @@ struct Checker { } (*this).check_call_args(callee, args, arg_names, paren); } - else if (std::holds_alternative::Assign>(_match_108._data)) { - auto& _v = std::get::Assign>(_match_108._data); + else if (std::holds_alternative::Assign>(_match_111._data)) { + auto& _v = std::get::Assign>(_match_111._data); auto& name = _v.name; auto& value = *_v.value; (*this).check_expr(value); @@ -5421,16 +5499,16 @@ struct Checker { } } } - else if (std::holds_alternative::Index>(_match_108._data)) { - auto& _v = std::get::Index>(_match_108._data); + else if (std::holds_alternative::Index>(_match_111._data)) { + auto& _v = std::get::Index>(_match_111._data); auto& object = *_v.object; auto& bracket = _v.bracket; auto& index = *_v.index; (*this).check_expr(object); (*this).check_expr(index); } - else if (std::holds_alternative::IndexSet>(_match_108._data)) { - auto& _v = std::get::IndexSet>(_match_108._data); + else if (std::holds_alternative::IndexSet>(_match_111._data)) { + auto& _v = std::get::IndexSet>(_match_111._data); auto& object = *_v.object; auto& bracket = _v.bracket; auto& index = *_v.index; @@ -5439,15 +5517,15 @@ struct Checker { (*this).check_expr(index); (*this).check_expr(value); } - else if (std::holds_alternative::Vector>(_match_108._data)) { - auto& _v = std::get::Vector>(_match_108._data); + else if (std::holds_alternative::Vector>(_match_111._data)) { + auto& _v = std::get::Vector>(_match_111._data); auto& elements = _v.elements; for (const auto& el : elements) { (*this).check_expr(el); } } - else if (std::holds_alternative::Map>(_match_108._data)) { - auto& _v = std::get::Map>(_match_108._data); + else if (std::holds_alternative::Map>(_match_111._data)) { + auto& _v = std::get::Map>(_match_111._data); auto& keys = _v.keys; auto& values = _v.values; for (const auto& k : keys) { @@ -5457,28 +5535,28 @@ struct Checker { (*this).check_expr(v); } } - else if (std::holds_alternative::Get>(_match_108._data)) { - auto& _v = std::get::Get>(_match_108._data); + else if (std::holds_alternative::Get>(_match_111._data)) { + auto& _v = std::get::Get>(_match_111._data); auto& object = *_v.object; auto& name = _v.name; (*this).check_expr(object); } - else if (std::holds_alternative::Set>(_match_108._data)) { - auto& _v = std::get::Set>(_match_108._data); + else if (std::holds_alternative::Set>(_match_111._data)) { + auto& _v = std::get::Set>(_match_111._data); auto& object = *_v.object; auto& name = _v.name; auto& value = *_v.value; (*this).check_expr(object); (*this).check_expr(value); } - else if (std::holds_alternative::StaticGet>(_match_108._data)) { - auto& _v = std::get::StaticGet>(_match_108._data); + else if (std::holds_alternative::StaticGet>(_match_111._data)) { + auto& _v = std::get::StaticGet>(_match_111._data); auto& object = *_v.object; auto& name = _v.name; { - const auto& _match_109 = object; - if (std::holds_alternative::Variable>(_match_109._data)) { - auto& _v = std::get::Variable>(_match_109._data); + const auto& _match_112 = object; + if (std::holds_alternative::Variable>(_match_112._data)) { + auto& _v = std::get::Variable>(_match_112._data); auto& tok = _v.name; /* pass */ } @@ -5487,26 +5565,26 @@ struct Checker { } } } - else if (std::holds_alternative::Cast>(_match_108._data)) { - auto& _v = std::get::Cast>(_match_108._data); + else if (std::holds_alternative::Cast>(_match_111._data)) { + auto& _v = std::get::Cast>(_match_111._data); auto& expr = *_v.expr; auto& target_type = _v.target_type; (*this).check_expr(expr); } - else if (std::holds_alternative::Throw>(_match_108._data)) { - auto& _v = std::get::Throw>(_match_108._data); + else if (std::holds_alternative::Throw>(_match_111._data)) { + auto& _v = std::get::Throw>(_match_111._data); auto& expr = *_v.expr; (*this).check_expr(expr); } - else if (std::holds_alternative::Range>(_match_108._data)) { - auto& _v = std::get::Range>(_match_108._data); + else if (std::holds_alternative::Range>(_match_111._data)) { + auto& _v = std::get::Range>(_match_111._data); auto& start = *_v.start; auto& end = *_v.end; (*this).check_expr(start); (*this).check_expr(end); } - else if (std::holds_alternative::Lambda>(_match_108._data)) { - auto& _v = std::get::Lambda>(_match_108._data); + else if (std::holds_alternative::Lambda>(_match_111._data)) { + auto& _v = std::get::Lambda>(_match_111._data); auto& params = _v.params; auto& body = *_v.body; (*this).push_scope(); @@ -5516,8 +5594,8 @@ struct Checker { (*this).check_expr(body); (*this).pop_scope(); } - else if (std::holds_alternative::BlockLambda>(_match_108._data)) { - auto& _v = std::get::BlockLambda>(_match_108._data); + else if (std::holds_alternative::BlockLambda>(_match_111._data)) { + auto& _v = std::get::BlockLambda>(_match_111._data); auto& params = _v.params; auto& body_id = _v.body_id; (*this).push_scope(); @@ -5526,13 +5604,13 @@ struct Checker { } (*this).pop_scope(); } - else if (std::holds_alternative::Own>(_match_108._data)) { - auto& _v = std::get::Own>(_match_108._data); + else if (std::holds_alternative::Own>(_match_111._data)) { + auto& _v = std::get::Own>(_match_111._data); auto& expr = *_v.expr; (*this).check_expr(expr); } - else if (std::holds_alternative::AddressOf>(_match_108._data)) { - auto& _v = std::get::AddressOf>(_match_108._data); + else if (std::holds_alternative::AddressOf>(_match_111._data)) { + auto& _v = std::get::AddressOf>(_match_111._data); auto& expr = *_v.expr; (*this).check_expr(expr); } @@ -5544,9 +5622,9 @@ struct Checker { void check_call_args(const Expr& callee, const std::vector& args, const std::vector& arg_names, const Token& paren) { { - const auto& _match_110 = callee; - if (std::holds_alternative::Variable>(_match_110._data)) { - auto& _v = std::get::Variable>(_match_110._data); + const auto& _match_113 = callee; + if (std::holds_alternative::Variable>(_match_113._data)) { + auto& _v = std::get::Variable>(_match_113._data); auto& name = _v.name; if ((this->known_funcs.count(name.lexeme) > 0)) { ExternFn fi = this->known_funcs[name.lexeme]; @@ -5557,8 +5635,8 @@ struct Checker { int64_t di = INT64_C(0); while ((di < static_cast(fi.param_defaults.size()))) { { - const auto& _match_111 = fi.param_defaults[di]; - if (_match_111._tag == "None") { + const auto& _match_114 = fi.param_defaults[di]; + if (_match_114._tag == "None") { required = (required + INT64_C(1)); } else { @@ -5943,27 +6021,27 @@ struct Parser { std::string type_to_string(const TypeNode& t) { { - const auto& _match_112 = t; - if (std::holds_alternative::Int>(_match_112._data)) { + const auto& _match_115 = t; + if (std::holds_alternative::Int>(_match_115._data)) { return std::string("int64_t"); } - else if (std::holds_alternative::Float>(_match_112._data)) { + else if (std::holds_alternative::Float>(_match_115._data)) { return std::string("double"); } - else if (std::holds_alternative::Str>(_match_112._data)) { + else if (std::holds_alternative::Str>(_match_115._data)) { return std::string("std::string"); } - else if (std::holds_alternative::Bool>(_match_112._data)) { + else if (std::holds_alternative::Bool>(_match_115._data)) { return std::string("bool"); } - else if (std::holds_alternative::Void>(_match_112._data)) { + else if (std::holds_alternative::Void>(_match_115._data)) { return std::string("void"); } - else if (std::holds_alternative::Auto>(_match_112._data)) { + else if (std::holds_alternative::Auto>(_match_115._data)) { return std::string("auto"); } - else if (std::holds_alternative::Custom>(_match_112._data)) { - auto& _v = std::get::Custom>(_match_112._data); + else if (std::holds_alternative::Custom>(_match_115._data)) { + auto& _v = std::get::Custom>(_match_115._data); auto& name = _v.name; auto& type_args = _v.type_args; if ((static_cast(type_args.size()) > INT64_C(0))) { @@ -5975,35 +6053,35 @@ struct Parser { } return name; } - else if (std::holds_alternative::Array>(_match_112._data)) { - auto& _v = std::get::Array>(_match_112._data); + else if (std::holds_alternative::Array>(_match_115._data)) { + auto& _v = std::get::Array>(_match_115._data); auto& inner = *_v.inner; return ((std::string("std::vector<") + ((*this).type_to_string(inner))) + std::string(">")); } - else if (std::holds_alternative::Int8>(_match_112._data)) { + else if (std::holds_alternative::Int8>(_match_115._data)) { return std::string("int8_t"); } - else if (std::holds_alternative::Int16>(_match_112._data)) { + else if (std::holds_alternative::Int16>(_match_115._data)) { return std::string("int16_t"); } - else if (std::holds_alternative::Int32>(_match_112._data)) { + else if (std::holds_alternative::Int32>(_match_115._data)) { return std::string("int32_t"); } - else if (std::holds_alternative::Float32>(_match_112._data)) { + else if (std::holds_alternative::Float32>(_match_115._data)) { return std::string("float"); } - else if (std::holds_alternative::USize>(_match_112._data)) { + else if (std::holds_alternative::USize>(_match_115._data)) { return std::string("size_t"); } - else if (std::holds_alternative::CString>(_match_112._data)) { + else if (std::holds_alternative::CString>(_match_115._data)) { return std::string("const char*"); } - else if (std::holds_alternative::Ptr>(_match_112._data)) { - auto& _v = std::get::Ptr>(_match_112._data); + else if (std::holds_alternative::Ptr>(_match_115._data)) { + auto& _v = std::get::Ptr>(_match_115._data); auto& inner = *_v.inner; return ((std::string("") + ((*this).type_to_string(inner))) + std::string("*")); } - else if (std::holds_alternative::Bytes>(_match_112._data)) { + else if (std::holds_alternative::Bytes>(_match_115._data)) { return std::string("std::vector"); } else { @@ -6021,20 +6099,20 @@ struct Parser { if ((*this).match_any(std::vector{TK_EQUAL})) { Expr value = (*this).assignment(); { - const auto& _match_113 = expr; - if (std::holds_alternative::Variable>(_match_113._data)) { - auto& _v = std::get::Variable>(_match_113._data); + const auto& _match_116 = expr; + if (std::holds_alternative::Variable>(_match_116._data)) { + auto& _v = std::get::Variable>(_match_116._data); auto& name = _v.name; return Expr::make_Assign(name, value); } - else if (std::holds_alternative::Get>(_match_113._data)) { - auto& _v = std::get::Get>(_match_113._data); + else if (std::holds_alternative::Get>(_match_116._data)) { + auto& _v = std::get::Get>(_match_116._data); auto& object = *_v.object; auto& name = _v.name; return Expr::make_Set(object, name, value); } - else if (std::holds_alternative::Index>(_match_113._data)) { - auto& _v = std::get::Index>(_match_113._data); + else if (std::holds_alternative::Index>(_match_116._data)) { + auto& _v = std::get::Index>(_match_116._data); auto& object = *_v.object; auto& bracket = _v.bracket; auto& index = *_v.index; @@ -6063,22 +6141,22 @@ struct Parser { } auto op_token = Token(base_type, base_lexeme, compound_op.line, compound_op.col); { - const auto& _match_114 = expr; - if (std::holds_alternative::Variable>(_match_114._data)) { - auto& _v = std::get::Variable>(_match_114._data); + const auto& _match_117 = expr; + if (std::holds_alternative::Variable>(_match_117._data)) { + auto& _v = std::get::Variable>(_match_117._data); auto& name = _v.name; Expr bin = Expr::make_Binary(Expr::make_Variable(name), op_token, rhs); return Expr::make_Assign(name, bin); } - else if (std::holds_alternative::Get>(_match_114._data)) { - auto& _v = std::get::Get>(_match_114._data); + else if (std::holds_alternative::Get>(_match_117._data)) { + auto& _v = std::get::Get>(_match_117._data); auto& object = *_v.object; auto& name = _v.name; Expr bin = Expr::make_Binary(Expr::make_Get(object, name), op_token, rhs); return Expr::make_Set(object, name, bin); } - else if (std::holds_alternative::Index>(_match_114._data)) { - auto& _v = std::get::Index>(_match_114._data); + else if (std::holds_alternative::Index>(_match_117._data)) { + auto& _v = std::get::Index>(_match_117._data); auto& object = *_v.object; auto& bracket = _v.bracket; auto& index = *_v.index; @@ -6174,9 +6252,9 @@ struct Parser { Expr call() { Expr expr = (*this).primary(); { - const auto& _match_115 = expr; - if (std::holds_alternative::Variable>(_match_115._data)) { - auto& _v = std::get::Variable>(_match_115._data); + const auto& _match_118 = expr; + if (std::holds_alternative::Variable>(_match_118._data)) { + auto& _v = std::get::Variable>(_match_118._data); auto& tok = _v.name; if ((*this).check(TK_LEFT_BRACKET)) { int64_t save_pos = this->current; @@ -6258,9 +6336,9 @@ struct Parser { Expr finish_call(const Expr& callee) { { - const auto& _match_116 = callee; - if (std::holds_alternative::Variable>(_match_116._data)) { - auto& _v = std::get::Variable>(_match_116._data); + const auto& _match_119 = callee; + if (std::holds_alternative::Variable>(_match_119._data)) { + auto& _v = std::get::Variable>(_match_119._data); auto& name = _v.name; if ((name.lexeme == std::string("cast"))) { Expr expr = (*this).expression(); @@ -6281,9 +6359,9 @@ struct Parser { (*this).match_any(std::vector{TK_REF, TK_REF_MUT}); Expr arg_expr = (*this).expression(); { - const auto& _match_117 = arg_expr; - if (std::holds_alternative::Assign>(_match_117._data)) { - auto& _v = std::get::Assign>(_match_117._data); + const auto& _match_120 = arg_expr; + if (std::holds_alternative::Assign>(_match_120._data)) { + auto& _v = std::get::Assign>(_match_120._data); auto& aname = _v.name; auto& avalue = *_v.value; arg_names.push_back(aname.lexeme); @@ -6303,9 +6381,9 @@ struct Parser { (*this).match_any(std::vector{TK_REF, TK_REF_MUT}); arg_expr = (*this).expression(); { - const auto& _match_118 = arg_expr; - if (std::holds_alternative::Assign>(_match_118._data)) { - auto& _v = std::get::Assign>(_match_118._data); + const auto& _match_121 = arg_expr; + if (std::holds_alternative::Assign>(_match_121._data)) { + auto& _v = std::get::Assign>(_match_121._data); auto& aname = _v.name; auto& avalue = *_v.value; arg_names.push_back(aname.lexeme); @@ -6854,11 +6932,11 @@ struct Parser { std::vector old_fnames = {}; bool is_unit_type = false; { - const auto& _match_119 = vtype; - if (std::holds_alternative::NullType>(_match_119._data)) { + const auto& _match_122 = vtype; + if (std::holds_alternative::NullType>(_match_122._data)) { is_unit_type = true; } - else if (std::holds_alternative::Void>(_match_119._data)) { + else if (std::holds_alternative::Void>(_match_122._data)) { is_unit_type = true; } else { From cdb1d2b7b3b0e6f90e4a8b4b82a7ad1c9be06286 Mon Sep 17 00:00:00 2001 From: Raumberg Date: Tue, 17 Feb 2026 17:45:23 +0300 Subject: [PATCH 17/19] docs: updated documentation, referenced type casting, multiline expressions, added default parameters, named parameters, trailing commas, also method chaining --- DOCUMENTATION.md | 285 ++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 244 insertions(+), 41 deletions(-) diff --git a/DOCUMENTATION.md b/DOCUMENTATION.md index 28683ba..ae2d4bf 100644 --- a/DOCUMENTATION.md +++ b/DOCUMENTATION.md @@ -12,20 +12,22 @@ 8. [Enums and Pattern Matching](#enums-and-pattern-matching) 9. [Generics](#generics) 10. [References and Ownership](#references-and-ownership) -11. [Lambdas](#lambdas) -12. [Collections](#collections) -13. [Bytes](#bytes) -14. [Strings](#strings) -15. [Error Handling](#error-handling) -16. [Imports and Modules](#imports-and-modules) -17. [Operator Overloading](#operator-overloading) -18. [Compile-Time Evaluation](#compile-time-evaluation) -19. [FFI (Foreign Function Interface)](#ffi-foreign-function-interface) -20. [Standard Library](#standard-library) -21. [Networking](#networking) -22. [Threading](#threading) -23. [Extension Methods](#extension-methods) -24. [Compiler and Bootstrap](#compiler-and-bootstrap) +11. [Type Casting](#type-casting) +12. [Lambdas](#lambdas) +13. [Collections](#collections) +14. [Bytes](#bytes) +15. [Strings](#strings) +16. [Multiline Expressions](#multiline-expressions) +17. [Error Handling](#error-handling) +18. [Imports and Modules](#imports-and-modules) +19. [Operator Overloading](#operator-overloading) +20. [Compile-Time Evaluation](#compile-time-evaluation) +21. [FFI (Foreign Function Interface)](#ffi-foreign-function-interface) +22. [Standard Library](#standard-library) +23. [Networking](#networking) +24. [Threading](#threading) +25. [Extension Methods](#extension-methods) +26. [Compiler and Bootstrap](#compiler-and-bootstrap) --- @@ -152,6 +154,66 @@ void fn say_hello(): print("hello") ``` +### Default Parameters + +Parameters can have default values. Parameters with defaults must come after required parameters: + +```lavina +string fn greet(string name, string greeting = "Hello"): + return "${greeting}, ${name}!" + +greet("Alice") // "Hello, Alice!" +greet("Alice", "Hi") // "Hi, Alice!" +``` + +Multiple defaults: + +```lavina +void fn connect(string host, int port = 8080, bool ssl = false): + // host is required, port and ssl are optional + pass + +connect("localhost") // port=8080, ssl=false +connect("localhost", 443) // ssl=false +connect("localhost", 443, true) // all specified +``` + +### Named Arguments + +At the call site, arguments can be passed by name using `name = value` syntax. Named arguments can appear in any order, but must come after all positional arguments: + +```lavina +connect("localhost", ssl = true, port = 443) +connect("localhost", port = 9000) +``` + +Named arguments work with both functions and struct constructors (see [Structs](#structs)). + +### Trailing Commas + +Trailing commas are allowed in function parameters, function calls, array literals, and hashmap literals: + +```lavina +void fn example( + int x, + int y, + int z, +): + pass + +example( + 1, + 2, + 3, +) + +auto items = [ + "first", + "second", + "third", +] +``` + ### Recursion ```lavina @@ -302,6 +364,31 @@ auto p = Point(1.0, 2.0) print(p.x) // 1.0 ``` +### Constructor with Default Parameters + +Struct constructors support default parameter values and named arguments: + +```lavina +struct Config: + string host + int port + bool debug + + constructor( + string host = "localhost", + int port = 8080, + bool debug = false, + ): + this.host = host + this.port = port + this.debug = debug + +auto c1 = Config() // all defaults +auto c2 = Config("0.0.0.0", 443) // debug=false +auto c3 = Config(port = 9000, debug = true) // named args, host="localhost" +auto c4 = Config("myhost", port = 3000) // mixed positional + named +``` + ### Struct Methods ```lavina @@ -535,6 +622,49 @@ auto moved = own data // data is moved into moved --- +## Type Casting + +The `as` keyword performs postfix type casting. It uses smart conversions based on the source and target types: + +```lavina +// String ↔ numeric conversions +string s = "42" +int n = s as int // string → int (parses the string) +float f = s as float // string → float + +int x = 100 +string sx = x as string // int → string ("100") + +float pi = 3.14 +string sp = pi as string // float → string +int ip = pi as int // float → int (truncates) +``` + +### Sized Integer Casts + +For FFI interop, cast between integer sizes: + +```lavina +int big = 1000 +int32 small = big as int32 // int → int32 +int16 tiny = big as int16 // int → int16 +usize sz = big as usize // int → usize + +int back = small as int // int32 → int +``` + +### Casting from Expressions + +The `as` keyword works on any expression, including method calls and indexing: + +```lavina +vector[string] args = os::args() +int port = args.at(1) as int // method call result → int +string first = args[0] as string // indexing result (already string) +``` + +--- + ## Lambdas ### Expression Lambdas @@ -768,13 +898,46 @@ parts.join(", ") // "a, b, c" --- +## Multiline Expressions + +Inside square brackets `[]` and curly braces `{}`, indentation is ignored. This allows writing long expressions across multiple lines without worrying about Lavina's indentation rules: + +```lavina +auto config = { + "host": "localhost", + "port": 8080, + "debug": true, +} + +auto matrix = [ + [1, 0, 0], + [0, 1, 0], + [0, 0, 1], +] +``` + +This also works for multiline function calls when combined with trailing commas: + +```lavina +auto result = build_request( + auth_uuid, + "example.com", + 443, + payload, +) +``` + +**Note**: Parentheses `()` do **not** suppress indentation — this is intentional because block lambdas use `(params):` followed by indented bodies, which require indentation tracking. + +--- + ## Error Handling ### Try / Catch ```lavina try: - auto data = fs_read("file.txt") + auto data = fs::read("file.txt") process(data) catch err: print("Error: ${err.what()}") @@ -988,14 +1151,17 @@ extern "httplib.h": Embed raw C++ code as an escape hatch: ```lavina -int fn str_to_int(string s): +string fn to_lower(string s): + string result = s cpp { - return std::stoll(s); + std::transform(result.begin(), result.end(), result.begin(), ::tolower); } - return 0 + return result ``` -The C++ code has access to all variables in scope. The `return 0` after the `cpp` block is needed to satisfy the Lavina type checker. +The C++ code has access to all variables in scope. The `return result` after the `cpp` block is needed to satisfy the Lavina type checker. + +**Note**: For string-to-number conversions, prefer `as int` or `as float` instead of cpp blocks (see [Type Casting](#type-casting)). --- @@ -1010,36 +1176,58 @@ The C++ code has access to all variables in scope. The `return 0` after the `cpp ### Filesystem -| Function | Description | -|----------|-------------| -| `fs_read(path)` | Read file contents as string. Throws on error. | -| `fs_write(path, content)` | Write string to file. Throws on error. | -| `fs_append(path, content)` | Append string to file. | -| `fs_exists(path)` | Check if file exists. Returns `bool`. | -| `fs_remove(path)` | Delete a file. Returns `bool`. | -| `fs_is_dir(path)` | Check if path is a directory. Returns `bool`. | -| `fs_listdir(path)` | List directory entries. Returns `vector[string]`. | -| `fs_read_lines(path)` | Read file as vector of lines. | +Use `import std::fs` or the built-in functions directly: + +| Module Function | Built-in | Description | +|----------------|----------|-------------| +| `fs::read(path)` | `__fs_read(path)` | Read file contents as string. | +| `fs::write(path, content)` | `__fs_write(path, content)` | Write string to file. | +| `fs::append(path, content)` | `__fs_append(path, content)` | Append string to file. | +| `fs::exists(path)` | `__fs_exists(path)` | Check if file exists. | +| `fs::remove(path)` | `__fs_remove(path)` | Delete a file. | +| `fs::is_dir(path)` | `__fs_is_dir(path)` | Check if path is a directory. | +| `fs::list_dir(path)` | `__fs_listdir(path)` | List directory entries. | +| `fs::read_lines(path)` | `__fs_read_lines(path)` | Read file as vector of lines. | + +```lavina +import std::fs + +string content = fs::read("config.txt") +fs::write("output.txt", "hello") +auto entries = fs::list_dir(".") +``` ### OS -| Function | Description | -|----------|-------------| -| `os_exec(cmd)` | Execute shell command. Returns exit code (`int`). | -| `os_args()` | Get command-line arguments as `vector[string]`. | -| `os_env(name)` | Get environment variable. Returns `""` if not set. | -| `os_clock()` | Milliseconds since epoch (`int`). | -| `os_sleep(ms)` | Sleep for given milliseconds. | -| `os_cwd()` | Get current working directory. | -| `exit(code)` | Exit the process with given code. | +Use `import std::os` or the built-in functions directly: + +| Module Function | Built-in | Description | +|----------------|----------|-------------| +| `os::args()` | `__os_args()` | Get command-line arguments as `vector[string]`. | +| `os::exec(cmd)` | `__os_exec(cmd)` | Execute shell command. Returns exit code. | +| `os::env(name)` | `__os_env(name)` | Get environment variable. | +| `os::time_ms()` | `__os_clock()` | Milliseconds since epoch. | +| `os::sleep(ms)` | `__os_sleep(ms)` | Sleep for given milliseconds. | +| `os::cwd()` | `__os_cwd()` | Get current working directory. | +| `exit(code)` | `exit(code)` | Exit the process with given code. | + +```lavina +import std::os + +auto args = os::args() +string cwd = os::cwd() +``` ### Conversion | Function | Description | |----------|-------------| | `to_string(value)` | Convert `int`, `float`, `bool` to `string`. | -| `to_int(value)` | Parse string to `int`. | -| `to_float(value)` | Parse string to `float`. | +| `value as int` | Parse string to `int`, or convert float to int. | +| `value as float` | Parse string to `float`, or convert int to float. | +| `value as string` | Convert int, float, bool to `string`. | + +See [Type Casting](#type-casting) for full details on the `as` keyword. ### Utility @@ -1363,6 +1551,21 @@ string greeting = "hello" print(greeting.shout()) // hello! ``` +### Method Chaining + +Extend methods support chaining — you can call one extend method on the result of another: + +```lavina +import std::collections + +vector[int] nums = [1, 2, 3, 4, 5] + +// Chain multiple operations +int result = nums.map((int x) => x * x).filter((int x) => x > 5).reduce((int acc, int x) => acc + x, 0) + +auto first_three_doubled = nums.map((int x) => x * 2).take(3) +``` + Extend methods work on: `vector`, `hashmap`, `hashset`, `string`, `bytes`, and custom types (structs/enums by name). Use `this` inside extend methods to refer to the object. Built-in methods (`.push()`, `.sort()`, etc.) take priority over extend methods. @@ -1414,7 +1617,7 @@ The compiler maintains C++ snapshots in `stages/`. Each snapshot is a complete s make bootstrap # Build from latest stage, verify fixed point make snapshot # Save new stage after codegen changes make evolve # Handle codegen output format changes (2-pass) -make test # Run test suite (42 tests) +make test # Run test suite (45 tests) ``` **Fixed point verification**: The bootstrap compiles `src/main.lv` twice — once with the previous stage and once with the newly built compiler. The outputs must be identical, proving the compiler correctly compiles itself. From 4ca342268207bce34b9a6d476c9606c9d78bdb1c Mon Sep 17 00:00:00 2001 From: Raumberg Date: Tue, 17 Feb 2026 20:08:53 +0300 Subject: [PATCH 18/19] src: refactoring, new type_utils.lv file --- src/checker.lv | 134 +-- src/codegen.lv | 396 +++---- src/parser.lv | 86 +- src/type_utils.lv | 162 +++ stages/stage-latest.cpp | 2170 ++++++++++++++++++--------------------- 5 files changed, 1330 insertions(+), 1618 deletions(-) create mode 100644 src/type_utils.lv diff --git a/src/checker.lv b/src/checker.lv index f6c87dc..ef58769 100644 --- a/src/checker.lv +++ b/src/checker.lv @@ -1,4 +1,4 @@ -import ast +import type_utils // ═══════════════════════════════════════════════════════════════ // Lavina Bootstrap Compiler — Type Checker @@ -49,9 +49,6 @@ class Checker: void fn error(ref string msg, ref Token t): this.errors.push("[${t.line}:${t.col}] Error: ${msg}") - void fn error_msg(ref string msg): - this.errors.push("Error: ${msg}") - void fn warn(ref string msg, ref Token t): this.warnings.push("[${t.line}:${t.col}] Warning: ${msg}") @@ -163,58 +160,18 @@ class Checker: _: pass // Same type check - string e = this.type_name(expected) - string a = this.type_name(actual) + string e = type_to_display(expected) + string a = type_to_display(actual) if e == a: return true // Integer family compatibility - bool e_is_int = false - bool a_is_int = false - match expected: - Int(): - e_is_int = true - Int8(): - e_is_int = true - Int16(): - e_is_int = true - Int32(): - e_is_int = true - USize(): - e_is_int = true - _: - pass - match actual: - Int(): - a_is_int = true - Int8(): - a_is_int = true - Int16(): - a_is_int = true - Int32(): - a_is_int = true - USize(): - a_is_int = true - _: - pass + bool e_is_int = is_integer_type(expected) + bool a_is_int = is_integer_type(actual) if e_is_int and a_is_int: return true // Float family compatibility - bool e_is_float = false - bool a_is_float = false - match expected: - Float(): - e_is_float = true - Float32(): - e_is_float = true - _: - pass - match actual: - Float(): - a_is_float = true - Float32(): - a_is_float = true - _: - pass + bool e_is_float = is_float_type(expected) + bool a_is_float = is_float_type(actual) if e_is_float and a_is_float: return true // Float accepts Int @@ -258,53 +215,6 @@ class Checker: pass return false - string fn type_name(ref TypeNode t): - match t: - Int(): - return "int" - Float(): - return "float" - Str(): - return "string" - Bool(): - return "bool" - Void(): - return "void" - Auto(): - return "auto" - Dynamic(): - return "dynamic" - NullType(): - return "null" - Custom(name, _): - return name - Array(inner): - return "vector[${this.type_name(inner)}]" - HashSet(inner): - return "set[${this.type_name(inner)}]" - HashMap(k, v): - return "map[${this.type_name(k)}, ${this.type_name(v)}]" - Nullable(inner): - return "${this.type_name(inner)}?" - Int8(): - return "int8" - Int16(): - return "int16" - Int32(): - return "int32" - Float32(): - return "float32" - USize(): - return "usize" - CString(): - return "cstring" - Ptr(inner): - return "ptr[${this.type_name(inner)}]" - Bytes(): - return "bytes" - _: - return "unknown" - // ── Type inference ───────────────────────────────────────── TypeNode fn infer_type(ref Expr e): @@ -415,17 +325,6 @@ class Checker: _: return TypeNode::Auto() - // ── Enum helpers ─────────────────────────────────────────── - - string fn find_enum_for_variant(ref string variant_name): - vector[string] keys = this.known_enums.keys() - for ref key in keys: - vector[EnumVariantNode] variants = this.known_enums[key] - for ref v in variants: - if v.name.lexeme == variant_name: - return key - return "" - // ── Statement checking ───────────────────────────────────── void fn register_builtins(): @@ -496,7 +395,7 @@ class Checker: pass _: if not this.types_compatible(var_type, init_type): - this.error("Cannot assign ${this.type_name(init_type)} to ${this.type_name(var_type)}", name) + this.error("Cannot assign ${type_to_display(init_type)} to ${type_to_display(var_type)}", name) this.declare(name.lexeme, var_type, "var", is_ref, true, name) Const(name, const_type, value, visibility, comptime_mode): this.check_expr(value) @@ -514,7 +413,7 @@ class Checker: _: auto val_type = this.infer_type(value) if not this.types_compatible(this.current_return_type, val_type): - this.error("Return type mismatch: expected ${this.type_name(this.current_return_type)}, got ${this.type_name(val_type)}", keyword) + this.error("Return type mismatch: expected ${type_to_display(this.current_return_type)}, got ${type_to_display(val_type)}", keyword) If(condition, then_branch, else_branch): this.check_expr(condition) this.check_stmt(then_branch) @@ -583,12 +482,15 @@ class Checker: _: pass + void fn declare_params(ref vector[Param] params): + for ref p in params: + this.declare(p.name.lexeme, p.param_type, "param", p.is_ref, true, p.name) + void fn check_function(ref Token name, ref vector[Param] params, ref TypeNode return_type, ref vector[Stmt] body): auto saved_return = this.current_return_type this.current_return_type = return_type this.push_scope() - for ref p in params: - this.declare(p.name.lexeme, p.param_type, "param", p.is_ref, true, p.name) + this.declare_params(params) for ref s in body: this.check_stmt(s) this.pop_scope() @@ -682,14 +584,12 @@ class Checker: this.check_expr(end) Lambda(params, body): this.push_scope() - for ref p in params: - this.declare(p.name.lexeme, p.param_type, "param", p.is_ref, true, p.name) + this.declare_params(params) this.check_expr(body) this.pop_scope() BlockLambda(params, body_id): this.push_scope() - for ref p in params: - this.declare(p.name.lexeme, p.param_type, "param", p.is_ref, true, p.name) + this.declare_params(params) this.pop_scope() Own(expr): this.check_expr(expr) @@ -738,7 +638,7 @@ class Checker: has_wildcard = true else: // Check variant exists - string enum_name = this.find_enum_for_variant(arm.pattern_name) + string enum_name = find_enum_for_variant(this.known_enums,arm.pattern_name) if enum_name == "": // Not a known variant — might be a binding, skip pass diff --git a/src/codegen.lv b/src/codegen.lv index dc253b1..ffcc856 100644 --- a/src/codegen.lv +++ b/src/codegen.lv @@ -1,4 +1,4 @@ -import ast +import type_utils // ═══════════════════════════════════════════════════════════════ // Lavina Bootstrap Compiler — C++ Code Generator @@ -79,21 +79,6 @@ class CppCodegen: if not this.is_dynamic_var(name): this.dynamic_vars.push(name) - string fn find_enum_for_variant(ref string variant_name): - string found = "" - int count = 0 - vector[string] keys = this.known_enums.keys() - for ref key in keys: - vector[EnumVariantNode] variants = this.known_enums[key] - for ref v in variants: - if v.name.lexeme == variant_name: - if count == 0: - found = key - count += 1 - if count == 1: - return found - return "" - EnumVariantNode fn get_variant_info(ref string enum_name, ref string variant_name): if this.known_enums.has(enum_name): vector[EnumVariantNode] variants = this.known_enums[enum_name] @@ -227,23 +212,8 @@ class CppCodegen: // ── Type emission ───────────────────────────────────────── string fn emit_type(ref TypeNode t): + // Thin wrapper over type_to_cpp: handles extern_type_names for Custom types match t: - Int(): - return "int64_t" - Float(): - return "double" - Str(): - return "std::string" - Bool(): - return "bool" - Void(): - return "void" - Auto(): - return "auto" - Dynamic(): - return "std::any" - NullType(): - return "std::nullptr_t" Custom(name, type_args): if type_args.len() > 0: vector[string] ta = [] @@ -261,24 +231,10 @@ class CppCodegen: return "std::unordered_map<${this.emit_type(key_type)}, ${this.emit_type(value_type)}>" Nullable(inner): return "std::optional<${this.emit_type(inner)}>" - Int8(): - return "int8_t" - Int16(): - return "int16_t" - Int32(): - return "int32_t" - Float32(): - return "float" - USize(): - return "size_t" - CString(): - return "const char*" Ptr(inner): return "${this.emit_type(inner)}*" - Bytes(): - return "std::vector" _: - return "auto" + return type_to_cpp(t) string fn token_to_cpp_op(Token t): if t.token_type == TK_PLUS: @@ -336,6 +292,60 @@ class CppCodegen: // ── Expression emission ─────────────────────────────────── + string fn emit_cast(ref Expr expr, ref TypeNode target_type, bool m): + string ex = this.emit_expr(expr, m) + string t = this.emit_type(target_type) + if this.is_dynamic_expression(expr): + return "std::any_cast<${t}>(${ex})" + TypeNode src = this.infer_source_type(expr) + // string -> int/float + if is_string_type(src): + if is_integer_type(target_type): + match target_type: + Int(): + return "__str_to_int(${ex})" + _: + return "static_cast<${t}>(__str_to_int(${ex}))" + if is_float_type(target_type): + match target_type: + Float(): + return "__str_to_float(${ex})" + _: + return "static_cast<${t}>(__str_to_float(${ex}))" + if is_bytes_type(target_type): + return "__bytes_from_string(${ex})" + // numeric/bool -> string + if is_string_type(target_type): + if is_integer_type(src) or is_float_type(src): + return "to_string(${ex})" + match src: + Bool(): + return "to_string(${ex})" + _: + pass + // bytes -> string + if is_bytes_type(src) and is_string_type(target_type): + return "__bytes_to_string(${ex})" + // string -> bytes + if is_string_type(src) and is_bytes_type(target_type): + return "__bytes_from_string(${ex})" + return "static_cast<${t}>(${ex})" + + string fn emit_member_prefix(ref Expr object, bool m): + if this.in_extend: + match object: + This(kw): + return "self." + _: + return "${this.emit_expr(object, m)}." + if m: + match object: + This(kw): + return "this->" + _: + return "${this.emit_expr(object, m)}." + return "${this.emit_expr(object, m)}." + string fn emit_expr(ref Expr e, bool m): match e: None(): @@ -383,33 +393,9 @@ class CppCodegen: entries.push("{${this.emit_expr(keys[i], m)}, ${this.emit_expr(values[i], m)}}") return "{{${entries.join(", ")}}}" Get(object, name): - if this.in_extend: - match object: - This(kw): - return "self.${name.lexeme}" - _: - return "${this.emit_expr(object, m)}.${name.lexeme}" - if m: - match object: - This(kw): - return "this->${name.lexeme}" - _: - return "${this.emit_expr(object, m)}.${name.lexeme}" - return "${this.emit_expr(object, m)}.${name.lexeme}" + return "${this.emit_member_prefix(object, m)}${name.lexeme}" Set(object, name, value): - if this.in_extend: - match object: - This(kw): - return "self.${name.lexeme} = ${this.emit_expr(value, m)}" - _: - return "${this.emit_expr(object, m)}.${name.lexeme} = ${this.emit_expr(value, m)}" - if m: - match object: - This(kw): - return "this->${name.lexeme} = ${this.emit_expr(value, m)}" - _: - return "${this.emit_expr(object, m)}.${name.lexeme} = ${this.emit_expr(value, m)}" - return "${this.emit_expr(object, m)}.${name.lexeme} = ${this.emit_expr(value, m)}" + return "${this.emit_member_prefix(object, m)}${name.lexeme} = ${this.emit_expr(value, m)}" StaticGet(object, name): if m: match object: @@ -423,44 +409,7 @@ class CppCodegen: return "self" return "(*this)" Cast(expr, target_type): - string ex = this.emit_expr(expr, m) - string t = this.emit_type(target_type) - if this.is_dynamic_expression(expr): - return "std::any_cast<${t}>(${ex})" - TypeNode src = this.infer_source_type(expr) - // string -> int/float - if this.is_string_type(src): - if this.is_integer_type(target_type): - match target_type: - Int(): - return "__str_to_int(${ex})" - _: - return "static_cast<${t}>(__str_to_int(${ex}))" - if this.is_float_type(target_type): - match target_type: - Float(): - return "__str_to_float(${ex})" - _: - return "static_cast<${t}>(__str_to_float(${ex}))" - if this.is_bytes_type(target_type): - return "__bytes_from_string(${ex})" - // numeric/bool -> string - if this.is_string_type(target_type): - if this.is_integer_type(src) or this.is_float_type(src): - return "to_string(${ex})" - match src: - Bool(): - return "to_string(${ex})" - _: - pass - // bytes -> string - if this.is_bytes_type(src) and this.is_string_type(target_type): - return "__bytes_to_string(${ex})" - // string -> bytes - if this.is_string_type(src) and this.is_bytes_type(target_type): - return "__bytes_from_string(${ex})" - // bytes <-> string (already handled above but keep for safety) - return "static_cast<${t}>(${ex})" + return this.emit_cast(expr, target_type, m) Throw(expr): return "throw std::runtime_error(${this.emit_expr(expr, m)})" Lambda(params, body): @@ -550,13 +499,29 @@ class CppCodegen: pi = pi + 1 return result + vector[string] fn emit_args(ref vector[Expr] args, bool m): + vector[string] result = [] + for ref a in args: + result.push(this.emit_expr(a, m)) + return result + + void fn apply_extern_wrapping(ref! vector[string] arg_strs, ref vector[Expr] args, ref string fn_name): + if this.extern_fn_params.has(fn_name): + vector[Param] eparams = this.extern_fn_params[fn_name] + for i in 0..arg_strs.len(): + if i < eparams.len(): + arg_strs[i] = this.wrap_extern_arg(arg_strs[i], args[i], eparams[i].param_type) + + string fn resolve_fn_name(ref string name): + if this.extern_fn_names.has(name): + return this.extern_fn_names[name] + return name + string fn emit_call_expr(ref Expr callee, ref vector[Expr] args, ref vector[string] arg_names, bool in_method): match callee: Get(object, name): string obj = this.emit_expr(object, in_method) - vector[string] arg_strs = [] - for ref a in args: - arg_strs.push(this.emit_expr(a, in_method)) + vector[string] arg_strs = this.emit_args(args, in_method) string remapped = this.try_remap_method(obj, name.lexeme, arg_strs) if remapped != "": return remapped @@ -566,9 +531,7 @@ class CppCodegen: return "${obj}.${name.lexeme}(${arg_strs.join(", ")})" StaticGet(object, name): string obj = this.emit_expr(object, in_method) - vector[string] arg_strs = [] - for ref a in args: - arg_strs.push(this.emit_expr(a, in_method)) + vector[string] arg_strs = this.emit_args(args, in_method) match object: Variable(tok): if this.is_known_enum(tok.lexeme): @@ -577,44 +540,23 @@ class CppCodegen: pass return "${obj}::${name.lexeme}(${arg_strs.join(", ")})" Variable(tok): - // Check if we have param info for named arg / default resolution + // Resolve named args / defaults if param info available if this.fn_params.has(tok.lexeme): vector[Param] fparams = this.fn_params[tok.lexeme] vector[Expr] fdefaults = [] if this.fn_defaults.has(tok.lexeme): fdefaults = this.fn_defaults[tok.lexeme] vector[string] arg_strs = this.resolve_named_args(args, arg_names, fparams, fdefaults, in_method) - if this.extern_fn_params.has(tok.lexeme): - vector[Param] eparams = this.extern_fn_params[tok.lexeme] - for i in 0..arg_strs.len(): - if i < eparams.len(): - arg_strs[i] = this.wrap_extern_arg(arg_strs[i], args[i], eparams[i].param_type) - string fn_name = tok.lexeme - if this.extern_fn_names.has(tok.lexeme): - fn_name = this.extern_fn_names[tok.lexeme] - return "${fn_name}(${arg_strs.join(", ")})" - vector[string] arg_strs = [] - for ref a in args: - arg_strs.push(this.emit_expr(a, in_method)) + this.apply_extern_wrapping(arg_strs, args, tok.lexeme) + return "${this.resolve_fn_name(tok.lexeme)}(${arg_strs.join(", ")})" + vector[string] arg_strs = this.emit_args(args, in_method) if tok.lexeme == "exit": return "lv_exit(${arg_strs.join(", ")})" - if this.extern_fn_params.has(tok.lexeme): - vector[Param] eparams = this.extern_fn_params[tok.lexeme] - for i in 0..arg_strs.len(): - if i < eparams.len(): - arg_strs[i] = this.wrap_extern_arg(arg_strs[i], args[i], eparams[i].param_type) - string fn_name = tok.lexeme - if this.extern_fn_names.has(tok.lexeme): - fn_name = this.extern_fn_names[tok.lexeme] - return "${fn_name}(${arg_strs.join(", ")})" - if this.extern_fn_names.has(tok.lexeme): - return "${this.extern_fn_names[tok.lexeme]}(${arg_strs.join(", ")})" - return "${tok.lexeme}(${arg_strs.join(", ")})" + this.apply_extern_wrapping(arg_strs, args, tok.lexeme) + return "${this.resolve_fn_name(tok.lexeme)}(${arg_strs.join(", ")})" _: string func = this.emit_expr(callee, in_method) - vector[string] arg_strs = [] - for ref a in args: - arg_strs.push(this.emit_expr(a, in_method)) + vector[string] arg_strs = this.emit_args(args, in_method) return "${func}(${arg_strs.join(", ")})" string fn try_remap_method(ref string obj, ref string method, ref vector[string] args): @@ -834,44 +776,6 @@ class CppCodegen: _: return TypeNode::Auto() - bool fn is_integer_type(ref TypeNode t): - match t: - Int(): - return true - Int8(): - return true - Int16(): - return true - Int32(): - return true - USize(): - return true - _: - return false - - bool fn is_float_type(ref TypeNode t): - match t: - Float(): - return true - Float32(): - return true - _: - return false - - bool fn is_string_type(ref TypeNode t): - match t: - Str(): - return true - _: - return false - - bool fn is_bytes_type(ref TypeNode t): - match t: - Bytes(): - return true - _: - return false - string fn try_extend_method(ref Expr object, ref string method, ref string obj, ref vector[string] args): string type_cat = this.get_type_category(object) if type_cat != "" and this.extend_methods.has(type_cat): @@ -1214,6 +1118,21 @@ class CppCodegen: _: pass + bool fn has_method_named(ref vector[Stmt] methods, string name): + for ref m in methods: + match m: + Function(mname, mparams, mret, mbody, mi, mc, ms, mv, mtp, m_defs): + if mname.lexeme == name: + return true + _: + pass + return false + + void fn emit_print_ops(ref string full_name, ref string tmpl_line, ref string to_str_expr): + this.output +="${tmpl_line}void print(const ${full_name}& _v) { std::cout << ${to_str_expr} << std::endl; }\n" + this.output +="${tmpl_line}std::string operator+(const std::string& _s, const ${full_name}& _v) { return _s + ${to_str_expr}; }\n" + this.output +="${tmpl_line}std::string operator+(const ${full_name}& _v, const std::string& _s) { return ${to_str_expr} + _s; }\n\n" + void fn emit_class(ref Token name, ref vector[Stmt] body, ref vector[string] type_params): vector[Stmt] init_body = [] vector[Param] init_params = [] @@ -1284,23 +1203,12 @@ class CppCodegen: this.output +="${this.indent()}};\n\n" // Auto-generate print/concat if to_string method exists - bool has_to_string = false - for ref m in methods: - match m: - Function(mname, mparams, mret, mbody, mi, mc, ms, mv, mtp, m_defs): - if mname.lexeme == "to_string": - has_to_string = true - _: - pass - if has_to_string: + if this.has_method_named(methods, "to_string"): string tmpl_line = this.template_prefix(type_params) string type_suffix = "" if type_params.len() > 0: type_suffix = "<${type_params.join(", ")}>" - string full_name = "${name.lexeme}${type_suffix}" - this.output +="${tmpl_line}${this.indent()}void print(const ${full_name}& _v) { std::cout << _v.to_string() << std::endl; }\n" - this.output +="${tmpl_line}${this.indent()}std::string operator+(const std::string& _s, const ${full_name}& _v) { return _s + _v.to_string(); }\n" - this.output +="${tmpl_line}${this.indent()}std::string operator+(const ${full_name}& _v, const std::string& _s) { return _v.to_string() + _s; }\n\n" + this.emit_print_ops("${name.lexeme}${type_suffix}", tmpl_line, "_v.to_string()") string fn infer_expr_type(ref Expr e, ref vector[string] param_names, ref vector[string] param_types): match e: @@ -1435,14 +1343,13 @@ class CppCodegen: this.output +="${this.indent()}}\n" this.indent_level -= 1 this.output +="${this.indent()}};\n\n" + string full_type = "${enum_name}${type_suffix}" if has_to_string: - this.output +="${tmpl_line}void print(const ${enum_name}${type_suffix}& e) { std::cout << e.to_string() << std::endl; }\n" - this.output +="${tmpl_line}std::string operator+(const std::string& s, const ${enum_name}${type_suffix}& e) { return s + e.to_string(); }\n" - this.output +="${tmpl_line}std::string operator+(const ${enum_name}${type_suffix}& e, const std::string& s) { return e.to_string() + s; }\n\n" + this.emit_print_ops(full_type, tmpl_line, "_v.to_string()") else: - this.output +="${tmpl_line}void print(const ${enum_name}${type_suffix}& e) { std::cout << \"${enum_name}(\" << e._tag << \")\" << std::endl; }\n" - this.output +="${tmpl_line}std::string operator+(const std::string& s, const ${enum_name}${type_suffix}& e) { return s + e._tag; }\n" - this.output +="${tmpl_line}std::string operator+(const ${enum_name}${type_suffix}& e, const std::string& s) { return e._tag + s; }\n\n" + this.output +="${tmpl_line}void print(const ${full_type}& _v) { std::cout << \"${enum_name}(\" << _v._tag << \")\" << std::endl; }\n" + this.output +="${tmpl_line}std::string operator+(const std::string& _s, const ${full_type}& _v) { return _s + _v._tag; }\n" + this.output +="${tmpl_line}std::string operator+(const ${full_type}& _v, const std::string& _s) { return _v._tag + _s; }\n\n" void fn emit_enum(ref Token name, ref vector[EnumVariantNode] variants, ref vector[Stmt] methods, ref vector[string] type_params): string enum_name = name.lexeme @@ -1472,14 +1379,7 @@ class CppCodegen: this.emit_enum_makers(enum_name, variants, type_suffix) for ref m in methods: this.emit_class_method(name, m) - bool has_to_string = false - for ref m in methods: - match m: - Function(mname, mparams, mret, mbody, mi, mc, ms, mv, mtp, m_defs): - if mname.lexeme == "to_string": - has_to_string = true - _: - pass + bool has_to_string = this.has_method_named(methods, "to_string") this.emit_enum_operators(enum_name, type_suffix, tmpl_line, has_to_string) // ── Match emission ──────────────────────────────────────── @@ -1511,7 +1411,7 @@ class CppCodegen: if not first: keyword = "else if" first = false - string ename = this.find_enum_for_variant(arm.pattern_name) + string ename = find_enum_for_variant(this.known_enums,arm.pattern_name) if ename != "": this.output +="${this.indent()}${keyword} (std::holds_alternative::${arm.pattern_name}>(${temp}._data)) {\n" else: @@ -1619,16 +1519,7 @@ class CppCodegen: // ── Main entry point ────────────────────────────────────── - string fn generate(ref vector[Stmt] stmts): - // First pass: collect extern declarations from modules and main - for mi in 0..this.module_stmts.len(): - for ref stmt in this.module_stmts[mi]: - match stmt: - Extern(header, import_path, link_lib, types, functions): - this.emit_stmt(stmt, false) - this.output = "" - _: - pass + void fn collect_externs(ref vector[Stmt] stmts): for ref stmt in stmts: match stmt: Extern(header, import_path, link_lib, types, functions): @@ -1637,19 +1528,7 @@ class CppCodegen: _: pass - // Collect extend declarations from modules and main - for mi in 0..this.module_stmts.len(): - for ref stmt in this.module_stmts[mi]: - match stmt: - Extend(target, methods, visibility): - string key = target.lexeme - if not this.extend_methods.has(key): - vector[Stmt] empty = [] - this.extend_methods[key] = empty - for ref m in methods: - this.extend_methods[key].push(m) - _: - pass + void fn collect_extends(ref vector[Stmt] stmts): for ref stmt in stmts: match stmt: Extend(target, methods, visibility): @@ -1662,24 +1541,7 @@ class CppCodegen: _: pass - this.declarations = "#include \"lavina.h\"\n" - for ref inc in this.extern_includes: - this.declarations +="${inc}\n" - this.declarations +="\n" - - // Emit modules in dependency order - for mi in 0..this.module_stmts.len(): - this.emit_module(mi) - - // Emit extension methods as free functions - auto ext_keys = this.extend_methods.keys() - for ref ek in ext_keys: - vector[Stmt] methods = this.extend_methods[ek] - for ref m in methods: - this.emit_extend_method(ek, m) - this.declarations += this.output - this.output = "" - + void fn emit_main_stmts(ref vector[Stmt] stmts): for ref stmt in stmts: match stmt: Function(name, params, return_type, body, is_inline, comptime_mode, is_static, visibility, type_params, param_defaults): @@ -1707,4 +1569,34 @@ class CppCodegen: this.declarations += this.output this.output = "" + string fn generate(ref vector[Stmt] stmts): + // First pass: collect extern declarations from modules and main + for mi in 0..this.module_stmts.len(): + this.collect_externs(this.module_stmts[mi]) + this.collect_externs(stmts) + + // Collect extend declarations from modules and main + for mi in 0..this.module_stmts.len(): + this.collect_extends(this.module_stmts[mi]) + this.collect_extends(stmts) + + this.declarations = "#include \"lavina.h\"\n" + for ref inc in this.extern_includes: + this.declarations +="${inc}\n" + this.declarations +="\n" + + // Emit modules in dependency order + for mi in 0..this.module_stmts.len(): + this.emit_module(mi) + + // Emit extension methods as free functions + auto ext_keys = this.extend_methods.keys() + for ref ek in ext_keys: + vector[Stmt] methods = this.extend_methods[ek] + for ref m in methods: + this.emit_extend_method(ek, m) + this.declarations += this.output + this.output = "" + + this.emit_main_stmts(stmts) return this.declarations diff --git a/src/parser.lv b/src/parser.lv index bf725e0..82dc212 100644 --- a/src/parser.lv +++ b/src/parser.lv @@ -1,4 +1,4 @@ -import ast +import type_utils // ═══════════════════════════════════════════════════════════════ // Lavina Bootstrap Compiler — Parser @@ -232,48 +232,6 @@ class Parser: t = TypeNode::Nullable(t) return t - string fn type_to_string(ref TypeNode t): - match t: - Int(): - return "int64_t" - Float(): - return "double" - Str(): - return "std::string" - Bool(): - return "bool" - Void(): - return "void" - Auto(): - return "auto" - Custom(name, type_args): - if type_args.len() > 0: - vector[string] ta = [] - for ref a in type_args: - ta.push(this.type_to_string(a)) - return "${name}<${ta.join(", ")}>" - return name - Array(inner): - return "std::vector<${this.type_to_string(inner)}>" - Int8(): - return "int8_t" - Int16(): - return "int16_t" - Int32(): - return "int32_t" - Float32(): - return "float" - USize(): - return "size_t" - CString(): - return "const char*" - Ptr(inner): - return "${this.type_to_string(inner)}*" - Bytes(): - return "std::vector" - _: - return "auto" - // ── Expression parsing ────────────────────────────────── Expr fn expression(): @@ -391,12 +349,12 @@ class Parser: vector[string] type_strs = [] try: TypeNode first_t = this.parse_type() - type_strs.push(this.type_to_string(first_t)) + type_strs.push(type_to_cpp(first_t)) while this.match_any([TK_COMMA]): if this.check(TK_RIGHT_BRACKET): break TypeNode next_t = this.parse_type() - type_strs.push(this.type_to_string(next_t)) + type_strs.push(type_to_cpp(next_t)) if not this.check(TK_RIGHT_BRACKET): is_type_args = false catch e: @@ -555,6 +513,17 @@ class Parser: auto t = this.peek() throw "Expect expression. Got ${t.token_type} at ${t.line}:${t.col}" + vector[string] fn parse_type_params(): + vector[string] type_params = [] + if this.match_any([TK_LEFT_BRACKET]): + type_params.push(this.consume(TK_IDENTIFIER, "Expect type parameter name.").lexeme) + while this.match_any([TK_COMMA]): + if this.check(TK_RIGHT_BRACKET): + break + type_params.push(this.consume(TK_IDENTIFIER, "Expect type parameter name.").lexeme) + this.consume(TK_RIGHT_BRACKET, "Expect ']' after type parameters.") + return type_params + vector[Param] fn parse_param_list(): vector[Param] params = [] this.last_param_defaults = [] @@ -812,14 +781,7 @@ class Parser: this.consume(TK_FN, "Expect 'fn' keyword after return type.") name = this.consume(TK_IDENTIFIER, "Expect function name.") - vector[string] type_params = [] - if this.match_any([TK_LEFT_BRACKET]): - type_params.push(this.consume(TK_IDENTIFIER, "Expect type parameter name.").lexeme) - while this.match_any([TK_COMMA]): - if this.check(TK_RIGHT_BRACKET): - break - type_params.push(this.consume(TK_IDENTIFIER, "Expect type parameter name.").lexeme) - this.consume(TK_RIGHT_BRACKET, "Expect ']' after type parameters.") + vector[string] type_params = this.parse_type_params() this.consume(TK_LEFT_PAREN, "Expect '(' after function name.") vector[Param] params = this.parse_param_list() @@ -841,14 +803,7 @@ class Parser: Stmt fn struct_declaration(string visibility): auto name = this.consume(TK_IDENTIFIER, "Expect struct name.") - vector[string] type_params = [] - if this.match_any([TK_LEFT_BRACKET]): - type_params.push(this.consume(TK_IDENTIFIER, "Expect type parameter name.").lexeme) - while this.match_any([TK_COMMA]): - if this.check(TK_RIGHT_BRACKET): - break - type_params.push(this.consume(TK_IDENTIFIER, "Expect type parameter name.").lexeme) - this.consume(TK_RIGHT_BRACKET, "Expect ']' after type parameters.") + vector[string] type_params = this.parse_type_params() this.consume(TK_COLON, "Expect ':' after struct name.") auto old = this.in_class_body this.in_class_body = true @@ -858,14 +813,7 @@ class Parser: Stmt fn enum_declaration(string visibility): auto name = this.consume(TK_IDENTIFIER, "Expect enum name.") - vector[string] type_params = [] - if this.match_any([TK_LEFT_BRACKET]): - type_params.push(this.consume(TK_IDENTIFIER, "Expect type parameter name.").lexeme) - while this.match_any([TK_COMMA]): - if this.check(TK_RIGHT_BRACKET): - break - type_params.push(this.consume(TK_IDENTIFIER, "Expect type parameter name.").lexeme) - this.consume(TK_RIGHT_BRACKET, "Expect ']' after type parameters.") + vector[string] type_params = this.parse_type_params() this.consume(TK_COLON, "Expect ':' after enum name.") this.match_any([TK_NEWLINE]) diff --git a/src/type_utils.lv b/src/type_utils.lv new file mode 100644 index 0000000..f0dad0d --- /dev/null +++ b/src/type_utils.lv @@ -0,0 +1,162 @@ +// type_utils.lv — shared type predicates and helpers +import ast + +bool fn is_integer_type(ref TypeNode t): + match t: + Int(): + return true + Int8(): + return true + Int16(): + return true + Int32(): + return true + USize(): + return true + _: + return false + return false + +bool fn is_float_type(ref TypeNode t): + match t: + Float(): + return true + Float32(): + return true + _: + return false + return false + +bool fn is_string_type(ref TypeNode t): + match t: + Str(): + return true + _: + return false + return false + +bool fn is_bytes_type(ref TypeNode t): + match t: + Bytes(): + return true + _: + return false + return false + +// TypeNode → C++ type string +string fn type_to_cpp(ref TypeNode t): + match t: + Int(): + return "int64_t" + Float(): + return "double" + Str(): + return "std::string" + Bool(): + return "bool" + Void(): + return "void" + Auto(): + return "auto" + Dynamic(): + return "std::any" + NullType(): + return "std::nullptr_t" + Custom(name, type_args): + if type_args.len() > 0: + vector[string] ta = [] + for ref a in type_args: + ta.push(type_to_cpp(a)) + return "${name}<${ta.join(", ")}>" + return name + Array(inner): + return "std::vector<${type_to_cpp(inner)}>" + HashSet(inner): + return "std::unordered_set<${type_to_cpp(inner)}>" + HashMap(key_type, value_type): + return "std::unordered_map<${type_to_cpp(key_type)}, ${type_to_cpp(value_type)}>" + Nullable(inner): + return "std::optional<${type_to_cpp(inner)}>" + Int8(): + return "int8_t" + Int16(): + return "int16_t" + Int32(): + return "int32_t" + Float32(): + return "float" + USize(): + return "size_t" + CString(): + return "const char*" + Ptr(inner): + return "${type_to_cpp(inner)}*" + Bytes(): + return "std::vector" + _: + return "auto" + return "auto" + +// TypeNode → Lavina display string (for error messages) +string fn type_to_display(ref TypeNode t): + match t: + Int(): + return "int" + Float(): + return "float" + Str(): + return "string" + Bool(): + return "bool" + Void(): + return "void" + Auto(): + return "auto" + Dynamic(): + return "dynamic" + NullType(): + return "null" + Custom(name, _): + return name + Array(inner): + return "vector[${type_to_display(inner)}]" + HashSet(inner): + return "set[${type_to_display(inner)}]" + HashMap(k, v): + return "map[${type_to_display(k)}, ${type_to_display(v)}]" + Nullable(inner): + return "${type_to_display(inner)}?" + Int8(): + return "int8" + Int16(): + return "int16" + Int32(): + return "int32" + Float32(): + return "float32" + USize(): + return "usize" + CString(): + return "cstring" + Ptr(inner): + return "ptr[${type_to_display(inner)}]" + Bytes(): + return "bytes" + _: + return "unknown" + return "unknown" + +string fn find_enum_for_variant(ref! hashmap[string, vector[EnumVariantNode]] known_enums, ref string variant_name): + string found = "" + int count = 0 + vector[string] keys = known_enums.keys() + for ref key in keys: + vector[EnumVariantNode] variants = known_enums[key] + for ref v in variants: + if v.name.lexeme == variant_name: + if count == 0: + found = key + count += 1 + if count == 1: + return found + return "" diff --git a/stages/stage-latest.cpp b/stages/stage-latest.cpp index 3bc65f2..feeae0c 100644 --- a/stages/stage-latest.cpp +++ b/stages/stage-latest.cpp @@ -1080,9 +1080,9 @@ struct TypeNode { } }; -void print(const TypeNode& e) { std::cout << "TypeNode(" << e._tag << ")" << std::endl; } -std::string operator+(const std::string& s, const TypeNode& e) { return s + e._tag; } -std::string operator+(const TypeNode& e, const std::string& s) { return e._tag + s; } +void print(const TypeNode& _v) { std::cout << "TypeNode(" << _v._tag << ")" << std::endl; } +std::string operator+(const std::string& _s, const TypeNode& _v) { return _s + _v._tag; } +std::string operator+(const TypeNode& _v, const std::string& _s) { return _v._tag + _s; } struct Param { Token name; @@ -1160,9 +1160,9 @@ struct Expr { } }; -void print(const Expr& e) { std::cout << "Expr(" << e._tag << ")" << std::endl; } -std::string operator+(const std::string& s, const Expr& e) { return s + e._tag; } -std::string operator+(const Expr& e, const std::string& s) { return e._tag + s; } +void print(const Expr& _v) { std::cout << "Expr(" << _v._tag << ")" << std::endl; } +std::string operator+(const std::string& _s, const Expr& _v) { return _s + _v._tag; } +std::string operator+(const Expr& _v, const std::string& _s) { return _v._tag + _s; } struct ExternType { std::string lavina_name; @@ -1244,9 +1244,278 @@ struct Stmt { } }; -void print(const Stmt& e) { std::cout << "Stmt(" << e._tag << ")" << std::endl; } -std::string operator+(const std::string& s, const Stmt& e) { return s + e._tag; } -std::string operator+(const Stmt& e, const std::string& s) { return e._tag + s; } +void print(const Stmt& _v) { std::cout << "Stmt(" << _v._tag << ")" << std::endl; } +std::string operator+(const std::string& _s, const Stmt& _v) { return _s + _v._tag; } +std::string operator+(const Stmt& _v, const std::string& _s) { return _v._tag + _s; } + +bool is_integer_type(const TypeNode& t) { + { + const auto& _match_0 = t; + if (std::holds_alternative::Int>(_match_0._data)) { + return true; + } + else if (std::holds_alternative::Int8>(_match_0._data)) { + return true; + } + else if (std::holds_alternative::Int16>(_match_0._data)) { + return true; + } + else if (std::holds_alternative::Int32>(_match_0._data)) { + return true; + } + else if (std::holds_alternative::USize>(_match_0._data)) { + return true; + } + else { + return false; + } + } + return false; +} + +bool is_float_type(const TypeNode& t) { + { + const auto& _match_1 = t; + if (std::holds_alternative::Float>(_match_1._data)) { + return true; + } + else if (std::holds_alternative::Float32>(_match_1._data)) { + return true; + } + else { + return false; + } + } + return false; +} + +bool is_string_type(const TypeNode& t) { + { + const auto& _match_2 = t; + if (std::holds_alternative::Str>(_match_2._data)) { + return true; + } + else { + return false; + } + } + return false; +} + +bool is_bytes_type(const TypeNode& t) { + { + const auto& _match_3 = t; + if (std::holds_alternative::Bytes>(_match_3._data)) { + return true; + } + else { + return false; + } + } + return false; +} + +std::string type_to_cpp(const TypeNode& t) { + { + const auto& _match_4 = t; + if (std::holds_alternative::Int>(_match_4._data)) { + return std::string("int64_t"); + } + else if (std::holds_alternative::Float>(_match_4._data)) { + return std::string("double"); + } + else if (std::holds_alternative::Str>(_match_4._data)) { + return std::string("std::string"); + } + else if (std::holds_alternative::Bool>(_match_4._data)) { + return std::string("bool"); + } + else if (std::holds_alternative::Void>(_match_4._data)) { + return std::string("void"); + } + else if (std::holds_alternative::Auto>(_match_4._data)) { + return std::string("auto"); + } + else if (std::holds_alternative::Dynamic>(_match_4._data)) { + return std::string("std::any"); + } + else if (std::holds_alternative::NullType>(_match_4._data)) { + return std::string("std::nullptr_t"); + } + else if (std::holds_alternative::Custom>(_match_4._data)) { + auto& _v = std::get::Custom>(_match_4._data); + auto& name = _v.name; + auto& type_args = _v.type_args; + if ((static_cast(type_args.size()) > INT64_C(0))) { + std::vector ta = {}; + for (const auto& a : type_args) { + ta.push_back(type_to_cpp(a)); + } + return ((((std::string("") + (name)) + std::string("<")) + (lv_join(ta, std::string(", ")))) + std::string(">")); + } + return name; + } + else if (std::holds_alternative::Array>(_match_4._data)) { + auto& _v = std::get::Array>(_match_4._data); + auto& inner = *_v.inner; + return ((std::string("std::vector<") + (type_to_cpp(inner))) + std::string(">")); + } + else if (std::holds_alternative::HashSet>(_match_4._data)) { + auto& _v = std::get::HashSet>(_match_4._data); + auto& inner = *_v.inner; + return ((std::string("std::unordered_set<") + (type_to_cpp(inner))) + std::string(">")); + } + else if (std::holds_alternative::HashMap>(_match_4._data)) { + auto& _v = std::get::HashMap>(_match_4._data); + auto& key_type = *_v.key_type; + auto& value_type = *_v.value_type; + return ((((std::string("std::unordered_map<") + (type_to_cpp(key_type))) + std::string(", ")) + (type_to_cpp(value_type))) + std::string(">")); + } + else if (std::holds_alternative::Nullable>(_match_4._data)) { + auto& _v = std::get::Nullable>(_match_4._data); + auto& inner = *_v.inner; + return ((std::string("std::optional<") + (type_to_cpp(inner))) + std::string(">")); + } + else if (std::holds_alternative::Int8>(_match_4._data)) { + return std::string("int8_t"); + } + else if (std::holds_alternative::Int16>(_match_4._data)) { + return std::string("int16_t"); + } + else if (std::holds_alternative::Int32>(_match_4._data)) { + return std::string("int32_t"); + } + else if (std::holds_alternative::Float32>(_match_4._data)) { + return std::string("float"); + } + else if (std::holds_alternative::USize>(_match_4._data)) { + return std::string("size_t"); + } + else if (std::holds_alternative::CString>(_match_4._data)) { + return std::string("const char*"); + } + else if (std::holds_alternative::Ptr>(_match_4._data)) { + auto& _v = std::get::Ptr>(_match_4._data); + auto& inner = *_v.inner; + return ((std::string("") + (type_to_cpp(inner))) + std::string("*")); + } + else if (std::holds_alternative::Bytes>(_match_4._data)) { + return std::string("std::vector"); + } + else { + return std::string("auto"); + } + } + return std::string("auto"); +} + +std::string type_to_display(const TypeNode& t) { + { + const auto& _match_5 = t; + if (std::holds_alternative::Int>(_match_5._data)) { + return std::string("int"); + } + else if (std::holds_alternative::Float>(_match_5._data)) { + return std::string("float"); + } + else if (std::holds_alternative::Str>(_match_5._data)) { + return std::string("string"); + } + else if (std::holds_alternative::Bool>(_match_5._data)) { + return std::string("bool"); + } + else if (std::holds_alternative::Void>(_match_5._data)) { + return std::string("void"); + } + else if (std::holds_alternative::Auto>(_match_5._data)) { + return std::string("auto"); + } + else if (std::holds_alternative::Dynamic>(_match_5._data)) { + return std::string("dynamic"); + } + else if (std::holds_alternative::NullType>(_match_5._data)) { + return std::string("null"); + } + else if (std::holds_alternative::Custom>(_match_5._data)) { + auto& _v = std::get::Custom>(_match_5._data); + auto& name = _v.name; + auto& _ = _v.type_args; + return name; + } + else if (std::holds_alternative::Array>(_match_5._data)) { + auto& _v = std::get::Array>(_match_5._data); + auto& inner = *_v.inner; + return ((std::string("vector[") + (type_to_display(inner))) + std::string("]")); + } + else if (std::holds_alternative::HashSet>(_match_5._data)) { + auto& _v = std::get::HashSet>(_match_5._data); + auto& inner = *_v.inner; + return ((std::string("set[") + (type_to_display(inner))) + std::string("]")); + } + else if (std::holds_alternative::HashMap>(_match_5._data)) { + auto& _v = std::get::HashMap>(_match_5._data); + auto& k = *_v.key_type; + auto& v = *_v.value_type; + return ((((std::string("map[") + (type_to_display(k))) + std::string(", ")) + (type_to_display(v))) + std::string("]")); + } + else if (std::holds_alternative::Nullable>(_match_5._data)) { + auto& _v = std::get::Nullable>(_match_5._data); + auto& inner = *_v.inner; + return ((std::string("") + (type_to_display(inner))) + std::string("?")); + } + else if (std::holds_alternative::Int8>(_match_5._data)) { + return std::string("int8"); + } + else if (std::holds_alternative::Int16>(_match_5._data)) { + return std::string("int16"); + } + else if (std::holds_alternative::Int32>(_match_5._data)) { + return std::string("int32"); + } + else if (std::holds_alternative::Float32>(_match_5._data)) { + return std::string("float32"); + } + else if (std::holds_alternative::USize>(_match_5._data)) { + return std::string("usize"); + } + else if (std::holds_alternative::CString>(_match_5._data)) { + return std::string("cstring"); + } + else if (std::holds_alternative::Ptr>(_match_5._data)) { + auto& _v = std::get::Ptr>(_match_5._data); + auto& inner = *_v.inner; + return ((std::string("ptr[") + (type_to_display(inner))) + std::string("]")); + } + else if (std::holds_alternative::Bytes>(_match_5._data)) { + return std::string("bytes"); + } + else { + return std::string("unknown"); + } + } + return std::string("unknown"); +} + +std::string find_enum_for_variant(std::unordered_map>& known_enums, const std::string& variant_name) { + std::string found = std::string(""); + int64_t count = INT64_C(0); + std::vector keys = lv_keys(known_enums); + for (const auto& key : keys) { + std::vector variants = known_enums[key]; + for (const auto& v : variants) { + if ((v.name.lexeme == variant_name)) { + if ((count == INT64_C(0))) { + found = key; + } + count = (count + INT64_C(1)); + } + } + } + if ((count == INT64_C(1))) { + return found; + } + return std::string(""); +} struct CppCodegen { std::string output; @@ -1329,27 +1598,6 @@ struct CppCodegen { } } - std::string find_enum_for_variant(const std::string& variant_name) { - std::string found = std::string(""); - int64_t count = INT64_C(0); - std::vector keys = lv_keys(this->known_enums); - for (const auto& key : keys) { - std::vector variants = this->known_enums[key]; - for (const auto& v : variants) { - if ((v.name.lexeme == variant_name)) { - if ((count == INT64_C(0))) { - found = key; - } - count = (count + INT64_C(1)); - } - } - } - if ((count == INT64_C(1))) { - return found; - } - return std::string(""); - } - EnumVariantNode get_variant_info(const std::string& enum_name, const std::string& variant_name) { if ((this->known_enums.count(enum_name) > 0)) { std::vector variants = this->known_enums[enum_name]; @@ -1378,12 +1626,12 @@ struct CppCodegen { bool type_contains_dynamic(const TypeNode& t) { { - const auto& _match_0 = t; - if (std::holds_alternative::Dynamic>(_match_0._data)) { + const auto& _match_6 = t; + if (std::holds_alternative::Dynamic>(_match_6._data)) { return true; } - else if (std::holds_alternative::Array>(_match_0._data)) { - auto& _v = std::get::Array>(_match_0._data); + else if (std::holds_alternative::Array>(_match_6._data)) { + auto& _v = std::get::Array>(_match_6._data); auto& inner = *_v.inner; return (*this).type_contains_dynamic(inner); } @@ -1415,11 +1663,11 @@ struct CppCodegen { std::string wrap_convert(std::string expr, const TypeNode& from, const TypeNode& expected) { { - const auto& _match_1 = from; - if (std::holds_alternative::Str>(_match_1._data)) { + const auto& _match_7 = from; + if (std::holds_alternative::Str>(_match_7._data)) { { - const auto& _match_2 = expected; - if (std::holds_alternative::CString>(_match_2._data)) { + const auto& _match_8 = expected; + if (std::holds_alternative::CString>(_match_8._data)) { return ((std::string("(") + (expr)) + std::string(").c_str()")); } else { @@ -1427,10 +1675,10 @@ struct CppCodegen { } } } - else if (std::holds_alternative::CString>(_match_1._data)) { + else if (std::holds_alternative::CString>(_match_7._data)) { { - const auto& _match_3 = expected; - if (std::holds_alternative::Str>(_match_3._data)) { + const auto& _match_9 = expected; + if (std::holds_alternative::Str>(_match_9._data)) { return ((std::string("std::string(") + (expr)) + std::string(")")); } else { @@ -1438,13 +1686,13 @@ struct CppCodegen { } } } - else if (std::holds_alternative::Int>(_match_1._data)) { + else if (std::holds_alternative::Int>(_match_7._data)) { { - const auto& _match_4 = expected; - if (std::holds_alternative::Int32>(_match_4._data)) { + const auto& _match_10 = expected; + if (std::holds_alternative::Int32>(_match_10._data)) { return ((std::string("static_cast(") + (expr)) + std::string(")")); } - else if (std::holds_alternative::USize>(_match_4._data)) { + else if (std::holds_alternative::USize>(_match_10._data)) { return ((std::string("static_cast(") + (expr)) + std::string(")")); } else { @@ -1452,10 +1700,10 @@ struct CppCodegen { } } } - else if (std::holds_alternative::Int32>(_match_1._data)) { + else if (std::holds_alternative::Int32>(_match_7._data)) { { - const auto& _match_5 = expected; - if (std::holds_alternative::Int>(_match_5._data)) { + const auto& _match_11 = expected; + if (std::holds_alternative::Int>(_match_11._data)) { return ((std::string("static_cast(") + (expr)) + std::string(")")); } else { @@ -1463,10 +1711,10 @@ struct CppCodegen { } } } - else if (std::holds_alternative::USize>(_match_1._data)) { + else if (std::holds_alternative::USize>(_match_7._data)) { { - const auto& _match_6 = expected; - if (std::holds_alternative::Int>(_match_6._data)) { + const auto& _match_12 = expected; + if (std::holds_alternative::Int>(_match_12._data)) { return ((std::string("static_cast(") + (expr)) + std::string(")")); } else { @@ -1474,10 +1722,10 @@ struct CppCodegen { } } } - else if (std::holds_alternative::Float>(_match_1._data)) { + else if (std::holds_alternative::Float>(_match_7._data)) { { - const auto& _match_7 = expected; - if (std::holds_alternative::Float32>(_match_7._data)) { + const auto& _match_13 = expected; + if (std::holds_alternative::Float32>(_match_13._data)) { return ((std::string("static_cast(") + (expr)) + std::string(")")); } else { @@ -1485,10 +1733,10 @@ struct CppCodegen { } } } - else if (std::holds_alternative::Float32>(_match_1._data)) { + else if (std::holds_alternative::Float32>(_match_7._data)) { { - const auto& _match_8 = expected; - if (std::holds_alternative::Float>(_match_8._data)) { + const auto& _match_14 = expected; + if (std::holds_alternative::Float>(_match_14._data)) { return ((std::string("static_cast(") + (expr)) + std::string(")")); } else { @@ -1505,16 +1753,16 @@ struct CppCodegen { std::string wrap_extern_arg(std::string expr, const Expr& original, const TypeNode& expected) { { - const auto& _match_9 = original; - if (std::holds_alternative::Literal>(_match_9._data)) { - auto& _v = std::get::Literal>(_match_9._data); + const auto& _match_15 = original; + if (std::holds_alternative::Literal>(_match_15._data)) { + auto& _v = std::get::Literal>(_match_15._data); auto& kind = _v.kind; auto& value = _v.value; if ((kind == std::string("null"))) { { - const auto& _match_10 = expected; - if (std::holds_alternative::Ptr>(_match_10._data)) { - auto& _v = std::get::Ptr>(_match_10._data); + const auto& _match_16 = expected; + if (std::holds_alternative::Ptr>(_match_16._data)) { + auto& _v = std::get::Ptr>(_match_16._data); auto& inner = *_v.inner; return std::string("nullptr"); } @@ -1525,8 +1773,8 @@ struct CppCodegen { } if ((kind == std::string("string"))) { { - const auto& _match_11 = expected; - if (std::holds_alternative::CString>(_match_11._data)) { + const auto& _match_17 = expected; + if (std::holds_alternative::CString>(_match_17._data)) { return ((std::string("(") + (expr)) + std::string(").c_str()")); } else { @@ -1536,11 +1784,11 @@ struct CppCodegen { } if ((kind == std::string("int"))) { { - const auto& _match_12 = expected; - if (std::holds_alternative::Int32>(_match_12._data)) { + const auto& _match_18 = expected; + if (std::holds_alternative::Int32>(_match_18._data)) { return ((std::string("static_cast(") + (expr)) + std::string(")")); } - else if (std::holds_alternative::USize>(_match_12._data)) { + else if (std::holds_alternative::USize>(_match_18._data)) { return ((std::string("static_cast(") + (expr)) + std::string(")")); } else { @@ -1550,8 +1798,8 @@ struct CppCodegen { } if ((kind == std::string("float"))) { { - const auto& _match_13 = expected; - if (std::holds_alternative::Float32>(_match_13._data)) { + const auto& _match_19 = expected; + if (std::holds_alternative::Float32>(_match_19._data)) { return ((std::string("static_cast(") + (expr)) + std::string(")")); } else { @@ -1561,8 +1809,8 @@ struct CppCodegen { } return expr; } - else if (std::holds_alternative::Variable>(_match_9._data)) { - auto& _v = std::get::Variable>(_match_9._data); + else if (std::holds_alternative::Variable>(_match_15._data)) { + auto& _v = std::get::Variable>(_match_15._data); auto& tok = _v.name; if ((this->var_types.count(tok.lexeme) > 0)) { TypeNode vt = this->var_types[tok.lexeme]; @@ -1578,33 +1826,9 @@ struct CppCodegen { std::string emit_type(const TypeNode& t) { { - const auto& _match_14 = t; - if (std::holds_alternative::Int>(_match_14._data)) { - return std::string("int64_t"); - } - else if (std::holds_alternative::Float>(_match_14._data)) { - return std::string("double"); - } - else if (std::holds_alternative::Str>(_match_14._data)) { - return std::string("std::string"); - } - else if (std::holds_alternative::Bool>(_match_14._data)) { - return std::string("bool"); - } - else if (std::holds_alternative::Void>(_match_14._data)) { - return std::string("void"); - } - else if (std::holds_alternative::Auto>(_match_14._data)) { - return std::string("auto"); - } - else if (std::holds_alternative::Dynamic>(_match_14._data)) { - return std::string("std::any"); - } - else if (std::holds_alternative::NullType>(_match_14._data)) { - return std::string("std::nullptr_t"); - } - else if (std::holds_alternative::Custom>(_match_14._data)) { - auto& _v = std::get::Custom>(_match_14._data); + const auto& _match_20 = t; + if (std::holds_alternative::Custom>(_match_20._data)) { + auto& _v = std::get::Custom>(_match_20._data); auto& name = _v.name; auto& type_args = _v.type_args; if ((static_cast(type_args.size()) > INT64_C(0))) { @@ -1619,55 +1843,34 @@ struct CppCodegen { } return name; } - else if (std::holds_alternative::Array>(_match_14._data)) { - auto& _v = std::get::Array>(_match_14._data); + else if (std::holds_alternative::Array>(_match_20._data)) { + auto& _v = std::get::Array>(_match_20._data); auto& inner = *_v.inner; return ((std::string("std::vector<") + ((*this).emit_type(inner))) + std::string(">")); } - else if (std::holds_alternative::HashSet>(_match_14._data)) { - auto& _v = std::get::HashSet>(_match_14._data); + else if (std::holds_alternative::HashSet>(_match_20._data)) { + auto& _v = std::get::HashSet>(_match_20._data); auto& inner = *_v.inner; return ((std::string("std::unordered_set<") + ((*this).emit_type(inner))) + std::string(">")); } - else if (std::holds_alternative::HashMap>(_match_14._data)) { - auto& _v = std::get::HashMap>(_match_14._data); + else if (std::holds_alternative::HashMap>(_match_20._data)) { + auto& _v = std::get::HashMap>(_match_20._data); auto& key_type = *_v.key_type; auto& value_type = *_v.value_type; return ((((std::string("std::unordered_map<") + ((*this).emit_type(key_type))) + std::string(", ")) + ((*this).emit_type(value_type))) + std::string(">")); } - else if (std::holds_alternative::Nullable>(_match_14._data)) { - auto& _v = std::get::Nullable>(_match_14._data); + else if (std::holds_alternative::Nullable>(_match_20._data)) { + auto& _v = std::get::Nullable>(_match_20._data); auto& inner = *_v.inner; return ((std::string("std::optional<") + ((*this).emit_type(inner))) + std::string(">")); } - else if (std::holds_alternative::Int8>(_match_14._data)) { - return std::string("int8_t"); - } - else if (std::holds_alternative::Int16>(_match_14._data)) { - return std::string("int16_t"); - } - else if (std::holds_alternative::Int32>(_match_14._data)) { - return std::string("int32_t"); - } - else if (std::holds_alternative::Float32>(_match_14._data)) { - return std::string("float"); - } - else if (std::holds_alternative::USize>(_match_14._data)) { - return std::string("size_t"); - } - else if (std::holds_alternative::CString>(_match_14._data)) { - return std::string("const char*"); - } - else if (std::holds_alternative::Ptr>(_match_14._data)) { - auto& _v = std::get::Ptr>(_match_14._data); + else if (std::holds_alternative::Ptr>(_match_20._data)) { + auto& _v = std::get::Ptr>(_match_20._data); auto& inner = *_v.inner; return ((std::string("") + ((*this).emit_type(inner))) + std::string("*")); } - else if (std::holds_alternative::Bytes>(_match_14._data)) { - return std::string("std::vector"); - } else { - return std::string("auto"); + return type_to_cpp(t); } } } @@ -1796,28 +1999,115 @@ struct CppCodegen { return std::string(""); } + std::string emit_cast(const Expr& expr, const TypeNode& target_type, bool m) { + std::string ex = (*this).emit_expr(expr, m); + std::string t = (*this).emit_type(target_type); + if ((*this).is_dynamic_expression(expr)) { + return ((((std::string("std::any_cast<") + (t)) + std::string(">(")) + (ex)) + std::string(")")); + } + TypeNode src = (*this).infer_source_type(expr); + if (is_string_type(src)) { + if (is_integer_type(target_type)) { + { + const auto& _match_21 = target_type; + if (std::holds_alternative::Int>(_match_21._data)) { + return ((std::string("__str_to_int(") + (ex)) + std::string(")")); + } + else { + return ((((std::string("static_cast<") + (t)) + std::string(">(__str_to_int(")) + (ex)) + std::string("))")); + } + } + } + if (is_float_type(target_type)) { + { + const auto& _match_22 = target_type; + if (std::holds_alternative::Float>(_match_22._data)) { + return ((std::string("__str_to_float(") + (ex)) + std::string(")")); + } + else { + return ((((std::string("static_cast<") + (t)) + std::string(">(__str_to_float(")) + (ex)) + std::string("))")); + } + } + } + if (is_bytes_type(target_type)) { + return ((std::string("__bytes_from_string(") + (ex)) + std::string(")")); + } + } + if (is_string_type(target_type)) { + if (is_integer_type(src) || is_float_type(src)) { + return ((std::string("to_string(") + (ex)) + std::string(")")); + } + { + const auto& _match_23 = src; + if (std::holds_alternative::Bool>(_match_23._data)) { + return ((std::string("to_string(") + (ex)) + std::string(")")); + } + else { + /* pass */ + } + } + } + if (is_bytes_type(src) && is_string_type(target_type)) { + return ((std::string("__bytes_to_string(") + (ex)) + std::string(")")); + } + if (is_string_type(src) && is_bytes_type(target_type)) { + return ((std::string("__bytes_from_string(") + (ex)) + std::string(")")); + } + return ((((std::string("static_cast<") + (t)) + std::string(">(")) + (ex)) + std::string(")")); + } + + std::string emit_member_prefix(const Expr& object, bool m) { + if (this->in_extend) { + { + const auto& _match_24 = object; + if (std::holds_alternative::This>(_match_24._data)) { + auto& _v = std::get::This>(_match_24._data); + auto& kw = _v.keyword; + return std::string("self."); + } + else { + return ((std::string("") + ((*this).emit_expr(object, m))) + std::string(".")); + } + } + } + if (m) { + { + const auto& _match_25 = object; + if (std::holds_alternative::This>(_match_25._data)) { + auto& _v = std::get::This>(_match_25._data); + auto& kw = _v.keyword; + return std::string("this->"); + } + else { + return ((std::string("") + ((*this).emit_expr(object, m))) + std::string(".")); + } + } + } + return ((std::string("") + ((*this).emit_expr(object, m))) + std::string(".")); + } + std::string emit_expr(const Expr& e, bool m) { { - const auto& _match_15 = e; - if (_match_15._tag == "None") { + const auto& _match_26 = e; + if (_match_26._tag == "None") { return std::string(""); } - else if (std::holds_alternative::Literal>(_match_15._data)) { - auto& _v = std::get::Literal>(_match_15._data); + else if (std::holds_alternative::Literal>(_match_26._data)) { + auto& _v = std::get::Literal>(_match_26._data); auto& kind = _v.kind; auto& value = _v.value; return (*this).emit_literal(kind, value); } - else if (std::holds_alternative::Unary>(_match_15._data)) { - auto& _v = std::get::Unary>(_match_15._data); + else if (std::holds_alternative::Unary>(_match_26._data)) { + auto& _v = std::get::Unary>(_match_26._data); auto& op = _v.op; auto& right = *_v.right; std::string r = (*this).emit_expr(right, m); std::string op_str = (*this).token_to_cpp_op(op); return ((((std::string("(") + (op_str)) + std::string("")) + (r)) + std::string(")")); } - else if (std::holds_alternative::Binary>(_match_15._data)) { - auto& _v = std::get::Binary>(_match_15._data); + else if (std::holds_alternative::Binary>(_match_26._data)) { + auto& _v = std::get::Binary>(_match_26._data); auto& left = *_v.left; auto& op = _v.op; auto& right = *_v.right; @@ -1826,24 +2116,24 @@ struct CppCodegen { std::string op_str = (*this).token_to_cpp_op(op); return ((((((std::string("(") + (l)) + std::string(" ")) + (op_str)) + std::string(" ")) + (r)) + std::string(")")); } - else if (std::holds_alternative::Grouping>(_match_15._data)) { - auto& _v = std::get::Grouping>(_match_15._data); + else if (std::holds_alternative::Grouping>(_match_26._data)) { + auto& _v = std::get::Grouping>(_match_26._data); auto& inner = *_v.inner; return ((std::string("(") + ((*this).emit_expr(inner, m))) + std::string(")")); } - else if (std::holds_alternative::Variable>(_match_15._data)) { - auto& _v = std::get::Variable>(_match_15._data); + else if (std::holds_alternative::Variable>(_match_26._data)) { + auto& _v = std::get::Variable>(_match_26._data); auto& name = _v.name; return name.lexeme; } - else if (std::holds_alternative::Assign>(_match_15._data)) { - auto& _v = std::get::Assign>(_match_15._data); + else if (std::holds_alternative::Assign>(_match_26._data)) { + auto& _v = std::get::Assign>(_match_26._data); auto& name = _v.name; auto& value = *_v.value; return ((((std::string("") + (name.lexeme)) + std::string(" = ")) + ((*this).emit_expr(value, m))) + std::string("")); } - else if (std::holds_alternative::Logical>(_match_15._data)) { - auto& _v = std::get::Logical>(_match_15._data); + else if (std::holds_alternative::Logical>(_match_26._data)) { + auto& _v = std::get::Logical>(_match_26._data); auto& left = *_v.left; auto& op = _v.op; auto& right = *_v.right; @@ -1855,31 +2145,31 @@ struct CppCodegen { } return ((((((std::string("") + (l)) + std::string(" ")) + (op_str)) + std::string(" ")) + (r)) + std::string("")); } - else if (std::holds_alternative::Call>(_match_15._data)) { - auto& _v = std::get::Call>(_match_15._data); + else if (std::holds_alternative::Call>(_match_26._data)) { + auto& _v = std::get::Call>(_match_26._data); auto& callee = *_v.callee; auto& paren = _v.paren; auto& args = _v.args; auto& arg_names = _v.arg_names; return (*this).emit_call_expr(callee, args, arg_names, m); } - else if (std::holds_alternative::Index>(_match_15._data)) { - auto& _v = std::get::Index>(_match_15._data); + else if (std::holds_alternative::Index>(_match_26._data)) { + auto& _v = std::get::Index>(_match_26._data); auto& object = *_v.object; auto& bracket = _v.bracket; auto& index = *_v.index; return ((((std::string("") + ((*this).emit_expr(object, m))) + std::string("[")) + ((*this).emit_expr(index, m))) + std::string("]")); } - else if (std::holds_alternative::IndexSet>(_match_15._data)) { - auto& _v = std::get::IndexSet>(_match_15._data); + else if (std::holds_alternative::IndexSet>(_match_26._data)) { + auto& _v = std::get::IndexSet>(_match_26._data); auto& object = *_v.object; auto& bracket = _v.bracket; auto& index = *_v.index; auto& value = *_v.value; return ((((((std::string("") + ((*this).emit_expr(object, m))) + std::string("[")) + ((*this).emit_expr(index, m))) + std::string("] = ")) + ((*this).emit_expr(value, m))) + std::string("")); } - else if (std::holds_alternative::Vector>(_match_15._data)) { - auto& _v = std::get::Vector>(_match_15._data); + else if (std::holds_alternative::Vector>(_match_26._data)) { + auto& _v = std::get::Vector>(_match_26._data); auto& elements = _v.elements; if ((static_cast(elements.size()) == INT64_C(0))) { return std::string("{}"); @@ -1890,8 +2180,8 @@ struct CppCodegen { } return ((std::string("std::vector{") + (lv_join(elems, std::string(", ")))) + std::string("}")); } - else if (std::holds_alternative::Map>(_match_15._data)) { - auto& _v = std::get::Map>(_match_15._data); + else if (std::holds_alternative::Map>(_match_26._data)) { + auto& _v = std::get::Map>(_match_26._data); auto& keys = _v.keys; auto& values = _v.values; std::vector entries = {}; @@ -1900,80 +2190,28 @@ struct CppCodegen { } return ((std::string("{{") + (lv_join(entries, std::string(", ")))) + std::string("}}")); } - else if (std::holds_alternative::Get>(_match_15._data)) { - auto& _v = std::get::Get>(_match_15._data); + else if (std::holds_alternative::Get>(_match_26._data)) { + auto& _v = std::get::Get>(_match_26._data); auto& object = *_v.object; auto& name = _v.name; - if (this->in_extend) { - { - const auto& _match_16 = object; - if (std::holds_alternative::This>(_match_16._data)) { - auto& _v = std::get::This>(_match_16._data); - auto& kw = _v.keyword; - return ((std::string("self.") + (name.lexeme)) + std::string("")); - } - else { - return ((((std::string("") + ((*this).emit_expr(object, m))) + std::string(".")) + (name.lexeme)) + std::string("")); - } - } - } - if (m) { - { - const auto& _match_17 = object; - if (std::holds_alternative::This>(_match_17._data)) { - auto& _v = std::get::This>(_match_17._data); - auto& kw = _v.keyword; - return ((std::string("this->") + (name.lexeme)) + std::string("")); - } - else { - return ((((std::string("") + ((*this).emit_expr(object, m))) + std::string(".")) + (name.lexeme)) + std::string("")); - } - } - } - return ((((std::string("") + ((*this).emit_expr(object, m))) + std::string(".")) + (name.lexeme)) + std::string("")); + return ((((std::string("") + ((*this).emit_member_prefix(object, m))) + std::string("")) + (name.lexeme)) + std::string("")); } - else if (std::holds_alternative::Set>(_match_15._data)) { - auto& _v = std::get::Set>(_match_15._data); + else if (std::holds_alternative::Set>(_match_26._data)) { + auto& _v = std::get::Set>(_match_26._data); auto& object = *_v.object; auto& name = _v.name; auto& value = *_v.value; - if (this->in_extend) { - { - const auto& _match_18 = object; - if (std::holds_alternative::This>(_match_18._data)) { - auto& _v = std::get::This>(_match_18._data); - auto& kw = _v.keyword; - return ((((std::string("self.") + (name.lexeme)) + std::string(" = ")) + ((*this).emit_expr(value, m))) + std::string("")); - } - else { - return ((((((std::string("") + ((*this).emit_expr(object, m))) + std::string(".")) + (name.lexeme)) + std::string(" = ")) + ((*this).emit_expr(value, m))) + std::string("")); - } - } - } - if (m) { - { - const auto& _match_19 = object; - if (std::holds_alternative::This>(_match_19._data)) { - auto& _v = std::get::This>(_match_19._data); - auto& kw = _v.keyword; - return ((((std::string("this->") + (name.lexeme)) + std::string(" = ")) + ((*this).emit_expr(value, m))) + std::string("")); - } - else { - return ((((((std::string("") + ((*this).emit_expr(object, m))) + std::string(".")) + (name.lexeme)) + std::string(" = ")) + ((*this).emit_expr(value, m))) + std::string("")); - } - } - } - return ((((((std::string("") + ((*this).emit_expr(object, m))) + std::string(".")) + (name.lexeme)) + std::string(" = ")) + ((*this).emit_expr(value, m))) + std::string("")); + return ((((((std::string("") + ((*this).emit_member_prefix(object, m))) + std::string("")) + (name.lexeme)) + std::string(" = ")) + ((*this).emit_expr(value, m))) + std::string("")); } - else if (std::holds_alternative::StaticGet>(_match_15._data)) { - auto& _v = std::get::StaticGet>(_match_15._data); + else if (std::holds_alternative::StaticGet>(_match_26._data)) { + auto& _v = std::get::StaticGet>(_match_26._data); auto& object = *_v.object; auto& name = _v.name; if (m) { { - const auto& _match_20 = object; - if (std::holds_alternative::This>(_match_20._data)) { - auto& _v = std::get::This>(_match_20._data); + const auto& _match_27 = object; + if (std::holds_alternative::This>(_match_27._data)) { + auto& _v = std::get::This>(_match_27._data); auto& kw = _v.keyword; return ((std::string("(*this)::") + (name.lexeme)) + std::string("")); } @@ -1984,96 +2222,43 @@ struct CppCodegen { } return ((((std::string("") + ((*this).emit_expr(object, m))) + std::string("::")) + (name.lexeme)) + std::string("")); } - else if (std::holds_alternative::This>(_match_15._data)) { - auto& _v = std::get::This>(_match_15._data); + else if (std::holds_alternative::This>(_match_26._data)) { + auto& _v = std::get::This>(_match_26._data); auto& keyword = _v.keyword; if (this->in_extend) { return std::string("self"); } return std::string("(*this)"); } - else if (std::holds_alternative::Cast>(_match_15._data)) { - auto& _v = std::get::Cast>(_match_15._data); + else if (std::holds_alternative::Cast>(_match_26._data)) { + auto& _v = std::get::Cast>(_match_26._data); auto& expr = *_v.expr; auto& target_type = _v.target_type; - std::string ex = (*this).emit_expr(expr, m); - std::string t = (*this).emit_type(target_type); - if ((*this).is_dynamic_expression(expr)) { - return ((((std::string("std::any_cast<") + (t)) + std::string(">(")) + (ex)) + std::string(")")); - } - TypeNode src = (*this).infer_source_type(expr); - if ((*this).is_string_type(src)) { - if ((*this).is_integer_type(target_type)) { - { - const auto& _match_21 = target_type; - if (std::holds_alternative::Int>(_match_21._data)) { - return ((std::string("__str_to_int(") + (ex)) + std::string(")")); - } - else { - return ((((std::string("static_cast<") + (t)) + std::string(">(__str_to_int(")) + (ex)) + std::string("))")); - } - } - } - if ((*this).is_float_type(target_type)) { - { - const auto& _match_22 = target_type; - if (std::holds_alternative::Float>(_match_22._data)) { - return ((std::string("__str_to_float(") + (ex)) + std::string(")")); - } - else { - return ((((std::string("static_cast<") + (t)) + std::string(">(__str_to_float(")) + (ex)) + std::string("))")); - } - } - } - if ((*this).is_bytes_type(target_type)) { - return ((std::string("__bytes_from_string(") + (ex)) + std::string(")")); - } - } - if ((*this).is_string_type(target_type)) { - if ((*this).is_integer_type(src) || (*this).is_float_type(src)) { - return ((std::string("to_string(") + (ex)) + std::string(")")); - } - { - const auto& _match_23 = src; - if (std::holds_alternative::Bool>(_match_23._data)) { - return ((std::string("to_string(") + (ex)) + std::string(")")); - } - else { - /* pass */ - } - } - } - if ((*this).is_bytes_type(src) && (*this).is_string_type(target_type)) { - return ((std::string("__bytes_to_string(") + (ex)) + std::string(")")); - } - if ((*this).is_string_type(src) && (*this).is_bytes_type(target_type)) { - return ((std::string("__bytes_from_string(") + (ex)) + std::string(")")); - } - return ((((std::string("static_cast<") + (t)) + std::string(">(")) + (ex)) + std::string(")")); + return (*this).emit_cast(expr, target_type, m); } - else if (std::holds_alternative::Throw>(_match_15._data)) { - auto& _v = std::get::Throw>(_match_15._data); + else if (std::holds_alternative::Throw>(_match_26._data)) { + auto& _v = std::get::Throw>(_match_26._data); auto& expr = *_v.expr; return ((std::string("throw std::runtime_error(") + ((*this).emit_expr(expr, m))) + std::string(")")); } - else if (std::holds_alternative::Lambda>(_match_15._data)) { - auto& _v = std::get::Lambda>(_match_15._data); + else if (std::holds_alternative::Lambda>(_match_26._data)) { + auto& _v = std::get::Lambda>(_match_26._data); auto& params = _v.params; auto& body = *_v.body; return (*this).emit_lambda(params, body, m); } - else if (std::holds_alternative::Own>(_match_15._data)) { - auto& _v = std::get::Own>(_match_15._data); + else if (std::holds_alternative::Own>(_match_26._data)) { + auto& _v = std::get::Own>(_match_26._data); auto& expr = *_v.expr; return ((std::string("std::move(") + ((*this).emit_expr(expr, m))) + std::string(")")); } - else if (std::holds_alternative::AddressOf>(_match_15._data)) { - auto& _v = std::get::AddressOf>(_match_15._data); + else if (std::holds_alternative::AddressOf>(_match_26._data)) { + auto& _v = std::get::AddressOf>(_match_26._data); auto& expr = *_v.expr; return ((std::string("&(") + ((*this).emit_expr(expr, m))) + std::string(")")); } - else if (std::holds_alternative::BlockLambda>(_match_15._data)) { - auto& _v = std::get::BlockLambda>(_match_15._data); + else if (std::holds_alternative::BlockLambda>(_match_26._data)) { + auto& _v = std::get::BlockLambda>(_match_26._data); auto& params = _v.params; auto& body_id = _v.body_id; return (*this).emit_block_lambda(params, body_id, m); @@ -2113,14 +2298,14 @@ struct CppCodegen { bool is_dynamic_expression(const Expr& e) { { - const auto& _match_24 = e; - if (std::holds_alternative::Variable>(_match_24._data)) { - auto& _v = std::get::Variable>(_match_24._data); + const auto& _match_28 = e; + if (std::holds_alternative::Variable>(_match_28._data)) { + auto& _v = std::get::Variable>(_match_28._data); auto& name = _v.name; return (*this).is_dynamic_var(name.lexeme); } - else if (std::holds_alternative::Index>(_match_24._data)) { - auto& _v = std::get::Index>(_match_24._data); + else if (std::holds_alternative::Index>(_match_28._data)) { + auto& _v = std::get::Index>(_match_28._data); auto& object = *_v.object; auto& bracket = _v.bracket; auto& index = *_v.index; @@ -2194,18 +2379,41 @@ struct CppCodegen { return result; } + std::vector emit_args(const std::vector& args, bool m) { + std::vector result = {}; + for (const auto& a : args) { + result.push_back((*this).emit_expr(a, m)); + } + return result; + } + + void apply_extern_wrapping(std::vector& arg_strs, const std::vector& args, const std::string& fn_name) { + if ((this->extern_fn_params.count(fn_name) > 0)) { + std::vector eparams = this->extern_fn_params[fn_name]; + for (int64_t i = INT64_C(0); i < static_cast(arg_strs.size()); i++) { + if ((i < static_cast(eparams.size()))) { + arg_strs[i] = (*this).wrap_extern_arg(arg_strs[i], args[i], eparams[i].param_type); + } + } + } + } + + std::string resolve_fn_name(const std::string& name) { + if ((this->extern_fn_names.count(name) > 0)) { + return this->extern_fn_names[name]; + } + return name; + } + std::string emit_call_expr(const Expr& callee, const std::vector& args, const std::vector& arg_names, bool in_method) { { - const auto& _match_25 = callee; - if (std::holds_alternative::Get>(_match_25._data)) { - auto& _v = std::get::Get>(_match_25._data); + const auto& _match_29 = callee; + if (std::holds_alternative::Get>(_match_29._data)) { + auto& _v = std::get::Get>(_match_29._data); auto& object = *_v.object; auto& name = _v.name; std::string obj = (*this).emit_expr(object, in_method); - std::vector arg_strs = {}; - for (const auto& a : args) { - arg_strs.push_back((*this).emit_expr(a, in_method)); - } + std::vector arg_strs = (*this).emit_args(args, in_method); std::string remapped = (*this).try_remap_method(obj, name.lexeme, arg_strs); if ((remapped != std::string(""))) { return remapped; @@ -2216,19 +2424,16 @@ struct CppCodegen { } return ((((((std::string("") + (obj)) + std::string(".")) + (name.lexeme)) + std::string("(")) + (lv_join(arg_strs, std::string(", ")))) + std::string(")")); } - else if (std::holds_alternative::StaticGet>(_match_25._data)) { - auto& _v = std::get::StaticGet>(_match_25._data); + else if (std::holds_alternative::StaticGet>(_match_29._data)) { + auto& _v = std::get::StaticGet>(_match_29._data); auto& object = *_v.object; auto& name = _v.name; std::string obj = (*this).emit_expr(object, in_method); - std::vector arg_strs = {}; - for (const auto& a : args) { - arg_strs.push_back((*this).emit_expr(a, in_method)); - } + std::vector arg_strs = (*this).emit_args(args, in_method); { - const auto& _match_26 = object; - if (std::holds_alternative::Variable>(_match_26._data)) { - auto& _v = std::get::Variable>(_match_26._data); + const auto& _match_30 = object; + if (std::holds_alternative::Variable>(_match_30._data)) { + auto& _v = std::get::Variable>(_match_30._data); auto& tok = _v.name; if ((*this).is_known_enum(tok.lexeme)) { return ((((((std::string("") + (obj)) + std::string("::make_")) + (name.lexeme)) + std::string("(")) + (lv_join(arg_strs, std::string(", ")))) + std::string(")")); @@ -2240,8 +2445,8 @@ struct CppCodegen { } return ((((((std::string("") + (obj)) + std::string("::")) + (name.lexeme)) + std::string("(")) + (lv_join(arg_strs, std::string(", ")))) + std::string(")")); } - else if (std::holds_alternative::Variable>(_match_25._data)) { - auto& _v = std::get::Variable>(_match_25._data); + else if (std::holds_alternative::Variable>(_match_29._data)) { + auto& _v = std::get::Variable>(_match_29._data); auto& tok = _v.name; if ((this->fn_params.count(tok.lexeme) > 0)) { std::vector fparams = this->fn_params[tok.lexeme]; @@ -2250,51 +2455,19 @@ struct CppCodegen { fdefaults = this->fn_defaults[tok.lexeme]; } std::vector arg_strs = (*this).resolve_named_args(args, arg_names, fparams, fdefaults, in_method); - if ((this->extern_fn_params.count(tok.lexeme) > 0)) { - std::vector eparams = this->extern_fn_params[tok.lexeme]; - for (int64_t i = INT64_C(0); i < static_cast(arg_strs.size()); i++) { - if ((i < static_cast(eparams.size()))) { - arg_strs[i] = (*this).wrap_extern_arg(arg_strs[i], args[i], eparams[i].param_type); - } - } - } - std::string fn_name = tok.lexeme; - if ((this->extern_fn_names.count(tok.lexeme) > 0)) { - fn_name = this->extern_fn_names[tok.lexeme]; - } - return ((((std::string("") + (fn_name)) + std::string("(")) + (lv_join(arg_strs, std::string(", ")))) + std::string(")")); - } - std::vector arg_strs = {}; - for (const auto& a : args) { - arg_strs.push_back((*this).emit_expr(a, in_method)); + (*this).apply_extern_wrapping(arg_strs, args, tok.lexeme); + return ((((std::string("") + ((*this).resolve_fn_name(tok.lexeme))) + std::string("(")) + (lv_join(arg_strs, std::string(", ")))) + std::string(")")); } + std::vector arg_strs = (*this).emit_args(args, in_method); if ((tok.lexeme == std::string("exit"))) { return ((std::string("lv_exit(") + (lv_join(arg_strs, std::string(", ")))) + std::string(")")); } - if ((this->extern_fn_params.count(tok.lexeme) > 0)) { - std::vector eparams = this->extern_fn_params[tok.lexeme]; - for (int64_t i = INT64_C(0); i < static_cast(arg_strs.size()); i++) { - if ((i < static_cast(eparams.size()))) { - arg_strs[i] = (*this).wrap_extern_arg(arg_strs[i], args[i], eparams[i].param_type); - } - } - std::string fn_name = tok.lexeme; - if ((this->extern_fn_names.count(tok.lexeme) > 0)) { - fn_name = this->extern_fn_names[tok.lexeme]; - } - return ((((std::string("") + (fn_name)) + std::string("(")) + (lv_join(arg_strs, std::string(", ")))) + std::string(")")); - } - if ((this->extern_fn_names.count(tok.lexeme) > 0)) { - return ((((std::string("") + (this->extern_fn_names[tok.lexeme])) + std::string("(")) + (lv_join(arg_strs, std::string(", ")))) + std::string(")")); - } - return ((((std::string("") + (tok.lexeme)) + std::string("(")) + (lv_join(arg_strs, std::string(", ")))) + std::string(")")); + (*this).apply_extern_wrapping(arg_strs, args, tok.lexeme); + return ((((std::string("") + ((*this).resolve_fn_name(tok.lexeme))) + std::string("(")) + (lv_join(arg_strs, std::string(", ")))) + std::string(")")); } else { std::string func = (*this).emit_expr(callee, in_method); - std::vector arg_strs = {}; - for (const auto& a : args) { - arg_strs.push_back((*this).emit_expr(a, in_method)); - } + std::vector arg_strs = (*this).emit_args(args, in_method); return ((((std::string("") + (func)) + std::string("(")) + (lv_join(arg_strs, std::string(", ")))) + std::string(")")); } } @@ -2467,25 +2640,25 @@ struct CppCodegen { std::string get_type_category(const Expr& object) { { - const auto& _match_27 = object; - if (std::holds_alternative::Variable>(_match_27._data)) { - auto& _v = std::get::Variable>(_match_27._data); + const auto& _match_31 = object; + if (std::holds_alternative::Variable>(_match_31._data)) { + auto& _v = std::get::Variable>(_match_31._data); auto& tok = _v.name; if ((this->var_types.count(tok.lexeme) > 0)) { TypeNode t = this->var_types[tok.lexeme]; return (*this).type_node_to_category(t); } } - else if (std::holds_alternative::Call>(_match_27._data)) { - auto& _v = std::get::Call>(_match_27._data); + else if (std::holds_alternative::Call>(_match_31._data)) { + auto& _v = std::get::Call>(_match_31._data); auto& callee = *_v.callee; auto& paren = _v.paren; auto& args = _v.args; auto& arg_names = _v.arg_names; { - const auto& _match_28 = callee; - if (std::holds_alternative::Get>(_match_28._data)) { - auto& _v = std::get::Get>(_match_28._data); + const auto& _match_32 = callee; + if (std::holds_alternative::Get>(_match_32._data)) { + auto& _v = std::get::Get>(_match_32._data); auto& inner_obj = *_v.object; auto& method_name = _v.name; std::string obj_cat = (*this).get_type_category(inner_obj); @@ -2507,31 +2680,31 @@ struct CppCodegen { std::string type_node_to_category(const TypeNode& t) { { - const auto& _match_29 = t; - if (std::holds_alternative::Array>(_match_29._data)) { - auto& _v = std::get::Array>(_match_29._data); + const auto& _match_33 = t; + if (std::holds_alternative::Array>(_match_33._data)) { + auto& _v = std::get::Array>(_match_33._data); auto& inner = *_v.inner; return std::string("vector"); } - else if (std::holds_alternative::HashMap>(_match_29._data)) { - auto& _v = std::get::HashMap>(_match_29._data); + else if (std::holds_alternative::HashMap>(_match_33._data)) { + auto& _v = std::get::HashMap>(_match_33._data); auto& k = *_v.key_type; auto& v = *_v.value_type; return std::string("hashmap"); } - else if (std::holds_alternative::HashSet>(_match_29._data)) { - auto& _v = std::get::HashSet>(_match_29._data); + else if (std::holds_alternative::HashSet>(_match_33._data)) { + auto& _v = std::get::HashSet>(_match_33._data); auto& inner = *_v.inner; return std::string("hashset"); } - else if (std::holds_alternative::Str>(_match_29._data)) { + else if (std::holds_alternative::Str>(_match_33._data)) { return std::string("string"); } - else if (std::holds_alternative::Bytes>(_match_29._data)) { + else if (std::holds_alternative::Bytes>(_match_33._data)) { return std::string("bytes"); } - else if (std::holds_alternative::Custom>(_match_29._data)) { - auto& _v = std::get::Custom>(_match_29._data); + else if (std::holds_alternative::Custom>(_match_33._data)) { + auto& _v = std::get::Custom>(_match_33._data); auto& name = _v.name; auto& type_args = _v.type_args; return name; @@ -2585,9 +2758,9 @@ struct CppCodegen { std::vector methods = this->extend_methods[obj_cat]; for (const auto& ext_m : methods) { { - const auto& _match_30 = ext_m; - if (std::holds_alternative::Function>(_match_30._data)) { - auto& _v = std::get::Function>(_match_30._data); + const auto& _match_34 = ext_m; + if (std::holds_alternative::Function>(_match_34._data)) { + auto& _v = std::get::Function>(_match_34._data); auto& mname = _v.name; auto& mparams = _v.params; auto& mret = _v.return_type; @@ -2617,9 +2790,9 @@ struct CppCodegen { TypeNode infer_source_type(const Expr& e) { { - const auto& _match_31 = e; - if (std::holds_alternative::Literal>(_match_31._data)) { - auto& _v = std::get::Literal>(_match_31._data); + const auto& _match_35 = e; + if (std::holds_alternative::Literal>(_match_35._data)) { + auto& _v = std::get::Literal>(_match_35._data); auto& kind = _v.kind; auto& value = _v.value; if ((kind == std::string("int"))) { @@ -2642,55 +2815,55 @@ struct CppCodegen { } return TypeNode::make_Auto(); } - else if (std::holds_alternative::Variable>(_match_31._data)) { - auto& _v = std::get::Variable>(_match_31._data); + else if (std::holds_alternative::Variable>(_match_35._data)) { + auto& _v = std::get::Variable>(_match_35._data); auto& tok = _v.name; if ((this->var_types.count(tok.lexeme) > 0)) { return this->var_types[tok.lexeme]; } return TypeNode::make_Auto(); } - else if (std::holds_alternative::Grouping>(_match_31._data)) { - auto& _v = std::get::Grouping>(_match_31._data); + else if (std::holds_alternative::Grouping>(_match_35._data)) { + auto& _v = std::get::Grouping>(_match_35._data); auto& inner = *_v.inner; return (*this).infer_source_type(inner); } - else if (std::holds_alternative::Cast>(_match_31._data)) { - auto& _v = std::get::Cast>(_match_31._data); + else if (std::holds_alternative::Cast>(_match_35._data)) { + auto& _v = std::get::Cast>(_match_35._data); auto& expr = *_v.expr; auto& target_type = _v.target_type; return target_type; } - else if (std::holds_alternative::Unary>(_match_31._data)) { - auto& _v = std::get::Unary>(_match_31._data); + else if (std::holds_alternative::Unary>(_match_35._data)) { + auto& _v = std::get::Unary>(_match_35._data); auto& op = _v.op; auto& right = *_v.right; return (*this).infer_source_type(right); } - else if (std::holds_alternative::Call>(_match_31._data)) { - auto& _v = std::get::Call>(_match_31._data); + else if (std::holds_alternative::Call>(_match_35._data)) { + auto& _v = std::get::Call>(_match_35._data); auto& callee = *_v.callee; auto& paren = _v.paren; auto& args = _v.args; auto& arg_names = _v.arg_names; { - const auto& _match_32 = callee; - if (std::holds_alternative::Get>(_match_32._data)) { - auto& _v = std::get::Get>(_match_32._data); + const auto& _match_36 = callee; + if (std::holds_alternative::Get>(_match_36._data)) { + auto& _v = std::get::Get>(_match_36._data); auto& obj = *_v.object; auto& method_tok = _v.name; TypeNode obj_type = (*this).infer_source_type(obj); std::string method = method_tok.lexeme; if ((method == std::string("at"))) { { - const auto& _match_33 = obj_type; - if (std::holds_alternative::Array>(_match_33._data)) { - auto& _v = std::get::Array>(_match_33._data); + const auto& _match_37 = obj_type; + if (std::holds_alternative::Array>(_match_37._data)) { + auto& _v = std::get::Array>(_match_37._data); auto& inner = *_v.inner; return inner; } - else if (std::holds_alternative::Custom>(_match_33._data)) { - auto& _v = std::get::Custom>(_match_33._data); + else if (std::holds_alternative::Custom>(_match_37._data)) { + auto& _v = std::get::Custom>(_match_37._data); auto& name = _v.name; auto& targs = _v.type_args; if ((static_cast(targs.size()) > INT64_C(0))) { @@ -2717,21 +2890,21 @@ struct CppCodegen { } return TypeNode::make_Auto(); } - else if (std::holds_alternative::Index>(_match_31._data)) { - auto& _v = std::get::Index>(_match_31._data); + else if (std::holds_alternative::Index>(_match_35._data)) { + auto& _v = std::get::Index>(_match_35._data); auto& obj = *_v.object; auto& bracket = _v.bracket; auto& idx = *_v.index; TypeNode obj_type = (*this).infer_source_type(obj); { - const auto& _match_34 = obj_type; - if (std::holds_alternative::Array>(_match_34._data)) { - auto& _v = std::get::Array>(_match_34._data); + const auto& _match_38 = obj_type; + if (std::holds_alternative::Array>(_match_38._data)) { + auto& _v = std::get::Array>(_match_38._data); auto& inner = *_v.inner; return inner; } - else if (std::holds_alternative::Custom>(_match_34._data)) { - auto& _v = std::get::Custom>(_match_34._data); + else if (std::holds_alternative::Custom>(_match_38._data)) { + auto& _v = std::get::Custom>(_match_38._data); auto& name = _v.name; auto& targs = _v.type_args; if ((static_cast(targs.size()) > INT64_C(0))) { @@ -2751,69 +2924,6 @@ struct CppCodegen { } } - bool is_integer_type(const TypeNode& t) { - { - const auto& _match_35 = t; - if (std::holds_alternative::Int>(_match_35._data)) { - return true; - } - else if (std::holds_alternative::Int8>(_match_35._data)) { - return true; - } - else if (std::holds_alternative::Int16>(_match_35._data)) { - return true; - } - else if (std::holds_alternative::Int32>(_match_35._data)) { - return true; - } - else if (std::holds_alternative::USize>(_match_35._data)) { - return true; - } - else { - return false; - } - } - } - - bool is_float_type(const TypeNode& t) { - { - const auto& _match_36 = t; - if (std::holds_alternative::Float>(_match_36._data)) { - return true; - } - else if (std::holds_alternative::Float32>(_match_36._data)) { - return true; - } - else { - return false; - } - } - } - - bool is_string_type(const TypeNode& t) { - { - const auto& _match_37 = t; - if (std::holds_alternative::Str>(_match_37._data)) { - return true; - } - else { - return false; - } - } - } - - bool is_bytes_type(const TypeNode& t) { - { - const auto& _match_38 = t; - if (std::holds_alternative::Bytes>(_match_38._data)) { - return true; - } - else { - return false; - } - } - } - std::string try_extend_method(const Expr& object, const std::string& method, const std::string& obj, const std::vector& args) { std::string type_cat = (*this).get_type_category(object); if ((type_cat != std::string("")) && (this->extend_methods.count(type_cat) > 0)) { @@ -3452,6 +3562,40 @@ struct CppCodegen { } } + bool has_method_named(const std::vector& methods, std::string name) { + for (const auto& m : methods) { + { + const auto& _match_55 = m; + if (std::holds_alternative::Function>(_match_55._data)) { + auto& _v = std::get::Function>(_match_55._data); + auto& mname = _v.name; + auto& mparams = _v.params; + auto& mret = _v.return_type; + auto& mbody = _v.body; + auto& mi = _v.is_inline; + auto& mc = _v.comptime_mode; + auto& ms = _v.is_static; + auto& mv = _v.visibility; + auto& mtp = _v.type_params; + auto& m_defs = _v.param_defaults; + if ((mname.lexeme == name)) { + return true; + } + } + else { + /* pass */ + } + } + } + return false; + } + + void emit_print_ops(const std::string& full_name, const std::string& tmpl_line, const std::string& to_str_expr) { + this->output = (this->output + ((((((std::string("") + (tmpl_line)) + std::string("void print(const ")) + (full_name)) + std::string("& _v) { std::cout << ")) + (to_str_expr)) + std::string(" << std::endl; }\n"))); + this->output = (this->output + ((((((std::string("") + (tmpl_line)) + std::string("std::string operator+(const std::string& _s, const ")) + (full_name)) + std::string("& _v) { return _s + ")) + (to_str_expr)) + std::string("; }\n"))); + this->output = (this->output + ((((((std::string("") + (tmpl_line)) + std::string("std::string operator+(const ")) + (full_name)) + std::string("& _v, const std::string& _s) { return ")) + (to_str_expr)) + std::string(" + _s; }\n\n"))); + } + void emit_class(const Token& name, const std::vector& body, const std::vector& type_params) { std::vector init_body = {}; std::vector init_params = {}; @@ -3462,9 +3606,9 @@ struct CppCodegen { std::vector let_field_types = {}; for (const auto& st : body) { { - const auto& _match_55 = st; - if (std::holds_alternative::Function>(_match_55._data)) { - auto& _v = std::get::Function>(_match_55._data); + const auto& _match_56 = st; + if (std::holds_alternative::Function>(_match_56._data)) { + auto& _v = std::get::Function>(_match_56._data); auto& fname = _v.name; auto& params = _v.params; auto& return_type = _v.return_type; @@ -3485,8 +3629,8 @@ struct CppCodegen { methods.push_back(st); } } - else if (std::holds_alternative::Let>(_match_55._data)) { - auto& _v = std::get::Let>(_match_55._data); + else if (std::holds_alternative::Let>(_match_56._data)) { + auto& _v = std::get::Let>(_match_56._data); auto& fname = _v.name; auto& var_type = _v.var_type; auto& init = _v.initializer; @@ -3513,21 +3657,21 @@ struct CppCodegen { std::vector seen = {}; for (const auto& st : init_body) { { - const auto& _match_56 = st; - if (std::holds_alternative::ExprStmt>(_match_56._data)) { - auto& _v = std::get::ExprStmt>(_match_56._data); + const auto& _match_57 = st; + if (std::holds_alternative::ExprStmt>(_match_57._data)) { + auto& _v = std::get::ExprStmt>(_match_57._data); auto& expr = _v.expr; { - const auto& _match_57 = expr; - if (std::holds_alternative::Set>(_match_57._data)) { - auto& _v = std::get::Set>(_match_57._data); + const auto& _match_58 = expr; + if (std::holds_alternative::Set>(_match_58._data)) { + auto& _v = std::get::Set>(_match_58._data); auto& object = *_v.object; auto& prop = _v.name; auto& value = *_v.value; { - const auto& _match_58 = object; - if (std::holds_alternative::This>(_match_58._data)) { - auto& _v = std::get::This>(_match_58._data); + const auto& _match_59 = object; + if (std::holds_alternative::This>(_match_59._data)) { + auto& _v = std::get::This>(_match_59._data); auto& kw = _v.keyword; bool already = false; for (const auto& s : seen) { @@ -3571,41 +3715,13 @@ struct CppCodegen { } this->indent_level = (this->indent_level - INT64_C(1)); this->output = (this->output + ((std::string("") + ((*this).indent())) + std::string("};\n\n"))); - bool has_to_string = false; - for (const auto& m : methods) { - { - const auto& _match_59 = m; - if (std::holds_alternative::Function>(_match_59._data)) { - auto& _v = std::get::Function>(_match_59._data); - auto& mname = _v.name; - auto& mparams = _v.params; - auto& mret = _v.return_type; - auto& mbody = _v.body; - auto& mi = _v.is_inline; - auto& mc = _v.comptime_mode; - auto& ms = _v.is_static; - auto& mv = _v.visibility; - auto& mtp = _v.type_params; - auto& m_defs = _v.param_defaults; - if ((mname.lexeme == std::string("to_string"))) { - has_to_string = true; - } - } - else { - /* pass */ - } - } - } - if (has_to_string) { + if ((*this).has_method_named(methods, std::string("to_string"))) { std::string tmpl_line = (*this).template_prefix(type_params); std::string type_suffix = std::string(""); if ((static_cast(type_params.size()) > INT64_C(0))) { type_suffix = ((std::string("<") + (lv_join(type_params, std::string(", ")))) + std::string(">")); } - std::string full_name = ((((std::string("") + (name.lexeme)) + std::string("")) + (type_suffix)) + std::string("")); - this->output = (this->output + ((((((std::string("") + (tmpl_line)) + std::string("")) + ((*this).indent())) + std::string("void print(const ")) + (full_name)) + std::string("& _v) { std::cout << _v.to_string() << std::endl; }\n"))); - this->output = (this->output + ((((((std::string("") + (tmpl_line)) + std::string("")) + ((*this).indent())) + std::string("std::string operator+(const std::string& _s, const ")) + (full_name)) + std::string("& _v) { return _s + _v.to_string(); }\n"))); - this->output = (this->output + ((((((std::string("") + (tmpl_line)) + std::string("")) + ((*this).indent())) + std::string("std::string operator+(const ")) + (full_name)) + std::string("& _v, const std::string& _s) { return _v.to_string() + _s; }\n\n"))); + (*this).emit_print_ops(((((std::string("") + (name.lexeme)) + std::string("")) + (type_suffix)) + std::string("")), tmpl_line, std::string("_v.to_string()")); } } @@ -3850,15 +3966,14 @@ struct CppCodegen { this->output = (this->output + ((std::string("") + ((*this).indent())) + std::string("}\n"))); this->indent_level = (this->indent_level - INT64_C(1)); this->output = (this->output + ((std::string("") + ((*this).indent())) + std::string("};\n\n"))); + std::string full_type = ((((std::string("") + (enum_name)) + std::string("")) + (type_suffix)) + std::string("")); if (has_to_string) { - this->output = (this->output + ((((((std::string("") + (tmpl_line)) + std::string("void print(const ")) + (enum_name)) + std::string("")) + (type_suffix)) + std::string("& e) { std::cout << e.to_string() << std::endl; }\n"))); - this->output = (this->output + ((((((std::string("") + (tmpl_line)) + std::string("std::string operator+(const std::string& s, const ")) + (enum_name)) + std::string("")) + (type_suffix)) + std::string("& e) { return s + e.to_string(); }\n"))); - this->output = (this->output + ((((((std::string("") + (tmpl_line)) + std::string("std::string operator+(const ")) + (enum_name)) + std::string("")) + (type_suffix)) + std::string("& e, const std::string& s) { return e.to_string() + s; }\n\n"))); + (*this).emit_print_ops(full_type, tmpl_line, std::string("_v.to_string()")); } else { - this->output = (this->output + ((((((((std::string("") + (tmpl_line)) + std::string("void print(const ")) + (enum_name)) + std::string("")) + (type_suffix)) + std::string("& e) { std::cout << \"")) + (enum_name)) + std::string("(\" << e._tag << \")\" << std::endl; }\n"))); - this->output = (this->output + ((((((std::string("") + (tmpl_line)) + std::string("std::string operator+(const std::string& s, const ")) + (enum_name)) + std::string("")) + (type_suffix)) + std::string("& e) { return s + e._tag; }\n"))); - this->output = (this->output + ((((((std::string("") + (tmpl_line)) + std::string("std::string operator+(const ")) + (enum_name)) + std::string("")) + (type_suffix)) + std::string("& e, const std::string& s) { return e._tag + s; }\n\n"))); + this->output = (this->output + ((((((std::string("") + (tmpl_line)) + std::string("void print(const ")) + (full_type)) + std::string("& _v) { std::cout << \"")) + (enum_name)) + std::string("(\" << _v._tag << \")\" << std::endl; }\n"))); + this->output = (this->output + ((((std::string("") + (tmpl_line)) + std::string("std::string operator+(const std::string& _s, const ")) + (full_type)) + std::string("& _v) { return _s + _v._tag; }\n"))); + this->output = (this->output + ((((std::string("") + (tmpl_line)) + std::string("std::string operator+(const ")) + (full_type)) + std::string("& _v, const std::string& _s) { return _v._tag + _s; }\n\n"))); } } @@ -3899,31 +4014,7 @@ struct CppCodegen { for (const auto& m : methods) { (*this).emit_class_method(name, m); } - bool has_to_string = false; - for (const auto& m : methods) { - { - const auto& _match_66 = m; - if (std::holds_alternative::Function>(_match_66._data)) { - auto& _v = std::get::Function>(_match_66._data); - auto& mname = _v.name; - auto& mparams = _v.params; - auto& mret = _v.return_type; - auto& mbody = _v.body; - auto& mi = _v.is_inline; - auto& mc = _v.comptime_mode; - auto& ms = _v.is_static; - auto& mv = _v.visibility; - auto& mtp = _v.type_params; - auto& m_defs = _v.param_defaults; - if ((mname.lexeme == std::string("to_string"))) { - has_to_string = true; - } - } - else { - /* pass */ - } - } - } + bool has_to_string = (*this).has_method_named(methods, std::string("to_string")); (*this).emit_enum_operators(enum_name, type_suffix, tmpl_line, has_to_string); } @@ -3956,7 +4047,7 @@ struct CppCodegen { keyword = std::string("else if"); } first = false; - std::string ename = (*this).find_enum_for_variant(arm.pattern_name); + std::string ename = find_enum_for_variant(this->known_enums, arm.pattern_name); if ((ename != std::string(""))) { this->output = (this->output + ((((((((((std::string("") + ((*this).indent())) + std::string("")) + (keyword)) + std::string(" (std::holds_alternative::")) + (arm.pattern_name)) + std::string(">(")) + (temp)) + std::string("._data)) {\n"))); } @@ -3974,9 +4065,9 @@ struct CppCodegen { bool is_self_ref = false; if ((bi < static_cast(vinfo.types.size()))) { { - const auto& _match_67 = vinfo.types[bi]; - if (std::holds_alternative::Custom>(_match_67._data)) { - auto& _v = std::get::Custom>(_match_67._data); + const auto& _match_66 = vinfo.types[bi]; + if (std::holds_alternative::Custom>(_match_66._data)) { + auto& _v = std::get::Custom>(_match_66._data); auto& n = _v.name; auto& _ = _v.type_args; if ((n == ename)) { @@ -4009,9 +4100,9 @@ struct CppCodegen { void emit_arm_body(const Stmt& arm_body, bool in_method) { { - const auto& _match_68 = arm_body; - if (std::holds_alternative::Block>(_match_68._data)) { - auto& _v = std::get::Block>(_match_68._data); + const auto& _match_67 = arm_body; + if (std::holds_alternative::Block>(_match_67._data)) { + auto& _v = std::get::Block>(_match_67._data); auto& stmts = _v.statements; for (const auto& st : stmts) { (*this).emit_stmt(st, in_method); @@ -4025,9 +4116,9 @@ struct CppCodegen { void emit_using_if_public(const std::string& ns, const Stmt& stmt) { { - const auto& _match_69 = stmt; - if (std::holds_alternative::Function>(_match_69._data)) { - auto& _v = std::get::Function>(_match_69._data); + const auto& _match_68 = stmt; + if (std::holds_alternative::Function>(_match_68._data)) { + auto& _v = std::get::Function>(_match_68._data); auto& name = _v.name; auto& params = _v.params; auto& return_type = _v.return_type; @@ -4042,8 +4133,8 @@ struct CppCodegen { this->output = (this->output + ((((std::string("using ") + (ns)) + std::string("::")) + (name.lexeme)) + std::string(";\n"))); } } - else if (std::holds_alternative::Class>(_match_69._data)) { - auto& _v = std::get::Class>(_match_69._data); + else if (std::holds_alternative::Class>(_match_68._data)) { + auto& _v = std::get::Class>(_match_68._data); auto& name = _v.name; auto& body = _v.body; auto& visibility = _v.visibility; @@ -4051,8 +4142,8 @@ struct CppCodegen { this->output = (this->output + ((((std::string("using ") + (ns)) + std::string("::")) + (name.lexeme)) + std::string(";\n"))); } } - else if (std::holds_alternative::Struct>(_match_69._data)) { - auto& _v = std::get::Struct>(_match_69._data); + else if (std::holds_alternative::Struct>(_match_68._data)) { + auto& _v = std::get::Struct>(_match_68._data); auto& name = _v.name; auto& body = _v.body; auto& visibility = _v.visibility; @@ -4061,8 +4152,8 @@ struct CppCodegen { this->output = (this->output + ((((std::string("using ") + (ns)) + std::string("::")) + (name.lexeme)) + std::string(";\n"))); } } - else if (std::holds_alternative::Enum>(_match_69._data)) { - auto& _v = std::get::Enum>(_match_69._data); + else if (std::holds_alternative::Enum>(_match_68._data)) { + auto& _v = std::get::Enum>(_match_68._data); auto& name = _v.name; auto& variants = _v.variants; auto& methods = _v.methods; @@ -4072,8 +4163,8 @@ struct CppCodegen { this->output = (this->output + ((((std::string("using ") + (ns)) + std::string("::")) + (name.lexeme)) + std::string(";\n"))); } } - else if (std::holds_alternative::Const>(_match_69._data)) { - auto& _v = std::get::Const>(_match_69._data); + else if (std::holds_alternative::Const>(_match_68._data)) { + auto& _v = std::get::Const>(_match_68._data); auto& name = _v.name; auto& const_type = _v.const_type; auto& value = _v.value; @@ -4083,8 +4174,8 @@ struct CppCodegen { this->output = (this->output + ((((std::string("using ") + (ns)) + std::string("::")) + (name.lexeme)) + std::string(";\n"))); } } - else if (std::holds_alternative::Let>(_match_69._data)) { - auto& _v = std::get::Let>(_match_69._data); + else if (std::holds_alternative::Let>(_match_68._data)) { + auto& _v = std::get::Let>(_match_68._data); auto& name = _v.name; auto& var_type = _v.var_type; auto& initializer = _v.initializer; @@ -4109,9 +4200,9 @@ struct CppCodegen { this->indent_level = INT64_C(1); for (const auto& stmt : this->module_stmts[index]) { { - const auto& _match_70 = stmt; - if (std::holds_alternative::Function>(_match_70._data)) { - auto& _v = std::get::Function>(_match_70._data); + const auto& _match_69 = stmt; + if (std::holds_alternative::Function>(_match_69._data)) { + auto& _v = std::get::Function>(_match_69._data); auto& name = _v.name; auto& params = _v.params; auto& return_type = _v.return_type; @@ -4129,8 +4220,8 @@ struct CppCodegen { (*this).emit_stmt(stmt, false); } } - else if (std::holds_alternative::Extern>(_match_70._data)) { - auto& _v = std::get::Extern>(_match_70._data); + else if (std::holds_alternative::Extern>(_match_69._data)) { + auto& _v = std::get::Extern>(_match_69._data); auto& header = _v.header; auto& import_path = _v.import_path; auto& link_lib = _v.link_lib; @@ -4161,32 +4252,12 @@ struct CppCodegen { this->output = std::string(""); } - std::string generate(const std::vector& stmts) { - for (int64_t mi = INT64_C(0); mi < static_cast(this->module_stmts.size()); mi++) { - for (const auto& stmt : this->module_stmts[mi]) { - { - const auto& _match_71 = stmt; - if (std::holds_alternative::Extern>(_match_71._data)) { - auto& _v = std::get::Extern>(_match_71._data); - auto& header = _v.header; - auto& import_path = _v.import_path; - auto& link_lib = _v.link_lib; - auto& types = _v.types; - auto& functions = _v.functions; - (*this).emit_stmt(stmt, false); - this->output = std::string(""); - } - else { - /* pass */ - } - } - } - } + void collect_externs(const std::vector& stmts) { for (const auto& stmt : stmts) { { - const auto& _match_72 = stmt; - if (std::holds_alternative::Extern>(_match_72._data)) { - auto& _v = std::get::Extern>(_match_72._data); + const auto& _match_70 = stmt; + if (std::holds_alternative::Extern>(_match_70._data)) { + auto& _v = std::get::Extern>(_match_70._data); auto& header = _v.header; auto& import_path = _v.import_path; auto& link_lib = _v.link_lib; @@ -4200,35 +4271,14 @@ struct CppCodegen { } } } - for (int64_t mi = INT64_C(0); mi < static_cast(this->module_stmts.size()); mi++) { - for (const auto& stmt : this->module_stmts[mi]) { - { - const auto& _match_73 = stmt; - if (std::holds_alternative::Extend>(_match_73._data)) { - auto& _v = std::get::Extend>(_match_73._data); - auto& target = _v.target_type; - auto& methods = _v.methods; - auto& visibility = _v.visibility; - std::string key = target.lexeme; - if ((!(this->extend_methods.count(key) > 0))) { - std::vector empty = {}; - this->extend_methods[key] = empty; - } - for (const auto& m : methods) { - this->extend_methods[key].push_back(m); - } - } - else { - /* pass */ - } - } - } - } + } + + void collect_extends(const std::vector& stmts) { for (const auto& stmt : stmts) { { - const auto& _match_74 = stmt; - if (std::holds_alternative::Extend>(_match_74._data)) { - auto& _v = std::get::Extend>(_match_74._data); + const auto& _match_71 = stmt; + if (std::holds_alternative::Extend>(_match_71._data)) { + auto& _v = std::get::Extend>(_match_71._data); auto& target = _v.target_type; auto& methods = _v.methods; auto& visibility = _v.visibility; @@ -4246,28 +4296,14 @@ struct CppCodegen { } } } - this->declarations = std::string("#include \"lavina.h\"\n"); - for (const auto& inc : this->extern_includes) { - this->declarations = (this->declarations + ((std::string("") + (inc)) + std::string("\n"))); - } - this->declarations = (this->declarations + std::string("\n")); - for (int64_t mi = INT64_C(0); mi < static_cast(this->module_stmts.size()); mi++) { - (*this).emit_module(mi); - } - auto ext_keys = lv_keys(this->extend_methods); - for (const auto& ek : ext_keys) { - std::vector methods = this->extend_methods[ek]; - for (const auto& m : methods) { - (*this).emit_extend_method(ek, m); - } - this->declarations = (this->declarations + this->output); - this->output = std::string(""); - } + } + + void emit_main_stmts(const std::vector& stmts) { for (const auto& stmt : stmts) { { - const auto& _match_75 = stmt; - if (std::holds_alternative::Function>(_match_75._data)) { - auto& _v = std::get::Function>(_match_75._data); + const auto& _match_72 = stmt; + if (std::holds_alternative::Function>(_match_72._data)) { + auto& _v = std::get::Function>(_match_72._data); auto& name = _v.name; auto& params = _v.params; auto& return_type = _v.return_type; @@ -4297,8 +4333,8 @@ struct CppCodegen { this->output = std::string(""); } } - else if (std::holds_alternative::Extern>(_match_75._data)) { - auto& _v = std::get::Extern>(_match_75._data); + else if (std::holds_alternative::Extern>(_match_72._data)) { + auto& _v = std::get::Extern>(_match_72._data); auto& header = _v.header; auto& import_path = _v.import_path; auto& link_lib = _v.link_lib; @@ -4306,8 +4342,8 @@ struct CppCodegen { auto& functions = _v.functions; /* pass */ } - else if (std::holds_alternative::Extend>(_match_75._data)) { - auto& _v = std::get::Extend>(_match_75._data); + else if (std::holds_alternative::Extend>(_match_72._data)) { + auto& _v = std::get::Extend>(_match_72._data); auto& target = _v.target_type; auto& methods = _v.methods; auto& visibility = _v.visibility; @@ -4320,6 +4356,35 @@ struct CppCodegen { } } } + } + + std::string generate(const std::vector& stmts) { + for (int64_t mi = INT64_C(0); mi < static_cast(this->module_stmts.size()); mi++) { + (*this).collect_externs(this->module_stmts[mi]); + } + (*this).collect_externs(stmts); + for (int64_t mi = INT64_C(0); mi < static_cast(this->module_stmts.size()); mi++) { + (*this).collect_extends(this->module_stmts[mi]); + } + (*this).collect_extends(stmts); + this->declarations = std::string("#include \"lavina.h\"\n"); + for (const auto& inc : this->extern_includes) { + this->declarations = (this->declarations + ((std::string("") + (inc)) + std::string("\n"))); + } + this->declarations = (this->declarations + std::string("\n")); + for (int64_t mi = INT64_C(0); mi < static_cast(this->module_stmts.size()); mi++) { + (*this).emit_module(mi); + } + auto ext_keys = lv_keys(this->extend_methods); + for (const auto& ek : ext_keys) { + std::vector methods = this->extend_methods[ek]; + for (const auto& m : methods) { + (*this).emit_extend_method(ek, m); + } + this->declarations = (this->declarations + this->output); + this->output = std::string(""); + } + (*this).emit_main_stmts(stmts); return this->declarations; } @@ -4367,10 +4432,6 @@ struct Checker { this->errors.push_back(((((((std::string("[") + (t.line)) + std::string(":")) + (t.col)) + std::string("] Error: ")) + (msg)) + std::string(""))); } - void error_msg(const std::string& msg) { - this->errors.push_back(((std::string("Error: ") + (msg)) + std::string(""))); - } - void warn(const std::string& msg, const Token& t) { this->warnings.push_back(((((((std::string("[") + (t.line)) + std::string(":")) + (t.col)) + std::string("] Warning: ")) + (msg)) + std::string(""))); } @@ -4447,27 +4508,27 @@ struct Checker { bool types_compatible(const TypeNode& expected, const TypeNode& actual) { { - const auto& _match_76 = actual; - if (std::holds_alternative::Array>(_match_76._data)) { - auto& _v = std::get::Array>(_match_76._data); + const auto& _match_73 = actual; + if (std::holds_alternative::Array>(_match_73._data)) { + auto& _v = std::get::Array>(_match_73._data); auto& a_inner = *_v.inner; { - const auto& _match_77 = a_inner; - if (std::holds_alternative::Auto>(_match_77._data)) { + const auto& _match_74 = a_inner; + if (std::holds_alternative::Auto>(_match_74._data)) { { - const auto& _match_78 = expected; - if (std::holds_alternative::Array>(_match_78._data)) { - auto& _v = std::get::Array>(_match_78._data); + const auto& _match_75 = expected; + if (std::holds_alternative::Array>(_match_75._data)) { + auto& _v = std::get::Array>(_match_75._data); auto& e_inner = *_v.inner; return true; } - else if (std::holds_alternative::HashSet>(_match_78._data)) { - auto& _v = std::get::HashSet>(_match_78._data); + else if (std::holds_alternative::HashSet>(_match_75._data)) { + auto& _v = std::get::HashSet>(_match_75._data); auto& e_inner = *_v.inner; return true; } - else if (std::holds_alternative::HashMap>(_match_78._data)) { - auto& _v = std::get::HashMap>(_match_78._data); + else if (std::holds_alternative::HashMap>(_match_75._data)) { + auto& _v = std::get::HashMap>(_match_75._data); auto& ek = *_v.key_type; auto& ev = *_v.value_type; return true; @@ -4487,14 +4548,14 @@ struct Checker { } } { - const auto& _match_79 = expected; - if (std::holds_alternative::Array>(_match_79._data)) { - auto& _v = std::get::Array>(_match_79._data); + const auto& _match_76 = expected; + if (std::holds_alternative::Array>(_match_76._data)) { + auto& _v = std::get::Array>(_match_76._data); auto& e_inner = *_v.inner; { - const auto& _match_80 = actual; - if (std::holds_alternative::Array>(_match_80._data)) { - auto& _v = std::get::Array>(_match_80._data); + const auto& _match_77 = actual; + if (std::holds_alternative::Array>(_match_77._data)) { + auto& _v = std::get::Array>(_match_77._data); auto& a_inner = *_v.inner; return (*this).types_compatible(e_inner, a_inner); } @@ -4508,14 +4569,14 @@ struct Checker { } } { - const auto& _match_81 = expected; - if (std::holds_alternative::Auto>(_match_81._data)) { + const auto& _match_78 = expected; + if (std::holds_alternative::Auto>(_match_78._data)) { return true; } - else if (_match_81._tag == "None") { + else if (_match_78._tag == "None") { return true; } - else if (std::holds_alternative::Dynamic>(_match_81._data)) { + else if (std::holds_alternative::Dynamic>(_match_78._data)) { return true; } else { @@ -4523,98 +4584,32 @@ struct Checker { } } { - const auto& _match_82 = actual; - if (std::holds_alternative::Auto>(_match_82._data)) { + const auto& _match_79 = actual; + if (std::holds_alternative::Auto>(_match_79._data)) { return true; } - else if (_match_82._tag == "None") { + else if (_match_79._tag == "None") { return true; } - else if (std::holds_alternative::Dynamic>(_match_82._data)) { + else if (std::holds_alternative::Dynamic>(_match_79._data)) { return true; } else { /* pass */ } } - std::string e = (*this).type_name(expected); - std::string a = (*this).type_name(actual); + std::string e = type_to_display(expected); + std::string a = type_to_display(actual); if ((e == a)) { return true; } - bool e_is_int = false; - bool a_is_int = false; - { - const auto& _match_83 = expected; - if (std::holds_alternative::Int>(_match_83._data)) { - e_is_int = true; - } - else if (std::holds_alternative::Int8>(_match_83._data)) { - e_is_int = true; - } - else if (std::holds_alternative::Int16>(_match_83._data)) { - e_is_int = true; - } - else if (std::holds_alternative::Int32>(_match_83._data)) { - e_is_int = true; - } - else if (std::holds_alternative::USize>(_match_83._data)) { - e_is_int = true; - } - else { - /* pass */ - } - } - { - const auto& _match_84 = actual; - if (std::holds_alternative::Int>(_match_84._data)) { - a_is_int = true; - } - else if (std::holds_alternative::Int8>(_match_84._data)) { - a_is_int = true; - } - else if (std::holds_alternative::Int16>(_match_84._data)) { - a_is_int = true; - } - else if (std::holds_alternative::Int32>(_match_84._data)) { - a_is_int = true; - } - else if (std::holds_alternative::USize>(_match_84._data)) { - a_is_int = true; - } - else { - /* pass */ - } - } + bool e_is_int = is_integer_type(expected); + bool a_is_int = is_integer_type(actual); if (e_is_int && a_is_int) { return true; } - bool e_is_float = false; - bool a_is_float = false; - { - const auto& _match_85 = expected; - if (std::holds_alternative::Float>(_match_85._data)) { - e_is_float = true; - } - else if (std::holds_alternative::Float32>(_match_85._data)) { - e_is_float = true; - } - else { - /* pass */ - } - } - { - const auto& _match_86 = actual; - if (std::holds_alternative::Float>(_match_86._data)) { - a_is_float = true; - } - else if (std::holds_alternative::Float32>(_match_86._data)) { - a_is_float = true; - } - else { - /* pass */ - } - } + bool e_is_float = is_float_type(expected); + bool a_is_float = is_float_type(actual); if (e_is_float && a_is_float) { return true; } @@ -4622,11 +4617,11 @@ struct Checker { return true; } { - const auto& _match_87 = expected; - if (std::holds_alternative::CString>(_match_87._data)) { + const auto& _match_80 = expected; + if (std::holds_alternative::CString>(_match_80._data)) { { - const auto& _match_88 = actual; - if (std::holds_alternative::Str>(_match_88._data)) { + const auto& _match_81 = actual; + if (std::holds_alternative::Str>(_match_81._data)) { return true; } else { @@ -4634,10 +4629,10 @@ struct Checker { } } } - else if (std::holds_alternative::Str>(_match_87._data)) { + else if (std::holds_alternative::Str>(_match_80._data)) { { - const auto& _match_89 = actual; - if (std::holds_alternative::CString>(_match_89._data)) { + const auto& _match_82 = actual; + if (std::holds_alternative::CString>(_match_82._data)) { return true; } else { @@ -4650,13 +4645,13 @@ struct Checker { } } { - const auto& _match_90 = expected; - if (std::holds_alternative::Nullable>(_match_90._data)) { - auto& _v = std::get::Nullable>(_match_90._data); + const auto& _match_83 = expected; + if (std::holds_alternative::Nullable>(_match_83._data)) { + auto& _v = std::get::Nullable>(_match_83._data); auto& inner = *_v.inner; { - const auto& _match_91 = actual; - if (std::holds_alternative::NullType>(_match_91._data)) { + const auto& _match_84 = actual; + if (std::holds_alternative::NullType>(_match_84._data)) { return true; } else { @@ -4669,13 +4664,13 @@ struct Checker { } } { - const auto& _match_92 = expected; - if (std::holds_alternative::Ptr>(_match_92._data)) { - auto& _v = std::get::Ptr>(_match_92._data); + const auto& _match_85 = expected; + if (std::holds_alternative::Ptr>(_match_85._data)) { + auto& _v = std::get::Ptr>(_match_85._data); auto& inner = *_v.inner; { - const auto& _match_93 = actual; - if (std::holds_alternative::NullType>(_match_93._data)) { + const auto& _match_86 = actual; + if (std::holds_alternative::NullType>(_match_86._data)) { return true; } else { @@ -4690,100 +4685,14 @@ struct Checker { return false; } - std::string type_name(const TypeNode& t) { - { - const auto& _match_94 = t; - if (std::holds_alternative::Int>(_match_94._data)) { - return std::string("int"); - } - else if (std::holds_alternative::Float>(_match_94._data)) { - return std::string("float"); - } - else if (std::holds_alternative::Str>(_match_94._data)) { - return std::string("string"); - } - else if (std::holds_alternative::Bool>(_match_94._data)) { - return std::string("bool"); - } - else if (std::holds_alternative::Void>(_match_94._data)) { - return std::string("void"); - } - else if (std::holds_alternative::Auto>(_match_94._data)) { - return std::string("auto"); - } - else if (std::holds_alternative::Dynamic>(_match_94._data)) { - return std::string("dynamic"); - } - else if (std::holds_alternative::NullType>(_match_94._data)) { - return std::string("null"); - } - else if (std::holds_alternative::Custom>(_match_94._data)) { - auto& _v = std::get::Custom>(_match_94._data); - auto& name = _v.name; - auto& _ = _v.type_args; - return name; - } - else if (std::holds_alternative::Array>(_match_94._data)) { - auto& _v = std::get::Array>(_match_94._data); - auto& inner = *_v.inner; - return ((std::string("vector[") + ((*this).type_name(inner))) + std::string("]")); - } - else if (std::holds_alternative::HashSet>(_match_94._data)) { - auto& _v = std::get::HashSet>(_match_94._data); - auto& inner = *_v.inner; - return ((std::string("set[") + ((*this).type_name(inner))) + std::string("]")); - } - else if (std::holds_alternative::HashMap>(_match_94._data)) { - auto& _v = std::get::HashMap>(_match_94._data); - auto& k = *_v.key_type; - auto& v = *_v.value_type; - return ((((std::string("map[") + ((*this).type_name(k))) + std::string(", ")) + ((*this).type_name(v))) + std::string("]")); - } - else if (std::holds_alternative::Nullable>(_match_94._data)) { - auto& _v = std::get::Nullable>(_match_94._data); - auto& inner = *_v.inner; - return ((std::string("") + ((*this).type_name(inner))) + std::string("?")); - } - else if (std::holds_alternative::Int8>(_match_94._data)) { - return std::string("int8"); - } - else if (std::holds_alternative::Int16>(_match_94._data)) { - return std::string("int16"); - } - else if (std::holds_alternative::Int32>(_match_94._data)) { - return std::string("int32"); - } - else if (std::holds_alternative::Float32>(_match_94._data)) { - return std::string("float32"); - } - else if (std::holds_alternative::USize>(_match_94._data)) { - return std::string("usize"); - } - else if (std::holds_alternative::CString>(_match_94._data)) { - return std::string("cstring"); - } - else if (std::holds_alternative::Ptr>(_match_94._data)) { - auto& _v = std::get::Ptr>(_match_94._data); - auto& inner = *_v.inner; - return ((std::string("ptr[") + ((*this).type_name(inner))) + std::string("]")); - } - else if (std::holds_alternative::Bytes>(_match_94._data)) { - return std::string("bytes"); - } - else { - return std::string("unknown"); - } - } - } - TypeNode infer_type(const Expr& e) { { - const auto& _match_95 = e; - if (_match_95._tag == "None") { + const auto& _match_87 = e; + if (_match_87._tag == "None") { return TypeNode::make_None(); } - else if (std::holds_alternative::Literal>(_match_95._data)) { - auto& _v = std::get::Literal>(_match_95._data); + else if (std::holds_alternative::Literal>(_match_87._data)) { + auto& _v = std::get::Literal>(_match_87._data); auto& kind = _v.kind; auto& value = _v.value; if ((kind == std::string("int"))) { @@ -4811,14 +4720,14 @@ struct Checker { } return TypeNode::make_Auto(); } - else if (std::holds_alternative::Variable>(_match_95._data)) { - auto& _v = std::get::Variable>(_match_95._data); + else if (std::holds_alternative::Variable>(_match_87._data)) { + auto& _v = std::get::Variable>(_match_87._data); auto& name = _v.name; auto sym = (*this).lookup(name.lexeme); return sym.sym_type; } - else if (std::holds_alternative::Binary>(_match_95._data)) { - auto& _v = std::get::Binary>(_match_95._data); + else if (std::holds_alternative::Binary>(_match_87._data)) { + auto& _v = std::get::Binary>(_match_87._data); auto& left = *_v.left; auto& op = _v.op; auto& right = *_v.right; @@ -4828,8 +4737,8 @@ struct Checker { return TypeNode::make_Bool(); } { - const auto& _match_96 = lt; - if (std::holds_alternative::Str>(_match_96._data)) { + const auto& _match_88 = lt; + if (std::holds_alternative::Str>(_match_88._data)) { return TypeNode::make_Str(); } else { @@ -4837,8 +4746,8 @@ struct Checker { } } { - const auto& _match_97 = lt; - if (std::holds_alternative::Float>(_match_97._data)) { + const auto& _match_89 = lt; + if (std::holds_alternative::Float>(_match_89._data)) { return TypeNode::make_Float(); } else { @@ -4846,8 +4755,8 @@ struct Checker { } } { - const auto& _match_98 = rt; - if (std::holds_alternative::Float>(_match_98._data)) { + const auto& _match_90 = rt; + if (std::holds_alternative::Float>(_match_90._data)) { return TypeNode::make_Float(); } else { @@ -4855,8 +4764,8 @@ struct Checker { } } { - const auto& _match_99 = lt; - if (std::holds_alternative::Int>(_match_99._data)) { + const auto& _match_91 = lt; + if (std::holds_alternative::Int>(_match_91._data)) { return TypeNode::make_Int(); } else { @@ -4865,8 +4774,8 @@ struct Checker { } return TypeNode::make_Auto(); } - else if (std::holds_alternative::Unary>(_match_95._data)) { - auto& _v = std::get::Unary>(_match_95._data); + else if (std::holds_alternative::Unary>(_match_87._data)) { + auto& _v = std::get::Unary>(_match_87._data); auto& op = _v.op; auto& right = *_v.right; if ((op.token_type == TK_BANG) || (op.token_type == TK_NOT)) { @@ -4874,36 +4783,36 @@ struct Checker { } return (*this).infer_type(right); } - else if (std::holds_alternative::Logical>(_match_95._data)) { - auto& _v = std::get::Logical>(_match_95._data); + else if (std::holds_alternative::Logical>(_match_87._data)) { + auto& _v = std::get::Logical>(_match_87._data); auto& left = *_v.left; auto& op = _v.op; auto& right = *_v.right; return TypeNode::make_Bool(); } - else if (std::holds_alternative::Call>(_match_95._data)) { - auto& _v = std::get::Call>(_match_95._data); + else if (std::holds_alternative::Call>(_match_87._data)) { + auto& _v = std::get::Call>(_match_87._data); auto& callee = *_v.callee; auto& paren = _v.paren; auto& args = _v.args; auto& arg_names = _v.arg_names; return (*this).infer_call_type(callee); } - else if (std::holds_alternative::Grouping>(_match_95._data)) { - auto& _v = std::get::Grouping>(_match_95._data); + else if (std::holds_alternative::Grouping>(_match_87._data)) { + auto& _v = std::get::Grouping>(_match_87._data); auto& inner = *_v.inner; return (*this).infer_type(inner); } - else if (std::holds_alternative::Index>(_match_95._data)) { - auto& _v = std::get::Index>(_match_95._data); + else if (std::holds_alternative::Index>(_match_87._data)) { + auto& _v = std::get::Index>(_match_87._data); auto& object = *_v.object; auto& bracket = _v.bracket; auto& index = *_v.index; auto ot = (*this).infer_type(object); { - const auto& _match_100 = ot; - if (std::holds_alternative::Array>(_match_100._data)) { - auto& _v = std::get::Array>(_match_100._data); + const auto& _match_92 = ot; + if (std::holds_alternative::Array>(_match_92._data)) { + auto& _v = std::get::Array>(_match_92._data); auto& inner = *_v.inner; return inner; } @@ -4912,8 +4821,8 @@ struct Checker { } } } - else if (std::holds_alternative::Vector>(_match_95._data)) { - auto& _v = std::get::Vector>(_match_95._data); + else if (std::holds_alternative::Vector>(_match_87._data)) { + auto& _v = std::get::Vector>(_match_87._data); auto& elements = _v.elements; if ((static_cast(elements.size()) > INT64_C(0))) { auto inner = (*this).infer_type(elements[INT64_C(0)]); @@ -4921,24 +4830,24 @@ struct Checker { } return TypeNode::make_Array(TypeNode::make_Auto()); } - else if (std::holds_alternative::Cast>(_match_95._data)) { - auto& _v = std::get::Cast>(_match_95._data); + else if (std::holds_alternative::Cast>(_match_87._data)) { + auto& _v = std::get::Cast>(_match_87._data); auto& expr = *_v.expr; auto& target_type = _v.target_type; return target_type; } - else if (std::holds_alternative::This>(_match_95._data)) { - auto& _v = std::get::This>(_match_95._data); + else if (std::holds_alternative::This>(_match_87._data)) { + auto& _v = std::get::This>(_match_87._data); auto& kw = _v.keyword; return TypeNode::make_Custom(this->current_class_name, {}); } - else if (std::holds_alternative::Own>(_match_95._data)) { - auto& _v = std::get::Own>(_match_95._data); + else if (std::holds_alternative::Own>(_match_87._data)) { + auto& _v = std::get::Own>(_match_87._data); auto& expr = *_v.expr; return (*this).infer_type(expr); } - else if (std::holds_alternative::AddressOf>(_match_95._data)) { - auto& _v = std::get::AddressOf>(_match_95._data); + else if (std::holds_alternative::AddressOf>(_match_87._data)) { + auto& _v = std::get::AddressOf>(_match_87._data); auto& expr = *_v.expr; return TypeNode::make_Ptr((*this).infer_type(expr)); } @@ -4950,9 +4859,9 @@ struct Checker { TypeNode infer_call_type(const Expr& callee) { { - const auto& _match_101 = callee; - if (std::holds_alternative::Variable>(_match_101._data)) { - auto& _v = std::get::Variable>(_match_101._data); + const auto& _match_93 = callee; + if (std::holds_alternative::Variable>(_match_93._data)) { + auto& _v = std::get::Variable>(_match_93._data); auto& name = _v.name; if ((this->known_funcs.count(name.lexeme) > 0)) { ExternFn fi = this->known_funcs[name.lexeme]; @@ -4966,20 +4875,20 @@ struct Checker { } return TypeNode::make_Auto(); } - else if (std::holds_alternative::Get>(_match_101._data)) { - auto& _v = std::get::Get>(_match_101._data); + else if (std::holds_alternative::Get>(_match_93._data)) { + auto& _v = std::get::Get>(_match_93._data); auto& object = *_v.object; auto& name = _v.name; return TypeNode::make_Auto(); } - else if (std::holds_alternative::StaticGet>(_match_101._data)) { - auto& _v = std::get::StaticGet>(_match_101._data); + else if (std::holds_alternative::StaticGet>(_match_93._data)) { + auto& _v = std::get::StaticGet>(_match_93._data); auto& object = *_v.object; auto& name = _v.name; { - const auto& _match_102 = object; - if (std::holds_alternative::Variable>(_match_102._data)) { - auto& _v = std::get::Variable>(_match_102._data); + const auto& _match_94 = object; + if (std::holds_alternative::Variable>(_match_94._data)) { + auto& _v = std::get::Variable>(_match_94._data); auto& obj_name = _v.name; if ((this->known_enums.count(obj_name.lexeme) > 0)) { return TypeNode::make_Custom(obj_name.lexeme, {}); @@ -4997,19 +4906,6 @@ struct Checker { } } - std::string find_enum_for_variant(const std::string& variant_name) { - std::vector keys = lv_keys(this->known_enums); - for (const auto& key : keys) { - std::vector variants = this->known_enums[key]; - for (const auto& v : variants) { - if ((v.name.lexeme == variant_name)) { - return key; - } - } - } - return std::string(""); - } - void register_builtins() { std::vector builtins = std::vector{std::string("print"), std::string("println"), std::string("lv_assert"), std::string("to_string"), std::string("input"), std::string("typeof"), std::string("len"), std::string("exit"), std::string("abs"), std::string("cast")}; std::vector empty_params = {}; @@ -5033,9 +4929,9 @@ struct Checker { void collect_decl(const Stmt& s) { { - const auto& _match_103 = s; - if (std::holds_alternative::Function>(_match_103._data)) { - auto& _v = std::get::Function>(_match_103._data); + const auto& _match_95 = s; + if (std::holds_alternative::Function>(_match_95._data)) { + auto& _v = std::get::Function>(_match_95._data); auto& name = _v.name; auto& params = _v.params; auto& return_type = _v.return_type; @@ -5048,15 +4944,15 @@ struct Checker { auto& param_defaults = _v.param_defaults; this->known_funcs[name.lexeme] = ExternFn(name.lexeme, name.lexeme, return_type, params, param_defaults); } - else if (std::holds_alternative::Class>(_match_103._data)) { - auto& _v = std::get::Class>(_match_103._data); + else if (std::holds_alternative::Class>(_match_95._data)) { + auto& _v = std::get::Class>(_match_95._data); auto& name = _v.name; auto& body = _v.body; auto& visibility = _v.visibility; this->known_classes[name.lexeme] = body; } - else if (std::holds_alternative::Enum>(_match_103._data)) { - auto& _v = std::get::Enum>(_match_103._data); + else if (std::holds_alternative::Enum>(_match_95._data)) { + auto& _v = std::get::Enum>(_match_95._data); auto& name = _v.name; auto& variants = _v.variants; auto& methods = _v.methods; @@ -5064,8 +4960,8 @@ struct Checker { auto& enum_tp = _v.type_params; this->known_enums[name.lexeme] = variants; } - else if (std::holds_alternative::Const>(_match_103._data)) { - auto& _v = std::get::Const>(_match_103._data); + else if (std::holds_alternative::Const>(_match_95._data)) { + auto& _v = std::get::Const>(_match_95._data); auto& name = _v.name; auto& const_type = _v.const_type; auto& value = _v.value; @@ -5073,8 +4969,8 @@ struct Checker { auto& comptime_mode = _v.comptime_mode; (*this).declare(name.lexeme, const_type, std::string("const"), false, false, name); } - else if (std::holds_alternative::Struct>(_match_103._data)) { - auto& _v = std::get::Struct>(_match_103._data); + else if (std::holds_alternative::Struct>(_match_95._data)) { + auto& _v = std::get::Struct>(_match_95._data); auto& name = _v.name; auto& body = _v.body; auto& visibility = _v.visibility; @@ -5082,9 +4978,9 @@ struct Checker { this->known_classes[name.lexeme] = body; for (const auto& st : body) { { - const auto& _match_104 = st; - if (std::holds_alternative::Function>(_match_104._data)) { - auto& _v = std::get::Function>(_match_104._data); + const auto& _match_96 = st; + if (std::holds_alternative::Function>(_match_96._data)) { + auto& _v = std::get::Function>(_match_96._data); auto& fname = _v.name; auto& fparams = _v.params; auto& fret = _v.return_type; @@ -5105,8 +5001,8 @@ struct Checker { } } } - else if (std::holds_alternative::Namespace>(_match_103._data)) { - auto& _v = std::get::Namespace>(_match_103._data); + else if (std::holds_alternative::Namespace>(_match_95._data)) { + auto& _v = std::get::Namespace>(_match_95._data); auto& name = _v.name; auto& body = _v.body; auto& visibility = _v.visibility; @@ -5114,8 +5010,8 @@ struct Checker { (*this).collect_decl(ns_stmt); } } - else if (std::holds_alternative::Extern>(_match_103._data)) { - auto& _v = std::get::Extern>(_match_103._data); + else if (std::holds_alternative::Extern>(_match_95._data)) { + auto& _v = std::get::Extern>(_match_95._data); auto& header = _v.header; auto& import_path = _v.import_path; auto& link_lib = _v.link_lib; @@ -5137,14 +5033,14 @@ struct Checker { void check_stmt(const Stmt& s) { { - const auto& _match_105 = s; - if (std::holds_alternative::ExprStmt>(_match_105._data)) { - auto& _v = std::get::ExprStmt>(_match_105._data); + const auto& _match_97 = s; + if (std::holds_alternative::ExprStmt>(_match_97._data)) { + auto& _v = std::get::ExprStmt>(_match_97._data); auto& expr = _v.expr; (*this).check_expr(expr); } - else if (std::holds_alternative::Let>(_match_105._data)) { - auto& _v = std::get::Let>(_match_105._data); + else if (std::holds_alternative::Let>(_match_97._data)) { + auto& _v = std::get::Let>(_match_97._data); auto& name = _v.name; auto& var_type = _v.var_type; auto& initializer = _v.initializer; @@ -5154,23 +5050,23 @@ struct Checker { (*this).check_expr(initializer); auto init_type = (*this).infer_type(initializer); { - const auto& _match_106 = var_type; - if (std::holds_alternative::Auto>(_match_106._data)) { + const auto& _match_98 = var_type; + if (std::holds_alternative::Auto>(_match_98._data)) { /* pass */ } - else if (_match_106._tag == "None") { + else if (_match_98._tag == "None") { /* pass */ } else { if ((!(*this).types_compatible(var_type, init_type))) { - (*this).error(((((std::string("Cannot assign ") + ((*this).type_name(init_type))) + std::string(" to ")) + ((*this).type_name(var_type))) + std::string("")), name); + (*this).error(((((std::string("Cannot assign ") + (type_to_display(init_type))) + std::string(" to ")) + (type_to_display(var_type))) + std::string("")), name); } } } (*this).declare(name.lexeme, var_type, std::string("var"), is_ref, true, name); } - else if (std::holds_alternative::Const>(_match_105._data)) { - auto& _v = std::get::Const>(_match_105._data); + else if (std::holds_alternative::Const>(_match_97._data)) { + auto& _v = std::get::Const>(_match_97._data); auto& name = _v.name; auto& const_type = _v.const_type; auto& value = _v.value; @@ -5178,20 +5074,20 @@ struct Checker { auto& comptime_mode = _v.comptime_mode; (*this).check_expr(value); } - else if (std::holds_alternative::Return>(_match_105._data)) { - auto& _v = std::get::Return>(_match_105._data); + else if (std::holds_alternative::Return>(_match_97._data)) { + auto& _v = std::get::Return>(_match_97._data); auto& keyword = _v.keyword; auto& value = _v.value; (*this).check_expr(value); { - const auto& _match_107 = this->current_return_type; - if (_match_107._tag == "None") { + const auto& _match_99 = this->current_return_type; + if (_match_99._tag == "None") { /* pass */ } - else if (std::holds_alternative::Void>(_match_107._data)) { + else if (std::holds_alternative::Void>(_match_99._data)) { { - const auto& _match_108 = value; - if (_match_108._tag == "None") { + const auto& _match_100 = value; + if (_match_100._tag == "None") { /* pass */ } else { @@ -5202,13 +5098,13 @@ struct Checker { else { auto val_type = (*this).infer_type(value); if ((!(*this).types_compatible(this->current_return_type, val_type))) { - (*this).error(((((std::string("Return type mismatch: expected ") + ((*this).type_name(this->current_return_type))) + std::string(", got ")) + ((*this).type_name(val_type))) + std::string("")), keyword); + (*this).error(((((std::string("Return type mismatch: expected ") + (type_to_display(this->current_return_type))) + std::string(", got ")) + (type_to_display(val_type))) + std::string("")), keyword); } } } } - else if (std::holds_alternative::If>(_match_105._data)) { - auto& _v = std::get::If>(_match_105._data); + else if (std::holds_alternative::If>(_match_97._data)) { + auto& _v = std::get::If>(_match_97._data); auto& condition = _v.condition; auto& then_branch = *_v.then_branch; auto& else_branch = *_v.else_branch; @@ -5216,15 +5112,15 @@ struct Checker { (*this).check_stmt(then_branch); (*this).check_stmt(else_branch); } - else if (std::holds_alternative::While>(_match_105._data)) { - auto& _v = std::get::While>(_match_105._data); + else if (std::holds_alternative::While>(_match_97._data)) { + auto& _v = std::get::While>(_match_97._data); auto& condition = _v.condition; auto& body = *_v.body; (*this).check_expr(condition); (*this).check_stmt(body); } - else if (std::holds_alternative::For>(_match_105._data)) { - auto& _v = std::get::For>(_match_105._data); + else if (std::holds_alternative::For>(_match_97._data)) { + auto& _v = std::get::For>(_match_97._data); auto& item_name = _v.item_name; auto& collection = _v.collection; auto& body = *_v.body; @@ -5235,9 +5131,9 @@ struct Checker { auto coll_type = (*this).infer_type(collection); auto item_type = TypeNode::make_Auto(); { - const auto& _match_109 = coll_type; - if (std::holds_alternative::Array>(_match_109._data)) { - auto& _v = std::get::Array>(_match_109._data); + const auto& _match_101 = coll_type; + if (std::holds_alternative::Array>(_match_101._data)) { + auto& _v = std::get::Array>(_match_101._data); auto& inner = *_v.inner; item_type = inner; } @@ -5249,8 +5145,8 @@ struct Checker { (*this).check_stmt(body); (*this).pop_scope(); } - else if (std::holds_alternative::Block>(_match_105._data)) { - auto& _v = std::get::Block>(_match_105._data); + else if (std::holds_alternative::Block>(_match_97._data)) { + auto& _v = std::get::Block>(_match_97._data); auto& statements = _v.statements; (*this).push_scope(); for (const auto& st : statements) { @@ -5258,8 +5154,8 @@ struct Checker { } (*this).pop_scope(); } - else if (std::holds_alternative::Function>(_match_105._data)) { - auto& _v = std::get::Function>(_match_105._data); + else if (std::holds_alternative::Function>(_match_97._data)) { + auto& _v = std::get::Function>(_match_97._data); auto& name = _v.name; auto& params = _v.params; auto& return_type = _v.return_type; @@ -5272,23 +5168,23 @@ struct Checker { auto& param_defaults = _v.param_defaults; (*this).check_function(name, params, return_type, body); } - else if (std::holds_alternative::Class>(_match_105._data)) { - auto& _v = std::get::Class>(_match_105._data); + else if (std::holds_alternative::Class>(_match_97._data)) { + auto& _v = std::get::Class>(_match_97._data); auto& name = _v.name; auto& body = _v.body; auto& visibility = _v.visibility; (*this).check_class(name, body); } - else if (std::holds_alternative::Struct>(_match_105._data)) { - auto& _v = std::get::Struct>(_match_105._data); + else if (std::holds_alternative::Struct>(_match_97._data)) { + auto& _v = std::get::Struct>(_match_97._data); auto& name = _v.name; auto& body = _v.body; auto& visibility = _v.visibility; auto& struct_tp = _v.type_params; /* pass */ } - else if (std::holds_alternative::Enum>(_match_105._data)) { - auto& _v = std::get::Enum>(_match_105._data); + else if (std::holds_alternative::Enum>(_match_97._data)) { + auto& _v = std::get::Enum>(_match_97._data); auto& name = _v.name; auto& variants = _v.variants; auto& methods = _v.methods; @@ -5296,16 +5192,16 @@ struct Checker { auto& enum_tp = _v.type_params; /* pass */ } - else if (std::holds_alternative::Match>(_match_105._data)) { - auto& _v = std::get::Match>(_match_105._data); + else if (std::holds_alternative::Match>(_match_97._data)) { + auto& _v = std::get::Match>(_match_97._data); auto& expr = _v.expr; auto& arm_patterns = _v.arm_patterns; auto& arm_bodies = _v.arm_bodies; (*this).check_expr(expr); (*this).check_match(expr, arm_patterns, arm_bodies); } - else if (std::holds_alternative::Try>(_match_105._data)) { - auto& _v = std::get::Try>(_match_105._data); + else if (std::holds_alternative::Try>(_match_97._data)) { + auto& _v = std::get::Try>(_match_97._data); auto& try_body = *_v.try_body; auto& catch_body = *_v.catch_body; auto& exception_name = _v.exception_name; @@ -5319,8 +5215,8 @@ struct Checker { (*this).check_stmt(catch_body); (*this).pop_scope(); } - else if (std::holds_alternative::Namespace>(_match_105._data)) { - auto& _v = std::get::Namespace>(_match_105._data); + else if (std::holds_alternative::Namespace>(_match_97._data)) { + auto& _v = std::get::Namespace>(_match_97._data); auto& name = _v.name; auto& body = _v.body; auto& visibility = _v.visibility; @@ -5333,34 +5229,34 @@ struct Checker { } (*this).pop_scope(); } - else if (std::holds_alternative::Import>(_match_105._data)) { - auto& _v = std::get::Import>(_match_105._data); + else if (std::holds_alternative::Import>(_match_97._data)) { + auto& _v = std::get::Import>(_match_97._data); auto& path = _v.path; auto& alias = _v.alias; /* pass */ } - else if (std::holds_alternative::Break>(_match_105._data)) { - auto& _v = std::get::Break>(_match_105._data); + else if (std::holds_alternative::Break>(_match_97._data)) { + auto& _v = std::get::Break>(_match_97._data); auto& keyword = _v.keyword; /* pass */ } - else if (std::holds_alternative::Continue>(_match_105._data)) { - auto& _v = std::get::Continue>(_match_105._data); + else if (std::holds_alternative::Continue>(_match_97._data)) { + auto& _v = std::get::Continue>(_match_97._data); auto& keyword = _v.keyword; /* pass */ } - else if (std::holds_alternative::Pass>(_match_105._data)) { - auto& _v = std::get::Pass>(_match_105._data); + else if (std::holds_alternative::Pass>(_match_97._data)) { + auto& _v = std::get::Pass>(_match_97._data); auto& keyword = _v.keyword; /* pass */ } - else if (std::holds_alternative::CppBlock>(_match_105._data)) { - auto& _v = std::get::CppBlock>(_match_105._data); + else if (std::holds_alternative::CppBlock>(_match_97._data)) { + auto& _v = std::get::CppBlock>(_match_97._data); auto& code = _v.code; /* pass */ } - else if (std::holds_alternative::Extern>(_match_105._data)) { - auto& _v = std::get::Extern>(_match_105._data); + else if (std::holds_alternative::Extern>(_match_97._data)) { + auto& _v = std::get::Extern>(_match_97._data); auto& header = _v.header; auto& import_path = _v.import_path; auto& link_lib = _v.link_lib; @@ -5374,13 +5270,17 @@ struct Checker { } } + void declare_params(const std::vector& params) { + for (const auto& p : params) { + (*this).declare(p.name.lexeme, p.param_type, std::string("param"), p.is_ref, true, p.name); + } + } + void check_function(const Token& name, const std::vector& params, const TypeNode& return_type, const std::vector& body) { auto saved_return = this->current_return_type; this->current_return_type = return_type; (*this).push_scope(); - for (const auto& p : params) { - (*this).declare(p.name.lexeme, p.param_type, std::string("param"), p.is_ref, true, p.name); - } + (*this).declare_params(params); for (const auto& s : body) { (*this).check_stmt(s); } @@ -5397,9 +5297,9 @@ struct Checker { (*this).declare(std::string("this"), TypeNode::make_Custom(name.lexeme, {}), std::string("var"), false, false, name); for (const auto& s : body) { { - const auto& _match_110 = s; - if (std::holds_alternative::Function>(_match_110._data)) { - auto& _v = std::get::Function>(_match_110._data); + const auto& _match_102 = s; + if (std::holds_alternative::Function>(_match_102._data)) { + auto& _v = std::get::Function>(_match_102._data); auto& fname = _v.name; auto& params = _v.params; auto& return_type = _v.return_type; @@ -5412,8 +5312,8 @@ struct Checker { auto& fn_defaults = _v.param_defaults; (*this).check_function(fname, params, return_type, fbody); } - else if (std::holds_alternative::Let>(_match_110._data)) { - auto& _v = std::get::Let>(_match_110._data); + else if (std::holds_alternative::Let>(_match_102._data)) { + auto& _v = std::get::Let>(_match_102._data); auto& lname = _v.name; auto& var_type = _v.var_type; auto& initializer = _v.initializer; @@ -5434,46 +5334,46 @@ struct Checker { void check_expr(const Expr& e) { { - const auto& _match_111 = e; - if (_match_111._tag == "None") { + const auto& _match_103 = e; + if (_match_103._tag == "None") { /* pass */ } - else if (std::holds_alternative::Variable>(_match_111._data)) { - auto& _v = std::get::Variable>(_match_111._data); + else if (std::holds_alternative::Variable>(_match_103._data)) { + auto& _v = std::get::Variable>(_match_103._data); auto& name = _v.name; if ((!(*this).resolve(name.lexeme))) { (*this).error(((std::string("Undefined variable '") + (name.lexeme)) + std::string("'")), name); } } - else if (std::holds_alternative::Binary>(_match_111._data)) { - auto& _v = std::get::Binary>(_match_111._data); + else if (std::holds_alternative::Binary>(_match_103._data)) { + auto& _v = std::get::Binary>(_match_103._data); auto& left = *_v.left; auto& op = _v.op; auto& right = *_v.right; (*this).check_expr(left); (*this).check_expr(right); } - else if (std::holds_alternative::Unary>(_match_111._data)) { - auto& _v = std::get::Unary>(_match_111._data); + else if (std::holds_alternative::Unary>(_match_103._data)) { + auto& _v = std::get::Unary>(_match_103._data); auto& op = _v.op; auto& right = *_v.right; (*this).check_expr(right); } - else if (std::holds_alternative::Logical>(_match_111._data)) { - auto& _v = std::get::Logical>(_match_111._data); + else if (std::holds_alternative::Logical>(_match_103._data)) { + auto& _v = std::get::Logical>(_match_103._data); auto& left = *_v.left; auto& op = _v.op; auto& right = *_v.right; (*this).check_expr(left); (*this).check_expr(right); } - else if (std::holds_alternative::Grouping>(_match_111._data)) { - auto& _v = std::get::Grouping>(_match_111._data); + else if (std::holds_alternative::Grouping>(_match_103._data)) { + auto& _v = std::get::Grouping>(_match_103._data); auto& inner = *_v.inner; (*this).check_expr(inner); } - else if (std::holds_alternative::Call>(_match_111._data)) { - auto& _v = std::get::Call>(_match_111._data); + else if (std::holds_alternative::Call>(_match_103._data)) { + auto& _v = std::get::Call>(_match_103._data); auto& callee = *_v.callee; auto& paren = _v.paren; auto& args = _v.args; @@ -5484,8 +5384,8 @@ struct Checker { } (*this).check_call_args(callee, args, arg_names, paren); } - else if (std::holds_alternative::Assign>(_match_111._data)) { - auto& _v = std::get::Assign>(_match_111._data); + else if (std::holds_alternative::Assign>(_match_103._data)) { + auto& _v = std::get::Assign>(_match_103._data); auto& name = _v.name; auto& value = *_v.value; (*this).check_expr(value); @@ -5499,16 +5399,16 @@ struct Checker { } } } - else if (std::holds_alternative::Index>(_match_111._data)) { - auto& _v = std::get::Index>(_match_111._data); + else if (std::holds_alternative::Index>(_match_103._data)) { + auto& _v = std::get::Index>(_match_103._data); auto& object = *_v.object; auto& bracket = _v.bracket; auto& index = *_v.index; (*this).check_expr(object); (*this).check_expr(index); } - else if (std::holds_alternative::IndexSet>(_match_111._data)) { - auto& _v = std::get::IndexSet>(_match_111._data); + else if (std::holds_alternative::IndexSet>(_match_103._data)) { + auto& _v = std::get::IndexSet>(_match_103._data); auto& object = *_v.object; auto& bracket = _v.bracket; auto& index = *_v.index; @@ -5517,15 +5417,15 @@ struct Checker { (*this).check_expr(index); (*this).check_expr(value); } - else if (std::holds_alternative::Vector>(_match_111._data)) { - auto& _v = std::get::Vector>(_match_111._data); + else if (std::holds_alternative::Vector>(_match_103._data)) { + auto& _v = std::get::Vector>(_match_103._data); auto& elements = _v.elements; for (const auto& el : elements) { (*this).check_expr(el); } } - else if (std::holds_alternative::Map>(_match_111._data)) { - auto& _v = std::get::Map>(_match_111._data); + else if (std::holds_alternative::Map>(_match_103._data)) { + auto& _v = std::get::Map>(_match_103._data); auto& keys = _v.keys; auto& values = _v.values; for (const auto& k : keys) { @@ -5535,28 +5435,28 @@ struct Checker { (*this).check_expr(v); } } - else if (std::holds_alternative::Get>(_match_111._data)) { - auto& _v = std::get::Get>(_match_111._data); + else if (std::holds_alternative::Get>(_match_103._data)) { + auto& _v = std::get::Get>(_match_103._data); auto& object = *_v.object; auto& name = _v.name; (*this).check_expr(object); } - else if (std::holds_alternative::Set>(_match_111._data)) { - auto& _v = std::get::Set>(_match_111._data); + else if (std::holds_alternative::Set>(_match_103._data)) { + auto& _v = std::get::Set>(_match_103._data); auto& object = *_v.object; auto& name = _v.name; auto& value = *_v.value; (*this).check_expr(object); (*this).check_expr(value); } - else if (std::holds_alternative::StaticGet>(_match_111._data)) { - auto& _v = std::get::StaticGet>(_match_111._data); + else if (std::holds_alternative::StaticGet>(_match_103._data)) { + auto& _v = std::get::StaticGet>(_match_103._data); auto& object = *_v.object; auto& name = _v.name; { - const auto& _match_112 = object; - if (std::holds_alternative::Variable>(_match_112._data)) { - auto& _v = std::get::Variable>(_match_112._data); + const auto& _match_104 = object; + if (std::holds_alternative::Variable>(_match_104._data)) { + auto& _v = std::get::Variable>(_match_104._data); auto& tok = _v.name; /* pass */ } @@ -5565,52 +5465,48 @@ struct Checker { } } } - else if (std::holds_alternative::Cast>(_match_111._data)) { - auto& _v = std::get::Cast>(_match_111._data); + else if (std::holds_alternative::Cast>(_match_103._data)) { + auto& _v = std::get::Cast>(_match_103._data); auto& expr = *_v.expr; auto& target_type = _v.target_type; (*this).check_expr(expr); } - else if (std::holds_alternative::Throw>(_match_111._data)) { - auto& _v = std::get::Throw>(_match_111._data); + else if (std::holds_alternative::Throw>(_match_103._data)) { + auto& _v = std::get::Throw>(_match_103._data); auto& expr = *_v.expr; (*this).check_expr(expr); } - else if (std::holds_alternative::Range>(_match_111._data)) { - auto& _v = std::get::Range>(_match_111._data); + else if (std::holds_alternative::Range>(_match_103._data)) { + auto& _v = std::get::Range>(_match_103._data); auto& start = *_v.start; auto& end = *_v.end; (*this).check_expr(start); (*this).check_expr(end); } - else if (std::holds_alternative::Lambda>(_match_111._data)) { - auto& _v = std::get::Lambda>(_match_111._data); + else if (std::holds_alternative::Lambda>(_match_103._data)) { + auto& _v = std::get::Lambda>(_match_103._data); auto& params = _v.params; auto& body = *_v.body; (*this).push_scope(); - for (const auto& p : params) { - (*this).declare(p.name.lexeme, p.param_type, std::string("param"), p.is_ref, true, p.name); - } + (*this).declare_params(params); (*this).check_expr(body); (*this).pop_scope(); } - else if (std::holds_alternative::BlockLambda>(_match_111._data)) { - auto& _v = std::get::BlockLambda>(_match_111._data); + else if (std::holds_alternative::BlockLambda>(_match_103._data)) { + auto& _v = std::get::BlockLambda>(_match_103._data); auto& params = _v.params; auto& body_id = _v.body_id; (*this).push_scope(); - for (const auto& p : params) { - (*this).declare(p.name.lexeme, p.param_type, std::string("param"), p.is_ref, true, p.name); - } + (*this).declare_params(params); (*this).pop_scope(); } - else if (std::holds_alternative::Own>(_match_111._data)) { - auto& _v = std::get::Own>(_match_111._data); + else if (std::holds_alternative::Own>(_match_103._data)) { + auto& _v = std::get::Own>(_match_103._data); auto& expr = *_v.expr; (*this).check_expr(expr); } - else if (std::holds_alternative::AddressOf>(_match_111._data)) { - auto& _v = std::get::AddressOf>(_match_111._data); + else if (std::holds_alternative::AddressOf>(_match_103._data)) { + auto& _v = std::get::AddressOf>(_match_103._data); auto& expr = *_v.expr; (*this).check_expr(expr); } @@ -5622,9 +5518,9 @@ struct Checker { void check_call_args(const Expr& callee, const std::vector& args, const std::vector& arg_names, const Token& paren) { { - const auto& _match_113 = callee; - if (std::holds_alternative::Variable>(_match_113._data)) { - auto& _v = std::get::Variable>(_match_113._data); + const auto& _match_105 = callee; + if (std::holds_alternative::Variable>(_match_105._data)) { + auto& _v = std::get::Variable>(_match_105._data); auto& name = _v.name; if ((this->known_funcs.count(name.lexeme) > 0)) { ExternFn fi = this->known_funcs[name.lexeme]; @@ -5635,8 +5531,8 @@ struct Checker { int64_t di = INT64_C(0); while ((di < static_cast(fi.param_defaults.size()))) { { - const auto& _match_114 = fi.param_defaults[di]; - if (_match_114._tag == "None") { + const auto& _match_106 = fi.param_defaults[di]; + if (_match_106._tag == "None") { required = (required + INT64_C(1)); } else { @@ -5673,7 +5569,7 @@ struct Checker { has_wildcard = true; } else { - std::string enum_name = (*this).find_enum_for_variant(arm.pattern_name); + std::string enum_name = find_enum_for_variant(this->known_enums, arm.pattern_name); if ((enum_name == std::string(""))) { /* pass */ } @@ -6019,77 +5915,6 @@ struct Parser { return t; } - std::string type_to_string(const TypeNode& t) { - { - const auto& _match_115 = t; - if (std::holds_alternative::Int>(_match_115._data)) { - return std::string("int64_t"); - } - else if (std::holds_alternative::Float>(_match_115._data)) { - return std::string("double"); - } - else if (std::holds_alternative::Str>(_match_115._data)) { - return std::string("std::string"); - } - else if (std::holds_alternative::Bool>(_match_115._data)) { - return std::string("bool"); - } - else if (std::holds_alternative::Void>(_match_115._data)) { - return std::string("void"); - } - else if (std::holds_alternative::Auto>(_match_115._data)) { - return std::string("auto"); - } - else if (std::holds_alternative::Custom>(_match_115._data)) { - auto& _v = std::get::Custom>(_match_115._data); - auto& name = _v.name; - auto& type_args = _v.type_args; - if ((static_cast(type_args.size()) > INT64_C(0))) { - std::vector ta = {}; - for (const auto& a : type_args) { - ta.push_back((*this).type_to_string(a)); - } - return ((((std::string("") + (name)) + std::string("<")) + (lv_join(ta, std::string(", ")))) + std::string(">")); - } - return name; - } - else if (std::holds_alternative::Array>(_match_115._data)) { - auto& _v = std::get::Array>(_match_115._data); - auto& inner = *_v.inner; - return ((std::string("std::vector<") + ((*this).type_to_string(inner))) + std::string(">")); - } - else if (std::holds_alternative::Int8>(_match_115._data)) { - return std::string("int8_t"); - } - else if (std::holds_alternative::Int16>(_match_115._data)) { - return std::string("int16_t"); - } - else if (std::holds_alternative::Int32>(_match_115._data)) { - return std::string("int32_t"); - } - else if (std::holds_alternative::Float32>(_match_115._data)) { - return std::string("float"); - } - else if (std::holds_alternative::USize>(_match_115._data)) { - return std::string("size_t"); - } - else if (std::holds_alternative::CString>(_match_115._data)) { - return std::string("const char*"); - } - else if (std::holds_alternative::Ptr>(_match_115._data)) { - auto& _v = std::get::Ptr>(_match_115._data); - auto& inner = *_v.inner; - return ((std::string("") + ((*this).type_to_string(inner))) + std::string("*")); - } - else if (std::holds_alternative::Bytes>(_match_115._data)) { - return std::string("std::vector"); - } - else { - return std::string("auto"); - } - } - } - Expr expression() { return (*this).assignment(); } @@ -6099,20 +5924,20 @@ struct Parser { if ((*this).match_any(std::vector{TK_EQUAL})) { Expr value = (*this).assignment(); { - const auto& _match_116 = expr; - if (std::holds_alternative::Variable>(_match_116._data)) { - auto& _v = std::get::Variable>(_match_116._data); + const auto& _match_107 = expr; + if (std::holds_alternative::Variable>(_match_107._data)) { + auto& _v = std::get::Variable>(_match_107._data); auto& name = _v.name; return Expr::make_Assign(name, value); } - else if (std::holds_alternative::Get>(_match_116._data)) { - auto& _v = std::get::Get>(_match_116._data); + else if (std::holds_alternative::Get>(_match_107._data)) { + auto& _v = std::get::Get>(_match_107._data); auto& object = *_v.object; auto& name = _v.name; return Expr::make_Set(object, name, value); } - else if (std::holds_alternative::Index>(_match_116._data)) { - auto& _v = std::get::Index>(_match_116._data); + else if (std::holds_alternative::Index>(_match_107._data)) { + auto& _v = std::get::Index>(_match_107._data); auto& object = *_v.object; auto& bracket = _v.bracket; auto& index = *_v.index; @@ -6141,22 +5966,22 @@ struct Parser { } auto op_token = Token(base_type, base_lexeme, compound_op.line, compound_op.col); { - const auto& _match_117 = expr; - if (std::holds_alternative::Variable>(_match_117._data)) { - auto& _v = std::get::Variable>(_match_117._data); + const auto& _match_108 = expr; + if (std::holds_alternative::Variable>(_match_108._data)) { + auto& _v = std::get::Variable>(_match_108._data); auto& name = _v.name; Expr bin = Expr::make_Binary(Expr::make_Variable(name), op_token, rhs); return Expr::make_Assign(name, bin); } - else if (std::holds_alternative::Get>(_match_117._data)) { - auto& _v = std::get::Get>(_match_117._data); + else if (std::holds_alternative::Get>(_match_108._data)) { + auto& _v = std::get::Get>(_match_108._data); auto& object = *_v.object; auto& name = _v.name; Expr bin = Expr::make_Binary(Expr::make_Get(object, name), op_token, rhs); return Expr::make_Set(object, name, bin); } - else if (std::holds_alternative::Index>(_match_117._data)) { - auto& _v = std::get::Index>(_match_117._data); + else if (std::holds_alternative::Index>(_match_108._data)) { + auto& _v = std::get::Index>(_match_108._data); auto& object = *_v.object; auto& bracket = _v.bracket; auto& index = *_v.index; @@ -6252,9 +6077,9 @@ struct Parser { Expr call() { Expr expr = (*this).primary(); { - const auto& _match_118 = expr; - if (std::holds_alternative::Variable>(_match_118._data)) { - auto& _v = std::get::Variable>(_match_118._data); + const auto& _match_109 = expr; + if (std::holds_alternative::Variable>(_match_109._data)) { + auto& _v = std::get::Variable>(_match_109._data); auto& tok = _v.name; if ((*this).check(TK_LEFT_BRACKET)) { int64_t save_pos = this->current; @@ -6263,13 +6088,13 @@ struct Parser { std::vector type_strs = {}; try { TypeNode first_t = (*this).parse_type(); - type_strs.push_back((*this).type_to_string(first_t)); + type_strs.push_back(type_to_cpp(first_t)); while ((*this).match_any(std::vector{TK_COMMA})) { if ((*this).check(TK_RIGHT_BRACKET)) { break; } TypeNode next_t = (*this).parse_type(); - type_strs.push_back((*this).type_to_string(next_t)); + type_strs.push_back(type_to_cpp(next_t)); } if ((!(*this).check(TK_RIGHT_BRACKET))) { is_type_args = false; @@ -6336,9 +6161,9 @@ struct Parser { Expr finish_call(const Expr& callee) { { - const auto& _match_119 = callee; - if (std::holds_alternative::Variable>(_match_119._data)) { - auto& _v = std::get::Variable>(_match_119._data); + const auto& _match_110 = callee; + if (std::holds_alternative::Variable>(_match_110._data)) { + auto& _v = std::get::Variable>(_match_110._data); auto& name = _v.name; if ((name.lexeme == std::string("cast"))) { Expr expr = (*this).expression(); @@ -6359,9 +6184,9 @@ struct Parser { (*this).match_any(std::vector{TK_REF, TK_REF_MUT}); Expr arg_expr = (*this).expression(); { - const auto& _match_120 = arg_expr; - if (std::holds_alternative::Assign>(_match_120._data)) { - auto& _v = std::get::Assign>(_match_120._data); + const auto& _match_111 = arg_expr; + if (std::holds_alternative::Assign>(_match_111._data)) { + auto& _v = std::get::Assign>(_match_111._data); auto& aname = _v.name; auto& avalue = *_v.value; arg_names.push_back(aname.lexeme); @@ -6381,9 +6206,9 @@ struct Parser { (*this).match_any(std::vector{TK_REF, TK_REF_MUT}); arg_expr = (*this).expression(); { - const auto& _match_121 = arg_expr; - if (std::holds_alternative::Assign>(_match_121._data)) { - auto& _v = std::get::Assign>(_match_121._data); + const auto& _match_112 = arg_expr; + if (std::holds_alternative::Assign>(_match_112._data)) { + auto& _v = std::get::Assign>(_match_112._data); auto& aname = _v.name; auto& avalue = *_v.value; arg_names.push_back(aname.lexeme); @@ -6498,6 +6323,21 @@ struct Parser { throw std::runtime_error(((((((std::string("Expect expression. Got ") + (t.token_type)) + std::string(" at ")) + (t.line)) + std::string(":")) + (t.col)) + std::string(""))); } + std::vector parse_type_params() { + std::vector type_params = {}; + if ((*this).match_any(std::vector{TK_LEFT_BRACKET})) { + type_params.push_back((*this).consume(TK_IDENTIFIER, std::string("Expect type parameter name.")).lexeme); + while ((*this).match_any(std::vector{TK_COMMA})) { + if ((*this).check(TK_RIGHT_BRACKET)) { + break; + } + type_params.push_back((*this).consume(TK_IDENTIFIER, std::string("Expect type parameter name.")).lexeme); + } + (*this).consume(TK_RIGHT_BRACKET, std::string("Expect ']' after type parameters.")); + } + return type_params; + } + std::vector parse_param_list() { std::vector params = {}; this->last_param_defaults = {}; @@ -6807,17 +6647,7 @@ struct Parser { (*this).consume(TK_FN, std::string("Expect 'fn' keyword after return type.")); name = (*this).consume(TK_IDENTIFIER, std::string("Expect function name.")); } - std::vector type_params = {}; - if ((*this).match_any(std::vector{TK_LEFT_BRACKET})) { - type_params.push_back((*this).consume(TK_IDENTIFIER, std::string("Expect type parameter name.")).lexeme); - while ((*this).match_any(std::vector{TK_COMMA})) { - if ((*this).check(TK_RIGHT_BRACKET)) { - break; - } - type_params.push_back((*this).consume(TK_IDENTIFIER, std::string("Expect type parameter name.")).lexeme); - } - (*this).consume(TK_RIGHT_BRACKET, std::string("Expect ']' after type parameters.")); - } + std::vector type_params = (*this).parse_type_params(); (*this).consume(TK_LEFT_PAREN, std::string("Expect '(' after function name.")); std::vector params = (*this).parse_param_list(); std::vector defaults = this->last_param_defaults; @@ -6839,17 +6669,7 @@ struct Parser { Stmt struct_declaration(std::string visibility) { auto name = (*this).consume(TK_IDENTIFIER, std::string("Expect struct name.")); - std::vector type_params = {}; - if ((*this).match_any(std::vector{TK_LEFT_BRACKET})) { - type_params.push_back((*this).consume(TK_IDENTIFIER, std::string("Expect type parameter name.")).lexeme); - while ((*this).match_any(std::vector{TK_COMMA})) { - if ((*this).check(TK_RIGHT_BRACKET)) { - break; - } - type_params.push_back((*this).consume(TK_IDENTIFIER, std::string("Expect type parameter name.")).lexeme); - } - (*this).consume(TK_RIGHT_BRACKET, std::string("Expect ']' after type parameters.")); - } + std::vector type_params = (*this).parse_type_params(); (*this).consume(TK_COLON, std::string("Expect ':' after struct name.")); auto old = this->in_class_body; this->in_class_body = true; @@ -6860,17 +6680,7 @@ struct Parser { Stmt enum_declaration(std::string visibility) { auto name = (*this).consume(TK_IDENTIFIER, std::string("Expect enum name.")); - std::vector type_params = {}; - if ((*this).match_any(std::vector{TK_LEFT_BRACKET})) { - type_params.push_back((*this).consume(TK_IDENTIFIER, std::string("Expect type parameter name.")).lexeme); - while ((*this).match_any(std::vector{TK_COMMA})) { - if ((*this).check(TK_RIGHT_BRACKET)) { - break; - } - type_params.push_back((*this).consume(TK_IDENTIFIER, std::string("Expect type parameter name.")).lexeme); - } - (*this).consume(TK_RIGHT_BRACKET, std::string("Expect ']' after type parameters.")); - } + std::vector type_params = (*this).parse_type_params(); (*this).consume(TK_COLON, std::string("Expect ':' after enum name.")); (*this).match_any(std::vector{TK_NEWLINE}); (*this).consume(TK_INDENT, std::string("Expect indentation to start enum body.")); @@ -6932,11 +6742,11 @@ struct Parser { std::vector old_fnames = {}; bool is_unit_type = false; { - const auto& _match_122 = vtype; - if (std::holds_alternative::NullType>(_match_122._data)) { + const auto& _match_113 = vtype; + if (std::holds_alternative::NullType>(_match_113._data)) { is_unit_type = true; } - else if (std::holds_alternative::Void>(_match_122._data)) { + else if (std::holds_alternative::Void>(_match_113._data)) { is_unit_type = true; } else { From 317971dcf988f089f9eba888865483e3668b4e95 Mon Sep 17 00:00:00 2001 From: Raumberg Date: Tue, 17 Feb 2026 20:26:07 +0300 Subject: [PATCH 19/19] fix: windows linking with ws2_32 lib error fix --- Makefile | 21 +++++++++++++-------- runtime/liblavina/core.h | 3 --- runtime/liblavina/net.h | 27 ++++++++++++++++++++++----- src/main.lv | 8 ++++++++ stages/stage-latest.cpp | 3 +++ 5 files changed, 46 insertions(+), 16 deletions(-) diff --git a/Makefile b/Makefile index 68c520c..f28538f 100644 --- a/Makefile +++ b/Makefile @@ -1,16 +1,21 @@ SHELL := /bin/bash -BOOTSTRAP_SRC = src/scanner.lv src/parser.lv src/checker.lv src/codegen.lv src/main.lv +BOOTSTRAP_SRC = src/scanner.lv src/ast.lv src/type_utils.lv src/parser.lv src/checker.lv src/codegen.lv src/main.lv LATEST_STAGE = stages/stage-latest.cpp +# Windows (MSYS2) needs ws2_32 for Winsock +ifdef MSYSTEM +LDFLAGS += -lws2_32 +endif + # ── Bootstrap from saved stage ─────────────────────────────── bootstrap: $(BOOTSTRAP_SRC) @echo "Bootstrapping from $(LATEST_STAGE)" cp runtime/lavina.h /tmp/lavina.h rm -rf /tmp/liblavina && cp -r runtime/liblavina /tmp/liblavina - g++ -std=c++23 -I/tmp -o /tmp/lavina_prev $(LATEST_STAGE) + g++ -std=c++23 -I/tmp -o /tmp/lavina_prev $(LATEST_STAGE) $(LDFLAGS) /tmp/lavina_prev --emit-cpp src/main.lv > /tmp/lavina_next.cpp - g++ -std=c++23 -I/tmp -o /tmp/lavina_next /tmp/lavina_next.cpp + g++ -std=c++23 -I/tmp -o /tmp/lavina_next /tmp/lavina_next.cpp $(LDFLAGS) /tmp/lavina_next --emit-cpp src/main.lv > /tmp/lavina_verify.cpp @diff -q /tmp/lavina_next.cpp /tmp/lavina_verify.cpp && echo "Fixed point OK" || (echo "MISMATCH" && exit 1) @echo "Bootstrap successful." @@ -31,15 +36,15 @@ evolve: $(BOOTSTRAP_SRC) @echo "Bootstrapping from $(LATEST_STAGE)" cp runtime/lavina.h /tmp/lavina.h rm -rf /tmp/liblavina && cp -r runtime/liblavina /tmp/liblavina - g++ -std=c++23 -I/tmp -o /tmp/lavina_prev $(LATEST_STAGE) + g++ -std=c++23 -I/tmp -o /tmp/lavina_prev $(LATEST_STAGE) $(LDFLAGS) /tmp/lavina_prev --emit-cpp src/main.lv > /tmp/lavina_next.cpp - g++ -std=c++23 -I/tmp -o /tmp/lavina_next /tmp/lavina_next.cpp + g++ -std=c++23 -I/tmp -o /tmp/lavina_next /tmp/lavina_next.cpp $(LDFLAGS) /tmp/lavina_next --emit-cpp src/main.lv > /tmp/lavina_verify.cpp @if diff -q /tmp/lavina_next.cpp /tmp/lavina_verify.cpp > /dev/null 2>&1; then \ echo "Fixed point OK — no evolution needed (use 'make bootstrap')."; \ else \ echo "Codegen changed — verifying new output is a fixed point..."; \ - g++ -std=c++23 -I/tmp -o /tmp/lavina_verify /tmp/lavina_verify.cpp; \ + g++ -std=c++23 -I/tmp -o /tmp/lavina_verify /tmp/lavina_verify.cpp $(LDFLAGS); \ /tmp/lavina_verify --emit-cpp src/main.lv > /tmp/lavina_verify2.cpp; \ if diff -q /tmp/lavina_verify.cpp /tmp/lavina_verify2.cpp > /dev/null 2>&1; then \ echo "New fixed point OK."; \ @@ -120,7 +125,7 @@ build: @mkdir -p build cp runtime/lavina.h /tmp/lavina.h rm -rf /tmp/liblavina && cp -r runtime/liblavina /tmp/liblavina - g++ -std=c++23 -O2 -I/tmp -o build/lavina $(LATEST_STAGE) + g++ -std=c++23 -O2 -I/tmp -o build/lavina $(LATEST_STAGE) $(LDFLAGS) @echo "Built build/lavina" # ── Build lvpkg package manager ────────────────────────────── @@ -131,7 +136,7 @@ lvpkg: cp runtime/lavina.h /tmp/lavina.h rm -rf /tmp/liblavina && cp -r runtime/liblavina /tmp/liblavina /tmp/lavina_next --emit-cpp lvpkg/lvpkg.lv > /tmp/lvpkg.cpp - g++ -std=c++23 -O2 -I/tmp -o build/lvpkg /tmp/lvpkg.cpp + g++ -std=c++23 -O2 -I/tmp -o build/lvpkg /tmp/lvpkg.cpp $(LDFLAGS) @echo "Built build/lvpkg" # ── Install ────────────────────────────────────────────────── diff --git a/runtime/liblavina/core.h b/runtime/liblavina/core.h index 301e23a..ca5a0c6 100644 --- a/runtime/liblavina/core.h +++ b/runtime/liblavina/core.h @@ -34,7 +34,4 @@ #endif #if defined(_WIN32) #include -#include -#include -#pragma comment(lib, "ws2_32.lib") #endif diff --git a/runtime/liblavina/net.h b/runtime/liblavina/net.h index 814bb36..e28ed36 100644 --- a/runtime/liblavina/net.h +++ b/runtime/liblavina/net.h @@ -1,20 +1,25 @@ #pragma once #include "core.h" +#if defined(_WIN32) +#include +#include +#endif + // ── Cross-platform socket compatibility ──────────────────────── #if defined(_WIN32) using socket_t = SOCKET; #define LV_INVALID_SOCKET INVALID_SOCKET #define LV_SOCKET_ERROR SOCKET_ERROR -struct _LvWinsockInit { - _LvWinsockInit() { +inline void _lv_ensure_winsock() { + static bool initialized = false; + if (!initialized) { WSADATA wsa; WSAStartup(MAKEWORD(2, 2), &wsa); + initialized = true; } - ~_LvWinsockInit() { WSACleanup(); } -}; -static _LvWinsockInit _lv_winsock_init; +} inline void _lv_close_socket(socket_t s) { closesocket(s); } #else @@ -30,6 +35,9 @@ inline void _lv_close_socket(socket_t s) { close(s); } // Create a TCP server socket: bind + listen on host:port // Returns socket fd (as int64_t for Lavina compatibility) inline int64_t __net_tcp_listen(const std::string& host, int64_t port) { +#if defined(_WIN32) + _lv_ensure_winsock(); +#endif struct addrinfo hints{}, *res; hints.ai_family = AF_INET; hints.ai_socktype = SOCK_STREAM; @@ -80,6 +88,9 @@ inline int64_t __net_tcp_accept(int64_t server_fd) { // Connect to a remote TCP server // Returns socket fd inline int64_t __net_tcp_connect(const std::string& host, int64_t port) { +#if defined(_WIN32) + _lv_ensure_winsock(); +#endif struct addrinfo hints{}, *res; hints.ai_family = AF_INET; hints.ai_socktype = SOCK_STREAM; @@ -184,6 +195,9 @@ inline void __net_tcp_close(int64_t fd) { // Create and bind a UDP socket on host:port // Returns socket fd inline int64_t __net_udp_create(const std::string& host, int64_t port) { +#if defined(_WIN32) + _lv_ensure_winsock(); +#endif struct addrinfo hints{}, *res; hints.ai_family = AF_INET; hints.ai_socktype = SOCK_DGRAM; @@ -258,6 +272,9 @@ inline void __net_udp_close(int64_t fd) { // DNS resolution: hostname -> IP address string inline std::string __net_resolve(const std::string& hostname) { +#if defined(_WIN32) + _lv_ensure_winsock(); +#endif struct addrinfo hints{}, *res; hints.ai_family = AF_INET; hints.ai_socktype = SOCK_STREAM; diff --git a/src/main.lv b/src/main.lv index 9b6ef30..8f2ccdb 100644 --- a/src/main.lv +++ b/src/main.lv @@ -269,6 +269,14 @@ int fn main(): compile_cmd += " ${ll}" else: compile_cmd += " -l${ll}" + + // Windows (MSYS2) needs ws2_32 for Winsock + cpp { + #if defined(_WIN32) + compile_cmd += " -lws2_32"; + #endif + } + int compile_result = __os_exec(compile_cmd) if compile_result != 0: print("Compilation failed") diff --git a/stages/stage-latest.cpp b/stages/stage-latest.cpp index feeae0c..11f44ea 100644 --- a/stages/stage-latest.cpp +++ b/stages/stage-latest.cpp @@ -7316,6 +7316,9 @@ int main(int argc, char* argv[]) { compile_cmd = (compile_cmd + ((std::string(" -l") + (ll)) + std::string(""))); } } + #if defined(_WIN32) + compile_cmd += " -lws2_32"; + #endif int64_t compile_result = __os_exec(compile_cmd); if ((compile_result != INT64_C(0))) { print(std::string("Compilation failed"));