diff options
Diffstat (limited to 'spec/inputs')
| -rw-r--r-- | spec/inputs/annotation.yue | 27 | ||||
| -rw-r--r-- | spec/inputs/annotation_before.yue | 16 | ||||
| -rw-r--r-- | spec/inputs/test/annotation_spec.yue | 160 | ||||
| -rw-r--r-- | spec/inputs/test/format_spec.yue | 4 |
4 files changed, 206 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 @@ | |||
| 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 | |||
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 @@ | |||
| 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! | ||
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 @@ | |||
| 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" | ||
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 | |||
