aboutsummaryrefslogtreecommitdiff
path: root/spec/outputs
diff options
context:
space:
mode:
authorLi Jin <dragon-fly@qq.com>2026-04-23 17:04:06 +0800
committerLi Jin <dragon-fly@qq.com>2026-04-23 17:04:06 +0800
commitacb80dec2706359027c0461073aded3420eaec56 (patch)
treede4e3393e6af6911f1e292fa2a4677e788062f04 /spec/outputs
parent7805872d0a1705fa2fc296b6c37a40f675a39591 (diff)
downloadyuescript-acb80dec2706359027c0461073aded3420eaec56.tar.gz
yuescript-acb80dec2706359027c0461073aded3420eaec56.tar.bz2
yuescript-acb80dec2706359027c0461073aded3420eaec56.zip
Add annotation statements and expand annotation tests
Diffstat (limited to 'spec/outputs')
-rw-r--r--spec/outputs/annotation.lua48
-rw-r--r--spec/outputs/annotation_before.lua55
-rw-r--r--spec/outputs/test/annotation_spec.lua162
-rw-r--r--spec/outputs/test/format_spec.lua3
4 files changed, 268 insertions, 0 deletions
diff --git a/spec/outputs/annotation.lua b/spec/outputs/annotation.lua
new file mode 100644
index 0000000..261bd7b
--- /dev/null
+++ b/spec/outputs/annotation.lua
@@ -0,0 +1,48 @@
1local A
2do
3 local _class_0
4 local _base_0 = {
5 setAdd = function(self, x, y)
6 self.x = x
7 self.y = y
8 return self.x + self.y
9 end
10 }
11 if _base_0.__index == nil then
12 _base_0.__index = _base_0
13 end
14 _class_0 = setmetatable({
15 __init = function(self, x, y)
16 if x == nil then
17 x = 0
18 end
19 if y == nil then
20 y = 0
21 end
22 self.x = x
23 self.y = y
24 end,
25 __base = _base_0,
26 __name = "A"
27 }, {
28 __index = _base_0,
29 __call = function(cls, ...)
30 local _self_0 = setmetatable({ }, _base_0)
31 cls.__init(_self_0, ...)
32 return _self_0
33 end
34 })
35 _base_0.__class = _class_0
36 A = _class_0
37end
38---@class A
39---@field x number
40---@field y number
41---@field setAdd fun(self: A, x: number, y: number): number Set fields and add number values.
42---@class AClass
43---@operator call:A
44---@cast A AClass
45local a = A()
46local res = a:setAdd(1, 2)
47print(a.x, a.y, a.y, res)
48return
diff --git a/spec/outputs/annotation_before.lua b/spec/outputs/annotation_before.lua
new file mode 100644
index 0000000..874ef37
--- /dev/null
+++ b/spec/outputs/annotation_before.lua
@@ -0,0 +1,55 @@
1local B
2do
3 local _class_0
4 local _base_0 = {
5 getTag = function(self)
6 return "before"
7 end
8 }
9 if _base_0.__index == nil then
10 _base_0.__index = _base_0
11 end
12 _class_0 = setmetatable({
13 __init = function() end,
14 __base = _base_0,
15 __name = "B"
16 }, {
17 __index = _base_0,
18 __call = function(cls, ...)
19 local _self_0 = setmetatable({ }, _base_0)
20 cls.__init(_self_0, ...)
21 return _self_0
22 end
23 })
24 _base_0.__class = _class_0
25 B = _class_0
26end
27-- "before":B
28local C
29do
30 local _class_0
31 local _base_0 = {
32 getTag = function(self)
33 return "after"
34 end
35 }
36 if _base_0.__index == nil then
37 _base_0.__index = _base_0
38 end
39 _class_0 = setmetatable({
40 __init = function() end,
41 __base = _base_0,
42 __name = "C"
43 }, {
44 __index = _base_0,
45 __call = function(cls, ...)
46 local _self_0 = setmetatable({ }, _base_0)
47 cls.__init(_self_0, ...)
48 return _self_0
49 end
50 })
51 _base_0.__class = _class_0
52 C = _class_0
53end
54-- "after":C
55return B():getTag(), C():getTag()
diff --git a/spec/outputs/test/annotation_spec.lua b/spec/outputs/test/annotation_spec.lua
new file mode 100644
index 0000000..866b9dc
--- /dev/null
+++ b/spec/outputs/test/annotation_spec.lua
@@ -0,0 +1,162 @@
1local to_lua
2do
3 local _obj_0 = require("yue")
4 to_lua = _obj_0.to_lua
5end
6local compile_and_run
7compile_and_run = function(code, config)
8 if config == nil then
9 config = { }
10 end
11 local lua_code, err = to_lua(code, config)
12 assert.is_nil(err)
13 assert.is_not_nil(lua_code)
14 local chunk, load_err = load(lua_code)
15 assert.is_nil(load_err)
16 assert.is_not_nil(chunk)
17 return chunk()
18end
19return describe("annotation", function()
20 it("should append generated text after annotated class by default", function()
21 local code = [[macro ClsDef = (code`ClassDecl) ->
22 className = code\match "^class%s+(%w+)"
23 return
24 type: "text"
25 before: false
26 code: "-- after:" .. className
27
28$[ClsDef]
29class A
30 getName: => "A"
31
32return
33]]
34 local result, err = to_lua(code)
35 assert.is_nil(err)
36 assert.is_not_nil(result)
37 assert.is_true(result:find("__name = \"A\"") ~= nil)
38 assert.is_true(result:find("%-%- after:A") ~= nil)
39 return assert.is_true(result:find("__name = \"A\"") < result:find("%-%- after:A"))
40 end)
41 it("should place generated text before the annotated statement when before is true", function()
42 local code = [[macro Before = (code`ClassDecl) ->
43 className = code\match "^class%s+(%w+)"
44 return
45 type: "text"
46 before: true
47 code: "-- before:" .. className
48
49$[Before]
50class B
51 getName: => "B"
52
53return
54]]
55 local result, err = to_lua(code)
56 assert.is_nil(err)
57 assert.is_not_nil(result)
58 assert.is_true(result:find("%-%- before:B") ~= nil)
59 assert.is_true(result:find("local B") ~= nil)
60 return assert.is_true(result:find("%-%- before:B") < result:find("local B"))
61 end)
62 it("should support annotation invocation arguments", function()
63 local code = [[macro Tag = (tag, code`ClassDecl) ->
64 className = code\match "^class%s+(%w+)"
65 return
66 type: "text"
67 before: false
68 code: "-- " .. tag .. ":" .. className
69
70$[Tag("entity")]
71class C
72 getName: => "C"
73
74return
75]]
76 local result, err = to_lua(code)
77 assert.is_nil(err)
78 assert.is_not_nil(result)
79 return assert.is_true(result:find("%-%- \"entity\":C") ~= nil)
80 end)
81 it("should report an error when annotation is not followed by a statement", function()
82 local code = [[macro Invalid = (code) -> ""
83$[Invalid]
84]]
85 local result, err = to_lua(code)
86 assert.is_nil(result)
87 return assert.is_true(err:match("annotation must be followed by a statement") ~= nil)
88 end)
89 it("should wrap annotated function to validate numeric arguments", function()
90 local code = [[macro ValidateNumberArgs = (code) ->
91 funcName = code\match "^(%w+)%s*="
92 return
93 type: "text"
94 before: false
95 code: table.concat {
96 "local __orig_#{funcName} = #{funcName}"
97 "#{funcName} = function(a, b)"
98 "\tassert(type(a) == \"number\", \"expected number for a\")"
99 "\tassert(type(b) == \"number\", \"expected number for b\")"
100 "\treturn __orig_#{funcName}(a, b)"
101 "end"
102 }, "\n"
103
104$[ValidateNumberArgs]
105add = (a, b) -> a + b
106
107ok, value = pcall -> add 3, 4
108bad_ok, bad_err = pcall -> add "3", 4
109return ok, value, bad_ok, bad_err
110]]
111 local ok, value, bad_ok, bad_err = compile_and_run(code)
112 assert.is_true(ok)
113 assert.same(value, 7)
114 assert.is_false(bad_ok)
115 return assert.is_true(bad_err:match("expected number for a") ~= nil)
116 end)
117 it("should wrap annotated function to validate return value", function()
118 local code = [[macro ValidateNumberReturn = (code) ->
119 funcName = code\match "^(%w+)%s*="
120 return
121 type: "text"
122 before: false
123 code: table.concat {
124 "local __orig_#{funcName} = #{funcName}"
125 "#{funcName} = function(...)"
126 "\tlocal result = __orig_#{funcName}(...)"
127 "\tassert(type(result) == \"number\", \"expected numeric return\")"
128 "\treturn result"
129 "end"
130 }, "\n"
131
132$[ValidateNumberReturn]
133toText = (value) -> tostring value
134
135ok, err = pcall -> toText 42
136return ok, err
137]]
138 local ok, err = compile_and_run(code)
139 assert.is_false(ok)
140 return assert.is_true(err:match("expected numeric return") ~= nil)
141 end)
142 return it("should use annotation arguments to register annotated classes", function()
143 local code = [[macro Register = (registry, code`ClassDecl) ->
144 className = code\match "^class%s+(%w+)"
145 return
146 type: "text"
147 before: false
148 code: "#{registry}[\"#{className}\"] = #{className}"
149
150registry = {}
151
152$[Register(registry)]
153class Worker
154 run: => "ok"
155
156return registry.Worker != nil, registry.Worker!\run!
157]]
158 local exists, result = compile_and_run(code)
159 assert.is_true(exists)
160 return assert.same(result, "ok")
161 end)
162end)
diff --git a/spec/outputs/test/format_spec.lua b/spec/outputs/test/format_spec.lua
index d38a0ad..1eb2fbb 100644
--- a/spec/outputs/test/format_spec.lua
+++ b/spec/outputs/test/format_spec.lua
@@ -26,6 +26,8 @@ local files = {
26 "spec/inputs/export_default.yue", 26 "spec/inputs/export_default.yue",
27 "spec/inputs/with_scope_shadow.yue", 27 "spec/inputs/with_scope_shadow.yue",
28 "spec/inputs/assign.yue", 28 "spec/inputs/assign.yue",
29 "spec/inputs/annotation.yue",
30 "spec/inputs/annotation_before.yue",
29 "spec/inputs/literals.yue", 31 "spec/inputs/literals.yue",
30 "spec/inputs/luarocks_upload.yue", 32 "spec/inputs/luarocks_upload.yue",
31 "spec/inputs/comprehension_nested.yue", 33 "spec/inputs/comprehension_nested.yue",
@@ -64,6 +66,7 @@ local files = {
64 "spec/inputs/test/continue_spec.yue", 66 "spec/inputs/test/continue_spec.yue",
65 "spec/inputs/test/varargs_assignment_spec.yue", 67 "spec/inputs/test/varargs_assignment_spec.yue",
66 "spec/inputs/test/advanced_macro_spec.yue", 68 "spec/inputs/test/advanced_macro_spec.yue",
69 "spec/inputs/test/annotation_spec.yue",
67 "spec/inputs/test/pipe_spec.yue", 70 "spec/inputs/test/pipe_spec.yue",
68 "spec/inputs/test/export_spec.yue", 71 "spec/inputs/test/export_spec.yue",
69 "spec/inputs/test/existential_spec.yue", 72 "spec/inputs/test/existential_spec.yue",