diff options
Diffstat (limited to 'doc/docs/doc/advanced/macro.md')
| -rw-r--r-- | doc/docs/doc/advanced/macro.md | 275 |
1 files changed, 275 insertions, 0 deletions
diff --git a/doc/docs/doc/advanced/macro.md b/doc/docs/doc/advanced/macro.md new file mode 100644 index 0000000..6d194c3 --- /dev/null +++ b/doc/docs/doc/advanced/macro.md | |||
| @@ -0,0 +1,275 @@ | |||
| 1 | # Macro | ||
| 2 | |||
| 3 | ## Common Usage | ||
| 4 | |||
| 5 | Macro function is used for evaluating a string in the compile time and insert the generated codes into final compilation. | ||
| 6 | |||
| 7 | ```yuescript | ||
| 8 | macro PI2 = -> math.pi * 2 | ||
| 9 | area = $PI2 * 5 | ||
| 10 | |||
| 11 | macro HELLO = -> "'hello world'" | ||
| 12 | print $HELLO | ||
| 13 | |||
| 14 | macro config = (debugging) -> | ||
| 15 | global debugMode = debugging == "true" | ||
| 16 | "" | ||
| 17 | |||
| 18 | macro asserts = (cond) -> | ||
| 19 | debugMode and "assert #{cond}" or "" | ||
| 20 | |||
| 21 | macro assert = (cond) -> | ||
| 22 | debugMode and "assert #{cond}" or "#{cond}" | ||
| 23 | |||
| 24 | $config true | ||
| 25 | $asserts item ~= nil | ||
| 26 | |||
| 27 | $config false | ||
| 28 | value = $assert item | ||
| 29 | |||
| 30 | -- the passed expressions are treated as strings | ||
| 31 | macro and = (...) -> "#{ table.concat {...}, ' and ' }" | ||
| 32 | if $and f1!, f2!, f3! | ||
| 33 | print "OK" | ||
| 34 | ``` | ||
| 35 | <YueDisplay> | ||
| 36 | |||
| 37 | ```yue | ||
| 38 | macro PI2 = -> math.pi * 2 | ||
| 39 | area = $PI2 * 5 | ||
| 40 | |||
| 41 | macro HELLO = -> "'hello world'" | ||
| 42 | print $HELLO | ||
| 43 | |||
| 44 | macro config = (debugging) -> | ||
| 45 | global debugMode = debugging == "true" | ||
| 46 | "" | ||
| 47 | |||
| 48 | macro asserts = (cond) -> | ||
| 49 | debugMode and "assert #{cond}" or "" | ||
| 50 | |||
| 51 | macro assert = (cond) -> | ||
| 52 | debugMode and "assert #{cond}" or "#{cond}" | ||
| 53 | |||
| 54 | $config true | ||
| 55 | $asserts item ~= nil | ||
| 56 | |||
| 57 | $config false | ||
| 58 | value = $assert item | ||
| 59 | |||
| 60 | -- the passed expressions are treated as strings | ||
| 61 | macro and = (...) -> "#{ table.concat {...}, ' and ' }" | ||
| 62 | if $and f1!, f2!, f3! | ||
| 63 | print "OK" | ||
| 64 | ``` | ||
| 65 | |||
| 66 | </YueDisplay> | ||
| 67 | |||
| 68 | ## Insert Raw Codes | ||
| 69 | |||
| 70 | A macro function can either return a YueScript string or a config table containing Lua codes. | ||
| 71 | ```yuescript | ||
| 72 | macro yueFunc = (var) -> "local #{var} = ->" | ||
| 73 | $yueFunc funcA | ||
| 74 | funcA = -> "fail to assign to the Yue macro defined variable" | ||
| 75 | |||
| 76 | macro luaFunc = (var) -> { | ||
| 77 | code: "local function #{var}() end" | ||
| 78 | type: "lua" | ||
| 79 | } | ||
| 80 | $luaFunc funcB | ||
| 81 | funcB = -> "fail to assign to the Lua macro defined variable" | ||
| 82 | |||
| 83 | macro lua = (code) -> { | ||
| 84 | :code | ||
| 85 | type: "lua" | ||
| 86 | } | ||
| 87 | |||
| 88 | -- the raw string leading and ending symbols are auto trimed | ||
| 89 | $lua[==[ | ||
| 90 | -- raw Lua codes insertion | ||
| 91 | if cond then | ||
| 92 | print("output") | ||
| 93 | end | ||
| 94 | ]==] | ||
| 95 | ``` | ||
| 96 | <YueDisplay> | ||
| 97 | |||
| 98 | ```yue | ||
| 99 | macro yueFunc = (var) -> "local #{var} = ->" | ||
| 100 | $yueFunc funcA | ||
| 101 | funcA = -> "fail to assign to the Yue macro defined variable" | ||
| 102 | |||
| 103 | macro luaFunc = (var) -> { | ||
| 104 | code: "local function #{var}() end" | ||
| 105 | type: "lua" | ||
| 106 | } | ||
| 107 | $luaFunc funcB | ||
| 108 | funcB = -> "fail to assign to the Lua macro defined variable" | ||
| 109 | |||
| 110 | macro lua = (code) -> { | ||
| 111 | :code | ||
| 112 | type: "lua" | ||
| 113 | } | ||
| 114 | |||
| 115 | -- the raw string leading and ending symbols are auto trimed | ||
| 116 | $lua[==[ | ||
| 117 | -- raw Lua codes insertion | ||
| 118 | if cond then | ||
| 119 | print("output") | ||
| 120 | end | ||
| 121 | ]==] | ||
| 122 | ``` | ||
| 123 | |||
| 124 | </YueDisplay> | ||
| 125 | |||
| 126 | ## Export Macro | ||
| 127 | |||
| 128 | Macro functions can be exported from a module and get imported in another module. You have to put export macro functions in a single file to be used, and only macro definition, macro importing and macro expansion in place can be put into the macro exporting module. | ||
| 129 | ```yuescript | ||
| 130 | -- file: utils.yue | ||
| 131 | export macro map = (items, action) -> "[#{action} for _ in *#{items}]" | ||
| 132 | export macro filter = (items, action) -> "[_ for _ in *#{items} when #{action}]" | ||
| 133 | export macro foreach = (items, action) -> "for _ in *#{items} | ||
| 134 | #{action}" | ||
| 135 | |||
| 136 | -- file main.yue | ||
| 137 | import "utils" as { | ||
| 138 | $, -- symbol to import all macros | ||
| 139 | $foreach: $each -- rename macro $foreach to $each | ||
| 140 | } | ||
| 141 | [1, 2, 3] |> $map(_ * 2) |> $filter(_ > 4) |> $each print _ | ||
| 142 | ``` | ||
| 143 | <YueDisplay> | ||
| 144 | |||
| 145 | ```yue | ||
| 146 | -- file: utils.yue | ||
| 147 | export macro map = (items, action) -> "[#{action} for _ in *#{items}]" | ||
| 148 | export macro filter = (items, action) -> "[_ for _ in *#{items} when #{action}]" | ||
| 149 | export macro foreach = (items, action) -> "for _ in *#{items} | ||
| 150 | #{action}" | ||
| 151 | |||
| 152 | -- file main.yue | ||
| 153 | -- import function is not available in browser, try it in a real environment | ||
| 154 | --[[ | ||
| 155 | import "utils" as { | ||
| 156 | $, -- symbol to import all macros | ||
| 157 | $foreach: $each -- rename macro $foreach to $each | ||
| 158 | } | ||
| 159 | [1, 2, 3] |> $map(_ * 2) |> $filter(_ > 4) |> $each print _ | ||
| 160 | ]] | ||
| 161 | ``` | ||
| 162 | |||
| 163 | </YueDisplay> | ||
| 164 | |||
| 165 | ## Builtin Macro | ||
| 166 | |||
| 167 | There are some builtin macros but you can override them by declaring macros with the same names. | ||
| 168 | ```yuescript | ||
| 169 | print $FILE -- get string of current module name | ||
| 170 | print $LINE -- get number 2 | ||
| 171 | ``` | ||
| 172 | <YueDisplay> | ||
| 173 | |||
| 174 | ```yue | ||
| 175 | print $FILE -- get string of current module name | ||
| 176 | print $LINE -- get number 2 | ||
| 177 | ``` | ||
| 178 | |||
| 179 | </YueDisplay> | ||
| 180 | |||
| 181 | ## Generating Macros with Macros | ||
| 182 | |||
| 183 | In YueScript, macro functions allow you to generate code at compile time. By nesting macro functions, you can create more complex generation patterns. This feature enables you to define a macro function that generates another macro function, allowing for more dynamic code generation. | ||
| 184 | |||
| 185 | ```yuescript | ||
| 186 | macro Enum = (...) -> | ||
| 187 | items = {...} | ||
| 188 | itemSet = {item, true for item in *items} | ||
| 189 | (item) -> | ||
| 190 | error "got \"#{item}\", expecting one of #{table.concat items, ', '}" unless itemSet[item] | ||
| 191 | "\"#{item}\"" | ||
| 192 | |||
| 193 | macro BodyType = $Enum( | ||
| 194 | Static | ||
| 195 | Dynamic | ||
| 196 | Kinematic | ||
| 197 | ) | ||
| 198 | |||
| 199 | print "Valid enum type:", $BodyType Static | ||
| 200 | -- print "Compilation error with enum type:", $BodyType Unknown | ||
| 201 | ``` | ||
| 202 | |||
| 203 | <YueDisplay> | ||
| 204 | |||
| 205 | ```yue | ||
| 206 | macro Enum = (...) -> | ||
| 207 | items = {...} | ||
| 208 | itemSet = {item, true for item in *items} | ||
| 209 | (item) -> | ||
| 210 | error "got \"#{item}\", expecting one of #{table.concat items, ', '}" unless itemSet[item] | ||
| 211 | "\"#{item}\"" | ||
| 212 | |||
| 213 | macro BodyType = $Enum( | ||
| 214 | Static | ||
| 215 | Dynamic | ||
| 216 | Kinematic | ||
| 217 | ) | ||
| 218 | |||
| 219 | print "Valid enum type:", $BodyType Static | ||
| 220 | -- print "Compilation error with enum type:", $BodyType Unknown | ||
| 221 | ``` | ||
| 222 | |||
| 223 | </YueDisplay> | ||
| 224 | |||
| 225 | ## Argument Validation | ||
| 226 | |||
| 227 | You can declare the expected AST node types in the argument list, and check whether the incoming macro arguments meet the expectations at compile time. | ||
| 228 | |||
| 229 | ```yuescript | ||
| 230 | macro printNumAndStr = (num `Num, str `String) -> | | ||
| 231 | print( | ||
| 232 | #{num} | ||
| 233 | #{str} | ||
| 234 | ) | ||
| 235 | |||
| 236 | $printNumAndStr 123, "hello" | ||
| 237 | ``` | ||
| 238 | <YueDisplay> | ||
| 239 | |||
| 240 | ```yue | ||
| 241 | macro printNumAndStr = (num `Num, str `String) -> | | ||
| 242 | print( | ||
| 243 | #{num} | ||
| 244 | #{str} | ||
| 245 | ) | ||
| 246 | |||
| 247 | $printNumAndStr 123, "hello" | ||
| 248 | ``` | ||
| 249 | |||
| 250 | </YueDisplay> | ||
| 251 | |||
| 252 | If you need more flexible argument checking, you can use the built-in `$is_ast` macro function to manually check at the appropriate place. | ||
| 253 | |||
| 254 | ```yuescript | ||
| 255 | macro printNumAndStr = (num, str) -> | ||
| 256 | error "expected Num as first argument" unless $is_ast Num, num | ||
| 257 | error "expected String as second argument" unless $is_ast String, str | ||
| 258 | "print(#{num}, #{str})" | ||
| 259 | |||
| 260 | $printNumAndStr 123, "hello" | ||
| 261 | ``` | ||
| 262 | <YueDisplay> | ||
| 263 | |||
| 264 | ```yue | ||
| 265 | macro printNumAndStr = (num, str) -> | ||
| 266 | error "expected Num as first argument" unless $is_ast Num, num | ||
| 267 | error "expected String as second argument" unless $is_ast String, str | ||
| 268 | "print(#{num}, #{str})" | ||
| 269 | |||
| 270 | $printNumAndStr 123, "hello" | ||
| 271 | ``` | ||
| 272 | |||
| 273 | </YueDisplay> | ||
| 274 | |||
| 275 | For more details about available AST nodes, please refer to the uppercased definitions in [yue_parser.cpp](https://github.com/IppClub/YueScript/blob/main/src/yuescript/yue_parser.cpp). | ||
