Skip to content

Commit acb80de

Browse files
committed
Add annotation statements and expand annotation tests
1 parent 7805872 commit acb80de

14 files changed

Lines changed: 584 additions & 27 deletions

CMakeLists.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,11 @@ if (LUA_EXEC_NAME MATCHES "luajit")
2828
NAMES luajit libluajit
2929
PATHS ${LUA_LIBDIR}
3030
NO_DEFAULT_PATH)
31+
elseif (LUA_VERSION_STRING MATCHES "Lua 5.5")
32+
find_library(LUA_LIBRARIES
33+
NAMES lua55 lua5.5 liblua55 liblua5.5 lua liblua
34+
PATHS ${LUA_LIBDIR}
35+
NO_DEFAULT_PATH)
3136
elseif (LUA_VERSION_STRING MATCHES "Lua 5.4")
3237
find_library(LUA_LIBRARIES
3338
NAMES lua54 lua5.4 liblua54 liblua5.4 lua liblua

spec/inputs/annotation.yue

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
macro ClsDef = (code`ClassDecl) ->
2+
className = code\match "^class%s+(%w+)"
3+
lines = table.concat [item\gsub "%-%-%-", "---@" for item in code\gmatch "(%-%-%-.-)\n"], "\n"
4+
return
5+
type: "text"
6+
before: false
7+
code: |
8+
---@class #{className}
9+
#{lines}
10+
---@class #{className}Class
11+
---@operator call:#{className}
12+
---@cast #{className} #{className}Class
13+
14+
$[ClsDef]
15+
class A
16+
---field x number
17+
---field y number
18+
new: (@x = 0, @y = 0) =>
19+
---field setAdd fun(self: A, x: number, y: number): number Set fields and add number values.
20+
setAdd: (@x, @y) => @x + @y
21+
22+
a = A!
23+
res = a::setAdd 1, 2
24+
print(a.x, a.y, a.y, res)
25+
26+
return
27+

spec/inputs/annotation_before.yue

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
macro Tag = (tag, code`ClassDecl) ->
2+
className = code\match "^class%s+(%w+)"
3+
return
4+
type: "text"
5+
before: tag == "before"
6+
code: "-- #{tag}:#{className}"
7+
8+
$[Tag("before")]
9+
class B
10+
getTag: => "before"
11+
12+
$[Tag("after")]
13+
class C
14+
getTag: => "after"
15+
16+
return B!\getTag!, C!\getTag!
Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
import to_lua from require "yue"
2+
3+
compile_and_run = (code, config = {}) ->
4+
lua_code, err = to_lua code, config
5+
assert.is_nil err
6+
assert.is_not_nil lua_code
7+
chunk, load_err = load lua_code
8+
assert.is_nil load_err
9+
assert.is_not_nil chunk
10+
chunk!
11+
12+
describe "annotation", ->
13+
it "should append generated text after annotated class by default", ->
14+
code = [[
15+
macro ClsDef = (code`ClassDecl) ->
16+
className = code\match "^class%s+(%w+)"
17+
return
18+
type: "text"
19+
before: false
20+
code: "-- after:" .. className
21+
22+
$[ClsDef]
23+
class A
24+
getName: => "A"
25+
26+
return
27+
]]
28+
result, err = to_lua code
29+
assert.is_nil err
30+
assert.is_not_nil result
31+
assert.is_true result\find("__name = \"A\"") != nil
32+
assert.is_true result\find("%-%- after:A") != nil
33+
assert.is_true result\find("__name = \"A\"") < result\find("%-%- after:A")
34+
35+
it "should place generated text before the annotated statement when before is true", ->
36+
code = [[
37+
macro Before = (code`ClassDecl) ->
38+
className = code\match "^class%s+(%w+)"
39+
return
40+
type: "text"
41+
before: true
42+
code: "-- before:" .. className
43+
44+
$[Before]
45+
class B
46+
getName: => "B"
47+
48+
return
49+
]]
50+
result, err = to_lua code
51+
assert.is_nil err
52+
assert.is_not_nil result
53+
assert.is_true result\find("%-%- before:B") != nil
54+
assert.is_true result\find("local B") != nil
55+
assert.is_true result\find("%-%- before:B") < result\find("local B")
56+
57+
it "should support annotation invocation arguments", ->
58+
code = [[
59+
macro Tag = (tag, code`ClassDecl) ->
60+
className = code\match "^class%s+(%w+)"
61+
return
62+
type: "text"
63+
before: false
64+
code: "-- " .. tag .. ":" .. className
65+
66+
$[Tag("entity")]
67+
class C
68+
getName: => "C"
69+
70+
return
71+
]]
72+
result, err = to_lua code
73+
assert.is_nil err
74+
assert.is_not_nil result
75+
assert.is_true result\find("%-%- \"entity\":C") != nil
76+
77+
it "should report an error when annotation is not followed by a statement", ->
78+
code = [[
79+
macro Invalid = (code) -> ""
80+
$[Invalid]
81+
]]
82+
result, err = to_lua code
83+
assert.is_nil result
84+
assert.is_true err\match("annotation must be followed by a statement") != nil
85+
86+
it "should wrap annotated function to validate numeric arguments", ->
87+
code = [[
88+
macro ValidateNumberArgs = (code) ->
89+
funcName = code\match "^(%w+)%s*="
90+
return
91+
type: "text"
92+
before: false
93+
code: table.concat {
94+
"local __orig_#{funcName} = #{funcName}"
95+
"#{funcName} = function(a, b)"
96+
"\tassert(type(a) == \"number\", \"expected number for a\")"
97+
"\tassert(type(b) == \"number\", \"expected number for b\")"
98+
"\treturn __orig_#{funcName}(a, b)"
99+
"end"
100+
}, "\n"
101+
102+
$[ValidateNumberArgs]
103+
add = (a, b) -> a + b
104+
105+
ok, value = pcall -> add 3, 4
106+
bad_ok, bad_err = pcall -> add "3", 4
107+
return ok, value, bad_ok, bad_err
108+
]]
109+
ok, value, bad_ok, bad_err = compile_and_run code
110+
assert.is_true ok
111+
assert.same value, 7
112+
assert.is_false bad_ok
113+
assert.is_true bad_err\match("expected number for a") != nil
114+
115+
it "should wrap annotated function to validate return value", ->
116+
code = [[
117+
macro ValidateNumberReturn = (code) ->
118+
funcName = code\match "^(%w+)%s*="
119+
return
120+
type: "text"
121+
before: false
122+
code: table.concat {
123+
"local __orig_#{funcName} = #{funcName}"
124+
"#{funcName} = function(...)"
125+
"\tlocal result = __orig_#{funcName}(...)"
126+
"\tassert(type(result) == \"number\", \"expected numeric return\")"
127+
"\treturn result"
128+
"end"
129+
}, "\n"
130+
131+
$[ValidateNumberReturn]
132+
toText = (value) -> tostring value
133+
134+
ok, err = pcall -> toText 42
135+
return ok, err
136+
]]
137+
ok, err = compile_and_run code
138+
assert.is_false ok
139+
assert.is_true err\match("expected numeric return") != nil
140+
141+
it "should use annotation arguments to register annotated classes", ->
142+
code = [[
143+
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+
150+
registry = {}
151+
152+
$[Register(registry)]
153+
class Worker
154+
run: => "ok"
155+
156+
return registry.Worker != nil, registry.Worker!\run!
157+
]]
158+
exists, result = compile_and_run code
159+
assert.is_true exists
160+
assert.same result, "ok"

spec/inputs/test/format_spec.yue

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ files = [
2626
"spec/inputs/export_default.yue"
2727
"spec/inputs/with_scope_shadow.yue"
2828
"spec/inputs/assign.yue"
29+
"spec/inputs/annotation.yue"
30+
"spec/inputs/annotation_before.yue"
2931
"spec/inputs/literals.yue"
3032
"spec/inputs/luarocks_upload.yue"
3133
"spec/inputs/comprehension_nested.yue"
@@ -64,6 +66,7 @@ files = [
6466
"spec/inputs/test/continue_spec.yue"
6567
"spec/inputs/test/varargs_assignment_spec.yue"
6668
"spec/inputs/test/advanced_macro_spec.yue"
69+
"spec/inputs/test/annotation_spec.yue"
6770
"spec/inputs/test/pipe_spec.yue"
6871
"spec/inputs/test/export_spec.yue"
6972
"spec/inputs/test/existential_spec.yue"
@@ -192,4 +195,3 @@ for file in *files
192195
assert.is_not_nil ast
193196
rewriteLineCol ast
194197
assert.same original_ast, ast
195-

spec/outputs/annotation.lua

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
local A
2+
do
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
37+
end
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
45+
local a = A()
46+
local res = a:setAdd(1, 2)
47+
print(a.x, a.y, a.y, res)
48+
return

spec/outputs/annotation_before.lua

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
local B
2+
do
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
26+
end
27+
-- "before":B
28+
local C
29+
do
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
53+
end
54+
-- "after":C
55+
return B():getTag(), C():getTag()

0 commit comments

Comments
 (0)