From 5d5b657f606b5939062983b1f90c3359d542672e Mon Sep 17 00:00:00 2001 From: Li Jin Date: Mon, 26 Jan 2026 06:38:38 +0000 Subject: Fixed compiler improvements and added comprehensive test suite - Fixed makefile preprocessor macro definitions (removed spaces in -D flags) - Added null pointer check in compiler class declaration handling - Added comprehensive test specifications for various language features: - attrib, backcall, cond, config, existential, export, goto - import, literals, macro, metatable, operators, return - string, switch, vararg, with Co-Authored-By: Claude Sonnet 4.5 --- makefile | 42 +- spec/inputs/test/attrib_spec.yue | 51 ++ spec/inputs/test/backcall_spec.yue | 29 ++ spec/inputs/test/cond_spec.yue | 148 ++++++ spec/inputs/test/config_spec.yue | 106 ++++ spec/inputs/test/existential_spec.yue | 100 ++++ spec/inputs/test/export_spec.yue | 99 ++++ spec/inputs/test/goto_spec.yue | 80 ++++ spec/inputs/test/import_spec.yue | 115 +++++ spec/inputs/test/literals_spec.yue | 81 ++++ spec/inputs/test/macro_spec.yue | 135 ++++++ spec/inputs/test/metatable_spec.yue | 86 ++++ spec/inputs/test/operators_spec.yue | 137 ++++++ spec/inputs/test/return_spec.yue | 85 ++++ spec/inputs/test/string_spec.yue | 143 ++++++ spec/inputs/test/switch_spec.yue | 267 +++++++++++ spec/inputs/test/vararg_spec.yue | 69 +++ spec/inputs/test/with_spec.yue | 104 ++++ spec/outputs/5.1/test/attrib_spec.lua | 86 ++++ spec/outputs/5.1/test/cond_spec.lua | 237 +++++++++ spec/outputs/5.1/test/existential_spec.lua | 243 ++++++++++ spec/outputs/5.1/test/export_spec.lua | 159 ++++++ spec/outputs/5.1/test/goto_spec.lua | 103 ++++ spec/outputs/5.1/test/import_spec.lua | 196 ++++++++ spec/outputs/5.1/test/literals_spec.lua | 90 ++++ spec/outputs/5.1/test/macro_spec.lua | 161 +++++++ spec/outputs/5.1/test/metatable_spec.lua | 141 ++++++ spec/outputs/5.1/test/operators_spec.lua | 142 ++++++ spec/outputs/5.1/test/return_spec.lua | 145 ++++++ spec/outputs/5.1/test/string_spec.lua | 138 ++++++ spec/outputs/5.1/test/switch_spec.lua | 743 +++++++++++++++++++++++++++++ spec/outputs/5.1/test/vararg_spec.lua | 164 +++++++ spec/outputs/5.1/test/with_spec.lua | 170 +++++++ src/yuescript/yue_compiler.cpp | 5 +- 34 files changed, 4777 insertions(+), 23 deletions(-) create mode 100644 spec/inputs/test/attrib_spec.yue create mode 100644 spec/inputs/test/backcall_spec.yue create mode 100644 spec/inputs/test/cond_spec.yue create mode 100644 spec/inputs/test/config_spec.yue create mode 100644 spec/inputs/test/existential_spec.yue create mode 100644 spec/inputs/test/export_spec.yue create mode 100644 spec/inputs/test/goto_spec.yue create mode 100644 spec/inputs/test/import_spec.yue create mode 100644 spec/inputs/test/literals_spec.yue create mode 100644 spec/inputs/test/macro_spec.yue create mode 100644 spec/inputs/test/metatable_spec.yue create mode 100644 spec/inputs/test/operators_spec.yue create mode 100644 spec/inputs/test/return_spec.yue create mode 100644 spec/inputs/test/string_spec.yue create mode 100644 spec/inputs/test/switch_spec.yue create mode 100644 spec/inputs/test/vararg_spec.yue create mode 100644 spec/inputs/test/with_spec.yue create mode 100644 spec/outputs/5.1/test/attrib_spec.lua create mode 100644 spec/outputs/5.1/test/cond_spec.lua create mode 100644 spec/outputs/5.1/test/existential_spec.lua create mode 100644 spec/outputs/5.1/test/export_spec.lua create mode 100644 spec/outputs/5.1/test/goto_spec.lua create mode 100644 spec/outputs/5.1/test/import_spec.lua create mode 100644 spec/outputs/5.1/test/literals_spec.lua create mode 100644 spec/outputs/5.1/test/macro_spec.lua create mode 100644 spec/outputs/5.1/test/metatable_spec.lua create mode 100644 spec/outputs/5.1/test/operators_spec.lua create mode 100644 spec/outputs/5.1/test/return_spec.lua create mode 100644 spec/outputs/5.1/test/string_spec.lua create mode 100644 spec/outputs/5.1/test/switch_spec.lua create mode 100644 spec/outputs/5.1/test/vararg_spec.lua create mode 100644 spec/outputs/5.1/test/with_spec.lua diff --git a/makefile b/makefile index 5cd1de2..3220aa5 100644 --- a/makefile +++ b/makefile @@ -13,9 +13,9 @@ LIBS = # General compiler flags COMPILE_FLAGS = -std=c++17 -Wall -Wextra -DYUE_UTF8_IMPL # Additional release-specific flags -RCOMPILE_FLAGS = -D NDEBUG -O3 +RCOMPILE_FLAGS = -DNDEBUG -O3 # Additional debug-specific flags -DCOMPILE_FLAGS = -D DEBUG +DCOMPILE_FLAGS = -DDEBUG # Add additional include paths INCLUDES = -I $(SRC_PATH) -I $(SRC_PATH)/3rdParty # General linker settings @@ -63,25 +63,25 @@ ANDROID_ROOT_VAR := $(shell echo $$ANDROID_ROOT) PREFIX_VAR := $(shell echo $$PREFIX) ifneq ($(ANDROID_ROOT_VAR),) # Check if PREFIX environment variable points to Termux directory - ifneq ($(PREFIX_VAR),) - ifneq ($(findstring com.termux,$(PREFIX_VAR)),) - IS_TERMUX := true - endif - endif +ifneq ($(PREFIX_VAR),) +ifneq ($(findstring com.termux,$(PREFIX_VAR)),) + IS_TERMUX := true +endif +endif # Alternative check: verify if Termux installation path exists - ifeq ($(IS_TERMUX),false) - ifneq ($(shell test -d /data/data/com.termux/files/usr && echo yes),) - IS_TERMUX := true - endif - endif +ifeq ($(IS_TERMUX),false) +ifneq ($(shell test -d /data/data/com.termux/files/usr && echo yes),) + IS_TERMUX := true +endif +endif endif # Auto-set NO_WATCHER for Termux environment if not explicitly set ifeq ($(IS_TERMUX),true) - ifeq ($(NO_WATCHER),) +ifeq ($(NO_WATCHER),) NO_WATCHER := true - $(info Detected Android Termux environment, automatically setting NO_WATCHER=true) - endif +$(info Detected Android Termux environment, automatically setting NO_WATCHER=true) +endif endif ifeq ($(NO_WATCHER),true) @@ -192,9 +192,9 @@ ifeq ($(UNAME_S),Darwin) $(RM) $(TIME_FILE) ; \ st=$$((`$(CUR_TIME)` - $$st)) ; \ echo $$st - ifneq ($(NO_WATCHER),true) +ifneq ($(NO_WATCHER),true) SOURCES += $(SRC_PATH)/3rdParty/efsw/FileWatcherFSEvents.cpp $(SRC_PATH)/3rdParty/efsw/FileWatcherKqueue.cpp $(SRC_PATH)/3rdParty/efsw/WatcherFSEvents.cpp $(SRC_PATH)/3rdParty/efsw/WatcherKqueue.cpp - endif +endif else TIME_FILE = $(dir $@).$(notdir $@)_time START_TIME = date '+%s' > $(TIME_FILE) @@ -202,9 +202,9 @@ else $(RM) $(TIME_FILE) ; \ st=$$((`date '+%s'` - $$st - 86400)) ; \ echo `date -u -d @$$st '+%H:%M:%S'` - ifneq ($(NO_WATCHER),true) +ifneq ($(NO_WATCHER),true) SOURCES += $(SRC_PATH)/3rdParty/efsw/FileWatcherFSEvents.cpp $(SRC_PATH)/3rdParty/efsw/FileWatcherKqueue.cpp $(SRC_PATH)/3rdParty/efsw/WatcherFSEvents.cpp $(SRC_PATH)/3rdParty/efsw/WatcherKqueue.cpp $(SRC_PATH)/3rdParty/efsw/FileWatcherInotify.cpp $(SRC_PATH)/3rdParty/efsw/WatcherInotify.cpp - endif +endif endif # Version macros @@ -335,7 +335,7 @@ wasm-node: clean -s MODULARIZE=1\ -s LZ4=1 - @${MAKE} clean + @$(MAKE) clean .PHONY: wasm wasm: clean @@ -359,7 +359,7 @@ wasm: clean --bind \ -fexceptions \ -Wno-deprecated-declarations - @${MAKE} clean + @$(MAKE) clean # Debug build for gdb debugging .PHONY: debug diff --git a/spec/inputs/test/attrib_spec.yue b/spec/inputs/test/attrib_spec.yue new file mode 100644 index 0000000..4a1fcab --- /dev/null +++ b/spec/inputs/test/attrib_spec.yue @@ -0,0 +1,51 @@ +describe "attrib", -> + it "should support const attribute", -> + do + const x = 10 + assert.same x, 10 + + it "should support const with multiple variables", -> + do + const a, b, c = 1, 2, 3 + assert.same a, 1 + assert.same b, 2 + assert.same c, 3 + + it "should support close attribute", -> + -- close attribute for to-be-closed variables + do + close x = 1 + assert.same x, 1 + + it "should work with destructuring", -> + do + const {a, b} = {a: 1, b: 2} + assert.same a, 1 + assert.same b, 2 + + it "should work in conditional", -> + do + flag = true + const x = 5 if flag + assert.same x, 5 + + it "should work with switch", -> + do + const y = switch 2 + when 2 then 100 + else 0 + assert.same y, 100 + + it "should work with table literals", -> + do + const [a, b] = [1, 2] + assert.same a, 1 + assert.same b, 2 + + it "should support close in expressions", -> + do + close result = if true + 42 + else + 0 + assert.same result, 42 diff --git a/spec/inputs/test/backcall_spec.yue b/spec/inputs/test/backcall_spec.yue new file mode 100644 index 0000000..9534e7c --- /dev/null +++ b/spec/inputs/test/backcall_spec.yue @@ -0,0 +1,29 @@ +describe "backcall", -> + it "should support basic backcall with <-", -> + results = {} + mock_map = (list, fn) -> + for item in *list + table.insert results, fn(item) + (x) <- mock_map {1, 2, 3} + x * 2 + assert.same results, {2, 4, 6} + + it "should support nested backcalls", -> + results = {} + mock_map = (list, fn) -> + for item in *list + fn(item) + mock_map {1, 2, 3, 4}, (x) -> + if x > 2 + table.insert results, x + assert.same results, {3, 4} + + it "should work with method call backcall", -> + results = {} + obj = { + process: (self, fn) -> + fn 42 + } + (value) <- obj\process + table.insert results, value + assert.same results, {42} diff --git a/spec/inputs/test/cond_spec.yue b/spec/inputs/test/cond_spec.yue new file mode 100644 index 0000000..9c7cac7 --- /dev/null +++ b/spec/inputs/test/cond_spec.yue @@ -0,0 +1,148 @@ +describe "cond", -> + it "should execute if branch when condition is true", -> + result = nil + if true + result = "yes" + assert.same result, "yes" + + it "should execute else branch when condition is false", -> + result = nil + if false + result = "yes" + else + result = "no" + assert.same result, "no" + + it "should support elseif chain", -> + value = 2 + result = switch value + when 1 then "one" + when 2 then "two" + else "other" + assert.same result, "two" + + it "should handle nested conditions", -> + result = nil + if true + if true + result = "nested" + assert.same result, "nested" + + it "should work as expression", -> + value = if true then "yes" else "no" + assert.same value, "yes" + + it "should work in string interpolation", -> + flag = true + result = "value is #{if flag then 1 else 0}" + assert.same result, "value is 1" + + it "should support chained comparisons", -> + assert.is_true 1 < 2 <= 2 < 3 + + it "should short-circuit and expression", -> + count = 0 + inc = -> + count += 1 + false + result = inc! and inc! + assert.same count, 1 + + it "should short-circuit or expression", -> + count = 0 + inc = -> + count += 1 + true + result = inc! or inc! + assert.same count, 1 + + it "should support unless keyword", -> + result = nil + unless false + result = "executed" + assert.same result, "executed" + + it "should support unless with else", -> + result = nil + unless true + result = "no" + else + result = "yes" + assert.same result, "yes" + + it "should handle postfix if", -> + result = nil + result = "yes" if true + assert.same result, "yes" + + it "should handle postfix unless", -> + result = nil + result = "yes" unless false + assert.same result, "yes" + + it "should evaluate truthiness correctly", -> + -- nil and false are falsy + assert.is_false if nil then true else false + assert.is_false if false then true else false + + -- Everything else is truthy + assert.is_true if 0 then true else false + assert.is_true if "" then true else false + assert.is_true if {} then true else false + assert.is_true if 1 then true else false + + it "should support and/or operators", -> + assert.same true and false, false + assert.same false or true, true + assert.same nil or "default", "default" + assert.same "value" or "default", "value" + + it "should handle complex boolean expressions", -> + a, b, c = true, false, true + result = a and b or c + assert.same result, c + + it "should support not operator", -> + assert.is_true not false + assert.is_true not nil + assert.is_false not true + assert.is_false not 1 + + it "should work with table as condition", -> + result = nil + if {} + result = "truthy" + assert.same result, "truthy" + + it "should work with string as condition", -> + result = nil + if "" + result = "truthy" + assert.same result, "truthy" + + it "should work with zero as condition", -> + result = nil + if 0 + result = "truthy" + assert.same result, "truthy" + + it "should support multiple elseif branches", -> + value = 3 + result = if value == 1 + "one" + elseif value == 2 + "two" + elseif value == 3 + "three" + else + "other" + assert.same result, "three" + + it "should handle then keyword syntax", -> + result = if true then "yes" else "no" + assert.same result, "yes" + + it "should work with function call in condition", -> + return_true = -> true + result = if return_true! then "yes" else "no" + assert.same result, "yes" diff --git a/spec/inputs/test/config_spec.yue b/spec/inputs/test/config_spec.yue new file mode 100644 index 0000000..2df8ef3 --- /dev/null +++ b/spec/inputs/test/config_spec.yue @@ -0,0 +1,106 @@ +describe "config", -> + -- Note: These tests verify that various compiler configs don't cause errors + -- Actual compiler config testing would require the compiler itself + + it "should handle implicit return", -> + -- implicitReturnRoot is the default + fn = -> + 42 + assert.same fn!, 42 + + it "should handle return in last position", -> + fn = -> + if true + 100 + else + 200 + assert.same fn!, 100 + + it "should work with various code patterns", -> + -- Test that code compiles without explicit config + x = 1 + 2 + y = if x > 0 then "positive" else "negative" + assert.same y, "positive" + + it "should handle class definitions", -> + class TestClass + value: 100 + get_value: => @value + instance = TestClass! + assert.same instance\get_value!, 100 + + it "should handle macro definitions", -> + macro test_macro = (x) -> "#{x} + 1" + result = $test_macro 5 + assert.same result, 6 + + it "should handle import statements", -> + import format from "string" + assert.is_true type(format) == "function" + + it "should handle string interpolation", -> + name = "world" + result = "hello #{name}" + assert.same result, "hello world" + + it "should handle comprehensions", -> + result = [x * 2 for x = 1, 5] + assert.same result, {2, 4, 6, 8, 10} + + it "should handle switch expressions", -> + result = switch 2 + when 1 then "one" + when 2 then "two" + else "other" + assert.same result, "two" + + it "should handle with statements", -> + obj = {x: 10, y: 20} + result = with obj + .x + .y + assert.same result, 30 + + it "should handle existential operators", -> + obj = {value: 100} + result = obj?.value + assert.same result, 100 + + it "should handle pipe operator", -> + result = {1, 2, 3} |> table.concat + assert.same result, "123" + + it "should handle loops", -> + sum = 0 + for i = 1, 5 + sum += i + assert.same sum, 15 + + it "should handle while loops", -> + count = 0 + while count < 3 + count += 1 + assert.same count, 3 + + it "should handle table literals", -> + t = { + key1: "value1" + key2: "value2" + } + assert.same t.key1, "value1" + + it "should handle function definitions", -> + fn = (a, b) -> a + b + assert.same fn(5, 3), 8 + + it "should handle nested functions", -> + outer = -> + inner = (x) -> x * 2 + inner 10 + assert.same outer!, 20 + + it "should handle destructure", -> + t = {x: 1, y: 2, z: 3} + {:x, :y, :z} = t + assert.same x, 1 + assert.same y, 2 + assert.same z, 3 diff --git a/spec/inputs/test/existential_spec.yue b/spec/inputs/test/existential_spec.yue new file mode 100644 index 0000000..f63967a --- /dev/null +++ b/spec/inputs/test/existential_spec.yue @@ -0,0 +1,100 @@ +describe "existential", -> + it "should handle ?. with existing object", -> + obj = {value: 42} + result = obj?.value + assert.same result, 42 + + it "should handle ?. with nil object", -> + obj = nil + result = obj?.value + assert.same result, nil + + it "should chain ?. calls", -> + obj = {nested: {value: 100}} + result = obj?.nested?.value + assert.same result, 100 + + it "should return nil in chain with nil", -> + obj = nil + result = obj?.nested?.value + assert.same result, nil + + it "should handle ?. with method call", -> + obj = {func: -> "result"} + result = obj?.func! + assert.same result, "result" + + it "should handle ? on table index", -> + tb = {[1]: "first"} + result = tb?[1] + assert.same result, "first" + + it "should return nil for missing index", -> + tb = {} + result = tb?[99] + assert.same result, nil + + it "should work with ? in if condition", -> + obj = {value: 5} + if obj?.value + result = "exists" + assert.same result, "exists" + + it "should combine ?. with and/or", -> + obj = {value: 10} + result = obj?.value and 20 or 30 + assert.same result, 20 + + it "should handle with? safely", -> + obj = {x: 1, y: 2} + sum = obj?.x + obj?.y + assert.same sum, 3 + + it "should return nil with with? on nil", -> + obj = nil + result = obj?.x + assert.same result, nil + + it "should handle false value correctly", -> + -- false is a valid value, not nil + obj = {value: false} + result = obj?.value + assert.same result, false + + it "should handle 0 value correctly", -> + -- 0 is a valid value, not nil + obj = {value: 0} + result = obj?.value + assert.same result, 0 + + it "should handle empty string correctly", -> + -- "" is a valid value, not nil + obj = {value: ""} + result = obj?.value + assert.same result, "" + + it "should handle empty table correctly", -> + -- {} is a valid value, not nil + obj = {value: {}} + result = obj?.value + assert.same type(result), "table" + + it "should work with deep chains", -> + obj = {a: {b: {c: {d: "deep"}}}} + result = obj?.a?.b?.c?.d + assert.same result, "deep" + + it "should break chain on first nil", -> + obj = {a: nil} + result = obj?.a?.b?.c + assert.same result, nil + + it "should handle ?. with string methods", -> + s = "hello" + result = s?\upper! + assert.same result, "HELLO" + + it "should handle ?. with nil string", -> + s = nil + result = s?\upper! + assert.same result, nil diff --git a/spec/inputs/test/export_spec.yue b/spec/inputs/test/export_spec.yue new file mode 100644 index 0000000..c6ea99b --- /dev/null +++ b/spec/inputs/test/export_spec.yue @@ -0,0 +1,99 @@ +describe "export", -> + it "should export basic variables", -> + a = 1 + b = 2 + c = 3 + assert.same a, 1 + assert.same b, 2 + assert.same c, 3 + + it "should export multiple variables at once", -> + x, y, z = 10, 20, 30 + assert.same x, 10 + assert.same y, 20 + assert.same z, 30 + + it "should export class definitions", -> + MyClass = class + value: 100 + assert.same MyClass.value, 100 + + it "should export function expressions", -> + my_func = -> 42 + assert.same my_func!, 42 + + it "should export conditional expressions", -> + result = if true + "yes" + else + "no" + assert.same result, "yes" + + it "should export switch expressions", -> + value = switch 5 + when 5 then 100 + else 0 + assert.same value, 100 + + it "should export with do block", -> + result = do + x = 5 + x * 2 + assert.same result, 10 + + it "should export comprehension", -> + doubled = [i * 2 for i = 1, 5] + assert.same doubled, {2, 4, 6, 8, 10} + + it "should export with pipe operator", -> + result = {1, 2, 3} |> table.concat + assert.same result, "123" + + it "should export nil values", -> + empty = nil + assert.same empty, nil + + it "should export tables", -> + config = { + key1: "value1" + key2: "value2" + } + assert.same config.key1, "value1" + assert.same config.key2, "value2" + + it "should export string values", -> + message = "hello world" + assert.same message, "hello world" + + it "should export boolean values", -> + flag_true = true + flag_false = false + assert.is_true flag_true + assert.is_false flag_false + + it "should export number values", -> + count = 42 + price = 19.99 + assert.same count, 42 + assert.same price, 19.99 + + it "should work in nested scope", -> + do + nested = "value" + assert.same nested, "value" + + it "should export function with parameters", -> + add = (a, b) -> a + b + assert.same add(5, 3), 8 + + it "should maintain export order", -> + first = 1 + second = 2 + third = 3 + assert.same first, 1 + assert.same second, 2 + assert.same third, 3 + + it "should work with complex expressions", -> + calc = (10 + 20) * 2 + assert.same calc, 60 diff --git a/spec/inputs/test/goto_spec.yue b/spec/inputs/test/goto_spec.yue new file mode 100644 index 0000000..fd2f401 --- /dev/null +++ b/spec/inputs/test/goto_spec.yue @@ -0,0 +1,80 @@ +describe "goto", -> + it "should support basic goto and label", -> + a = 0 + ::start:: + a += 1 + if a < 5 + goto start + assert.same a, 5 + + it "should support conditional goto", -> + a = 0 + ::loop:: + a += 1 + goto done if a == 3 + goto loop + ::done:: + assert.same a, 3 + + it "should support goto in nested loops", -> + count = 0 + for x = 1, 3 + for y = 1, 3 + count += 1 + if x == 2 and y == 2 + goto found + ::found:: + assert.same count, 4 -- (1,1), (1,2), (1,3), (2,1), (2,2) + + it "should support multiple labels", -> + a = 0 + ::first:: + a += 1 + goto second if a == 2 + goto first + ::second:: + assert.same a, 2 + + it "should work with for loops", -> + sum = 0 + for i = 1, 10 + sum += i + goto done if i == 5 + ::done:: + assert.same sum, 15 -- 1+2+3+4+5 + + it "should work with while loops", -> + count = 0 + while true + count += 1 + goto endwhile if count == 3 + ::endwhile:: + assert.same count, 3 + + it "should skip rest of loop with goto", -> + values = {} + for i = 1, 5 + goto continue if i % 2 == 0 + table.insert values, i + ::continue:: + assert.same values, {1, 3, 5} + + it "should support goto with switch", -> + result = "default" + value = 2 + switch value + when 1 + goto case_one + when 2 + goto case_two + goto default_label + ::case_one:: + result = "one" + goto finish + ::case_two:: + result = "two" + goto finish + ::default_label:: + result = "default" + ::finish:: + assert.same result, "two" diff --git a/spec/inputs/test/import_spec.yue b/spec/inputs/test/import_spec.yue new file mode 100644 index 0000000..deeb4a0 --- /dev/null +++ b/spec/inputs/test/import_spec.yue @@ -0,0 +1,115 @@ +describe "import", -> + it "should import from table expression", -> + source = {hello: "world", foo: "bar"} + import hello, foo from source + assert.same hello, "world" + assert.same foo, "bar" + + it "should import with backslash escaping", -> + source = {x: 1, y: 2, z: 3} + import x, \y, z from source + assert.same x, 1 + assert.same y, 2 + assert.same z, 3 + + it "should import from string module", -> + -- Test with string library + import format from "string" + assert.is_true type(format) == "function" + + it "should import from table with dot path", -> + -- Using string.sub as an example + import sub from "string" + result = sub "hello", 1, 2 + assert.same result, "he" + + it "should import multiple values with table destructuring", -> + source = {a: 1, b: 2, c: 3} + import a, b, c from source + assert.same a, 1 + assert.same b, 2 + assert.same c, 3 + + it "should import with multi-line format", -> + source = {x: 1, y: 2, z: 3} + import x, y, z from source + assert.same x, 1 + assert.same y, 2 + assert.same z, 3 + + it "should import using from syntax", -> + source = {foo: "bar", baz: "qux"} + from source import foo, baz + assert.same foo, "bar" + assert.same baz, "qux" + + it "should handle import with computed expressions", -> + source = {first: 1, second: 2} + target = source + import first, second from target + assert.same first, 1 + assert.same second, 2 + + it "should import from nested table paths", -> + deep = {outer: {inner: "value"}} + import outer from deep + assert.same outer.inner, "value" + + it "should support importing Lua standard library functions", -> + import print, type from "_G" + assert.is_true type(print) == "function" + assert.is_true type(type) == "function" + + it "should handle empty import gracefully", -> + -- Empty module shouldn't cause errors + source = {} + import dummy from source + assert.same dummy, nil + + it "should work with table index expressions", -> + source = {normal: "ok"} + import normal from source + assert.same normal, "ok" + + it "should support chaining imports from same source", -> + source = {a: 1, b: 2, c: 3} + import a, b from source + import c from source + assert.same a, 1 + assert.same b, 2 + assert.same c, 3 + + it "should handle importing from table returned by function", -> + get_table = -> {x: 100, y: 200} + import x, y from get_table! + assert.same x, 100 + assert.same y, 200 + + it "should support from with multi-line import", -> + source = {item1: 1, item2: 2, item3: 3} + from source import item1, item2, item3 + assert.same item1, 1 + assert.same item2, 2 + assert.same item3, 3 + + it "should work with import from string literal", -> + import char from "string" + assert.same char(65), "A" + + it "should support import with table literal keys", -> + source = {normal_key: "value2"} + import normal_key from source + assert.same normal_key, "value2" + + it "should handle consecutive imports", -> + source1 = {a: 1} + source2 = {b: 2} + import a from source1 + import b from source2 + assert.same a, 1 + assert.same b, 2 + + it "should support importing from complex expressions", -> + get_source = -> {result: 42} + import result from get_source! + assert.same result, 42 diff --git a/spec/inputs/test/literals_spec.yue b/spec/inputs/test/literals_spec.yue new file mode 100644 index 0000000..10bd6b3 --- /dev/null +++ b/spec/inputs/test/literals_spec.yue @@ -0,0 +1,81 @@ +describe "literals", -> + it "should support integer literals", -> + assert.same 123, 123 + + it "should support float literals", -> + assert.same 1.5, 1.5 + + it "should support scientific notation", -> + assert.same 1.5e2, 150 + + it "should support negative numbers", -> + assert.same -42, -42 + + it "should support hexadecimal literals", -> + assert.same 0xff, 255 + + it "should support hexadecimal with uppercase", -> + assert.same 0XFF, 255 + + it "should support binary literals", -> + assert.same 0b101, 5 + + it "should support binary with uppercase", -> + assert.same 0B101, 5 + + it "should support number with underscores", -> + assert.same 1_000_000, 1000000 + + it "should support hex with underscores", -> + assert.same 0xDE_AD_BE_EF, 0xDEADBEEF + + it "should support double quote strings", -> + assert.same "hello", "hello" + + it "should support single quote strings", -> + assert.same 'world', 'world' + + it "should support multi-line strings with [[", -> + s = [[ + hello + world + ]] + assert.is_true s\match "hello" + + it "should support multi-line strings with [=[", -> + s = [==[ + test + ]==] + assert.is_true s\match "test" + + it "should support boolean true", -> + assert.same true, true + + it "should support boolean false", -> + assert.same false, false + + it "should support nil", -> + assert.same nil, nil + + it "should support empty table", -> + t = {} + assert.same #t, 0 + + it "should support table with keys", -> + t = {a: 1, b: 2} + assert.same t.a, 1 + assert.same t.b, 2 + + it "should support array literal", -> + t = {1, 2, 3} + assert.same t[1], 1 + assert.same t[2], 2 + assert.same t[3], 3 + + it "should support mixed table", -> + t = { + 1, 2, 3 + key: "value" + } + assert.same t[1], 1 + assert.same t.key, "value" diff --git a/spec/inputs/test/macro_spec.yue b/spec/inputs/test/macro_spec.yue new file mode 100644 index 0000000..a4a170b --- /dev/null +++ b/spec/inputs/test/macro_spec.yue @@ -0,0 +1,135 @@ +describe "macro", -> + it "should define and call basic macro", -> + macro double = (x) -> "#{x} * 2" + result = $double 5 + assert.same result, 10 + + it "should maintain hygiene in macros", -> + macro get_value_hygienic = -> + (-> + local a = 1 + a + 1)! + a = 8 + result = $get_value_hygienic! + assert.same result, 2 + + it "should validate AST types", -> + macro NumAndStr = (num`Num, str`SingleString) -> "[#{num}, #{str}]" + result = $NumAndStr 123, 'xyz' + assert.same result, "[123, xyz]" + + it "should support simple code generation", -> + macro add_one = (x) -> "#{x} + 1" + result = $add_one 10 + assert.same result, 11 + + it "should support nested macro calls", -> + macro inc = (x) -> "#{x} + 1" + macro double_inc = (x) -> $inc($inc(x)) + result = $double_inc 5 + assert.same result, 7 + + it "should respect macro scope in do blocks", -> + macro outer = -> "outer" + do + macro inner = -> "inner" + result = $inner! + assert.same result, "inner" + result = $outer! + assert.same result, "outer" + + it "should provide $LINE macro", -> + line_num = $LINE + assert.is_true line_num > 0 + + it "should inject Lua code", -> + macro lua_code = (code) -> {:code, type: "lua"} + x = 0 + $lua_code [[ + local function f(a) + return a + 1 + end + x = x + f(3) + ]] + assert.same x, 4 + + it "should work in conditional compilation", -> + macro if_debug = (debug_code) -> + if $LINE > 0 + debug_code + else + "" + result = $if_debug "debug mode" + assert.same result, "debug mode" + + it "should work with class system", -> + class Thing + value: 100 + get_value: => @value + instance = Thing! + assert.same instance\get_value!, 100 + + it "should handle macro in switch expressions", -> + macro to_value = (x) -> x + result = switch $to_value "test" + when "test" + "matched" + else + "no match" + assert.same result, "matched" + + it "should support macro in expression context", -> + macro triple = (x) -> "#{x} * 3" + result = 5 + $triple 2 + assert.same result, 11 + + it "should handle $is_ast for type checking", -> + macro check_num = (x) -> + unless $is_ast(Num, x) + error "expected number" + x + result = $check_num 42 + assert.same result, 42 + + it "should work with string interpolation", -> + macro format_result = (name, value) -> "#{name}: #{value}" + result = $format_result "test", 123 + assert.same result, "test: 123" + + it "should support function call syntax", -> + macro my_func = (x, y) -> "#{x} + #{y}" + result = $my_func(5, 10) + assert.same result, 15 + + it "should handle empty macro return", -> + macro skip = -> "" + a = 1 + $skip + a = 2 + assert.same a, 2 + + it "should work with table literals", -> + macro make_point = (x, y) -> "{x: #{x}, y: #{y}}" + point = $make_point 10, 20 + assert.same point.x, 10 + assert.same point.y, 20 + + it "should support conditional expressions in macro", -> + macro add_one = (x) -> "#{x} + 1" + result = $add_one 5 + assert.same result, 6 + + it "should work with comprehension", -> + macro doubled_list = (items) -> "[_ * 2 for _ in *#{items}]" + result = $doubled_list {1, 2, 3} + assert.same result, {2, 4, 6} + + it "should support complex expression macros", -> + macro calc = (a, b, c) -> "#{a} + #{b} * #{c}" + result = $calc 1, 2, 3 + assert.same result, 7 + + it "should work with string literals", -> + macro greet = (name) -> '"Hello, #{name}"' + result = $greet "World" + assert.same result, "Hello, World" diff --git a/spec/inputs/test/metatable_spec.yue b/spec/inputs/test/metatable_spec.yue new file mode 100644 index 0000000..9a2ae6a --- /dev/null +++ b/spec/inputs/test/metatable_spec.yue @@ -0,0 +1,86 @@ +describe "metatable", -> + it "should get metatable with <> syntax", -> + obj = setmetatable {value: 42}, {__index: {extra: "data"}} + mt = obj.<> + assert.is_true mt ~= nil + + it "should set metatable with <>", -> + obj = {} + obj.<> = {__index: {value: 100}} + assert.same obj.value, 100 + + it "should access metatable with <>", -> + obj = setmetatable {}, {__index: {value: 50}} + result = obj.<>.__index.value + assert.same result, 50 + + it "should work with metamethod", -> + obj = setmetatable {}, { + __index: (self, key) -> + if key == "computed" + return "computed_value" + } + assert.same obj.computed, "computed_value" + + it "should work with metamethod", -> + obj = setmetatable {}, { + __newindex: (self, key, value) -> + rawset self, "stored_" .. key, value + } + obj.test = 123 + assert.same obj.stored_test, 123 + + it "should work with metamethod", -> + obj = setmetatable({value: 10}, { + __add: (a, b) -> a.value + b.value + }) + obj2 = setmetatable({value: 20}, { + __add: (a, b) -> a.value + b.value + }) + result = obj + obj2 + assert.same result, 30 + + it "should work with metamethod", -> + obj = setmetatable {}, { + __call: (self, x) -> x * 2 + } + result = obj 5 + assert.same result, 10 + + it "should work with metamethod", -> + obj = setmetatable {value: 42}, { + __tostring: (self) -> "Value: #{self.value}" + } + result = tostring obj + assert.same result, "Value: 42" + + it "should work with metamethod", -> + obj1 = setmetatable({id: 1}, { + __eq: (a, b) -> a.id == b.id + }) + obj2 = setmetatable({id: 1}, { + __eq: (a, b) -> a.id == b.id + }) + assert.is_true obj1 == obj2 + + it "should destructure metatable", -> + obj = setmetatable {}, { + new: -> "new result" + update: -> "update result" + } + {:new, :update} = obj.<> + assert.is_true type(new) == "function" + assert.is_true type(update) == "function" + + it "should check if two objects have same metatable", -> + mt = {value: 100} + obj1 = setmetatable {}, mt + obj2 = setmetatable {}, mt + assert.is_true obj1.<> == obj2.<> + + it "should work with metamethod", -> + obj = setmetatable {value: "hello"}, { + __concat: (a, b) -> a.value .. b + } + result = obj .. " world" + assert.same result, "hello world" diff --git a/spec/inputs/test/operators_spec.yue b/spec/inputs/test/operators_spec.yue new file mode 100644 index 0000000..9b5585b --- /dev/null +++ b/spec/inputs/test/operators_spec.yue @@ -0,0 +1,137 @@ +describe "operators", -> + it "should support addition", -> + assert.same 1 + 2, 3 + + it "should support subtraction", -> + assert.same 5 - 3, 2 + + it "should support multiplication", -> + assert.same 4 * 3, 12 + + it "should support division", -> + assert.same 10 / 2, 5 + + it "should support modulo", -> + assert.same 10 % 3, 1 + + it "should support exponentiation", -> + assert.same 2 ^ 3, 8 + + it "should support unary minus", -> + assert.same -5, -5 + + it "should support equality comparison", -> + assert.is_true 1 == 1 + assert.is_false 1 == 2 + + it "should support inequality comparison", -> + assert.is_true 1 ~= 2 + assert.is_false 1 ~= 1 + + it "should support less than", -> + assert.is_true 1 < 2 + assert.is_false 2 < 1 + + it "should support greater than", -> + assert.is_true 2 > 1 + assert.is_false 1 > 2 + + it "should support less than or equal", -> + assert.is_true 1 <= 2 + assert.is_true 2 <= 2 + assert.is_false 3 <= 2 + + it "should support greater than or equal", -> + assert.is_true 2 >= 1 + assert.is_true 2 >= 2 + assert.is_false 1 >= 2 + + it "should support logical and", -> + assert.same true and false, false + assert.same true and true, true + assert.same false and true, false + + it "should support logical or", -> + assert.same true or false, true + assert.same false or true, true + assert.same false or false, false + + it "should support logical not", -> + assert.same not true, false + assert.same not false, true + assert.same not nil, true + + it "should support bitwise and", -> + assert.same 5 & 3, 1 -- 101 & 011 = 001 + + it "should support bitwise or", -> + assert.same 5 | 3, 7 -- 101 | 011 = 111 + + it "should support bitwise xor", -> + assert.same 5 ~ 3, 6 -- 101 ~ 011 = 110 + + it "should support left shift", -> + assert.same 2 << 3, 16 + + it "should support right shift", -> + assert.same 16 >> 2, 4 + + it "should support string concatenation", -> + assert.same "hello" .. " world", "hello world" + + it "should support length operator", -> + assert.same #"hello", 5 + assert.same #{1, 2, 3}, 3 + + it "should respect operator precedence", -> + assert.same 1 + 2 * 3, 7 -- multiplication before addition + assert.same (1 + 2) * 3, 9 -- parentheses first + + it "should support compound assignment", -> + x = 10 + x += 5 + assert.same x, 15 + + it "should support compound subtraction", -> + x = 10 + x -= 3 + assert.same x, 7 + + it "should support compound multiplication", -> + x = 5 + x *= 2 + assert.same x, 10 + + it "should support compound division", -> + x = 20 + x /= 4 + assert.same x, 5 + + it "should handle division by zero", -> + -- Lua returns inf or nan + result = pcall(-> + x = 10 / 0 + ) + assert.is_true result -- doesn't error in Lua + + it "should handle very large numbers", -> + big = 1e100 + assert.is_true big > 0 + + it "should handle very small numbers", -> + small = 1e-100 + assert.is_true small > 0 + + it "should support negation", -> + assert.same -10, -10 + assert.same --5, 5 + + it "should work with complex expressions", -> + result = (1 + 2) * (3 + 4) / 2 + assert.same result, 10.5 + + it "should support power with decimal", -> + assert.same 4 ^ 0.5, 2 + + it "should handle modulo with negative numbers", -> + assert.same -10 % 3, 2 -- Lua's modulo behavior diff --git a/spec/inputs/test/return_spec.yue b/spec/inputs/test/return_spec.yue new file mode 100644 index 0000000..3bf0bed --- /dev/null +++ b/spec/inputs/test/return_spec.yue @@ -0,0 +1,85 @@ +describe "return", -> + it "should return from comprehension", -> + fn = -> + return [x * 2 for x = 1, 5] + result = fn! + assert.same result, {2, 4, 6, 8, 10} + + it "should return from table comprehension", -> + fn = -> + return {k, v for k, v in pairs {a: 1, b: 2}} + result = fn! + assert.same type(result), "table" + + it "should return from nested if", -> + fn = (a, b) -> + if a + if b + return "both" + else + return "only a" + else + return "neither" + assert.same fn(true, true), "both" + assert.same fn(true, false), "only a" + assert.same fn(false, false), "neither" + + it "should return from switch", -> + fn = (value) -> + return switch value + when 1 then "one" + when 2 then "two" + else "other" + assert.same fn(1), "one" + assert.same fn(2), "two" + assert.same fn(3), "other" + + it "should return table literal", -> + fn = -> + return + value: 42 + name: "test" + result = fn! + assert.same result.value, 42 + assert.same result.name, "test" + + it "should return array literal", -> + fn = -> + return + * 1 + * 2 + * 3 + result = fn! + assert.same result, {1, 2, 3} + + it "should return from with statement", -> + fn = (obj) -> + result = obj.value + return result + assert.same fn({value: 100}), 100 + + it "should return nil implicitly", -> + fn -> print "no return" + assert.same fn!, nil + + it "should return multiple values", -> + fn -> 1, 2, 3 + a, b, c = fn! + assert.same a, 1 + assert.same b, 2 + assert.same c, 3 + + it "should return from function call", -> + fn = -> + inner = -> 42 + return inner! + assert.same fn!, 42 + + it "should handle return in expression context", -> + fn = (cond) -> + if cond + return "yes" + else + return "no" + assert.same fn(true), "yes" + assert.same fn(false), "no" diff --git a/spec/inputs/test/string_spec.yue b/spec/inputs/test/string_spec.yue new file mode 100644 index 0000000..b790518 --- /dev/null +++ b/spec/inputs/test/string_spec.yue @@ -0,0 +1,143 @@ +describe "string", -> + it "should support single quote strings", -> + s = 'hello' + assert.same s, "hello" + + it "should support double quote strings", -> + s = "world" + assert.same s, "world" + + it "should support escape sequences", -> + s = "hello\nworld" + assert.is_true s\match("\n") ~= nil + + it "should support escaped quotes", -> + s = "he said \"hello\"" + assert.same s, 'he said "hello"' + + it "should support backslash escape", -> + s = "\\" + assert.same s, "\\" + + it "should support multi-line strings with [[ ]]", -> + s = [[ + hello + world + ]] + assert.is_true s\match("hello") ~= nil + assert.is_true s\match("world") ~= nil + + it "should support multi-line strings with [=[ ]=]", -> + s = [==[ + hello + world + ]==] + assert.is_true s\match("hello") ~= nil + assert.is_true s\match("world") ~= nil + + it "should support string interpolation with double quotes", -> + name = "world" + s = "hello #{name}" + assert.same s, "hello world" + + it "should support expression interpolation", -> + a, b = 1, 2 + s = "#{a} + #{b} = #{a + b}" + assert.same s, "1 + 2 = 3" + + it "should not interpolate in single quotes", -> + name = "world" + s = 'hello #{name}' + assert.same s, "hello #{name}" + + it "should escape interpolation with \\#", -> + name = "world" + s = "hello \\#{name}" + assert.same s, "hello #{name}" + + it "should support method calls on string literals", -> + result = "hello"\upper! + assert.same result, "HELLO" + + it "should support chained method calls", -> + result = "hello world"\upper!\match "HELLO" + assert.same result, "HELLO" + + it "should support YAML style strings", -> + s = | + hello + world + assert.is_true s\match("hello") ~= nil + assert.is_true s\match("world") ~= nil + + it "should support YAML style with interpolation", -> + name = "test" + s = | + hello #{name} + assert.same s, "hello test\n" + + it "should support string concatenation", -> + s = "hello" .. " " .. "world" + assert.same s, "hello world" + + it "should handle empty strings", -> + s = "" + assert.same s, "" + + it "should support Unicode characters", -> + s = "hello 世界" + assert.is_true s\match("世界") ~= nil + + it "should support string length", -> + s = "hello" + assert.same #s, 5 + + it "should support multi-line YAML with complex content", -> + config = | + key1: value1 + key2: value2 + key3: value3 + assert.is_true config\match("key1") ~= nil + + it "should support interpolation in YAML strings", -> + x, y = 10, 20 + s = | + point: + x: #{x} + y: #{y} + assert.is_true s\match("x: 10") ~= nil + assert.is_true s\match("y: 20") ~= nil + + it "should support function call in interpolation", -> + s = "result: #{-> 42}" + assert.same s, "result: 42" + + it "should support table indexing in interpolation", -> + t = {value: 100} + s = "value: #{t.value}" + assert.same s, "value: 100" + + it "should handle escaped characters correctly", -> + s = "tab:\t, newline:\n, return:\r" + assert.is_true s\match("\t") ~= nil + assert.is_true s\match("\n") ~= nil + + it "should support string methods with colon syntax", -> + s = "hello" + assert.same s\sub(1, 2), "he" + + it "should work in expressions", -> + result = "hello" .. " world" + assert.same result, "hello world" + + it "should support octal escape", -> + s = "\65" + assert.same s, "A" + + it "should support hex escape", -> + s = "\x41" + assert.same s, "A" + + it "should support unicode escape", -> + s = "\u{4e16}" + assert.same s, "世" diff --git a/spec/inputs/test/switch_spec.yue b/spec/inputs/test/switch_spec.yue new file mode 100644 index 0000000..3696cbe --- /dev/null +++ b/spec/inputs/test/switch_spec.yue @@ -0,0 +1,267 @@ +describe "switch", -> + it "should match single value", -> + value = "cool" + result = switch value + when "cool" + "matched" + else + "not matched" + assert.same result, "matched" + + it "should match multiple values with or", -> + hi = "world" + matched = false + switch hi + when "one", "two" + matched = true + assert.is_false matched + + hi = "one" + switch hi + when "one", "two" + matched = true + assert.is_true matched + + it "should execute else branch when no match", -> + value = "other" + result = switch value + when "cool" + "matched cool" + when "yeah" + "matched yeah" + else + "else branch" + assert.same result, "else branch" + + it "should destructure table with single key", -> + tb = {x: 100} + result = switch tb + when :x + x + else + "no match" + assert.same result, 100 + + it "should destructure table with multiple keys", -> + tb = {x: 100, y: 200} + result = switch tb + when :x, :y + x + y + else + "no match" + assert.same result, 300 + + it "should destructure table with default values", -> + tb = {a: 1} + switch tb + when {:a = 1, :b = 2} + assert.same a, 1 + assert.same b, 2 + + it "should destructure nested tables", -> + dict = { + {} + {1, 2, 3} + a: b: c: 1 + x: y: z: 1 + } + matched = false + switch dict + when { + first + {one, two, three} + a: b: :c + x: y: :z + } + matched = first == {} and one == 1 and two == 2 and three == 3 and c == 1 and z == 1 + assert.is_true matched + + it "should destructure arrays with exact match", -> + tb = {1, 2, 3} + result = switch tb + when [1, 2, 3] + "exact match" + else + "no match" + assert.same result, "exact match" + + it "should destructure arrays with variables", -> + tb = {1, "b", 3} + result = switch tb + when [1, b, 3] + b + else + "no match" + assert.same result, "b" + + it "should destructure arrays with defaults", -> + tb = {1, 2} + result = switch tb + when [1, 2, b = 3] + b + else + "no match" + assert.same result, 3 + + it "should match pattern with __class", -> + class ClassA + class ClassB + item = ClassA! + result = switch item + when __class: ClassA + "Object A" + when __class: ClassB + "Object B" + else + "unknown" + assert.same result, "Object A" + + it "should match pattern with metatable", -> + tb = setmetatable {}, {__mode: "v"} + metatable_matched = false + switch tb + when <>: mt + metatable_matched = mt ~= nil + assert.is_true metatable_matched + + it "should use switch as expression in assignment", -> + tb = {x: "abc"} + matched = switch tb + when 1 + "1" + when :x + x + when false + "false" + else + nil + assert.same matched, "abc" + + it "should use switch in return statement", -> + fn = (tb) -> + switch tb + when nil + "invalid" + when :a, :b + "#{a + b}" + when 1, 2, 3, 4, 5 + "number 1 - 5" + else + "should not reach here" + assert.same fn({a: 1, b: 2}), "3" + assert.same fn(3), "number 1 - 5" + assert.same fn(nil), "invalid" + + it "should support pattern matching assignment with :=", -> + v = "hello" + matched = false + switch v := "hello" + when "hello" + matched = true + else + matched = false + assert.is_true matched + assert.same v, "hello" + + it "should match with computed expressions", -> + hi = 4 + matched = false + switch hi + when 3+1, (-> 4)!, 5-1 + matched = true + assert.is_true matched + + it "should handle nested array destructuring", -> + tb = { + {a: 1, b: 2} + {a: 3, b: 4} + {a: 5, b: 6} + "fourth" + } + result = switch tb + when [ + {a: 1, b: 2} + {a: 3, b: 4} + {a: 5, b: 6} + fourth + ] + fourth + else + "no match" + assert.same result, "fourth" + + it "should match combined patterns", -> + tb = {success: true, result: "data"} + result = switch tb + when success: true, :result + {"success", result} + when success: false + {"failed", result} + else + {"invalid"} + assert.same result, {"success", "data"} + + it "should match type discriminated patterns", -> + tb = {type: "success", content: "data"} + result = switch tb + when {type: "success", :content} + {"success", content} + when {type: "error", :content} + {"error", content} + else + {"invalid"} + assert.same result, {"success", "data"} + + it "should match with wildcard array capture", -> + clientData = {"Meta", "CUST_1001", "CHK123"} + metadata = nil + customerId = nil + checksum = nil + switch clientData + when [...capturedMetadata, customerId, checksum] + metadata = capturedMetadata + assert.same metadata, {"Meta"} + assert.same customerId, "CUST_1001" + assert.same checksum, "CHK123" + + it "should work with complex tuple patterns", -> + handlePath = (segments) -> + switch segments + when [..._, resource, action] + {"Resource: #{resource}", "Action: #{action}"} + else + {"no match"} + result = handlePath {"admin", "logs", "view"} + assert.same result, {"Resource: logs", "Action: view"} + + it "should match boolean false correctly", -> + items = { + {x: 100, y: 200} + {width: 300, height: 400} + false + } + results = {} + for item in *items + switch item + when :x, :y + table.insert results, "Vec2" + when :width, :height + table.insert results, "Size" + when false + table.insert results, "None" + assert.same results, {"Vec2", "Size", "None"} + + it "should handle switch with then syntax", -> + value = "cool" + result = switch value + when "cool" then "matched cool" + else "else branch" + assert.same result, "matched cool" + + it "should handle switch in function call", -> + getValue = -> + switch something + when 1 then "yes" + else "no" + something = 1 + assert.same getValue!, "yes" diff --git a/spec/inputs/test/vararg_spec.yue b/spec/inputs/test/vararg_spec.yue new file mode 100644 index 0000000..4d2557f --- /dev/null +++ b/spec/inputs/test/vararg_spec.yue @@ -0,0 +1,69 @@ +describe "vararg", -> + it "should pass varargs to function", -> + sum = (...) -> + total = 0 + for i = 1, select("#", ...) + if type(select(i, ...)) == "number" + total += select(i, ...) + total + result = sum 1, 2, 3, 4, 5 + assert.same result, 15 + + it "should handle empty varargs", -> + fn = (...) -> select "#", ... + result = fn! + assert.same result, 0 + + it "should spread varargs in function call", -> + receiver = (a, b, c) -> {a, b, c} + source = -> 1, 2, 3 + result = receiver source! + assert.same result, {1, 2, 3} + + it "should use varargs in table", -> + fn = (...) -> {...} + result = fn 1, 2, 3 + assert.same result, {1, 2, 3} + + it "should forward varargs", -> + middle = (fn, ...) -> fn(...) + inner = (a, b, c) -> a + b + c + result = middle inner, 1, 2, 3 + assert.same result, 6 + + it "should count varargs with select", -> + fn = (...) -> select "#", ... + assert.same fn(1, 2, 3), 3 + assert.same fn("a", "b"), 2 + assert.same fn!, 0 + + it "should select from varargs", -> + fn = (...) -> select 2, ... + result = fn 1, 2, 3 + assert.same result, 2 + + it "should work with named parameters and varargs", -> + fn = (first, ...) -> + {first, select("#", ...)} + result = fn "first", "second", "third" + assert.same result, {"first", 2} + + it "should handle nil in varargs", -> + fn = (...) -> + count = select "#", ... + has_nil = false + for i = 1, count + has_nil = true if select(i, ...) == nil + {count, has_nil} + result = fn 1, nil, 3 + assert.same result, {3, true} + + it "should work with table unpack", -> + fn = (...) -> {...} + result = fn table.unpack {1, 2, 3} + assert.same result, {1, 2, 3} + + it "should work with varargs in comprehension", -> + fn = (...) -> [x * 2 for x in *{...}] + result = fn 1, 2, 3, 4, 5 + assert.same result, {2, 4, 6, 8, 10} diff --git a/spec/inputs/test/with_spec.yue b/spec/inputs/test/with_spec.yue new file mode 100644 index 0000000..c3b8428 --- /dev/null +++ b/spec/inputs/test/with_spec.yue @@ -0,0 +1,104 @@ +describe "with", -> + it "should access property with . syntax", -> + obj = {value: 42} + with obj + result = .value + assert.same result, 42 + + it "should call method with : syntax", -> + obj = {func: -> "result"} + with obj + result = \func! + assert.same result, "result" + + it "should work as statement", -> + obj = {x: 10, y: 20} + with obj + .sum = .x + .y + assert.same obj.sum, 30 + + it "should support nested with", -> + outer = {inner: {value: 100}} + with outer.inner + result = .value + assert.same result, 100 + + it "should work with? safely", -> + obj = {x: 5} + with obj + result = .x + assert.same result, 5 + + it "should work with if inside with", -> + obj = {x: 10, y: 20} + with obj + if .x > 5 + result = .x + .y + assert.same result, 30 + + it "should work with switch inside with", -> + obj = {type: "add", a: 5, b: 3} + with obj + result = switch .type + when "add" then .a + .b + else 0 + assert.same result, 8 + + it "should work with loop inside with", -> + obj = {items: {1, 2, 3}} + sum = 0 + with obj + for item in *.items + sum += item + assert.same sum, 6 + + it "should work with destructure", -> + obj = {x: 1, y: 2, z: 3} + with obj + {x, y, z} = obj + assert.same x, 1 + assert.same y, 2 + assert.same z, 3 + + it "should handle simple with body", -> + obj = {value: 42} + with obj + .value2 = 100 + assert.same obj.value2, 100 + + it "should work with return inside", -> + obj = {value: 100} + fn = -> + with obj + return .value + assert.same fn!, 100 + + it "should work with break inside", -> + sum = 0 + for i = 1, 5 + obj = {value: i} + with obj + if .value == 3 + break + sum += .value + assert.same sum, 3 -- 1 + 2 + + it "should chain property access", -> + obj = {a: {b: {c: 42}}} + with obj.a.b + result = .c + assert.same result, 42 + + it "should work with multiple statements", -> + obj = {x: 1, y: 2} + sum = 0 + with obj + sum += .x + sum += .y + assert.same sum, 3 + + it "should preserve object reference", -> + obj = {value: 42} + with obj + .value = 100 + assert.same obj.value, 100 diff --git a/spec/outputs/5.1/test/attrib_spec.lua b/spec/outputs/5.1/test/attrib_spec.lua new file mode 100644 index 0000000..6fd2287 --- /dev/null +++ b/spec/outputs/5.1/test/attrib_spec.lua @@ -0,0 +1,86 @@ +return describe("attrib", function() -- 1 + it("should support const attribute", function() -- 2 + do -- 3 + local x = 10 -- 4 + return assert.same(x, 10) -- 5 + end -- 3 + end) -- 2 + it("should support const with multiple variables", function() -- 7 + do -- 8 + local a , b , c = 1, 2, 3 -- 9 + assert.same(a, 1) -- 10 + assert.same(b, 2) -- 11 + return assert.same(c, 3) -- 12 + end -- 8 + end) -- 7 + it("should support close attribute", function() -- 14 + do -- 16 + local x = 1 -- 17 + return assert.same(x, 1) -- 18 + end -- 16 + end) -- 14 + it("should work with destructuring", function() -- 20 + do -- 21 + local a, b -- 22 + do -- 22 + local _obj_0 = { -- 22 + a = 1, -- 22 + b = 2 -- 22 + } -- 22 + a, b = _obj_0[1], _obj_0[2] -- 22 + end -- 22 + assert.same(a, 1) -- 23 + return assert.same(b, 2) -- 24 + end -- 21 + end) -- 20 + it("should work in conditional", function() -- 26 + do -- 27 + local flag = true -- 28 + local x -- 29 + if flag then -- 29 + x = 5 -- 29 + end -- 29 + return assert.same(x, 5) -- 30 + end -- 27 + end) -- 26 + it("should work with switch", function() -- 32 + do -- 33 + local y -- 34 + do -- 34 + local _exp_0 = 2 -- 34 + if 2 == _exp_0 then -- 35 + y = 100 -- 35 + else -- 36 + y = 0 -- 36 + end -- 34 + end -- 34 + return assert.same(y, 100) -- 37 + end -- 33 + end) -- 32 + it("should work with table literals", function() -- 39 + do -- 40 + local a, b -- 41 + do -- 41 + local _obj_0 = { -- 41 + 1, -- 41 + 2 -- 41 + } -- 41 + a, b = _obj_0[1], _obj_0[2] -- 41 + end -- 41 + assert.same(a, 1) -- 42 + return assert.same(b, 2) -- 43 + end -- 40 + end) -- 39 + return it("should support close in expressions", function() -- 45 + do -- 46 + local result -- 47 + if true then -- 47 + result = 42 -- 48 + else -- 50 + result = 0 -- 50 + end -- 47 + local _close_0 = result -- 47 + return assert.same(result, 42) -- 51 + end -- 46 + end) -- 45 +end) -- 1 diff --git a/spec/outputs/5.1/test/cond_spec.lua b/spec/outputs/5.1/test/cond_spec.lua new file mode 100644 index 0000000..c89a1cc --- /dev/null +++ b/spec/outputs/5.1/test/cond_spec.lua @@ -0,0 +1,237 @@ +local _anon_func_0 = function(flag) -- 37 + if flag then -- 37 + return 1 -- 37 + else -- 37 + return 0 -- 37 + end -- 37 +end -- 37 +local _anon_func_1 = function() -- 85 + if nil then -- 85 + return true -- 85 + else -- 85 + return false -- 85 + end -- 85 +end -- 85 +local _anon_func_2 = function() -- 86 + if false then -- 86 + return true -- 86 + else -- 86 + return false -- 86 + end -- 86 +end -- 86 +local _anon_func_3 = function() -- 89 + if 0 then -- 89 + return true -- 89 + else -- 89 + return false -- 89 + end -- 89 +end -- 89 +local _anon_func_4 = function() -- 90 + if "" then -- 90 + return true -- 90 + else -- 90 + return false -- 90 + end -- 90 +end -- 90 +local _anon_func_5 = function() -- 91 + if { } then -- 91 + return true -- 91 + else -- 91 + return false -- 91 + end -- 91 +end -- 91 +local _anon_func_6 = function() -- 92 + if 1 then -- 92 + return true -- 92 + else -- 92 + return false -- 92 + end -- 92 +end -- 92 +return describe("cond", function() -- 1 + it("should execute if branch when condition is true", function() -- 2 + local result = nil -- 3 + if true then -- 4 + result = "yes" -- 5 + end -- 4 + return assert.same(result, "yes") -- 6 + end) -- 2 + it("should execute else branch when condition is false", function() -- 8 + local result = nil -- 9 + if false then -- 10 + result = "yes" -- 11 + else -- 13 + result = "no" -- 13 + end -- 10 + return assert.same(result, "no") -- 14 + end) -- 8 + it("should support elseif chain", function() -- 16 + local value = 2 -- 17 + local result -- 18 + if 1 == value then -- 19 + result = "one" -- 19 + elseif 2 == value then -- 20 + result = "two" -- 20 + else -- 21 + result = "other" -- 21 + end -- 18 + return assert.same(result, "two") -- 22 + end) -- 16 + it("should handle nested conditions", function() -- 24 + local result = nil -- 25 + if true then -- 26 + if true then -- 27 + result = "nested" -- 28 + end -- 27 + end -- 26 + return assert.same(result, "nested") -- 29 + end) -- 24 + it("should work as expression", function() -- 31 + local value -- 32 + if true then -- 32 + value = "yes" -- 32 + else -- 32 + value = "no" -- 32 + end -- 32 + return assert.same(value, "yes") -- 33 + end) -- 31 + it("should work in string interpolation", function() -- 35 + local flag = true -- 36 + local result = "value is " .. tostring(_anon_func_0(flag)) -- 37 + return assert.same(result, "value is 1") -- 38 + end) -- 35 + it("should support chained comparisons", function() -- 40 + return assert.is_true(1 < 2 and 2 <= 2 and 2 < 3) -- 41 + end) -- 40 + it("should short-circuit and expression", function() -- 43 + local count = 0 -- 44 + local inc -- 45 + inc = function() -- 45 + count = count + 1 -- 46 + return false -- 47 + end -- 45 + local result = inc() and inc() -- 48 + return assert.same(count, 1) -- 49 + end) -- 43 + it("should short-circuit or expression", function() -- 51 + local count = 0 -- 52 + local inc -- 53 + inc = function() -- 53 + count = count + 1 -- 54 + return true -- 55 + end -- 53 + local result = inc() or inc() -- 56 + return assert.same(count, 1) -- 57 + end) -- 51 + it("should support unless keyword", function() -- 59 + local result = nil -- 60 + if not false then -- 61 + result = "executed" -- 62 + end -- 61 + return assert.same(result, "executed") -- 63 + end) -- 59 + it("should support unless with else", function() -- 65 + local result = nil -- 66 + if not true then -- 67 + result = "no" -- 68 + else -- 70 + result = "yes" -- 70 + end -- 67 + return assert.same(result, "yes") -- 71 + end) -- 65 + it("should handle postfix if", function() -- 73 + local result = nil -- 74 + if true then -- 75 + result = "yes" -- 75 + end -- 75 + return assert.same(result, "yes") -- 76 + end) -- 73 + it("should handle postfix unless", function() -- 78 + local result = nil -- 79 + if not false then -- 80 + result = "yes" -- 80 + end -- 80 + return assert.same(result, "yes") -- 81 + end) -- 78 + it("should evaluate truthiness correctly", function() -- 83 + assert.is_false(_anon_func_1()) -- 85 + assert.is_false(_anon_func_2()) -- 86 + assert.is_true(_anon_func_3()) -- 89 + assert.is_true(_anon_func_4()) -- 90 + assert.is_true(_anon_func_5()) -- 91 + return assert.is_true(_anon_func_6()) -- 92 + end) -- 83 + it("should support and/or operators", function() -- 94 + assert.same(true and false, false) -- 95 + assert.same(false or true, true) -- 96 + assert.same(nil or "default", "default") -- 97 + return assert.same("value" or "default", "value") -- 98 + end) -- 94 + it("should handle complex boolean expressions", function() -- 100 + local a, b, c = true, false, true -- 101 + local result = a and b or c -- 102 + return assert.same(result, c) -- 103 + end) -- 100 + it("should support not operator", function() -- 105 + assert.is_true(not false) -- 106 + assert.is_true(not nil) -- 107 + assert.is_false(not true) -- 108 + return assert.is_false(not 1) -- 109 + end) -- 105 + it("should work with table as condition", function() -- 111 + local result = nil -- 112 + if { } then -- 113 + result = "truthy" -- 114 + end -- 113 + return assert.same(result, "truthy") -- 115 + end) -- 111 + it("should work with string as condition", function() -- 117 + local result = nil -- 118 + if "" then -- 119 + result = "truthy" -- 120 + end -- 119 + return assert.same(result, "truthy") -- 121 + end) -- 117 + it("should work with zero as condition", function() -- 123 + local result = nil -- 124 + if 0 then -- 125 + result = "truthy" -- 126 + end -- 125 + return assert.same(result, "truthy") -- 127 + end) -- 123 + it("should support multiple elseif branches", function() -- 129 + local value = 3 -- 130 + local result -- 131 + if value == 1 then -- 131 + result = "one" -- 132 + elseif value == 2 then -- 133 + result = "two" -- 134 + elseif value == 3 then -- 135 + result = "three" -- 136 + else -- 138 + result = "other" -- 138 + end -- 131 + return assert.same(result, "three") -- 139 + end) -- 129 + it("should handle then keyword syntax", function() -- 141 + local result -- 142 + if true then -- 142 + result = "yes" -- 142 + else -- 142 + result = "no" -- 142 + end -- 142 + return assert.same(result, "yes") -- 143 + end) -- 141 + return it("should work with function call in condition", function() -- 145 + local return_true -- 146 + return_true = function() -- 146 + return true -- 146 + end -- 146 + local result -- 147 + if return_true() then -- 147 + result = "yes" -- 147 + else -- 147 + result = "no" -- 147 + end -- 147 + return assert.same(result, "yes") -- 148 + end) -- 145 +end) -- 1 diff --git a/spec/outputs/5.1/test/existential_spec.lua b/spec/outputs/5.1/test/existential_spec.lua new file mode 100644 index 0000000..8b8064a --- /dev/null +++ b/spec/outputs/5.1/test/existential_spec.lua @@ -0,0 +1,243 @@ +local _anon_func_0 = function(obj) -- 39 + if obj ~= nil then -- 39 + return obj.value -- 39 + end -- 39 + return nil -- 39 +end -- 39 +local _anon_func_1 = function(obj) -- 45 + if obj ~= nil then -- 45 + return obj.value -- 45 + end -- 45 + return nil -- 45 +end -- 45 +local _anon_func_2 = function(obj) -- 50 + if obj ~= nil then -- 50 + return obj.x -- 50 + end -- 50 + return nil -- 50 +end -- 50 +local _anon_func_3 = function(obj) -- 50 + if obj ~= nil then -- 50 + return obj.y -- 50 + end -- 50 + return nil -- 50 +end -- 50 +return describe("existential", function() -- 1 + it("should handle ?. with existing object", function() -- 2 + local obj = { -- 3 + value = 42 -- 3 + } -- 3 + local result -- 4 + if obj ~= nil then -- 4 + result = obj.value -- 4 + end -- 4 + return assert.same(result, 42) -- 5 + end) -- 2 + it("should handle ?. with nil object", function() -- 7 + local obj = nil -- 8 + local result -- 9 + if obj ~= nil then -- 9 + result = obj.value -- 9 + end -- 9 + return assert.same(result, nil) -- 10 + end) -- 7 + it("should chain ?. calls", function() -- 12 + local obj = { -- 13 + nested = { -- 13 + value = 100 -- 13 + } -- 13 + } -- 13 + local result -- 14 + if obj ~= nil then -- 14 + do -- 14 + local _obj_0 = obj.nested -- 14 + if _obj_0 ~= nil then -- 14 + result = _obj_0.value -- 14 + end -- 14 + end -- 14 + end -- 14 + return assert.same(result, 100) -- 15 + end) -- 12 + it("should return nil in chain with nil", function() -- 17 + local obj = nil -- 18 + local result -- 19 + if obj ~= nil then -- 19 + do -- 19 + local _obj_0 = obj.nested -- 19 + if _obj_0 ~= nil then -- 19 + result = _obj_0.value -- 19 + end -- 19 + end -- 19 + end -- 19 + return assert.same(result, nil) -- 20 + end) -- 17 + it("should handle ?. with method call", function() -- 22 + local obj = { -- 23 + func = function() -- 23 + return "result" -- 23 + end -- 23 + } -- 23 + local result -- 24 + if obj ~= nil then -- 24 + result = obj.func() -- 24 + end -- 24 + return assert.same(result, "result") -- 25 + end) -- 22 + it("should handle ? on table index", function() -- 27 + local tb = { -- 28 + [1] = "first" -- 28 + } -- 28 + local result -- 29 + if tb ~= nil then -- 29 + result = tb[1] -- 29 + end -- 29 + return assert.same(result, "first") -- 30 + end) -- 27 + it("should return nil for missing index", function() -- 32 + local tb = { } -- 33 + local result -- 34 + if tb ~= nil then -- 34 + result = tb[99] -- 34 + end -- 34 + return assert.same(result, nil) -- 35 + end) -- 32 + it("should work with ? in if condition", function() -- 37 + local obj = { -- 38 + value = 5 -- 38 + } -- 38 + if _anon_func_0(obj) then -- 39 + local result = "exists" -- 40 + end -- 39 + return assert.same(result, "exists") -- 41 + end) -- 37 + it("should combine ?. with and/or", function() -- 43 + local obj = { -- 44 + value = 10 -- 44 + } -- 44 + local result = _anon_func_1(obj) and 20 or 30 -- 45 + return assert.same(result, 20) -- 46 + end) -- 43 + it("should handle with? safely", function() -- 48 + local obj = { -- 49 + x = 1, -- 49 + y = 2 -- 49 + } -- 49 + local sum = _anon_func_2(obj) + _anon_func_3(obj) -- 50 + return assert.same(sum, 3) -- 51 + end) -- 48 + it("should return nil with with? on nil", function() -- 53 + local obj = nil -- 54 + local result -- 55 + if obj ~= nil then -- 55 + result = obj.x -- 55 + end -- 55 + return assert.same(result, nil) -- 56 + end) -- 53 + it("should handle false value correctly", function() -- 58 + local obj = { -- 60 + value = false -- 60 + } -- 60 + local result -- 61 + if obj ~= nil then -- 61 + result = obj.value -- 61 + end -- 61 + return assert.same(result, false) -- 62 + end) -- 58 + it("should handle 0 value correctly", function() -- 64 + local obj = { -- 66 + value = 0 -- 66 + } -- 66 + local result -- 67 + if obj ~= nil then -- 67 + result = obj.value -- 67 + end -- 67 + return assert.same(result, 0) -- 68 + end) -- 64 + it("should handle empty string correctly", function() -- 70 + local obj = { -- 72 + value = "" -- 72 + } -- 72 + local result -- 73 + if obj ~= nil then -- 73 + result = obj.value -- 73 + end -- 73 + return assert.same(result, "") -- 74 + end) -- 70 + it("should handle empty table correctly", function() -- 76 + local obj = { -- 78 + value = { } -- 78 + } -- 78 + local result -- 79 + if obj ~= nil then -- 79 + result = obj.value -- 79 + end -- 79 + return assert.same(type(result), "table") -- 80 + end) -- 76 + it("should work with deep chains", function() -- 82 + local obj = { -- 83 + a = { -- 83 + b = { -- 83 + c = { -- 83 + d = "deep" -- 83 + } -- 83 + } -- 83 + } -- 83 + } -- 83 + local result -- 84 + if obj ~= nil then -- 84 + do -- 84 + local _obj_0 = obj.a -- 84 + if _obj_0 ~= nil then -- 84 + do -- 84 + local _obj_1 = _obj_0.b -- 84 + if _obj_1 ~= nil then -- 84 + do -- 84 + local _obj_2 = _obj_1.c -- 84 + if _obj_2 ~= nil then -- 84 + result = _obj_2.d -- 84 + end -- 84 + end -- 84 + end -- 84 + end -- 84 + end -- 84 + end -- 84 + end -- 84 + return assert.same(result, "deep") -- 85 + end) -- 82 + it("should break chain on first nil", function() -- 87 + local obj = { -- 88 + a = nil -- 88 + } -- 88 + local result -- 89 + if obj ~= nil then -- 89 + do -- 89 + local _obj_0 = obj.a -- 89 + if _obj_0 ~= nil then -- 89 + do -- 89 + local _obj_1 = _obj_0.b -- 89 + if _obj_1 ~= nil then -- 89 + result = _obj_1.c -- 89 + end -- 89 + end -- 89 + end -- 89 + end -- 89 + end -- 89 + return assert.same(result, nil) -- 90 + end) -- 87 + it("should handle ?. with string methods", function() -- 92 + local s = "hello" -- 93 + local result -- 94 + if s ~= nil then -- 94 + result = s:upper() -- 94 + end -- 94 + return assert.same(result, "HELLO") -- 95 + end) -- 92 + return it("should handle ?. with nil string", function() -- 97 + local s = nil -- 98 + local result -- 99 + if s ~= nil then -- 99 + result = s:upper() -- 99 + end -- 99 + return assert.same(result, nil) -- 100 + end) -- 97 +end) -- 1 diff --git a/spec/outputs/5.1/test/export_spec.lua b/spec/outputs/5.1/test/export_spec.lua new file mode 100644 index 0000000..f40a298 --- /dev/null +++ b/spec/outputs/5.1/test/export_spec.lua @@ -0,0 +1,159 @@ +return describe("export", function() -- 1 + it("should export basic variables", function() -- 2 + local a = 1 -- 3 + local b = 2 -- 4 + local c = 3 -- 5 + assert.same(a, 1) -- 6 + assert.same(b, 2) -- 7 + return assert.same(c, 3) -- 8 + end) -- 2 + it("should export multiple variables at once", function() -- 10 + local x, y, z = 10, 20, 30 -- 11 + assert.same(x, 10) -- 12 + assert.same(y, 20) -- 13 + return assert.same(z, 30) -- 14 + end) -- 10 + it("should export class definitions", function() -- 16 + local MyClass -- 17 + do -- 17 + local _class_0 -- 17 + local _base_0 = { -- 17 + value = 100 -- 17 + } -- 17 + if _base_0.__index == nil then -- 17 + _base_0.__index = _base_0 -- 17 + end -- 17 + _class_0 = setmetatable({ -- 17 + __init = function() end, -- 17 + __base = _base_0, -- 17 + __name = "MyClass" -- 17 + }, { -- 17 + __index = _base_0, -- 17 + __call = function(cls, ...) -- 17 + local _self_0 = setmetatable({ }, _base_0) -- 17 + cls.__init(_self_0, ...) -- 17 + return _self_0 -- 17 + end -- 17 + }) -- 17 + _base_0.__class = _class_0 -- 17 + MyClass = _class_0 -- 17 + end -- 17 + return assert.same(MyClass.value, 100) -- 19 + end) -- 16 + it("should export function expressions", function() -- 21 + local my_func -- 22 + my_func = function() -- 22 + return 42 -- 22 + end -- 22 + return assert.same(my_func(), 42) -- 23 + end) -- 21 + it("should export conditional expressions", function() -- 25 + local result -- 26 + if true then -- 26 + result = "yes" -- 27 + else -- 29 + result = "no" -- 29 + end -- 26 + return assert.same(result, "yes") -- 30 + end) -- 25 + it("should export switch expressions", function() -- 32 + local value -- 33 + do -- 33 + local _exp_0 = 5 -- 33 + if 5 == _exp_0 then -- 34 + value = 100 -- 34 + else -- 35 + value = 0 -- 35 + end -- 33 + end -- 33 + return assert.same(value, 100) -- 36 + end) -- 32 + it("should export with do block", function() -- 38 + local result -- 39 + do -- 39 + local x = 5 -- 40 + result = x * 2 -- 41 + end -- 39 + return assert.same(result, 10) -- 42 + end) -- 38 + it("should export comprehension", function() -- 44 + local doubled -- 45 + do -- 45 + local _accum_0 = { } -- 45 + local _len_0 = 1 -- 45 + for i = 1, 5 do -- 45 + _accum_0[_len_0] = i * 2 -- 45 + _len_0 = _len_0 + 1 -- 45 + end -- 45 + doubled = _accum_0 -- 45 + end -- 45 + return assert.same(doubled, { -- 46 + 2, -- 46 + 4, -- 46 + 6, -- 46 + 8, -- 46 + 10 -- 46 + }) -- 46 + end) -- 44 + it("should export with pipe operator", function() -- 48 + local result = table.concat({ -- 49 + 1, -- 49 + 2, -- 49 + 3 -- 49 + }) -- 49 + return assert.same(result, "123") -- 50 + end) -- 48 + it("should export nil values", function() -- 52 + local empty = nil -- 53 + return assert.same(empty, nil) -- 54 + end) -- 52 + it("should export tables", function() -- 56 + local config = { -- 58 + key1 = "value1", -- 58 + key2 = "value2" -- 59 + } -- 57 + assert.same(config.key1, "value1") -- 61 + return assert.same(config.key2, "value2") -- 62 + end) -- 56 + it("should export string values", function() -- 64 + local message = "hello world" -- 65 + return assert.same(message, "hello world") -- 66 + end) -- 64 + it("should export boolean values", function() -- 68 + local flag_true = true -- 69 + local flag_false = false -- 70 + assert.is_true(flag_true) -- 71 + return assert.is_false(flag_false) -- 72 + end) -- 68 + it("should export number values", function() -- 74 + local count = 42 -- 75 + local price = 19.99 -- 76 + assert.same(count, 42) -- 77 + return assert.same(price, 19.99) -- 78 + end) -- 74 + it("should work in nested scope", function() -- 80 + do -- 81 + local nested = "value" -- 82 + end -- 81 + return assert.same(nested, "value") -- 83 + end) -- 80 + it("should export function with parameters", function() -- 85 + local add -- 86 + add = function(a, b) -- 86 + return a + b -- 86 + end -- 86 + return assert.same(add(5, 3), 8) -- 87 + end) -- 85 + it("should maintain export order", function() -- 89 + local first = 1 -- 90 + local second = 2 -- 91 + local third = 3 -- 92 + assert.same(first, 1) -- 93 + assert.same(second, 2) -- 94 + return assert.same(third, 3) -- 95 + end) -- 89 + return it("should work with complex expressions", function() -- 97 + local calc = (10 + 20) * 2 -- 98 + return assert.same(calc, 60) -- 99 + end) -- 97 +end) -- 1 diff --git a/spec/outputs/5.1/test/goto_spec.lua b/spec/outputs/5.1/test/goto_spec.lua new file mode 100644 index 0000000..e0921c8 --- /dev/null +++ b/spec/outputs/5.1/test/goto_spec.lua @@ -0,0 +1,103 @@ +return describe("goto", function() -- 1 + it("should support basic goto and label", function() -- 2 + local a = 0 -- 3 + ::start:: -- 4 + a = a + 1 -- 5 + if a < 5 then -- 6 + goto start -- 7 + end -- 6 + return assert.same(a, 5) -- 8 + end) -- 2 + it("should support conditional goto", function() -- 10 + local a = 0 -- 11 + ::loop:: -- 12 + a = a + 1 -- 13 + if a == 3 then -- 14 + goto done -- 14 + end -- 14 + goto loop -- 15 + ::done:: -- 16 + return assert.same(a, 3) -- 17 + end) -- 10 + it("should support goto in nested loops", function() -- 19 + local count = 0 -- 20 + for x = 1, 3 do -- 21 + for y = 1, 3 do -- 22 + count = count + 1 -- 23 + if x == 2 and y == 2 then -- 24 + goto found -- 25 + end -- 24 + end -- 22 + end -- 21 + ::found:: -- 26 + return assert.same(count, 4) -- 27 + end) -- 19 + it("should support multiple labels", function() -- 29 + local a = 0 -- 30 + ::first:: -- 31 + a = a + 1 -- 32 + if a == 2 then -- 33 + goto second -- 33 + end -- 33 + goto first -- 34 + ::second:: -- 35 + return assert.same(a, 2) -- 36 + end) -- 29 + it("should work with for loops", function() -- 38 + local sum = 0 -- 39 + for i = 1, 10 do -- 40 + sum = sum + i -- 41 + if i == 5 then -- 42 + goto done -- 42 + end -- 42 + end -- 40 + ::done:: -- 43 + return assert.same(sum, 15) -- 44 + end) -- 38 + it("should work with while loops", function() -- 46 + local count = 0 -- 47 + while true do -- 48 + count = count + 1 -- 49 + if count == 3 then -- 50 + goto endwhile -- 50 + end -- 50 + end -- 48 + ::endwhile:: -- 51 + return assert.same(count, 3) -- 52 + end) -- 46 + it("should skip rest of loop with goto", function() -- 54 + local values = { } -- 55 + for i = 1, 5 do -- 56 + if i % 2 == 0 then -- 57 + goto continue -- 57 + end -- 57 + table.insert(values, i) -- 58 + ::continue:: -- 59 + end -- 56 + return assert.same(values, { -- 60 + 1, -- 60 + 3, -- 60 + 5 -- 60 + }) -- 60 + end) -- 54 + return it("should support goto with switch", function() -- 62 + local result = "default" -- 63 + local value = 2 -- 64 + if 1 == value then -- 66 + goto case_one -- 67 + elseif 2 == value then -- 68 + goto case_two -- 69 + end -- 65 + goto default_label -- 70 + ::case_one:: -- 71 + result = "one" -- 72 + goto finish -- 73 + ::case_two:: -- 74 + result = "two" -- 75 + goto finish -- 76 + ::default_label:: -- 77 + result = "default" -- 78 + ::finish:: -- 79 + return assert.same(result, "two") -- 80 + end) -- 62 +end) -- 1 diff --git a/spec/outputs/5.1/test/import_spec.lua b/spec/outputs/5.1/test/import_spec.lua new file mode 100644 index 0000000..f77ef04 --- /dev/null +++ b/spec/outputs/5.1/test/import_spec.lua @@ -0,0 +1,196 @@ +return describe("import", function() -- 1 + it("should import from table expression", function() -- 2 + local source = { -- 3 + hello = "world", -- 3 + foo = "bar" -- 3 + } -- 3 + local hello, foo = source.hello, source.foo -- 4 + assert.same(hello, "world") -- 5 + return assert.same(foo, "bar") -- 6 + end) -- 2 + it("should import with backslash escaping", function() -- 8 + local source = { -- 9 + x = 1, -- 9 + y = 2, -- 9 + z = 3 -- 9 + } -- 9 + local x, y, z = source.x, (function() -- 10 + local _base_0 = source -- 10 + local _fn_0 = _base_0.y -- 10 + return _fn_0 and function(...) -- 10 + return _fn_0(_base_0, ...) -- 10 + end -- 10 + end)(), source.z -- 10 + assert.same(x, 1) -- 11 + assert.same(y, 2) -- 12 + return assert.same(z, 3) -- 13 + end) -- 8 + it("should import from string module", function() -- 15 + local format -- 17 + do -- 17 + local _obj_0 = require("string") -- 17 + format = _obj_0.format -- 17 + end -- 17 + return assert.is_true(type(format) == "function") -- 18 + end) -- 15 + it("should import from table with dot path", function() -- 20 + local sub -- 22 + do -- 22 + local _obj_0 = require("string") -- 22 + sub = _obj_0.sub -- 22 + end -- 22 + local result = sub("hello", 1, 2) -- 23 + return assert.same(result, "he") -- 24 + end) -- 20 + it("should import multiple values with table destructuring", function() -- 26 + local source = { -- 27 + a = 1, -- 27 + b = 2, -- 27 + c = 3 -- 27 + } -- 27 + local a, b, c = source.a, source.b, source.c -- 28 + assert.same(a, 1) -- 29 + assert.same(b, 2) -- 30 + return assert.same(c, 3) -- 31 + end) -- 26 + it("should import with multi-line format", function() -- 33 + local source = { -- 34 + x = 1, -- 34 + y = 2, -- 34 + z = 3 -- 34 + } -- 34 + local x, y, z = source.x, source.y, source.z -- 35 + assert.same(x, 1) -- 36 + assert.same(y, 2) -- 37 + return assert.same(z, 3) -- 38 + end) -- 33 + it("should import using from syntax", function() -- 40 + local source = { -- 41 + foo = "bar", -- 41 + baz = "qux" -- 41 + } -- 41 + local foo, baz = source.foo, source.baz -- 42 + assert.same(foo, "bar") -- 43 + return assert.same(baz, "qux") -- 44 + end) -- 40 + it("should handle import with computed expressions", function() -- 46 + local source = { -- 47 + first = 1, -- 47 + second = 2 -- 47 + } -- 47 + local target = source -- 48 + local first, second = target.first, target.second -- 49 + assert.same(first, 1) -- 50 + return assert.same(second, 2) -- 51 + end) -- 46 + it("should import from nested table paths", function() -- 53 + local deep = { -- 54 + outer = { -- 54 + inner = "value" -- 54 + } -- 54 + } -- 54 + local outer = deep.outer -- 55 + return assert.same(outer.inner, "value") -- 56 + end) -- 53 + it("should support importing Lua standard library functions", function() -- 58 + local print, type -- 59 + do -- 59 + local _obj_0 = require("_G") -- 59 + print, type = _obj_0.print, _obj_0.type -- 59 + end -- 59 + assert.is_true(type(print) == "function") -- 60 + return assert.is_true(type(type) == "function") -- 61 + end) -- 58 + it("should handle empty import gracefully", function() -- 63 + local source = { } -- 65 + local dummy = source.dummy -- 66 + return assert.same(dummy, nil) -- 67 + end) -- 63 + it("should work with table index expressions", function() -- 69 + local source = { -- 70 + normal = "ok" -- 70 + } -- 70 + local normal = source.normal -- 71 + return assert.same(normal, "ok") -- 72 + end) -- 69 + it("should support chaining imports from same source", function() -- 74 + local source = { -- 75 + a = 1, -- 75 + b = 2, -- 75 + c = 3 -- 75 + } -- 75 + local a, b = source.a, source.b -- 76 + local c = source.c -- 77 + assert.same(a, 1) -- 78 + assert.same(b, 2) -- 79 + return assert.same(c, 3) -- 80 + end) -- 74 + it("should handle importing from table returned by function", function() -- 82 + local get_table -- 83 + get_table = function() -- 83 + return { -- 83 + x = 100, -- 83 + y = 200 -- 83 + } -- 83 + end -- 83 + local x, y -- 84 + do -- 84 + local _obj_0 = get_table() -- 84 + x, y = _obj_0.x, _obj_0.y -- 84 + end -- 84 + assert.same(x, 100) -- 85 + return assert.same(y, 200) -- 86 + end) -- 82 + it("should support from with multi-line import", function() -- 88 + local source = { -- 89 + item1 = 1, -- 89 + item2 = 2, -- 89 + item3 = 3 -- 89 + } -- 89 + local item1, item2, item3 = source.item1, source.item2, source.item3 -- 90 + assert.same(item1, 1) -- 91 + assert.same(item2, 2) -- 92 + return assert.same(item3, 3) -- 93 + end) -- 88 + it("should work with import from string literal", function() -- 95 + local char -- 96 + do -- 96 + local _obj_0 = require("string") -- 96 + char = _obj_0.char -- 96 + end -- 96 + return assert.same(char(65), "A") -- 97 + end) -- 95 + it("should support import with table literal keys", function() -- 99 + local source = { -- 100 + normal_key = "value2" -- 100 + } -- 100 + local normal_key = source.normal_key -- 101 + return assert.same(normal_key, "value2") -- 102 + end) -- 99 + it("should handle consecutive imports", function() -- 104 + local source1 = { -- 105 + a = 1 -- 105 + } -- 105 + local source2 = { -- 106 + b = 2 -- 106 + } -- 106 + local a = source1.a -- 107 + local b = source2.b -- 108 + assert.same(a, 1) -- 109 + return assert.same(b, 2) -- 110 + end) -- 104 + return it("should support importing from complex expressions", function() -- 112 + local get_source -- 113 + get_source = function() -- 113 + return { -- 113 + result = 42 -- 113 + } -- 113 + end -- 113 + local result -- 114 + do -- 114 + local _obj_0 = get_source() -- 114 + result = _obj_0.result -- 114 + end -- 114 + return assert.same(result, 42) -- 115 + end) -- 112 +end) -- 1 diff --git a/spec/outputs/5.1/test/literals_spec.lua b/spec/outputs/5.1/test/literals_spec.lua new file mode 100644 index 0000000..3bbfb37 --- /dev/null +++ b/spec/outputs/5.1/test/literals_spec.lua @@ -0,0 +1,90 @@ +return describe("literals", function() -- 1 + it("should support integer literals", function() -- 2 + return assert.same(123, 123) -- 3 + end) -- 2 + it("should support float literals", function() -- 5 + return assert.same(1.5, 1.5) -- 6 + end) -- 5 + it("should support scientific notation", function() -- 8 + return assert.same(1.5e2, 150) -- 9 + end) -- 8 + it("should support negative numbers", function() -- 11 + return assert.same(-42, -42) -- 12 + end) -- 11 + it("should support hexadecimal literals", function() -- 14 + return assert.same(0xff, 255) -- 15 + end) -- 14 + it("should support hexadecimal with uppercase", function() -- 17 + return assert.same(0XFF, 255) -- 18 + end) -- 17 + it("should support binary literals", function() -- 20 + return assert.same(5, 5) -- 21 + end) -- 20 + it("should support binary with uppercase", function() -- 23 + return assert.same(5, 5) -- 24 + end) -- 23 + it("should support number with underscores", function() -- 26 + return assert.same(1000000, 1000000) -- 27 + end) -- 26 + it("should support hex with underscores", function() -- 29 + return assert.same(0xDEADBEEF, 0xDEADBEEF) -- 30 + end) -- 29 + it("should support double quote strings", function() -- 32 + return assert.same("hello", "hello") -- 33 + end) -- 32 + it("should support single quote strings", function() -- 35 + return assert.same('world', 'world') -- 36 + end) -- 35 + it("should support multi-line strings with [[", function() -- 38 + local s = [[ hello + world + ]] -- 39 + return assert.is_true(s:match("hello")) -- 43 + end) -- 38 + it("should support multi-line strings with [=[", function() -- 45 + local s = [==[ test + ]==] -- 46 + return assert.is_true(s:match("test")) -- 49 + end) -- 45 + it("should support boolean true", function() -- 51 + return assert.same(true, true) -- 52 + end) -- 51 + it("should support boolean false", function() -- 54 + return assert.same(false, false) -- 55 + end) -- 54 + it("should support nil", function() -- 57 + return assert.same(nil, nil) -- 58 + end) -- 57 + it("should support empty table", function() -- 60 + local t = { } -- 61 + return assert.same(#t, 0) -- 62 + end) -- 60 + it("should support table with keys", function() -- 64 + local t = { -- 65 + a = 1, -- 65 + b = 2 -- 65 + } -- 65 + assert.same(t.a, 1) -- 66 + return assert.same(t.b, 2) -- 67 + end) -- 64 + it("should support array literal", function() -- 69 + local t = { -- 70 + 1, -- 70 + 2, -- 70 + 3 -- 70 + } -- 70 + assert.same(t[1], 1) -- 71 + assert.same(t[2], 2) -- 72 + return assert.same(t[3], 3) -- 73 + end) -- 69 + return it("should support mixed table", function() -- 75 + local t = { -- 77 + 1, -- 77 + 2, -- 77 + 3, -- 77 + key = "value" -- 78 + } -- 76 + assert.same(t[1], 1) -- 80 + return assert.same(t.key, "value") -- 81 + end) -- 75 +end) -- 1 diff --git a/spec/outputs/5.1/test/macro_spec.lua b/spec/outputs/5.1/test/macro_spec.lua new file mode 100644 index 0000000..3d60580 --- /dev/null +++ b/spec/outputs/5.1/test/macro_spec.lua @@ -0,0 +1,161 @@ +return describe("macro", function() -- 1 + it("should define and call basic macro", function() -- 2 + local result = (5 * 2) -- 4 + return assert.same(result, 10) -- 5 + end) -- 2 + it("should maintain hygiene in macros", function() -- 7 + local a = 8 -- 12 + local result = 2 -- 13 + return assert.same(result, 2) -- 14 + end) -- 7 + it("should validate AST types", function() -- 16 + local result = { -- 18 + 123, -- 18 + 'xyz' -- 18 + } -- 18 + return assert.same(result, "[123, xyz]") -- 19 + end) -- 16 + it("should support simple code generation", function() -- 21 + local result = (10 + 1) -- 23 + return assert.same(result, 11) -- 24 + end) -- 21 + it("should support nested macro calls", function() -- 26 + local result = 7 -- 29 + return assert.same(result, 7) -- 30 + end) -- 26 + it("should respect macro scope in do blocks", function() -- 32 + do -- 34 + local result = inner -- 36 + assert.same(result, "inner") -- 37 + end -- 34 + local result = outer -- 38 + return assert.same(result, "outer") -- 39 + end) -- 32 + it("should provide $LINE macro", function() -- 41 + local line_num = 42 -- 42 + return assert.is_true(line_num > 0) -- 43 + end) -- 41 + it("should inject Lua code", function() -- 45 + local x = 0 -- 47 + do -- 48 +local function f(a) + return a + 1 + end + x = x + f(3) + end -- 48 + return assert.same(x, 4) -- 54 + end) -- 45 + it("should work in conditional compilation", function() -- 56 + local result = "debug mode" -- 62 + return assert.same(result, "debug mode") -- 63 + end) -- 56 + it("should work with class system", function() -- 65 + local Thing -- 66 + do -- 66 + local _class_0 -- 66 + local _base_0 = { -- 66 + value = 100, -- 68 + get_value = function(self) -- 68 + return self.value -- 68 + end -- 66 + } -- 66 + if _base_0.__index == nil then -- 66 + _base_0.__index = _base_0 -- 66 + end -- 66 + _class_0 = setmetatable({ -- 66 + __init = function() end, -- 66 + __base = _base_0, -- 66 + __name = "Thing" -- 66 + }, { -- 66 + __index = _base_0, -- 66 + __call = function(cls, ...) -- 66 + local _self_0 = setmetatable({ }, _base_0) -- 66 + cls.__init(_self_0, ...) -- 66 + return _self_0 -- 66 + end -- 66 + }) -- 66 + _base_0.__class = _class_0 -- 66 + Thing = _class_0 -- 66 + end -- 66 + local instance = Thing() -- 69 + return assert.same(instance:get_value(), 100) -- 70 + end) -- 65 + it("should handle macro in switch expressions", function() -- 72 + local result -- 74 + do -- 74 + local _exp_0 = "test" -- 74 + if "test" == _exp_0 then -- 75 + result = "matched" -- 76 + else -- 78 + result = "no match" -- 78 + end -- 74 + end -- 74 + return assert.same(result, "matched") -- 79 + end) -- 72 + it("should support macro in expression context", function() -- 81 + local result = 5 + (2 * 3) -- 83 + return assert.same(result, 11) -- 84 + end) -- 81 + it("should handle $is_ast for type checking", function() -- 86 + local result = 42 -- 91 + return assert.same(result, 42) -- 92 + end) -- 86 + it("should work with string interpolation", function() -- 94 + local result = { -- 96 + ["test"] = 123 -- 96 + } -- 96 + return assert.same(result, "test: 123") -- 97 + end) -- 94 + it("should support function call syntax", function() -- 99 + local result = (5 + 10) -- 101 + return assert.same(result, 15) -- 102 + end) -- 99 + it("should handle empty macro return", function() -- 104 + local a = 1 -- 106 + a = 2 -- 108 + return assert.same(a, 2) -- 109 + end) -- 104 + it("should work with table literals", function() -- 111 + local point = { -- 113 + x = 10, -- 113 + y = 20 -- 113 + } -- 113 + assert.same(point.x, 10) -- 114 + return assert.same(point.y, 20) -- 115 + end) -- 111 + it("should support conditional expressions in macro", function() -- 117 + local result = (5 + 1) -- 119 + return assert.same(result, 6) -- 120 + end) -- 117 + it("should work with comprehension", function() -- 122 + local result -- 124 + do -- 124 + local _accum_0 = { } -- 124 + local _len_0 = 1 -- 124 + local _list_0 = { -- 124 + 1, -- 124 + 2, -- 124 + 3 -- 124 + } -- 124 + for _index_0 = 1, #_list_0 do -- 124 + local _ = _list_0[_index_0] -- 124 + _accum_0[_len_0] = _ * 2 -- 124 + _len_0 = _len_0 + 1 -- 124 + end -- 124 + result = _accum_0 -- 124 + end -- 124 + return assert.same(result, { -- 125 + 2, -- 125 + 4, -- 125 + 6 -- 125 + }) -- 125 + end) -- 122 + it("should support complex expression macros", function() -- 127 + local result = (1 + 2 * 3) -- 129 + return assert.same(result, 7) -- 130 + end) -- 127 + return it("should work with string literals", function() -- 132 + local result = "Hello, " .. tostring(name) -- 134 + return assert.same(result, "Hello, World") -- 135 + end) -- 132 +end) -- 1 diff --git a/spec/outputs/5.1/test/metatable_spec.lua b/spec/outputs/5.1/test/metatable_spec.lua new file mode 100644 index 0000000..8088b0c --- /dev/null +++ b/spec/outputs/5.1/test/metatable_spec.lua @@ -0,0 +1,141 @@ +return describe("metatable", function() -- 1 + it("should get metatable with <> syntax", function() -- 2 + local obj = setmetatable({ -- 3 + value = 42 -- 3 + }, { -- 3 + __index = { -- 3 + extra = "data" -- 3 + } -- 3 + }) -- 3 + local mt = getmetatable(obj) -- 4 + return assert.is_true(mt ~= nil) -- 5 + end) -- 2 + it("should set metatable with <>", function() -- 7 + local obj = { } -- 8 + setmetatable(obj, { -- 9 + __index = { -- 9 + value = 100 -- 9 + } -- 9 + }) -- 9 + return assert.same(obj.value, 100) -- 10 + end) -- 7 + it("should access metatable with <>", function() -- 12 + local obj = setmetatable({ }, { -- 13 + __index = { -- 13 + value = 50 -- 13 + } -- 13 + }) -- 13 + local result = getmetatable(obj).__index.value -- 14 + return assert.same(result, 50) -- 15 + end) -- 12 + it("should work with metamethod", function() -- 17 + local obj = setmetatable({ }, { -- 19 + __index = function(self, key) -- 19 + if key == "computed" then -- 20 + return "computed_value" -- 21 + end -- 20 + end -- 19 + }) -- 18 + return assert.same(obj.computed, "computed_value") -- 23 + end) -- 17 + it("should work with metamethod", function() -- 25 + local obj = setmetatable({ }, { -- 27 + __newindex = function(self, key, value) -- 27 + return rawset(self, "stored_" .. key, value) -- 28 + end -- 27 + }) -- 26 + obj.test = 123 -- 30 + return assert.same(obj.stored_test, 123) -- 31 + end) -- 25 + it("should work with metamethod", function() -- 33 + local obj = setmetatable({ -- 34 + value = 10 -- 34 + }, { -- 35 + __add = function(a, b) -- 35 + return a.value + b.value -- 35 + end -- 35 + }) -- 34 + local obj2 = setmetatable({ -- 37 + value = 20 -- 37 + }, { -- 38 + __add = function(a, b) -- 38 + return a.value + b.value -- 38 + end -- 38 + }) -- 37 + local result = obj + obj2 -- 40 + return assert.same(result, 30) -- 41 + end) -- 33 + it("should work with metamethod", function() -- 43 + local obj = setmetatable({ }, { -- 45 + __call = function(self, x) -- 45 + return x * 2 -- 45 + end -- 45 + }) -- 44 + local result = obj(5) -- 47 + return assert.same(result, 10) -- 48 + end) -- 43 + it("should work with metamethod", function() -- 50 + local obj = setmetatable({ -- 51 + value = 42 -- 51 + }, { -- 52 + __tostring = function(self) -- 52 + return "Value: " .. tostring(self.value) -- 52 + end -- 52 + }) -- 51 + local result = tostring(obj) -- 54 + return assert.same(result, "Value: 42") -- 55 + end) -- 50 + it("should work with metamethod", function() -- 57 + local obj1 = setmetatable({ -- 58 + id = 1 -- 58 + }, { -- 59 + __eq = function(a, b) -- 59 + return a.id == b.id -- 59 + end -- 59 + }) -- 58 + local obj2 = setmetatable({ -- 61 + id = 1 -- 61 + }, { -- 62 + __eq = function(a, b) -- 62 + return a.id == b.id -- 62 + end -- 62 + }) -- 61 + return assert.is_true(obj1 == obj2) -- 64 + end) -- 57 + it("should destructure metatable", function() -- 66 + local obj = setmetatable({ }, { -- 68 + new = function() -- 68 + return "new result" -- 68 + end, -- 68 + update = function() -- 69 + return "update result" -- 69 + end -- 69 + }) -- 67 + local new, update -- 71 + do -- 71 + local _obj_0 = getmetatable(obj) -- 71 + new, update = _obj_0.new, _obj_0.update -- 71 + end -- 71 + assert.is_true(type(new) == "function") -- 72 + return assert.is_true(type(update) == "function") -- 73 + end) -- 66 + it("should check if two objects have same metatable", function() -- 75 + local mt = { -- 76 + value = 100 -- 76 + } -- 76 + local obj1 = setmetatable({ }, mt) -- 77 + local obj2 = setmetatable({ }, mt) -- 78 + return assert.is_true(getmetatable(obj1) == getmetatable(obj2)) -- 79 + end) -- 75 + return it("should work with metamethod", function() -- 81 + local obj = setmetatable({ -- 82 + value = "hello" -- 82 + }, { -- 83 + __concat = function(a, b) -- 83 + return a.value .. b -- 83 + end -- 83 + }) -- 82 + local result = obj .. " world" -- 85 + return assert.same(result, "hello world") -- 86 + end) -- 81 +end) -- 1 diff --git a/spec/outputs/5.1/test/operators_spec.lua b/spec/outputs/5.1/test/operators_spec.lua new file mode 100644 index 0000000..661c422 --- /dev/null +++ b/spec/outputs/5.1/test/operators_spec.lua @@ -0,0 +1,142 @@ +return describe("operators", function() -- 1 + it("should support addition", function() -- 2 + return assert.same(1 + 2, 3) -- 3 + end) -- 2 + it("should support subtraction", function() -- 5 + return assert.same(5 - 3, 2) -- 6 + end) -- 5 + it("should support multiplication", function() -- 8 + return assert.same(4 * 3, 12) -- 9 + end) -- 8 + it("should support division", function() -- 11 + return assert.same(10 / 2, 5) -- 12 + end) -- 11 + it("should support modulo", function() -- 14 + return assert.same(10 % 3, 1) -- 15 + end) -- 14 + it("should support exponentiation", function() -- 17 + return assert.same(2 ^ 3, 8) -- 18 + end) -- 17 + it("should support unary minus", function() -- 20 + return assert.same(-5, -5) -- 21 + end) -- 20 + it("should support equality comparison", function() -- 23 + assert.is_true(1 == 1) -- 24 + return assert.is_false(1 == 2) -- 25 + end) -- 23 + it("should support inequality comparison", function() -- 27 + assert.is_true(1 ~= 2) -- 28 + return assert.is_false(1 ~= 1) -- 29 + end) -- 27 + it("should support less than", function() -- 31 + assert.is_true(1 < 2) -- 32 + return assert.is_false(2 < 1) -- 33 + end) -- 31 + it("should support greater than", function() -- 35 + assert.is_true(2 > 1) -- 36 + return assert.is_false(1 > 2) -- 37 + end) -- 35 + it("should support less than or equal", function() -- 39 + assert.is_true(1 <= 2) -- 40 + assert.is_true(2 <= 2) -- 41 + return assert.is_false(3 <= 2) -- 42 + end) -- 39 + it("should support greater than or equal", function() -- 44 + assert.is_true(2 >= 1) -- 45 + assert.is_true(2 >= 2) -- 46 + return assert.is_false(1 >= 2) -- 47 + end) -- 44 + it("should support logical and", function() -- 49 + assert.same(true and false, false) -- 50 + assert.same(true and true, true) -- 51 + return assert.same(false and true, false) -- 52 + end) -- 49 + it("should support logical or", function() -- 54 + assert.same(true or false, true) -- 55 + assert.same(false or true, true) -- 56 + return assert.same(false or false, false) -- 57 + end) -- 54 + it("should support logical not", function() -- 59 + assert.same(not true, false) -- 60 + assert.same(not false, true) -- 61 + return assert.same(not nil, true) -- 62 + end) -- 59 + it("should support bitwise and", function() -- 64 + return assert.same(5 & 3, 1) -- 65 + end) -- 64 + it("should support bitwise or", function() -- 67 + return assert.same(5 | 3, 7) -- 68 + end) -- 67 + it("should support bitwise xor", function() -- 70 + return assert.same(5 ~ 3, 6) -- 71 + end) -- 70 + it("should support left shift", function() -- 73 + return assert.same(2 << 3, 16) -- 74 + end) -- 73 + it("should support right shift", function() -- 76 + return assert.same(16 >> 2, 4) -- 77 + end) -- 76 + it("should support string concatenation", function() -- 79 + return assert.same("hello" .. " world", "hello world") -- 80 + end) -- 79 + it("should support length operator", function() -- 82 + assert.same(#"hello", 5) -- 83 + return assert.same(#{ -- 84 + 1, -- 84 + 2, -- 84 + 3 -- 84 + }, 3) -- 84 + end) -- 82 + it("should respect operator precedence", function() -- 86 + assert.same(1 + 2 * 3, 7) -- 87 + return assert.same((1 + 2) * 3, 9) -- 88 + end) -- 86 + it("should support compound assignment", function() -- 90 + local x = 10 -- 91 + x = x + 5 -- 92 + return assert.same(x, 15) -- 93 + end) -- 90 + it("should support compound subtraction", function() -- 95 + local x = 10 -- 96 + x = x - 3 -- 97 + return assert.same(x, 7) -- 98 + end) -- 95 + it("should support compound multiplication", function() -- 100 + local x = 5 -- 101 + x = x * 2 -- 102 + return assert.same(x, 10) -- 103 + end) -- 100 + it("should support compound division", function() -- 105 + local x = 20 -- 106 + x = x / 4 -- 107 + return assert.same(x, 5) -- 108 + end) -- 105 + it("should handle division by zero", function() -- 110 + local result = pcall(function() -- 112 + local x = 10 / 0 -- 113 + end) -- 112 + return assert.is_true(result) -- 115 + end) -- 110 + it("should handle very large numbers", function() -- 117 + local big = 1e100 -- 118 + return assert.is_true(big > 0) -- 119 + end) -- 117 + it("should handle very small numbers", function() -- 121 + local small = 1e-100 -- 122 + return assert.is_true(small > 0) -- 123 + end) -- 121 + it("should support negation", function() -- 125 + assert.same(-10, -10) -- 126 + return assert.same -- 127 + end) -- 125 + it("should work with complex expressions", function() -- 129 + local result = (1 + 2) * (3 + 4) / 2 -- 130 + return assert.same(result, 10.5) -- 131 + end) -- 129 + it("should support power with decimal", function() -- 133 + return assert.same(4 ^ 0.5, 2) -- 134 + end) -- 133 + return it("should handle modulo with negative numbers", function() -- 136 + return assert.same(-10 % 3, 2) -- 137 + end) -- 136 +end) -- 1 diff --git a/spec/outputs/5.1/test/return_spec.lua b/spec/outputs/5.1/test/return_spec.lua new file mode 100644 index 0000000..4e6580f --- /dev/null +++ b/spec/outputs/5.1/test/return_spec.lua @@ -0,0 +1,145 @@ +return describe("return", function() -- 1 + it("should return from comprehension", function() -- 2 + local fn -- 3 + fn = function() -- 3 + local _accum_0 = { } -- 4 + local _len_0 = 1 -- 4 + for x = 1, 5 do -- 4 + _accum_0[_len_0] = x * 2 -- 4 + _len_0 = _len_0 + 1 -- 4 + end -- 4 + return _accum_0 -- 4 + end -- 3 + local result = fn() -- 5 + return assert.same(result, { -- 6 + 2, -- 6 + 4, -- 6 + 6, -- 6 + 8, -- 6 + 10 -- 6 + }) -- 6 + end) -- 2 + it("should return from table comprehension", function() -- 8 + local fn -- 9 + fn = function() -- 9 + local _tbl_0 = { } -- 10 + for k, v in pairs({ -- 10 + a = 1, -- 10 + b = 2 -- 10 + }) do -- 10 + _tbl_0[k] = v -- 10 + end -- 10 + return _tbl_0 -- 10 + end -- 9 + local result = fn() -- 11 + return assert.same(type(result), "table") -- 12 + end) -- 8 + it("should return from nested if", function() -- 14 + local fn -- 15 + fn = function(a, b) -- 15 + if a then -- 16 + if b then -- 17 + return "both" -- 18 + else -- 20 + return "only a" -- 20 + end -- 17 + else -- 22 + return "neither" -- 22 + end -- 16 + end -- 15 + assert.same(fn(true, true), "both") -- 23 + assert.same(fn(true, false), "only a") -- 24 + return assert.same(fn(false, false), "neither") -- 25 + end) -- 14 + it("should return from switch", function() -- 27 + local fn -- 28 + fn = function(value) -- 28 + if 1 == value then -- 30 + return "one" -- 30 + elseif 2 == value then -- 31 + return "two" -- 31 + else -- 32 + return "other" -- 32 + end -- 29 + end -- 28 + assert.same(fn(1), "one") -- 33 + assert.same(fn(2), "two") -- 34 + return assert.same(fn(3), "other") -- 35 + end) -- 27 + it("should return table literal", function() -- 37 + local fn -- 38 + fn = function() -- 38 + return { -- 40 + value = 42, -- 40 + name = "test" -- 41 + } -- 39 + end -- 38 + local result = fn() -- 42 + assert.same(result.value, 42) -- 43 + return assert.same(result.name, "test") -- 44 + end) -- 37 + it("should return array literal", function() -- 46 + local fn -- 47 + fn = function() -- 47 + return { -- 49 + 1, -- 49 + 2, -- 50 + 3 -- 51 + } -- 48 + end -- 47 + local result = fn() -- 52 + return assert.same(result, { -- 53 + 1, -- 53 + 2, -- 53 + 3 -- 53 + }) -- 53 + end) -- 46 + it("should return from with statement", function() -- 55 + local fn -- 56 + fn = function(obj) -- 56 + local result = obj.value -- 57 + return result -- 58 + end -- 56 + return assert.same(fn({ -- 59 + value = 100 -- 59 + }), 100) -- 59 + end) -- 55 + it("should return nil implicitly", function() -- 61 + fn(function() -- 62 + return print("no return") -- 62 + end) -- 62 + return assert.same(fn(), nil) -- 63 + end) -- 61 + it("should return multiple values", function() -- 65 + fn(function() -- 66 + return 1, 2, 3 -- 66 + end) -- 66 + local a, b, c = fn() -- 67 + assert.same(a, 1) -- 68 + assert.same(b, 2) -- 69 + return assert.same(c, 3) -- 70 + end) -- 65 + it("should return from function call", function() -- 72 + local fn -- 73 + fn = function() -- 73 + local inner -- 74 + inner = function() -- 74 + return 42 -- 74 + end -- 74 + return inner() -- 75 + end -- 73 + return assert.same(fn(), 42) -- 76 + end) -- 72 + return it("should handle return in expression context", function() -- 78 + local fn -- 79 + fn = function(cond) -- 79 + if cond then -- 80 + return "yes" -- 81 + else -- 83 + return "no" -- 83 + end -- 80 + end -- 79 + assert.same(fn(true), "yes") -- 84 + return assert.same(fn(false), "no") -- 85 + end) -- 78 +end) -- 1 diff --git a/spec/outputs/5.1/test/string_spec.lua b/spec/outputs/5.1/test/string_spec.lua new file mode 100644 index 0000000..95eb470 --- /dev/null +++ b/spec/outputs/5.1/test/string_spec.lua @@ -0,0 +1,138 @@ +return describe("string", function() -- 1 + it("should support single quote strings", function() -- 2 + local s = 'hello' -- 3 + return assert.same(s, "hello") -- 4 + end) -- 2 + it("should support double quote strings", function() -- 6 + local s = "world" -- 7 + return assert.same(s, "world") -- 8 + end) -- 6 + it("should support escape sequences", function() -- 10 + local s = "hello\nworld" -- 11 + return assert.is_true(s:match("\n") ~= nil) -- 12 + end) -- 10 + it("should support escaped quotes", function() -- 14 + local s = "he said \"hello\"" -- 15 + return assert.same(s, 'he said "hello"') -- 16 + end) -- 14 + it("should support backslash escape", function() -- 18 + local s = "\\" -- 19 + return assert.same(s, "\\") -- 20 + end) -- 18 + it("should support multi-line strings with [[ ]]", function() -- 22 + local s = [[ hello + world + ]] -- 23 + assert.is_true(s:match("hello") ~= nil) -- 27 + return assert.is_true(s:match("world") ~= nil) -- 28 + end) -- 22 + it("should support multi-line strings with [=[ ]=]", function() -- 30 + local s = [==[ hello + world + ]==] -- 31 + assert.is_true(s:match("hello") ~= nil) -- 35 + return assert.is_true(s:match("world") ~= nil) -- 36 + end) -- 30 + it("should support string interpolation with double quotes", function() -- 38 + local name = "world" -- 39 + local s = "hello " .. tostring(name) -- 40 + return assert.same(s, "hello world") -- 41 + end) -- 38 + it("should support expression interpolation", function() -- 43 + local a, b = 1, 2 -- 44 + local s = tostring(a) .. " + " .. tostring(b) .. " = " .. tostring(a + b) -- 45 + return assert.same(s, "1 + 2 = 3") -- 46 + end) -- 43 + it("should not interpolate in single quotes", function() -- 48 + local name = "world" -- 49 + local s = 'hello #{name}' -- 50 + return assert.same(s, "hello " .. tostring(name)) -- 51 + end) -- 48 + it("should escape interpolation with \#", function() -- 53 + local name = "world" -- 54 + local s = "hello \\" .. tostring(name) -- 55 + return assert.same(s, "hello " .. tostring(name)) -- 56 + end) -- 53 + it("should support method calls on string literals", function() -- 58 + local result = ("hello"):upper() -- 59 + return assert.same(result, "HELLO") -- 60 + end) -- 58 + it("should support chained method calls", function() -- 62 + local result = ("hello world"):upper():match("HELLO") -- 63 + return assert.same(result, "HELLO") -- 64 + end) -- 62 + it("should support YAML style strings", function() -- 66 + local s = "hello\nworld" -- 67 + assert.is_true(s:match("hello") ~= nil) -- 70 + return assert.is_true(s:match("world") ~= nil) -- 71 + end) -- 66 + it("should support YAML style with interpolation", function() -- 73 + local name = "test" -- 74 + local s = "hello " .. tostring(name) -- 75 + return assert.same(s, "hello test\n") -- 77 + end) -- 73 + it("should support string concatenation", function() -- 79 + local s = "hello" .. " " .. "world" -- 80 + return assert.same(s, "hello world") -- 81 + end) -- 79 + it("should handle empty strings", function() -- 83 + local s = "" -- 84 + return assert.same(s, "") -- 85 + end) -- 83 + it("should support Unicode characters", function() -- 87 + local s = "hello 世界" -- 88 + return assert.is_true(s:match("世界") ~= nil) -- 89 + end) -- 87 + it("should support string length", function() -- 91 + local s = "hello" -- 92 + return assert.same(#s, 5) -- 93 + end) -- 91 + it("should support multi-line YAML with complex content", function() -- 95 + local config = "key1: value1\nkey2: value2\nkey3: value3" -- 96 + return assert.is_true(config:match("key1") ~= nil) -- 100 + end) -- 95 + it("should support interpolation in YAML strings", function() -- 102 + local x, y = 10, 20 -- 103 + local s = "point:\n\tx: " .. tostring(x) .. "\n\ty: " .. tostring(y) -- 104 + assert.is_true(s:match("x: 10") ~= nil) -- 108 + return assert.is_true(s:match("y: 20") ~= nil) -- 109 + end) -- 102 + it("should support function call in interpolation", function() -- 111 + local s = "result: " .. tostring(function() -- 112 + return 42 -- 112 + end) -- 112 + return assert.same(s, "result: 42") -- 113 + end) -- 111 + it("should support table indexing in interpolation", function() -- 115 + local t = { -- 116 + value = 100 -- 116 + } -- 116 + local s = "value: " .. tostring(t.value) -- 117 + return assert.same(s, "value: 100") -- 118 + end) -- 115 + it("should handle escaped characters correctly", function() -- 120 + local s = "tab:\t, newline:\n, return:\r" -- 121 + assert.is_true(s:match("\t") ~= nil) -- 122 + return assert.is_true(s:match("\n") ~= nil) -- 123 + end) -- 120 + it("should support string methods with colon syntax", function() -- 125 + local s = "hello" -- 126 + return assert.same(s:sub(1, 2), "he") -- 127 + end) -- 125 + it("should work in expressions", function() -- 129 + local result = "hello" .. " world" -- 130 + return assert.same(result, "hello world") -- 131 + end) -- 129 + it("should support octal escape", function() -- 133 + local s = "\65" -- 134 + return assert.same(s, "A") -- 135 + end) -- 133 + it("should support hex escape", function() -- 137 + local s = "\x41" -- 138 + return assert.same(s, "A") -- 139 + end) -- 137 + return it("should support unicode escape", function() -- 141 + local s = "\u{4e16}" -- 142 + return assert.same(s, "世") -- 143 + end) -- 141 +end) -- 1 diff --git a/spec/outputs/5.1/test/switch_spec.lua b/spec/outputs/5.1/test/switch_spec.lua new file mode 100644 index 0000000..5b13970 --- /dev/null +++ b/spec/outputs/5.1/test/switch_spec.lua @@ -0,0 +1,743 @@ +return describe("switch", function() -- 1 + it("should match single value", function() -- 2 + local value = "cool" -- 3 + local result -- 4 + if "cool" == value then -- 5 + result = "matched" -- 6 + else -- 8 + result = "not matched" -- 8 + end -- 4 + return assert.same(result, "matched") -- 9 + end) -- 2 + it("should match multiple values with or", function() -- 11 + local hi = "world" -- 12 + local matched = false -- 13 + if "one" == hi or "two" == hi then -- 15 + matched = true -- 16 + end -- 14 + assert.is_false(matched) -- 17 + hi = "one" -- 19 + if "one" == hi or "two" == hi then -- 21 + matched = true -- 22 + end -- 20 + return assert.is_true(matched) -- 23 + end) -- 11 + it("should execute else branch when no match", function() -- 25 + local value = "other" -- 26 + local result -- 27 + if "cool" == value then -- 28 + result = "matched cool" -- 29 + elseif "yeah" == value then -- 30 + result = "matched yeah" -- 31 + else -- 33 + result = "else branch" -- 33 + end -- 27 + return assert.same(result, "else branch") -- 34 + end) -- 25 + it("should destructure table with single key", function() -- 36 + local tb = { -- 37 + x = 100 -- 37 + } -- 37 + local result -- 38 + do -- 39 + local _type_0 = type(tb) -- 39 + local _tab_0 = "table" == _type_0 or "userdata" == _type_0 -- 39 + local _match_0 = false -- 39 + if _tab_0 then -- 39 + local x = tb.x -- 39 + if x ~= nil then -- 39 + _match_0 = true -- 39 + result = x -- 40 + end -- 39 + end -- 39 + if not _match_0 then -- 39 + result = "no match" -- 42 + end -- 38 + end -- 38 + return assert.same(result, 100) -- 43 + end) -- 36 + it("should destructure table with multiple keys", function() -- 45 + local tb = { -- 46 + x = 100, -- 46 + y = 200 -- 46 + } -- 46 + local result -- 47 + do -- 48 + local _type_0 = type(tb) -- 48 + local _tab_0 = "table" == _type_0 or "userdata" == _type_0 -- 48 + local _match_0 = false -- 48 + if _tab_0 then -- 48 + local x = tb.x -- 48 + local y = tb.y -- 48 + if x ~= nil and y ~= nil then -- 48 + _match_0 = true -- 48 + result = x + y -- 49 + end -- 48 + end -- 48 + if not _match_0 then -- 48 + result = "no match" -- 51 + end -- 47 + end -- 47 + return assert.same(result, 300) -- 52 + end) -- 45 + it("should destructure table with default values", function() -- 54 + local tb = { -- 55 + a = 1 -- 55 + } -- 55 + local _type_0 = type(tb) -- 57 + local _tab_0 = "table" == _type_0 or "userdata" == _type_0 -- 57 + if _tab_0 then -- 57 + local a = tb.a -- 57 + local b = tb.b -- 57 + if a == nil then -- 57 + a = 1 -- 57 + end -- 57 + if b == nil then -- 57 + b = 2 -- 57 + end -- 57 + assert.same(a, 1) -- 58 + return assert.same(b, 2) -- 59 + end -- 56 + end) -- 54 + it("should destructure nested tables", function() -- 61 + local dict = { -- 63 + { }, -- 63 + { -- 64 + 1, -- 64 + 2, -- 64 + 3 -- 64 + }, -- 64 + a = { -- 65 + b = { -- 65 + c = 1 -- 65 + } -- 65 + }, -- 65 + x = { -- 66 + y = { -- 66 + z = 1 -- 66 + } -- 66 + } -- 66 + } -- 62 + local matched = false -- 68 + do -- 70 + local _type_0 = type(dict) -- 70 + local _tab_0 = "table" == _type_0 or "userdata" == _type_0 -- 70 + if _tab_0 then -- 70 + local first = dict[1] -- 70 + local one -- 70 + do -- 70 + local _obj_0 = dict[2] -- 70 + local _type_1 = type(_obj_0) -- 70 + if "table" == _type_1 or "userdata" == _type_1 then -- 70 + one = _obj_0[1] -- 70 + end -- 70 + end -- 70 + local two -- 70 + do -- 70 + local _obj_0 = dict[2] -- 70 + local _type_1 = type(_obj_0) -- 70 + if "table" == _type_1 or "userdata" == _type_1 then -- 70 + two = _obj_0[2] -- 70 + end -- 70 + end -- 70 + local three -- 70 + do -- 70 + local _obj_0 = dict[2] -- 70 + local _type_1 = type(_obj_0) -- 70 + if "table" == _type_1 or "userdata" == _type_1 then -- 70 + three = _obj_0[3] -- 70 + end -- 70 + end -- 70 + local c -- 70 + do -- 70 + local _obj_0 = dict.a -- 70 + local _type_1 = type(_obj_0) -- 70 + if "table" == _type_1 or "userdata" == _type_1 then -- 70 + do -- 70 + local _obj_1 = _obj_0.b -- 70 + local _type_2 = type(_obj_1) -- 70 + if "table" == _type_2 or "userdata" == _type_2 then -- 70 + c = _obj_1.c -- 70 + end -- 70 + end -- 70 + end -- 70 + end -- 70 + local z -- 70 + do -- 70 + local _obj_0 = dict.x -- 70 + local _type_1 = type(_obj_0) -- 70 + if "table" == _type_1 or "userdata" == _type_1 then -- 70 + do -- 70 + local _obj_1 = _obj_0.y -- 70 + local _type_2 = type(_obj_1) -- 70 + if "table" == _type_2 or "userdata" == _type_2 then -- 70 + z = _obj_1.z -- 70 + end -- 70 + end -- 70 + end -- 70 + end -- 70 + if first ~= nil and one ~= nil and two ~= nil and three ~= nil and c ~= nil and z ~= nil then -- 70 + matched = first == { } and one == 1 and two == 2 and three == 3 and c == 1 and z == 1 -- 76 + end -- 70 + end -- 69 + end -- 69 + return assert.is_true(matched) -- 77 + end) -- 61 + it("should destructure arrays with exact match", function() -- 79 + local tb = { -- 80 + 1, -- 80 + 2, -- 80 + 3 -- 80 + } -- 80 + local result -- 81 + do -- 82 + local _type_0 = type(tb) -- 82 + local _tab_0 = "table" == _type_0 or "userdata" == _type_0 -- 82 + local _match_0 = false -- 82 + if _tab_0 then -- 82 + if 1 == tb[1] and 2 == tb[2] and 3 == tb[3] then -- 82 + _match_0 = true -- 82 + result = "exact match" -- 83 + end -- 82 + end -- 82 + if not _match_0 then -- 82 + result = "no match" -- 85 + end -- 81 + end -- 81 + return assert.same(result, "exact match") -- 86 + end) -- 79 + it("should destructure arrays with variables", function() -- 88 + local tb = { -- 89 + 1, -- 89 + "b", -- 89 + 3 -- 89 + } -- 89 + local result -- 90 + do -- 91 + local _type_0 = type(tb) -- 91 + local _tab_0 = "table" == _type_0 or "userdata" == _type_0 -- 91 + local _match_0 = false -- 91 + if _tab_0 then -- 91 + local b = tb[2] -- 91 + if 1 == tb[1] and b ~= nil and 3 == tb[3] then -- 91 + _match_0 = true -- 91 + result = b -- 92 + end -- 91 + end -- 91 + if not _match_0 then -- 91 + result = "no match" -- 94 + end -- 90 + end -- 90 + return assert.same(result, "b") -- 95 + end) -- 88 + it("should destructure arrays with defaults", function() -- 97 + local tb = { -- 98 + 1, -- 98 + 2 -- 98 + } -- 98 + local result -- 99 + do -- 100 + local _type_0 = type(tb) -- 100 + local _tab_0 = "table" == _type_0 or "userdata" == _type_0 -- 100 + local _match_0 = false -- 100 + if _tab_0 then -- 100 + local b = tb[3] -- 100 + if b == nil then -- 100 + b = 3 -- 100 + end -- 100 + if 1 == tb[1] and 2 == tb[2] then -- 100 + _match_0 = true -- 100 + result = b -- 101 + end -- 100 + end -- 100 + if not _match_0 then -- 100 + result = "no match" -- 103 + end -- 99 + end -- 99 + return assert.same(result, 3) -- 104 + end) -- 97 + it("should match pattern with __class", function() -- 106 + local ClassA -- 107 + do -- 107 + local _class_0 -- 107 + local _base_0 = { } -- 107 + if _base_0.__index == nil then -- 107 + _base_0.__index = _base_0 -- 107 + end -- 107 + _class_0 = setmetatable({ -- 107 + __init = function() end, -- 107 + __base = _base_0, -- 107 + __name = "ClassA" -- 107 + }, { -- 107 + __index = _base_0, -- 107 + __call = function(cls, ...) -- 107 + local _self_0 = setmetatable({ }, _base_0) -- 107 + cls.__init(_self_0, ...) -- 107 + return _self_0 -- 107 + end -- 107 + }) -- 107 + _base_0.__class = _class_0 -- 107 + ClassA = _class_0 -- 107 + end -- 107 + local ClassB -- 108 + do -- 108 + local _class_0 -- 108 + local _base_0 = { } -- 108 + if _base_0.__index == nil then -- 108 + _base_0.__index = _base_0 -- 108 + end -- 108 + _class_0 = setmetatable({ -- 108 + __init = function() end, -- 108 + __base = _base_0, -- 108 + __name = "ClassB" -- 108 + }, { -- 108 + __index = _base_0, -- 108 + __call = function(cls, ...) -- 108 + local _self_0 = setmetatable({ }, _base_0) -- 108 + cls.__init(_self_0, ...) -- 108 + return _self_0 -- 108 + end -- 108 + }) -- 108 + _base_0.__class = _class_0 -- 108 + ClassB = _class_0 -- 108 + end -- 108 + local item = ClassA() -- 109 + local result -- 110 + do -- 111 + local _type_0 = type(item) -- 111 + local _tab_0 = "table" == _type_0 or "userdata" == _type_0 -- 111 + local _match_0 = false -- 111 + if _tab_0 then -- 111 + ClassA = item.__class -- 111 + if ClassA ~= nil then -- 111 + _match_0 = true -- 111 + result = "Object A" -- 112 + end -- 111 + end -- 111 + if not _match_0 then -- 111 + local _match_1 = false -- 113 + if _tab_0 then -- 113 + ClassB = item.__class -- 113 + if ClassB ~= nil then -- 113 + _match_1 = true -- 113 + result = "Object B" -- 114 + end -- 113 + end -- 113 + if not _match_1 then -- 113 + result = "unknown" -- 116 + end -- 110 + end -- 110 + end -- 110 + return assert.same(result, "Object A") -- 117 + end) -- 106 + it("should match pattern with metatable", function() -- 119 + local tb = setmetatable({ }, { -- 120 + __mode = "v" -- 120 + }) -- 120 + local metatable_matched = false -- 121 + do -- 123 + local _type_0 = type(tb) -- 123 + local _tab_0 = "table" == _type_0 or "userdata" == _type_0 -- 123 + if _tab_0 then -- 123 + local mt = getmetatable(tb) -- 123 + if mt ~= nil then -- 123 + metatable_matched = mt ~= nil -- 124 + end -- 123 + end -- 122 + end -- 122 + return assert.is_true(metatable_matched) -- 125 + end) -- 119 + it("should use switch as expression in assignment", function() -- 127 + local tb = { -- 128 + x = "abc" -- 128 + } -- 128 + local matched -- 129 + if 1 == tb then -- 130 + matched = "1" -- 131 + else -- 132 + do -- 132 + local _type_0 = type(tb) -- 132 + local _tab_0 = "table" == _type_0 or "userdata" == _type_0 -- 132 + local _match_0 = false -- 132 + if _tab_0 then -- 132 + local x = tb.x -- 132 + if x ~= nil then -- 132 + _match_0 = true -- 132 + matched = x -- 133 + end -- 132 + end -- 132 + if not _match_0 then -- 132 + if false == tb then -- 134 + matched = "false" -- 135 + else -- 137 + matched = nil -- 137 + end -- 129 + end -- 129 + end -- 129 + end -- 129 + return assert.same(matched, "abc") -- 138 + end) -- 127 + it("should use switch in return statement", function() -- 140 + local fn -- 141 + fn = function(tb) -- 141 + if nil == tb then -- 143 + return "invalid" -- 144 + else -- 145 + local _type_0 = type(tb) -- 145 + local _tab_0 = "table" == _type_0 or "userdata" == _type_0 -- 145 + local _match_0 = false -- 145 + if _tab_0 then -- 145 + local a = tb.a -- 145 + local b = tb.b -- 145 + if a ~= nil and b ~= nil then -- 145 + _match_0 = true -- 145 + return tostring(a + b) -- 146 + end -- 145 + end -- 145 + if not _match_0 then -- 145 + if 1 == tb or 2 == tb or 3 == tb or 4 == tb or 5 == tb then -- 147 + return "number 1 - 5" -- 148 + else -- 150 + return "should not reach here" -- 150 + end -- 142 + end -- 142 + end -- 142 + end -- 141 + assert.same(fn({ -- 151 + a = 1, -- 151 + b = 2 -- 151 + }), "3") -- 151 + assert.same(fn(3), "number 1 - 5") -- 152 + return assert.same(fn(nil), "invalid") -- 153 + end) -- 140 + it("should support pattern matching assignment with :=", function() -- 155 + local v = "hello" -- 156 + local matched = false -- 157 + do -- 158 + v = "hello" -- 158 + if "hello" == v then -- 159 + matched = true -- 160 + else -- 162 + matched = false -- 162 + end -- 158 + end -- 158 + assert.is_true(matched) -- 163 + return assert.same(v, "hello") -- 164 + end) -- 155 + it("should match with computed expressions", function() -- 166 + local hi = 4 -- 167 + local matched = false -- 168 + if (3 + 1) == hi or (function() -- 170 + return 4 -- 170 + end)() == hi or (5 - 1) == hi then -- 170 + matched = true -- 171 + end -- 169 + return assert.is_true(matched) -- 172 + end) -- 166 + it("should handle nested array destructuring", function() -- 174 + local tb = { -- 176 + { -- 176 + a = 1, -- 176 + b = 2 -- 176 + }, -- 176 + { -- 177 + a = 3, -- 177 + b = 4 -- 177 + }, -- 177 + { -- 178 + a = 5, -- 178 + b = 6 -- 178 + }, -- 178 + "fourth" -- 179 + } -- 175 + local result -- 181 + do -- 182 + local _type_0 = type(tb) -- 182 + local _tab_0 = "table" == _type_0 or "userdata" == _type_0 -- 182 + local _match_0 = false -- 182 + if _tab_0 then -- 182 + local fourth = tb[4] -- 182 + local _val_0 -- 182 + do -- 182 + local _obj_0 = tb[1] -- 182 + if _obj_0 ~= nil then -- 182 + _val_0 = _obj_0.a -- 182 + end -- 182 + end -- 182 + local _val_1 -- 182 + do -- 182 + local _obj_0 = tb[1] -- 182 + if _obj_0 ~= nil then -- 182 + _val_1 = _obj_0.b -- 182 + end -- 182 + end -- 182 + local _val_2 -- 182 + do -- 182 + local _obj_0 = tb[2] -- 182 + if _obj_0 ~= nil then -- 182 + _val_2 = _obj_0.a -- 182 + end -- 182 + end -- 182 + local _val_3 -- 182 + do -- 182 + local _obj_0 = tb[2] -- 182 + if _obj_0 ~= nil then -- 182 + _val_3 = _obj_0.b -- 182 + end -- 182 + end -- 182 + local _val_4 -- 182 + do -- 182 + local _obj_0 = tb[3] -- 182 + if _obj_0 ~= nil then -- 182 + _val_4 = _obj_0.a -- 182 + end -- 182 + end -- 182 + local _val_5 -- 182 + do -- 182 + local _obj_0 = tb[3] -- 182 + if _obj_0 ~= nil then -- 182 + _val_5 = _obj_0.b -- 182 + end -- 182 + end -- 182 + if 1 == _val_0 and 2 == _val_1 and 3 == _val_2 and 4 == _val_3 and 5 == _val_4 and 6 == _val_5 and fourth ~= nil then -- 182 + _match_0 = true -- 182 + result = fourth -- 188 + end -- 182 + end -- 182 + if not _match_0 then -- 182 + result = "no match" -- 190 + end -- 181 + end -- 181 + return assert.same(result, "fourth") -- 191 + end) -- 174 + it("should match combined patterns", function() -- 193 + local tb = { -- 194 + success = true, -- 194 + result = "data" -- 194 + } -- 194 + local result -- 195 + do -- 196 + local _type_0 = type(tb) -- 196 + local _tab_0 = "table" == _type_0 or "userdata" == _type_0 -- 196 + local _match_0 = false -- 196 + if _tab_0 then -- 196 + result = tb.result -- 196 + if true == tb.success and result ~= nil then -- 196 + _match_0 = true -- 196 + result = { -- 197 + "success", -- 197 + result -- 197 + } -- 197 + end -- 196 + end -- 196 + if not _match_0 then -- 196 + local _match_1 = false -- 198 + if _tab_0 then -- 198 + if false == tb.success then -- 198 + _match_1 = true -- 198 + result = { -- 199 + "failed", -- 199 + result -- 199 + } -- 199 + end -- 198 + end -- 198 + if not _match_1 then -- 198 + result = { -- 201 + "invalid" -- 201 + } -- 201 + end -- 195 + end -- 195 + end -- 195 + return assert.same(result, { -- 202 + "success", -- 202 + "data" -- 202 + }) -- 202 + end) -- 193 + it("should match type discriminated patterns", function() -- 204 + local tb = { -- 205 + type = "success", -- 205 + content = "data" -- 205 + } -- 205 + local result -- 206 + do -- 207 + local _type_0 = type(tb) -- 207 + local _tab_0 = "table" == _type_0 or "userdata" == _type_0 -- 207 + local _match_0 = false -- 207 + if _tab_0 then -- 207 + local content = tb.content -- 207 + if "success" == tb.type and content ~= nil then -- 207 + _match_0 = true -- 207 + result = { -- 208 + "success", -- 208 + content -- 208 + } -- 208 + end -- 207 + end -- 207 + if not _match_0 then -- 207 + local _match_1 = false -- 209 + if _tab_0 then -- 209 + local content = tb.content -- 209 + if "error" == tb.type and content ~= nil then -- 209 + _match_1 = true -- 209 + result = { -- 210 + "error", -- 210 + content -- 210 + } -- 210 + end -- 209 + end -- 209 + if not _match_1 then -- 209 + result = { -- 212 + "invalid" -- 212 + } -- 212 + end -- 206 + end -- 206 + end -- 206 + return assert.same(result, { -- 213 + "success", -- 213 + "data" -- 213 + }) -- 213 + end) -- 204 + it("should match with wildcard array capture", function() -- 215 + local clientData = { -- 216 + "Meta", -- 216 + "CUST_1001", -- 216 + "CHK123" -- 216 + } -- 216 + local metadata = nil -- 217 + local customerId = nil -- 218 + local checksum = nil -- 219 + do -- 221 + local _type_0 = type(clientData) -- 221 + local _tab_0 = "table" == _type_0 or "userdata" == _type_0 -- 221 + if _tab_0 then -- 221 + local capturedMetadata -- 221 + do -- 221 + local _accum_0 = { } -- 221 + local _len_0 = 1 -- 221 + local _max_0 = #clientData + -3 + 1 -- 221 + for _index_0 = 1, _max_0 do -- 221 + local _item_0 = clientData[_index_0] -- 221 + _accum_0[_len_0] = _item_0 -- 221 + _len_0 = _len_0 + 1 -- 221 + end -- 221 + capturedMetadata = _accum_0 -- 221 + end -- 221 + customerId = clientData[#clientData - 1] -- 221 + checksum = clientData[#clientData] -- 221 + if customerId ~= nil and checksum ~= nil then -- 221 + metadata = capturedMetadata -- 222 + end -- 221 + end -- 220 + end -- 220 + assert.same(metadata, { -- 223 + "Meta" -- 223 + }) -- 223 + assert.same(customerId, "CUST_1001") -- 224 + return assert.same(checksum, "CHK123") -- 225 + end) -- 215 + it("should work with complex tuple patterns", function() -- 227 + local handlePath -- 228 + handlePath = function(segments) -- 228 + local _type_0 = type(segments) -- 230 + local _tab_0 = "table" == _type_0 or "userdata" == _type_0 -- 230 + local _match_0 = false -- 230 + if _tab_0 then -- 230 + local resource = segments[#segments - 1] -- 230 + local action = segments[#segments] -- 230 + if resource ~= nil and action ~= nil then -- 230 + _match_0 = true -- 230 + return { -- 231 + "Resource: " .. tostring(resource), -- 231 + "Action: " .. tostring(action) -- 231 + } -- 231 + end -- 230 + end -- 230 + if not _match_0 then -- 230 + return { -- 233 + "no match" -- 233 + } -- 233 + end -- 229 + end -- 228 + local result = handlePath({ -- 234 + "admin", -- 234 + "logs", -- 234 + "view" -- 234 + }) -- 234 + return assert.same(result, { -- 235 + "Resource: logs", -- 235 + "Action: view" -- 235 + }) -- 235 + end) -- 227 + it("should match boolean false correctly", function() -- 237 + local items = { -- 239 + { -- 239 + x = 100, -- 239 + y = 200 -- 239 + }, -- 239 + { -- 240 + width = 300, -- 240 + height = 400 -- 240 + }, -- 240 + false -- 241 + } -- 238 + local results = { } -- 243 + for _index_0 = 1, #items do -- 244 + local item = items[_index_0] -- 244 + local _type_0 = type(item) -- 246 + local _tab_0 = "table" == _type_0 or "userdata" == _type_0 -- 246 + local _match_0 = false -- 246 + if _tab_0 then -- 246 + local x = item.x -- 246 + local y = item.y -- 246 + if x ~= nil and y ~= nil then -- 246 + _match_0 = true -- 246 + table.insert(results, "Vec2") -- 247 + end -- 246 + end -- 246 + if not _match_0 then -- 246 + local _match_1 = false -- 248 + if _tab_0 then -- 248 + local width = item.width -- 248 + local height = item.height -- 248 + if width ~= nil and height ~= nil then -- 248 + _match_1 = true -- 248 + table.insert(results, "Size") -- 249 + end -- 248 + end -- 248 + if not _match_1 then -- 248 + if false == item then -- 250 + table.insert(results, "None") -- 251 + end -- 245 + end -- 245 + end -- 245 + end -- 244 + return assert.same(results, { -- 252 + "Vec2", -- 252 + "Size", -- 252 + "None" -- 252 + }) -- 252 + end) -- 237 + it("should handle switch with then syntax", function() -- 254 + local value = "cool" -- 255 + local result -- 256 + if "cool" == value then -- 257 + result = "matched cool" -- 257 + else -- 258 + result = "else branch" -- 258 + end -- 256 + return assert.same(result, "matched cool") -- 259 + end) -- 254 + return it("should handle switch in function call", function() -- 261 + local getValue -- 262 + getValue = function() -- 262 + local _exp_0 = something -- 263 + if 1 == _exp_0 then -- 264 + return "yes" -- 264 + else -- 265 + return "no" -- 265 + end -- 263 + end -- 262 + local something = 1 -- 266 + return assert.same(getValue(), "yes") -- 267 + end) -- 261 +end) -- 1 diff --git a/spec/outputs/5.1/test/vararg_spec.lua b/spec/outputs/5.1/test/vararg_spec.lua new file mode 100644 index 0000000..a95e64e --- /dev/null +++ b/spec/outputs/5.1/test/vararg_spec.lua @@ -0,0 +1,164 @@ +return describe("vararg", function() -- 1 + it("should pass varargs to function", function() -- 2 + local sum -- 3 + sum = function(...) -- 3 + local total = 0 -- 4 + for i = 1, select("#", ...) do -- 5 + if type(select(i, ...)) == "number" then -- 6 + total = total + select(i, ...) -- 7 + end -- 6 + end -- 5 + return total -- 8 + end -- 3 + local result = sum(1, 2, 3, 4, 5) -- 9 + return assert.same(result, 15) -- 10 + end) -- 2 + it("should handle empty varargs", function() -- 12 + local fn -- 13 + fn = function(...) -- 13 + return select("#", ...) -- 13 + end -- 13 + local result = fn() -- 14 + return assert.same(result, 0) -- 15 + end) -- 12 + it("should spread varargs in function call", function() -- 17 + local receiver -- 18 + receiver = function(a, b, c) -- 18 + return { -- 18 + a, -- 18 + b, -- 18 + c -- 18 + } -- 18 + end -- 18 + local source -- 19 + source = function() -- 19 + return 1, 2, 3 -- 19 + end -- 19 + local result = receiver(source()) -- 20 + return assert.same(result, { -- 21 + 1, -- 21 + 2, -- 21 + 3 -- 21 + }) -- 21 + end) -- 17 + it("should use varargs in table", function() -- 23 + local fn -- 24 + fn = function(...) -- 24 + return { -- 24 + ... -- 24 + } -- 24 + end -- 24 + local result = fn(1, 2, 3) -- 25 + return assert.same(result, { -- 26 + 1, -- 26 + 2, -- 26 + 3 -- 26 + }) -- 26 + end) -- 23 + it("should forward varargs", function() -- 28 + local middle -- 29 + middle = function(fn, ...) -- 29 + return fn(...) -- 29 + end -- 29 + local inner -- 30 + inner = function(a, b, c) -- 30 + return a + b + c -- 30 + end -- 30 + local result = middle(inner, 1, 2, 3) -- 31 + return assert.same(result, 6) -- 32 + end) -- 28 + it("should count varargs with select", function() -- 34 + local fn -- 35 + fn = function(...) -- 35 + return select("#", ...) -- 35 + end -- 35 + assert.same(fn(1, 2, 3), 3) -- 36 + assert.same(fn("a", "b"), 2) -- 37 + return assert.same(fn(), 0) -- 38 + end) -- 34 + it("should select from varargs", function() -- 40 + local fn -- 41 + fn = function(...) -- 41 + return select(2, ...) -- 41 + end -- 41 + local result = fn(1, 2, 3) -- 42 + return assert.same(result, 2) -- 43 + end) -- 40 + it("should work with named parameters and varargs", function() -- 45 + local fn -- 46 + fn = function(first, ...) -- 46 + return { -- 47 + first, -- 47 + select("#", ...) -- 47 + } -- 47 + end -- 46 + local result = fn("first", "second", "third") -- 48 + return assert.same(result, { -- 49 + "first", -- 49 + 2 -- 49 + }) -- 49 + end) -- 45 + it("should handle nil in varargs", function() -- 51 + local fn -- 52 + fn = function(...) -- 52 + local count = select("#", ...) -- 53 + local has_nil = false -- 54 + for i = 1, count do -- 55 + if select(i, ...) == nil then -- 56 + has_nil = true -- 56 + end -- 56 + end -- 55 + return { -- 57 + count, -- 57 + has_nil -- 57 + } -- 57 + end -- 52 + local result = fn(1, nil, 3) -- 58 + return assert.same(result, { -- 59 + 3, -- 59 + true -- 59 + }) -- 59 + end) -- 51 + it("should work with table unpack", function() -- 61 + local fn -- 62 + fn = function(...) -- 62 + return { -- 62 + ... -- 62 + } -- 62 + end -- 62 + local result = fn(table.unpack({ -- 63 + 1, -- 63 + 2, -- 63 + 3 -- 63 + })) -- 63 + return assert.same(result, { -- 64 + 1, -- 64 + 2, -- 64 + 3 -- 64 + }) -- 64 + end) -- 61 + return it("should work with varargs in comprehension", function() -- 66 + local fn -- 67 + fn = function(...) -- 67 + local _accum_0 = { } -- 67 + local _len_0 = 1 -- 67 + local _list_0 = { -- 67 + ... -- 67 + } -- 67 + for _index_0 = 1, #_list_0 do -- 67 + local x = _list_0[_index_0] -- 67 + _accum_0[_len_0] = x * 2 -- 67 + _len_0 = _len_0 + 1 -- 67 + end -- 67 + return _accum_0 -- 67 + end -- 67 + local result = fn(1, 2, 3, 4, 5) -- 68 + return assert.same(result, { -- 69 + 2, -- 69 + 4, -- 69 + 6, -- 69 + 8, -- 69 + 10 -- 69 + }) -- 69 + end) -- 66 +end) -- 1 diff --git a/spec/outputs/5.1/test/with_spec.lua b/spec/outputs/5.1/test/with_spec.lua new file mode 100644 index 0000000..f3f10e3 --- /dev/null +++ b/spec/outputs/5.1/test/with_spec.lua @@ -0,0 +1,170 @@ +return describe("with", function() -- 1 + it("should access property with . syntax", function() -- 2 + local obj = { -- 3 + value = 42 -- 3 + } -- 3 + do -- 4 + local result = obj.value -- 5 + end -- 4 + return assert.same(result, 42) -- 6 + end) -- 2 + it("should call method with : syntax", function() -- 8 + local obj = { -- 9 + func = function() -- 9 + return "result" -- 9 + end -- 9 + } -- 9 + do -- 10 + local result = obj:func() -- 11 + end -- 10 + return assert.same(result, "result") -- 12 + end) -- 8 + it("should work as statement", function() -- 14 + local obj = { -- 15 + x = 10, -- 15 + y = 20 -- 15 + } -- 15 + obj.sum = obj.x + obj.y -- 17 + return assert.same(obj.sum, 30) -- 18 + end) -- 14 + it("should support nested with", function() -- 20 + local outer = { -- 21 + inner = { -- 21 + value = 100 -- 21 + } -- 21 + } -- 21 + do -- 22 + local _with_0 = outer.inner -- 22 + local result = _with_0.value -- 23 + end -- 22 + return assert.same(result, 100) -- 24 + end) -- 20 + it("should work with? safely", function() -- 26 + local obj = { -- 27 + x = 5 -- 27 + } -- 27 + do -- 28 + local result = obj.x -- 29 + end -- 28 + return assert.same(result, 5) -- 30 + end) -- 26 + it("should work with if inside with", function() -- 32 + local obj = { -- 33 + x = 10, -- 33 + y = 20 -- 33 + } -- 33 + if obj.x > 5 then -- 35 + local result = obj.x + obj.y -- 36 + end -- 35 + return assert.same(result, 30) -- 37 + end) -- 32 + it("should work with switch inside with", function() -- 39 + local obj = { -- 40 + type = "add", -- 40 + a = 5, -- 40 + b = 3 -- 40 + } -- 40 + do -- 41 + local result -- 42 + local _exp_0 = obj.type -- 42 + if "add" == _exp_0 then -- 43 + result = obj.a + obj.b -- 43 + else -- 44 + result = 0 -- 44 + end -- 42 + end -- 41 + return assert.same(result, 8) -- 45 + end) -- 39 + it("should work with loop inside with", function() -- 47 + local obj = { -- 48 + items = { -- 48 + 1, -- 48 + 2, -- 48 + 3 -- 48 + } -- 48 + } -- 48 + local sum = 0 -- 49 + local _list_0 = obj.items -- 51 + for _index_0 = 1, #_list_0 do -- 51 + local item = _list_0[_index_0] -- 51 + sum = sum + item -- 52 + end -- 51 + return assert.same(sum, 6) -- 53 + end) -- 47 + it("should work with destructure", function() -- 55 + local obj = { -- 56 + x = 1, -- 56 + y = 2, -- 56 + z = 3 -- 56 + } -- 56 + do -- 57 + local x, y, z = obj[1], obj[2], obj[3] -- 58 + end -- 57 + assert.same(x, 1) -- 59 + assert.same(y, 2) -- 60 + return assert.same(z, 3) -- 61 + end) -- 55 + it("should handle simple with body", function() -- 63 + local obj = { -- 64 + value = 42 -- 64 + } -- 64 + obj.value2 = 100 -- 66 + return assert.same(obj.value2, 100) -- 67 + end) -- 63 + it("should work with return inside", function() -- 69 + local obj = { -- 70 + value = 100 -- 70 + } -- 70 + local fn -- 71 + fn = function() -- 71 + return obj.value -- 73 + end -- 71 + return assert.same(fn(), 100) -- 74 + end) -- 69 + it("should work with break inside", function() -- 76 + local sum = 0 -- 77 + for i = 1, 5 do -- 78 + local obj = { -- 79 + value = i -- 79 + } -- 79 + if obj.value == 3 then -- 81 + break -- 82 + end -- 81 + sum = sum + obj.value -- 83 + end -- 78 + return assert.same(sum, 3) -- 84 + end) -- 76 + it("should chain property access", function() -- 86 + local obj = { -- 87 + a = { -- 87 + b = { -- 87 + c = 42 -- 87 + } -- 87 + } -- 87 + } -- 87 + do -- 88 + local _with_0 = obj.a.b -- 88 + local result = _with_0.c -- 89 + end -- 88 + return assert.same(result, 42) -- 90 + end) -- 86 + it("should work with multiple statements", function() -- 92 + local obj = { -- 93 + x = 1, -- 93 + y = 2 -- 93 + } -- 93 + local sum = 0 -- 94 + do -- 95 + sum = sum + obj.x -- 96 + sum = sum + obj.y -- 97 + end -- 95 + return assert.same(sum, 3) -- 98 + end) -- 92 + return it("should preserve object reference", function() -- 100 + local obj = { -- 101 + value = 42 -- 101 + } -- 101 + obj.value = 100 -- 103 + return assert.same(obj.value, 100) -- 104 + end) -- 100 +end) -- 1 diff --git a/src/yuescript/yue_compiler.cpp b/src/yuescript/yue_compiler.cpp index f2a4c7a..d20b94b 100644 --- a/src/yuescript/yue_compiler.cpp +++ b/src/yuescript/yue_compiler.cpp @@ -10224,8 +10224,9 @@ private: } BLOCK_END } else if (auto expList = expListFrom(statement)) { - auto value = singleValueFrom(expList); - clsDecl = value->get_by_path(); + if (auto value = singleValueFrom(expList)) { + clsDecl = value->get_by_path(); + } } if (clsDecl) { auto variable = clsDecl->name.as(); -- cgit v1.2.3-55-g6feb