aboutsummaryrefslogtreecommitdiff
path: root/spec/outputs/test/annotation_spec.lua
diff options
context:
space:
mode:
Diffstat (limited to 'spec/outputs/test/annotation_spec.lua')
-rw-r--r--spec/outputs/test/annotation_spec.lua162
1 files changed, 162 insertions, 0 deletions
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)