├── .github └── FUNDING.yml ├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── developer-notes.md ├── lang ├── .gitignore ├── Makefile ├── ast_boolean_const_eval.lua ├── ast_const_eval.lua ├── ast_validate.lua ├── bcread.lua ├── bcsave.lua ├── bytecode.lua ├── compile.lua ├── generator.lua ├── id_generator.lua ├── lexer.lua ├── lua_ast.lua ├── luacode_generator.lua ├── meson.build ├── operator.lua ├── parser.lua ├── reader.lua └── util.lua ├── meson.build ├── meson_options.txt ├── run-lexer.lua ├── run.lua ├── scripts ├── test-bytecode-hex.py ├── test-bytecode.py ├── test-luacode-output.py └── test-output.py ├── src ├── Makefile ├── language.c ├── language.h ├── language_loaders.c ├── language_loaders.h ├── luajit-x.c └── meson.build └── tests ├── assign-hazard-1.lua ├── assign-hazard-2.lua ├── closure-1.lua ├── closure-2.lua ├── closure-2B.lua ├── closure-3.lua ├── closure-3b.lua ├── comment-1.lua ├── comment-2.lua ├── comment-3.lua ├── concatenate-1.lua ├── concatenate-2.lua ├── embedded-comment-1.lua ├── expect ├── closure-2.expect1.txt ├── closure-2B.expect1.txt ├── closure-3.expect1.txt ├── closure-3b.expect1.txt ├── expr-prio-1.expect1.txt ├── for-statement-3.expect1.txt ├── forin-3.expect1.txt ├── goto-for-1.expect1.txt ├── logical-tests-3.expect1.txt ├── logical-tests-4b.expect1.txt ├── logical-value-1.expect1.txt ├── not-operator-3.expect1.txt ├── repeat-test-2.expect1.txt ├── send-epression-call-2.expect1.txt ├── sort-algo.expect1.txt ├── table-4.expect1.txt ├── test-6.expect1.txt └── test-9.expect1.txt ├── expr-bracket-1.lua ├── expr-logical-1.lua ├── expr-logical-2.lua ├── expr-logical-3.lua ├── expr-prio-1.lua ├── expr-return-1.lua ├── expr-var-return-1.lua ├── fft-1.lua ├── for-statement-1.lua ├── for-statement-2.lua ├── for-statement-3.lua ├── for-statement-4.lua ├── forin-1.lua ├── forin-2.lua ├── forin-3.lua ├── forin-4.lua ├── func-assign-1.lua ├── func-assign-2.lua ├── func-assign-3.lua ├── func-declaration-1.lua ├── func-empty-1.lua ├── func-vararg-1.lua ├── func.lua ├── function-rec.lua ├── function-tailrec.lua ├── goto-cleanup-1.lua ├── goto-closure-1.lua ├── goto-closure-2.lua ├── goto-for-1.lua ├── goto-markov-1.lua ├── goto-markov-2.lua ├── goto-nested-1.lua ├── goto-nested-2.lua ├── goto-redo-1.lua ├── goto-tail-1.lua ├── if-1.lua ├── jacobi-sor.lua ├── kcdata-1.lua ├── kcdata-2.lua ├── lexer-64int.lua ├── lexer-number-1.lua ├── logical-tests-1.lua ├── logical-tests-2.lua ├── logical-tests-3.lua ├── logical-tests-3b.lua ├── logical-tests-3c.lua ├── logical-tests-3d.lua ├── logical-tests-4.lua ├── logical-tests-4b.lua ├── logical-tests-5.lua ├── logical-tests-6.lua ├── logical-value-1.lua ├── logical-value-2.lua ├── mixed-assign-1.lua ├── mixed-assign-2.lua ├── mixed-assign-3.lua ├── multi-assign-1.lua ├── multi-assign-2b.lua ├── multi-call-1.lua ├── multi-call-2.lua ├── multi-returns-1.lua ├── nested-ifs-1.lua ├── nested-ifs-2.lua ├── nested-ifs-3.lua ├── nested-ifs-4.lua ├── not-operator-1.lua ├── not-operator-2.lua ├── not-operator-3.lua ├── not-operator-4.lua ├── number-inf-zero.lua ├── number-nan.lua ├── numbers-1.lua ├── printf-1.lua ├── rand-init.lua ├── repeat-1.lua ├── repeat-test-1.lua ├── repeat-test-2.lua ├── send-expr.lua ├── send-expression-call-1.lua ├── send-expression-call-2.lua ├── send-expression-call-3.lua ├── sort-algo.lua ├── string-escapes-1.lua ├── string-method-1.lua ├── string-method-2.lua ├── struct-assign-1.lua ├── table-1.lua ├── table-2.lua ├── table-3.lua ├── table-4.lua ├── table-5.lua ├── table-6.lua ├── table-7.lua ├── table-8.lua ├── test-1.lua ├── test-10.lua ├── test-2.lua ├── test-3.lua ├── test-4.lua ├── test-5.lua ├── test-6.lua ├── test-7.lua ├── test-8.lua ├── test-9.lua ├── uclo-1.lua ├── uclo-2.lua ├── uclo-3.lua ├── uclo-4.lua ├── vararg-pack-1.lua ├── vararg-pack-2.lua ├── vararg-pack-3.lua ├── vararg-pack-4.lua ├── vararg-pack-5.lua ├── vararg-pack-6.lua ├── while-loop-1.lua ├── while-loop-2.lua └── while-loop-2b.lua /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: franko 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | build* 3 | tests/log/* 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | LuaJIT Language Toolkit, a toolkit for language implementations. 3 | 4 | Copyright (C) 2013-2014 Francesco Abbate. All rights reserved. 5 | 6 | [ MIT license: http://www.opensource.org/licenses/mit-license.php ] 7 | 8 | Based on Nyanga's language implementation of Richard Hundt. Copyright 9 | license of Nyanga's original work: 10 | 11 | =============================================================================== 12 | 13 | Nyanga -- Modifiable OO Lua Dialect. http://github.com/richardhundt/nyanga 14 | 15 | Copyright (C) 2013-2014 Richard Hundt and contributors. All rights reserved. 16 | 17 | Permission is hereby granted, free of charge, to any person obtaining a copy 18 | of this software and associated documentation files (the "Software"), to deal 19 | in the Software without restriction, including without limitation the rights 20 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 21 | copies of the Software, and to permit persons to whom the Software is 22 | furnished to do so, subject to the following conditions: 23 | 24 | The above copyright notice and this permission notice shall be included in 25 | all copies or substantial portions of the Software. 26 | 27 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 28 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 29 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 30 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 31 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 32 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 33 | THE SOFTWARE. 34 | 35 | [ MIT license: http://www.opensource.org/licenses/mit-license.php ] 36 | 37 | =============================================================================== 38 | 39 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | 2 | # Makefile 3 | # 4 | # Copyright (C) 2014 Francesco Abbate 5 | # 6 | # This program is free software; you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation; either version 3 of the License, or (at 9 | # your option) any later version. 10 | # 11 | # This program is distributed in the hope that it will be useful, but 12 | # WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | # General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with this program; if not, write to the Free Software 18 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 19 | # 20 | 21 | all: lang src 22 | 23 | lang: 24 | $(MAKE) -C lang 25 | 26 | src: lang 27 | $(MAKE) -C src 28 | 29 | clean: 30 | $(MAKE) -C src clean 31 | $(MAKE) -C lang clean 32 | 33 | .PHONY: clean all lang src 34 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | LuaJIT Language Toolkit 2 | === 3 | 4 | The LuaJIT Language Toolkit is an implementation of the Lua programming language written in Lua itself. 5 | It works by generating LuaJIT bytecode, including debug information, and uses LuaJIT's virtual machine to run the generated bytecode. 6 | 7 | On its own, the language toolkit does not do anything useful, since LuaJIT itself does the same things natively. 8 | The purpose of the language toolkit is to provide a starting point to implement a programming language that targets the LuaJIT virtual machine. 9 | 10 | With the LuaJIT Language Toolkit, it is easy to create a new language or modify the Lua language because the parser is cleanly separated from the bytecode generator and the virtual machine. 11 | 12 | The toolkit implements a complete pipeline to parse a Lua program, generate an AST, and generate the corresponding bytecode. 13 | 14 | Lexer 15 | --- 16 | 17 | Its role is to recognize lexical elements from the program text. 18 | It takes the text of the program as input and produces a stream of "tokens" as its output. 19 | 20 | Using the language toolkit you can run the lexer only, to examinate the stream of tokens: 21 | 22 | ``` 23 | luajit run-lexer.lua tests/test-1.lua 24 | ``` 25 | 26 | The command above will lex the following code fragment: 27 | 28 | ```lua 29 | local x = {} 30 | for k = 1, 10 do 31 | x[k] = k*k + 1 32 | end 33 | ``` 34 | 35 | ...to generate the list of tokens: 36 | 37 | TK_local 38 | TK_name x 39 | = 40 | { 41 | } 42 | TK_for 43 | TK_name k 44 | = 45 | TK_number 1 46 | , 47 | TK_number 10 48 | TK_do 49 | TK_name x 50 | [ 51 | TK_name k 52 | ] 53 | = 54 | TK_name k 55 | * 56 | TK_name k 57 | + 58 | TK_number 1 59 | TK_end 60 | 61 | Each line represents a token where the first element is the kind of token and the second element is its value, if any. 62 | 63 | The Lexer's code is an almost literal translation of the LuaJIT's lexer. 64 | 65 | Parser 66 | --- 67 | 68 | The parser takes the token stream from the lexer and builds statements and expressions according to the language's grammar. 69 | The parser is based on a list of parsing rules that are invoked each time the input matches a given rule. 70 | When the input matches a rule, a corresponding function in the AST (abstract syntax tree) module is called to build an AST node. 71 | The generated nodes in turns are passed as arguments to the other parsing rules until the whole program is parsed and a complete AST is built for the program text. 72 | 73 | The AST is very useful as an abstraction of the structure of the program, and is easier to manipulate. 74 | 75 | What distinguishes the language toolkit from LuaJIT is that the parser phase generates an AST, and the bytecode generation is done in a separate phase only when the AST is complete. 76 | 77 | LuaJIT itself operates differently. 78 | During the parsing phase it does not generate any AST but instead the bytecode is directly generated and loaded into the memory to be executed by the VM. 79 | This means that LuaJIT's C implementation performs the three operations: 80 | 81 | - parse the program text 82 | - generate the bytecode 83 | - load the bytecode into memory 84 | 85 | in one single pass. 86 | This approach is remarkable and very efficient, but makes it difficult to modify or extend the programming language. 87 | 88 | ### Parsing Rule example ### 89 | 90 | To illustrate how parsing works in the language toolkit, let us make an example. 91 | The grammar rule for the "return" statement is: 92 | 93 | ``` 94 | explist ::= {exp ','} exp 95 | 96 | return_stmt ::= return [explist] 97 | ``` 98 | 99 | In this case the toolkit parser's rule will parse the optional expression list by calling the function `expr_list`. 100 | Then, once the expressions are parsed the AST's rule `ast:return_stmt(exps, line)` will be invoked by passing the expressions list obtained before. 101 | 102 | ```lua 103 | local function parse_return(ast, ls, line) 104 | ls:next() -- Skip 'return'. 105 | ls.fs.has_return = true 106 | local exps 107 | if EndOfBlock[ls.token] or ls.token == ';' then -- Base return. 108 | exps = { } 109 | else -- Return with one or more values. 110 | exps = expr_list(ast, ls) 111 | end 112 | return ast:return_stmt(exps, line) 113 | end 114 | ``` 115 | 116 | As you can see, the AST functions are invoked using the `ast` object. 117 | 118 | In addition, the parser provides information about: 119 | 120 | * the function prototype 121 | * the syntactic scope 122 | 123 | The first is used to keep track of some information about the current function being parsed. 124 | 125 | The syntactic scope rules tell the user's rule when a new syntactic block begins or end. 126 | Currently this is not really used by the AST builder but it can be useful for other implementations. 127 | 128 | The Abstract Syntax Tree (AST) 129 | --- 130 | 131 | The abstract syntax tree represent the whole Lua program, with all the information the parser has gathered about it. 132 | 133 | One possible approach to implement a new programming language is to generate an AST that more closely corresponds to the target programming language, and then transform the tree into a Lua AST in a separate phase. 134 | 135 | Another possible approach is to directly generate the appropriate Lua AST nodes from the parser itself. 136 | 137 | Currently the language toolkit does not perform any additional transformations, and just passes the AST to the bytecode generator module. 138 | 139 | Bytecode Generator 140 | --- 141 | 142 | Once the AST is generated, it can be fed to the bytecode generator module, which will generate the corresponding LuaJIT bytecode. 143 | 144 | The bytecode generator is based on the original work of Richard Hundt for the Nyanga programming language. 145 | It was largely modified by myself to produce optimized code similar to what LuaJIT would generate, itself. 146 | A lot of work was also done to ensure the correctness of the bytecode and of the debug information. 147 | 148 | Alternative Lua Code generator 149 | ------------------------------ 150 | 151 | Instead of passing the AST to the bytecode generator, an alternative module can be used to generate Lua code. 152 | The module is called "luacode-generator" and can be used exactly like the bytecode generator. 153 | 154 | The Lua code generator has the advantage of being more simple and more safe as the code is parsed directly by LuaJIT, ensuring from the beginning complete compatibility of the bytecode. 155 | 156 | Currently the Lua Code Generator backend does not preserve the line numbers of the original source code. This is meant to be fixed in the future. 157 | 158 | Use this backend instead of the bytecode generator if you prefer to have a more safe backend to convert the Lua AST to code. 159 | The module can also be used for pretty-printing a Lua AST, since the code itself is probably the most human readable representation of the AST. 160 | 161 | C API 162 | --- 163 | 164 | The language toolkit provides a very simple set of C APIs to implement a custom language. 165 | The functions provided by the C API are: 166 | 167 | ```c 168 | /* The functions above are the equivalent of the luaL_* corresponding 169 | functions. */ 170 | extern int language_init(lua_State *L); 171 | extern int language_report(lua_State *L, int status); 172 | extern int language_loadbuffer(lua_State *L, const char *buff, size_t sz, const char *name); 173 | extern int language_loadfile(lua_State *L, const char *filename); 174 | 175 | 176 | /* This function push on the stack a Lua table with the functions: 177 | loadstring, loadfile, dofile and loader. 178 | The first three function can replace the Lua functions while the 179 | last one, loader, can be used as a customized "loader" function for 180 | the "require" function. */ 181 | extern int luaopen_langloaders(lua_State *L); 182 | 183 | /* OPTIONAL: 184 | Load into package.preload lang.* modules using embedded bytecode. */ 185 | extern void language_bc_preload(lua_State *L) 186 | ``` 187 | 188 | The functions above can be used to create a custom LuaJIT executable that use the language toolkit implementation. 189 | 190 | When the function `language_*` is used, an independent `lua_State` is created behind the scenes and used to compile the bytecode. 191 | Once the bytecode is generated it is loaded into the user's `lua_State` ready to be executed. 192 | The approach of using a separate Lua's state ensure that the process of compiling does not interfere with the user's application. 193 | 194 | The function `language_bc_preload` is useful to create a standalone executable that does not depend on the presence of the Lua files at runtime. 195 | The `lang.*` are compiled into bytecode and stored as static C data into the executable. 196 | By calling the function `language_bc_preload` all the modules are *preloaded* using the embedded bytecode. 197 | This feature can be disabled by changing the `BC_PRELOAD` variable in `src/Makefile`. 198 | 199 | How to build 200 | --- 201 | 202 | The LuaJIT Language toolkit can be compiled and optionally installed using Meson. Ensure that Meson is installed, the easyest way is to use PIP, the Python installer. Ensure also that LuaJIT is correctly installed since it is required for the language toolkit. 203 | 204 | Once Meson and LuaJIT are installed configure the build with the command: 205 | 206 | ```sh 207 | meson setup build 208 | ``` 209 | 210 | so that the 'build' directory will be used to build. You may also pass the preload option: 211 | 212 | ```sh 213 | meson setup -Dpreload=true build 214 | ``` 215 | 216 | then to build use 'ninja', the default Meson's backend. 217 | 218 | ```sh 219 | # build 220 | ninja -C build 221 | 222 | # install 223 | ninja -C build install 224 | ``` 225 | 226 | The Meson-based build will take care of installing all the required Lua files, the library itself, the luajit-x executable and a pkg-config file. 227 | 228 | Please note that when using the 'preload' option the Lua files will not be installed since they are embedded in the library itself. 229 | 230 | Running the Application 231 | --- 232 | 233 | The application can be run with the following command: 234 | 235 | ``` 236 | luajit run.lua [lua-options] 237 | ``` 238 | 239 | The "run.lua" script will just invoke the complete pipeline of the lexer, parser and bytecode generator and it will pass the bytecode to luajit with "loadstring". 240 | 241 | The language toolkit also provides a customized executable named `luajit-x` that uses the language toolkit's pipeline instead of the native one. 242 | Otherwise, the program `luajit-x` works exactly the same as `luajit` itself, and accepts the same options. 243 | 244 | In the standard build `luajit-x` will contain the `lang.*` modules as embedded bytecode data so that it does not rely on the Lua files at runtime. 245 | 246 | This means that you can experiment with the language by modifying the Lua implementation of the language and test the changes immediately. 247 | If the option `BC_PRELOAD` in `src/Makefile` is activated you just need to recompile `luajit-x`. 248 | 249 | If you works with the Lua files of the language toolkit you may choose to disable the `BC_PRELOAD` variable to avoid recompiling the executable for each change in the Lua code. 250 | 251 | ### Generated Bytecode ### 252 | 253 | You can inspect the bytecode generated by the language toolkit by using the "-b" options. 254 | They can be invoked either with standard luajit by using "run.lua" or directly using the customized program `luajit-x`. 255 | 256 | For example you can inspect the bytecode using the following command: 257 | 258 | ``` 259 | luajit run.lua -bl tests/test-1.lua 260 | ``` 261 | 262 | or alternatively: 263 | 264 | ``` 265 | ./src/luajit-x -bl tests/test-1.lua 266 | ``` 267 | 268 | where we suppose that you are running `luajit-x` from the language toolkit's root directory. 269 | 270 | Either way, when you use one of the two commands above to generate the bytecode you will the see following on the screen: 271 | 272 | ``` 273 | -- BYTECODE -- "test-1.lua":0-7 274 | 00001 TNEW 0 0 275 | 0002 KSHORT 1 1 276 | 0003 KSHORT 2 10 277 | 0004 KSHORT 3 1 278 | 0005 FORI 1 => 0010 279 | 0006 => MULVV 5 4 4 280 | 0007 ADDVN 5 5 0 ; 1 281 | 0008 TSETV 5 0 4 282 | 0009 FORL 1 => 0006 283 | 0010 => KSHORT 1 1 284 | 0011 KSHORT 2 10 285 | 0012 KSHORT 3 1 286 | 0013 FORI 1 => 0018 287 | 0014 => GGET 5 0 ; "print" 288 | 0015 TGETV 6 0 4 289 | 0016 CALL 5 1 2 290 | 0017 FORL 1 => 0014 291 | 0018 => RET0 0 1 292 | ``` 293 | 294 | You can compare it with the bytecode generated natively by LuaJIT using the command: 295 | 296 | ``` 297 | luajit -bl tests/test-1.lua 298 | ``` 299 | 300 | In the example above the generated bytecode will be *identical* to that generated by LuaJIT. 301 | This is not an accident, since the Language Toolkit's bytecode generator is designed to produce the same bytecode that LuaJIT itself would generate. 302 | In some cases, the generated code will differ. But, this is not considered a big problem as long as the generated code is still semantically correct. 303 | 304 | ### Bytecode Annotated Dump ### 305 | 306 | In addition to the standard LuaJIT bytecode functions, the language toolkit also supports a special debug mode where the bytecode is printed byte-by-byte in hex format with some annotations on the right side of the screen. 307 | The annotations will explain the meaning of each chunk of bytes and decode them as appropriate. 308 | 309 | For example: 310 | 311 | ``` 312 | luajit run.lua -bx tests/test-1.lua 313 | ``` 314 | 315 | will display something like: 316 | 317 | ``` 318 | 1b 4c 4a 01 | Header LuaJIT 2.0 BC 319 | 00 | Flags: None 320 | 11 40 74 65 73 74 73 2f | Chunkname: @tests/test-1.lua 321 | 74 65 73 74 2d 31 2e 6c | 322 | 75 61 | 323 | | .. prototype .. 324 | 8a 01 | prototype length 138 325 | 02 | prototype flags PROTO_VARARG 326 | 00 | parameters number 0 327 | 07 | framesize 7 328 | 00 01 01 12 | size uv: 0 kgc: 1 kn: 1 bc: 19 329 | 31 | debug size 49 330 | 00 07 | firstline: 0 numline: 7 331 | | .. bytecode .. 332 | 32 00 00 00 | 0001 TNEW 0 0 333 | 27 01 01 00 | 0002 KSHORT 1 1 334 | 27 02 0a 00 | 0003 KSHORT 2 10 335 | 27 03 01 00 | 0004 KSHORT 3 1 336 | 49 01 04 80 | 0005 FORI 1 => 0010 337 | 20 05 04 04 | 0006 => MULVV 5 4 4 338 | 14 05 00 05 | 0007 ADDVN 5 5 0 ; 1 339 | 39 05 04 00 | 0008 TSETV 5 0 4 340 | 4b 01 fc 7f | 0009 FORL 1 => 0006 341 | 27 01 01 00 | 0010 => KSHORT 1 1 342 | 27 02 0a 00 | 0011 KSHORT 2 10 343 | 27 03 01 00 | 0012 KSHORT 3 1 344 | 49 01 04 80 | 0013 FORI 1 => 0018 345 | 34 05 00 00 | 0014 => GGET 5 0 ; "print" 346 | 36 06 04 00 | 0015 TGETV 6 0 4 347 | 3e 05 02 01 | 0016 CALL 5 1 2 348 | 4b 01 fc 7f | 0017 FORL 1 => 0014 349 | 47 00 01 00 | 0018 => RET0 0 1 350 | | .. uv .. 351 | | .. kgc .. 352 | 0a 70 72 69 6e 74 | kgc: "print" 353 | | .. knum .. 354 | 02 | knum int: 1 355 | | .. debug .. 356 | 01 | pc001: line 1 357 | 02 | pc002: line 2 358 | 02 | pc003: line 2 359 | 02 | pc004: line 2 360 | 02 | pc005: line 2 361 | ... 362 | ``` 363 | 364 | This kind of output is especially useful for debugging the language toolkit itself because it does account for every byte of the bytecode and include all the sections of the bytecode. 365 | For example, you will be able to inspect the `kgc` or `knum` sections where the prototype's constants are stored. 366 | The output will also include the debug section in decoded form so that it can be easily inspected. 367 | 368 | There is a small trick to compare with the bytecode generated by LuaJIT because this latter it doesn't support the `-bx` option. You should generate first the bytecode using luajit: 369 | 370 | ``` 371 | luajit -bg tests/test-1.lua test-1.bc 372 | ``` 373 | 374 | and then you can use the language toolkit with the `-bx` option to dump the content on the luajit generated bytecode: 375 | 376 | ``` 377 | luajit run.lua -bx test-1.bc 378 | ``` 379 | 380 | so that you can compare the two outputs. 381 | 382 | Current Status 383 | --- 384 | 385 | Currently LuaJIT Language Toolkit should be considered as beta software. 386 | 387 | The implementation is now complete in term of features and well tested, even for the most complex cases, and a complete test suite is used to verify the correctness of the generated bytecode. 388 | 389 | The language toolkit is currently capable of executing itself. 390 | This means that the language toolkit is able to correctly compile and load all of its module and execute them correctly. 391 | 392 | Yet some bugs are probably present and you should be cautious when you use LuaJIT language toolkit. 393 | -------------------------------------------------------------------------------- /developer-notes.md: -------------------------------------------------------------------------------- 1 | # Developer's Notes for LuaJIT Language Toolkit 2 | 3 | ## Build Instructions 4 | 5 | The standard method to build the luajit language toolkit is to use the Meson build system. 6 | 7 | Here an example of using meson: 8 | 9 | ```sh 10 | meson setup build # configure the build 11 | ninja -C build # build 12 | ``` 13 | 14 | Of course Meson and Ninja are required. In alternative Meson can use GNU Make as a backend but using Ninja is recommended. 15 | 16 | In addition to Meson a working luajit installed in your path is also needed. 17 | 18 | At your convenience you may install LuaJIT using the [Little Library Helper](https://github.com/franko/lhelper). 19 | Its usage is to create an build environment and to install luajit: 20 | 21 | ```bash 22 | lhelper create luajit-env # create a new environment 23 | lhelper activate luajit-env # activate the environment 24 | lhelper install luajit # install luajit executable and library 25 | ``` 26 | 27 | The environment will remains and can be later reused using again the 'lhelper activate' command. 28 | 29 | On Linux or other unix-like environment Lhelper can be used or you may install luajit using the standard package manager. 30 | 31 | ## Running the tests 32 | 33 | The language toolkit has a large corpus of tests. They can be ran in different modes but it always involves running the standard luajit executable and compare the output with the language toolkit. 34 | 35 | The output can be compared in the following ways: 36 | 37 | 1. compare directly the output of the Lua test program 38 | 2. compare the listing of the bytecode instructions 39 | 3. compare the hexadecimal dump of the bytecode, byte-per-byte 40 | 41 | To run the tests the following commands should be used respectively, depending on the mode: 42 | 43 | 1. `python2 scripts/test-output.by ` 44 | 2. `python2 scripts/test-bytecode.by ` 45 | 3. `python2 scripts/test-bytecode-hex.by ` 46 | 47 | For each command the directory tests/log will be populated with some logs to compare the differences in the output and the listing of the generated bytecode. 48 | -------------------------------------------------------------------------------- /lang/.gitignore: -------------------------------------------------------------------------------- 1 | *.h 2 | 3 | -------------------------------------------------------------------------------- /lang/Makefile: -------------------------------------------------------------------------------- 1 | 2 | LANG_LUA_FILES = ast-boolean-const-eval.lua ast-const-eval.lua bcread.lua bcsave.lua \ 3 | bytecode.lua compile.lua generator.lua lexer.lua id-generator.lua lua-ast.lua operator.lua \ 4 | parser.lua reader.lua 5 | 6 | LANG_H_FILES = $(LANG_LUA_FILES:%.lua=%.h) 7 | 8 | all: $(LANG_H_FILES) 9 | 10 | clean: 11 | rm -fr $(LANG_H_FILES) 12 | 13 | %.h: %.lua 14 | luajit -b $< $@ 15 | 16 | .PHONY: all clean -------------------------------------------------------------------------------- /lang/ast_boolean_const_eval.lua: -------------------------------------------------------------------------------- 1 | local BoolConstRule = { } 2 | 3 | -- A function that return a numeric constant if an AST node evaluate to an 4 | -- arithmetic constant or "nil" otherwise. 5 | -- The implementation of the function is given below. 6 | local const_eval 7 | 8 | local function dirop_compute(o, a, b) 9 | if o == 'and' then return a and b 10 | elseif o == 'or' then return a or b 11 | end 12 | end 13 | 14 | function BoolConstRule.Literal(node) 15 | local v = node.value 16 | if type(v) == 'boolean' then return v end 17 | end 18 | 19 | function BoolConstRule.BinaryExpression(node) 20 | local o = node.operator 21 | local a = const_eval(node.left) 22 | if a ~= nil then 23 | local b = const_eval(node.right) 24 | if b ~= nil then 25 | return dirop_compute(o, a, b) 26 | end 27 | end 28 | end 29 | 30 | function BoolConstRule.UnaryExpression(node) 31 | local o = node.operator 32 | if o == 'not' then 33 | local v = const_eval(node.argument) 34 | if v ~= nil then return not v end 35 | end 36 | end 37 | 38 | function const_eval(node) 39 | local rule = BoolConstRule[node.kind] 40 | if rule then 41 | return rule(node) 42 | end 43 | end 44 | 45 | return const_eval 46 | -------------------------------------------------------------------------------- /lang/ast_const_eval.lua: -------------------------------------------------------------------------------- 1 | local ConstRule = { } 2 | 3 | -- A function that return a numeric constant if an AST node evaluate to an 4 | -- arithmetic constant or "nil" otherwise. 5 | -- The implementation of the function is given below. 6 | local const_eval 7 | 8 | local function dirop_compute(o, a, b) 9 | if o == '+' then return a + b 10 | elseif o == '-' then return a - b 11 | elseif o == '*' then return a * b 12 | elseif o == '/' then return (a ~= 0 or b ~= 0) and (a / b) or nil 13 | elseif o == '%' then return a % b 14 | elseif o == '^' then return a ^ b 15 | end 16 | end 17 | 18 | function ConstRule.Literal(node) 19 | local v = node.value 20 | if type(v) == 'number' then return v end 21 | end 22 | 23 | function ConstRule.BinaryExpression(node) 24 | local o = node.operator 25 | local a = const_eval(node.left) 26 | if a then 27 | local b = const_eval(node.right) 28 | if b then 29 | return dirop_compute(o, a, b) 30 | end 31 | end 32 | end 33 | 34 | function ConstRule.UnaryExpression(node) 35 | local o = node.operator 36 | if o == '-' then 37 | local v = const_eval(node.argument) 38 | if v then return -v end 39 | end 40 | end 41 | 42 | function const_eval(node) 43 | local rule = ConstRule[node.kind] 44 | if rule then 45 | return rule(node) 46 | end 47 | end 48 | 49 | return const_eval 50 | -------------------------------------------------------------------------------- /lang/ast_validate.lua: -------------------------------------------------------------------------------- 1 | -- 2 | -- Each entry of "syntax" describe a node of the AST tree. 3 | -- The "properties" field gives the specification for the properties 4 | -- of each node. 5 | -- 6 | -- Each "properties" entry is of the form: 7 | -- 8 | -- = 9 | -- 10 | -- where is a recursive type defined as follow: 11 | -- it can be: 12 | -- 13 | -- "Expression", 14 | -- "Statement", 15 | -- ... 16 | -- to indicate a specific kind of "node". Alternatively a node can be 17 | -- specified as; 18 | -- 19 | -- { type = "node", kind = "Statement" } 20 | -- 21 | -- In addition an can be also: 22 | -- 23 | -- { type = "literal", value = "string" } 24 | -- 25 | -- { type = "enum", values = {"a", "b", "c"} } 26 | -- 27 | -- { type = "list", kind = } 28 | -- 29 | -- { type = "choice", values = {, , ...} } 30 | -- 31 | -- The latter two are defined recursively. A "list" is Lua table of element of a 32 | -- given type. The "choice" allow an element to be either of one type or another. 33 | -- 34 | 35 | local syntax = { 36 | Node = { 37 | kind = "Node", 38 | abstract = true 39 | }, 40 | Expression = { 41 | kind = "Expression", 42 | base = "Node", 43 | abstract = true, 44 | }, 45 | Statement = { 46 | kind = "Statement", 47 | base = "Node", 48 | abstract = true, 49 | }, 50 | Chunk = { 51 | kind = "Chunk", 52 | base = "Node", 53 | properties = { 54 | body = { 55 | type = "list", 56 | kind = "Statement" 57 | }, 58 | chunkname = { type = "literal", value = "string" }, 59 | } 60 | }, 61 | Identifier = { 62 | kind = "Identifier", 63 | base = "Expression", 64 | properties = { 65 | name = { type = "literal", value = "string" }, 66 | } 67 | }, 68 | Vararg = { 69 | kind = "Vararg", 70 | base = "Identifier", 71 | properties = { } 72 | }, 73 | BinaryExpression = { 74 | kind = "BinaryExpression", 75 | base = "Expression", 76 | properties = { 77 | operator = { 78 | type = "enum", 79 | values = { 80 | "+", "-", "*", "/", "^", "%", 81 | "==", "~=", ">=", ">", "<=", "<", 82 | } 83 | }, 84 | left = "Expression", 85 | right = "Expression", 86 | } 87 | }, 88 | ConcatenateExpression = { 89 | kind = "ConcatenateExpression", 90 | base = "Expression", 91 | properties = { 92 | terms = { 93 | type = "list", 94 | kind = "Expression", 95 | } 96 | } 97 | }, 98 | UnaryExpression = { 99 | kind = "UnaryExpression", 100 | base = "Expression", 101 | properties = { 102 | operator = { 103 | type = "enum", 104 | values = { "not", "-", "#" }, 105 | }, 106 | argument = "Expression", 107 | } 108 | }, 109 | ExpressionValue = { 110 | kind = "ExpressionValue", 111 | base = "Expression", 112 | properties = { 113 | value = "Expression", 114 | } 115 | }, 116 | AssignmentExpression = { 117 | kind = "AssignmentExpression", 118 | base = "Statement", 119 | properties = { 120 | left = { 121 | type = "list", 122 | kind = { type = "choice", values = { "MemberExpression", "Identifier" } }, 123 | }, 124 | right = { 125 | type = "list", 126 | kind = "Expression", 127 | } 128 | } 129 | }, 130 | LogicalExpression = { 131 | kind = "LogicalExpression", 132 | base = "Expression", 133 | properties = { 134 | operator = { 135 | type = "enum", 136 | values = { "and", "or" } 137 | }, 138 | left = "Expression", 139 | right = "Expression", 140 | } 141 | }, 142 | MemberExpression = { 143 | kind = "MemberExpression", 144 | base = "Expression", 145 | properties = { 146 | object = "Expression", 147 | property = "Expression", 148 | computed = { 149 | type = "literal", 150 | value = "boolean", 151 | default = false 152 | }, 153 | } 154 | }, 155 | CallExpression = { 156 | kind = "CallExpression", 157 | base = "Expression", 158 | properties = { 159 | callee = "Expression", 160 | arguments = { type = "list", kind = "Expression" }, 161 | } 162 | }, 163 | SendExpression = { 164 | kind = "SendExpression", 165 | base = "Expression", 166 | properties = { 167 | receiver = "Expression", 168 | method = "Identifier", 169 | arguments = { 170 | type = "list", 171 | kind = "Expression" 172 | } 173 | } 174 | }, 175 | Literal = { 176 | kind = "Literal", 177 | base = "Expression", 178 | properties = { 179 | value = { 180 | type = "choice", 181 | values = { 182 | { type = "literal", value = "string" }, 183 | { type = "literal", value = "number" }, 184 | { type = "literal", value = "nil" }, 185 | { type = "literal", value = "boolean" }, 186 | { type = "literal", value = "cdata" }, 187 | } 188 | }, 189 | } 190 | }, 191 | Table = { 192 | kind = "Table", 193 | base = "Expression", 194 | properties = { 195 | array_entries = { 196 | type = "list", 197 | kind = "Expression", 198 | }, 199 | hash_keys = { 200 | type = "list", 201 | kind = "Expression", 202 | }, 203 | hash_values = { 204 | type = "list", 205 | kind = "Expression", 206 | }, 207 | } 208 | }, 209 | ExpressionStatement = { 210 | kind = "ExpressionStatement", 211 | base = "Statement", 212 | properties = { 213 | expression = { 214 | type = "choice", 215 | values = { "Statement", "Expression" }, 216 | } 217 | } 218 | }, 219 | StatementsGroup = { 220 | kind = "StatementsGroup", 221 | base = "Statement", 222 | properties = { 223 | statements = { 224 | type = "list", 225 | kind = "Statement", 226 | } 227 | } 228 | }, 229 | EmptyStatement = { 230 | kind = "EmptyStatement", 231 | base = "Statement", 232 | properties = { }, 233 | }, 234 | DoStatement = { 235 | kind = "DoStatement", 236 | base = "Statement", 237 | properties = { 238 | body = { 239 | type = "list", 240 | kind = "Statement", 241 | } 242 | } 243 | }, 244 | IfStatement = { 245 | kind = "IfStatement", 246 | base = "Statement", 247 | properties = { 248 | tests = { 249 | type = "list", 250 | kind = "Expression", 251 | }, 252 | cons = { 253 | type = "list", 254 | kind = { type = "list", kind = "Statement" }, 255 | }, 256 | alternate = { 257 | type = "list", 258 | kind = "Statement", 259 | optional = true, 260 | } 261 | } 262 | }, 263 | LabelStatement = { 264 | kind = "LabelStatement", 265 | base = "Statement", 266 | properties = { 267 | label = { type = "literal", value = "string" }, 268 | } 269 | }, 270 | GotoStatement = { 271 | kind = "GotoStatement", 272 | base = "Statement", 273 | properties = { 274 | label = { type = "literal", value = "string" } 275 | } 276 | }, 277 | BreakStatement = { 278 | kind = "BreakStatement", 279 | base = "Statement", 280 | properties = { }, 281 | }, 282 | ReturnStatement = { 283 | kind = "ReturnStatement", 284 | base = "Statement", 285 | properties = { 286 | arguments = { 287 | type = "list", 288 | kind = "Expression" 289 | } 290 | } 291 | }, 292 | WhileStatement = { 293 | kind = "WhileStatement", 294 | base = "Statement", 295 | properties = { 296 | test = "Expression", 297 | body = { 298 | type = "list", 299 | kind = "Statement" 300 | } 301 | } 302 | }, 303 | RepeatStatement = { 304 | kind = "RepeatStatement", 305 | base = "Statement", 306 | properties = { 307 | test = "Expression", 308 | body = { 309 | type = "list", 310 | kind = "Statement", 311 | }, 312 | } 313 | }, 314 | ForInit = { 315 | kind = "ForInit", 316 | base = "Expression", 317 | properties = { 318 | id = "Identifier", 319 | value = "Expression", 320 | } 321 | }, 322 | ForStatement = { 323 | kind = "ForStatement", 324 | base = "Statement", 325 | properties = { 326 | init = "ForInit", 327 | last = "Expression", 328 | step = { 329 | type = "node", 330 | kind = "Expression", 331 | optional = true, 332 | }, 333 | body = { 334 | type = "list", 335 | kind = "Statement", 336 | }, 337 | } 338 | }, 339 | ForNames = { 340 | kind = "ForNames", 341 | base = "Expression", 342 | properties = { 343 | names = { 344 | type = "list", 345 | kind = "Identifier", 346 | } 347 | } 348 | }, 349 | ForInStatement = { 350 | kind = "ForInStatement", 351 | base = "Statement", 352 | properties = { 353 | namelist = "ForNames", 354 | explist = { 355 | type = "list", 356 | kind = "Expression" 357 | }, 358 | body = { 359 | type = "list", 360 | kind = "Statement", 361 | }, 362 | } 363 | }, 364 | LocalDeclaration = { 365 | kind = "LocalDeclaration", 366 | base = "Statement", 367 | properties = { 368 | names = { 369 | type = "list", 370 | kind = "Identifier" 371 | }, 372 | expressions = { 373 | type = "list", 374 | kind = "Expression" 375 | } 376 | } 377 | }, 378 | FunctionDeclaration = { 379 | kind = "FunctionDeclaration", 380 | base = "Statement", 381 | properties = { 382 | id = { 383 | type = "choice", 384 | values = { "MemberExpression", "Identifier" }, 385 | }, 386 | body = { 387 | type = "list", 388 | kind = "Statement", 389 | }, 390 | params = { 391 | type = "list", 392 | kind = "Identifier", 393 | }, 394 | vararg = { 395 | type = "literal", 396 | value = "boolean", 397 | default = false 398 | }, 399 | locald = { 400 | type = "literal", 401 | value = "boolean", 402 | default = false 403 | } 404 | } 405 | }, 406 | FunctionExpression = { 407 | kind = "FunctionExpression", 408 | base = "Expression", 409 | properties = { 410 | body = { 411 | type = "list", 412 | kind = "Statement", 413 | }, 414 | params = { 415 | type = "list", 416 | kind = "Identifier", 417 | }, 418 | vararg = { 419 | type = "literal", 420 | value = "boolean", 421 | default = false 422 | } 423 | } 424 | } 425 | } 426 | 427 | local check 428 | 429 | local function iskind(prop, tag) 430 | if type(prop) ~= "table" then 431 | return false 432 | end 433 | local meta = syntax[prop.kind] 434 | while meta do 435 | if meta.kind == tag then 436 | return true 437 | end 438 | meta = syntax[meta.base] 439 | end 440 | return false 441 | end 442 | 443 | local function isnode(prop) 444 | return iskind(prop, "Node") 445 | end 446 | 447 | local function kind2str(spec) 448 | if type(spec) == "string" then 449 | return spec 450 | elseif spec.type == "node" then 451 | return spec.kind 452 | elseif spec.type == "list" then 453 | local etype = kind2str(spec.kind) 454 | return "list of " .. etype 455 | elseif spec.type == "enum" then 456 | local ls = {} 457 | for i = 1, #spec.values do ls[i] = spec.values[i] end 458 | return table.concat(ls, ", ") 459 | elseif spec.type == "literal" then 460 | return "literal " .. spec.value 461 | elseif spec.type == "choice" then 462 | local ls = {} 463 | for i = 1, #spec.values do ls[i] = kind2str(spec.values[i]) end 464 | return table.concat(ls, "|") 465 | else 466 | error("internal error: invalid spec type") 467 | end 468 | end 469 | 470 | local function check_node(tag, prop) 471 | if not isnode(prop) then 472 | return false, "expected Node" 473 | end 474 | if not iskind(prop, tag) then 475 | return false, "expected " .. tag 476 | end 477 | return true 478 | end 479 | 480 | local function check_list(spec, prop) 481 | if type(prop) ~= "table" then 482 | return false, "expected list of "..kind2str(spec.kind).." (got "..type(prop)..")" 483 | end 484 | if isnode(prop) then 485 | return false, "expected list of "..kind2str(spec.kind).." (got node)" 486 | end 487 | for i=1, #prop do 488 | local ok, err = check(spec.kind, prop[i]) 489 | if not ok then 490 | return false, err.." (got "..prop[i].kind..")" 491 | end 492 | end 493 | return true 494 | end 495 | 496 | local function check_enum(spec, prop) 497 | for i=1, #spec.values do 498 | if prop == spec.values[i] then return true end 499 | end 500 | return false, "expected one of "..kind2str(spec).." (got '"..tostring(prop).."')" 501 | end 502 | 503 | local function check_literal(spec, prop) 504 | assert(type(spec.value) == "string") 505 | if type(prop) ~= spec.value then 506 | return false, "expected "..spec.value.." (got "..type(prop)..")" 507 | end 508 | return true 509 | end 510 | 511 | local function check_choice(spec, prop) 512 | for i = 1, #spec.values do 513 | if check(spec.values[i], prop) then 514 | return true 515 | end 516 | end 517 | return false, "expected one of "..kind2str(spec).." (got '"..tostring(prop).."')" 518 | end 519 | 520 | function check(spec, prop) 521 | if type(spec) == "string" then 522 | return check_node(spec, prop) 523 | elseif spec.type == "node" then 524 | return check_node(spec.kind, prop) 525 | elseif spec.type == "list" then 526 | return check_list(spec, prop) 527 | elseif spec.type == "enum" then 528 | return check_enum(spec, prop) 529 | elseif spec.type == "literal" then 530 | return check_literal(spec, prop) 531 | elseif spec.type == "choice" then 532 | return check_choice(spec, prop) 533 | else 534 | error("internal error: invalid spec type") 535 | end 536 | end 537 | 538 | local function validate(meta, node) 539 | if meta == nil then 540 | error("unknown node kind: "..node.kind) 541 | end 542 | for name, spec in pairs(meta.properties) do 543 | if node[name] == nil and type(spec.default) ~= 'nil' then 544 | node[name] = spec.default 545 | end 546 | local prop = node[name] 547 | if prop ~= nil or not spec.optional then 548 | local ok, er = check(spec, prop) 549 | if not ok then 550 | error(er.." for "..(node.kind or "?").."."..name) 551 | end 552 | end 553 | end 554 | return node 555 | end 556 | 557 | local function build(kind, props) 558 | local meta = syntax[kind] 559 | props.kind = kind 560 | return validate(meta, props) 561 | end 562 | 563 | return { 564 | syntax = syntax, 565 | build = build, 566 | } 567 | -------------------------------------------------------------------------------- /lang/compile.lua: -------------------------------------------------------------------------------- 1 | local lex_setup = require('lang.lexer') 2 | local parse = require('lang.parser') 3 | local lua_ast = require('lang.lua_ast') 4 | local reader = require('lang.reader') 5 | 6 | -- Two kind of backend can be used to generate the code from the AST: 7 | -- - "generator", generates LuaJIT bytecode 8 | -- - "luacode_generator", generates Lua code 9 | -- 10 | -- Both can be used interchangeably, they take the AST tree and produce 11 | -- a string that can be passed to the function "loadstring". 12 | -- In the case of the bytecode generator the string will be actually a 13 | -- binary blob that corresponds to the generated bytecode. 14 | 15 | 16 | local function lang_toolkit_error(msg) 17 | if string.sub(msg, 1, 9) == "LLT-ERROR" then 18 | return false, "luajit-lang-toolkit: " .. string.sub(msg, 10) 19 | else 20 | error(msg) 21 | end 22 | end 23 | 24 | local function compile(reader, filename, options) 25 | local generator 26 | if options and options.code then 27 | generator = require('lang.luacode_generator') 28 | else 29 | generator = require('lang.generator') 30 | end 31 | local ls = lex_setup(reader, filename) 32 | local ast_builder = lua_ast.New() 33 | local parse_success, ast_tree = pcall(parse, ast_builder, ls) 34 | if not parse_success then 35 | return lang_toolkit_error(ast_tree) 36 | end 37 | local success, luacode = pcall(generator, ast_tree, filename) 38 | if not success then 39 | return lang_toolkit_error(luacode) 40 | end 41 | return true, luacode 42 | end 43 | 44 | local function lang_loadstring(src, filename, options) 45 | return compile(reader.string(src), filename or "stdin", options) 46 | end 47 | 48 | local function lang_loadfile(filename, options) 49 | return compile(reader.file(filename), filename or "stdin", options) 50 | end 51 | 52 | return { string = lang_loadstring, file = lang_loadfile } 53 | -------------------------------------------------------------------------------- /lang/id_generator.lua: -------------------------------------------------------------------------------- 1 | local function unique_name(variables, name) 2 | if variables:lookup(name) ~= nil then 3 | local prefix, index = string.match(name, "^(.+)(%d+)$") 4 | if not prefix then 5 | prefix, index = name, 1 6 | else 7 | index = tonumber(index) + 1 8 | end 9 | local test_name = prefix .. tostring(index) 10 | while variables:lookup(test_name) ~= nil do 11 | index = index + 1 12 | test_name = prefix .. tostring(index) 13 | end 14 | return test_name 15 | else 16 | return name 17 | end 18 | end 19 | 20 | local function pseudo(name) 21 | return '@' .. name 22 | end 23 | 24 | local function pseudo_match(pseudo_name) 25 | return string.match(pseudo_name, "^@(.+)$") 26 | end 27 | 28 | local function genid(variables, name) 29 | local pname = pseudo(name or "_") 30 | local uname = unique_name(variables, pname) 31 | return variables:declare(uname) 32 | end 33 | 34 | local function normalize(variables, raw_name) 35 | local name = pseudo_match(raw_name) 36 | local uname = unique_name(variables, name) 37 | return uname 38 | end 39 | 40 | local function close_gen_variables(variables) 41 | local vars = variables.current.vars 42 | for i = 1, #vars do 43 | local id = vars[i] 44 | if pseudo_match(id.name) then 45 | id.name = normalize(variables, id.name) 46 | end 47 | end 48 | end 49 | 50 | return { genid = genid, close_gen_variables = close_gen_variables } 51 | -------------------------------------------------------------------------------- /lang/lexer.lua: -------------------------------------------------------------------------------- 1 | local ffi = require('ffi') 2 | 3 | local band = bit.band 4 | local strsub, strbyte, strchar = string.sub, string.byte, string.char 5 | 6 | local ASCII_0, ASCII_9 = 48, 57 7 | local ASCII_a, ASCII_f, ASCII_z = 97, 102, 122 8 | local ASCII_A, ASCII_Z = 65, 90 9 | 10 | local END_OF_STREAM = -1 11 | 12 | local ReservedKeyword = {['and'] = 1, ['break'] = 2, ['do'] = 3, ['else'] = 4, ['elseif'] = 5, ['end'] = 6, ['false'] = 7, ['for'] = 8, ['function'] = 9, ['goto'] = 10, ['if'] = 11, ['in'] = 12, ['local'] = 13, ['nil'] = 14, ['not'] = 15, ['or'] = 16, ['repeat'] = 17, ['return'] = 18, ['then'] = 19, ['true'] = 20, ['until'] = 21, ['while'] = 22 } 13 | 14 | local uint64, int64 = ffi.typeof('uint64_t'), ffi.typeof('int64_t') 15 | local complex = ffi.typeof('complex') 16 | 17 | local TokenSymbol = { TK_ge = '>=', TK_le = '<=' , TK_concat = '..', TK_eq = '==', TK_ne = '~=', TK_eof = '' } 18 | 19 | local function token2str(tok) 20 | if string.match(tok, "^TK_") then 21 | return TokenSymbol[tok] or string.sub(tok, 4) 22 | else 23 | return tok 24 | end 25 | end 26 | 27 | local function error_lex(chunkname, tok, line, em, ...) 28 | local emfmt = string.format(em, ...) 29 | local msg = string.format("%s:%d: %s", chunkname, line, emfmt) 30 | if tok then 31 | msg = string.format("%s near '%s'", msg, tok) 32 | end 33 | error("LLT-ERROR" .. msg, 0) 34 | end 35 | 36 | local function lex_error(ls, token, em, ...) 37 | local tok 38 | if token == 'TK_name' or token == 'TK_string' or token == 'TK_number' then 39 | tok = ls.save_buf 40 | elseif token then 41 | tok = token2str(token) 42 | end 43 | error_lex(ls.chunkname, tok, ls.linenumber, em, ...) 44 | end 45 | 46 | local function char_isident(c) 47 | if type(c) == 'string' then 48 | local b = strbyte(c) 49 | if b >= ASCII_0 and b <= ASCII_9 then 50 | return true 51 | elseif b >= ASCII_a and b <= ASCII_z then 52 | return true 53 | elseif b >= ASCII_A and b <= ASCII_Z then 54 | return true 55 | else 56 | return (c == '_') 57 | end 58 | end 59 | return false 60 | end 61 | 62 | local function char_isdigit(c) 63 | if type(c) == 'string' then 64 | local b = strbyte(c) 65 | return b >= ASCII_0 and b <= ASCII_9 66 | end 67 | return false 68 | end 69 | 70 | local function char_isspace(c) 71 | local b = strbyte(c) 72 | return b >= 9 and b <= 13 or b == 32 73 | end 74 | 75 | local function byte(ls, n) 76 | local k = ls.p + n 77 | return strsub(ls.data, k, k) 78 | end 79 | 80 | local function skip(ls, n) 81 | ls.n = ls.n - n 82 | ls.p = ls.p + n 83 | end 84 | 85 | local function pop(ls) 86 | local k = ls.p 87 | local c = strsub(ls.data, k, k) 88 | ls.p = k + 1 89 | ls.n = ls.n - 1 90 | return c 91 | end 92 | 93 | local function fillbuf(ls) 94 | local data = ls:read_func() 95 | if not data then 96 | return END_OF_STREAM 97 | end 98 | ls.data, ls.n, ls.p = data, #data, 1 99 | return pop(ls) 100 | end 101 | 102 | local function nextchar(ls) 103 | local c = ls.n > 0 and pop(ls) or fillbuf(ls) 104 | ls.current = c 105 | return c 106 | end 107 | 108 | local function curr_is_newline(ls) 109 | local c = ls.current 110 | return (c == '\n' or c == '\r') 111 | end 112 | 113 | local function resetbuf(ls) 114 | ls.save_buf = '' 115 | end 116 | 117 | local function resetbuf_tospace(ls) 118 | ls.space_buf = ls.space_buf .. ls.save_buf 119 | ls.save_buf = '' 120 | end 121 | 122 | local function spaceadd(ls, str) 123 | ls.space_buf = ls.space_buf .. str 124 | end 125 | 126 | local function save(ls, c) 127 | ls.save_buf = ls.save_buf .. c 128 | end 129 | 130 | local function savespace_and_next(ls) 131 | ls.space_buf = ls.space_buf .. ls.current 132 | nextchar(ls) 133 | end 134 | 135 | local function save_and_next(ls) 136 | ls.save_buf = ls.save_buf .. ls.current 137 | nextchar(ls) 138 | end 139 | 140 | local function get_string(ls, init_skip, end_skip) 141 | return strsub(ls.save_buf, init_skip + 1, - (end_skip + 1)) 142 | end 143 | 144 | local function get_space_string(ls) 145 | local s = ls.space_buf 146 | ls.space_buf = '' 147 | return s 148 | end 149 | 150 | local function inclinenumber(ls) 151 | local old = ls.current 152 | savespace_and_next(ls) -- skip `\n' or `\r' 153 | if curr_is_newline(ls) and ls.current ~= old then 154 | savespace_and_next(ls) -- skip `\n\r' or `\r\n' 155 | end 156 | ls.linenumber = ls.linenumber + 1 157 | end 158 | 159 | local function skip_sep(ls) 160 | local count = 0 161 | local s = ls.current 162 | assert(s == '[' or s == ']') 163 | save_and_next(ls) 164 | while ls.current == '=' do 165 | save_and_next(ls) 166 | count = count + 1 167 | end 168 | return ls.current == s and count or (-count - 1) 169 | end 170 | 171 | local function build_64int(str) 172 | local u = str[#str - 2] 173 | local x = (u == 117 and uint64(0) or int64(0)) 174 | local i = 1 175 | while str[i] >= ASCII_0 and str[i] <= ASCII_9 do 176 | x = 10 * x + (str[i] - ASCII_0) 177 | i = i + 1 178 | end 179 | return x 180 | end 181 | 182 | -- Only lower case letters are accepted. 183 | local function byte_to_hexdigit(b) 184 | if b >= ASCII_0 and b <= ASCII_9 then 185 | return b - ASCII_0 186 | elseif b >= ASCII_a and b <= ASCII_f then 187 | return 10 + (b - ASCII_a) 188 | else 189 | return -1 190 | end 191 | end 192 | 193 | local function build_64hex(str) 194 | local u = str[#str - 2] 195 | local x = (u == 117 and uint64(0) or int64(0)) 196 | local i = 3 197 | while str[i] do 198 | local n = byte_to_hexdigit(str[i]) 199 | if n < 0 then break end 200 | x = 16 * x + n 201 | i = i + 1 202 | end 203 | return x 204 | end 205 | 206 | local function strnumdump(str) 207 | local t = {} 208 | for i = 1, #str do 209 | local c = strsub(str, i, i) 210 | if char_isident(c) then 211 | t[i] = strbyte(c) 212 | else 213 | return nil 214 | end 215 | end 216 | return t 217 | end 218 | 219 | local function lex_number(ls) 220 | local lower = string.lower 221 | local xp = 'e' 222 | local c = ls.current 223 | if c == '0' then 224 | save_and_next(ls) 225 | local xc = ls.current 226 | if xc == 'x' or xc == 'X' then xp = 'p' end 227 | end 228 | while char_isident(ls.current) or ls.current == '.' or 229 | ((ls.current == '-' or ls.current == '+') and lower(c) == xp) do 230 | c = lower(ls.current) 231 | save(ls, c) 232 | nextchar(ls) 233 | end 234 | local str = ls.save_buf 235 | local x 236 | if strsub(str, -1, -1) == 'i' then 237 | local img = tonumber(strsub(str, 1, -2)) 238 | if img then x = complex(0, img) end 239 | elseif strsub(str, -2, -1) == 'll' then 240 | local t = strnumdump(str) 241 | if t then 242 | x = xp == 'e' and build_64int(t) or build_64hex(t) 243 | end 244 | else 245 | x = tonumber(str) 246 | end 247 | if x then 248 | return x 249 | else 250 | lex_error(ls, 'TK_number', "malformed number") 251 | end 252 | end 253 | 254 | local function read_long_string(ls, sep, ret_value) 255 | save_and_next(ls) -- skip 2nd `[' 256 | if curr_is_newline(ls) then -- string starts with a newline? 257 | inclinenumber(ls) -- skip it 258 | end 259 | while true do 260 | local c = ls.current 261 | if c == END_OF_STREAM then 262 | lex_error(ls, 'TK_eof', ret_value and "unfinished long string" or "unfinished long comment") 263 | elseif c == ']' then 264 | if skip_sep(ls) == sep then 265 | save_and_next(ls) -- skip 2nd `[' 266 | break 267 | end 268 | elseif c == '\n' or c == '\r' then 269 | save(ls, '\n') 270 | inclinenumber(ls) 271 | if not ret_value then 272 | resetbuf(ls) -- avoid wasting space 273 | end 274 | else 275 | if ret_value then save_and_next(ls) 276 | else nextchar(ls) end 277 | end 278 | end 279 | if ret_value then 280 | return get_string(ls, 2 + sep, 2 + sep) 281 | end 282 | end 283 | 284 | local Escapes = { 285 | a = '\a', b = '\b', f = '\f', n = '\n', r = '\r', t = '\t', 286 | v = '\v', 287 | } 288 | 289 | local function hex_char(c) 290 | if string.match(c, '^%x') then 291 | local b = band(strbyte(c), 15) 292 | if not char_isdigit(c) then b = b + 9 end 293 | return b 294 | end 295 | end 296 | 297 | local function read_escape_char(ls) 298 | local c = nextchar(ls) -- Skip the '\\'. 299 | local esc = Escapes[c] 300 | if esc then 301 | save(ls, esc) 302 | nextchar(ls) 303 | elseif c == 'x' then -- Hexadecimal escape '\xXX'. 304 | local ch1 = hex_char(nextchar(ls)) 305 | local hc 306 | if ch1 then 307 | local ch2 = hex_char(nextchar(ls)) 308 | if ch2 then 309 | hc = strchar(ch1 * 16 + ch2) 310 | end 311 | end 312 | if not hc then 313 | lex_error(ls, 'TK_string', "invalid escape sequence") 314 | end 315 | save(ls, hc) 316 | nextchar(ls) 317 | elseif c == 'z' then -- Skip whitespace. 318 | nextchar(ls) 319 | while char_isspace(ls.current) do 320 | if curr_is_newline(ls) then inclinenumber(ls) else nextchar(ls) end 321 | end 322 | elseif c == '\n' or c == '\r' then 323 | save(ls, '\n') 324 | inclinenumber(ls) 325 | elseif c == '\\' or c == '\"' or c == '\'' then 326 | save(ls, c) 327 | nextchar(ls) 328 | elseif c == END_OF_STREAM then 329 | else 330 | if not char_isdigit(c) then 331 | lex_error(ls, 'TK_string', "invalid escape sequence") 332 | end 333 | local bc = band(strbyte(c), 15) -- Decimal escape '\ddd'. 334 | if char_isdigit(nextchar(ls)) then 335 | bc = bc * 10 + band(strbyte(ls.current), 15) 336 | if char_isdigit(nextchar(ls)) then 337 | bc = bc * 10 + band(strbyte(ls.current), 15) 338 | if bc > 255 then 339 | lex_error(ls, 'TK_string', "invalid escape sequence") 340 | end 341 | nextchar(ls) 342 | end 343 | end 344 | save(ls, strchar(bc)) 345 | end 346 | end 347 | 348 | local function read_string(ls, delim) 349 | save_and_next(ls) 350 | while ls.current ~= delim do 351 | local c = ls.current 352 | if c == END_OF_STREAM then 353 | lex_error(ls, 'TK_eof', "unfinished string") 354 | elseif c == '\n' or c == '\r' then 355 | lex_error(ls, 'TK_string', "unfinished string") 356 | elseif c == '\\' then 357 | read_escape_char(ls) 358 | else 359 | save_and_next(ls) 360 | end 361 | end 362 | save_and_next(ls) -- skip delimiter 363 | return get_string(ls, 1, 1) 364 | end 365 | 366 | local function skip_line(ls) 367 | while not curr_is_newline(ls) and ls.current ~= END_OF_STREAM do 368 | savespace_and_next(ls) 369 | end 370 | end 371 | 372 | local function llex(ls) 373 | resetbuf(ls) 374 | while true do 375 | local current = ls.current 376 | if char_isident(current) then 377 | if char_isdigit(current) then -- Numeric literal. 378 | return 'TK_number', lex_number(ls) 379 | end 380 | repeat 381 | save_and_next(ls) 382 | until not char_isident(ls.current) 383 | local s = get_string(ls, 0, 0) 384 | local reserved = ReservedKeyword[s] 385 | if reserved then 386 | return 'TK_' .. s 387 | else 388 | return 'TK_name', s 389 | end 390 | end 391 | if current == '\n' or current == '\r' then 392 | inclinenumber(ls) 393 | elseif current == ' ' or current == '\t' or current == '\b' or current == '\f' then 394 | savespace_and_next(ls) 395 | -- nextchar(ls) 396 | elseif current == '-' then 397 | nextchar(ls) 398 | if ls.current ~= '-' then return '-' end 399 | -- else is a comment 400 | nextchar(ls) 401 | spaceadd(ls, '--') 402 | if ls.current == '[' then 403 | local sep = skip_sep(ls) 404 | resetbuf_tospace(ls) -- `skip_sep' may dirty the buffer 405 | if sep >= 0 then 406 | read_long_string(ls, sep, false) -- long comment 407 | resetbuf_tospace(ls) 408 | else 409 | skip_line(ls) 410 | end 411 | else 412 | skip_line(ls) 413 | end 414 | elseif current == '[' then 415 | local sep = skip_sep(ls) 416 | if sep >= 0 then 417 | local str = read_long_string(ls, sep, true) 418 | return 'TK_string', str 419 | elseif sep == -1 then 420 | return '[' 421 | else 422 | lex_error(ls, 'TK_string', "delimiter error") 423 | end 424 | elseif current == '=' then 425 | nextchar(ls) 426 | if ls.current ~= '=' then return '=' else nextchar(ls); return 'TK_eq' end 427 | elseif current == '<' then 428 | nextchar(ls) 429 | if ls.current ~= '=' then return '<' else nextchar(ls); return 'TK_le' end 430 | elseif current == '>' then 431 | nextchar(ls) 432 | if ls.current ~= '=' then return '>' else nextchar(ls); return 'TK_ge' end 433 | elseif current == '~' then 434 | nextchar(ls) 435 | if ls.current ~= '=' then return '~' else nextchar(ls); return 'TK_ne' end 436 | elseif current == ':' then 437 | nextchar(ls) 438 | if ls.current ~= ':' then return ':' else nextchar(ls); return 'TK_label' end 439 | elseif current == '"' or current == "'" then 440 | local str = read_string(ls, current) 441 | return 'TK_string', str 442 | elseif current == '.' then 443 | save_and_next(ls) 444 | if ls.current == '.' then 445 | nextchar(ls) 446 | if ls.current == '.' then 447 | nextchar(ls) 448 | return 'TK_dots' -- ... 449 | end 450 | return 'TK_concat' -- .. 451 | elseif not char_isdigit(ls.current) then 452 | return '.' 453 | else 454 | return 'TK_number', lex_number(ls) 455 | end 456 | elseif current == END_OF_STREAM then 457 | return 'TK_eof' 458 | else 459 | nextchar(ls) 460 | return current -- Single-char tokens (+ - / ...). 461 | end 462 | end 463 | end 464 | 465 | local Lexer = { 466 | token2str = token2str, 467 | error = lex_error, 468 | } 469 | 470 | function Lexer.next(ls) 471 | ls.lastline = ls.linenumber 472 | if ls.tklookahead == 'TK_eof' then -- No lookahead token? 473 | ls.token, ls.tokenval = llex(ls) -- Get nextchar token. 474 | ls.space = get_space_string(ls) 475 | else 476 | ls.token, ls.tokenval = ls.tklookahead, ls.tklookaheadval 477 | ls.space = ls.spaceahead 478 | ls.tklookahead = 'TK_eof' 479 | end 480 | end 481 | 482 | function Lexer.lookahead(ls) 483 | assert(ls.tklookahead == 'TK_eof') 484 | ls.tklookahead, ls.tklookaheadval = llex(ls) 485 | ls.spaceahead = get_space_string(ls) 486 | return ls.tklookahead 487 | end 488 | 489 | local LexerClass = { __index = Lexer } 490 | 491 | local function lex_setup(read_func, chunkname) 492 | local header = false 493 | local ls = { 494 | n = 0, 495 | tklookahead = 'TK_eof', -- No look-ahead token. 496 | linenumber = 1, 497 | lastline = 1, 498 | read_func = read_func, 499 | chunkname = chunkname, 500 | space_buf = '' 501 | } 502 | nextchar(ls) 503 | if ls.current == '\xef' and ls.n >= 2 and 504 | byte(ls, 0) == '\xbb' and byte(ls, 1) == '\xbf' then -- Skip UTF-8 BOM (if buffered). 505 | ls.n = ls.n - 2 506 | ls.p = ls.p + 2 507 | nextchar(ls) 508 | header = true 509 | end 510 | if ls.current == '#' then 511 | repeat 512 | nextchar(ls) 513 | if ls.current == END_OF_STREAM then return ls end 514 | until curr_is_newline(ls) 515 | inclinenumber(ls) 516 | header = true 517 | end 518 | return setmetatable(ls, LexerClass) 519 | end 520 | 521 | return lex_setup 522 | -------------------------------------------------------------------------------- /lang/lua_ast.lua: -------------------------------------------------------------------------------- 1 | local id_generator = require("lang.id_generator") 2 | 3 | local function build(kind, node) 4 | node.kind = kind 5 | return node 6 | end 7 | 8 | local function ident(name, line) 9 | return build("Identifier", { name = name, line = line }) 10 | end 11 | 12 | local function literal(value, line) 13 | return build("Literal", { value = value, line = line }) 14 | end 15 | 16 | local function field(obj, name, line) 17 | return build("MemberExpression", { object = obj, property = ident(name), computed = false, line = line }) 18 | end 19 | 20 | local function logical_binop(op, left, right, line) 21 | return build("LogicalExpression", { operator = op, left = left, right = right, line = line }) 22 | end 23 | 24 | local function binop(op, left, right, line) 25 | return build("BinaryExpression", { operator = op, left = left, right = right, line = line }) 26 | end 27 | 28 | local function empty_table(line) 29 | return build("Table", { keyvals = { }, line = line }) 30 | end 31 | 32 | local function does_multi_return(expr) 33 | local k = expr.kind 34 | return k == "CallExpression" or k == "SendExpression" or k == "Vararg" 35 | end 36 | 37 | local AST = { } 38 | 39 | local function func_decl(id, body, params, vararg, locald, firstline, lastline) 40 | return build("FunctionDeclaration", { 41 | id = id, 42 | body = body, 43 | params = params, 44 | vararg = vararg, 45 | locald = locald, 46 | firstline = firstline, 47 | lastline = lastline, 48 | line = firstline, 49 | }) 50 | end 51 | 52 | local function func_expr(body, params, vararg, firstline, lastline) 53 | return build("FunctionExpression", { body = body, params = params, vararg = vararg, firstline = firstline, lastline = lastline }) 54 | end 55 | 56 | function AST.expr_function(ast, args, body, proto) 57 | return func_expr(body, args, proto.varargs, proto.firstline, proto.lastline) 58 | end 59 | 60 | function AST.local_function_decl(ast, name, args, body, proto) 61 | local id = ast:var_declare(name) 62 | return func_decl(id, body, args, proto.varargs, true, proto.firstline, proto.lastline) 63 | end 64 | 65 | function AST.function_decl(ast, path, args, body, proto) 66 | return func_decl(path, body, args, proto.varargs, false, proto.firstline, proto.lastline) 67 | end 68 | 69 | function AST.func_parameters_decl(ast, args, vararg) 70 | local params = {} 71 | for i = 1, #args do 72 | params[i] = ast:var_declare(args[i]) 73 | end 74 | if vararg then 75 | params[#params + 1] = ast:expr_vararg() 76 | end 77 | return params 78 | end 79 | 80 | function AST.chunk(ast, body, chunkname, firstline, lastline) 81 | return build("Chunk", { body = body, chunkname = chunkname, firstline = firstline, lastline = lastline }) 82 | end 83 | 84 | function AST.local_decl(ast, vlist, exps, line) 85 | local ids = {} 86 | for k = 1, #vlist do 87 | ids[k] = ast:var_declare(vlist[k]) 88 | end 89 | return build("LocalDeclaration", { names = ids, expressions = exps, line = line }) 90 | end 91 | 92 | function AST.assignment_expr(ast, vars, exps, line) 93 | return build("AssignmentExpression", { left = vars, right = exps, line = line }) 94 | end 95 | 96 | function AST.expr_index(ast, v, index, line) 97 | return build("MemberExpression", { object = v, property = index, computed = true, line = line }) 98 | end 99 | 100 | function AST.expr_property(ast, v, prop, line) 101 | local index = ident(prop, line) 102 | return build("MemberExpression", { object = v, property = index, computed = false, line = line }) 103 | end 104 | 105 | function AST.literal(ast, val) 106 | return build("Literal", { value = val }) 107 | end 108 | 109 | function AST.expr_vararg(ast) 110 | return build("Vararg", { }) 111 | end 112 | 113 | function AST.expr_brackets(ast, expr) 114 | expr.bracketed = true 115 | return expr 116 | end 117 | 118 | function AST.set_expr_last(ast, expr) 119 | if expr.bracketed and does_multi_return(expr) then 120 | expr.bracketed = nil 121 | return build("ExpressionValue", { value = expr }) 122 | else 123 | return expr 124 | end 125 | end 126 | 127 | function AST.expr_table(ast, keyvals, line) 128 | return build("Table", { keyvals = keyvals, line = line }) 129 | end 130 | 131 | function AST.expr_unop(ast, op, v, line) 132 | return build("UnaryExpression", { operator = op, argument = v, line = line }) 133 | end 134 | 135 | local function concat_append(ts, node) 136 | local n = #ts 137 | if node.kind == "ConcatenateExpression" then 138 | for k = 1, #node.terms do ts[n + k] = node.terms[k] end 139 | else 140 | ts[n + 1] = node 141 | end 142 | end 143 | 144 | function AST.expr_binop(ast, op, expa, expb, line) 145 | local binop_body = (op ~= '..' and { operator = op, left = expa, right = expb, line = line }) 146 | if binop_body then 147 | if op == 'and' or op == 'or' then 148 | return build("LogicalExpression", binop_body) 149 | else 150 | return build("BinaryExpression", binop_body) 151 | end 152 | else 153 | local terms = { } 154 | concat_append(terms, expa) 155 | concat_append(terms, expb) 156 | return build("ConcatenateExpression", { terms = terms, line = expa.line }) 157 | end 158 | end 159 | 160 | function AST.identifier(ast, name) 161 | return ident(name) 162 | end 163 | 164 | function AST.expr_method_call(ast, v, key, args, line) 165 | local m = ident(key) 166 | return build("SendExpression", { receiver = v, method = m, arguments = args, line = line }) 167 | end 168 | 169 | function AST.expr_function_call(ast, v, args, line) 170 | return build("CallExpression", { callee = v, arguments = args, line = line }) 171 | end 172 | 173 | function AST.return_stmt(ast, exps, line) 174 | return build("ReturnStatement", { arguments = exps, line = line }) 175 | end 176 | 177 | function AST.break_stmt(ast, line) 178 | return build("BreakStatement", { line = line }) 179 | end 180 | 181 | function AST.label_stmt(ast, name, line) 182 | return build("LabelStatement", { label = name, line = line }) 183 | end 184 | 185 | function AST.new_statement_expr(ast, expr, line) 186 | return build("ExpressionStatement", { expression = expr, line = line }) 187 | end 188 | 189 | function AST.if_stmt(ast, tests, cons, else_branch, line) 190 | return build("IfStatement", { tests = tests, cons = cons, alternate = else_branch, line = line }) 191 | end 192 | 193 | function AST.do_stmt(ast, body, line, lastline) 194 | return build("DoStatement", { body = body, line = line, lastline = lastline}) 195 | end 196 | 197 | function AST.while_stmt(ast, test, body, line, lastline) 198 | return build("WhileStatement", { test = test, body = body, line = line, lastline = lastline }) 199 | end 200 | 201 | function AST.repeat_stmt(ast, test, body, line, lastline) 202 | return build("RepeatStatement", { test = test, body = body, line = line, lastline = lastline }) 203 | end 204 | 205 | function AST.for_stmt(ast, var, init, last, step, body, line, lastline) 206 | local for_init = build("ForInit", { id = var, value = init, line = line }) 207 | return build("ForStatement", { init = for_init, last = last, step = step, body = body, line = line, lastline = lastline }) 208 | end 209 | 210 | function AST.for_iter_stmt(ast, vars, exps, body, line, lastline) 211 | local names = build("ForNames", { names = vars, line = line }) 212 | return build("ForInStatement", { namelist = names, explist = exps, body = body, line = line, lastline = lastline }) 213 | end 214 | 215 | function AST.goto_stmt(ast, name, line) 216 | return build("GotoStatement", { label = name, line = line }) 217 | end 218 | 219 | function AST.var_declare(ast, name) 220 | local id = ident(name) 221 | ast.variables:declare(name) 222 | return id 223 | end 224 | 225 | function AST.genid(ast, name) 226 | return id_generator.genid(ast.variables, name) 227 | end 228 | 229 | function AST.fscope_begin(ast) 230 | ast.variables:scope_enter() 231 | end 232 | 233 | function AST.fscope_end(ast) 234 | -- It is important to call id_generator.close_gen_variables before 235 | -- leaving the "variables" scope. 236 | id_generator.close_gen_variables(ast.variables) 237 | ast.variables:scope_exit() 238 | end 239 | 240 | local ASTClass = { __index = AST } 241 | 242 | local function new_scope(parent_scope) 243 | return { 244 | vars = { }, 245 | parent = parent_scope, 246 | } 247 | end 248 | 249 | local function new_variables_registry(create, match) 250 | local declare = function(self, name) 251 | local vars = self.current.vars 252 | local entry = create(name) 253 | vars[#vars+1] = entry 254 | return entry 255 | end 256 | 257 | local scope_enter = function(self) 258 | self.current = new_scope(self.current) 259 | end 260 | 261 | local scope_exit = function(self) 262 | self.current = self.current.parent 263 | end 264 | 265 | local lookup = function(self, name) 266 | local scope = self.current 267 | while scope do 268 | for i = 1, #scope.vars do 269 | if match(scope.vars[i], name) then 270 | return scope 271 | end 272 | end 273 | scope = scope.parent 274 | end 275 | end 276 | 277 | return { declare = declare, scope_enter = scope_enter, scope_exit = scope_exit, lookup = lookup } 278 | end 279 | 280 | local function new_ast() 281 | local match_id_name = function(id, name) return id.name == name end 282 | local vars = new_variables_registry(ident, match_id_name) 283 | return setmetatable({ variables = vars }, ASTClass) 284 | end 285 | 286 | return { New = new_ast } 287 | -------------------------------------------------------------------------------- /lang/luacode_generator.lua: -------------------------------------------------------------------------------- 1 | -- 2 | -- luacode_generator.lua 3 | -- 4 | -- This file is part of the LuaJIT Language Toolkit. 5 | -- 6 | -- Module to generate the Lua code that corresponds to a given Lua AST Tree. 7 | -- Can be used as an alternative to the bytecode generator. 8 | 9 | local operator = require("lang.operator") 10 | 11 | local strbyte, strsub = string.byte, string.sub 12 | 13 | local LuaReservedKeyword = {['and'] = 1, ['break'] = 2, ['do'] = 3, ['else'] = 4, ['elseif'] = 5, ['end'] = 6, ['false'] = 7, ['for'] = 8, ['function'] = 9, ['goto'] = 10, ['if'] = 11, ['in'] = 12, ['local'] = 13, ['nil'] = 14, ['not'] = 15, ['or'] = 16, ['repeat'] = 17, ['return'] = 18, ['then'] = 19, ['true'] = 20, ['until'] = 21, ['while'] = 22 } 14 | 15 | local ASCII_0, ASCII_9 = 48, 57 16 | local ASCII_a, ASCII_z = 97, 122 17 | local ASCII_A, ASCII_Z = 65, 90 18 | 19 | local function char_isletter(c) 20 | local b = strbyte(c) 21 | if b >= ASCII_a and b <= ASCII_z then 22 | return true 23 | elseif b >= ASCII_A and b <= ASCII_Z then 24 | return true 25 | else 26 | return (c == '_') 27 | end 28 | end 29 | 30 | local function char_isdigit(c) 31 | local b = strbyte(c) 32 | return b >= ASCII_0 and b <= ASCII_9 33 | end 34 | 35 | local function replace_cc(c) 36 | local esc = { 37 | ['\a'] = [[\a]], ['\b'] = [[\b]], ['\f'] = [[\f]], ['\n'] = [[\n]], ['\r'] = [[\r]], ['\t'] = [[\t]], ['\v'] = [[\v]] 38 | } 39 | return esc[c] and esc[c] or ('\\' .. string.format("%d", string.byte(c))) 40 | end 41 | 42 | local function escape(s) 43 | s = string.gsub(s, "[\"\\]", "\\%1") 44 | return string.gsub(s, "%c", replace_cc) 45 | end 46 | 47 | local StatementRule = { } 48 | local ExpressionRule = { } 49 | 50 | local concat = table.concat 51 | local format = string.format 52 | 53 | local function is_string(node) 54 | return node.kind == "Literal" and type(node.value) == "string" 55 | end 56 | 57 | local function is_const(node, val) 58 | return node.kind == "Literal" and node.value == val 59 | end 60 | 61 | local function is_literal(node) 62 | local k = node.kind 63 | return (k == "Literal" or k == "Table") 64 | end 65 | 66 | local function string_is_ident(str) 67 | local c = strsub(str, 1, 1) 68 | if c == '' or not char_isletter(c) then 69 | return false 70 | end 71 | for k = 2, #str do 72 | c = strsub(str, k, k) 73 | if not char_isletter(c) and not char_isdigit(c) then 74 | return false 75 | end 76 | end 77 | return not LuaReservedKeyword[str] 78 | end 79 | 80 | local function comma_sep_list(ls, f) 81 | local strls 82 | if f then 83 | strls = { } 84 | for k = 1, #ls do strls[k] = f(ls[k]) end 85 | else 86 | strls = ls 87 | end 88 | return concat(strls, ", ") 89 | end 90 | 91 | local function as_parameter(node) 92 | return node.kind == "Vararg" and "..." or node.name 93 | end 94 | 95 | function ExpressionRule:Identifier(node) 96 | return node.name, operator.ident_priority 97 | end 98 | 99 | function ExpressionRule:Literal(node) 100 | local val = node.value 101 | local str = type(val) == "string" and format("\"%s\"", escape(val)) or tostring(val) 102 | return str, operator.ident_priority 103 | end 104 | 105 | function ExpressionRule:MemberExpression(node) 106 | local object, prio = self:expr_emit(node.object) 107 | if prio < operator.ident_priority or is_literal(node.object) then 108 | object = "(" .. object .. ")" 109 | end 110 | local exp 111 | if node.computed then 112 | local prop = self:expr_emit(node.property) 113 | exp = format("%s[%s]", object, prop) 114 | else 115 | exp = format("%s.%s", object, node.property.name) 116 | end 117 | return exp, operator.ident_priority 118 | end 119 | 120 | function ExpressionRule:Vararg() 121 | return "...", operator.ident_priority 122 | end 123 | 124 | function ExpressionRule:ExpressionValue(node) 125 | return "(" .. self:expr_emit(node.value) .. ")" 126 | end 127 | 128 | function ExpressionRule:BinaryExpression(node) 129 | local oper = node.operator 130 | local lprio = operator.left_priority(oper) 131 | local rprio = operator.right_priority(oper) 132 | local a, alprio, arprio = self:expr_emit(node.left) 133 | local b, blprio, brprio = self:expr_emit(node.right) 134 | if not arprio then arprio = alprio end 135 | if not brprio then brprio = blprio end 136 | local ap = arprio < lprio and format("(%s)", a) or a 137 | local bp = blprio <= rprio and format("(%s)", b) or b 138 | return format("%s %s %s", ap, oper, bp), lprio, rprio 139 | end 140 | 141 | function ExpressionRule:UnaryExpression(node) 142 | local arg, arg_prio = self:expr_emit(node.argument) 143 | local op_prio = operator.unary_priority 144 | if arg_prio < op_prio then arg = format("(%s)", arg) end 145 | return format("%s %s", node.operator, arg), operator.unary_priority 146 | end 147 | 148 | ExpressionRule.LogicalExpression = ExpressionRule.BinaryExpression 149 | 150 | function ExpressionRule:ConcatenateExpression(node) 151 | local ls = { } 152 | local cat_prio = operator.left_priority("..") 153 | for k = 1, #node.terms do 154 | local kprio 155 | ls[k], kprio = self:expr_emit(node.terms[k]) 156 | if kprio < cat_prio then ls[k] = format("(%s)", ls[k]) end 157 | end 158 | return concat(ls, " .. "), cat_prio 159 | end 160 | 161 | function ExpressionRule:Table(node) 162 | local hash = { } 163 | local last = #node.keyvals 164 | for i = 1, last do 165 | local kv = node.keyvals[i] 166 | local val = self:expr_emit(kv[1]) 167 | local key = kv[2] 168 | if key then 169 | if is_string(key) and string_is_ident(key.value) then 170 | hash[i] = format("%s = %s", key.value, val) 171 | else 172 | hash[i] = format("[%s] = %s", self:expr_emit(key), val) 173 | end 174 | else 175 | if i == last and kv[1].bracketed then 176 | hash[i] = format("(%s)", val) 177 | else 178 | hash[i] = format("%s", val) 179 | end 180 | end 181 | end 182 | local content = "" 183 | if #hash > 0 then 184 | content = comma_sep_list(hash) 185 | end 186 | return "{" .. content .. "}", operator.ident_priority 187 | end 188 | 189 | function ExpressionRule:CallExpression(node) 190 | local callee, prio = self:expr_emit(node.callee) 191 | if prio < operator.ident_priority then 192 | callee = "(" .. callee .. ")" 193 | end 194 | local exp = format("%s(%s)", callee, self:expr_list(node.arguments)) 195 | return exp, operator.ident_priority 196 | end 197 | 198 | function ExpressionRule:SendExpression(node) 199 | local rec, prio = self:expr_emit(node.receiver) 200 | if prio < operator.ident_priority or is_literal(node.receiver) then 201 | rec = "(" .. rec .. ")" 202 | end 203 | local method = node.method.name 204 | local exp = format("%s:%s(%s)", rec, method, self:expr_list(node.arguments)) 205 | return exp, operator.ident_priority 206 | end 207 | 208 | function StatementRule:StatementsGroup(node) 209 | for i = 1, #node.statements do 210 | self:emit(node.statements[i]) 211 | end 212 | end 213 | 214 | function StatementRule:FunctionDeclaration(node) 215 | self:proto_enter(0) 216 | local name = self:expr_emit(node.id) 217 | local header = format("function %s(%s)", name, comma_sep_list(node.params, as_parameter)) 218 | if node.locald then 219 | header = "local " .. header 220 | end 221 | self:add_section(header, node.body) 222 | local child_proto = self:proto_leave() 223 | self.proto:merge(child_proto) 224 | end 225 | 226 | function ExpressionRule:FunctionExpression(node) 227 | self:proto_enter() 228 | local header = format("function(%s)", comma_sep_list(node.params, as_parameter)) 229 | self:add_section(header, node.body) 230 | local child_proto = self:proto_leave() 231 | return child_proto:inline(), 0 232 | end 233 | 234 | function StatementRule:CallExpression(node) 235 | local line = self:expr_emit(node) 236 | self:add_line(line) 237 | end 238 | 239 | function StatementRule:ForStatement(node) 240 | local init = node.init 241 | local istart = self:expr_emit(init.value) 242 | local iend = self:expr_emit(node.last) 243 | local header 244 | if node.step and not is_const(node.step, 1) then 245 | local step = self:expr_emit(node.step) 246 | header = format("for %s = %s, %s, %s do", init.id.name, istart, iend, step) 247 | else 248 | header = format("for %s = %s, %s do", init.id.name, istart, iend) 249 | end 250 | self:add_section(header, node.body) 251 | end 252 | 253 | function StatementRule:ForInStatement(node) 254 | local vars = comma_sep_list(node.namelist.names, as_parameter) 255 | local explist = self:expr_list(node.explist) 256 | local header = format("for %s in %s do", vars, explist) 257 | self:add_section(header, node.body) 258 | end 259 | 260 | function StatementRule:DoStatement(node) 261 | self:add_section("do", node.body) 262 | end 263 | 264 | function StatementRule:WhileStatement(node) 265 | local test = self:expr_emit(node.test) 266 | local header = format("while %s do", test) 267 | self:add_section(header, node.body) 268 | end 269 | 270 | function StatementRule:RepeatStatement(node) 271 | self:add_section("repeat", node.body, true) 272 | local test = self:expr_emit(node.test) 273 | local until_line = format("until %s", test) 274 | self:add_line(until_line) 275 | end 276 | 277 | function StatementRule:BreakStatement() 278 | self:add_line("break") 279 | end 280 | 281 | function StatementRule:IfStatement(node) 282 | local ncons = #node.tests 283 | for i = 1, ncons do 284 | local header_tag = i == 1 and "if" or "elseif" 285 | local test = self:expr_emit(node.tests[i]) 286 | local header = format("%s %s then", header_tag, test) 287 | self:add_section(header, node.cons[i], true) 288 | end 289 | if node.alternate then 290 | self:add_section("else", node.alternate, true) 291 | end 292 | self:add_line("end") 293 | end 294 | 295 | function StatementRule:LocalDeclaration(node) 296 | local line 297 | local names = comma_sep_list(node.names, as_parameter) 298 | if #node.expressions > 0 then 299 | line = format("local %s = %s", names, self:expr_list(node.expressions)) 300 | else 301 | line = format("local %s", names) 302 | end 303 | self:add_line(line) 304 | end 305 | 306 | function StatementRule:AssignmentExpression(node) 307 | local line = format("%s = %s", self:expr_list(node.left), self:expr_list(node.right)) 308 | self:add_line(line) 309 | end 310 | 311 | function StatementRule:Chunk(node) 312 | self:list_emit(node.body) 313 | end 314 | 315 | function StatementRule:ExpressionStatement(node) 316 | local line = self:expr_emit(node.expression) 317 | self:add_line(line) 318 | end 319 | 320 | function StatementRule:ReturnStatement(node) 321 | local line = format("return %s", self:expr_list(node.arguments)) 322 | self:add_line(line) 323 | end 324 | 325 | function StatementRule:LabelStatement(node) 326 | self:add_line("::" .. node.label .. "::") 327 | end 328 | 329 | function StatementRule:GotoStatement(node) 330 | self:add_line("goto " .. node.label) 331 | end 332 | 333 | local function proto_inline(proto) 334 | -- remove leading whitespaces from first line 335 | if #proto.code > 0 then 336 | proto.code[1] = string.gsub(proto.code[1], "^%s*", "") 337 | end 338 | return concat(proto.code, "\n") 339 | end 340 | 341 | local function proto_merge(proto, child) 342 | for k = 1, #child.code do 343 | local line = child.code[k] 344 | local indent_str = string.rep(" ", proto.indent) 345 | proto.code[#proto.code + 1] = indent_str .. line 346 | end 347 | end 348 | 349 | local function proto_new(parent, indent) 350 | local ind = 0 351 | if indent then 352 | ind = indent 353 | elseif parent then 354 | ind = parent.indent 355 | end 356 | local proto = { code = { }, indent = ind, parent = parent } 357 | proto.inline = proto_inline 358 | proto.merge = proto_merge 359 | return proto 360 | end 361 | 362 | local function generate(tree, name) 363 | 364 | local self = { line = 0 } 365 | self.proto = proto_new() 366 | self.chunkname = tree.chunkname 367 | 368 | function self:proto_enter(indent) 369 | self.proto = proto_new(self.proto, indent) 370 | end 371 | 372 | function self:proto_leave() 373 | local proto = self.proto 374 | self.proto = proto.parent 375 | return proto 376 | end 377 | 378 | local function to_expr(node) 379 | return self:expr_emit(node) 380 | end 381 | 382 | function self:compile_code() 383 | return concat(self.code, "\n") 384 | end 385 | 386 | function self:indent_more() 387 | local proto = self.proto 388 | proto.indent = proto.indent + 1 389 | end 390 | 391 | function self:indent_less() 392 | local proto = self.proto 393 | proto.indent = proto.indent - 1 394 | end 395 | 396 | function self:line(line) 397 | -- FIXME: ignored for the moment 398 | end 399 | 400 | function self:add_line(line) 401 | local proto = self.proto 402 | local indent = string.rep(" ", proto.indent) 403 | proto.code[#proto.code + 1] = indent .. line 404 | end 405 | 406 | function self:add_section(header, body, omit_end) 407 | self:add_line(header) 408 | self:indent_more() 409 | self:list_emit(body) 410 | self:indent_less() 411 | if not omit_end then 412 | self:add_line("end") 413 | end 414 | end 415 | 416 | function self:expr_emit(node) 417 | local rule = ExpressionRule[node.kind] 418 | if not rule then error("cannot find an expression rule for " .. node.kind) end 419 | return rule(self, node) 420 | end 421 | 422 | function self:expr_list(exps) 423 | return comma_sep_list(exps, to_expr) 424 | end 425 | 426 | function self:emit(node) 427 | local rule = StatementRule[node.kind] 428 | if not rule then error("cannot find a statement rule for " .. node.kind) end 429 | rule(self, node) 430 | if node.line then self:line(node.line) end 431 | end 432 | 433 | function self:list_emit(node_list) 434 | for i = 1, #node_list do 435 | self:emit(node_list[i]) 436 | end 437 | end 438 | 439 | self:emit(tree) 440 | 441 | return self:proto_leave():inline() 442 | end 443 | 444 | return generate 445 | -------------------------------------------------------------------------------- /lang/meson.build: -------------------------------------------------------------------------------- 1 | lang_sources = [ 2 | 'ast_boolean_const_eval.lua', 3 | 'ast_const_eval.lua', 4 | 'bcread.lua', 5 | 'bcsave.lua', 6 | 'bytecode.lua', 7 | 'compile.lua', 8 | 'generator.lua', 9 | 'lexer.lua', 10 | 'id_generator.lua', 11 | 'lua_ast.lua', 12 | 'operator.lua', 13 | 'parser.lua', 14 | 'reader.lua', 15 | ] 16 | 17 | luajit = find_program('luajit') 18 | 19 | if bytecode_preload 20 | lang_bc_sources = [] 21 | foreach lua_source : lang_sources 22 | source_basename = lua_source.split('.')[0] 23 | module_name = 'lang_' + source_basename.underscorify() 24 | message(module_name + ' ' + source_basename + '.c') 25 | lang_bc_sources += custom_target(source_basename + '.c', 26 | input: lua_source, 27 | output: '@BASENAME@.c', 28 | command: [luajit, '-b', '-n', module_name, '@INPUT@', '@OUTPUT@'] 29 | ) 30 | endforeach 31 | lang_bc_lib = static_library('langbc', lang_bc_sources) 32 | else 33 | lua_module_install_dir = 'share/lua/5.1' 34 | install_data(lang_sources, install_dir: lua_module_install_dir + '/lang') 35 | endif 36 | -------------------------------------------------------------------------------- /lang/operator.lua: -------------------------------------------------------------------------------- 1 | -- Priorities for each binary operator. 2 | -- (left priority) * 256 + (right priority) 3 | -- modulus is your friend 4 | local binop = { 5 | ['+'] = 6 * 256 + 6, ['-'] = 6 * 256 + 6, ['*'] = 7 * 256 + 7, ['/'] = 7 * 256 + 7, ['%'] = 7 * 256 + 7, 6 | ['^'] = 10* 256 + 9, ['..'] = 5 * 256 + 4, -- POW CONCAT (right associative) 7 | ['=='] = 3 * 256 + 3, ['~='] = 3 * 256 + 3, 8 | ['<'] = 3 * 256 + 3, ['>='] = 3 * 256 + 3, ['>'] = 3 * 256 + 3, ['<='] = 3 * 256 + 3, 9 | ['and']= 2 * 256 + 2, ['or'] = 1 * 256 + 1, 10 | } 11 | 12 | local unary_priority = 8 13 | 14 | -- Pseudo priority of a simple identifier. Should be higher than any 15 | -- others operator's priority. 16 | local ident_priority = 16 17 | 18 | local function is_binop(op) 19 | return binop[op] 20 | end 21 | 22 | local function left_priority(op) 23 | return bit.rshift(binop[op], 8) 24 | end 25 | 26 | local function right_priority(op) 27 | return bit.band(binop[op], 0xff) 28 | end 29 | 30 | return { 31 | is_binop = is_binop, 32 | left_priority = left_priority, 33 | right_priority = right_priority, 34 | unary_priority = unary_priority, 35 | ident_priority = ident_priority, 36 | } 37 | -------------------------------------------------------------------------------- /lang/reader.lua: -------------------------------------------------------------------------------- 1 | local strsub = string.sub 2 | 3 | local function new_string_reader(src) 4 | local pos = 1 5 | local function reader() 6 | local chunk = strsub(src, pos, pos + 4096 - 32) 7 | pos = pos + #chunk 8 | return #chunk > 0 and chunk or nil 9 | end 10 | return reader 11 | end 12 | 13 | local function new_file_reader(filename) 14 | local f 15 | if filename then 16 | f = assert(io.open(filename, 'r'), "cannot open file " .. filename) 17 | else 18 | f = io.stdin 19 | end 20 | local function reader() 21 | return f:read(4096 - 32) 22 | end 23 | return reader 24 | end 25 | 26 | return { 27 | string = new_string_reader, 28 | file = new_file_reader, 29 | } 30 | -------------------------------------------------------------------------------- /lang/util.lua: -------------------------------------------------------------------------------- 1 | local exports = { } 2 | 3 | local function dump(node, level) 4 | if not level then level = 1 end 5 | if type(node) == 'nil' then 6 | return "null" 7 | end 8 | if type(node) == "string" then 9 | return '"'..node..'"' 10 | end 11 | if type(node) == "number" then 12 | return node 13 | end 14 | if type(node) == "boolean" or type(node) == "cdata" then 15 | return tostring(node) 16 | end 17 | if type(node) == "function" then 18 | return tostring(node) 19 | end 20 | 21 | local buff = { } 22 | local dent = string.rep(" ", level) 23 | local tput = table.insert 24 | 25 | if #node == 0 and next(node, nil) then 26 | tput(buff, "{") 27 | local i_buff = { } 28 | local p_buff = { } 29 | for k,data in pairs(node) do 30 | tput(buff, "\n"..dent..dump(k)..': '..dump(data, level + 1)) 31 | if next(node, k) then 32 | tput(buff, ",") 33 | end 34 | end 35 | tput(buff, "\n"..string.rep(" ", level - 1).."}") 36 | else 37 | tput(buff, "[") 38 | for i,data in pairs(node) do 39 | tput(buff, "\n"..dent..dump(data, level + 1)) 40 | if i ~= #node then 41 | tput(buff, ",") 42 | end 43 | end 44 | tput(buff, "\n"..string.rep(" ", level - 1).."]") 45 | end 46 | 47 | return table.concat(buff, "") 48 | end 49 | 50 | exports.dump = dump 51 | 52 | return exports 53 | -------------------------------------------------------------------------------- /meson.build: -------------------------------------------------------------------------------- 1 | project('luajit-lang-toolkit', 'c', version : '1.0', default_options : 'c_std=c99') 2 | 3 | pkg = import('pkgconfig') 4 | 5 | bytecode_preload = get_option('preload') 6 | 7 | subdir('lang') 8 | subdir('src') 9 | -------------------------------------------------------------------------------- /meson_options.txt: -------------------------------------------------------------------------------- 1 | option('preload', type : 'boolean', value : false, description: 'bytecode preload') 2 | -------------------------------------------------------------------------------- /run-lexer.lua: -------------------------------------------------------------------------------- 1 | local lex_setup = require("lang.lexer") 2 | local reader = require("lang.reader") 3 | local filename = assert(..., "usage: luajit run-lexer.lua ") 4 | 5 | local ls = lex_setup(reader.file(filename), filename) 6 | 7 | repeat 8 | ls:next() 9 | if ls.tokenval then 10 | print(ls.token, ls.tokenval) 11 | else 12 | print(ls.token) 13 | end 14 | until ls.token == "TK_eof" 15 | -------------------------------------------------------------------------------- /run.lua: -------------------------------------------------------------------------------- 1 | local function usage() 2 | io.stderr:write[[ 3 | LuaJIT Language Toolkit usage: luajit [options]... [script [args]...]. 4 | 5 | Available options are: 6 | -b ... Save or list bytecode. 7 | -c ... Generate Lua code and run. 8 | If followed by the "v" option the generated Lua code 9 | will be printed. 10 | ]] 11 | os.exit(1) 12 | end 13 | 14 | local function check(success, result) 15 | if not success then 16 | io.stderr:write(result .. "\n") 17 | os.exit(1) 18 | else 19 | return result 20 | end 21 | end 22 | 23 | local filename 24 | 25 | local args = {...} 26 | local opt = {} 27 | local k = 1 28 | while args[k] do 29 | local a = args[k] 30 | if string.sub(args[k], 1, 1) == "-" then 31 | if string.sub(a, 2, 2) == "b" then 32 | local j = 1 33 | if #a > 2 then 34 | args[j] = "-" .. string.sub(a, 3) 35 | j = j + 1 36 | else 37 | table.remove(args, j) 38 | end 39 | require("lang.bcsave").start(unpack(args)) 40 | os.exit(0) 41 | elseif string.sub(a, 2, 2) == "c" then 42 | opt.code = true 43 | local copt = string.sub(a, 3, 3) 44 | if copt == "v" then 45 | opt.debug = true 46 | elseif copt ~= "" then 47 | print("Invalid Lua code option: ", copt) 48 | usage() 49 | end 50 | elseif string.sub(a, 2, 2) == "v" then 51 | opt.debug = true 52 | else 53 | print("Invalid option: ", args[k]) 54 | usage() 55 | end 56 | else 57 | filename = args[k] 58 | end 59 | k = k + 1 60 | end 61 | 62 | if not filename then usage() end 63 | 64 | local compile = require("lang.compile") 65 | 66 | -- Compute the bytecode string for the given filename. 67 | local luacode = check(compile.file(filename, opt)) 68 | if opt.debug then 69 | print(luacode) 70 | print('\n\nOutput:') 71 | end 72 | local fn = assert(loadstring(luacode)) 73 | fn() 74 | 75 | -------------------------------------------------------------------------------- /scripts/test-bytecode-hex.py: -------------------------------------------------------------------------------- 1 | import os 2 | import re 3 | import sys 4 | import subprocess 5 | import StringIO 6 | from glob import glob 7 | 8 | if len(sys.argv) != 2: 9 | print("usage: %s " % sys.argv[0]) 10 | sys.exit(1) 11 | 12 | build_dir = sys.argv[1] 13 | 14 | test_dir = "tests" 15 | luajit_exec = "luajit" 16 | luajit_x = os.path.join(build_dir, "src/luajit-x") 17 | diff_exec = "diff" 18 | 19 | windows_os = (os.name == 'nt') 20 | 21 | def lua_files(test_dir): 22 | for dirpath, dirnames, filenames in os.walk(test_dir): 23 | for filename in sorted(filenames): 24 | m = re.match(r'([^.]+)\.lua$', filename) 25 | if m: 26 | yield dirpath, m.group(1) 27 | 28 | def do_process(cmd, dst): 29 | src = subprocess.Popen(cmd, stdout = subprocess.PIPE).stdout 30 | for line in src: 31 | if windows_os: 32 | line = line.replace('\r\n', '\n') 33 | dst.write(line) 34 | 35 | def do_process_output(cmd): 36 | sf = StringIO.StringIO() 37 | do_process(cmd, sf) 38 | s = sf.getvalue() 39 | sf.close() 40 | return s 41 | 42 | def source_fullname_ref(fullname): 43 | fullname_alias = re.sub(r'\.lua$', '.alias.lua', fullname) 44 | return fullname_alias if os.path.isfile(fullname_alias) else fullname 45 | 46 | def expected_bytecode(name, fullname): 47 | subprocess.check_call([luajit_exec, "-bg", source_fullname_ref(fullname), ".out.raw"]) 48 | s = do_process_output([luajit_x, "-bx", ".out.raw"]) 49 | yield s, "luajit" 50 | expect_dir = os.path.join("tests", "expect_hex") 51 | for expect_filename in glob(os.path.join(expect_dir, "*.txt")): 52 | efilename = os.path.basename(expect_filename) 53 | m = re.match(r'([^.]+)\.(expect\d+)\.txt$', efilename) 54 | if m and m.group(1) == name: 55 | ef = open(expect_filename, "rb") 56 | sf = StringIO.StringIO() 57 | parse(ef, sf) 58 | s = sf.getvalue() 59 | ef.close() 60 | sf.close() 61 | yield s, m.group(2) 62 | 63 | def write_diff(a, b, a_name, b_name): 64 | fna = "tests/log/%s.txt" % a_name 65 | fnb = "tests/log/%s.%s.txt" % (a_name, b_name) 66 | af = open(fna, "wb") 67 | bf = open(fnb, "wb") 68 | af.write(a) 69 | bf.write(b) 70 | af.close() 71 | bf.close() 72 | 73 | diff_output = subprocess.Popen([diff_exec, "-U", "4", fna, fnb], stdout=subprocess.PIPE).communicate()[0] 74 | if windows_os: 75 | diff_output = diff_output.replace('\r\n', '\n') 76 | diff_file = open("tests/log/%s.%s.diff" % (a_name, b_name), "w") 77 | diff_file.write(diff_output) 78 | diff_file.close() 79 | 80 | def compare_to_ref(name, fullname, output_test): 81 | for s, source in expected_bytecode(name, fullname): 82 | if s == output_test: 83 | return "pass", source 84 | else: 85 | write_diff(output_test, s, name, source) 86 | return "fail", None 87 | 88 | if not os.path.isdir("tests/log"): 89 | try: 90 | print "Creating directory tests/log..." 91 | os.mkdir("tests/log") 92 | except: 93 | print "Error creating directory tests/log." 94 | sys.exit(1) 95 | 96 | try: 97 | subprocess.check_call([luajit_exec, "-e", ""]) 98 | except: 99 | print "Error calling luajit." 100 | print "Please make sure that luajit executable is in the current PATH." 101 | sys.exit(1) 102 | 103 | for filename in glob("tests/log/*"): 104 | os.remove(filename) 105 | 106 | for dirpath, name in lua_files(test_dir): 107 | fullname = os.path.join(dirpath, name + ".lua") 108 | 109 | output_test = do_process_output([luajit_x, "-bx", fullname]) 110 | msg, source = compare_to_ref(name, fullname, output_test) 111 | 112 | led = " " if msg == "pass" else "*" 113 | msg_ext = "%s / %s" % (msg, source) if source and source != "luajit" else msg 114 | 115 | print("%s %-24s%s" % (led, name, msg_ext)) 116 | sys.stdout.flush() 117 | -------------------------------------------------------------------------------- /scripts/test-bytecode.py: -------------------------------------------------------------------------------- 1 | import os 2 | import re 3 | import sys 4 | import subprocess 5 | import StringIO 6 | from glob import glob 7 | 8 | if len(sys.argv) != 2: 9 | print("usage: %s " % sys.argv[0]) 10 | sys.exit(1) 11 | 12 | build_dir = sys.argv[1] 13 | 14 | test_dir = "tests" 15 | luajit_exec = "luajit" 16 | luajit_x = os.path.join(build_dir, "src/luajit-x") 17 | diff_exec = "diff" 18 | 19 | windows_os = (os.name == 'nt') 20 | 21 | def lua_files(test_dir): 22 | for dirpath, dirnames, filenames in os.walk(test_dir): 23 | for filename in sorted(filenames): 24 | m = re.match(r'([^.]+)\.lua$', filename) 25 | if m: 26 | yield dirpath, m.group(1) 27 | 28 | class LabelSource: 29 | def __init__(self): 30 | self.index = 1 31 | self.defs = {} 32 | def get(self, lab): 33 | if not lab in self.defs: 34 | self.defs[lab] = "X%03d" % self.index 35 | self.index += 1 36 | return self.defs[lab] 37 | 38 | def proto_lines(bcfile): 39 | for line in bcfile: 40 | if re.match(r'\s*$', line): break 41 | yield line 42 | 43 | def normalize(source, outfile): 44 | labels = LabelSource() 45 | for line in source: 46 | rline = None 47 | m = re.match(r'(\d{4}) ( |=>) (.*)', line) 48 | lab, ref, rem = m.groups() 49 | rem = re.sub(r'\r+', r'', rem) 50 | mr = re.match(r'([A-Z0-9]+\s+)(\d+) => (\d+)(.*)', rem) 51 | if mr: 52 | ins, reg, jmp, xrem = mr.groups() 53 | jmp = labels.get(jmp) 54 | rem = "%s%s => %s%s" % (ins, reg, jmp, xrem) 55 | if ref == '=>': 56 | lab = labels.get(lab) 57 | else: 58 | lab = " " 59 | rline = "%4s %s %s\n" % (lab, ref, rem) 60 | 61 | outfile.write(rline) 62 | 63 | def parse(bcfile, outfile): 64 | for line in bcfile: 65 | m = re.match(r'-- BYTECODE -- ', line) 66 | if m: 67 | if windows_os: line = line.replace('\r\n', '\n') 68 | outfile.write(line) 69 | normalize(proto_lines(bcfile), outfile) 70 | 71 | def do_process(cmd, dst): 72 | src = subprocess.Popen(cmd, stdout = subprocess.PIPE).stdout 73 | parse(src, dst) 74 | 75 | def do_process_output(cmd): 76 | sf = StringIO.StringIO() 77 | do_process(cmd, sf) 78 | s = sf.getvalue() 79 | sf.close() 80 | return s 81 | 82 | def source_fullname_ref(fullname): 83 | fullname_alias = re.sub(r'\.lua$', '.alias.lua', fullname) 84 | return fullname_alias if os.path.isfile(fullname_alias) else fullname 85 | 86 | def expected_bytecode(name, fullname): 87 | s = do_process_output([luajit_exec, "-bl", source_fullname_ref(fullname)]) 88 | s = re.sub(r'\.alias\.lua([^a-z])', r'.lua\1', s) 89 | yield s, "luajit" 90 | expect_dir = os.path.join("tests", "expect") 91 | for expect_filename in glob(os.path.join(expect_dir, "*.txt")): 92 | efilename = os.path.basename(expect_filename) 93 | m = re.match(r'([^.]+)\.(expect\d+)\.txt$', efilename) 94 | if m and m.group(1) == name: 95 | ef = open(expect_filename, "r") 96 | sf = StringIO.StringIO() 97 | parse(ef, sf) 98 | s = sf.getvalue() 99 | ef.close() 100 | sf.close() 101 | yield s, m.group(2) 102 | 103 | def write_diff(a, b, a_name, b_name): 104 | fna = "tests/log/%s.txt" % a_name 105 | fnb = "tests/log/%s.%s.txt" % (a_name, b_name) 106 | af = open(fna, "w") 107 | bf = open(fnb, "w") 108 | af.write(a) 109 | bf.write(b) 110 | af.close() 111 | bf.close() 112 | 113 | diff_output = subprocess.Popen([diff_exec, "-U", "4", fna, fnb], stdout=subprocess.PIPE).communicate()[0] 114 | if windows_os: 115 | diff_output = diff_output.replace('\r\n', '\n') 116 | diff_file = open("tests/log/%s.%s.diff" % (a_name, b_name), "w") 117 | diff_file.write(diff_output) 118 | diff_file.close() 119 | 120 | def compare_to_ref(name, fullname, output_test): 121 | for s, source in expected_bytecode(name, fullname): 122 | if s == output_test: 123 | return "pass", source 124 | else: 125 | write_diff(output_test, s, name, source) 126 | return "fail", None 127 | 128 | if not os.path.isdir("tests/log"): 129 | try: 130 | print "Creating directory tests/log..." 131 | os.mkdir("tests/log") 132 | except: 133 | print "Error creating directory tests/log." 134 | sys.exit(1) 135 | 136 | try: 137 | subprocess.check_call([luajit_exec, "-e", ""]) 138 | except: 139 | print "Error calling luajit." 140 | print "Please make sure that luajit executable is in the current PATH." 141 | sys.exit(1) 142 | 143 | for filename in glob("tests/log/*"): 144 | os.remove(filename) 145 | 146 | for dirpath, name in lua_files(test_dir): 147 | fullname = os.path.join(dirpath, name + ".lua") 148 | 149 | output_test = do_process_output([luajit_x, "-bl", fullname]) 150 | msg, source = compare_to_ref(name, fullname, output_test) 151 | 152 | led = " " if msg == "pass" else "*" 153 | msg_ext = "%s / %s" % (msg, source) if source and source != "luajit" else msg 154 | 155 | print("%s %-24s%s" % (led, name, msg_ext)) 156 | sys.stdout.flush() 157 | -------------------------------------------------------------------------------- /scripts/test-luacode-output.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import os 3 | import re 4 | import subprocess 5 | 6 | test_dir = "tests" 7 | luajit_exec = "luajit" 8 | 9 | if not os.path.isdir("tests/log"): 10 | try: 11 | print "Creating directory tests/log..." 12 | os.mkdir("tests/log") 13 | except: 14 | print "Error creating directory tests/log." 15 | sys.exit(1) 16 | 17 | try: 18 | subprocess.check_call([luajit_exec, "-e", ""]) 19 | except: 20 | print "Error calling luajit." 21 | print "Please make sure that luajit executable is in the current PATH." 22 | sys.exit(1) 23 | 24 | def source_fullname_ref(fullname): 25 | fullname_alias = re.sub(r'\.lua$', '.alias.lua', fullname) 26 | return fullname_alias if os.path.isfile(fullname_alias) else fullname 27 | 28 | for dirpath, dirnames, filenames in os.walk(test_dir): 29 | for filename in sorted(filenames): 30 | m = re.match(r'([^.]+)\.lua$', filename) 31 | if m: 32 | fullname = os.path.join(dirpath, filename) 33 | test_name = m.group(1) 34 | out_tst = None 35 | run_error = False 36 | try: 37 | out_tst = subprocess.check_output([luajit_exec, "run.lua", "-c", fullname]) 38 | except subprocess.CalledProcessError: 39 | run_error = True 40 | out_ref = subprocess.check_output([luajit_exec, source_fullname_ref(fullname)]) 41 | led, msg = None, None 42 | if run_error: 43 | led, msg = "*", "fail to run" 44 | elif out_tst == out_ref: 45 | if out_tst in ["", "\n", "\r\n"] or not out_tst: 46 | led, msg = "-", "pass / no output" 47 | else: 48 | led, msg = " ", "pass" 49 | else: 50 | led, msg = "*", "fail" 51 | log = open("tests/log/%s.output.diff" % test_name, "w") 52 | log.write("*** reference ***\n%s\n" % out_ref) 53 | log.write("*** test program ***\n%s\n" % out_tst) 54 | log.close() 55 | 56 | print("%s %-24s %s" % (led, test_name, msg)) 57 | -------------------------------------------------------------------------------- /scripts/test-output.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import os 3 | import re 4 | import subprocess 5 | 6 | if len(sys.argv) != 2: 7 | print("usage: %s " % sys.argv[0]) 8 | sys.exit(1) 9 | 10 | build_dir = sys.argv[1] 11 | 12 | test_dir = "tests" 13 | luajit_exec = "luajit" 14 | luajit_x = os.path.join(build_dir, "src/luajit-x") 15 | 16 | if not os.path.isdir("tests/log"): 17 | try: 18 | print "Creating directory tests/log..." 19 | os.mkdir("tests/log") 20 | except: 21 | print "Error creating directory tests/log." 22 | sys.exit(1) 23 | 24 | try: 25 | subprocess.check_call([luajit_exec, "-e", ""]) 26 | except: 27 | print "Error calling luajit." 28 | print "Please make sure that luajit executable is in the current PATH." 29 | sys.exit(1) 30 | 31 | def source_fullname_ref(fullname): 32 | fullname_alias = re.sub(r'\.lua$', '.alias.lua', fullname) 33 | return fullname_alias if os.path.isfile(fullname_alias) else fullname 34 | 35 | for dirpath, dirnames, filenames in os.walk(test_dir): 36 | for filename in sorted(filenames): 37 | m = re.match(r'([^.]+)\.lua$', filename) 38 | if m: 39 | fullname = os.path.join(dirpath, filename) 40 | test_name = m.group(1) 41 | out_tst = None 42 | run_error = False 43 | try: 44 | out_tst = subprocess.check_output([luajit_x, fullname]) 45 | except subprocess.CalledProcessError: 46 | run_error = True 47 | out_ref = subprocess.check_output([luajit_exec, source_fullname_ref(fullname)]) 48 | led, msg = None, None 49 | if run_error: 50 | led, msg = "*", "fail to run" 51 | elif out_tst == out_ref: 52 | if out_tst in ["", "\n", "\r\n"] or not out_tst: 53 | led, msg = "-", "pass / no output" 54 | else: 55 | led, msg = " ", "pass" 56 | else: 57 | led, msg = "*", "fail" 58 | log = open("tests/log/%s.output.diff" % test_name, "w") 59 | log.write("*** reference ***\n%s\n" % out_ref) 60 | log.write("*** test program ***\n%s\n" % out_tst) 61 | log.close() 62 | 63 | print("%s %-24s %s" % (led, test_name, msg)) 64 | sys.stdout.flush() 65 | -------------------------------------------------------------------------------- /src/Makefile: -------------------------------------------------------------------------------- 1 | 2 | # Makefile 3 | # 4 | # Copyright (C) 2014 Francesco Abbate 5 | # 6 | # This program is free software; you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation; either version 3 of the License, or (at 9 | # your option) any later version. 10 | # 11 | # This program is distributed in the hope that it will be useful, but 12 | # WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | # General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with this program; if not, write to the Free Software 18 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 19 | # 20 | 21 | # If the following variable is set to "yes" the lang.* modules will be 22 | # embedded in the executable and preloaded into the Lua's state. 23 | BC_PRELOAD = yes 24 | 25 | ifneq (,$(findstring Windows,$(OS))) 26 | HOST_SYS= Windows 27 | else 28 | HOST_SYS:= $(shell uname -s) 29 | ifneq (,$(findstring CYGWIN,$(TARGET_SYS))) 30 | HOST_SYS= Windows 31 | else ifeq (Darwin,$(HOST_SYS)) 32 | LFLAGS += -pagezero_size 10000 -image_base 100000000 33 | endif 34 | endif 35 | 36 | AR= ar rcu 37 | RANLIB= ranlib 38 | 39 | ifeq ($(HOST_SYS),Windows) 40 | LUAJIT_CFLAGS := -IC:/fra/local/include 41 | LUAJIT_LIBS := -LC:/fra/local/lib -lluajit 42 | else 43 | LUAJIT_CFLAGS := $(shell pkg-config luajit --cflags) 44 | LUAJIT_LIBS := $(shell pkg-config luajit --libs) 45 | endif 46 | 47 | CC = gcc 48 | CFLAGS = -g -Wall 49 | 50 | CFLAGS += $(LUAJIT_CFLAGS) 51 | LIBS += $(LUAJIT_LIBS) 52 | 53 | INCLUDES += -I.. 54 | 55 | COMPILE = $(CC) $(CFLAGS) $(DEFS) $(INCLUDES) 56 | 57 | ifeq ($(strip $(BC_PRELOAD)),yes) 58 | LANG_SRC_FILES = language_bcloader.c 59 | DEFS += -DBC_PRELOAD 60 | endif 61 | 62 | LANG_SRC_FILES += language.c language_loaders.c 63 | LANG_OBJ_FILES := $(LANG_SRC_FILES:%.c=%.o) 64 | DEP_FILES := $(LANG_SRC_FILES:%.c=.deps/%.P) 65 | 66 | DEPS_MAGIC := $(shell mkdir .deps > /dev/null 2>&1 || :) 67 | 68 | TARGETS = liblang.a 69 | 70 | all: $(TARGETS) luajit-x 71 | 72 | luajit-x: $(LANG_OBJ_FILES) luajit-x.o 73 | $(CC) -o $@ $(LANG_OBJ_FILES) luajit-x.o $(LIBS) 74 | 75 | liblang.a: $(LANG_OBJ_FILES) 76 | @echo Archive $@ 77 | @$(AR) $@ $? 78 | @$(RANLIB) $@ 79 | 80 | %.o: %.c 81 | @echo Compiling $< 82 | @$(COMPILE) -Wp,-MMD,.deps/$(*F).pp -c $< 83 | @-cp .deps/$(*F).pp .deps/$(*F).P; \ 84 | tr ' ' '\012' < .deps/$(*F).pp \ 85 | | sed -e 's/^\\$$//' -e '/^$$/ d' -e '/:$$/ d' -e 's/$$/ :/' \ 86 | >> .deps/$(*F).P; \ 87 | rm .deps/$(*F).pp 88 | 89 | .PHONY: clean all 90 | 91 | clean: 92 | rm -fr *.o *.a $(TARGETS) 93 | 94 | -include $(DEP_FILES) 95 | -------------------------------------------------------------------------------- /src/language.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "lua.h" 7 | #include "luaconf.h" 8 | #include "lauxlib.h" 9 | #include "lualib.h" 10 | #include "language.h" 11 | 12 | #define likely(x) __builtin_expect(!!(x), 1) 13 | #define unlikely(x) __builtin_expect(!!(x), 0) 14 | 15 | /* The position in the Lua stack of the loadstring and loadfile functions. */ 16 | #define MY_LOADSTRING_INDEX 1 17 | #define MY_LOADFILE_INDEX 2 18 | 19 | lua_State *parser_L; 20 | 21 | /* Pop a string from "parser_L" top of the stack and push it in "L" stack. */ 22 | static void error_xtransfer(lua_State *L) 23 | { 24 | const char *msg = lua_tostring(parser_L, -1); 25 | lua_pushstring(L, msg); 26 | lua_pop(parser_L, 1); 27 | } 28 | 29 | static int loadbuffer_xtransfer(lua_State *L, const char *filename) 30 | { 31 | size_t code_len; 32 | const char *code = lua_tolstring(parser_L, -1, &code_len); 33 | int status = luaL_loadbuffer(L, code, code_len, filename); 34 | lua_pop(parser_L, 1); 35 | return status; 36 | } 37 | 38 | int 39 | language_init(lua_State *L) { 40 | parser_L = luaL_newstate(); 41 | if (unlikely(parser_L == NULL)) { 42 | lua_pushstring(L, "cannot create state: not enough memory"); 43 | return LUA_ERRRUN; 44 | } 45 | luaL_openlibs(parser_L); 46 | #ifdef BC_PRELOAD 47 | language_bc_preload(parser_L); 48 | #endif 49 | 50 | lua_getglobal(parser_L, "require"); 51 | lua_pushstring(parser_L, "lang.compile"); 52 | int load_status = lua_pcall(parser_L, 1, 1, 0); 53 | if (unlikely(load_status != 0)) { 54 | error_xtransfer(L); 55 | return LUA_ERRRUN; 56 | } 57 | if (lua_istable(parser_L, -1)) { 58 | lua_pushstring(parser_L, "string"); 59 | lua_rawget(parser_L, -2); 60 | lua_pushstring(parser_L, "file"); 61 | lua_rawget(parser_L, -3); 62 | lua_remove(parser_L, -3); 63 | } else { 64 | lua_pop(parser_L, 1); 65 | lua_pushstring(L, "module \"lang.compile\" does not load properly"); 66 | return LUA_ERRRUN; 67 | } 68 | 69 | if (!lua_isfunction(parser_L, MY_LOADSTRING_INDEX) || 70 | !lua_isfunction(parser_L, MY_LOADFILE_INDEX)) { 71 | lua_pop(parser_L, 2); 72 | lua_pushstring(L, "invalid compile functions"); 73 | load_status = LUA_ERRRUN; 74 | } 75 | return load_status; 76 | } 77 | 78 | static int 79 | language_check_error(lua_State *L, const char *filename, int parse_status) 80 | { 81 | if (parse_status != 0) { 82 | error_xtransfer(L); 83 | return LUA_LANGERR; 84 | } 85 | int compile_success = lua_toboolean(parser_L, -2); 86 | if (!compile_success) { 87 | error_xtransfer(L); 88 | lua_pop(parser_L, 1); /* Pop the boolean value. */ 89 | return LUA_ERRSYNTAX; 90 | } 91 | int load_status = loadbuffer_xtransfer(L, filename); 92 | lua_pop(parser_L, 1); /* Pop the boolean value. */ 93 | return load_status; 94 | } 95 | 96 | int 97 | language_loadbuffer(lua_State *L, const char *buff, size_t sz, const char *name) 98 | { 99 | /* Check if the string begin with the bytecode header. */ 100 | if (sz >= 4 && buff[0] == 0x1b && buff[1] == 0x4c && buff[2] == 0x4a) { 101 | return luaL_loadbuffer(L, buff, sz, name); 102 | } 103 | lua_pushvalue(parser_L, MY_LOADSTRING_INDEX); 104 | lua_pushlstring(parser_L, buff, sz); 105 | lua_pushstring(parser_L, name); 106 | int parse_status = lua_pcall(parser_L, 2, 2, 0); 107 | return language_check_error(L, name, parse_status); 108 | } 109 | 110 | static void l_message(const char *pname, const char *msg) 111 | { 112 | if (pname) fprintf(stderr, "%s: ", pname); 113 | fprintf(stderr, "%s\n", msg); 114 | fflush(stderr); 115 | } 116 | 117 | int language_report(lua_State *_L, int status) 118 | { 119 | lua_State *L = parser_L; 120 | if (status && !lua_isnil(L, -1)) { 121 | const char *msg = lua_tostring(L, -1); 122 | if (msg == NULL) msg = "(error object is not a string)"; 123 | l_message("", msg); 124 | lua_pop(L, 1); 125 | } 126 | return status; 127 | } 128 | 129 | int 130 | language_loadfile(lua_State *L, const char *filename) 131 | { 132 | lua_pushvalue(parser_L, MY_LOADFILE_INDEX); 133 | lua_pushstring(parser_L, filename); 134 | int parse_status = lua_pcall(parser_L, 1, 2, 0); 135 | return language_check_error(L, filename, parse_status); 136 | } 137 | -------------------------------------------------------------------------------- /src/language.h: -------------------------------------------------------------------------------- 1 | #ifndef LANGUAGE_H 2 | #define LANGUAGE_H 3 | 4 | #include "lua.h" 5 | 6 | extern int language_init(lua_State *L); 7 | extern int language_report(lua_State *L, int status); 8 | extern int language_loadbuffer(lua_State *L, const char *buff, size_t sz, const char *name); 9 | extern int language_loadfile(lua_State *L, const char *filename); 10 | 11 | /* Indicate an internal error of the "language" implementation. 12 | * LUA_ERRERR is supposed to be the last acceptable value from lua.h. */ 13 | #define LUA_LANGERR (LUA_ERRERR + 1) 14 | 15 | #endif 16 | -------------------------------------------------------------------------------- /src/language_loaders.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "lua.h" 4 | #include "lauxlib.h" 5 | #include "language.h" 6 | #include "language_loaders.h" 7 | 8 | static int 9 | language_lua_loadfile(lua_State* L) 10 | { 11 | const char *filename; 12 | if (lua_isnoneornil(L, 1)) { 13 | filename = NULL; 14 | } else { 15 | filename = luaL_checkstring(L, 1); 16 | } 17 | 18 | int status = language_loadfile(L, filename); 19 | if (status != 0) { 20 | return lua_error(L); 21 | } 22 | return 1; 23 | } 24 | 25 | static int 26 | language_lua_dofile(lua_State* L) 27 | { 28 | language_lua_loadfile(L); 29 | int n = lua_gettop(L) - 1; 30 | lua_call(L, 0, LUA_MULTRET); 31 | return lua_gettop(L) - n; 32 | } 33 | 34 | static int 35 | language_lua_loadstring(lua_State* L) 36 | { 37 | size_t len; 38 | const char *s = luaL_checklstring(L, 1, &len); 39 | const char *name; 40 | if (lua_isnoneornil(L, 2)) { 41 | name = NULL; 42 | } else { 43 | name = luaL_checkstring(L, 2); 44 | } 45 | int status = language_loadbuffer(L, s, len, name); 46 | if (status != 0) { 47 | return lua_error(L); 48 | } 49 | return 1; 50 | } 51 | 52 | static int readable(const char *filename) 53 | { 54 | FILE *f = fopen(filename, "r"); /* try to open file */ 55 | if (f == NULL) return 0; /* open failed */ 56 | fclose(f); 57 | return 1; 58 | } 59 | 60 | static const char *pushnexttemplate(lua_State *L, const char *path) 61 | { 62 | const char *l; 63 | while (*path == *LUA_PATHSEP) path++; /* skip separators */ 64 | if (*path == '\0') return NULL; /* no more templates */ 65 | l = strchr(path, *LUA_PATHSEP); /* find next separator */ 66 | if (l == NULL) l = path + strlen(path); 67 | lua_pushlstring(L, path, (size_t)(l - path)); /* template */ 68 | return l; 69 | } 70 | 71 | static const char *searchpath (lua_State *L, const char *name, 72 | const char *path, const char *sep, 73 | const char *dirsep) 74 | { 75 | luaL_Buffer msg; /* to build error message */ 76 | luaL_buffinit(L, &msg); 77 | if (*sep != '\0') /* non-empty separator? */ 78 | name = luaL_gsub(L, name, sep, dirsep); /* replace it by 'dirsep' */ 79 | while ((path = pushnexttemplate(L, path)) != NULL) { 80 | const char *filename = luaL_gsub(L, lua_tostring(L, -1), 81 | LUA_PATH_MARK, name); 82 | lua_remove(L, -2); /* remove path template */ 83 | if (readable(filename)) /* does file exist and is readable? */ 84 | return filename; /* return that file name */ 85 | lua_pushfstring(L, "\n\tno file " LUA_QS, filename); 86 | lua_remove(L, -2); /* remove file name */ 87 | luaL_addvalue(&msg); /* concatenate error msg. entry */ 88 | } 89 | luaL_pushresult(&msg); /* create error message */ 90 | return NULL; /* not found */ 91 | } 92 | 93 | static void push_package_field(lua_State *L, const char *pname) 94 | { 95 | lua_getfield(L, LUA_GLOBALSINDEX, "package"); 96 | lua_getfield(L, -1, pname); 97 | lua_remove(L, -2); 98 | } 99 | 100 | static const char *findfile(lua_State *L, const char *name, 101 | const char *pname) 102 | { 103 | const char *path; 104 | push_package_field(L, pname); 105 | path = lua_tostring(L, -1); 106 | if (path == NULL) 107 | luaL_error(L, LUA_QL("package.%s") " must be a string", pname); 108 | return searchpath(L, name, path, ".", LUA_DIRSEP); 109 | } 110 | 111 | static void loaderror(lua_State *L, const char *filename) 112 | { 113 | luaL_error(L, "error loading module " LUA_QS " from file " LUA_QS ":\n\t%s", 114 | lua_tostring(L, 1), filename, lua_tostring(L, -1)); 115 | } 116 | 117 | static int ltk_package_loader_lua(lua_State *L) 118 | { 119 | const char *filename; 120 | const char *name = luaL_checkstring(L, 1); 121 | filename = findfile(L, name, "path"); 122 | if (filename == NULL) return 1; /* library not found in this path */ 123 | if (language_loadfile(L, filename) != 0) 124 | loaderror(L, filename); 125 | return 1; /* library loaded successfully */ 126 | } 127 | 128 | static const luaL_Reg language_lib[] = { 129 | { "loadstring", language_lua_loadstring }, 130 | { "loadfile", language_lua_loadfile }, 131 | { "dofile", language_lua_dofile }, 132 | { "loader", ltk_package_loader_lua }, 133 | { NULL, NULL } 134 | }; 135 | 136 | int luaopen_langloaders(lua_State *L) 137 | { 138 | lua_newtable(L); 139 | luaL_register(L, NULL, language_lib); 140 | return 1; 141 | } 142 | -------------------------------------------------------------------------------- /src/language_loaders.h: -------------------------------------------------------------------------------- 1 | #ifndef LUA_LANGUAGE_GS_H 2 | #define LUA_LANGUAGE_GS_H 3 | 4 | #include "lua.h" 5 | 6 | extern int luaopen_langloaders(lua_State *L); 7 | 8 | #endif 9 | -------------------------------------------------------------------------------- /src/luajit-x.c: -------------------------------------------------------------------------------- 1 | /* 2 | ** LuaJIT Language Toolkit read-eval-print (REPL). 3 | ** Copyright (C) 2014 Francesco Abbate. 4 | ** 5 | ** Derived from: 6 | ** LuaJIT frontend. Runs commands, scripts, read-eval-print (REPL) etc. 7 | ** Copyright (C) 2005-2014 Mike Pall. See Copyright Notice in luajit.h 8 | ** 9 | ** Major portions taken verbatim or adapted from the Lua interpreter. 10 | ** Copyright (C) 1994-2008 Lua.org, PUC-Rio. See Copyright Notice in lua.h 11 | */ 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #define luajit_c 19 | 20 | #include "lua.h" 21 | #include "lauxlib.h" 22 | #include "lualib.h" 23 | #include "luajit.h" 24 | #include "language.h" 25 | #include "language_loaders.h" 26 | 27 | #ifdef BC_PRELOAD 28 | #include "language_bcloader.h" 29 | #endif 30 | 31 | #if defined(__linux__) 32 | #include 33 | #define lua_stdin_is_tty() isatty(0) 34 | #elif defined(_WIN32) 35 | #include 36 | #define lua_stdin_is_tty() _isatty(_fileno(stdin)) 37 | #else 38 | #define lua_stdin_is_tty() 1 39 | #endif 40 | 41 | static lua_State *globalL = NULL; 42 | static const char *progname = LUA_PROGNAME; 43 | 44 | static void lstop(lua_State *L, lua_Debug *ar) 45 | { 46 | (void)ar; /* unused arg. */ 47 | lua_sethook(L, NULL, 0, 0); 48 | /* Avoid luaL_error -- a C hook doesn't add an extra frame. */ 49 | luaL_where(L, 0); 50 | lua_pushfstring(L, "%sinterrupted!", lua_tostring(L, -1)); 51 | lua_error(L); 52 | } 53 | 54 | static void laction(int i) 55 | { 56 | signal(i, SIG_DFL); /* if another SIGINT happens before lstop, 57 | terminate process (default action) */ 58 | lua_sethook(globalL, lstop, LUA_MASKCALL | LUA_MASKRET | LUA_MASKCOUNT, 1); 59 | } 60 | 61 | static void print_usage(void) 62 | { 63 | fprintf(stderr, 64 | "usage: %s [options]... [script [args]...].\n" 65 | "Available options are:\n" 66 | " -e chunk Execute string " LUA_QL("chunk") ".\n" 67 | " -l name Require library " LUA_QL("name") ".\n" 68 | " -b ... Save or list bytecode.\n" 69 | " -j cmd Perform LuaJIT control command.\n" 70 | " -i Enter interactive mode after executing " LUA_QL("script") ".\n" 71 | " -v Show version information.\n" 72 | " -- Stop handling options.\n" 73 | " - Execute stdin and stop handling options.\n" 74 | , 75 | progname); 76 | fflush(stderr); 77 | } 78 | 79 | static void l_message(const char *pname, const char *msg) 80 | { 81 | if (pname) fprintf(stderr, "%s: ", pname); 82 | fprintf(stderr, "%s\n", msg); 83 | fflush(stderr); 84 | } 85 | 86 | static int report(lua_State *L, int status) 87 | { 88 | if (status && !lua_isnil(L, -1)) { 89 | const char *msg = lua_tostring(L, -1); 90 | if (msg == NULL) msg = "(error object is not a string)"; 91 | l_message(progname, msg); 92 | lua_pop(L, 1); 93 | } 94 | return status; 95 | } 96 | 97 | static int traceback(lua_State *L) 98 | { 99 | if (!lua_isstring(L, 1)) { /* Non-string error object? Try metamethod. */ 100 | if (lua_isnoneornil(L, 1) || 101 | !luaL_callmeta(L, 1, "__tostring") || 102 | !lua_isstring(L, -1)) 103 | return 1; /* Return non-string error object. */ 104 | lua_remove(L, 1); /* Replace object by result of __tostring metamethod. */ 105 | } 106 | luaL_traceback(L, L, lua_tostring(L, 1), 1); 107 | return 1; 108 | } 109 | 110 | static int docall(lua_State *L, int narg, int clear) 111 | { 112 | int status; 113 | int base = lua_gettop(L) - narg; /* function index */ 114 | lua_pushcfunction(L, traceback); /* push traceback function */ 115 | lua_insert(L, base); /* put it under chunk and args */ 116 | signal(SIGINT, laction); 117 | status = lua_pcall(L, narg, (clear ? 0 : LUA_MULTRET), base); 118 | signal(SIGINT, SIG_DFL); 119 | lua_remove(L, base); /* remove traceback function */ 120 | /* force a complete garbage collection in case of errors */ 121 | if (status != 0) lua_gc(L, LUA_GCCOLLECT, 0); 122 | return status; 123 | } 124 | 125 | static void print_version(void) 126 | { 127 | fputs("LuaJIT Language Toolkit frontend.\n", stdout); 128 | fputs(LUAJIT_VERSION " -- " LUAJIT_COPYRIGHT ". " LUAJIT_URL "\n", stdout); 129 | } 130 | 131 | static void print_jit_status(lua_State *L) 132 | { 133 | int n; 134 | const char *s; 135 | lua_getfield(L, LUA_REGISTRYINDEX, "_LOADED"); 136 | lua_getfield(L, -1, "jit"); /* Get jit.* module table. */ 137 | lua_remove(L, -2); 138 | lua_getfield(L, -1, "status"); 139 | lua_remove(L, -2); 140 | n = lua_gettop(L); 141 | lua_call(L, 0, LUA_MULTRET); 142 | fputs(lua_toboolean(L, n) ? "JIT: ON" : "JIT: OFF", stdout); 143 | for (n++; (s = lua_tostring(L, n)); n++) { 144 | putc(' ', stdout); 145 | fputs(s, stdout); 146 | } 147 | putc('\n', stdout); 148 | } 149 | 150 | static int getargs(lua_State *L, char **argv, int n) 151 | { 152 | int narg; 153 | int i; 154 | int argc = 0; 155 | while (argv[argc]) argc++; /* count total number of arguments */ 156 | narg = argc - (n + 1); /* number of arguments to the script */ 157 | luaL_checkstack(L, narg + 3, "too many arguments to script"); 158 | for (i = n+1; i < argc; i++) 159 | lua_pushstring(L, argv[i]); 160 | lua_createtable(L, narg, n + 1); 161 | for (i = 0; i < argc; i++) { 162 | lua_pushstring(L, argv[i]); 163 | lua_rawseti(L, -2, i - n); 164 | } 165 | return narg; 166 | } 167 | 168 | static int dofile(lua_State *L, const char *name) 169 | { 170 | int status = language_loadfile(L, name) || docall(L, 0, 1); 171 | return report(L, status); 172 | } 173 | 174 | static int dostring(lua_State *L, const char *s, const char *name) 175 | { 176 | int status = language_loadbuffer(L, s, strlen(s), name) || docall(L, 0, 1); 177 | return report(L, status); 178 | } 179 | 180 | static int dolibrary(lua_State *L, const char *name) 181 | { 182 | lua_getglobal(L, "require"); 183 | lua_pushstring(L, name); 184 | return report(L, docall(L, 1, 1)); 185 | } 186 | 187 | static void write_prompt(lua_State *L, int firstline) 188 | { 189 | const char *p; 190 | lua_getfield(L, LUA_GLOBALSINDEX, firstline ? "_PROMPT" : "_PROMPT2"); 191 | p = lua_tostring(L, -1); 192 | if (p == NULL) p = firstline ? LUA_PROMPT : LUA_PROMPT2; 193 | fputs(p, stdout); 194 | fflush(stdout); 195 | lua_pop(L, 1); /* remove global */ 196 | } 197 | 198 | static int incomplete(lua_State *L, int status) 199 | { 200 | if (status == LUA_ERRSYNTAX) { 201 | size_t lmsg; 202 | const char *msg = lua_tolstring(L, -1, &lmsg); 203 | const char *tp = msg + lmsg - (sizeof(LUA_QL("")) - 1); 204 | if (strstr(msg, LUA_QL("")) == tp) { 205 | lua_pop(L, 1); 206 | return 1; 207 | } 208 | } 209 | return 0; /* else... */ 210 | } 211 | 212 | static int pushline(lua_State *L, int firstline) 213 | { 214 | char buf[LUA_MAXINPUT]; 215 | write_prompt(L, firstline); 216 | if (fgets(buf, LUA_MAXINPUT, stdin)) { 217 | size_t len = strlen(buf); 218 | if (len > 0 && buf[len-1] == '\n') 219 | buf[len-1] = '\0'; 220 | if (firstline && buf[0] == '=') 221 | lua_pushfstring(L, "return %s", buf+1); 222 | else 223 | lua_pushstring(L, buf); 224 | return 1; 225 | } 226 | return 0; 227 | } 228 | 229 | static int loadline(lua_State *L) 230 | { 231 | int status; 232 | lua_settop(L, 0); 233 | if (!pushline(L, 1)) 234 | return -1; /* no input */ 235 | for (;;) { /* repeat until gets a complete line */ 236 | status = language_loadbuffer(L, lua_tostring(L, 1), lua_strlen(L, 1), "=stdin"); 237 | if (!incomplete(L, status)) break; /* cannot try to add lines? */ 238 | if (!pushline(L, 0)) /* no more input? */ 239 | return -1; 240 | lua_pushliteral(L, "\n"); /* add a new line... */ 241 | lua_insert(L, -2); /* ...between the two lines */ 242 | lua_concat(L, 3); /* join them */ 243 | } 244 | lua_remove(L, 1); /* remove line */ 245 | return status; 246 | } 247 | 248 | static void dotty(lua_State *L) 249 | { 250 | int status; 251 | const char *oldprogname = progname; 252 | progname = NULL; 253 | while ((status = loadline(L)) != -1) { 254 | if (status == 0) status = docall(L, 0, 0); 255 | report(L, status); 256 | if (status == 0 && lua_gettop(L) > 0) { /* any result to print? */ 257 | lua_getglobal(L, "print"); 258 | lua_insert(L, 1); 259 | if (lua_pcall(L, lua_gettop(L)-1, 0, 0) != 0) 260 | l_message(progname, 261 | lua_pushfstring(L, "error calling " LUA_QL("print") " (%s)", 262 | lua_tostring(L, -1))); 263 | } 264 | } 265 | lua_settop(L, 0); /* clear stack */ 266 | fputs("\n", stdout); 267 | fflush(stdout); 268 | progname = oldprogname; 269 | } 270 | 271 | static int handle_script(lua_State *L, char **argv, int n) 272 | { 273 | int status; 274 | const char *fname; 275 | int narg = getargs(L, argv, n); /* collect arguments */ 276 | lua_setglobal(L, "arg"); 277 | fname = argv[n]; 278 | if (strcmp(fname, "-") == 0 && strcmp(argv[n-1], "--") != 0) 279 | fname = NULL; /* stdin */ 280 | status = language_loadfile(L, fname); 281 | lua_insert(L, -(narg+1)); 282 | if (status == 0) 283 | status = docall(L, narg, 0); 284 | else 285 | lua_pop(L, narg); 286 | return report(L, status); 287 | } 288 | 289 | /* Load add-on module. */ 290 | static int loadlangmodule(lua_State *L) 291 | { 292 | lua_getglobal(L, "require"); 293 | lua_pushliteral(L, "lang."); 294 | lua_pushvalue(L, -3); 295 | lua_concat(L, 2); 296 | if (lua_pcall(L, 1, 1, 0)) { 297 | const char *msg = lua_tostring(L, -1); 298 | if (msg && !strncmp(msg, "module ", 7)) 299 | goto nomodule; 300 | return report(L, 1); 301 | } 302 | lua_getfield(L, -1, "start"); 303 | if (lua_isnil(L, -1)) { 304 | nomodule: 305 | l_message(progname, 306 | "unknown language toolkit command or lang.* modules not installed"); 307 | return 1; 308 | } 309 | lua_remove(L, -2); /* Drop module table. */ 310 | return 0; 311 | } 312 | 313 | /* Save or list bytecode. */ 314 | static int dobytecode(lua_State *L, char **argv) 315 | { 316 | int narg = 0; 317 | #ifdef BC_PRELOAD 318 | language_bc_preload(L); 319 | #endif 320 | lua_pushliteral(L, "bcsave"); 321 | if (loadlangmodule(L)) 322 | return 1; 323 | if (argv[0][2]) { 324 | narg++; 325 | argv[0][1] = '-'; 326 | lua_pushstring(L, argv[0]+1); 327 | } 328 | for (argv++; *argv != NULL; narg++, argv++) { 329 | lua_pushstring(L, *argv); 330 | } 331 | return report(L, lua_pcall(L, narg, 0, 0)); 332 | } 333 | 334 | /* check that argument has no extra characters at the end */ 335 | #define notail(x) {if ((x)[2] != '\0') return -1;} 336 | 337 | #define FLAGS_INTERACTIVE 1 338 | #define FLAGS_VERSION 2 339 | #define FLAGS_EXEC 4 340 | #define FLAGS_OPTION 8 341 | 342 | static int collectargs(char **argv, int *flags) 343 | { 344 | int i; 345 | for (i = 1; argv[i] != NULL; i++) { 346 | if (argv[i][0] != '-') /* Not an option? */ 347 | return i; 348 | switch (argv[i][1]) { /* Check option. */ 349 | case '-': 350 | notail(argv[i]); 351 | return (argv[i+1] != NULL ? i+1 : 0); 352 | case '\0': 353 | return i; 354 | case 'i': 355 | notail(argv[i]); 356 | *flags |= FLAGS_INTERACTIVE; 357 | /* fallthrough */ 358 | case 'v': 359 | notail(argv[i]); 360 | *flags |= FLAGS_VERSION; 361 | break; 362 | case 'e': 363 | *flags |= FLAGS_EXEC; 364 | case 'j': /* LuaJIT extension */ 365 | case 'l': 366 | *flags |= FLAGS_OPTION; 367 | if (argv[i][2] == '\0') { 368 | i++; 369 | if (argv[i] == NULL) return -1; 370 | } 371 | break; 372 | case 'b': /* LuaJIT extension */ 373 | if (*flags) return -1; 374 | *flags |= FLAGS_EXEC; 375 | return 0; 376 | default: return -1; /* invalid option */ 377 | } 378 | } 379 | return 0; 380 | } 381 | 382 | static int runargs(lua_State *L, char **argv, int n) 383 | { 384 | int i; 385 | for (i = 1; i < n; i++) { 386 | if (argv[i] == NULL) continue; 387 | lua_assert(argv[i][0] == '-'); 388 | switch (argv[i][1]) { /* option */ 389 | case 'e': { 390 | const char *chunk = argv[i] + 2; 391 | if (*chunk == '\0') chunk = argv[++i]; 392 | lua_assert(chunk != NULL); 393 | if (dostring(L, chunk, "=(command line)") != 0) 394 | return 1; 395 | break; 396 | } 397 | case 'l': { 398 | const char *filename = argv[i] + 2; 399 | if (*filename == '\0') filename = argv[++i]; 400 | lua_assert(filename != NULL); 401 | if (dolibrary(L, filename)) 402 | return 1; /* stop if file fails */ 403 | break; 404 | } 405 | case 'b': /* LuaJIT extension */ 406 | return dobytecode(L, argv+i); 407 | default: break; 408 | } 409 | } 410 | return 0; 411 | } 412 | 413 | static struct Smain { 414 | char **argv; 415 | int argc; 416 | int status; 417 | } smain; 418 | 419 | static void override_loaders(lua_State *L) 420 | { 421 | lua_getfield(L, LUA_GLOBALSINDEX, "package"); 422 | lua_getfield(L, -1, "loaders"); 423 | lua_remove(L, -2); 424 | 425 | luaopen_langloaders(L); 426 | lua_getfield(L, -1, "loadstring"); 427 | lua_setfield(L, LUA_GLOBALSINDEX, "loadstring"); 428 | 429 | lua_getfield(L, -1, "loadfile"); 430 | lua_setfield(L, LUA_GLOBALSINDEX, "loadfile"); 431 | 432 | lua_getfield(L, -1, "dofile"); 433 | lua_setfield(L, LUA_GLOBALSINDEX, "dofile"); 434 | 435 | lua_getfield(L, -1, "loader"); 436 | lua_rawseti(L, -3, 2); 437 | lua_pop(L, 2); 438 | } 439 | 440 | static int pmain(lua_State *L) 441 | { 442 | struct Smain *s = &smain; 443 | char **argv = s->argv; 444 | int script; 445 | int flags = 0; 446 | globalL = L; 447 | if (argv[0] && argv[0][0]) progname = argv[0]; 448 | LUAJIT_VERSION_SYM(); /* linker-enforced version check */ 449 | s->status = language_init(L); 450 | if (s->status != 0) { 451 | report(L, s->status); 452 | return 0; 453 | } 454 | script = collectargs(argv, &flags); 455 | if (script < 0) { /* invalid args? */ 456 | print_usage(); 457 | s->status = 1; 458 | return 0; 459 | } 460 | lua_gc(L, LUA_GCSTOP, 0); /* stop collector during initialization */ 461 | luaL_openlibs(L); /* open libraries */ 462 | override_loaders(L); 463 | lua_gc(L, LUA_GCRESTART, -1); 464 | if ((flags & FLAGS_VERSION)) print_version(); 465 | s->status = runargs(L, argv, (script > 0) ? script : s->argc); 466 | if (s->status != 0) return 0; 467 | if (script) { 468 | s->status = handle_script(L, argv, script); 469 | if (s->status != 0) return 0; 470 | } 471 | if ((flags & FLAGS_INTERACTIVE)) { 472 | print_jit_status(L); 473 | dotty(L); 474 | } else if (script == 0 && !(flags & (FLAGS_EXEC|FLAGS_VERSION))) { 475 | if (lua_stdin_is_tty()) { 476 | print_version(); 477 | print_jit_status(L); 478 | dotty(L); 479 | } else { 480 | dofile(L, NULL); /* executes stdin as a file */ 481 | } 482 | } 483 | return 0; 484 | } 485 | 486 | int main(int argc, char **argv) 487 | { 488 | int status; 489 | lua_State *L = lua_open(); /* create state */ 490 | if (L == NULL) { 491 | l_message(argv[0], "cannot create state: not enough memory"); 492 | return EXIT_FAILURE; 493 | } 494 | smain.argc = argc; 495 | smain.argv = argv; 496 | status = lua_cpcall(L, pmain, NULL); 497 | report(L, status); 498 | lua_close(L); 499 | return (status || smain.status) ? EXIT_FAILURE : EXIT_SUCCESS; 500 | } 501 | -------------------------------------------------------------------------------- /src/meson.build: -------------------------------------------------------------------------------- 1 | lang_cflags = [] 2 | 3 | if host_machine.system() == 'darwin' 4 | lang_cflags += ['-pagezero_size', '10000', '-image_base', '100000000'] 5 | endif 6 | 7 | lang_sources = ['language.c', 'language_loaders.c'] 8 | 9 | if bytecode_preload 10 | lang_whole_libs = [lang_bc_lib] 11 | lang_export_dynamic = true 12 | else 13 | lang_whole_libs = [] 14 | lang_export_dynamic = false 15 | endif 16 | 17 | luajit_dep = dependency('luajit') 18 | 19 | liblang = static_library('ljlangtk', 20 | lang_sources, 21 | c_args: lang_cflags, 22 | dependencies: luajit_dep, 23 | install: true, 24 | ) 25 | 26 | luajit_x = executable('luajit-x', 27 | 'luajit-x.c', 28 | dependencies: luajit_dep, 29 | c_args: lang_cflags, 30 | link_with: liblang, 31 | link_whole: lang_whole_libs, 32 | export_dynamic: lang_export_dynamic, 33 | install: true, 34 | ) 35 | 36 | liblang_dep = declare_dependency( 37 | include_directories: include_directories('.'), 38 | link_with: liblang, 39 | dependencies: luajit_dep, 40 | ) 41 | 42 | install_headers('language.h', 'language_loaders.h') 43 | 44 | pkg.generate(liblang, 45 | filebase : 'ljlangtk', 46 | name : 'LuaJIT Lang Toolkit', 47 | description : 'The LuaJIT Language Toolkit', 48 | url : 'https://github.com/franko/luajit-lang-toolkit', 49 | ) 50 | -------------------------------------------------------------------------------- /tests/assign-hazard-1.lua: -------------------------------------------------------------------------------- 1 | local t = {3, 7} 2 | local i = #t + 1 3 | t[i], t = 5, 11 4 | print(t) 5 | 6 | -------------------------------------------------------------------------------- /tests/assign-hazard-2.lua: -------------------------------------------------------------------------------- 1 | local t = {3, 7} 2 | local i = #t + 1 3 | t[i], i = 5, 11 4 | print(t[1], t[2], t[3], i) 5 | 6 | -------------------------------------------------------------------------------- /tests/closure-1.lua: -------------------------------------------------------------------------------- 1 | local function foo(x, y) 2 | local a, b, c 3 | local function boo(z) 4 | a, b = z*z + 1, z - 1 5 | end 6 | boo(x * y - x + y) 7 | c = x + y 8 | return a * b * c 9 | end 10 | 11 | print(foo(3, 7)) -------------------------------------------------------------------------------- /tests/closure-2.lua: -------------------------------------------------------------------------------- 1 | local function make(n) 2 | return { 3 | incr = function(i) n = n + i end, 4 | mult = function(i) n = n * i end, 5 | get = function() return n end, 6 | } 7 | end 8 | 9 | local obj = make(7) 10 | obj.mult(3) 11 | obj.incr(1) 12 | obj.mult(2) 13 | obj.incr(5) 14 | print(obj.get()) 15 | -------------------------------------------------------------------------------- /tests/closure-2B.lua: -------------------------------------------------------------------------------- 1 | local function make(p) 2 | if p > 10 then 3 | local n = p + 1 4 | return { 5 | incr = function(i) n = n + i end, 6 | mult = function(i) n = n * i end, 7 | get = function() return n end, 8 | } 9 | else 10 | local n = 2*p 11 | return { 12 | incr = function(i) n = n + i end, 13 | mult = function(i) n = n * i end, 14 | get = function() return n end, 15 | } 16 | end 17 | end 18 | 19 | local obj = make(7) 20 | obj.mult(3) 21 | obj.incr(1) 22 | obj.mult(2) 23 | obj.incr(5) 24 | print(obj.get()) 25 | -------------------------------------------------------------------------------- /tests/closure-3.lua: -------------------------------------------------------------------------------- 1 | local function foo(n) 2 | local f 3 | for k = n, n + 10 do 4 | if k % 7 == 0 then 5 | f = function(x) return k + x end 6 | break 7 | end 8 | end 9 | return f 10 | end 11 | 12 | local f = foo(1) 13 | print(f(3)) 14 | -------------------------------------------------------------------------------- /tests/closure-3b.lua: -------------------------------------------------------------------------------- 1 | 2 | local function foo(n) 3 | local f 4 | local s = 0 5 | for k = 1, n do 6 | local ksq, kc, kq = k*k, k^3, k^4 7 | if k % 7 == 0 then 8 | local kq = k/7 + 1 9 | f = function(x) return x + k end 10 | return f 11 | end 12 | local kadd = kc - kq 13 | s = s + kadd 14 | end 15 | return f 16 | end 17 | 18 | local f = foo(10) 19 | print(f(3)) 20 | -------------------------------------------------------------------------------- /tests/comment-1.lua: -------------------------------------------------------------------------------- 1 | --[[ This is just 2 | a very long comment 3 | ]] 4 | print("hello") 5 | 6 | -------------------------------------------------------------------------------- /tests/comment-2.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | This is just 3 | a very long comment!! 4 | ]] 5 | print("hello") 6 | 7 | -------------------------------------------------------------------------------- /tests/comment-3.lua: -------------------------------------------------------------------------------- 1 | --[==[ 2 | This is un elaborate Lua-style, long comment. 3 | Really long. 4 | And elaborate. 5 | ]==] 6 | print("hello") 7 | 8 | -------------------------------------------------------------------------------- /tests/concatenate-1.lua: -------------------------------------------------------------------------------- 1 | local function foo(a, flag) 2 | return a .. (flag and 'boom' or 'pst') 3 | end 4 | 5 | print(foo('ciao', true), foo('ciao', false)) -------------------------------------------------------------------------------- /tests/concatenate-2.lua: -------------------------------------------------------------------------------- 1 | local function foo(a, b, c) 2 | return a .. b .. c 3 | end 4 | 5 | print(foo('ciao', 'ciccio', 'boum')) 6 | -------------------------------------------------------------------------------- /tests/embedded-comment-1.lua: -------------------------------------------------------------------------------- 1 | local foo, bar = 3, 7 2 | print("foo bar"--[[]], foo, bar) 3 | 4 | -------------------------------------------------------------------------------- /tests/expect/closure-2.expect1.txt: -------------------------------------------------------------------------------- 1 | -- BYTECODE -- closure-2.lua:3-3 2 | 0001 UGET 1 0 ; n 3 | 0002 ADDVV 1 1 0 4 | 0003 USETV 0 1 ; n 5 | 0004 RET0 0 1 6 | 7 | -- BYTECODE -- closure-2.lua:4-4 8 | 0001 UGET 1 0 ; n 9 | 0002 MULVV 1 1 0 10 | 0003 USETV 0 1 ; n 11 | 0004 RET0 0 1 12 | 13 | -- BYTECODE -- closure-2.lua:5-5 14 | 0001 UGET 0 0 ; n 15 | 0002 RET1 0 2 16 | 17 | -- BYTECODE -- closure-2.lua:1-7 18 | 0001 TNEW 1 4096 19 | 0002 FNEW 2 1 ; closure-2.lua:3 20 | 0003 TSETS 2 1 0 ; "incr" 21 | 0004 FNEW 2 3 ; closure-2.lua:4 22 | 0005 TSETS 2 1 2 ; "mult" 23 | 0006 FNEW 2 5 ; closure-2.lua:5 24 | 0007 TSETS 2 1 4 ; "get" 25 | 0008 UCLO 0 => 0009 26 | 0009 => RET1 1 2 27 | 28 | -- BYTECODE -- closure-2.lua:0-15 29 | 0001 FNEW 0 0 ; closure-2.lua:1 30 | 0002 MOV 1 0 31 | 0003 KSHORT 2 7 32 | 0004 CALL 1 2 2 33 | 0005 TGETS 2 1 1 ; "mult" 34 | 0006 KSHORT 3 3 35 | 0007 CALL 2 1 2 36 | 0008 TGETS 2 1 2 ; "incr" 37 | 0009 KSHORT 3 1 38 | 0010 CALL 2 1 2 39 | 0011 TGETS 2 1 1 ; "mult" 40 | 0012 KSHORT 3 2 41 | 0013 CALL 2 1 2 42 | 0014 TGETS 2 1 2 ; "incr" 43 | 0015 KSHORT 3 5 44 | 0016 CALL 2 1 2 45 | 0017 GGET 2 3 ; "print" 46 | 0018 TGETS 3 1 4 ; "get" 47 | 0019 CALL 3 0 1 48 | 0020 CALLM 2 1 0 49 | 0021 RET0 0 1 50 | 51 | -------------------------------------------------------------------------------- /tests/expect/closure-2B.expect1.txt: -------------------------------------------------------------------------------- 1 | -- BYTECODE -- closure-2B.lua:5-5 2 | 0001 UGET 1 0 ; n 3 | 0002 ADDVV 1 1 0 4 | 0003 USETV 0 1 ; n 5 | 0004 RET0 0 1 6 | 7 | -- BYTECODE -- closure-2B.lua:6-6 8 | 0001 UGET 1 0 ; n 9 | 0002 MULVV 1 1 0 10 | 0003 USETV 0 1 ; n 11 | 0004 RET0 0 1 12 | 13 | -- BYTECODE -- closure-2B.lua:7-7 14 | 0001 UGET 0 0 ; n 15 | 0002 RET1 0 2 16 | 17 | -- BYTECODE -- closure-2B.lua:12-12 18 | 0001 UGET 1 0 ; n 19 | 0002 ADDVV 1 1 0 20 | 0003 USETV 0 1 ; n 21 | 0004 RET0 0 1 22 | 23 | -- BYTECODE -- closure-2B.lua:13-13 24 | 0001 UGET 1 0 ; n 25 | 0002 MULVV 1 1 0 26 | 0003 USETV 0 1 ; n 27 | 0004 RET0 0 1 28 | 29 | -- BYTECODE -- closure-2B.lua:14-14 30 | 0001 UGET 0 0 ; n 31 | 0002 RET1 0 2 32 | 33 | -- BYTECODE -- closure-2B.lua:1-17 34 | 0001 KSHORT 1 10 35 | 0002 ISGE 1 0 36 | 0003 JMP 1 => 0015 37 | 0004 ADDVN 1 0 0 ; 1 38 | 0005 TNEW 2 4096 39 | 0006 FNEW 3 1 ; closure-2B.lua:5 40 | 0007 TSETS 3 2 0 ; "incr" 41 | 0008 FNEW 3 3 ; closure-2B.lua:6 42 | 0009 TSETS 3 2 2 ; "mult" 43 | 0010 FNEW 3 5 ; closure-2B.lua:7 44 | 0011 TSETS 3 2 4 ; "get" 45 | 0012 UCLO 0 => 0013 46 | 0013 => RET1 2 2 47 | 0014 UCLO 1 => 0026 48 | 0015 => MULNV 1 0 1 ; 2 49 | 0016 TNEW 2 4096 50 | 0017 FNEW 3 6 ; closure-2B.lua:12 51 | 0018 TSETS 3 2 0 ; "incr" 52 | 0019 FNEW 3 7 ; closure-2B.lua:13 53 | 0020 TSETS 3 2 2 ; "mult" 54 | 0021 FNEW 3 8 ; closure-2B.lua:14 55 | 0022 TSETS 3 2 4 ; "get" 56 | 0023 UCLO 0 => 0024 57 | 0024 => RET1 2 2 58 | 0025 UCLO 1 => 0026 59 | 0026 => RET0 0 1 60 | 61 | -- BYTECODE -- closure-2B.lua:0-25 62 | 0001 FNEW 0 0 ; closure-2B.lua:1 63 | 0002 MOV 1 0 64 | 0003 KSHORT 2 7 65 | 0004 CALL 1 2 2 66 | 0005 TGETS 2 1 1 ; "mult" 67 | 0006 KSHORT 3 3 68 | 0007 CALL 2 1 2 69 | 0008 TGETS 2 1 2 ; "incr" 70 | 0009 KSHORT 3 1 71 | 0010 CALL 2 1 2 72 | 0011 TGETS 2 1 1 ; "mult" 73 | 0012 KSHORT 3 2 74 | 0013 CALL 2 1 2 75 | 0014 TGETS 2 1 2 ; "incr" 76 | 0015 KSHORT 3 5 77 | 0016 CALL 2 1 2 78 | 0017 GGET 2 3 ; "print" 79 | 0018 TGETS 3 1 4 ; "get" 80 | 0019 CALL 3 0 1 81 | 0020 CALLM 2 1 0 82 | 0021 RET0 0 1 83 | 84 | -------------------------------------------------------------------------------- /tests/expect/closure-3.expect1.txt: -------------------------------------------------------------------------------- 1 | -- BYTECODE -- closure-3.lua:5-5 2 | 0001 UGET 1 0 ; k 3 | 0002 ADDVV 1 1 0 4 | 0003 RET1 1 2 5 | 6 | -- BYTECODE -- closure-3.lua:1-10 7 | 0001 KPRI 1 0 8 | 0002 MOV 2 0 9 | 0003 ADDVN 3 0 0 ; 10 10 | 0004 KSHORT 4 1 11 | 0005 FORI 2 => 0013 12 | 0006 => MODVN 6 5 1 ; 7 13 | 0007 ISNEN 6 2 ; 0 14 | 0008 JMP 6 => 0011 15 | 0009 FNEW 1 0 ; closure-3.lua:5 16 | 0010 UCLO 2 => 0013 17 | 0011 => UCLO 5 => 0012 18 | 0012 => FORL 2 => 0006 19 | 0013 => RET1 1 2 20 | 21 | -- BYTECODE -- closure-3.lua:0-14 22 | 0001 FNEW 0 0 ; closure-3.lua:1 23 | 0002 MOV 1 0 24 | 0003 KSHORT 2 1 25 | 0004 CALL 1 2 2 26 | 0005 GGET 2 1 ; "print" 27 | 0006 MOV 3 1 28 | 0007 KSHORT 4 3 29 | 0008 CALL 3 0 2 30 | 0009 CALLM 2 1 0 31 | 0010 RET0 0 1 32 | 33 | -------------------------------------------------------------------------------- /tests/expect/closure-3b.expect1.txt: -------------------------------------------------------------------------------- 1 | -- BYTECODE -- closure-3b.lua:9-9 2 | 0001 UGET 1 0 ; k 3 | 0002 ADDVV 1 0 1 4 | 0003 RET1 1 2 5 | 6 | -- BYTECODE -- closure-3b.lua:2-16 7 | 0001 KPRI 1 0 8 | 0002 KSHORT 2 0 9 | 0003 KSHORT 3 1 10 | 0004 MOV 4 0 11 | 0005 KSHORT 5 1 12 | 0006 FORI 3 => 0024 13 | 0007 => MULVV 7 6 6 14 | 0008 KSHORT 8 3 15 | 0009 POW 8 6 8 16 | 0010 KSHORT 9 4 17 | 0011 POW 9 6 9 18 | 0012 MODVN 10 6 0 ; 7 19 | 0013 ISNEN 10 1 ; 0 20 | 0014 JMP 10 => 0020 21 | 0015 DIVVN 10 6 0 ; 7 22 | 0016 ADDVN 10 10 2 ; 1 23 | 0017 FNEW 1 0 ; closure-3b.lua:9 24 | 0018 UCLO 0 => 0019 25 | 0019 => RET1 1 2 26 | 0020 => SUBVV 10 8 9 27 | 0021 ADDVV 2 2 10 28 | 0022 UCLO 6 => 0023 29 | 0023 => FORL 3 => 0007 30 | 0024 => RET1 1 2 31 | 32 | -- BYTECODE -- closure-3b.lua:0-20 33 | 0001 FNEW 0 0 ; closure-3b.lua:2 34 | 0002 MOV 1 0 35 | 0003 KSHORT 2 10 36 | 0004 CALL 1 2 2 37 | 0005 GGET 2 1 ; "print" 38 | 0006 MOV 3 1 39 | 0007 KSHORT 4 3 40 | 0008 CALL 3 0 2 41 | 0009 CALLM 2 1 0 42 | 0010 RET0 0 1 43 | 44 | -------------------------------------------------------------------------------- /tests/expect/expr-prio-1.expect1.txt: -------------------------------------------------------------------------------- 1 | -- BYTECODE -- expr-prio-1.lua:0-14 2 | 0001 KSHORT 0 2 3 | 0002 KSHORT 1 3 4 | 0003 ADDNV 2 0 0 ; 2 5 | 0004 MULVV 2 2 1 6 | 0005 MULNV 3 1 0 ; 2 7 | 0006 ADDVV 3 0 3 8 | 0007 KSHORT 4 2 9 | 0008 POW 5 0 1 10 | 0009 POW 4 4 5 11 | 0010 KSHORT 5 2 12 | 0011 POW 5 5 0 13 | 0012 POW 5 5 1 14 | 0013 MULNV 6 0 0 ; 2 15 | 0014 DIVVV 6 6 1 16 | 0015 DIVVV 7 0 1 17 | 0016 MULNV 7 7 0 ; 2 18 | 0017 ADDNV 8 0 0 ; 2 19 | 0018 ADDVV 8 8 1 20 | 0019 ADDVV 9 0 1 21 | 0020 ADDNV 9 9 0 ; 2 22 | 0021 UNM 10 0 23 | 0022 MULVV 10 10 1 24 | 0023 KSHORT 11 2 25 | 0024 POW 11 0 11 26 | 0025 UNM 11 11 27 | 0026 UNM 12 0 28 | 0027 KSHORT 13 2 29 | 0028 POW 12 12 13 30 | 0029 ADDVV 11 11 12 31 | 0030 GGET 12 0 ; "print" 32 | 0031 MOV 13 2 33 | 0032 MOV 14 3 34 | 0033 MOV 15 4 35 | 0034 MOV 16 5 36 | 0035 MOV 17 6 37 | 0036 MOV 18 7 38 | 0037 MOV 19 8 39 | 0038 MOV 20 9 40 | 0039 MOV 21 10 41 | 0040 MOV 22 11 42 | 0041 CALL 12 1 11 43 | 0042 RET0 0 1 44 | 45 | -------------------------------------------------------------------------------- /tests/expect/for-statement-3.expect1.txt: -------------------------------------------------------------------------------- 1 | -- BYTECODE -- for-statement-3.lua:6-6 2 | 0001 UGET 1 0 ; i 3 | 0002 ADDVV 1 1 0 4 | 0003 RET1 1 2 5 | 6 | -- BYTECODE -- for-statement-3.lua:1-9 7 | 0001 KSHORT 1 0 8 | 0002 TNEW 2 0 9 | 0003 KSHORT 3 1 10 | 0004 MOV 4 0 11 | 0005 KSHORT 5 1 12 | 0006 FORI 3 => 0013 13 | 0007 => MULVV 7 6 6 14 | 0008 ADDVV 1 1 7 15 | 0009 FNEW 7 0 ; for-statement-3.lua:6 16 | 0010 TSETV 7 2 6 17 | 0011 UCLO 6 => 0012 18 | 0012 => FORL 3 => 0007 19 | 0013 => MOV 3 1 20 | 0014 MOV 4 2 21 | 0015 RET 3 3 22 | 23 | -- BYTECODE -- for-statement-3.lua:0-14 24 | 0001 FNEW 0 0 ; for-statement-3.lua:1 25 | 0002 MOV 1 0 26 | 0003 KSHORT 2 10 27 | 0004 CALL 1 3 2 28 | 0005 GGET 3 1 ; "print" 29 | 0006 MOV 4 1 30 | 0007 CALL 3 1 2 31 | 0008 GGET 3 1 ; "print" 32 | 0009 TGETB 4 2 1 33 | 0010 KSHORT 5 7 34 | 0011 CALL 4 2 2 35 | 0012 TGETB 5 2 2 36 | 0013 KSHORT 6 7 37 | 0014 CALL 5 2 2 38 | 0015 TGETB 6 2 3 39 | 0016 KSHORT 7 7 40 | 0017 CALL 6 0 2 41 | 0018 CALLM 3 1 2 42 | 0019 RET0 0 1 43 | 44 | -------------------------------------------------------------------------------- /tests/expect/forin-3.expect1.txt: -------------------------------------------------------------------------------- 1 | -- BYTECODE -- forin-3.lua:6-6 2 | 0001 UGET 1 0 ; i 3 | 0002 ADDVV 1 1 0 4 | 0003 RET1 1 2 5 | 6 | -- BYTECODE -- forin-3.lua:1-9 7 | 0001 KSHORT 1 0 8 | 0002 TNEW 2 0 9 | 0003 GGET 3 0 ; "ipairs" 10 | 0004 MOV 4 0 11 | 0005 CALL 3 4 2 12 | 0006 JMP 6 => 0012 13 | 0007 => MULVV 8 7 7 14 | 0008 ADDVV 1 1 8 15 | 0009 FNEW 8 1 ; forin-3.lua:6 16 | 0010 TSETV 8 2 6 17 | 0011 UCLO 6 => 0012 18 | 0012 => ITERC 6 3 3 19 | 0013 ITERL 6 => 0007 20 | 0014 MOV 3 1 21 | 0015 MOV 4 2 22 | 0016 RET 3 3 23 | 24 | -- BYTECODE -- forin-3.lua:0-14 25 | 0001 FNEW 0 0 ; forin-3.lua:1 26 | 0002 MOV 1 0 27 | 0003 TDUP 2 1 28 | 0004 CALL 1 3 2 29 | 0005 GGET 3 2 ; "print" 30 | 0006 MOV 4 1 31 | 0007 CALL 3 1 2 32 | 0008 GGET 3 2 ; "print" 33 | 0009 TGETB 4 2 1 34 | 0010 KSHORT 5 7 35 | 0011 CALL 4 2 2 36 | 0012 TGETB 5 2 2 37 | 0013 KSHORT 6 7 38 | 0014 CALL 5 2 2 39 | 0015 TGETB 6 2 3 40 | 0016 KSHORT 7 7 41 | 0017 CALL 6 0 2 42 | 0018 CALLM 3 1 2 43 | 0019 RET0 0 1 44 | 45 | -------------------------------------------------------------------------------- /tests/expect/goto-for-1.expect1.txt: -------------------------------------------------------------------------------- 1 | -- BYTECODE -- goto-for-1.lua:1-10 2 | 0001 GGET 1 0 ; "ipairs" 3 | 0002 MOV 2 0 4 | 0003 CALL 1 4 2 5 | 0004 JMP 4 => 0012 6 | 0005 => MODVN 6 5 0 ; 2 7 | 0006 ISNEN 6 1 ; 0 8 | 0007 JMP 6 => 0012 9 | 0008 GGET 6 1 ; "print" 10 | 0009 KSTR 7 2 ; "list has even number" 11 | 0010 CALL 6 1 2 12 | 0011 JMP 1 => 0017 13 | 0012 => ITERC 4 3 3 14 | 0013 ITERL 4 => 0005 15 | 0014 GGET 1 1 ; "print" 16 | 0015 KSTR 2 3 ; "list lacks even number" 17 | 0016 CALL 1 1 2 18 | 0017 => RET0 0 1 19 | 20 | -- BYTECODE -- goto-for-1.lua:0-13 21 | 0001 FNEW 0 0 ; goto-for-1.lua:1 22 | 0002 MOV 1 0 23 | 0003 TDUP 2 1 24 | 0004 CALL 1 1 2 25 | 0005 RET0 0 1 26 | 27 | -------------------------------------------------------------------------------- /tests/expect/logical-tests-3.expect1.txt: -------------------------------------------------------------------------------- 1 | -- BYTECODE -- logical-tests-3.lua:1-4 2 | 0001 ISLT 0 1 3 | 0002 JMP 2 => 0005 4 | 0003 KPRI 2 1 5 | 0004 JMP 3 => 0012 6 | 0005 => MULVV 2 0 0 7 | 0006 MULVV 3 1 1 8 | 0007 ISLT 2 3 9 | 0008 JMP 2 => 0011 10 | 0009 KPRI 2 1 11 | 0010 JMP 3 => 0012 12 | 0011 => KPRI 2 2 13 | 0012 => RET1 2 2 14 | 15 | -- BYTECODE -- logical-tests-3.lua:0-7 16 | 0001 FNEW 0 0 ; logical-tests-3.lua:1 17 | 0002 GGET 1 1 ; "print" 18 | 0003 MOV 2 0 19 | 0004 KSHORT 3 3 20 | 0005 KSHORT 4 7 21 | 0006 CALL 2 2 3 22 | 0007 MOV 3 0 23 | 0008 KSHORT 4 7 24 | 0009 KSHORT 5 3 25 | 0010 CALL 3 0 3 26 | 0011 CALLM 1 1 1 27 | 0012 RET0 0 1 28 | 29 | -------------------------------------------------------------------------------- /tests/expect/logical-tests-4b.expect1.txt: -------------------------------------------------------------------------------- 1 | -- BYTECODE -- logical-tests-4b.lua:1-3 2 | 0001 ISLT 0 1 3 | 0002 JMP 2 => 0005 4 | 0003 KPRI 2 1 5 | 0004 JMP 3 => 0006 6 | 0005 => KPRI 2 2 7 | 0006 => NOT 2 2 8 | 0007 RET1 2 2 9 | 10 | -- BYTECODE -- logical-tests-4b.lua:0-7 11 | 0001 FNEW 0 0 ; logical-tests-4b.lua:1 12 | 0002 KSHORT 1 7 13 | 0003 KSHORT 2 3 14 | 0004 GGET 3 1 ; "print" 15 | 0005 MOV 4 0 16 | 0006 MOV 5 1 17 | 0007 MOV 6 2 18 | 0008 CALL 4 2 3 19 | 0009 MOV 5 0 20 | 0010 MOV 6 2 21 | 0011 MOV 7 1 22 | 0012 CALL 5 0 3 23 | 0013 CALLM 3 1 1 24 | 0014 RET0 0 1 25 | 26 | -------------------------------------------------------------------------------- /tests/expect/logical-value-1.expect1.txt: -------------------------------------------------------------------------------- 1 | -- BYTECODE -- logical-value-1.lua:1-3 2 | 0001 ISLT 0 1 3 | 0002 JMP 2 => 0005 4 | 0003 KPRI 2 1 5 | 0004 JMP 3 => 0006 6 | 0005 => ADDVV 2 0 1 7 | 0006 => RET1 2 2 8 | 9 | -- BYTECODE -- logical-value-1.lua:5-7 10 | 0001 ISLT 0 1 11 | 0002 JMP 3 => 0005 12 | 0003 KPRI 3 1 13 | 0004 JMP 4 => 0009 14 | 0005 => MOV 3 2 15 | 0006 MOV 4 0 16 | 0007 MOV 5 1 17 | 0008 CALL 3 2 3 18 | 0009 => RET1 3 2 19 | 20 | -- BYTECODE -- logical-value-1.lua:9-11 21 | 0001 ISLT 0 1 22 | 0002 JMP 2 => 0005 23 | 0003 KPRI 2 1 24 | 0004 JMP 3 => 0010 25 | 0005 => ISEQV 0 1 26 | 0006 JMP 2 => 0009 27 | 0007 KPRI 2 1 28 | 0008 JMP 3 => 0010 29 | 0009 => KPRI 2 2 30 | 0010 => RET1 2 2 31 | 32 | -- BYTECODE -- logical-value-1.lua:13-15 33 | 0001 ISGE 0 1 34 | 0002 JMP 2 => 0005 35 | 0003 KPRI 2 2 36 | 0004 JMP 3 => 0006 37 | 0005 => SUBVV 2 0 1 38 | 0006 => RET1 2 2 39 | 40 | -- BYTECODE -- logical-value-1.lua:17-19 41 | 0001 ISGE 0 1 42 | 0002 JMP 2 => 0005 43 | 0003 KPRI 2 2 44 | 0004 JMP 3 => 0010 45 | 0005 => ISEQV 0 1 46 | 0006 JMP 2 => 0009 47 | 0007 KPRI 2 1 48 | 0008 JMP 3 => 0010 49 | 0009 => KPRI 2 2 50 | 0010 => RET1 2 2 51 | 52 | -- BYTECODE -- logical-value-1.lua:21-21 53 | 0001 SUBVV 2 0 1 54 | 0002 RET1 2 2 55 | 56 | -- BYTECODE -- logical-value-1.lua:0-28 57 | 0001 FNEW 0 0 ; logical-value-1.lua:1 58 | 0002 FNEW 1 1 ; logical-value-1.lua:5 59 | 0003 FNEW 2 2 ; logical-value-1.lua:9 60 | 0004 FNEW 3 3 ; logical-value-1.lua:13 61 | 0005 FNEW 4 4 ; logical-value-1.lua:17 62 | 0006 FNEW 5 5 ; logical-value-1.lua:21 63 | 0007 KSHORT 6 3 64 | 0008 KSHORT 7 7 65 | 0009 GGET 8 6 ; "print" 66 | 0010 MOV 9 0 67 | 0011 MOV 10 6 68 | 0012 MOV 11 7 69 | 0013 CALL 9 2 3 70 | 0014 MOV 10 0 71 | 0015 MOV 11 7 72 | 0016 MOV 12 6 73 | 0017 CALL 10 0 3 74 | 0018 CALLM 8 1 1 75 | 0019 GGET 8 6 ; "print" 76 | 0020 MOV 9 1 77 | 0021 MOV 10 6 78 | 0022 MOV 11 7 79 | 0023 MOV 12 5 80 | 0024 CALL 9 2 4 81 | 0025 MOV 10 1 82 | 0026 MOV 11 7 83 | 0027 MOV 12 6 84 | 0028 MOV 13 5 85 | 0029 CALL 10 0 4 86 | 0030 CALLM 8 1 1 87 | 0031 GGET 8 6 ; "print" 88 | 0032 MOV 9 2 89 | 0033 MOV 10 6 90 | 0034 MOV 11 7 91 | 0035 CALL 9 2 3 92 | 0036 MOV 10 2 93 | 0037 MOV 11 7 94 | 0038 MOV 12 6 95 | 0039 CALL 10 0 3 96 | 0040 CALLM 8 1 1 97 | 0041 GGET 8 6 ; "print" 98 | 0042 MOV 9 3 99 | 0043 MOV 10 6 100 | 0044 MOV 11 7 101 | 0045 CALL 9 2 3 102 | 0046 MOV 10 3 103 | 0047 MOV 11 7 104 | 0048 MOV 12 6 105 | 0049 CALL 10 0 3 106 | 0050 CALLM 8 1 1 107 | 0051 GGET 8 6 ; "print" 108 | 0052 MOV 9 4 109 | 0053 MOV 10 6 110 | 0054 MOV 11 7 111 | 0055 CALL 9 2 3 112 | 0056 MOV 10 4 113 | 0057 MOV 11 7 114 | 0058 MOV 12 6 115 | 0059 CALL 10 0 3 116 | 0060 CALLM 8 1 1 117 | 0061 RET0 0 1 118 | 119 | -------------------------------------------------------------------------------- /tests/expect/not-operator-3.expect1.txt: -------------------------------------------------------------------------------- 1 | -- BYTECODE -- not-operator-3.lua:0-5 2 | 0001 KSHORT 0 1 3 | 0002 ISEQN 0 0 ; 1 4 | 0003 JMP 0 => 0006 5 | 0004 KPRI 0 1 6 | 0005 JMP 1 => 0007 7 | 0006 => KPRI 0 2 8 | 0007 => KSHORT 1 1 9 | 0008 ISEQN 1 1 ; 0 10 | 0009 JMP 1 => 0012 11 | 0010 KPRI 1 1 12 | 0011 JMP 2 => 0013 13 | 0012 => KPRI 1 2 14 | 0013 => GGET 2 0 ; "print" 15 | 0014 NOT 3 0 16 | 0015 NOT 4 0 17 | 0016 ISF 4 18 | 0017 JMP 5 => 0019 19 | 0018 KSHORT 4 10 20 | 0019 => NOT 5 0 21 | 0020 IST 5 22 | 0021 JMP 6 => 0023 23 | 0022 KSHORT 5 10 24 | 0023 => CALL 2 1 4 25 | 0024 GGET 2 0 ; "print" 26 | 0025 NOT 3 1 27 | 0026 NOT 4 1 28 | 0027 ISF 4 29 | 0028 JMP 5 => 0030 30 | 0029 KSHORT 4 10 31 | 0030 => NOT 5 1 32 | 0031 IST 5 33 | 0032 JMP 6 => 0034 34 | 0033 KSHORT 5 10 35 | 0034 => CALL 2 1 4 36 | 0035 RET0 0 1 37 | 38 | -------------------------------------------------------------------------------- /tests/expect/repeat-test-2.expect1.txt: -------------------------------------------------------------------------------- 1 | -- BYTECODE -- repeat-test-2.lua:1-11 2 | 0001 KSHORT 1 0 3 | 0002 KSHORT 2 1 4 | 0003 KPRI 3 0 5 | 0004 => LOOP 4 => 0015 6 | 0005 MULVV 4 2 2 7 | 0006 ADDVV 1 1 4 8 | 0007 ADDVN 2 2 0 ; 1 9 | 0008 ISLT 0 2 10 | 0009 JMP 4 => 0012 11 | 0010 KPRI 3 1 12 | 0011 JMP 4 => 0013 13 | 0012 => KSHORT 3 1 14 | 0013 => ISF 3 15 | 0014 JMP 4 => 0004 16 | 0015 => RET1 1 2 17 | 18 | -- BYTECODE -- repeat-test-2.lua:0-14 19 | 0001 FNEW 0 0 ; repeat-test-2.lua:1 20 | 0002 GGET 1 1 ; "print" 21 | 0003 MOV 2 0 22 | 0004 KSHORT 3 20 23 | 0005 CALL 2 0 2 24 | 0006 CALLM 1 1 0 25 | 0007 RET0 0 1 26 | 27 | -------------------------------------------------------------------------------- /tests/expect/send-epression-call-2.expect1.txt: -------------------------------------------------------------------------------- 1 | -- BYTECODE -- send-epression-call-2.lua:1-3 2 | 0001 TGETS 2 0 0 ; "value" 3 | 0002 MULVV 2 1 2 4 | 0003 RET1 2 2 5 | 6 | -- BYTECODE -- send-epression-call-2.lua:5-7 7 | 0001 TNEW 1 2048 8 | 0002 TSETS 0 1 0 ; "value" 9 | 0003 UGET 2 0 ; foo 10 | 0004 TSETS 2 1 1 ; "foo" 11 | 0005 RET1 1 2 12 | 13 | -- BYTECODE -- send-epression-call-2.lua:0-10 14 | 0001 FNEW 0 0 ; send-epression-call-2.lua:1 15 | 0002 FNEW 1 1 ; send-epression-call-2.lua:5 16 | 0003 GGET 2 2 ; "print" 17 | 0004 MOV 3 1 18 | 0005 KNUM 4 0 ; 3.141592 19 | 0006 CALL 3 2 2 20 | 0007 MOV 4 3 21 | 0008 TGETS 3 3 3 ; "foo" 22 | 0009 KSHORT 5 2 23 | 0010 CALL 3 0 3 24 | 0011 CALLM 2 1 0 25 | 0012 UCLO 0 => 0013 26 | 0013 => RET0 0 1 27 | 28 | -------------------------------------------------------------------------------- /tests/expect/table-4.expect1.txt: -------------------------------------------------------------------------------- 1 | -- BYTECODE -- table-4.lua:0-3 2 | 0001 TDUP 0 0 3 | 0002 TDUP 1 2 4 | 0003 TSETS 1 0 1 ; "list" 5 | 0004 GGET 1 3 ; "print" 6 | 0005 TGETS 2 0 4 ; "pi" 7 | 0006 TGETB 3 0 1 8 | 0007 TGETB 4 0 2 9 | 0008 TGETB 5 0 3 10 | 0009 TGETS 6 0 1 ; "list" 11 | 0010 LEN 6 6 12 | 0011 CALL 1 1 6 13 | 0012 RET0 0 1 14 | 15 | -------------------------------------------------------------------------------- /tests/expect/test-6.expect1.txt: -------------------------------------------------------------------------------- 1 | -- BYTECODE -- test-6.lua:0-6 2 | 0001 KPRI 0 0 3 | 0002 KPRI 1 0 4 | 0003 ISTC 0 1 5 | 0004 JMP 2 => 0006 6 | 0005 KSHORT 0 12 7 | 0006 => GGET 2 0 ; "print" 8 | 0007 MOV 3 0 9 | 0008 MOV 4 1 10 | 0009 CALL 2 1 3 11 | 0010 RET0 0 1 12 | 13 | -------------------------------------------------------------------------------- /tests/expect/test-9.expect1.txt: -------------------------------------------------------------------------------- 1 | -- BYTECODE -- test-9.lua:0-4 2 | 0001 KSHORT 0 5 3 | 0002 KSHORT 1 10 4 | 0003 ISGE 1 0 5 | 0004 JMP 1 => 0007 6 | 0005 KPRI 0 2 7 | 0006 JMP 1 => 0008 8 | 0007 => ADDVN 0 0 0 ; 1 9 | 0008 => GGET 1 0 ; "print" 10 | 0009 MOV 2 0 11 | 0010 CALL 1 1 2 12 | 0011 RET0 0 1 13 | 14 | -------------------------------------------------------------------------------- /tests/expr-bracket-1.lua: -------------------------------------------------------------------------------- 1 | local function foo(x) 2 | return x, x + 1, 2*x 3 | end 4 | 5 | local function prova(...) 6 | return ... 7 | end 8 | 9 | local function one(...) 10 | return (...) 11 | end 12 | 13 | local function single_value(x) 14 | return (foo(x)) 15 | end 16 | 17 | local function alls(x) 18 | return foo(x) 19 | end 20 | 21 | print(foo(2)) 22 | print( (foo(2)) ) 23 | 24 | print(alls(foo(2))) 25 | print(single_value(foo(2))) 26 | 27 | print( one(foo(2)) ) 28 | 29 | print( prova(foo(2)) ) 30 | -------------------------------------------------------------------------------- /tests/expr-logical-1.lua: -------------------------------------------------------------------------------- 1 | local function foo(x, y) 2 | return x < y and x*x or y*y 3 | end 4 | 5 | local x, y = 3, 7 6 | print(foo(x, y)) 7 | -------------------------------------------------------------------------------- /tests/expr-logical-2.lua: -------------------------------------------------------------------------------- 1 | local function boo(a, b) 2 | if b then return a else return a + 1 end 3 | end 4 | 5 | local function foo(x, y) 6 | local z = x + y 7 | return boo(z, x < y and x or y) 8 | end 9 | 10 | print(foo(3, 7)) 11 | -------------------------------------------------------------------------------- /tests/expr-logical-3.lua: -------------------------------------------------------------------------------- 1 | local function foo(x, y) 2 | return (x < y and x*x or y*y) + 2*x*y + 7 3 | end 4 | 5 | print(foo(3, 7), foo(7, 3)) 6 | -------------------------------------------------------------------------------- /tests/expr-prio-1.lua: -------------------------------------------------------------------------------- 1 | local x, y = 2, 3 2 | 3 | local a = (2 + x) * y 4 | local b = x + 2 * y 5 | local c = 2 ^ x ^ y 6 | local d = (2 ^ x) ^ y 7 | local e = 2 * x / y 8 | local f = 2 * (x / y) 9 | local g = 2 + x + y 10 | local h = 2 + (x + y) 11 | local i = -x * y 12 | local l = -x^2 + (-x)^2 13 | 14 | print(a, b, c, d, e, f, g, h, i, l) -------------------------------------------------------------------------------- /tests/expr-return-1.lua: -------------------------------------------------------------------------------- 1 | local function f1(x, y, z) 2 | return y 3 | end 4 | 5 | local function f2(x, y, z) 6 | return x, y, z 7 | end 8 | 9 | local function f3(x, y, z) 10 | return x + y + z 11 | end 12 | 13 | local u = 7 14 | local function f4(x) 15 | return x + u 16 | end 17 | 18 | local function f5(x) 19 | return u 20 | end 21 | 22 | local a, b, c = 3, 5, 21 23 | print(f1(a, b, c)) 24 | print(f2(a, b, c)) 25 | print(f3(a, b, c)) 26 | print(f4(a)) 27 | print(f5(a)) 28 | -------------------------------------------------------------------------------- /tests/expr-var-return-1.lua: -------------------------------------------------------------------------------- 1 | local function multi(...) 2 | return ... 3 | end 4 | 5 | local function onearg1(x, ...) 6 | print("onearg1", x) 7 | return x, ... 8 | end 9 | 10 | local function onearg2(x, ...) 11 | print("onearg2", x) 12 | return ... 13 | end 14 | 15 | local a, b, c = 3, 7, 21 16 | print(multi(a, b, c)) 17 | print(onearg1(a, b, c)) 18 | print(onearg2(a, b, c)) 19 | -------------------------------------------------------------------------------- /tests/fft-1.lua: -------------------------------------------------------------------------------- 1 | local sin, pi = math.sin, math.pi 2 | 3 | local function fft_bitreverse(v, n) 4 | local j = 0 5 | for i=0,2*n-4,2 do 6 | if i < j then 7 | v[i+1], v[i+2], v[j+1], v[j+2] = v[j+1], v[j+2], v[i+1], v[i+2] 8 | end 9 | local k = n 10 | while k <= j do j = j - k; k = k / 2 end 11 | j = j + k 12 | end 13 | end 14 | 15 | local function fft_transform(v, n, dir) 16 | if n <= 1 then return end 17 | fft_bitreverse(v, n) 18 | local dual = 1 19 | repeat 20 | local dual2 = 2*dual 21 | for i=1,2*n-1,2*dual2 do 22 | local j = i+dual2 23 | local ir, ii = v[i], v[i+1] 24 | local jr, ji = v[j], v[j+1] 25 | v[j], v[j+1] = ir - jr, ii - ji 26 | v[i], v[i+1] = ir + jr, ii + ji 27 | end 28 | local theta = dir * pi / dual 29 | local s, s2 = sin(theta), 2.0 * sin(theta * 0.5)^2 30 | local wr, wi = 1.0, 0.0 31 | for a=3,dual2-1,2 do 32 | wr, wi = wr - s*wi - s2*wr, wi + s*wr - s2*wi 33 | for i=a,a+2*(n-dual2),2*dual2 do 34 | local j = i+dual2 35 | local jr, ji = v[j], v[j+1] 36 | local dr, di = wr*jr - wi*ji, wr*ji + wi*jr 37 | local ir, ii = v[i], v[i+1] 38 | v[j], v[j+1] = ir - dr, ii - di 39 | v[i], v[i+1] = ir + dr, ii + di 40 | end 41 | end 42 | dual = dual2 43 | until dual >= n 44 | end 45 | 46 | local n = 64 47 | 48 | local v = {} 49 | for k = 1, 2*n do 50 | v[k] = (k - 1) % (n / 4) 51 | end 52 | 53 | fft_transform(v, n, -1) 54 | for k = 1, 2*n do print(v[k]) end 55 | 56 | fft_transform(v, n, 1) 57 | for k = 1, 2*n do print(v[k] / n) end 58 | -------------------------------------------------------------------------------- /tests/for-statement-1.lua: -------------------------------------------------------------------------------- 1 | local function foo(n) 2 | local sum = 0 3 | for i = 1, n do 4 | sum = sum + i*i 5 | end 6 | return sum 7 | end 8 | 9 | print(foo(10)) 10 | -------------------------------------------------------------------------------- /tests/for-statement-2.lua: -------------------------------------------------------------------------------- 1 | local function foo(n) 2 | local sum = 0 3 | for i = n, n*n+n, n+1 do 4 | sum = sum + i*i 5 | end 6 | return sum 7 | end 8 | 9 | print(foo(10)) 10 | -------------------------------------------------------------------------------- /tests/for-statement-3.lua: -------------------------------------------------------------------------------- 1 | local function foo(n) 2 | local sum = 0 3 | local fs = {} 4 | for i = 1, n do 5 | sum = sum + i*i 6 | fs[i] = function(x) return i + x end 7 | end 8 | return sum, fs 9 | end 10 | 11 | local s, fs = foo(10) 12 | print(s) 13 | print(fs[1](7), fs[2](7), fs[3](7)) 14 | -------------------------------------------------------------------------------- /tests/for-statement-4.lua: -------------------------------------------------------------------------------- 1 | local foo = function() 2 | return 4 3 | end 4 | 5 | for i = 1, foo() do 6 | print("A", i) 7 | end 8 | 9 | for i = 1, 4 do 10 | print("B", i) 11 | end 12 | -------------------------------------------------------------------------------- /tests/forin-1.lua: -------------------------------------------------------------------------------- 1 | local function foo(ls) 2 | local sum = 0 3 | for i, v in ipairs(ls) do 4 | sum = sum + v*v 5 | end 6 | return sum 7 | end 8 | 9 | print(foo {1, 3, 7, 12}) 10 | -------------------------------------------------------------------------------- /tests/forin-2.lua: -------------------------------------------------------------------------------- 1 | local function squares_iter(ls, i) 2 | if i + 1 <= #ls then 3 | local v = ls[i + 1] 4 | return i + 1, v, v*v 5 | end 6 | end 7 | 8 | local function squares(ls) 9 | return squares_iter, ls, 0 10 | end 11 | 12 | local function foo(ls) 13 | local s, ssq = 0, 0 14 | for i, v, vsq in squares(ls) do 15 | s = s + v 16 | ssq = ssq + vsq 17 | end 18 | return s, ssq 19 | end 20 | 21 | print(foo {3, 7, -2, 5}) 22 | -------------------------------------------------------------------------------- /tests/forin-3.lua: -------------------------------------------------------------------------------- 1 | local function foo(ls) 2 | local sum = 0 3 | local fs = {} 4 | for i, v in ipairs(ls) do 5 | sum = sum + v*v 6 | fs[i] = function(x) return i + x end 7 | end 8 | return sum, fs 9 | end 10 | 11 | local sum, fs = foo {1, 3, 7, 12} 12 | print(sum) 13 | print(fs[1](7), fs[2](7), fs[3](7)) 14 | -------------------------------------------------------------------------------- /tests/forin-4.lua: -------------------------------------------------------------------------------- 1 | -- Use a very weak pseudo-number generator just for testing purpose. 2 | local function my_random(s) 3 | s.x = (16807 * s.x) % 2147483647 4 | return s.x 5 | end 6 | 7 | local function my_iter(s, i) 8 | if i < 20 then return i + 1, my_random(s) end 9 | end 10 | 11 | local function foo() 12 | local s = { x = 13 } 13 | for i, value in my_iter, s, 0 do 14 | print(i, value) 15 | end 16 | end 17 | 18 | foo() 19 | -------------------------------------------------------------------------------- /tests/func-assign-1.lua: -------------------------------------------------------------------------------- 1 | local a, b = 3, 7 2 | 3 | local function foo(k) 4 | return 2*k + 1 5 | end 6 | 7 | print(foo(a)) 8 | -------------------------------------------------------------------------------- /tests/func-assign-2.lua: -------------------------------------------------------------------------------- 1 | local foo 2 | 3 | local function init(choice) 4 | if choice == 1 then 5 | function foo(x) 6 | return x*x + 1 7 | end 8 | else 9 | function foo(x) 10 | return x 11 | end 12 | end 13 | end 14 | 15 | init(1) 16 | print(foo(3)) 17 | -------------------------------------------------------------------------------- /tests/func-assign-3.lua: -------------------------------------------------------------------------------- 1 | local foo = function(k) return k*k+1 end 2 | print(foo(3)) 3 | -------------------------------------------------------------------------------- /tests/func-declaration-1.lua: -------------------------------------------------------------------------------- 1 | local foo = {} 2 | 3 | function foo.bar(x) 4 | return x*x + 1 5 | end 6 | 7 | print(foo.bar(4)) 8 | -------------------------------------------------------------------------------- /tests/func-empty-1.lua: -------------------------------------------------------------------------------- 1 | local function func() 2 | end 3 | 4 | print(".", func(), ".") 5 | 6 | -------------------------------------------------------------------------------- /tests/func-vararg-1.lua: -------------------------------------------------------------------------------- 1 | local format = string.format 2 | 3 | local function printf(fmt, ...) 4 | print(format(fmt, ...)) 5 | end 6 | 7 | local name = "Ciccio" 8 | printf("Ciao %s, come stai ?", name) 9 | local a, b = 3.25, 225 10 | printf("Some numbers, float: %g, integer: %i", a, b) 11 | 12 | -------------------------------------------------------------------------------- /tests/func.lua: -------------------------------------------------------------------------------- 1 | local function foo(a, b) 2 | return 2*a + b 3 | end 4 | print(foo(3,4)) 5 | 6 | -------------------------------------------------------------------------------- /tests/function-rec.lua: -------------------------------------------------------------------------------- 1 | local function fact(n) 2 | if n <= 1 then 3 | return 1 4 | else 5 | return n * fact(n-1) 6 | end 7 | end 8 | 9 | print(fact(5)) 10 | -------------------------------------------------------------------------------- /tests/function-tailrec.lua: -------------------------------------------------------------------------------- 1 | local function fact(n, accu) 2 | if n <= 1 then 3 | return accu 4 | else 5 | return fact(n - 1, n * accu) 6 | end 7 | end 8 | 9 | print(fact(5, 1)) 10 | -------------------------------------------------------------------------------- /tests/goto-cleanup-1.lua: -------------------------------------------------------------------------------- 1 | 2 | local function undo_h() print("undo_h") end 3 | local function undo_g() print("undo_g") end 4 | 5 | function f(x, y, z) 6 | if x > y then goto fail end 7 | if x > z then goto cleanup_g end 8 | if x > y - z then goto cleanup_h end 9 | do 10 | print("got it!") 11 | return true 12 | end -- need do/end? 13 | 14 | ::cleanup_h:: 15 | undo_h() 16 | ::cleanup_g:: 17 | undo_g() 18 | ::fail:: 19 | return false 20 | end 21 | 22 | print(f(3, 10, 20)) 23 | print(f(3, 0, 20)) 24 | print(f(3, 10, 0)) 25 | print(f(3, 10, 10)) 26 | -------------------------------------------------------------------------------- /tests/goto-closure-1.lua: -------------------------------------------------------------------------------- 1 | local ls = { } 2 | 3 | for z=1,10 do 4 | for y=1,10 do 5 | for x=1,10 do 6 | if x^2 + y^2 == z^2 then 7 | ls[#ls+1] = function() return x, y, z end 8 | goto zcontinue 9 | end 10 | end end ::zcontinue:: end 11 | 12 | for i = 1, #ls do 13 | print('found a Pythagorean triple', ls[i]()) 14 | end 15 | -------------------------------------------------------------------------------- /tests/goto-closure-2.lua: -------------------------------------------------------------------------------- 1 | local l = { } 2 | 3 | for x=1, 5 do ::redo:: 4 | local y = x^2 + 1 5 | if x < 30 then 6 | l[#l+1] = function() return y end 7 | x = y 8 | goto redo 9 | end 10 | end 11 | 12 | for k = 1, #l do 13 | print(l[k]()) 14 | end -------------------------------------------------------------------------------- /tests/goto-for-1.lua: -------------------------------------------------------------------------------- 1 | local function hasfn(t) 2 | for _, x in ipairs(t) do 3 | if x % 2 == 0 then 4 | print 'list has even number' 5 | goto has 6 | end 7 | end 8 | print 'list lacks even number' 9 | ::has:: 10 | end 11 | 12 | hasfn( { 1, 3, 7, 9, 4, 11, 13} ) 13 | -------------------------------------------------------------------------------- /tests/goto-markov-1.lua: -------------------------------------------------------------------------------- 1 | -- Use a very weak pseudo-number generator just for testing purpose. 2 | local function my_random(s) 3 | s.x = (16807 * s.x) % 2147483647 4 | return s.x % 50 5 | end 6 | 7 | local function markov() 8 | local s = { x = 13 } 9 | ::a:: 10 | print('A') 11 | if my_random(s) < 10 then goto c end 12 | ::b:: 13 | print('B') 14 | if my_random(s) < 20 then goto d end 15 | ::c:: 16 | print('C') 17 | if my_random(s) < 5 then goto a end 18 | ::d:: 19 | print('D') 20 | if my_random(s) < 48 then goto b else return end 21 | end 22 | 23 | markov() 24 | -------------------------------------------------------------------------------- /tests/goto-markov-2.lua: -------------------------------------------------------------------------------- 1 | -- Use a very weak pseudo-number generator just for testing purpose. 2 | local function my_random(s) 3 | s.x = (16807 * s.x) % 2147483647 4 | return s.x % 50 5 | end 6 | 7 | local function foo(a) 8 | local s = { x = 13 } 9 | local ls = { } 10 | ::a:: 11 | local x = 2*a + 1 12 | ls[#ls+1] = function() return x end 13 | if my_random(s) < 10 then goto a end 14 | ::b:: 15 | ls[#ls+1] = function() return x end 16 | if my_random(s) < 20 then goto b end 17 | ::c:: 18 | local y = x^2 + 1 19 | ls[#ls+1] = function() return y end 20 | if my_random(s) < 40 then goto a end 21 | return ls 22 | end 23 | 24 | local ls = foo(7) 25 | for i = 1, #ls do 26 | print(ls[i]()) 27 | end 28 | -------------------------------------------------------------------------------- /tests/goto-nested-1.lua: -------------------------------------------------------------------------------- 1 | for z=1,10 do 2 | for y=1,10 do 3 | for x=1,10 do 4 | if x^2 + y^2 == z^2 then 5 | print('found a Pythagorean triple:', x, y, z) 6 | goto done 7 | end 8 | end 9 | end 10 | end 11 | ::done:: 12 | -------------------------------------------------------------------------------- /tests/goto-nested-2.lua: -------------------------------------------------------------------------------- 1 | for z=1,10 do 2 | for y=1,10 do 3 | for x=1,10 do 4 | if x^2 + y^2 == z^2 then 5 | print('found a Pythagorean triple:', x, y, z) 6 | print('now trying next z...') 7 | goto zcontinue 8 | end 9 | end 10 | end 11 | ::zcontinue:: 12 | end 13 | -------------------------------------------------------------------------------- /tests/goto-redo-1.lua: -------------------------------------------------------------------------------- 1 | for x=1, 10 do ::redo:: 2 | print(x) 3 | x = x^2 + 1 4 | if x < 100 then goto redo end 5 | end 6 | -------------------------------------------------------------------------------- /tests/goto-tail-1.lua: -------------------------------------------------------------------------------- 1 | function fact(n, ans) 2 | ::call:: 3 | if n == 0 then 4 | return ans 5 | else 6 | n, ans = n - 1, ans * n 7 | goto call 8 | end 9 | end 10 | print(fact(6, 1)) 11 | -------------------------------------------------------------------------------- /tests/if-1.lua: -------------------------------------------------------------------------------- 1 | local function foo(x, y) 2 | if x < y then 3 | return x + y 4 | else 5 | return x - y 6 | end 7 | end 8 | 9 | print(foo(3,7), foo(7,3)) 10 | -------------------------------------------------------------------------------- /tests/jacobi-sor.lua: -------------------------------------------------------------------------------- 1 | local RANDOM_SEED = 101009 -- Must be odd. 2 | 3 | ------------------------------------------------------------------------------ 4 | -- This is a Lagged Fibonacci Pseudo-random Number Generator with 5 | -- j, k, M = 5, 17, 31. Pretty weak, but same as C/Java SciMark. 6 | ------------------------------------------------------------------------------ 7 | 8 | local rand 9 | 10 | local function rand_init(seed) 11 | local Rm, Rj = {}, 1 12 | for i=1,17 do Rm[i] = 0 end 13 | for i=17,1,-1 do 14 | seed = (seed*9069) % (2^31) 15 | Rm[i] = seed 16 | end 17 | 18 | function rand() 19 | local j, m = Rj, Rm 20 | local h = j - 5 21 | if h < 1 then h = h + 17 end 22 | local k = m[h] - m[j] 23 | if k < 0 then k = k + 2147483647 end 24 | m[j] = k 25 | if j < 17 then Rj = j + 1 else Rj = 1 end 26 | return k * (1.0/2147483647.0) 27 | end 28 | end 29 | 30 | local function random_vector(n) 31 | local v = {} 32 | for x=1,n do v[x] = rand() end 33 | return v 34 | end 35 | 36 | local function quasi_diag_randmat(n, alpha) 37 | local a = {} 38 | for y = 1, n do 39 | local v = {} 40 | a[y] = v 41 | for x = 1, n do 42 | local z = rand() 43 | v[x] = x == y and z or alpha * z / n 44 | end 45 | end 46 | return a 47 | end 48 | 49 | local function sor_iter(A, n, b, x, om) 50 | for i = 1, n do 51 | local sig = 0 52 | local Ai = A[i] 53 | for j = 1, n do 54 | if i ~= j then 55 | sig = sig + Ai[j] * x[j] 56 | end 57 | end 58 | x[i] = x[i] + om * ((b[i] - sig) / Ai[i] - x[i]) 59 | end 60 | end 61 | 62 | local function print_vector(v, n) 63 | for i = 1, n do 64 | io.write(string.format("%12g ", v[i])) 65 | end 66 | io.write("\n") 67 | end 68 | 69 | rand_init(RANDOM_SEED) 70 | 71 | local N = 100 72 | local A = quasi_diag_randmat(N, 0.1) 73 | local b = random_vector(N) 74 | local x = {} 75 | for i = 1, N do x[i] = b[i] / A[i][i] end 76 | 77 | local xp = {} 78 | for i = 1, N do xp[i] = 0 end 79 | 80 | for cycle = 1, 100 do 81 | sor_iter(A, N, b, x, 1.25) 82 | 83 | local diff = 0 84 | for i = 1, N do 85 | local d = (x[i] - xp[i])^2 86 | xp[i] = x[i] 87 | diff = diff + d 88 | end 89 | 90 | diff = math.sqrt(diff) 91 | 92 | print(string.format("Iteration: %4i, Residual: %14g", cycle, diff)) 93 | 94 | if diff < 1.0e-6 then break end 95 | end 96 | 97 | print("Solution:") 98 | print_vector(x, N) 99 | 100 | local res = 0 101 | for i = 1, N do 102 | local t = 0 103 | local Ai = A[i] 104 | for j = 1, N do t = t + Ai[j] * x[j] end 105 | res = res + (t - b[i])^2 106 | end 107 | 108 | print(string.format("Exactness residual: %g", res)) 109 | -------------------------------------------------------------------------------- /tests/kcdata-1.lua: -------------------------------------------------------------------------------- 1 | print(4ULL) 2 | 3 | -------------------------------------------------------------------------------- /tests/kcdata-2.lua: -------------------------------------------------------------------------------- 1 | local function foo(n) 2 | return 1ULL * n 3 | end 4 | 5 | print(foo(8)) 6 | 7 | -------------------------------------------------------------------------------- /tests/lexer-64int.lua: -------------------------------------------------------------------------------- 1 | 2 | print(499234445333ll) 3 | print(499234445333ull) 4 | print(0xa34cd34ff09ll) 5 | print(0xa34cd34ff09ull) 6 | print(0xa34cd34ff09ULL) 7 | print(0xA34CD34FF09ull) 8 | 9 | 10 | -------------------------------------------------------------------------------- /tests/lexer-number-1.lua: -------------------------------------------------------------------------------- 1 | local x = 2.35 2 | local xe = 2.35e-6 3 | local ie = 35e-6 4 | local y = 23455ULL 5 | local z = 4i 6 | local h = 0x456354 7 | local hf = 0x456354p-06 8 | print(x, xe, ie, y, z, h, hf) 9 | -------------------------------------------------------------------------------- /tests/logical-tests-1.lua: -------------------------------------------------------------------------------- 1 | local function foo(a, b) 2 | local x 3 | if a < b then x = 0 end 4 | if a > b then x = 1 end 5 | if a <= b then x = 2 end 6 | if a >= b then x = 3 end 7 | if a == b then x = 4 end 8 | if a ~= b then x = 5 end 9 | if not (a < b) then x = 6 end 10 | return x 11 | end 12 | 13 | local x, y = 7, 3 14 | print(foo(x, y), foo(y, x), foo(x, x)) 15 | -------------------------------------------------------------------------------- /tests/logical-tests-2.lua: -------------------------------------------------------------------------------- 1 | local function foo(a, b) 2 | if 2 * a + b then print('boom') end 3 | end 4 | 5 | local x, y = 7, 3 6 | foo(x, y) 7 | -------------------------------------------------------------------------------- /tests/logical-tests-3.lua: -------------------------------------------------------------------------------- 1 | local function foo(x, y) 2 | local a = (x < y) and (x*x < y*y) 3 | return a 4 | end 5 | 6 | print(foo(3, 7), foo(7, 3)) 7 | -------------------------------------------------------------------------------- /tests/logical-tests-3b.lua: -------------------------------------------------------------------------------- 1 | local function foo(x, y) 2 | if (x < y) and (x*x < y*y) then 3 | print('boom') 4 | end 5 | end 6 | 7 | local x, y = 3, 7 8 | foo(x, y) 9 | -------------------------------------------------------------------------------- /tests/logical-tests-3c.lua: -------------------------------------------------------------------------------- 1 | local function foo(x, y) 2 | if (x < y) or (x*x < y*y) then 3 | print('boom') 4 | end 5 | end 6 | 7 | local x, y = 3, 7 8 | foo(x, y) 9 | -------------------------------------------------------------------------------- /tests/logical-tests-3d.lua: -------------------------------------------------------------------------------- 1 | local function foo(x, y) 2 | local c = (x < y) 3 | return c 4 | end 5 | 6 | local x, y = 3, 7 7 | print(foo(x, y)) 8 | -------------------------------------------------------------------------------- /tests/logical-tests-4.lua: -------------------------------------------------------------------------------- 1 | local function foo(a, b) 2 | return not (a + b) 3 | end 4 | 5 | local x, y = 7, 3 6 | print(foo(x, y), foo(y, x)) 7 | -------------------------------------------------------------------------------- /tests/logical-tests-4b.lua: -------------------------------------------------------------------------------- 1 | local function foo(a, b) 2 | return not (a < b) 3 | end 4 | 5 | local x, y = 7, 3 6 | print(foo(x, y), foo(y, x)) 7 | -------------------------------------------------------------------------------- /tests/logical-tests-5.lua: -------------------------------------------------------------------------------- 1 | local pi = 3.14 2 | 3 | if jit and jit.status then 4 | print("JIT!") 5 | end 6 | 7 | print(pi) 8 | -------------------------------------------------------------------------------- /tests/logical-tests-6.lua: -------------------------------------------------------------------------------- 1 | local function foo(a, b, c, d) 2 | a = a or 1 3 | b = b + 2*d + c 4 | c = c + 1 5 | return a + b*c + 2*d 6 | end 7 | 8 | local sum = 0 9 | for k = 1, 400 do 10 | sum = sum + foo(k, -2, 3, k + 1) 11 | end 12 | print(sum) 13 | 14 | -------------------------------------------------------------------------------- /tests/logical-value-1.lua: -------------------------------------------------------------------------------- 1 | local function foo1(x, y) 2 | return x < y and x + y 3 | end 4 | 5 | local function foo1bis(x, y, somef) 6 | return x < y and somef(x, y) 7 | end 8 | 9 | local function foo2(x, y) 10 | return x < y and x == y 11 | end 12 | 13 | local function foo3(x, y) 14 | return x < y or x - y 15 | end 16 | 17 | local function foo4(x, y) 18 | return x < y or x == y 19 | end 20 | 21 | local function diff(a, b) return a - b end 22 | 23 | local x, y = 3, 7 24 | print(foo1(x, y), foo1(y, x)) 25 | print(foo1bis(x, y, diff), foo1bis(y, x, diff)) 26 | print(foo2(x, y), foo2(y, x)) 27 | print(foo3(x, y), foo3(y, x)) 28 | print(foo4(x, y), foo4(y, x)) -------------------------------------------------------------------------------- /tests/logical-value-2.lua: -------------------------------------------------------------------------------- 1 | local function foo(x, y) 2 | if x < y and x == y then 3 | return x 4 | else 5 | return y 6 | end 7 | end 8 | 9 | local function boo(x, y) 10 | if x < y or x == y then 11 | return x 12 | else 13 | return y 14 | end 15 | end 16 | 17 | local x, y = 3, 7 18 | print(foo(x, y), foo(y, x)) 19 | print(boo(x, y), boo(y, x)) 20 | -------------------------------------------------------------------------------- /tests/mixed-assign-1.lua: -------------------------------------------------------------------------------- 1 | local x = {} 2 | for k = 1, 10 do 3 | k, x[k] = k + 1, k*k + 1 4 | end 5 | print(x[1], x[2], x[3]) 6 | -------------------------------------------------------------------------------- /tests/mixed-assign-2.lua: -------------------------------------------------------------------------------- 1 | local function foo(k) 2 | return k*k, 2*k + 1 3 | end 4 | 5 | local x = 3 6 | local a, b, c 7 | a, b, c = 13, foo(x) 8 | print(a, b, c) 9 | -------------------------------------------------------------------------------- /tests/mixed-assign-3.lua: -------------------------------------------------------------------------------- 1 | local function foo(k) 2 | return k*k, 2*k + 1 3 | end 4 | 5 | local a, b 6 | a, b = 13, foo(3) 7 | print(a, b) 8 | -------------------------------------------------------------------------------- /tests/multi-assign-1.lua: -------------------------------------------------------------------------------- 1 | local function foo(k) 2 | return k, k*k, 2*k + 1 3 | end 4 | 5 | local x = 3 6 | local a, b, c = foo(x) 7 | print(a, b, c) -------------------------------------------------------------------------------- /tests/multi-assign-2b.lua: -------------------------------------------------------------------------------- 1 | local function foo(v, i) 2 | v.f[i+1], v.x, v.s[i] = i*i, i+5, 7*i 3 | end 4 | 5 | local v = { } 6 | v.f, v.s, ciao = v, v, 'ciccio' 7 | foo(v, 1) 8 | print(#v, v[1], v[2]) 9 | -------------------------------------------------------------------------------- /tests/multi-call-1.lua: -------------------------------------------------------------------------------- 1 | local function foo(k) 2 | return k, k*k, 2*k + 1 3 | end 4 | 5 | print(foo(3)) 6 | -------------------------------------------------------------------------------- /tests/multi-call-2.lua: -------------------------------------------------------------------------------- 1 | local format = string.format 2 | 3 | local function printf(...) 4 | print(format(...)) 5 | end 6 | 7 | printf("%s %d", 'ciccio', 124) 8 | -------------------------------------------------------------------------------- /tests/multi-returns-1.lua: -------------------------------------------------------------------------------- 1 | local function foo(k) 2 | return k, k*k, 2*k+1 3 | end 4 | 5 | local function give(n) 6 | local k = 3*n + 2 7 | return foo(k) 8 | end 9 | 10 | print(give(5)) 11 | -------------------------------------------------------------------------------- /tests/nested-ifs-1.lua: -------------------------------------------------------------------------------- 1 | local function foo(x, y) 2 | if x < y then 3 | if x*x < y*y then 4 | return x*x 5 | else 6 | return y*y 7 | end 8 | else 9 | return x + y 10 | end 11 | end 12 | 13 | print(foo(3, 4), foo(4, 3)) 14 | -------------------------------------------------------------------------------- /tests/nested-ifs-2.lua: -------------------------------------------------------------------------------- 1 | local function foo(x, y) 2 | if x < y then 3 | if x*x < y*y then 4 | return x*x 5 | else 6 | return y*y 7 | end 8 | else 9 | if x + y < x - y then 10 | return x 11 | else 12 | return y 13 | end 14 | end 15 | end 16 | 17 | print(foo(3, 4), foo(4, 3)) 18 | -------------------------------------------------------------------------------- /tests/nested-ifs-3.lua: -------------------------------------------------------------------------------- 1 | local function foo(x, y) 2 | if x < y then 3 | if x*x < y*y then 4 | if x + y < x - y then 5 | return x 6 | else 7 | return y 8 | end 9 | else 10 | return y*y 11 | end 12 | else 13 | return x + y 14 | end 15 | end 16 | 17 | print(foo(3, 4), foo(4, 3)) 18 | -------------------------------------------------------------------------------- /tests/nested-ifs-4.lua: -------------------------------------------------------------------------------- 1 | local function foo(x, y) 2 | if x < y then 3 | return x + y 4 | elseif x + y < x - y then 5 | return x - y 6 | elseif x * y < x / y then 7 | return x * y 8 | else 9 | return x / y 10 | end 11 | end 12 | 13 | print(foo(3, 4), foo(4, 3)) 14 | -------------------------------------------------------------------------------- /tests/not-operator-1.lua: -------------------------------------------------------------------------------- 1 | print((not true) and 10) 2 | -------------------------------------------------------------------------------- /tests/not-operator-2.lua: -------------------------------------------------------------------------------- 1 | print((not true) and 10, (not false) and 10, (not true) or 10, (not false) or 10) 2 | -------------------------------------------------------------------------------- /tests/not-operator-3.lua: -------------------------------------------------------------------------------- 1 | local a = (7 % 2 == 1) 2 | local b = (8 % 2 == 1) 3 | print(not a, (not a) and 10, (not a) or 10) 4 | print(not b, (not b) and 10, (not b) or 10) 5 | -------------------------------------------------------------------------------- /tests/not-operator-4.lua: -------------------------------------------------------------------------------- 1 | local a = (7 % 2 == 1) 2 | local b = (8 % 2 == 1) 3 | local c = (9 % 2 == 1) 4 | 5 | if not a then print("a") else print("b") end 6 | if not a and 10 then print("a") else print("b") end 7 | if not a or 10 then print("a") else print("b") end 8 | 9 | if not b then print("a") else print("b") end 10 | if not b and 10 then print("a") else print("b") end 11 | if not b or 10 then print("a") else print("b") end 12 | 13 | if (not a) and b or c then print("a") else print("b") end 14 | if (not a) and (not b or c) or c then print("a") else print("b") end 15 | if (not a or c) and not b or c then print("a") else print("b") end 16 | 17 | if (not b) and a or b then print("a") else print("b") end 18 | if (not b) and (not c or b) or b then print("a") else print("b") end 19 | if (not b or a) and not c or b then print("a") else print("b") end 20 | -------------------------------------------------------------------------------- /tests/number-inf-zero.lua: -------------------------------------------------------------------------------- 1 | function foo(a) 2 | local x = 1/0 3 | local y = 0/1 4 | local w = a/0 5 | local z = 0/a 6 | print(x, y, w, z) 7 | end 8 | 9 | foo(7) 10 | 11 | -------------------------------------------------------------------------------- /tests/number-nan.lua: -------------------------------------------------------------------------------- 1 | function foo() 2 | local x = 0/0 3 | print(x) 4 | end 5 | 6 | foo() 7 | 8 | -------------------------------------------------------------------------------- /tests/numbers-1.lua: -------------------------------------------------------------------------------- 1 | 2 | print(0x31, 0x9e, 0x31ef, 0x9ea1, 0x31ef3c, 0x9ea13c, 0x31ef3cea, 0x9eef3cea, 0x31ef3cea09, 0x9eef3cea09) 3 | print(-0x31, -0x9e, -0x31ef, -0x9ea1, -0x31ef3c, -0x9ea13c, -0x31ef3cea, -0x9eef3cea, -0x31ef3cea09, -0x9eef3cea09) 4 | 5 | -------------------------------------------------------------------------------- /tests/printf-1.lua: -------------------------------------------------------------------------------- 1 | 2 | local s1 = "Hello" 3 | local s2 = "Ciccio" 4 | 5 | local format = string.format 6 | 7 | local some_table = { pi = 3.14 } 8 | 9 | local function printf(...) 10 | io.write(format(...)) 11 | end 12 | 13 | printf("I say: %s %s!\n", s1, s2) 14 | -------------------------------------------------------------------------------- /tests/rand-init.lua: -------------------------------------------------------------------------------- 1 | local function darray() return {} end 2 | local iarray = darray 3 | 4 | local rand, rand_init 5 | 6 | if jit and jit.status and jit.status() then 7 | -- LJ2 has bit operations and zero-based arrays (internally). 8 | local bit = require("bit") 9 | local band, sar = bit.band, bit.arshift 10 | function rand_init(seed) 11 | local Rm, Rj, Ri = iarray(17), 16, 11 12 | for i=0,16 do Rm[i] = 0 end 13 | for i=16,0,-1 do 14 | seed = band(seed*9069, 0x7fffffff) 15 | Rm[i] = seed 16 | end 17 | function rand() 18 | local i = band(Ri+1, sar(Ri-16, 31)) 19 | local j = band(Rj+1, sar(Rj-16, 31)) 20 | Ri, Rj = i, j 21 | local k = band(Rm[i] - Rm[j], 0x7fffffff) 22 | Rm[j] = k 23 | return k * (1.0/2147483647.0) 24 | end 25 | end 26 | else 27 | function rand_init(seed) 28 | local Rm, Rj = {}, 1 29 | for i=1,17 do Rm[i] = 0 end 30 | for i=17,1,-1 do 31 | seed = (seed*9069) % (2^31) 32 | Rm[i] = seed 33 | end 34 | function rand() 35 | local j, m = Rj, Rm 36 | local h = j - 5 37 | if h < 1 then h = h + 17 end 38 | local k = m[h] - m[j] 39 | if k < 0 then k = k + 2147483647 end 40 | m[j] = k 41 | if j < 17 then Rj = j + 1 else Rj = 1 end 42 | return k * (1.0/2147483647.0) 43 | end 44 | end 45 | end 46 | 47 | rand_init(15) 48 | for k = 1, 10 do 49 | print(rand()) 50 | end 51 | -------------------------------------------------------------------------------- /tests/repeat-1.lua: -------------------------------------------------------------------------------- 1 | local function foo(n) 2 | local i, sum = 1, 0 3 | repeat 4 | sum = sum + i*i 5 | i = i + 1 6 | until i > 10 7 | return sum 8 | end 9 | 10 | print(foo(10)) 11 | -------------------------------------------------------------------------------- /tests/repeat-test-1.lua: -------------------------------------------------------------------------------- 1 | local function foo(n) 2 | local sum = 0 3 | local i = 1 4 | repeat 5 | sum = sum + i*i 6 | i = i + 1 7 | until i > n 8 | return sum 9 | end 10 | 11 | print(foo(20)) 12 | -------------------------------------------------------------------------------- /tests/repeat-test-2.lua: -------------------------------------------------------------------------------- 1 | local function foo(n) 2 | local sum = 0 3 | local i = 1 4 | local test 5 | repeat 6 | sum = sum + i*i 7 | i = i + 1 8 | test = i > n and 1 9 | until test 10 | return sum 11 | end 12 | 13 | print(foo(20)) 14 | -------------------------------------------------------------------------------- /tests/send-expr.lua: -------------------------------------------------------------------------------- 1 | local boo = { value = 7, compute= function(self, i) return self.value*i+1 end } 2 | print(boo:compute(3) + boo:compute(7)) 3 | 4 | -------------------------------------------------------------------------------- /tests/send-expression-call-1.lua: -------------------------------------------------------------------------------- 1 | local function foo(self, k) 2 | return k * self.value 3 | end 4 | 5 | local obj = { value = 3.141592, foo = foo } 6 | print(obj:foo(2)) 7 | -------------------------------------------------------------------------------- /tests/send-expression-call-2.lua: -------------------------------------------------------------------------------- 1 | local function foo(self, k) 2 | return k * self.value 3 | end 4 | 5 | local function create_obj(x) 6 | return { value = x, foo = foo } 7 | end 8 | 9 | print(create_obj(3.141592):foo(2)) 10 | -------------------------------------------------------------------------------- /tests/send-expression-call-3.lua: -------------------------------------------------------------------------------- 1 | local a1 = 2 | "hhm" .. 3 | "wql" .. 4 | "mkf" .. 5 | "wde" .. 6 | "gff" .. 7 | "pwe" .. 8 | "wsa" .. 9 | "ivh" .. 10 | "uem" .. 11 | "zve" .. 12 | "jmr" .. 13 | "afq" .. 14 | "jpt" .. 15 | "zdb" .. 16 | "sqr" .. 17 | "ruv" .. 18 | "yhy" .. 19 | "xky" .. 20 | "jhs" .. 21 | "poy" .. 22 | "xdn" .. 23 | "zrh" 24 | local a2 = "rjo" .. 25 | "mkg" .. 26 | "bud" .. 27 | "jjx" .. 28 | "ozl" .. 29 | "dup" .. 30 | "azb" .. 31 | "kse" .. 32 | "uzd" .. 33 | "kay" .. 34 | "kbb" .. 35 | "vft" .. 36 | "eyk" .. 37 | "gvx" .. 38 | "wnl" .. 39 | "lxb" .. 40 | "nbc" .. 41 | "iyc" .. 42 | "hmz" .. 43 | "mxt" .. 44 | "hid" .. 45 | "soa" .. 46 | "mlp" .. 47 | "gbq" .. 48 | "app" .. 49 | "vyg" .. 50 | "hbg" .. 51 | "erq" .. 52 | "ofu" .. 53 | "cqd" .. 54 | "vza" .. 55 | "kvi" .. 56 | "jkv" .. 57 | "rav" .. 58 | "kfr" .. 59 | "cpd" .. 60 | "iax" .. 61 | "arm" .. 62 | "fmj" .. 63 | "ums" 64 | 65 | local a3 = "hjq" .. 66 | "hyx" .. 67 | "zyf" .. 68 | "eky" .. 69 | "rzm" .. 70 | "bij" .. 71 | "uzf" .. 72 | "glm" .. 73 | "dqj" .. 74 | "zcl" .. 75 | "sme" .. 76 | "fns" .. 77 | "iyq" .. 78 | "uif" .. 79 | "klu" .. 80 | "qxn" .. 81 | "dcv" .. 82 | "ozx" .. 83 | "tzg" .. 84 | "wtw" .. 85 | "sdh" .. 86 | "emd" .. 87 | "bxw" .. 88 | "uim" .. 89 | "qlz" .. 90 | "env" .. 91 | "vqn" .. 92 | "fgh" .. 93 | "ymr" .. 94 | "lzn" .. 95 | "ryt" .. 96 | "lnx" .. 97 | "her" .. 98 | "sun" .. 99 | "xej" .. 100 | "rvn" .. 101 | "usz" .. 102 | "voe" .. 103 | "ouj" .. 104 | "oqf" .. 105 | "dth" .. 106 | "dad" .. 107 | "jyy" .. 108 | "lea" .. 109 | "dom" .. 110 | "gkv" .. 111 | "djy" .. 112 | "dib" .. 113 | "lkj" .. 114 | "min" .. 115 | "kjw" .. 116 | "ahj" .. 117 | "zxo" .. 118 | "dzl" .. 119 | "alv" .. 120 | "viu" .. 121 | "yyf" .. 122 | "knj" .. 123 | "xqy" .. 124 | "xgy" .. 125 | "lci" .. 126 | "rdr" .. 127 | "yqy" .. 128 | "ycp" .. 129 | "bmc" .. 130 | "fiv" .. 131 | "ifd" .. 132 | "xgz" .. 133 | "eqt" .. 134 | "dhv" .. 135 | "tal" .. 136 | "mww" .. 137 | "pet" .. 138 | "edk" .. 139 | "xkh" .. 140 | "srg" .. 141 | "lfg" .. 142 | "sxa" .. 143 | "lyv" 144 | 145 | local a4 = "rgg" .. 146 | "brp" .. 147 | "jms" .. 148 | "qvr" .. 149 | "efh" .. 150 | "hze" .. 151 | "ahh" .. 152 | "gbq" .. 153 | "lsw" .. 154 | "xai" .. 155 | "mzu" .. 156 | "wbd" .. 157 | "bsl" .. 158 | "uqq" .. 159 | "dml" .. 160 | "vae" .. 161 | "azo" .. 162 | "yge" .. 163 | "xbs" .. 164 | "gxg" .. 165 | "sei" .. 166 | "mpz" .. 167 | "mws" .. 168 | "jdm" .. 169 | "ydo" .. 170 | "rkc" .. 171 | "pli" .. 172 | "cjo" .. 173 | "zxc" .. 174 | "dfi" .. 175 | "rtv" .. 176 | "gjr" .. 177 | "mpk" .. 178 | "tdo" .. 179 | "ase" .. 180 | "oqq" .. 181 | "jge" .. 182 | "qqg" .. 183 | "qkc" .. 184 | "ohp" .. 185 | "jly" .. 186 | "sud" .. 187 | "yub" .. 188 | "ocx" .. 189 | "zzw" .. 190 | "hzx" .. 191 | "dvl" .. 192 | "pgc" .. 193 | "jkv" .. 194 | "jnl" .. 195 | "pzz" .. 196 | "paq" .. 197 | "aae" .. 198 | "has" .. 199 | "baj" .. 200 | "ipq" .. 201 | "etq" .. 202 | "gmc" .. 203 | "tvt" .. 204 | "xtb" .. 205 | "byk" .. 206 | "nim" .. 207 | "lqm" .. 208 | "bfg" .. 209 | "aln" .. 210 | "psy" .. 211 | "wdz" .. 212 | "nqq" .. 213 | "pph" .. 214 | "why" .. 215 | "kbo" .. 216 | "def" .. 217 | "mar" .. 218 | "aip" .. 219 | "bsh" .. 220 | "jhq" .. 221 | "fit" .. 222 | "ojr" 223 | local a5 = "ncq" .. 224 | "cmj" .. 225 | "ohg" .. 226 | "zkv" .. 227 | "mkt" .. 228 | "szw" .. 229 | "qvz" .. 230 | "qez" .. 231 | "gtz" .. 232 | "srp" .. 233 | "yil" .. 234 | "iyt" .. 235 | "kwj" .. 236 | "dfp" .. 237 | "bij" .. 238 | "byw" .. 239 | "cpn" .. 240 | "mak" .. 241 | "cwy" .. 242 | "pfu" .. 243 | "aji" .. 244 | "hgi" .. 245 | "dhs" .. 246 | "qpc" .. 247 | "udc" .. 248 | "fmp" .. 249 | "kre" .. 250 | "ngf" .. 251 | "xdw" .. 252 | "hut" .. 253 | "gqv" .. 254 | "sio" .. 255 | "lpa" .. 256 | "ooh" .. 257 | "jdf" .. 258 | "llk" .. 259 | "nro" .. 260 | "kvw" .. 261 | "fwq" .. 262 | "afu" .. 263 | "yxi" .. 264 | "nlh" .. 265 | "mnm" .. 266 | "dtj" .. 267 | "zom" .. 268 | "vkb" .. 269 | "zrw" .. 270 | "xii" .. 271 | "sdq" .. 272 | "ddo" .. 273 | "ght" .. 274 | "whw" .. 275 | "lfk" .. 276 | "mzm" .. 277 | "zls" .. 278 | "ytx" .. 279 | "bkl" .. 280 | "von" .. 281 | "tzf" .. 282 | "ibg" .. 283 | "dsu" .. 284 | "dqc" .. 285 | "rzn" .. 286 | "mlz" .. 287 | "dza" .. 288 | "tnc" .. 289 | "uww" .. 290 | "dtn" .. 291 | "xhl" .. 292 | "gxe" .. 293 | "nfh" .. 294 | "egu" .. 295 | "mgm" .. 296 | "vyp" .. 297 | "kau" .. 298 | "jis" .. 299 | "bkm" .. 300 | "qxy" .. 301 | "nph" .. 302 | "sjp" .. 303 | "zxs" 304 | 305 | local obj = { value = 3.141592, foo = function(self, i) return i*self.value end } 306 | print(obj:foo(2)) 307 | -------------------------------------------------------------------------------- /tests/sort-algo.lua: -------------------------------------------------------------------------------- 1 | -- 2 | -- GSL shell interactive interface to GSL library 3 | -- Based on the Lua programming language 4 | -- 5 | -- Copyright (C) 2009-2013 Francesco Abbate. 6 | -- See Copyright Notice in gsl-shell-jit.c 7 | -- 8 | -- This file provided an implementation of the quicksort algorithm. 9 | -- Based on the libstdc++ std::sort implementation included with GCC. 10 | -- 11 | 12 | local bit = require 'bit' 13 | 14 | local band, rshift = bit.band, bit.rshift 15 | 16 | local insertion_thresold = 16 17 | 18 | local function less_than(a, b) 19 | return a < b 20 | end 21 | 22 | local function lg2(a) 23 | local c = 0 24 | while a > 0 do 25 | a = rshift(a, 1) 26 | c = c + 1 27 | end 28 | return c - 1 29 | end 30 | 31 | local function div2(a) 32 | return rshift(a, 1) 33 | end 34 | 35 | local function heapsort(array, i0, i1, f) 36 | f = f or less_than 37 | 38 | local function push_heap(first, hole, top, value) 39 | local parent = div2(hole - 1) 40 | while hole > top and f(array[first + parent], value) do 41 | array[first + hole] = array[first + parent] 42 | hole = parent 43 | parent = div2(hole - 1) 44 | end 45 | array[first + hole] = value 46 | end 47 | 48 | local function adjust_heap(first, hole, len, value) 49 | local top = hole 50 | local second = hole 51 | while second < div2(len - 1) do 52 | second = 2 * (second + 1) 53 | if f(array[first + second], array[first + (second - 1)]) then 54 | second = second - 1 55 | end 56 | array[first + hole] = array[first + second] 57 | hole = second 58 | end 59 | if band(len, 1) == 0 and second == div2(len - 2) then 60 | second = 2 * (second + 1) 61 | array[first + hole] = array[first + (second - 1)] 62 | hole = second - 1 63 | end 64 | push_heap(first, hole, top, value) 65 | end 66 | 67 | local function pop_heap(first, last, result) 68 | local value = array[result] 69 | array[result] = array[first] 70 | adjust_heap(first, 0, last - first, value) 71 | end 72 | 73 | local function make_heap(first, last) 74 | if last - first < 2 then return end 75 | local len = last - first 76 | local parent = div2(len - 2) 77 | while true do 78 | local value = array[first + parent] 79 | adjust_heap(first, parent, len, value) 80 | if parent == 0 then 81 | return 82 | end 83 | parent = parent - 1 84 | end 85 | end 86 | 87 | local function heap_select(first, middle, last) 88 | make_heap(first, middle) 89 | for i = middle, last - 1 do 90 | if f(array[i], array[first]) then 91 | pop_heap(first, middle, i) 92 | end 93 | end 94 | end 95 | 96 | local function sort_heap(first, last) 97 | while last - first > 1 do 98 | last = last - 1 99 | pop_heap(first, last, last) 100 | end 101 | end 102 | 103 | heap_select(i0, i1 + 1, i1 + 1) 104 | sort_heap(i0, i1 + 1) 105 | end 106 | 107 | local function insertion_sort(array, compare, istart, iend) 108 | for i = istart + 1, iend do 109 | local current_value = array[i] 110 | local hole_index = i 111 | while hole_index > istart and compare(current_value, array[hole_index - 1]) do 112 | array[hole_index] = array[hole_index - 1] 113 | hole_index = hole_index - 1 114 | end 115 | array[hole_index] = current_value 116 | end 117 | end 118 | 119 | local function quicksort(array, i0, i1, f) 120 | f = f or less_than 121 | 122 | local function move_median_first(a, b, c) 123 | if f(array[a], array[b]) then 124 | if f(array[b], array[c]) then 125 | array[a], array[b] = array[b], array[a] 126 | else 127 | array[a], array[c] = array[c], array[a] 128 | end 129 | elseif f(array[a], array[c]) then 130 | return 131 | elseif f(array[b], array[c]) then 132 | array[a], array[c] = array[c], array[a] 133 | else 134 | array[a], array[b] = array[b], array[a] 135 | end 136 | end 137 | 138 | local function partition(first, last, pivot_value) 139 | while true do 140 | while f(array[first], pivot_value) do 141 | first = first + 1 142 | end 143 | while f(pivot_value, array[last]) do 144 | last = last - 1 145 | end 146 | if first >= last then 147 | return first 148 | end 149 | array[first], array[last] = array[last], array[first] 150 | first = first + 1 151 | last = last - 1 152 | end 153 | end 154 | 155 | local function partition_pivot(first, last) 156 | local mid = div2(first + last) 157 | move_median_first(first, mid, last) 158 | return partition(first + 1, last, array[first]) 159 | end 160 | 161 | local function quicksort_loop(first, last, depth) 162 | while last - first > insertion_thresold do 163 | if depth == 0 then 164 | heapsort(array, first, last, f) 165 | return 166 | end 167 | depth = depth - 1 168 | local cut = partition_pivot(first, last) 169 | quicksort_loop(cut, last, depth) 170 | -- array[first], array[first + 1] = array[first + 1], array[first] 171 | last = cut - 1 172 | end 173 | end 174 | 175 | local complete = quicksort_loop(i0, i1, 2 * lg2(i1 - i0 + 1)) 176 | insertion_sort(array, f, i0, i1) 177 | end 178 | 179 | local function array_search(array, i0, i1, val) 180 | for k = i0, i1 do 181 | if array[k] == val then return k end 182 | end 183 | end 184 | 185 | -- sort arrays "array" and "slave" in place for indices from i0 to i1 186 | -- based on values of "array" using the comparison function "f" 187 | local function quicksort_mirror(array, slave, i0, i1, f) 188 | 189 | local function swap(index, a, b) 190 | array[a], array[b] = array[b], array[a] 191 | slave[a], slave[b] = slave[b], slave[a] 192 | index[a], index[b] = index[b], index[a] 193 | end 194 | 195 | local n = i1 - i0 + 1 196 | local id, iv = {}, {} 197 | for k = 1, n do id[k], iv[k] = k, k end 198 | quicksort(id, i0, i1, function(a, b) return f(array[a], array[b]) end) 199 | for k = 1, n do 200 | local val = id[k] 201 | if val > k then 202 | swap(iv, k, val) 203 | elseif val < k then 204 | val = array_search(iv, k, n, val) 205 | swap(iv, k, val) 206 | end 207 | end 208 | end 209 | 210 | -- Use a very weak pseudo-number generator just for testing purpose. 211 | local function my_random(s) 212 | s.x = (16807 * s.x) % 2147483647 213 | return s.x 214 | end 215 | 216 | local function test_sort(name, sort_fn, gen) 217 | local s = { x = 934 } -- random runmber generator 218 | print(string.format("******** %s **********", name)) 219 | local x={}; for i = 1, 1000 do x[i] = gen(s, i) end 220 | quicksort(x, 1, #x) 221 | for k = 1, 1000 do print(x[k]) end 222 | end 223 | 224 | test_sort("QUICKSORT 1", quicksort, function(s) return my_random(s) % 65536 end) 225 | test_sort("QUICKSORT 2", quicksort, function(s, i) return i end) 226 | test_sort("QUICKSORT 3", quicksort, function(s, i) return 1000 - i end) 227 | test_sort("HEAPSORT 1", heapsort, function(s) return my_random(s) % 65536 end) 228 | test_sort("HEAPSORT 2", heapsort, function(s, i) return i end) 229 | test_sort("HEAPSORT 3", heapsort, function(s, i) return 1000 - i end) 230 | -------------------------------------------------------------------------------- /tests/string-escapes-1.lua: -------------------------------------------------------------------------------- 1 | print("foo\z bar") 2 | print("foo\z 3 | bar") 4 | print("foo\ 5 | bar") 6 | print("\97") 7 | print("\"\'\\") 8 | print("\x65") 9 | 10 | -------------------------------------------------------------------------------- /tests/string-method-1.lua: -------------------------------------------------------------------------------- 1 | print(("hi"):rep(3)) 2 | 3 | -------------------------------------------------------------------------------- /tests/string-method-2.lua: -------------------------------------------------------------------------------- 1 | print(("hi").rep("Hello! ", 3)) 2 | 3 | -------------------------------------------------------------------------------- /tests/struct-assign-1.lua: -------------------------------------------------------------------------------- 1 | local function init(x) 2 | return {name= 'foo', value = x*x} 3 | end 4 | 5 | local s = init(2.5) 6 | local v = s.value 7 | print(v) 8 | -------------------------------------------------------------------------------- /tests/table-1.lua: -------------------------------------------------------------------------------- 1 | local name = 'secret' 2 | local t = { pi = 3.14, ciao = 'Hello!', [name] = 'sshhhh....' } 3 | print(t.pi, t.ciao, t.secret) 4 | -------------------------------------------------------------------------------- /tests/table-2.lua: -------------------------------------------------------------------------------- 1 | local t = { pi = 3.14, ciao = 'hello', 3, 7, 'boo', 21 } 2 | print(t.pi, t[1], t[2], t[3], t[4]) 3 | -------------------------------------------------------------------------------- /tests/table-3.lua: -------------------------------------------------------------------------------- 1 | local x, y = 3, 4 2 | local t = {[y-x] = 1, [2*x-y] = 4, [x] = 9, [y] = 16, [2*y-x] = 25 } 3 | print(t[1], t[2], t[3], t[4], t[5]) 4 | -------------------------------------------------------------------------------- /tests/table-4.lua: -------------------------------------------------------------------------------- 1 | local t = { pi = 3.14, ciao = 'hello', 1, list = {3, 7, 'boum'}, 4, 9} 2 | print(t.pi, t[1], t[2], t[3], #t.list) 3 | -------------------------------------------------------------------------------- /tests/table-5.lua: -------------------------------------------------------------------------------- 1 | local t = {[0] = 7, 9, 14, 17} 2 | for k = 0, 3 do print(t[k]) end 3 | 4 | -------------------------------------------------------------------------------- /tests/table-6.lua: -------------------------------------------------------------------------------- 1 | local html = { 2 | ["<"] = "<", 3 | [">"] = ">", 4 | ["&"] = "&" 5 | } 6 | 7 | for k, v in pairs(html) do 8 | print(k, v) 9 | end 10 | 11 | -------------------------------------------------------------------------------- /tests/table-7.lua: -------------------------------------------------------------------------------- 1 | print(({"Go away!", "Hello", "Bof!"})[2]) 2 | 3 | -------------------------------------------------------------------------------- /tests/table-8.lua: -------------------------------------------------------------------------------- 1 | local eq = function(n, t1, t2) 2 | for i = 1, n do 3 | if t1[i] ~= t2[i] then 4 | local msg = "[" .. i .. "]: " 5 | msg = msg .. (tostring(t1[i]) or "nil") .. " " .. (tostring(t2[i]) or "nil") 6 | error(msg, 2) 7 | end 8 | end 9 | end 10 | local foo = function(...) 11 | return ... 12 | end 13 | local triple, single = {2, 3, 4}, {2, nil, nil} 14 | eq(4, {foo(2,3,4)}, triple) 15 | eq(4, {(foo(2,3,4))}, single) 16 | eq(6, {foo(2,3,4), 3, 4}, triple) 17 | eq(6, {2, 3, (foo(4,5,6))}, triple) 18 | eq(6, {2, 3, foo(4,5,6)}, {2, 3, 4, 5, 6}) 19 | -------------------------------------------------------------------------------- /tests/test-1.lua: -------------------------------------------------------------------------------- 1 | local x = {} 2 | for k = 1, 10 do 3 | x[k] = k*k + 1 4 | end 5 | for k = 1, 10 do print(x[k]) end 6 | 7 | -------------------------------------------------------------------------------- /tests/test-10.lua: -------------------------------------------------------------------------------- 1 | local a, b = 2*math.pi + 1, math.exp(1) / 2 2 | print(a, b) 3 | 4 | -------------------------------------------------------------------------------- /tests/test-2.lua: -------------------------------------------------------------------------------- 1 | local k = "foo" 2 | k = tonumber(k) and "foo" or k 3 | print("K", k) 4 | 5 | -------------------------------------------------------------------------------- /tests/test-3.lua: -------------------------------------------------------------------------------- 1 | local a, b = 3, 5 2 | local foo = {} 3 | 4 | foo.a, foo.b = a, b 5 | 6 | print(foo.a, foo.b) 7 | 8 | -------------------------------------------------------------------------------- /tests/test-4.lua: -------------------------------------------------------------------------------- 1 | local k = "foo" 2 | function some_f(s) return string.byte(s) end 3 | k = tonumber(k) or some_f(k) or k 4 | print("K", k) 5 | 6 | -------------------------------------------------------------------------------- /tests/test-5.lua: -------------------------------------------------------------------------------- 1 | local a 2 | a = a and 15 3 | local b 4 | b = b or 10 5 | print(a, b) 6 | 7 | 8 | -------------------------------------------------------------------------------- /tests/test-6.lua: -------------------------------------------------------------------------------- 1 | local a 2 | local b 3 | a = b or 12 4 | print(a, b) 5 | 6 | -------------------------------------------------------------------------------- /tests/test-7.lua: -------------------------------------------------------------------------------- 1 | local a 2 | a = "something" and ("'" .. type(a)) 3 | print(a) 4 | 5 | -------------------------------------------------------------------------------- /tests/test-8.lua: -------------------------------------------------------------------------------- 1 | local a 2 | a = nil or ("'" .. type(a)) 3 | print(a) 4 | 5 | -------------------------------------------------------------------------------- /tests/test-9.lua: -------------------------------------------------------------------------------- 1 | local a = 5 2 | a = (a > 10) or a+1 3 | print(a) 4 | -------------------------------------------------------------------------------- /tests/uclo-1.lua: -------------------------------------------------------------------------------- 1 | local z1 2 | local i = 1 3 | while i <= 2 do 4 | local j = i 5 | z1 = z1 or function() return j end 6 | i = i + 1 7 | end 8 | print(z1()) 9 | -------------------------------------------------------------------------------- /tests/uclo-2.lua: -------------------------------------------------------------------------------- 1 | local z1, z2 2 | for i=1,10 do 3 | local function f() return i end 4 | if z1 then z2 = f else z1 = f end 5 | end 6 | print(z1()) 7 | print(z2()) 8 | -------------------------------------------------------------------------------- /tests/uclo-3.lua: -------------------------------------------------------------------------------- 1 | local z1, z2 2 | local i = 1 3 | repeat 4 | local j = i 5 | local function f() return j end 6 | if z1 then z2 = f else z1 = f end 7 | i = i + 1 8 | until i > 10 9 | print(z1()) 10 | print(z2()) 11 | -------------------------------------------------------------------------------- /tests/uclo-4.lua: -------------------------------------------------------------------------------- 1 | local x = 1 2 | local function f() 3 | local y = 0 4 | for i=1,100 do y=y+x end 5 | return y 6 | end 7 | print(f()) 8 | x = 2 9 | print(f()) 10 | -------------------------------------------------------------------------------- /tests/vararg-pack-1.lua: -------------------------------------------------------------------------------- 1 | local function foo(...) 2 | local arg = {...} 3 | for k = 1, #arg do 4 | print(arg[k]) 5 | end 6 | end 7 | 8 | foo("hello", "boy") 9 | 10 | -------------------------------------------------------------------------------- /tests/vararg-pack-2.lua: -------------------------------------------------------------------------------- 1 | local function foo(msg, ...) 2 | local arg = {...} 3 | print(msg) 4 | for k = 1, #arg do 5 | print(arg[k]) 6 | end 7 | end 8 | 9 | foo("Hey!", "hello", "boy") 10 | 11 | -------------------------------------------------------------------------------- /tests/vararg-pack-3.lua: -------------------------------------------------------------------------------- 1 | local function foo(msg, ...) 2 | local arg = {"wait", "a", "moment", ...} 3 | print(msg) 4 | for k = 1, #arg do 5 | print(arg[k]) 6 | end 7 | end 8 | 9 | foo("Hey", "hello", "boy") 10 | 11 | -------------------------------------------------------------------------------- /tests/vararg-pack-4.lua: -------------------------------------------------------------------------------- 1 | local function foo(x) 2 | return x, 2*x, x^2-1 3 | end 4 | 5 | local function boo(x) 6 | local arg = { foo(x) } 7 | for k = 1, #arg do 8 | print(arg[k]) 9 | end 10 | end 11 | 12 | boo(7) 13 | 14 | -------------------------------------------------------------------------------- /tests/vararg-pack-5.lua: -------------------------------------------------------------------------------- 1 | local function foo(...) 2 | local arg = {..., "friend"} 3 | for k = 1, #arg do 4 | print(arg[k]) 5 | end 6 | end 7 | 8 | foo("hello", "boy") 9 | 10 | -------------------------------------------------------------------------------- /tests/vararg-pack-6.lua: -------------------------------------------------------------------------------- 1 | local foo = function(...) 2 | local arg = {n=select('#',...),...} 3 | -- arg.n is the real size 4 | for i = 1,arg.n do 5 | print(arg[i]) 6 | end 7 | end 8 | 9 | foo("hello", nil, nil, "boy", 3, 4) 10 | 11 | -------------------------------------------------------------------------------- /tests/while-loop-1.lua: -------------------------------------------------------------------------------- 1 | local sum = 0 2 | local i = 1 3 | while i < 10 do 4 | sum = sum + i*i 5 | i = i + 1 6 | end 7 | print(sum) 8 | -------------------------------------------------------------------------------- /tests/while-loop-2.lua: -------------------------------------------------------------------------------- 1 | local function foo(k) 2 | return k*k < 49 3 | end 4 | 5 | local sum = 0 6 | local i = 1 7 | while i < 10 and foo(i) do 8 | sum = sum + i*i 9 | i = i + 1 10 | end 11 | print(sum) 12 | -------------------------------------------------------------------------------- /tests/while-loop-2b.lua: -------------------------------------------------------------------------------- 1 | local sum = 0 2 | local i = 1 3 | while i < 10 and i*i < 49 do 4 | sum = sum + i*i 5 | i = i + 1 6 | end 7 | print(sum) 8 | --------------------------------------------------------------------------------