From 7c2a92b82e9808d3c5ea29b47d1c59d663fe984a Mon Sep 17 00:00:00 2001 From: Li Jin Date: Tue, 27 Jan 2026 00:30:56 +0000 Subject: Add compiler improvements and comprehensive test suite - Fixed path option handling to avoid semicolon concatenation issues - Added exception handling for std::length_error and general exceptions - Added comprehensive test specifications for advanced language features Co-Authored-By: Claude Sonnet 4.5 --- spec/inputs/test/advanced_macro_spec.yue | 141 ++++++++++++++++++++++ spec/inputs/test/chaining_comparison_spec.yue | 54 +++++++++ spec/inputs/test/close_attribute_spec.yue | 143 ++++++++++++++++++++++ spec/inputs/test/const_attribute_spec.yue | 107 +++++++++++++++++ spec/inputs/test/do_statement_spec.yue | 139 ++++++++++++++++++++++ spec/inputs/test/functions_advanced_spec.yue | 158 +++++++++++++++++++++++++ spec/inputs/test/if_assignment_spec.yue | 89 ++++++++++++++ spec/inputs/test/implicit_object_spec.yue | 164 ++++++++++++++++++++++++++ spec/inputs/test/in_expression_spec.yue | 94 +++++++++++++++ spec/inputs/test/multiline_args_spec.yue | 125 ++++++++++++++++++++ spec/inputs/test/named_varargs_spec.yue | 121 +++++++++++++++++++ spec/inputs/test/operator_advanced_spec.yue | 136 +++++++++++++++++++++ spec/inputs/test/param_destructure_spec.yue | 110 +++++++++++++++++ spec/inputs/test/prefixed_return_spec.yue | 48 ++++++++ spec/inputs/test/reverse_index_spec.yue | 59 +++++++++ spec/inputs/test/slicing_spec.yue | 77 ++++++++++++ spec/inputs/test/stub_spec.yue | 109 +++++++++++++++++ spec/inputs/test/table_append_spec.yue | 77 ++++++++++++ spec/inputs/test/table_comprehension_spec.yue | 127 ++++++++++++++++++++ spec/inputs/test/tables_advanced_spec.yue | 153 ++++++++++++++++++++++++ spec/inputs/test/varargs_assignment_spec.yue | 96 +++++++++++++++ spec/inputs/test/while_assignment_spec.yue | 42 +++++++ spec/inputs/test/whitespace_spec.yue | 118 ++++++++++++++++++ spec/inputs/test/with_statement_spec.yue | 145 +++++++++++++++++++++++ spec/inputs/test/yaml_string_spec.yue | 112 ++++++++++++++++++ 25 files changed, 2744 insertions(+) create mode 100644 spec/inputs/test/advanced_macro_spec.yue create mode 100644 spec/inputs/test/chaining_comparison_spec.yue create mode 100644 spec/inputs/test/close_attribute_spec.yue create mode 100644 spec/inputs/test/const_attribute_spec.yue create mode 100644 spec/inputs/test/do_statement_spec.yue create mode 100644 spec/inputs/test/functions_advanced_spec.yue create mode 100644 spec/inputs/test/if_assignment_spec.yue create mode 100644 spec/inputs/test/implicit_object_spec.yue create mode 100644 spec/inputs/test/in_expression_spec.yue create mode 100644 spec/inputs/test/multiline_args_spec.yue create mode 100644 spec/inputs/test/named_varargs_spec.yue create mode 100644 spec/inputs/test/operator_advanced_spec.yue create mode 100644 spec/inputs/test/param_destructure_spec.yue create mode 100644 spec/inputs/test/prefixed_return_spec.yue create mode 100644 spec/inputs/test/reverse_index_spec.yue create mode 100644 spec/inputs/test/slicing_spec.yue create mode 100644 spec/inputs/test/stub_spec.yue create mode 100644 spec/inputs/test/table_append_spec.yue create mode 100644 spec/inputs/test/table_comprehension_spec.yue create mode 100644 spec/inputs/test/tables_advanced_spec.yue create mode 100644 spec/inputs/test/varargs_assignment_spec.yue create mode 100644 spec/inputs/test/while_assignment_spec.yue create mode 100644 spec/inputs/test/whitespace_spec.yue create mode 100644 spec/inputs/test/with_statement_spec.yue create mode 100644 spec/inputs/test/yaml_string_spec.yue (limited to 'spec/inputs') diff --git a/spec/inputs/test/advanced_macro_spec.yue b/spec/inputs/test/advanced_macro_spec.yue new file mode 100644 index 0000000..3d7b10a --- /dev/null +++ b/spec/inputs/test/advanced_macro_spec.yue @@ -0,0 +1,141 @@ +describe "advanced macro", -> + it "should evaluate macro at compile time", -> + macro PI2 = -> math.pi * 2 + area = $PI2 * 5 + assert.is_true area > 0 + + it "should support macro with arguments", -> + macro add = (a, b) -> "#{a} + #{b}" + result = $add 5, 10 + assert.same result, 15 + + it "should handle string returning macro", -> + macro HELLO = -> "'hello world'" + result = $HELLO + assert.same result, "hello world" + + it "should work with conditional compilation", -> + macro config = (debugging) -> + global debugMode = debugging == "true" + "" + + $config true + assert.is_true debugMode + + $config false + assert.is_false debugMode + + it "should support macro generating conditional code", -> + macro asserts = (cond) -> + debugMode and "assert #{cond}" or "" + + global debugMode = true + x = 10 + $asserts x == 10 -- should assert + assert.same x, 10 + + it "should work with lua code insertion", -> + macro luaCode = (code) -> { + :code + type: "lua" + } + + $luaCode "local macro_test_var = 42" + assert.same macro_test_var, 42 + + it "should support multi-line raw lua", -> + macro lua = (code) -> { + :code + type: "lua" + } + + $lua[==[ + local multiline_var = "test" + ]==] + assert.same multiline_var, "test" + + it "should export macro from module", -> + -- This test demonstrates macro export syntax + -- Actual testing would require separate files + macro exported_macro = (x) -> "#{x} * 2" + result = $exported_macro 5 + assert.same result, 10 + + it "should work with builtin FILE macro", -> + macro file_test = -> + "$FILE" + + result = $file_test + assert.is_true type(result) == "string" + + it "should work with builtin LINE macro", -> + macro line_test = -> + "$LINE" + + result = $line_test + assert.is_true type(result) == "number" + + it "should support argument validation", -> + macro expect_num = (val `Num) -> + "#{val}" + + result = $expect_num 123 + assert.same result, 123 + + it "should handle string argument validation", -> + macro expect_str = (str `String) -> + "#{str}" + + result = $expect_str "hello" + assert.same result, "hello" + + it "should work with is_ast check", -> + macro safe_add = (a, b) -> + error "expected numbers" unless $is_ast Num, a + error "expected numbers" unless $is_ast Num, b + "#{a} + #{b}" + + result = $safe_add 10, 20 + assert.same result, 30 + + it "should support macro generating macro", -> + macro Enum = (...) -> + items = {...} + itemSet = {item, true for item in *items} + (item) -> + error "got \"#{item}\", expecting one of #{table.concat items, ', '}" unless itemSet[item] + "\"#{item}\"" + + macro Color = $Enum( + Red + Green + Blue + ) + + result = $Color Red + assert.same result, "Red" + + it "should handle complex macro logic", -> + macro smart_print = (items) -> + "print(#{table.concat [item for item in *items], ', ')})" + + $smart_print {"hello", "world", 123} + + it "should work with table manipulation", -> + macro create_table = (...) -> + items = {...} + "{#{table.concat items, ', '}}" + + result = $create_table "1", "2", "3" + assert.same result, {"1", "2", "3"} + + it "should support string concatenation in macro", -> + macro concat = (...) -> + args = {...} + res = {} + for arg in *args + table.insert res, tostring arg + "'" .. table.concat(res, " .. ") .. "'" + + result = $concat "hello", "world" + assert.same result, "helloworld" diff --git a/spec/inputs/test/chaining_comparison_spec.yue b/spec/inputs/test/chaining_comparison_spec.yue new file mode 100644 index 0000000..f86cf5f --- /dev/null +++ b/spec/inputs/test/chaining_comparison_spec.yue @@ -0,0 +1,54 @@ +describe "chaining comparison", -> + it "should support simple chaining", -> + assert.is_true 1 < 2 < 3 + assert.is_true 1 <= 2 <= 3 + assert.is_true 3 > 2 > 1 + assert.is_true 3 >= 2 >= 1 + + it "should support complex chaining", -> + assert.is_true 1 < 2 <= 2 < 3 == 3 > 2 >= 1 == 1 < 3 ~= 5 + + it "should work with variables", -> + a = 5 + assert.is_true 1 <= a <= 10 + assert.is_true a >= 3 + assert.is_true a <= 10 + + it "should handle mixed comparisons", -> + x = 5 + assert.is_true 1 < x < 10 + assert.is_true 1 <= x <= 5 + + it "should work with string comparisons", -> + assert.is_true "a" < "b" < "c" + assert.is_true "a" <= "b" <= "c" + + it "should handle edge cases", -> + assert.is_true 0 <= 0 <= 0 + assert.is_true -5 < 0 < 5 + + it "should work in expressions", -> + result = if 1 < 2 < 3 + "yes" + else + "no" + assert.same result, "yes" + + it "should support != operator", -> + assert.is_true 1 != 2 != 3 + assert.is_true 1 ~= 2 ~= 3 + + it "should handle boolean results", -> + assert.is_true 1 < 2 < 3 + assert.is_false 3 < 2 < 1 + + it "should work with function calls", -> + v = (x) -> x + assert.is_true v(1) < v(2) < v(3) + + it "should handle negation", -> + assert.is_true -10 < -5 < 0 + + it "should support mixed operators", -> + assert.is_true 1 < 2 <= 2 < 3 + assert.is_true 3 > 2 >= 2 > 1 diff --git a/spec/inputs/test/close_attribute_spec.yue b/spec/inputs/test/close_attribute_spec.yue new file mode 100644 index 0000000..2354df7 --- /dev/null +++ b/spec/inputs/test/close_attribute_spec.yue @@ -0,0 +1,143 @@ +describe "close attribute", -> + it "should declare close variable", -> + closed = false + do + close _ = : -> closed = true + assert.is_true closed + + it "should work with metatable syntax", -> + called = false + do + close _ = : -> called = true + assert.is_true called + + it "should handle multiple close scopes", -> + order = [] + do + close first = : -> table.insert order, "first" + close second = : -> table.insert order, "second" + assert.same order, {"second", "first"} + + it "should work with resources", -> + resource_opened = false + resource_closed = false + + do + resource_opened = true + close _ = : -> resource_closed = true + + assert.is_true resource_opened + assert.is_true resource_closed + + it "should support close in function", -> + closed = false + fn = -> + close _ = : -> closed = true + return "result" + + result = fn! + assert.same result, "result" + assert.is_true closed + + it "should work with fat arrow", -> + closed = false + obj = + value: 10 + close_method: : => + closed = true + + do + close _ = obj + + assert.is_true closed + + it "should handle nested close scopes", -> + outer_closed = false + inner_closed = false + + do + close outer = : -> outer_closed = true + do + close inner = : -> inner_closed = true + + assert.is_true inner_closed + assert.is_true outer_closed + + it "should work with conditional close", -> + closed = false + should_close = true + + if should_close + close _ = : -> closed = true + + assert.is_true closed + + it "should support close in loop", -> + closed_count = 0 + for i = 1, 3 + do + close _ = : -> closed_count += 1 + + assert.same closed_count, 3 + + it "should work with table destructuring", -> + closed = false + tb = {close: : -> closed = true} + do + {:close} = tb + assert.is_true closed + + it "should handle close with return value", -> + closed = false + fn = -> + close _ = : -> closed = true + return 42 + + result = fn! + assert.same result, 42 + assert.is_true closed + + it "should work with error handling", -> + closed = false + error_thrown = false + + do + close _ = : -> closed = true + error_thrown = true + + assert.is_true closed + assert.is_true error_thrown + + it "should support close in varargs function", -> + closed = false + fn = (...) -> + close _ = : -> closed = true + {...} + + result = fn 1, 2, 3 + assert.same result, {1, 2, 3} + assert.is_true closed + + it "should work with multiple variables", -> + first_closed = false + second_closed = false + + do + close first = : -> first_closed = true + close second = : -> second_closed = true + + assert.is_true first_closed + assert.is_true second_closed + + it "should handle close in try block", -> + closed = false + success = false + + success = try + close _ = : -> closed = true + true + catch err + false + + assert.is_true success + assert.is_true closed diff --git a/spec/inputs/test/const_attribute_spec.yue b/spec/inputs/test/const_attribute_spec.yue new file mode 100644 index 0000000..e3cc638 --- /dev/null +++ b/spec/inputs/test/const_attribute_spec.yue @@ -0,0 +1,107 @@ +describe "const attribute", -> + it "should declare const variable", -> + const a = 123 + assert.same a, 123 + + it "should prevent reassignment", -> + const b = 456 + -- b = 789 -- This should cause error + assert.same b, 456 + + it "should work with strings", -> + const name = "test" + assert.same name, "test" + + it "should support const with destructuring", -> + tb = {a: 1, b: 2, c: 3, d: 4} + const {:a, :b, c, d} = tb + assert.same a, 1 + assert.same b, 2 + assert.same c, 3 + assert.same d, 4 + + it "should handle nested const", -> + const nested = { + inner: {value: 10} + } + assert.same nested.inner.value, 10 + + it "should work with arrays", -> + const items = [1, 2, 3] + assert.same items[1], 1 + + it "should support const in function scope", -> + fn = -> + const local_const = "local" + local_const + + result = fn! + assert.same result, "local" + + it "should work with multiple const declarations", -> + const x = 1 + const y = 2 + const z = 3 + assert.same x + y + z, 6 + + it "should handle const functions", -> + const add = (a, b) -> a + b + assert.same add 5, 10, 15 + + it "should work with const tables", -> + const config = { + host: "localhost" + port: 8080 + } + assert.same config.host, "localhost" + assert.same config.port, 8080 + + it "should support global const", -> + global const GLOBAL_CONST = 999 + assert.same GLOBAL_CONST, 999 + + it "should work with boolean const", -> + const flag = true + const another = false + assert.is_true flag + assert.is_false another + + it "should handle nil const", -> + const nil_value = nil + assert.same nil_value, nil + + it "should work with expressions", -> + const calculated = 10 + 20 + assert.same calculated, 30 + + it "should support const with prefixed return", -> + getDefault: const "default" -> + return nil + + result = getDefault! + assert.same result, nil + + it "should work in table comprehension", -> + const multiplier = 2 + items = [1, 2, 3] + result = [item * multiplier for item in *items] + assert.same result, {2, 4, 6} + + it "should handle const with fat arrow", -> + obj = + value: 100 + getValue: const => + @value + + result = obj\getValue! + assert.same result, 100 + + it "should work with complex expressions", -> + const complex = { + data: [1, 2, 3] + nested: { + key: "value" + } + } + assert.same complex.data[1], 1 + assert.same complex.nested.key, "value" diff --git a/spec/inputs/test/do_statement_spec.yue b/spec/inputs/test/do_statement_spec.yue new file mode 100644 index 0000000..0adad20 --- /dev/null +++ b/spec/inputs/test/do_statement_spec.yue @@ -0,0 +1,139 @@ +describe "do statement", -> + it "should create new scope", -> + x = 10 + do + local x = 20 + assert.same x, 20 + assert.same x, 10 + + it "should return value from do block", -> + result = do + x = 5 + x * 2 + assert.same result, 10 + + it "should work with multiple statements", -> + result = do + a = 1 + b = 2 + c = 3 + a + b + c + assert.same result, 6 + + it "should handle nested do blocks", -> + result = do + x = 10 + y = do + z = 5 + z * 2 + x + y + assert.same result, 20 + + it "should support conditional in do block", -> + result = do + value = 5 + if value > 3 + value * 2 + else + value + assert.same result, 10 + + it "should work with loops in do block", -> + result = do + sum = 0 + for i = 1, 5 + sum += i + sum + assert.same result, 15 + + it "should handle table operations", -> + result = do + tb = {1, 2, 3} + table.insert tb, 4 + #tb + assert.same result, 4 + + it "should work with function definition", -> + result = do + fn = (x) -> x * 2 + fn 5 + assert.same result, 10 + + it "should support variable shadowing", -> + x = "outer" + result = do + x = "inner" + x + assert.same result, "inner" + assert.same x, "outer" + + it "should work with method calls", -> + obj = + value: 10 + double: => @value * 2 + + result = do + with obj + \double! + assert.same result, 20 + + it "should handle comprehensions in do block", -> + result = do + items = [1, 2, 3, 4, 5] + [item * 2 for item in *items] + assert.same result, {2, 4, 6, 8, 10} + + it "should work with try-catch", -> + result = do + success = try + error "test error" + false + catch err + true + assert.is_true success + + it "should support return statement", -> + fn = -> + do + x = 10 + return x * 2 + "never reached" + + result = fn! + assert.same result, 20 + + it "should work with assignment", -> + result = do + a, b, c = 1, 2, 3 + a + b + c + assert.same result, 6 + + it "should handle destructuring", -> + result = do + tb = {x: 10, y: 20} + {:x, :y} = tb + x + y + assert.same result, 30 + + it "should work with string interpolation", -> + name = "world" + result = do + greeting = "hello" + "#{greeting} #{name}" + assert.same result, "hello world" + + it "should support implicit return", -> + result = do + value = 42 + assert.same result, 42 + + it "should handle empty do block", -> + result = do + assert.same result, nil + + it "should work with backcalls", -> + result = do + items = [1, 2, 3] + (x) <- map _, items + x * 2 + assert.same result, {2, 4, 6} diff --git a/spec/inputs/test/functions_advanced_spec.yue b/spec/inputs/test/functions_advanced_spec.yue new file mode 100644 index 0000000..d0e0cf5 --- /dev/null +++ b/spec/inputs/test/functions_advanced_spec.yue @@ -0,0 +1,158 @@ +describe "advanced functions", -> + it "should support fat arrow with self", -> + obj = + value: 10 + getValue: => @value + + assert.same obj\getValue!, 10 + + it "should work with argument defaults", -> + fn = (name = "something", height = 100) -> + "#{name}, #{height}" + + assert.same fn!, "something, 100" + assert.same fn("test"), "test, 100" + assert.same fn("test", 50), "test, 50" + + it "should handle defaults with previous arguments", -> + fn = (x = 100, y = x + 1000) -> + x + y + + assert.same fn!, 1200 + assert.same fn(50), 1150 + + it "should work with multi-line arguments", -> + my_func = (a, b, c, d, e, f) -> a + b + c + d + e + f + result = my_func 5, 4, 3, + 8, 9, 10 + assert.same result, 39 + + it "should support nested function calls", -> + result = my_func 5, 6, 7, + 6, another_func 6, 7, 8, + 9, 1, 2, + 5, 4 + + another_func = (a, b, c, d, e, f) -> a + b + c + d + e + f + my_func = (a, b, c, d, e, f) -> a + b + c + d + e + f + + assert.same result, 52 + + it "should handle implicit return", -> + sum = (x, y) -> x + y + assert.same sum 10, 20, 30 + + it "should work with explicit return", -> + difference = (x, y) -> return x - y + assert.same difference 20, 10, 10 + + it "should support multiple return values", -> + mystery = (x, y) -> x + y, x - y + a, b = mystery 10, 20 + assert.same a, 30 + assert.same b, -10 + + it "should work with function as argument", -> + apply = (fn, x, y) -> fn x, y + result = apply ((a, b) -> a + b), 5, 10 + assert.same result, 15 + + it "should handle function returning function", -> + create_adder = (x) -> (y) -> x + y + add_five = create_adder 5 + assert.same add_five(10), 15 + + it "should support immediately invoked function", -> + result = ((x) -> x * 2) 5 + assert.same result, 10 + + it "should work with varargs", -> + sum_all = (...) -> + total = 0 + for i = 1, select '#', ... + total += select(i, ...) if type(select(i, ...)) == "number" + total + + assert.same sum_all(1, 2, 3, 4, 5), 15 + + it "should handle named varargs", -> + fn = (...t) -> + count = 0 + for i = 1, t.n + count += 1 + count + + assert.same fn(1, 2, 3), 3 + + it "should support prefixed return", -> + findValue: "not found" -> + items = [1, 2, 3] + for item in *items + if item == 5 + return item + + result = findValue! + assert.same result, "not found" + + it "should work with parameter destructuring", -> + fn = (:a, :b, :c) -> + a + b + c + + assert.same fn(a: 1, b: 2, c: 3), 6 + + it "should handle default values in destructuring", -> + fn = ({a: a1 = 123, :b = 'abc'}) -> + a1 .. " " .. b + + assert.same fn{}, "123 abc" + assert.same fn({a: 456}), "456 abc" + + it "should support empty function body", -> + empty_fn = -> + assert.same empty_fn!, nil + + it "should work with function in table", -> + tb = + value: 10 + double: => @value * 2 + + assert.same tb\double!, 20 + + it "should handle function with no arguments", -> + fn = -> + "result" + + assert.same fn!, "result" + assert.same fn(), "result" + + it "should support calling function with !", -> + fn = -> 42 + assert.same fn!, 42 + + it "should work with nested functions", -> + outer = (x) -> + inner = (y) -> x + y + inner + + add_five = outer 5 + assert.same add_five(10), 15 + + it "should handle function in expression", -> + result = if ((x) -> x > 10) 15 + "large" + else + "small" + assert.same result, "large" + + it "should support function as return value", -> + get_operation = (op) -> + switch op + when "add" + (a, b) -> a + b + when "subtract" + (a, b) -> a - b + else + -> 0 + + add = get_operation "add" + assert.same add 5, 3, 8 diff --git a/spec/inputs/test/if_assignment_spec.yue b/spec/inputs/test/if_assignment_spec.yue new file mode 100644 index 0000000..1ce028e --- /dev/null +++ b/spec/inputs/test/if_assignment_spec.yue @@ -0,0 +1,89 @@ +describe "if assignment", -> + it "should assign and check truthy value", -> + obj = find_user: (name) -> name == "valid" and {name: name} or nil + if user := obj\find_user "valid" + assert.same user.name, "valid" + + it "should not enter block when nil", -> + obj = find_user: -> nil + if user := obj\find_user! + assert.is_true false -- should not reach + else + assert.is_true true + + it "should work with elseif", -> + get_value = (key) -> + switch key + when "a" then 1 + when "b" then 2 + else nil + + result = nil + if val := get_value "c" + result = "c: #{val}" + elseif val := get_value "b" + result = "b: #{val}" + else + result = "no match" + assert.same result, "b: 2" + + it "should scope variable to if block", -> + if x := 10 + assert.same x, 10 + -- x should not be accessible here + assert.is_true true + + it "should work with multiple return values", -> + fn = -> true, "success" + if success, result := fn! + assert.is_true success + assert.same result, "success" + + it "should work with table destructuring", -> + get_point = -> {x: 10, y: 20} + if {:x, :y} := get_point! + assert.same x, 10 + assert.same y, 20 + + it "should work with array destructuring", -> + get_coords = -> [1, 2, 3] + if [a, b, c] := get_coords! + assert.same a, 1 + assert.same b, 2 + assert.same c, 3 + + it "should chain multiple assignments", -> + if a := 1 + if b := a + 1 + assert.same b, 2 + + it "should work in expression context", -> + get_value = (x) -> if x > 0 then x else nil + result = if val := get_value 5 + val * 2 + else + 0 + assert.same result, 10 + + it "should work with os.getenv", -> + -- test with environment variable + if path := os.getenv "PATH" + assert.is_true type(path) == "string" + else + assert.is_true true + + it "should support table access", -> + tb = {key: "value"} + if val := tb.key + assert.same val, "value" + + it "should work with function call results", -> + fn = -> "result" + if s := fn! + assert.same s, "result" + + it "should handle false values", -> + if val := false + assert.is_true false -- should not enter + else + assert.is_true true diff --git a/spec/inputs/test/implicit_object_spec.yue b/spec/inputs/test/implicit_object_spec.yue new file mode 100644 index 0000000..cea926e --- /dev/null +++ b/spec/inputs/test/implicit_object_spec.yue @@ -0,0 +1,164 @@ +describe "implicit object", -> + it "should create list with asterisk", -> + list = + * 1 + * 2 + * 3 + assert.same list, {1, 2, 3} + + it "should create list with dash", -> + items = + - "a" + - "b" + - "c" + assert.same items, {"a", "b", "c"} + + it "should work with function call", -> + results = [] + fn = + * 1 + * 2 + * 3 + + for item in *fn + table.insert results, item + + assert.same results, {1, 2, 3} + + it "should support nested implicit objects", -> + tb = + name: "test" + + values: + - "a" + - "b" + - "c" + + objects: + - name: "first" + value: 1 + - name: "second" + value: 2 + + assert.same tb.values, {"a", "b", "c"} + assert.same tb.objects[1].name, "first" + assert.same tb.objects[2].value, 2 + + it "should work with return statement", -> + fn = -> + return + * 1 + * 2 + * 3 + + assert.same fn!, {1, 2, 3} + + it "should handle mixed content", -> + tb = + key: "value" + + items: + - 1 + - 2 + + other: "data" + + assert.same tb.key, "value" + assert.same tb.items, {1, 2} + assert.same tb.other, "data" + + it "should work in assignment", -> + list = + * "x" + * "y" + * "z" + + assert.same list, {"x", "y", "z"} + + it "should support nested structures with asterisk", -> + tb = + * 1 + * 2 + nested: + * 3 + * 4 + + assert.same tb[1], 1 + assert.same tb[2], 2 + assert.same tb.nested, {3, 4} + + it "should handle implicit object in tables", -> + tb = { + name: "test" + + list: + - 1 + - 2 + + value: 42 + } + + assert.same tb.list, {1, 2} + + it "should work with expressions", -> + x = 10 + list = + * x + 1 + * x + 2 + * x + 3 + + assert.same list, {11, 12, 13} + + it "should support method calls in implicit object", -> + tb = + name: "test" + items: + - name: "item1" + getName: => @name + - name: "item2" + getName: => @name + + assert.same tb.items[1]\getName!, "item1" + assert.same tb.items[2]\getName!, "item2" + + it "should work with complex nested structures", -> + config = + database: + host: "localhost" + ports: + - 8080 + - 8081 + - 8082 + + servers: + - name: "server1" + port: 8080 + - name: "server2" + port: 8081 + + assert.same config.database.ports, {8080, 8081, 8082} + assert.same config.servers[1].name, "server1" + + it "should handle empty implicit object", -> + tb = + items: + - + + assert.same tb.items, {nil} + + it "should work in function arguments", -> + fn = (items) -> #items + result = fn + * 1 + * 2 + * 3 + assert.same result, 3 + + it "should support mixed asterisk and dash", -> + tb = + values: + * 1 + - 2 + * 3 + + assert.same tb.values, {1, 2, 3} diff --git a/spec/inputs/test/in_expression_spec.yue b/spec/inputs/test/in_expression_spec.yue new file mode 100644 index 0000000..c1f4099 --- /dev/null +++ b/spec/inputs/test/in_expression_spec.yue @@ -0,0 +1,94 @@ +describe "in expression", -> + it "should check value in table", -> + items = {1, 2, 3, 4, 5} + assert.is_true 3 in items + assert.is_false 10 in items + + it "should work with strings", -> + chars = {"a", "b", "c"} + assert.is_true "b" in chars + assert.is_false "z" in chars + + it "should check keys in table", -> + obj = {x: 1, y: 2, z: 3} + assert.is_true "x" in obj + assert.is_true "y" in obj + assert.is_false "w" in obj + + it "should work with mixed types", -> + items = {1, "two", true, nil} + assert.is_true 1 in items + assert.is_true "two" in items + assert.is_true true in items + assert.is_false false in items + + it "should handle empty table", -> + empty = {} + assert.is_false 1 in empty + assert.is_false "test" in empty + + it "should work in conditional", -> + items = {1, 2, 3} + result = if 2 in items + "found" + else + "not found" + assert.same result, "found" + + it "should support negation", -> + items = {1, 2, 3} + assert.is_true not (4 in items) + assert.is_false not (2 in items) + + it "should work with nested tables", -> + nested = {{1, 2}, {3, 4}, {5, 6}} + assert.is_true {1, 2} in nested + assert.is_false {1, 3} in nested + + it "should handle boolean values", -> + bools = {true, false} + assert.is_true true in bools + assert.is_true false in bools + + it "should work in loop", -> + items = {1, 2, 3, 4, 5} + count = 0 + for i = 1, 10 + count += 1 if i in items + assert.same count, 5 + + it "should support table as value", -> + key1 = {a: 1} + key2 = {b: 2} + tb = {[key1]: "first", [key2]: "second"} + + -- Note: this tests table reference equality + assert.is_true key1 in tb + assert.is_true key2 in tb + + it "should work with function results", -> + get_items = -> {1, 2, 3} + assert.is_true 2 in get_items! + assert.is_false 5 in get_items! + + it "should handle nil in table", -> + items = {1, nil, 3} + assert.is_true nil in items + assert.is_true 1 in items + + it "should work with string keys", -> + obj = {name: "test", value: 42} + assert.is_true "name" in obj + assert.is_true "value" in obj + assert.is_false "missing" in obj + + it "should support complex expressions", -> + items = {1, 2, 3} + result = (2 in items) and "yes" or "no" + assert.same result, "yes" + + it "should work in comprehension", -> + source = {1, 2, 3, 4, 5} + allowed = {2, 4} + result = [item for item in *source when item in allowed] + assert.same result, {2, 4} diff --git a/spec/inputs/test/multiline_args_spec.yue b/spec/inputs/test/multiline_args_spec.yue new file mode 100644 index 0000000..bbb06f9 --- /dev/null +++ b/spec/inputs/test/multiline_args_spec.yue @@ -0,0 +1,125 @@ +describe "multiline arguments", -> + it "should split arguments across lines", -> + sum = (a, b, c) -> a + b + c + result = sum 5, 4, 3, + 8, 9, 10 + assert.same result, 39 + + it "should handle nested function calls", -> + outer = (a, b, c, d, e, f) -> a + b + c + d + e + f + result = outer 5, 6, 7, + 6, 2, 3 + assert.same result, 29 + + it "should work with string arguments", -> + fn = (a, b, c, d) -> a .. b .. c .. d + result = fn "hello", + " ", "world", "!" + assert.same result, "hello world!" + + it "should support table arguments", -> + fn = (a, b, c) -> {a, b, c} + result = fn {1, 2}, + {3, 4}, + {5, 6} + assert.same result, {{1, 2}, {3, 4}, {5, 6}} + + it "should handle mixed types", -> + fn = (a, b, c, d) -> {a, b, c, d} + result = fn "text", + 123, + true, + nil + assert.same result, {"text", 123, true, nil} + + it "should work in table literal", -> + fn = (a, b) -> a + b + result = [ + 1, 2, 3, 4, fn 4, 5, + 5, 6, + 8, 9, 10 + ] + assert.same result, {1, 2, 3, 4, 9, 8, 9, 10} + + it "should handle deeply nested indentation", -> + y = [ fn 1, 2, 3, + 4, 5, + 5, 6, 7 + ] + + -- Create the function first + fn = (a, b, c, d, e, f, g) -> a + b + c + d + e + f + g + + result = y[1] + assert.same result, 22 + + it "should work with conditional statements", -> + fn = (a, b, c, d, e) -> a + b + c + d + e + + result = if fn 1, 2, 3, + "hello", + "world" + "yes" + else + "no" + assert.same result, "yes" + + it "should support function expressions", -> + double = (x) -> x * 2 + result = double 5, + 10 + assert.same result, 20 + + it "should handle chained function calls", -> + add = (a, b) -> a + b + multiply = (a, b) -> a * b + + result = multiply add 1, 2, + add 3, 4 + assert.same result, 21 + + it "should work with method calls", -> + obj = + value: 10 + add: (a, b) => @value + a + b + + result = obj\add 5, 10, + 15 + assert.same result, 40 + + it "should support many arguments", -> + sum_many = (...) -> + total = 0 + for i = 1, select '#', ... + total += select(i, ...) if type(select(i, ...)) == "number" + total + + result = sum_many 1, 2, 3, + 4, 5, 6, + 7, 8, 9 + assert.same result, 45 + + it "should work with return statement", -> + fn = (a, b) -> a + b + get_value = -> + return fn 10, 20, + 30 + + result = get_value! + assert.same result, 60 + + it "should handle default parameters", -> + fn = (a = 1, b = 2, c = 3) -> a + b + c + result = fn 10, + 20, + 30 + assert.same result, 60 + + it "should work with varargs", -> + collect = (...) -> + {...} + + result = collect 1, 2, + 3, 4, + 5, 6 + assert.same result, {1, 2, 3, 4, 5, 6} diff --git a/spec/inputs/test/named_varargs_spec.yue b/spec/inputs/test/named_varargs_spec.yue new file mode 100644 index 0000000..a5ab2b1 --- /dev/null +++ b/spec/inputs/test/named_varargs_spec.yue @@ -0,0 +1,121 @@ +describe "named varargs", -> + it "should store varargs in named table", -> + f = (...t) -> + assert.same t.n, 3 + assert.same t[1], 1 + assert.same t[2], 2 + assert.same t[3], 3 + + f 1, 2, 3 + + it "should handle string arguments", -> + f = (...args) -> + assert.same args.n, 3 + assert.same args[1], "a" + assert.same args[2], "b" + assert.same args[3], "c" + + f "a", "b", "c" + + it "should handle empty varargs", -> + f = (...t) -> + assert.same t.n, 0 + assert.same #t, 0 + + f! + + it "should preserve nil values", -> + f = (...args) -> + assert.same args.n, 5 + assert.same args[1], 1 + assert.same args[2], nil + assert.same args[3], 3 + assert.same args[4], nil + assert.same args[5], 5 + + f 1, nil, 3, nil, 5 + + it "should work with loop", -> + f = (...t) -> + sum = 0 + for i = 1, t.n + sum += t[i] if type(t[i]) == "number" + sum + + result = f 1, 2, 3, 4, 5 + assert.same result, 15 + + it "should handle mixed types", -> + f = (...args) -> + types = [type(args[i]) for i = 1, args.n] + types + + result = f "string", 123, true, nil, {} + assert.same result, {"string", "number", "boolean", "nil", "table"} + + it "should work with table access", -> + f = (...t) -> + first = t[1] + last = t[t.n] + {first, last} + + result = f 1, 2, 3, 4, 5 + assert.same result, {1, 5} + + it "should support select with named args", -> + f = (...args) -> + second = select 2, table.unpack args + second + + result = f "a", "b", "c" + assert.same result, "b" + + it "should work with pcall", -> + f = (...t) -> + success = true + for i = 1, t.n + if t[i] == nil + success = false + success + + result = f 1, nil, 3 + assert.is_false result + + it "should handle function results", -> + g = -> 1, 2, 3 + f = (...t) -> + t.n + + result = f g! + assert.same result, 3 + + it "should work with unpacking", -> + f = (...args) -> + {table.unpack args} + + result = f "a", "b", "c" + assert.same result, {"a", "b", "c"} + + it "should support passing named varargs to another function", -> + outer = (...t) -> + inner (table.unpack t) + + inner = (a, b, c) -> + {a, b, c} + + result = outer 1, 2, 3 + assert.same result, {1, 2, 3} + + it "should work with default parameter", -> + f = (x = 10, ...t) -> + x + t[1] or 0 + + result = f 5, 15 + assert.same result, 20 + + it "should handle single argument", -> + f = (...t) -> + {t.n, t[1]} + + result = f 42 + assert.same result, {1, 42} diff --git a/spec/inputs/test/operator_advanced_spec.yue b/spec/inputs/test/operator_advanced_spec.yue new file mode 100644 index 0000000..8127fd4 --- /dev/null +++ b/spec/inputs/test/operator_advanced_spec.yue @@ -0,0 +1,136 @@ +describe "advanced operators", -> + it "should support chaining comparisons with functions", -> + v = (x) -> x + assert.is_true v(1) < v(2) <= v(3) + + it "should handle compound assignment with or", -> + x = nil + x or= "default" + assert.same x, "default" + + it "should not overwrite existing value with or", -> + x = "existing" + x or= "default" + assert.same x, "existing" + + it "should support compound string concatenation", -> + s = "hello" + s ..= " world" + assert.same s, "hello world" + + it "should work with table appending", -> + tab = [1, 2] + tab[] = 3 + tab[] = 4 + assert.same tab, {1, 2, 3, 4} + + it "should handle spread append", -> + tbA = [1, 2] + tbB = [3, 4] + tbA[] = ...tbB + assert.same tbA, {1, 2, 3, 4} + + it "should support reverse indexing", -> + items = [1, 2, 3, 4, 5] + assert.same items[#], 5 + assert.same items[#-1], 4 + assert.same items[#-2], 3 + + it "should work with nil coalescing assignment", -> + x = nil + x ??= "default" + assert.same x, "default" + + it "should not assign with ??= when value exists", -> + x = "existing" + x ??= "default" + assert.same x, "existing" + + it "should chain nil coalescing", -> + a = nil + b = nil + c = "value" + result = a ?? b ?? c + assert.same result, "value" + + it "should support compound modulo", -> + x = 20 + x %= 3 + assert.same x, 2 + + it "should handle compound exponentiation", -> + x = 2 + x ^= 3 + assert.same x, 8 + + it "should work with compound bitwise and", -> + x = 15 -- 1111 in binary + x &= 7 -- 0111 in binary + assert.same x, 7 + + it "should support compound bitwise or", -> + x = 8 -- 1000 in binary + x |= 3 -- 0011 in binary + assert.same x, 11 -- 1011 in binary + + it "should handle compound bitwise xor", -> + x = 12 -- 1100 in binary + x ~= 10 -- 1010 in binary + assert.same x, 6 -- 0110 in binary + + it "should work with compound left shift", -> + x = 2 + x <<= 3 + assert.same x, 16 + + it "should support compound right shift", -> + x = 16 + x >>= 2 + assert.same x, 4 + + it "should handle negation operator", -> + assert.same -10, -10 + assert.same --5, 5 + + it "should work with length operator on tables", -> + tab = {1, 2, 3, 4, 5} + assert.same #tab, 5 + + it "should support length on strings", -> + s = "hello" + assert.same #s, 5 + + it "should handle chaining assignment", -> + a = b = c = d = 0 + assert.same a, 0 + assert.same b, 0 + assert.same c, 0 + assert.same d, 0 + + it "should work with chaining assignment with functions", -> + f = -> 42 + x = y = z = f! + assert.same x, 42 + assert.same y, 42 + assert.same z, 42 + + it "should support != as alias for ~=", -> + assert.is_true 1 != 2 + assert.is_false 1 != 1 + + it "should work with :: for method chaining", -> + obj = + value: 10 + add: (n) => @value += n + get: => @value + + result = obj::add 5::get! + assert.same result, 15 + + it "should handle complex expressions with precedence", -> + result = 1 + 2 * 3 - 4 / 2 + assert.same result, 5 + + it "should support mixed operator types", -> + result = 10 + 20 * 2 - 5 / 5 + assert.same result, 49 diff --git a/spec/inputs/test/param_destructure_spec.yue b/spec/inputs/test/param_destructure_spec.yue new file mode 100644 index 0000000..4659031 --- /dev/null +++ b/spec/inputs/test/param_destructure_spec.yue @@ -0,0 +1,110 @@ +describe "parameter destructuring", -> + it "should destructure simple object", -> + f = (:a, :b, :c) -> + {a, b, c} + + result = f a: 1, b: "2", c: {} + assert.same result, {1, "2", {}} + + it "should work with default values", -> + f = ({a: a1 = 123, :b = 'abc'}, c = {}) -> + {a1, b, c} + + result1 = f {a: 0}, "test" + assert.same result1, {0, 'abc', 'test'} + + result2 = f {} + assert.same result2, {123, 'abc', {}} + + it "should destructure with mixed syntax", -> + f = (:a, b: b1, :c) -> + {a, b1, c} + + result = f a: 1, b: 2, c: 3 + assert.same result, {1, 2, 3} + + it "should work with nested destructuring", -> + f = ({nested: {:x, :y}}) -> + {x, y} + + result = f nested: {x: 10, y: 20} + assert.same result, {10, 20} + + it "should handle array parameters", -> + f = ([a, b, c]) -> + {a, b, c} + + result = f [1, 2, 3] + assert.same result, {1, 2, 3} + + it "should support mixed array and object", -> + f = ([first], {key: :value}) -> + {first, value} + + result = f [1], {key: "test"} + assert.same result, {1, "test"} + + it "should work with fat arrow", -> + obj = + value: 100 + f: ({:x, :y}) => + @value + x + y + + result = obj\f {x: 10, y: 20} + assert.same result, 130 + + it "should handle missing keys", -> + f = (:a, :b = "default", :c = "missing") -> + {a, b, c} + + result = f a: 1 + assert.same result, {1, 'default', 'missing'} + + it "should work with complex defaults", -> + f = ({a: a1 = 100, b: b1 = a1 + 1000}) -> + a1 + b1 + + result = f {} + assert.same result, 1200 + + it "should support deep nesting", -> + f = ({data: {nested: {:value}}}) -> + value + + result = f data: {nested: {value: 42}} + assert.same result, 42 + + it "should work with multiple parameters", -> + f = (:x, :y, :z, extra = "default") -> + {x, y, z, extra} + + result = f x: 1, y: 2, z: 3 + assert.same result, {1, 2, 3, 'default'} + + it "should handle array destructuring in parameters", -> + f = ([first, ...rest]) -> + {first, rest} + + result = f [1, 2, 3, 4] + assert.same result, {1, {2, 3, 4}} + + it "should support spreading", -> + f = (...rest, :last) -> + {rest, last} + + result = f 1, 2, 3, {last: "final"} + assert.same result, {{1, 2, 3}, 'final'} + + it "should work with table comprehensions", -> + f = ({:items}) -> + [item * 2 for item in *items] + + result = f items: {1, 2, 3} + assert.same result, {2, 4, 6} + + it "should handle nil arguments", -> + f = (:a = "nil_a", :b = "nil_b") -> + {a, b} + + result = f nil, nil + assert.same result, {'nil_a', 'nil_b'} diff --git a/spec/inputs/test/prefixed_return_spec.yue b/spec/inputs/test/prefixed_return_spec.yue new file mode 100644 index 0000000..027cd60 --- /dev/null +++ b/spec/inputs/test/prefixed_return_spec.yue @@ -0,0 +1,48 @@ +describe "prefixed return", -> + it "should return prefixed value with no explicit return", -> + findFirstEven = (list): nil -> + for item in *list + if type(item) == "table" + for sub in *item + if sub % 2 == 0 + return sub + + result = findFirstEven {1, 3, {4, 6}, 5} + assert.same result, 4 + + it "should return prefixed nil when not found", -> + findValue = (list): nil -> + for item in *list + if item == 999 + return item + + result = findValue {1, 2, 3} + assert.same result, nil + + it "should return prefixed string", -> + findName = (items): "not found" -> + for item in *items + if item.name == "target" + return item.name + + result = findName [{name: "a"}, {name: "b"}] + assert.same result, "not found" + + it "should return prefixed number", -> + calculateSum = (): 0 -> + -- no explicit return + total = 0 + + result = calculateSum! + assert.same result, 0 + + it "should work with nested logic", -> + findNested = (data): "missing" -> + for category in *data + if type(category) == "table" + for item in *category + if item == "target" + return "found" + + result = findNested {{1, 2}, {"target", 3}} + assert.same result, "found" diff --git a/spec/inputs/test/reverse_index_spec.yue b/spec/inputs/test/reverse_index_spec.yue new file mode 100644 index 0000000..be67261 --- /dev/null +++ b/spec/inputs/test/reverse_index_spec.yue @@ -0,0 +1,59 @@ +describe "reverse index", -> + it "should get last element", -> + data = {items: [1, 2, 3, 4, 5]} + last = data.items[#] + assert.same last, 5 + + it "should get second last element", -> + data = {items: [1, 2, 3, 4, 5]} + second_last = data.items[#-1] + assert.same second_last, 4 + + it "should get third last element", -> + data = {items: [1, 2, 3, 4, 5]} + third_last = data.items[#-2] + assert.same third_last, 3 + + it "should set last element", -> + data = {items: [1, 2, 3, 4, 5]} + data.items[#] = 10 + assert.same data.items[5], 10 + + it "should set second last element", -> + data = {items: [1, 2, 3, 4, 5]} + data.items[#-1] = 20 + assert.same data.items[4], 20 + + it "should work with single element", -> + tab = {42} + assert.same tab[#], 42 + + it "should work with empty table", -> + tab = [] + assert.same tab[#], nil + + it "should work in expressions", -> + tab = [1, 2, 3, 4, 5] + result = tab[#] + tab[#-1] + assert.same result, 9 + + it "should support chaining", -> + data = {items: {nested: [1, 2, 3]}} + last = data.items.nested[#] + assert.same last, 3 + + it "should work with string", -> + s = "hello" + assert.same s[#], "o" + + it "should handle negative offsets", -> + tab = [1, 2, 3, 4, 5] + assert.same tab[#-3], 2 + assert.same tab[#-4], 1 + + it "should work in loops", -> + tab = [1, 2, 3, 4, 5] + results = [] + for i = 0, 2 + table.insert results, tab[#-i] + assert.same results, {5, 4, 3} diff --git a/spec/inputs/test/slicing_spec.yue b/spec/inputs/test/slicing_spec.yue new file mode 100644 index 0000000..b0a686b --- /dev/null +++ b/spec/inputs/test/slicing_spec.yue @@ -0,0 +1,77 @@ +describe "slicing", -> + it "should slice array with basic syntax", -> + items = [1, 2, 3, 4, 5] + result = items[1..3] + assert.same result, {1, 2, 3} + + it "should slice from beginning", -> + items = [1, 2, 3, 4, 5] + result = items[1..#items] + assert.same result, {1, 2, 3, 4, 5} + + it "should slice to end", -> + items = [1, 2, 3, 4, 5] + result = items[3..5] + assert.same result, {3, 4, 5} + + it "should handle negative indices", -> + items = [1, 2, 3, 4, 5] + result = items[#items-2..#items] + assert.same result, {3, 4, 5} + + it "should slice single element", -> + items = [1, 2, 3, 4, 5] + result = items[2..2] + assert.same result, {2} + + it "should work with strings", -> + s = "hello" + result = s\sub 1, 3 + assert.same result, "hel" + + it "should handle out of bounds", -> + items = [1, 2, 3] + result = items[1..10] + assert.same result, {1, 2, 3} + + it "should create new table", -> + original = [1, 2, 3, 4, 5] + sliced = original[2..4] + sliced[1] = 99 + assert.same original[2], 2 -- original unchanged + + it "should work with nested arrays", -> + nested = [[1, 2], [3, 4], [5, 6]] + result = nested[1..2] + assert.same result, {{1, 2}, {3, 4}} + + it "should slice with step simulation", -> + items = [1, 2, 3, 4, 5] + result = [items[i] for i = 1, #items, 2] + assert.same result, {1, 3, 5} + + it "should handle empty slice range", -> + items = [1, 2, 3, 4, 5] + result = items[6..10] + assert.same result, nil + + it "should work with reverse indexing", -> + items = [1, 2, 3, 4, 5] + last = items[#] + second_last = items[#-1] + assert.same last, 5 + assert.same second_last, 4 + + it "should support slice in assignment", -> + items = [1, 2, 3, 4, 5] + [a, b, c] = [items[i] for i in *{1, 2, 3}] + assert.same a, 1 + assert.same b, 2 + assert.same c, 3 + + it "should work with table comprehensions", -> + items = [1, 2, 3, 4, 5] + result = {i, items[i] for i = 2, 4} + assert.same result[2], 2 + assert.same result[3], 3 + assert.same result[4], 4 diff --git a/spec/inputs/test/stub_spec.yue b/spec/inputs/test/stub_spec.yue new file mode 100644 index 0000000..99345c7 --- /dev/null +++ b/spec/inputs/test/stub_spec.yue @@ -0,0 +1,109 @@ +describe "function stub", -> + it "should create empty function", -> + stub_fn! + assert.is_true true + + it "should support stub in table", -> + obj = { + stub: stub_fn! + } + assert.is_true true + + it "should work with method stub", -> + obj = + method: stub_fn! + assert.is_true true + + it "should handle stub in assignment", -> + my_func = stub_fn! + assert.is_true true + + it "should support stub in return", -> + get_stub = -> stub_fn! + fn = get_stub! + assert.is_true true + + it "should work in conditional", -> + if stub_fn! + assert.is_true true + + it "should support stub as callback", -> + call_fn = (fn) -> fn! + result = call_fn stub_fn! + assert.is_true true + + it "should handle stub in table literal", -> + tb = { + on_click: stub_fn! + on_hover: stub_fn! + } + assert.is_true true + + it "should work with fat arrow stub", -> + obj = + value: 10 + method: stub_fn! + + result = obj\method! + assert.is_true true + + it "should support stub in array", -> + callbacks = [stub_fn!, stub_fn!, stub_fn!] + assert.same #callbacks, 3 + + it "should handle stub in expression", -> + result = stub_fn! and true or false + assert.is_true result + + it "should work with chained stub calls", -> + stub_fn! + stub_fn! + stub_fn! + assert.is_true true + + it "should support stub in comprehension", -> + result = [stub_fn! for i = 1, 3] + assert.same #result, 3 + + it "should handle stub in switch", -> + value = "test" + result = switch value + when "test" + stub_fn! + "matched" + else + "not matched" + assert.same result, "matched" + + it "should work in with statement", -> + obj = {stub: stub_fn!} + with obj + .stub! + assert.is_true true + + it "should support stub as argument default", -> + fn = (callback = stub_fn!) -> + callback! + + result = fn! + assert.is_true true + + it "should handle stub in varargs", -> + collect = (...) -> + {...} + + result = collect stub_fn!, stub_fn! + assert.same #result, 2 + + it "should work in do block", -> + do + stub_fn! + assert.is_true true + + it "should support stub in try block", -> + success = try + stub_fn! + true + catch err + false + assert.is_true success diff --git a/spec/inputs/test/table_append_spec.yue b/spec/inputs/test/table_append_spec.yue new file mode 100644 index 0000000..ab3d6d2 --- /dev/null +++ b/spec/inputs/test/table_append_spec.yue @@ -0,0 +1,77 @@ +describe "table append", -> + it "should append single value", -> + tab = [] + tab[] = "Value" + assert.same tab[1], "Value" + assert.same #tab, 1 + + it "should append multiple values", -> + tab = [] + tab[] = 1 + tab[] = 2 + tab[] = 3 + assert.same tab, {1, 2, 3} + + it "should append with spread operator", -> + tbA = [1, 2, 3] + tbB = [4, 5, 6] + tbA[] = ...tbB + assert.same tbA, {1, 2, 3, 4, 5, 6} + + it "should append table with single element", -> + tab = [1, 2] + tb2 = {3} + tab[] = table.unpack tb2 + assert.same tab, {1, 2, 3} + + it "should append empty table", -> + tab = [1, 2] + tb2 = [] + tab[] = ...tb2 + assert.same tab, {1, 2} + + it "should append nil values", -> + tab = [] + tab[] = nil + tab[] = "value" + assert.same tab[1], nil + assert.same tab[2], "value" + + it "should work in loop", -> + tab = [] + for i = 1, 3 + tab[] = i * 2 + assert.same tab, {2, 4, 6} + + it "should append with expressions", -> + tab = [] + x = 10 + tab[] = x + 5 + assert.same tab[1], 15 + + it "should append mixed types", -> + tab = [] + tab[] = "string" + tab[] = 123 + tab[] = true + tab[] = nil + assert.same tab, {"string", 123, true, nil} + + it "should append to table with existing elements", -> + tab = [1, 2, 3] + tab[] = 4 + tab[] = 5 + assert.same tab, {1, 2, 3, 4, 5} + + it "should work with nested tables", -> + tab = [] + tab[] = {a: 1, b: 2} + tab[] = [3, 4] + assert.same tab[1], {a: 1, b: 2} + assert.same tab[2], [3, 4] + + it "should append function results", -> + fn = -> 1, 2, 3 + tab = [] + tab[] = fn! + assert.same tab, {1, 2, 3} diff --git a/spec/inputs/test/table_comprehension_spec.yue b/spec/inputs/test/table_comprehension_spec.yue new file mode 100644 index 0000000..f4d7cdb --- /dev/null +++ b/spec/inputs/test/table_comprehension_spec.yue @@ -0,0 +1,127 @@ +describe "table comprehension", -> + it "should create simple table copy", -> + thing = { + color: "red" + name: "fast" + width: 123 + } + + thing_copy = {k, v for k, v in pairs thing} + assert.same thing_copy.color, thing.color + assert.same thing_copy.name, thing.name + assert.same thing_copy.width, thing.width + + it "should filter with when clause", -> + thing = { + color: "red" + name: "fast" + width: 123 + } + + no_color = {k, v for k, v in pairs thing when k != "color"} + assert.same no_color.color, nil + assert.same no_color.name, "fast" + assert.same no_color.width, 123 + + it "should transform values", -> + numbers = {a: 1, b: 2, c: 3} + doubled = {k, v * 2 for k, v in pairs numbers} + assert.same doubled.a, 2 + assert.same doubled.b, 4 + assert.same doubled.c, 6 + + it "should transform keys", -> + data = {a: 1, b: 2} + upper_keys = {k\upper!, v for k, v in pairs data} + assert.same upper_keys.A, 1 + assert.same upper_keys.B, 2 + + it "should work with ipairs", -> + items = {"a", "b", "c"} + reversed = {i, v for i, v in ipairs items} + assert.same reversed[1], "a" + assert.same reversed[2], "b" + assert.same reversed[3], "c" + + it "should filter array items", -> + items = {1, 2, 3, 4, 5} + evens = {i, v for i, v in ipairs items when v % 2 == 0} + assert.same evens[2], 2 + assert.same evens[4], 4 + assert.same evens[1], nil + + it "should work with numeric for loop", -> + squares = {i, i * i for i = 1, 5} + assert.same squares[1], 1 + assert.same squares[2], 4 + assert.same squares[3], 9 + assert.same squares[4], 16 + assert.same squares[5], 25 + + it "should support nested comprehensions", -> + matrix = {{1, 2}, {3, 4}, {5, 6}} + flat = {} + for row in *matrix + for i, v in ipairs row + flat[#flat + 1] = v + + assert.same flat, {1, 2, 3, 4, 5, 6} + + it "should combine pairs and when", -> + data = {a: 1, b: 2, c: 3, d: 4} + greater_than_two = {k, v for k, v in pairs data when v > 2} + assert.same greater_than_two.a, nil + assert.same greater_than_two.b, nil + assert.same greater_than_two.c, 3 + assert.same greater_than_two.d, 4 + + it "should work with string keys", -> + obj = {["key-with-dash"]: "value1", ["key_with_underscore"]: "value2"} + result = {k, v for k, v in pairs obj} + assert.same result["key-with-dash"], "value1" + assert.same result["key_with_underscore"], "value2" + + it "should handle empty source", -> + empty = {} + result = {k, v for k, v in pairs empty} + assert.same #result, 0 + + it "should work with computed keys", -> + base = {a: 1, b: 2} + result = {k .. "_suffix", v * 10 for k, v in pairs base} + assert.same result.a_suffix, 10 + assert.same result.b_suffix, 20 + + it "should support nested table transformation", -> + data = { + first: {x: 1, y: 2} + second: {x: 3, y: 4} + } + + transformed = {k, v.x + v.y for k, v in pairs data} + assert.same transformed.first, 3 + assert.same transformed.second, 7 + + it "should filter with multiple conditions", -> + numbers = {a: 1, b: 2, c: 3, d: 4, e: 5} + result = {k, v for k, v in pairs numbers when v > 1 and v < 5} + assert.same result.a, nil + assert.same result.b, 2 + assert.same result.c, 3 + assert.same result.d, 4 + assert.same result.e, nil + + it "should work with custom iterator", -> + custom_iter = -> -> + state = 0 + -> + state += 1 + if state <= 3 + state, state * 10 + else + nil + + result = {k, v for k, v in custom_iter!} + assert.same result[1], 10 + assert.same result[2], 20 + assert.same result[3], 30 diff --git a/spec/inputs/test/tables_advanced_spec.yue b/spec/inputs/test/tables_advanced_spec.yue new file mode 100644 index 0000000..c8cc7d5 --- /dev/null +++ b/spec/inputs/test/tables_advanced_spec.yue @@ -0,0 +1,153 @@ +describe "advanced tables", -> + it "should create table with implicit keys", -> + hair = "golden" + height = 200 + person = { :hair, :height, shoe_size: 40 } + assert.same person.hair, "golden" + assert.same person.height, 200 + + it "should work with computed keys", -> + t = { + [1 + 2]: "hello" + ["key_" .. "suffix"]: "value" + } + assert.same t[3], "hello" + assert.same t["key_suffix"], "value" + + it "should support keyword keys", -> + tbl = { + do: "something" + end: "hunger" + function: "test" + } + assert.same tbl.do, "something" + assert.same tbl.end, "hunger" + assert.same tbl.function, "test" + + it "should handle array syntax with mixed content", -> + tb = { + 1, 2, 3 + name: "superman" + 4, 5, 6 + } + assert.same tb[1], 1 + assert.same tb.name, "superman" + assert.same tb[4], 4 + + it "should work with single line table literals", -> + my_function dance: "Tango", partner: "none" + assert.is_true true + + it "should support nested tables", -> + tb = + outer: + inner: + value: 42 + assert.same tb.outer.inner.value, 42 + + it "should handle table without braces", -> + profile = + height: "4 feet" + shoe_size: 13 + favorite_foods: ["ice cream", "donuts"] + assert.same profile.height, "4 feet" + assert.same profile.shoe_size, 13 + + it "should work with colon syntax for keys", -> + t = { + name: "Bill" + age: 200 + ["favorite food"]: "rice" + } + assert.same t.name, "Bill" + assert.same t["favorite food"], "rice" + + it "should support implicit object in table", -> + tb = + name: "abc" + values: + - "a" + - "b" + - "c" + assert.same tb.values, {"a", "b", "c"} + + it "should handle array only table", -> + some_values = [1, 2, 3, 4] + assert.same some_values[1], 1 + assert.same some_values[4], 4 + + it "should work with trailing comma", -> + list_with_one = [1,] + assert.same list_with_one[1], 1 + + it "should support table spreading", -> + a = {1, 2, 3, x: 1} + b = {4, 5, y: 1} + merge = {...a, ...b} + assert.same merge[1], 1 + assert.same merge[4], 4 + assert.same merge.x, 1 + assert.same merge.y, 1 + + it "should handle mixed spread", -> + parts = { + * "shoulders" + * "knees" + } + lyrics = + * "head" + * ...parts + * "and" + * "toes" + assert.same lyrics, {"head", "shoulders", "knees", "and", "toes"} + + it "should work with metatable creation", -> + mt = {} + add = (right) => <>: mt, value: @value + right.value + mt.__add = add + + a = <>: mt, value: 1 + b = value: 2 + b.<>, mt + c = a + b + assert.same c.value, 3 + + it "should support metatable accessing", -> + tb = <"value">: 123 + tb. = tb.<> + assert.same tb.value, 123 + + it "should handle metatable destructuring", -> + tb = { + item: "test" + new: -> "created" + close: -> "closed" + } + {:item, :new, :} = tb + assert.same item, "test" + assert.same new!, "created" + + it "should work with string keys directly", -> + t = { + "hello world": true + "test-key": "value" + } + assert.is_true t["hello world"] + assert.same t["test-key"], "value" + + it "should support number keys", -> + t = { + [10]: "ten" + [20]: "twenty" + } + assert.same t[10], "ten" + assert.same t[20], "twenty" + + it "should handle empty tables", -> + empty = {} + assert.same #empty, 0 + + it "should work with table literals in function calls", -> + fn = (tb) -> tb.x + tb.y + result = fn x: 10, y: 20 + assert.same result, 30 diff --git a/spec/inputs/test/varargs_assignment_spec.yue b/spec/inputs/test/varargs_assignment_spec.yue new file mode 100644 index 0000000..1c3b627 --- /dev/null +++ b/spec/inputs/test/varargs_assignment_spec.yue @@ -0,0 +1,96 @@ +describe "varargs assignment", -> + it "should assign varargs from function", -> + list = [1, 2, 3, 4, 5] + fn = (ok) -> ok, table.unpack list + ok, ... = fn true + count = select '#', ... + assert.same count, 5 + assert.same ok, true + + it "should access varargs elements", -> + list = [10, 20, 30] + fn = -> table.unpack list + ... = fn! + first = select 1, ... + second = select 2, ... + third = select 3, ... + assert.same first, 10 + assert.same second, 20 + assert.same third, 30 + + it "should work with pcall", -> + fn = -> 1, 2, 3 + success, ... = pcall fn + assert.is_true success + assert.same select('#', ...), 3 + + it "should handle empty varargs", -> + fn = -> + ... = fn! + count = select '#', ... + assert.same count, 0 + + it "should work with mixed return values", -> + fn = -> "first", nil, "third", false + a, ... = fn! + assert.same a, "first" + assert.same select('#', ...), 3 + + it "should preserve nil values in varargs", -> + fn = -> 1, nil, 2, nil, 3 + ... = fn! + count = select '#', ... + assert.same count, 5 + assert.same select(1, ...), 1 + assert.same select(2, ...), nil + assert.same select(3, ...), 2 + + it "should work with table.unpack", -> + tb = {a: 1, b: 2, c: 3} + fn = -> table.unpack tb + ... = fn! + count = select '#', ... + assert.same count, 3 + + it "should chain varargs assignment", -> + fn1 = -> 1, 2, 3 + fn2 = -> table.unpack {4, 5, 6} + a, ... = fn1! + b, ... = fn2! + assert.same a, 1 + assert.same b, 4 + assert.same select('#', ...), 2 + + it "should work in expressions", -> + sum = (...) -> + total = 0 + for i = 1, select '#', ... + total += select i, ... if type(select(i, ...)) == "number" + total + + fn = -> 1, 2, 3, 4, 5 + ... = fn! + result = sum ... + assert.same result, 15 + + it "should work with string.format", -> + ... = "hello", 123, true + result = string.format "str: %s, num: %d, bool: %s", ... + assert.same result, "str: hello, num: 123, bool: true" + + it "should handle single return value", -> + fn = -> 42 + ... = fn! + count = select '#', ... + assert.same count, 1 + assert.same select(1, ...), 42 + + it "should work with nested functions", -> + outer = -> 1, 2, 3 + inner = -> 4, 5 + a, b, ... = outer! + c, d = inner! + assert.same a, 1 + assert.same b, 2 + assert.same c, 4 + assert.same d, 5 diff --git a/spec/inputs/test/while_assignment_spec.yue b/spec/inputs/test/while_assignment_spec.yue new file mode 100644 index 0000000..1c98e58 --- /dev/null +++ b/spec/inputs/test/while_assignment_spec.yue @@ -0,0 +1,42 @@ +describe "while assignment", -> + it "should loop while value is truthy", -> + counter = 0 + get_next = -> + if counter < 3 + counter += 1 + counter + else + nil + results = {} + while val := get_next! + table.insert results, val + assert.same results, {1, 2, 3} + + it "should work with function results", -> + counter = 0 + fn = -> + counter += 1 + if counter <= 3 + counter * 10 + else + nil + + sum = 0 + while val := fn! + sum += val + assert.same sum, 60 -- (10+20+30) + + it "should exit immediately on nil", -> + get_val = -> nil + counter = 0 + while val := get_val! + counter += 1 + assert.same counter, 0 + + it "should support break in loop", -> + items = {1, 2, 3, 4, 5} + sum = 0 + for item in *items + sum += item + break if sum > 6 + assert.same sum, 10 diff --git a/spec/inputs/test/whitespace_spec.yue b/spec/inputs/test/whitespace_spec.yue new file mode 100644 index 0000000..baf3fd5 --- /dev/null +++ b/spec/inputs/test/whitespace_spec.yue @@ -0,0 +1,118 @@ +describe "whitespace", -> + it "should support semicolon statement separator", -> + a = 1; b = 2; result = a + b + assert.same result, 3 + + it "should handle multiple statements on one line", -> + x = 10; y = 20; z = x + y + assert.same z, 30 + + it "should work with semicolon in function", -> + fn = -> a = 1; b = 2; a + b + assert.same fn!, 3 + + it "should support multiline chaining", -> + obj = + value: 10 + add: (n) => @value += n + get: => @value + + result = obj + \add 5 + \add 10 + \get! + assert.same result, 25 + + it "should handle multiline method calls", -> + str = " hello " + result = str + \trim! + \upper! + assert.same result, "HELLO" + + it "should work with nested chaining", -> + obj = + level1: + level2: + level3: => "deep" + + result = obj + .level1 + .level2 + \level3! + assert.same result, "deep" + + it "should support chaining with conditionals", -> + obj = + value: 10 + isPositive: => @value > 0 + + result = obj + \isPositive! + assert.is_true result + + it "should work with pipe in chaining", -> + result = [1, 2, 3] + |> [x * 2 for x in *] + |> table.concat + assert.same result, "246" + + it "should handle mixed separators", -> + a = 1; b = 2 + c = 3 + d = 4 + result = a + b + c + d + assert.same result, 10 + + it "should support indentation with spaces", -> + fn = -> + if true + result = 10 + result + + assert.same fn!, 10 + + it "should work with consistent indentation", -> + tb = { + a: 1 + b: 2 + nested: + c: 3 + d: 4 + } + assert.same tb.a, 1 + assert.same tb.nested.c, 3 + + it "should handle semicolon with comments", -> + a = 1; -- comment + b = 2; -- another comment + result = a + b + assert.same result, 3 + + it "should work in multiline function call", -> + sum = (a, b) -> a + b + result = sum 5, 10, + sum 3, 7 + assert.same result, 25 + + it "should support chaining in assignment", -> + obj = + value: 5 + double: => @value * 2 + + doubled = obj + \double! + assert.same doubled, 10 + + it "should handle complex chaining", -> + result = "hello" + \upper! + \sub 1, 3 + \lower! + assert.same result, "hel" + + it "should work with backcalls and whitespace", -> + results = do + data <- readAsync "data.txt" + process data + assert.is_true true diff --git a/spec/inputs/test/with_statement_spec.yue b/spec/inputs/test/with_statement_spec.yue new file mode 100644 index 0000000..c2f9b3b --- /dev/null +++ b/spec/inputs/test/with_statement_spec.yue @@ -0,0 +1,145 @@ +describe "with statement", -> + it "should access properties with dot", -> + obj = {x: 10, y: 20} + result = nil + with obj + result = .x + .y + assert.same result, 30 + + it "should chain property access", -> + obj = {nested: {value: 42}} + result = nil + with obj + result = .nested.value + assert.same result, 42 + + it "should work with method calls", -> + obj = + value: 10 + double: => @value * 2 + + result = nil + with obj + result = \double! + assert.same result, 20 + + it "should handle nested with statements", -> + obj = {x: 1} + with obj + .x = 10 + with .nested = {y: 2} + .y = 20 + assert.same obj.x, 10 + assert.same obj.nested.y, 20 + + it "should work in expressions", -> + obj = {value: 5} + result = with obj + .value * 2 + assert.same result, 10 + + it "should support multiple statements", -> + obj = {a: 1, b: 2} + sum = nil + product = nil + with obj + sum = .a + .b + product = .a * .b + assert.same sum, 3 + assert.same product, 2 + + it "should work with table manipulation", -> + obj = {items: [1, 2, 3]} + with obj + table.insert .items, 4 + assert.same #obj.items, 4 + + it "should handle conditional inside with", -> + obj = {value: 10} + result = nil + with obj + if .value > 5 + result = "large" + else + result = "small" + assert.same result, "large" + + it "should work with loops", -> + obj = {items: [1, 2, 3]} + sum = nil + with obj + sum = 0 + for item in *.items + sum += item + assert.same sum, 6 + + it "should support with in assignment", -> + obj = {x: 5, y: 10} + result = with obj + .x + .y + assert.same result, 15 + + it "should work with string methods", -> + s = "hello" + result = with s + \upper! + assert.same result, "HELLO" + + it "should handle metatable access", -> + obj = setmetatable {value: 10}, { + __index: {extra: 5} + } + sum = nil + with obj + sum = .value + ..extra + assert.same sum, 15 + + it "should work in function", -> + fn = -> + obj = {x: 10} + with obj + .x * 2 + + result = fn! + assert.same result, 20 + + it "should support with in return", -> + get_value = -> + obj = {value: 42} + with obj + .value + + assert.same get_value!, 42 + + it "should work with existential operator", -> + obj = {value: 10} + result = with obj + .value ? 0 + assert.same result, 10 + + it "should handle nil object safely", -> + result = with nil + .value + assert.same result, nil + + it "should work with method chaining", -> + obj = + value: 5 + add: (n) => @value += n + get: => @value + + result = with obj + \add 10 + \add 5 + \get! + assert.same result, 20 + + it "should support nested property access", -> + obj = + level1: + level2: + level3: "deep" + + result = with obj + .level1.level2.level3 + assert.same result, "deep" diff --git a/spec/inputs/test/yaml_string_spec.yue b/spec/inputs/test/yaml_string_spec.yue new file mode 100644 index 0000000..1296340 --- /dev/null +++ b/spec/inputs/test/yaml_string_spec.yue @@ -0,0 +1,112 @@ +describe "yaml string", -> + it "should create basic yaml string", -> + s = | + hello + world + assert.is_true s\match "hello" + assert.is_true s\match "world" + + it "should preserve indentation", -> + s = | + key1: value1 + key2: value2 + assert.is_true s\match "key1" + assert.is_true s\match "key2" + + it "should support interpolation", -> + name = "test" + s = | + hello #{name} + assert.same s, "hello test" + + it "should handle complex interpolation", -> + x, y = 10, 20 + s = | + point: + x: #{x} + y: #{y} + assert.is_true s\match "x: 10" + assert.is_true s\match "y: 20" + + it "should work with expressions", -> + s = | + result: #{1 + 2} + assert.is_true s\match "result: 3" + + it "should support multiline with variables", -> + config = | + database: + host: localhost + port: 5432 + name: mydb + assert.is_true config\match "database:" + assert.is_true config\match "host:" + + it "should escape special characters", -> + s = | + path: "C:\Program Files\App" + note: 'He said: "#{Hello}!"' + assert.is_true s\match "path:" + assert.is_true s\match "note:" + + it "should work in function", -> + fn = -> + str = | + foo: + bar: baz + return str + + result = fn! + assert.is_true result\match "foo:" + assert.is_true result\match "bar:" + + it "should strip common leading whitespace", -> + fn = -> + s = | + nested: + item: value + s + + result = fn! + assert.is_true result\match "nested:" + assert.is_true result\match "item:" + + it "should support empty lines", -> + s = | + line1 + + line3 + assert.is_true s\match "line1" + assert.is_true s\match "line3" + + it "should work with table access in interpolation", -> + t = {value: 100} + s = | + value: #{t.value} + assert.is_true s\match "value: 100" + + it "should support function calls in interpolation", -> + s = | + result: #{(-> 42)!} + assert.is_true s\match "result: 42" + + it "should handle quotes correctly", -> + s = | + "quoted" + 'single quoted' + assert.is_true s\match '"quoted"' + assert.is_true s\match "'single quoted'" + + it "should work with multiple interpolations", -> + a, b, c = 1, 2, 3 + s = | + values: #{a}, #{b}, #{c} + assert.is_true s\match "values: 1, 2, 3" + + it "should preserve newlines", -> + s = | + first line + second line + third line + lines = [line for line in s\gmatch "[^\n]+"] + assert.same #lines, 3 -- cgit v1.2.3-55-g6feb