aboutsummaryrefslogtreecommitdiff
path: root/spec
diff options
context:
space:
mode:
Diffstat (limited to 'spec')
-rw-r--r--spec/inputs/annotation.yue27
-rw-r--r--spec/inputs/annotation_before.yue16
-rw-r--r--spec/inputs/test/annotation_spec.yue160
-rw-r--r--spec/inputs/test/format_spec.yue4
-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
8 files changed, 474 insertions, 1 deletions
diff --git a/spec/inputs/annotation.yue b/spec/inputs/annotation.yue
new file mode 100644
index 0000000..2124ad3
--- /dev/null
+++ b/spec/inputs/annotation.yue
@@ -0,0 +1,27 @@
1macro 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]
15class 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
22a = A!
23res = a::setAdd 1, 2
24print(a.x, a.y, a.y, res)
25
26return
27
diff --git a/spec/inputs/annotation_before.yue b/spec/inputs/annotation_before.yue
new file mode 100644
index 0000000..9b11aad
--- /dev/null
+++ b/spec/inputs/annotation_before.yue
@@ -0,0 +1,16 @@
1macro 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")]
9class B
10 getTag: => "before"
11
12$[Tag("after")]
13class C
14 getTag: => "after"
15
16return B!\getTag!, C!\getTag!
diff --git a/spec/inputs/test/annotation_spec.yue b/spec/inputs/test/annotation_spec.yue
new file mode 100644
index 0000000..3cd1d5a
--- /dev/null
+++ b/spec/inputs/test/annotation_spec.yue
@@ -0,0 +1,160 @@
1import to_lua from require "yue"
2
3compile_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
12describe "annotation", ->
13 it "should append generated text after annotated class by default", ->
14 code = [[
15macro 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]
23class A
24 getName: => "A"
25
26return
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 = [[
37macro 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]
45class B
46 getName: => "B"
47
48return
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 = [[
59macro 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")]
67class C
68 getName: => "C"
69
70return
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 = [[
79macro 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 = [[
88macro 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]
103add = (a, b) -> a + b
104
105ok, value = pcall -> add 3, 4
106bad_ok, bad_err = pcall -> add "3", 4
107return 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 = [[
117macro 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]
132toText = (value) -> tostring value
133
134ok, err = pcall -> toText 42
135return 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 = [[
143macro 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 exists, result = compile_and_run code
159 assert.is_true exists
160 assert.same result, "ok"
diff --git a/spec/inputs/test/format_spec.yue b/spec/inputs/test/format_spec.yue
index 310b610..a76a5dd 100644
--- a/spec/inputs/test/format_spec.yue
+++ b/spec/inputs/test/format_spec.yue
@@ -26,6 +26,8 @@ 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 @@ 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"
@@ -192,4 +195,3 @@ for file in *files
192 assert.is_not_nil ast 195 assert.is_not_nil ast
193 rewriteLineCol ast 196 rewriteLineCol ast
194 assert.same original_ast, ast 197 assert.same original_ast, ast
195
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",