├── CHANGES ├── LICENSE ├── Makefile ├── README ├── clua ├── examples-lua ├── bisect.lua ├── cf.lua ├── echo.lua ├── env.lua ├── factorial.lua ├── fib.lua ├── fibfor.lua ├── globals.lua ├── hello.lua ├── life.lua ├── luac.lua ├── printf.lua ├── readonly.lua ├── sieve.lua ├── sort.lua ├── table.lua ├── trace-calls.lua ├── trace-globals.lua └── xd.lua ├── lib ├── gg.lua ├── lexer.lua ├── lua2c │ ├── ast2cast.lua │ └── cast2string.lua ├── metalua │ ├── base.lua │ ├── runtime.lua │ ├── string2.lua │ └── table2.lua ├── mlp_expr.lua ├── mlp_ext.lua ├── mlp_lexer.lua ├── mlp_meta.lua ├── mlp_misc.lua ├── mlp_stat.lua └── mlp_table.lua └── lua2c.lua /CHANGES: -------------------------------------------------------------------------------- 1 | 2009-03-13 2 | upgraded to metalua 0.5 from git 3 | 4 | 2008-09-20 5 | upgraded to metalua libraries to git version 6 | c995760d4ba67c4250ac6029c30ce9491e34ccc2 , which incorporates parsing 7 | bug patches and new lineinfo/comments in ASTs. 8 | added: Lua source comments now transfered to C source. 9 | added: C functions converted from Lua functions are now named based on 10 | the Lua function name (e.g. "function example()" --> "int 11 | lcf1_example(lua_State*)"). 12 | refactor: split lua2c.lua into lua2c.lua + ast2cast.lua + cast2string.lua. 13 | changed: made ast_to_cast reentrant. 14 | 15 | 2008-08-02 16 | added: support LUA_INIT environment variable. 17 | Recommended by Shmuel Zeigerman 18 | 19 | 2008-08-01 20 | added: -C (C source generation only) command-line switch to clua. 21 | added: debugging: assert stack checks between statements 22 | fixed: warning from "unused variable `lc_nextra'" 23 | 24 | 2008-07-31 25 | added: -c (compile only) command-line switch to clua. 26 | fixed numeric ops with metamethods returning non-numbers 27 | fixed: handle "(error object is not a string)" 28 | fixed: elseif expression evaluation evaluation order 29 | 30 | 2008-07-30 31 | implemented: ignore shebang lines (#...) 32 | implemented: globals via lua_getfield/lua_setfield/LUA_ENVIRONINDEX 33 | rather than lua_getglobal/lua_setglobal. 34 | fixed bug in local function when an upvalue. 35 | fixed mlp/gg lexer bug in numeric escapes \ddd in strings. 36 | fixed mlp/gg lexer bug in "\\\n" in strings. 37 | fixed mlp/gg lexer bug in numbers, e.g. ".1" 38 | 39 | 2008-07-29 40 | optimized locals not used as upvalues 41 | fixed numeric break stack bug 42 | fixed bug in length (#) op for non-temporary object 43 | 44 | 2008-07-26 45 | implemented closures/upvalues. note: in this initial stage, 46 | all locals are implemented as upvalues, which is slow 47 | (this is planned to be improved) 48 | implemented "local function" 49 | implemented numeric constants +-inf, nan 50 | fixed multiassignment 51 | other bug fixes 52 | 53 | 2008-07-22 54 | implemented: vararg (...), command-line arguments, 55 | unary -, # (length), repeat/until, elseif, `Localrec 56 | fixed function calls where number of actual parameters not equal 57 | to number of formal parameters. 58 | other bug fixes 59 | 60 | 2008-07-19 61 | major refactoring: Conversion process is now 62 | Lua string -> Lua AST -> C AST -> C string. 63 | (last two were previously combined) 64 | renamed lua2c script to clua 65 | other bug fixes 66 | 67 | 2008-07-18 68 | implemented: <=, for in, arg 69 | fixed if stack usage 70 | 71 | 2008-07-17 72 | implemented: do, not 73 | fixed and/or 74 | other bug fixes 75 | 76 | 2008-07-16 77 | implemented: concat, table, method call, assign to locals, while. 78 | arithmetic binary ops now generate functions rather than inline code. 79 | many bug fixes 80 | 81 | 2008-07-15 82 | initial experimental version 83 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidm/lua2c/c5b239dd5a9fad5718ffaa16e6a30cca8053ba92/LICENSE -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for building in g++/gnumake. 2 | 3 | all : 4 | @echo nothing to do 5 | 6 | # builds redistributable 7 | TAG=`date +%Y-%m-%d|tr -d '-'` 8 | dist : 9 | DIR=lua2c-$(TAG) ; \ 10 | (rm -fr $$DIR && \ 11 | mkdir $$DIR && \ 12 | mkdir $$DIR/examples-lua && \ 13 | mkdir $$DIR/lib && \ 14 | mkdir $$DIR/lib/metalua && \ 15 | cp README LICENSE CHANGES Makefile lua2c.lua clua \ 16 | $$DIR && \ 17 | cp examples-lua/*.lua $$DIR/examples-lua/ && \ 18 | cp lib/*.lua $$DIR/lib/ && \ 19 | cp lib/metalua/*.lua $$DIR/lib/metalua/ && \ 20 | tar czvf $$DIR.tar.gz $$DIR ) 21 | 22 | clean : 23 | rm -f *.stackdump 24 | rm -f `find . -name '*~'` 25 | rm -f `find . -name '*.c' -o -name '*.exe'` 26 | rm -fr lua2c-* 27 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | lua2c - converts Lua 5.1 source code to C code. 2 | 3 | == Description == 4 | 5 | This utility converts a given Lua source file into an equivalent C 6 | source file written in terms of Lua C API calls. At least, this works 7 | for a large subset of the Lua language (see limitations below). 8 | 9 | The compiler is written entirely in Lua, and no build/install is 10 | needed. This project reuses Metalua's gg/mlp parser to convert Lua 11 | source to a Metalua-style [1] AST over which the compiler then 12 | operates. lua2c does not require Metalua itself though since gg/mlp 13 | is bundled in the distribution and is written in pure Lua. 14 | 15 | == Usage == 16 | 17 | Example usage: 18 | 19 | lua lua2c.lua test/bisect.lua 20 | 21 | which generates a C file to standard output. 22 | 23 | You may also use the shell script "clua" to compile Lua->C->machine 24 | code and execute all in one step. However, you may need to edit the 25 | variables in the file to match your system since this utility invokes 26 | the C compiler. 27 | 28 | ./clua test/bisect.lua 29 | 30 | lua2c can even compile itself! (Note: the -c option compiles only 31 | without running.) 32 | 33 | ./clua -c lua2c.lua # compile lua2c binary 34 | 35 | ./lua2c examples-lua/bisect.lua # test 36 | 37 | == Related Work == 38 | 39 | * luac2c - This related effort by Hisham Muhammad [2] converts Lua 40 | bytecodes to C source, whereas this project converts Lua 41 | source to C source. luac2c runs into a few similar 42 | limitations as given below. luac2c has remained experimental but 43 | superseded Luiz Henrique De Figueiredo's very basic but similarly 44 | named lua2c for Lua 4.0 [3]. 45 | 46 | * luagen++ [4] uses C++ expression templates to translate 47 | Lua-like C++ statements to C API calls. Some of the code 48 | generation structure is actually fairly similar to lua2c. 49 | 50 | * Python Pyrex [5] does something similar in Python but has the 51 | added goal of lowering the boundary between C and Python 52 | code. Something like that could be done with lua2c, 53 | especially since lua2c uses the extensible gg parser. 54 | 55 | * Clue by David Given [6] does the opposite: convert C source 56 | to Lua source. 57 | 58 | * luac + BinToCee allow compilation of Lua source to bytecodes 59 | and/or embedding in a C file. 60 | 61 | == Potential Uses == 62 | 63 | I think this project not only is theoretically nice to have but 64 | has a number of potential uses: 65 | 66 | * Provide another approach of compiling Lua to machine code 67 | (rather than luac + bin2c). 68 | 69 | * Streamline the interface between C and Lua code and allow 70 | efficient access to C datatypes from Lua (see Pyrex above). 71 | 72 | * Compile Lua to optimized C. For example, by statically 73 | determining that certain variables are used in a restricted 74 | way (e.g. by decorating the Lua source file with pragmas or 75 | determining this implicitly by inference), certain code 76 | constructs might be simplified to use plain C rather that 77 | the Lua C API. This could allow certain Lua code written 78 | with sufficient care to run at native speeds. Since it compiles 79 | to C, it will even work on CPUs where LuaJIT is not available. 80 | 81 | == Limitations / Status == 82 | 83 | WARNING: This code passes much of the Lua 5.1 test suite [7] and can 84 | compile itself, but the code is new and there can still be errors. 85 | In particular, a few language features (e.g. coroutines) are not 86 | implemented. See comments in lua2c.lua for details. Please report 87 | bugs/patches on the wiki. 88 | 89 | lua2c does not currently support coroutines, functions that normally 90 | reject C functions (e.g. setfenv), and possibly tail call 91 | optimizations. Not all of these have the exact analogue in C. 92 | Coroutines might not ever be supported. However, some solutions might 93 | be explored [8][9], including possibly generating code that maintains 94 | the coroutine context in Lua tables. 95 | 96 | Closures and upvalues are implemented, but creating and accessing 97 | upvalues is somewhat slow due to the implementation (see implementation 98 | notes below) and hopefully can be improved. 99 | 100 | Once the code is more complete/robust, more attention can be given to 101 | optimizing the code generation. Performance was 25%-75% of regular 102 | Lua when running a few tests [11], but hopefully future optimizations 103 | will improve that. 104 | 105 | == Lua 5.2 Notes == 106 | 107 | Note: LuaFiveTwo (as of 5.2.0-work4) deprecates getfenv and setfenv, 108 | which eliminates one of the limitations above. LuaFiveTwo has new lua_arith 109 | and lua_compare C API function, which eliminate the need for lua2c to 110 | reimplement these functions. LuaFiveTwo also has new lua_yieldk, lua_callk, 111 | and lua_pcallk functions for coroutines and might help to implement 112 | coroutines in lua2c. 113 | 114 | == Project Page == 115 | 116 | The project page is currently http://lua-users.org/wiki/LuaToCee . 117 | 118 | == Download == 119 | 120 | * Latest development source (recommended): http://github.com/davidm/lua2c/ . 121 | From here you can browse the source, download a tar/zip snapshot, or 122 | checkout with git by running "{{git clone 123 | git://github.com/davidm/lua2c.git}}". 124 | * Last release distribution: (see Project Page above) 125 | 126 | == Licensing == 127 | 128 | (c) 2008 David Manura. Licensed under the same terms as Lua (MIT 129 | license). See included LICENSE file for full licensing details. 130 | Please post any patches/improvements. 131 | 132 | == References == 133 | 134 | * [1] Metalua AST - http://metalua.luaforge.net/manual006.html#toc17 135 | * [2] luac2c (previously named luatoc) - LuaList:2006-07/msg00144.html 136 | * [3] LHF's lua2c for Lua 4.0 - LuaList:2002-01/msg00296.html 137 | * [4] luagen++ - LuaGenPlusPlus 138 | * [5] Pyrex - http://www.cosc.canterbury.ac.nz/greg.ewing/python/Pyrex/ 139 | * [6] Clue - http://cluecc.sourceforge.net/ 140 | * [7] Lua 5.1 test suite - 141 | http://www.inf.puc-rio.br/~roberto/lua/lua5.1-tests.tar.gz 142 | * [8] Wikipedia:Coroutine - Implementations for C 143 | http://en.wikipedia.org/wiki/Coroutine#Implementations_for_C 144 | * [9] Coco - http://luajit.org/coco.html Coco - 145 | True C coroutine semantics (used in LuaJIT) 146 | * [10] BinToCee - http://lua-users.org/wiki/BinToCee 147 | * [11] The Computer Language Benchmarks Game 148 | http://shootout.alioth.debian.org/gp4/lua.php 149 | * http://lua-users.org/wiki/LuaImplementations - 150 | other source translators and Lua reimplementations 151 | -------------------------------------------------------------------------------- /clua: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # Shell script wrapper to convert Lua to C and optionally 4 | # compile and run it. 5 | # 6 | # option -c causes compilation only (no run) 7 | # option -C causes C source generation only (no compile or run) 8 | 9 | pushd "`dirname \"$0\"`" > /dev/null 10 | export CWD="$PWD" 11 | popd > /dev/null 12 | 13 | # You may need to change these variables: 14 | LUA=lua 15 | CC=gcc 16 | CFLAGS="-O3 -fomit-frame-pointer -DNDEBUG -Wall -Ilua/src" 17 | #CFLAGS="-O2 -DNDEBUG -Wall -Ilua/src" 18 | #CFLAGS=-g 19 | LFLAGS="-Llua/src -llua" 20 | LUA2C="${LUA} $CWD/lua2c.lua" 21 | #LUA2C="./lua2c" 22 | 23 | COMPILEONLY= 24 | if [ "$1" = "-c" ] 25 | then 26 | COMPILEONLY=1 27 | shift 28 | elif [ "$1" = "-C" ] 29 | then 30 | COMPILEONLY=2 31 | shift 32 | fi 33 | if [ "$1" = "" ] 34 | then 35 | echo "usage: clua [options] [filename.lua] ..." 36 | echo " options:" 37 | echo " -c compile only (no run)" 38 | echo " -C generate C source only (no compile or run)" 39 | exit 1 40 | fi 41 | 42 | LUAFILE=$1 43 | FILENAME=${LUAFILE%.*} 44 | CFILE=${FILENAME}.c 45 | 46 | LUA_PATH=$CWD/lib/?.lua ${LUA2C} ${LUAFILE} > ${CFILE} || exit 1 47 | if [ "$COMPILEONLY" = "2" ] 48 | then 49 | exit 0 50 | fi 51 | 52 | ${CC} ${CFLAGS} ${CFILE} -o ${FILENAME} ${LFLAGS} || exit 1 53 | if [ "$COMPILEONLY" = "1" ] 54 | then 55 | exit 0 56 | fi 57 | 58 | shift 59 | ./${FILENAME} $@ 60 | -------------------------------------------------------------------------------- /examples-lua/bisect.lua: -------------------------------------------------------------------------------- 1 | -- bisection method for solving non-linear equations 2 | 3 | delta=1e-6 -- tolerance 4 | 5 | function bisect(f,a,b,fa,fb) 6 | local c=(a+b)/2 7 | io.write(n," c=",c," a=",a," b=",b,"\n") 8 | if c==a or c==b or math.abs(a-b)y end) 58 | show("after reverse selection sort",x) 59 | qsort(x,1,n,function (x,y) return x>> ",string.rep(" ",level)) 9 | if t~=nil and t.currentline>=0 then io.write(t.short_src,":",t.currentline," ") end 10 | t=debug.getinfo(2) 11 | if event=="call" then 12 | level=level+1 13 | else 14 | level=level-1 if level<0 then level=0 end 15 | end 16 | if t.what=="main" then 17 | if event=="call" then 18 | io.write("begin ",t.short_src) 19 | else 20 | io.write("end ",t.short_src) 21 | end 22 | elseif t.what=="Lua" then 23 | -- table.foreach(t,print) 24 | io.write(event," ",t.name or "(Lua)"," <",t.linedefined,":",t.short_src,">") 25 | else 26 | io.write(event," ",t.name or "(C)"," [",t.what,"] ") 27 | end 28 | io.write("\n") 29 | end 30 | 31 | debug.sethook(hook,"cr") 32 | level=0 33 | -------------------------------------------------------------------------------- /examples-lua/trace-globals.lua: -------------------------------------------------------------------------------- 1 | -- trace assigments to global variables 2 | 3 | do 4 | -- a tostring that quotes strings. note the use of the original tostring. 5 | local _tostring=tostring 6 | local tostring=function(a) 7 | if type(a)=="string" then 8 | return string.format("%q",a) 9 | else 10 | return _tostring(a) 11 | end 12 | end 13 | 14 | local log=function (name,old,new) 15 | local t=debug.getinfo(3,"Sl") 16 | local line=t.currentline 17 | io.write(t.short_src) 18 | if line>=0 then io.write(":",line) end 19 | io.write(": ",name," is now ",tostring(new)," (was ",tostring(old),")","\n") 20 | end 21 | 22 | local g={} 23 | local set=function (t,name,value) 24 | log(name,g[name],value) 25 | g[name]=value 26 | end 27 | setmetatable(getfenv(),{__index=g,__newindex=set}) 28 | end 29 | 30 | -- an example 31 | 32 | a=1 33 | b=2 34 | a=10 35 | b=20 36 | b=nil 37 | b=200 38 | print(a,b,c) 39 | -------------------------------------------------------------------------------- /examples-lua/xd.lua: -------------------------------------------------------------------------------- 1 | -- hex dump 2 | -- usage: lua xd.lua < file 3 | 4 | local offset=0 5 | while true do 6 | local s=io.read(16) 7 | if s==nil then return end 8 | io.write(string.format("%08X ",offset)) 9 | string.gsub(s,"(.)", 10 | function (c) io.write(string.format("%02X ",string.byte(c))) end) 11 | io.write(string.rep(" ",3*(16-string.len(s)))) 12 | io.write(" ",string.gsub(s,"%c","."),"\n") 13 | offset=offset+16 14 | end 15 | -------------------------------------------------------------------------------- /lib/gg.lua: -------------------------------------------------------------------------------- 1 | ---------------------------------------------------------------------- 2 | -- Metalua. 3 | -- 4 | -- Summary: parser generator. Collection of higher order functors, 5 | -- which allow to build and combine parsers. Relies on a lexer 6 | -- that supports the same API as the one exposed in mll.lua. 7 | -- 8 | ---------------------------------------------------------------------- 9 | -- 10 | -- Copyright (c) 2006-2008, Fabien Fleutot . 11 | -- 12 | -- This software is released under the MIT Licence, see licence.txt 13 | -- for details. 14 | -- 15 | ---------------------------------------------------------------------- 16 | 17 | -------------------------------------------------------------------------------- 18 | -- 19 | -- Exported API: 20 | -- 21 | -- Parser generators: 22 | -- * [gg.sequence()] 23 | -- * [gg.multisequence()] 24 | -- * [gg.expr()] 25 | -- * [gg.list()] 26 | -- * [gg.onkeyword()] 27 | -- * [gg.optkeyword()] 28 | -- 29 | -- Other functions: 30 | -- * [gg.parse_error()] 31 | -- * [gg.make_parser()] 32 | -- * [gg.is_parser()] 33 | -- 34 | -------------------------------------------------------------------------------- 35 | 36 | module("gg", package.seeall) 37 | 38 | ------------------------------------------------------------------------------- 39 | -- parser metatable, which maps __call to method parse, and adds some 40 | -- error tracing boilerplate. 41 | ------------------------------------------------------------------------------- 42 | local parser_metatable = { } 43 | function parser_metatable.__call (parser, lx, ...) 44 | --printf ("Call parser %q of type %q", parser.name or "?", parser.kind) 45 | if mlc.metabugs then 46 | return parser:parse (lx, ...) 47 | --local x = parser:parse (lx, ...) 48 | --printf ("Result of parser %q: %s", 49 | -- parser.name or "?", 50 | -- _G.table.tostring(x, "nohash", 80)) 51 | --return x 52 | else 53 | local li = lx:lineinfo_right() or { "?", "?", "?", "?" } 54 | local status, ast = pcall (parser.parse, parser, lx, ...) 55 | if status then return ast else 56 | error (string.format ("%s\n - (l.%s, c.%s, k.%s) in parser %s", 57 | ast:strmatch "gg.lua:%d+: (.*)" or ast, 58 | li[1], li[2], li[3], parser.name or parser.kind)) 59 | end 60 | end 61 | end 62 | 63 | ------------------------------------------------------------------------------- 64 | -- Turn a table into a parser, mainly by setting the metatable. 65 | ------------------------------------------------------------------------------- 66 | function make_parser(kind, p) 67 | p.kind = kind 68 | if not p.transformers then p.transformers = { } end 69 | function p.transformers:add (x) 70 | table.insert (self, x) 71 | end 72 | setmetatable (p, parser_metatable) 73 | return p 74 | end 75 | 76 | ------------------------------------------------------------------------------- 77 | -- Return true iff [x] is a parser. 78 | -- If it's a gg-generated parser, return the name of its kind. 79 | ------------------------------------------------------------------------------- 80 | function is_parser (x) 81 | return type(x)=="function" or getmetatable(x)==parser_metatable and x.kind 82 | end 83 | 84 | ------------------------------------------------------------------------------- 85 | -- Parse a sequence, without applying builder nor transformers 86 | ------------------------------------------------------------------------------- 87 | local function raw_parse_sequence (lx, p) 88 | local r = { } 89 | for i=1, #p do 90 | e=p[i] 91 | if type(e) == "string" then 92 | if not lx:is_keyword (lx:next(), e) then 93 | parse_error (lx, "Keyword '%s' expected", e) end 94 | elseif is_parser (e) then 95 | table.insert (r, e (lx)) 96 | else 97 | gg.parse_error (lx,"Sequence `%s': element #%i is not a string ".. 98 | "nor a parser: %s", 99 | p.name, i, table.tostring(e)) 100 | end 101 | end 102 | return r 103 | end 104 | 105 | ------------------------------------------------------------------------------- 106 | -- Parse a multisequence, without applying multisequence transformers. 107 | -- The sequences are completely parsed. 108 | ------------------------------------------------------------------------------- 109 | local function raw_parse_multisequence (lx, sequence_table, default) 110 | local seq_parser = sequence_table[lx:is_keyword(lx:peek())] 111 | if seq_parser then return seq_parser (lx) 112 | elseif default then return default (lx) 113 | else return false end 114 | end 115 | 116 | ------------------------------------------------------------------------------- 117 | -- Applies all transformers listed in parser on ast. 118 | ------------------------------------------------------------------------------- 119 | local function transform (ast, parser, fli, lli) 120 | if parser.transformers then 121 | for _, t in ipairs (parser.transformers) do ast = t(ast) or ast end 122 | end 123 | if type(ast) == 'table'then 124 | local ali = ast.lineinfo 125 | if not ali or ali.first~=fli or ali.last~=lli then 126 | ast.lineinfo = { first = fli, last = lli } 127 | end 128 | end 129 | return ast 130 | end 131 | 132 | ------------------------------------------------------------------------------- 133 | -- Generate a tracable parsing error (not implemented yet) 134 | ------------------------------------------------------------------------------- 135 | function parse_error(lx, fmt, ...) 136 | local li = lx:lineinfo_left() or {-1,-1,-1, ""} 137 | local msg = string.format("line %i, char %i: "..fmt, li[1], li[2], ...) 138 | local src = lx.src 139 | if li[3]>0 and src then 140 | local i, j = li[3], li[3] 141 | while src:sub(i,i) ~= '\n' and i>=0 do i=i-1 end 142 | while src:sub(j,j) ~= '\n' and j<=#src do j=j+1 end 143 | local srcline = src:sub (i+1, j-1) 144 | local idx = string.rep (" ", li[2]).."^" 145 | msg = string.format("%s\n>>> %s\n>>> %s", msg, srcline, idx) 146 | end 147 | error(msg) 148 | end 149 | 150 | ------------------------------------------------------------------------------- 151 | -- 152 | -- Sequence parser generator 153 | -- 154 | ------------------------------------------------------------------------------- 155 | -- Input fields: 156 | -- 157 | -- * [builder]: how to build an AST out of sequence parts. let [x] be the list 158 | -- of subparser results (keywords are simply omitted). [builder] can be: 159 | -- - [nil], in which case the result of parsing is simply [x] 160 | -- - a string, which is then put as a tag on [x] 161 | -- - a function, which takes [x] as a parameter and returns an AST. 162 | -- 163 | -- * [name]: the name of the parser. Used for debug messages 164 | -- 165 | -- * [transformers]: a list of AST->AST functions, applied in order on ASTs 166 | -- returned by the parser. 167 | -- 168 | -- * Table-part entries corresponds to keywords (strings) and subparsers 169 | -- (function and callable objects). 170 | -- 171 | -- After creation, the following fields are added: 172 | -- * [parse] the parsing function lexer->AST 173 | -- * [kind] == "sequence" 174 | -- * [name] is set, if it wasn't in the input. 175 | -- 176 | ------------------------------------------------------------------------------- 177 | function sequence (p) 178 | make_parser ("sequence", p) 179 | 180 | ------------------------------------------------------------------- 181 | -- Parsing method 182 | ------------------------------------------------------------------- 183 | function p:parse (lx) 184 | -- Raw parsing: 185 | local fli = lx:lineinfo_right() 186 | local seq = raw_parse_sequence (lx, self) 187 | local lli = lx:lineinfo_left() 188 | 189 | -- Builder application: 190 | local builder, tb = self.builder, type (self.builder) 191 | if tb == "string" then seq.tag = builder 192 | elseif tb == "function" or builder and builder.__call then seq = builder(seq) 193 | elseif builder == nil then -- nothing 194 | else error ("Invalid builder of type "..tb.." in sequence") end 195 | seq = transform (seq, self, fli, lli) 196 | assert (not seq or seq.lineinfo) 197 | return seq 198 | end 199 | 200 | ------------------------------------------------------------------- 201 | -- Construction 202 | ------------------------------------------------------------------- 203 | -- Try to build a proper name 204 | if not p.name and type(p[1])=="string" then 205 | p.name = p[1].." ..." 206 | if type(p[#p])=="string" then p.name = p.name .. " " .. p[#p] end 207 | else 208 | p.name = "" 209 | end 210 | 211 | return p 212 | end -- 213 | 214 | 215 | ------------------------------------------------------------------------------- 216 | -- 217 | -- Multiple, keyword-driven, sequence parser generator 218 | -- 219 | ------------------------------------------------------------------------------- 220 | -- in [p], useful fields are: 221 | -- 222 | -- * [transformers]: as usual 223 | -- 224 | -- * [name]: as usual 225 | -- 226 | -- * Table-part entries must be sequence parsers, or tables which can 227 | -- be turned into a sequence parser by [gg.sequence]. These 228 | -- sequences must start with a keyword, and this initial keyword 229 | -- must be different for each sequence. The table-part entries will 230 | -- be removed after [gg.multisequence] returns. 231 | -- 232 | -- * [default]: the parser to run if the next keyword in the lexer is 233 | -- none of the registered initial keywords. If there's no default 234 | -- parser and no suitable initial keyword, the multisequence parser 235 | -- simply returns [false]. 236 | -- 237 | -- After creation, the following fields are added: 238 | -- 239 | -- * [parse] the parsing function lexer->AST 240 | -- 241 | -- * [sequences] the table of sequences, indexed by initial keywords. 242 | -- 243 | -- * [add] method takes a sequence parser or a config table for 244 | -- [gg.sequence], and adds/replaces the corresponding sequence 245 | -- parser. If the keyword was already used, the former sequence is 246 | -- removed and a warning is issued. 247 | -- 248 | -- * [get] method returns a sequence by its initial keyword 249 | -- 250 | -- * [kind] == "multisequence" 251 | -- 252 | ------------------------------------------------------------------------------- 253 | function multisequence (p) 254 | make_parser ("multisequence", p) 255 | 256 | ------------------------------------------------------------------- 257 | -- Add a sequence (might be just a config table for [gg.sequence]) 258 | ------------------------------------------------------------------- 259 | function p:add (s) 260 | -- compile if necessary: 261 | local keyword = s[1] 262 | if not is_parser(s) then sequence(s) end 263 | if is_parser(s) ~= 'sequence' or type(keyword) ~= "string" then 264 | if self.default then -- two defaults 265 | error ("In a multisequence parser, all but one sequences ".. 266 | "must start with a keyword") 267 | else self.default = s end -- first default 268 | elseif self.sequences[keyword] then -- duplicate keyword 269 | eprintf (" *** Warning: keyword %q overloaded in multisequence ***", keyword) 270 | self.sequences[keyword] = s 271 | else -- newly caught keyword 272 | self.sequences[keyword] = s 273 | end 274 | end -- 275 | 276 | ------------------------------------------------------------------- 277 | -- Get the sequence starting with this keyword. [kw :: string] 278 | ------------------------------------------------------------------- 279 | function p:get (kw) return self.sequences [kw] end 280 | 281 | ------------------------------------------------------------------- 282 | -- Remove the sequence starting with keyword [kw :: string] 283 | ------------------------------------------------------------------- 284 | function p:del (kw) 285 | if not self.sequences[kw] then 286 | eprintf("*** Warning: trying to delete sequence starting ".. 287 | "with %q from a multisequence having no such ".. 288 | "entry ***", kw) end 289 | local removed = self.sequences[kw] 290 | self.sequences[kw] = nil 291 | return removed 292 | end 293 | 294 | ------------------------------------------------------------------- 295 | -- Parsing method 296 | ------------------------------------------------------------------- 297 | function p:parse (lx) 298 | local fli = lx:lineinfo_right() 299 | local x = raw_parse_multisequence (lx, self.sequences, self.default) 300 | local lli = lx:lineinfo_left() 301 | return transform (x, self, fli, lli) 302 | end 303 | 304 | ------------------------------------------------------------------- 305 | -- Construction 306 | ------------------------------------------------------------------- 307 | -- Register the sequences passed to the constructor. They're going 308 | -- from the array part of the parser to the hash part of field 309 | -- [sequences] 310 | p.sequences = { } 311 | for i=1, #p do p:add (p[i]); p[i] = nil end 312 | 313 | -- FIXME: why is this commented out? 314 | --if p.default and not is_parser(p.default) then sequence(p.default) end 315 | return p 316 | end -- 317 | 318 | 319 | ------------------------------------------------------------------------------- 320 | -- 321 | -- Expression parser generator 322 | -- 323 | ------------------------------------------------------------------------------- 324 | -- 325 | -- Expression configuration relies on three tables: [prefix], [infix] 326 | -- and [suffix]. Moreover, the primary parser can be replaced by a 327 | -- table: in this case the [primary] table will be passed to 328 | -- [gg.multisequence] to create a parser. 329 | -- 330 | -- Each of these tables is a modified multisequence parser: the 331 | -- differences with respect to regular multisequence config tables are: 332 | -- 333 | -- * the builder takes specific parameters: 334 | -- - for [prefix], it takes the result of the prefix sequence parser, 335 | -- and the prefixed expression 336 | -- - for [infix], it takes the left-hand-side expression, the results 337 | -- of the infix sequence parser, and the right-hand-side expression. 338 | -- - for [suffix], it takes the suffixed expression, and theresult 339 | -- of the suffix sequence parser. 340 | -- 341 | -- * the default field is a list, with parameters: 342 | -- - [parser] the raw parsing function 343 | -- - [transformers], as usual 344 | -- - [prec], the operator's precedence 345 | -- - [assoc] for [infix] table, the operator's associativity, which 346 | -- can be "left", "right" or "flat" (default to left) 347 | -- 348 | -- In [p], useful fields are: 349 | -- * [transformers]: as usual 350 | -- * [name]: as usual 351 | -- * [primary]: the atomic expression parser, or a multisequence config 352 | -- table (mandatory) 353 | -- * [prefix]: prefix operators config table, see above. 354 | -- * [infix]: infix operators config table, see above. 355 | -- * [suffix]: suffix operators config table, see above. 356 | -- 357 | -- After creation, these fields are added: 358 | -- * [kind] == "expr" 359 | -- * [parse] as usual 360 | -- * each table is turned into a multisequence, and therefore has an 361 | -- [add] method 362 | -- 363 | ------------------------------------------------------------------------------- 364 | function expr (p) 365 | make_parser ("expr", p) 366 | 367 | ------------------------------------------------------------------- 368 | -- parser method. 369 | -- In addition to the lexer, it takes an optional precedence: 370 | -- it won't read expressions whose precedence is lower or equal 371 | -- to [prec]. 372 | ------------------------------------------------------------------- 373 | function p:parse (lx, prec) 374 | prec = prec or 0 375 | 376 | ------------------------------------------------------ 377 | -- Extract the right parser and the corresponding 378 | -- options table, for (pre|in|suff)fix operators. 379 | -- Options include prec, assoc, transformers. 380 | ------------------------------------------------------ 381 | local function get_parser_info (tab) 382 | local p2 = tab:get (lx:is_keyword (lx:peek())) 383 | if p2 then -- keyword-based sequence found 384 | local function parser(lx) return raw_parse_sequence(lx, p2) end 385 | return parser, p2 386 | else -- Got to use the default parser 387 | local d = tab.default 388 | if d then return d.parse or d.parser, d 389 | else return false, false end 390 | end 391 | end 392 | 393 | ------------------------------------------------------ 394 | -- Look for a prefix sequence. Multiple prefixes are 395 | -- handled through the recursive [p.parse] call. 396 | -- Notice the double-transform: one for the primary 397 | -- expr, and one for the one with the prefix op. 398 | ------------------------------------------------------ 399 | local function handle_prefix () 400 | local fli = lx:lineinfo_right() 401 | local p2_func, p2 = get_parser_info (self.prefix) 402 | local op = p2_func and p2_func (lx) 403 | if op then -- Keyword-based sequence found 404 | local ili = lx:lineinfo_right() -- Intermediate LineInfo 405 | local e = p2.builder (op, self:parse (lx, p2.prec)) 406 | local lli = lx:lineinfo_left() 407 | return transform (transform (e, p2, ili, lli), self, fli, lli) 408 | else -- No prefix found, get a primary expression 409 | local e = self.primary(lx) 410 | local lli = lx:lineinfo_left() 411 | return transform (e, self, fli, lli) 412 | end 413 | end -- 414 | 415 | ------------------------------------------------------ 416 | -- Look for an infix sequence+right-hand-side operand. 417 | -- Return the whole binary expression result, 418 | -- or false if no operator was found. 419 | ------------------------------------------------------ 420 | local function handle_infix (e) 421 | local p2_func, p2 = get_parser_info (self.infix) 422 | if not p2 then return false end 423 | 424 | ----------------------------------------- 425 | -- Handle flattening operators: gather all operands 426 | -- of the series in [list]; when a different operator 427 | -- is found, stop, build from [list], [transform] and 428 | -- return. 429 | ----------------------------------------- 430 | if (not p2.prec or p2.prec>prec) and p2.assoc=="flat" then 431 | local fli = lx:lineinfo_right() 432 | local pflat, list = p2, { e } 433 | repeat 434 | local op = p2_func(lx) 435 | if not op then break end 436 | table.insert (list, self:parse (lx, p2.prec)) 437 | local _ -- We only care about checking that p2==pflat 438 | _, p2 = get_parser_info (self.infix) 439 | until p2 ~= pflat 440 | local e2 = pflat.builder (list) 441 | local lli = lx:lineinfo_left() 442 | return transform (transform (e2, pflat, fli, lli), self, fli, lli) 443 | 444 | ----------------------------------------- 445 | -- Handle regular infix operators: [e] the LHS is known, 446 | -- just gather the operator and [e2] the RHS. 447 | -- Result goes in [e3]. 448 | ----------------------------------------- 449 | elseif p2.prec and p2.prec>prec or 450 | p2.prec==prec and p2.assoc=="right" then 451 | local fli = e.lineinfo.first -- lx:lineinfo_right() 452 | local op = p2_func(lx) 453 | if not op then return false end 454 | local e2 = self:parse (lx, p2.prec) 455 | local e3 = p2.builder (e, op, e2) 456 | local lli = lx:lineinfo_left() 457 | return transform (transform (e3, p2, fli, lli), self, fli, lli) 458 | 459 | ----------------------------------------- 460 | -- Check for non-associative operators, and complain if applicable. 461 | ----------------------------------------- 462 | elseif p2.assoc=="none" and p2.prec==prec then 463 | parser_error (lx, "non-associative operator!") 464 | 465 | ----------------------------------------- 466 | -- No infix operator suitable at that precedence 467 | ----------------------------------------- 468 | else return false end 469 | 470 | end -- 471 | 472 | ------------------------------------------------------ 473 | -- Look for a suffix sequence. 474 | -- Return the result of suffix operator on [e], 475 | -- or false if no operator was found. 476 | ------------------------------------------------------ 477 | local function handle_suffix (e) 478 | -- FIXME bad fli, must take e.lineinfo.first 479 | local p2_func, p2 = get_parser_info (self.suffix) 480 | if not p2 then return false end 481 | if not p2.prec or p2.prec>=prec then 482 | --local fli = lx:lineinfo_right() 483 | local fli = e.lineinfo.first 484 | local op = p2_func(lx) 485 | if not op then return false end 486 | local lli = lx:lineinfo_left() 487 | e = p2.builder (e, op) 488 | e = transform (transform (e, p2, fli, lli), self, fli, lli) 489 | return e 490 | end 491 | return false 492 | end -- 493 | 494 | ------------------------------------------------------ 495 | -- Parser body: read suffix and (infix+operand) 496 | -- extensions as long as we're able to fetch more at 497 | -- this precedence level. 498 | ------------------------------------------------------ 499 | local e = handle_prefix() 500 | repeat 501 | local x = handle_suffix (e); e = x or e 502 | local y = handle_infix (e); e = y or e 503 | until not (x or y) 504 | 505 | -- No transform: it already happened in operators handling 506 | return e 507 | end -- 508 | 509 | ------------------------------------------------------------------- 510 | -- Construction 511 | ------------------------------------------------------------------- 512 | if not p.primary then p.primary=p[1]; p[1]=nil end 513 | for _, t in ipairs{ "primary", "prefix", "infix", "suffix" } do 514 | if not p[t] then p[t] = { } end 515 | if not is_parser(p[t]) then multisequence(p[t]) end 516 | end 517 | function p:add(...) return self.primary:add(...) end 518 | return p 519 | end -- 520 | 521 | 522 | ------------------------------------------------------------------------------- 523 | -- 524 | -- List parser generator 525 | -- 526 | ------------------------------------------------------------------------------- 527 | -- In [p], the following fields can be provided in input: 528 | -- 529 | -- * [builder]: takes list of subparser results, returns AST 530 | -- * [transformers]: as usual 531 | -- * [name]: as usual 532 | -- 533 | -- * [terminators]: list of strings representing the keywords which 534 | -- might mark the end of the list. When non-empty, the list is 535 | -- allowed to be empty. A string is treated as a single-element 536 | -- table, whose element is that string, e.g. ["do"] is the same as 537 | -- [{"do"}]. 538 | -- 539 | -- * [separators]: list of strings representing the keywords which can 540 | -- separate elements of the list. When non-empty, one of these 541 | -- keyword has to be found between each element. Lack of a separator 542 | -- indicates the end of the list. A string is treated as a 543 | -- single-element table, whose element is that string, e.g. ["do"] 544 | -- is the same as [{"do"}]. If [terminators] is empty/nil, then 545 | -- [separators] has to be non-empty. 546 | -- 547 | -- After creation, the following fields are added: 548 | -- * [parse] the parsing function lexer->AST 549 | -- * [kind] == "list" 550 | -- 551 | ------------------------------------------------------------------------------- 552 | function list (p) 553 | make_parser ("list", p) 554 | 555 | ------------------------------------------------------------------- 556 | -- Parsing method 557 | ------------------------------------------------------------------- 558 | function p:parse (lx) 559 | 560 | ------------------------------------------------------ 561 | -- Used to quickly check whether there's a terminator 562 | -- or a separator immediately ahead 563 | ------------------------------------------------------ 564 | local function peek_is_in (keywords) 565 | return keywords and lx:is_keyword(lx:peek(), unpack(keywords)) end 566 | 567 | local x = { } 568 | local fli = lx:lineinfo_right() 569 | 570 | -- if there's a terminator to start with, don't bother trying 571 | if not peek_is_in (self.terminators) then 572 | repeat table.insert (x, self.primary (lx)) -- read one element 573 | until 574 | -- First reason to stop: There's a separator list specified, 575 | -- and next token isn't one. Otherwise, consume it with [lx:next()] 576 | self.separators and not(peek_is_in (self.separators) and lx:next()) or 577 | -- Other reason to stop: terminator token ahead 578 | peek_is_in (self.terminators) or 579 | -- Last reason: end of file reached 580 | lx:peek().tag=="Eof" 581 | end 582 | 583 | local lli = lx:lineinfo_left() 584 | 585 | -- Apply the builder. It can be a string, or a callable value, 586 | -- or simply nothing. 587 | local b = self.builder 588 | if b then 589 | if type(b)=="string" then x.tag = b -- b is a string, use it as a tag 590 | elseif type(b)=="function" then x=b(x) 591 | else 592 | local bmt = getmetatable(b) 593 | if bmt and bmt.__call then x=b(x) end 594 | end 595 | end 596 | return transform (x, self, fli, lli) 597 | end -- 598 | 599 | ------------------------------------------------------------------- 600 | -- Construction 601 | ------------------------------------------------------------------- 602 | if not p.primary then p.primary = p[1]; p[1] = nil end 603 | if type(p.terminators) == "string" then p.terminators = { p.terminators } 604 | elseif p.terminators and #p.terminators == 0 then p.terminators = nil end 605 | if type(p.separators) == "string" then p.separators = { p.separators } 606 | elseif p.separators and #p.separators == 0 then p.separators = nil end 607 | 608 | return p 609 | end -- 610 | 611 | 612 | ------------------------------------------------------------------------------- 613 | -- 614 | -- Keyword-conditionned parser generator 615 | -- 616 | ------------------------------------------------------------------------------- 617 | -- 618 | -- Only apply a parser if a given keyword is found. The result of 619 | -- [gg.onkeyword] parser is the result of the subparser (modulo 620 | -- [transformers] applications). 621 | -- 622 | -- lineinfo: the keyword is *not* included in the boundaries of the 623 | -- resulting lineinfo. A review of all usages of gg.onkeyword() in the 624 | -- implementation of metalua has shown that it was the appropriate choice 625 | -- in every case. 626 | -- 627 | -- Input fields: 628 | -- 629 | -- * [name]: as usual 630 | -- 631 | -- * [transformers]: as usual 632 | -- 633 | -- * [peek]: if non-nil, the conditionning keyword is left in the lexeme 634 | -- stream instead of being consumed. 635 | -- 636 | -- * [primary]: the subparser. 637 | -- 638 | -- * [keywords]: list of strings representing triggering keywords. 639 | -- 640 | -- * Table-part entries can contain strings, and/or exactly one parser. 641 | -- Strings are put in [keywords], and the parser is put in [primary]. 642 | -- 643 | -- After the call, the following fields will be set: 644 | -- 645 | -- * [parse] the parsing method 646 | -- * [kind] == "onkeyword" 647 | -- * [primary] 648 | -- * [keywords] 649 | -- 650 | ------------------------------------------------------------------------------- 651 | function onkeyword (p) 652 | make_parser ("onkeyword", p) 653 | 654 | ------------------------------------------------------------------- 655 | -- Parsing method 656 | ------------------------------------------------------------------- 657 | function p:parse(lx) 658 | if lx:is_keyword (lx:peek(), unpack(self.keywords)) then 659 | --local fli = lx:lineinfo_right() 660 | if not self.peek then lx:next() end 661 | local content = self.primary (lx) 662 | --local lli = lx:lineinfo_left() 663 | local fli, lli = content.lineinfo.first, content.lineinfo.last 664 | return transform (content, p, fli, lli) 665 | else return false end 666 | end 667 | 668 | ------------------------------------------------------------------- 669 | -- Construction 670 | ------------------------------------------------------------------- 671 | if not p.keywords then p.keywords = { } end 672 | for _, x in ipairs(p) do 673 | if type(x)=="string" then table.insert (p.keywords, x) 674 | else assert (not p.primary and is_parser (x)); p.primary = x end 675 | end 676 | if not next (p.keywords) then 677 | eprintf("Warning, no keyword to trigger gg.onkeyword") end 678 | assert (p.primary, 'no primary parser in gg.onkeyword') 679 | return p 680 | end -- 681 | 682 | 683 | ------------------------------------------------------------------------------- 684 | -- 685 | -- Optional keyword consummer pseudo-parser generator 686 | -- 687 | ------------------------------------------------------------------------------- 688 | -- 689 | -- This doesn't return a real parser, just a function. That function parses 690 | -- one of the keywords passed as parameters, and returns it. It returns 691 | -- [false] if no matching keyword is found. 692 | -- 693 | -- Notice that tokens returned by lexer already carry lineinfo, therefore 694 | -- there's no need to add them, as done usually through transform() calls. 695 | ------------------------------------------------------------------------------- 696 | function optkeyword (...) 697 | local args = {...} 698 | if type (args[1]) == "table" then 699 | assert (#args == 1) 700 | args = args[1] 701 | end 702 | for _, v in ipairs(args) do assert (type(v)=="string") end 703 | return function (lx) 704 | local x = lx:is_keyword (lx:peek(), unpack (args)) 705 | if x then lx:next(); return x 706 | else return false end 707 | end 708 | end 709 | 710 | 711 | ------------------------------------------------------------------------------- 712 | -- 713 | -- Run a parser with a special lexer 714 | -- 715 | ------------------------------------------------------------------------------- 716 | -- 717 | -- This doesn't return a real parser, just a function. 718 | -- First argument is the lexer class to be used with the parser, 719 | -- 2nd is the parser itself. 720 | -- The resulting parser returns whatever the argument parser does. 721 | -- 722 | ------------------------------------------------------------------------------- 723 | function with_lexer(new_lexer, parser) 724 | 725 | ------------------------------------------------------------------- 726 | -- Most gg functions take their parameters in a table, so it's 727 | -- better to silently accept when with_lexer{ } is called with 728 | -- its arguments in a list: 729 | ------------------------------------------------------------------- 730 | if not parser and #new_lexer==2 and type(new_lexer[1])=='table' then 731 | return with_lexer(unpack(new_lexer)) 732 | end 733 | 734 | ------------------------------------------------------------------- 735 | -- Save the current lexer, switch it for the new one, run the parser, 736 | -- restore the previous lexer, even if the parser caused an error. 737 | ------------------------------------------------------------------- 738 | return function (lx) 739 | local old_lexer = getmetatable(lx) 740 | lx:sync() 741 | setmetatable(lx, new_lexer) 742 | local status, result = pcall(parser, lx) 743 | lx:sync() 744 | setmetatable(lx, old_lexer) 745 | if status then return result else error(result) end 746 | end 747 | end 748 | -------------------------------------------------------------------------------- /lib/lexer.lua: -------------------------------------------------------------------------------- 1 | ---------------------------------------------------------------------- 2 | -- Metalua: $Id: mll.lua,v 1.3 2006/11/15 09:07:50 fab13n Exp $ 3 | -- 4 | -- Summary: generic Lua-style lexer definition. You need this plus 5 | -- some keyword additions to create the complete Lua lexer, 6 | -- as is done in mlp_lexer.lua. 7 | -- 8 | -- TODO: 9 | -- 10 | -- * Make it easy to define new flavors of strings. Replacing the 11 | -- lexer.patterns.long_string regexp by an extensible list, with 12 | -- customizable token tag, would probably be enough. Maybe add: 13 | -- + an index of capture for the regexp, that would specify 14 | -- which capture holds the content of the string-like token 15 | -- + a token tag 16 | -- + or a string->string transformer function. 17 | -- 18 | -- * There are some _G.table to prevent a namespace clash which has 19 | -- now disappered. remove them. 20 | ---------------------------------------------------------------------- 21 | -- 22 | -- Copyright (c) 2006, Fabien Fleutot . 23 | -- 24 | -- This software is released under the MIT Licence, see licence.txt 25 | -- for details. 26 | -- 27 | ---------------------------------------------------------------------- 28 | 29 | module ("lexer", package.seeall) 30 | 31 | require 'metalua.runtime' 32 | 33 | 34 | lexer = { alpha={ }, sym={ } } 35 | lexer.__index=lexer 36 | 37 | local debugf = function() end 38 | --local debugf=printf 39 | 40 | ---------------------------------------------------------------------- 41 | -- Patterns used by [lexer:extract] to decompose the raw string into 42 | -- correctly tagged tokens. 43 | ---------------------------------------------------------------------- 44 | lexer.patterns = { 45 | spaces = "^[ \r\n\t]*()", 46 | short_comment = "^%-%-([^\n]*)()\n", 47 | final_short_comment = "^%-%-([^\n]*)()$", 48 | long_comment = "^%-%-%[(=*)%[\n?(.-)%]%1%]()", 49 | long_string = "^%[(=*)%[\n?(.-)%]%1%]()", 50 | number_mantissa = { "^%d+%.?%d*()", "^%d*%.%d+()" }, 51 | number_exponant = "^[eE][%+%-]?%d+()", 52 | number_hex = "^0[xX]%x+()", 53 | word = "^([%a_][%w_]*)()" 54 | } 55 | 56 | ---------------------------------------------------------------------- 57 | -- unescape a whole string, applying [unesc_digits] and 58 | -- [unesc_letter] as many times as required. 59 | ---------------------------------------------------------------------- 60 | local function unescape_string (s) 61 | 62 | -- Turn the digits of an escape sequence into the corresponding 63 | -- character, e.g. [unesc_digits("123") == string.char(123)]. 64 | local function unesc_digits (backslashes, digits) 65 | if #backslashes%2==0 then 66 | -- Even number of backslashes, they escape each other, not the digits. 67 | -- Return them so that unesc_letter() can treaat them 68 | return backslashes..digits 69 | else 70 | -- Remove the odd backslash, which escapes the number sequence. 71 | -- The rest will be returned and parsed by unesc_letter() 72 | backslashes = backslashes :sub (1,-2) 73 | end 74 | local k, j, i = digits:reverse():byte(1, 3) 75 | local z = _G.string.byte "0" 76 | local code = (k or z) + 10*(j or z) + 100*(i or z) - 111*z 77 | if code > 255 then 78 | error ("Illegal escape sequence '\\"..digits.."' in string: ASCII codes must be in [0..255]") 79 | end 80 | return backslashes .. string.char (code) 81 | end 82 | 83 | -- Take a letter [x], and returns the character represented by the 84 | -- sequence ['\\'..x], e.g. [unesc_letter "n" == "\n"]. 85 | local function unesc_letter(x) 86 | local t = { 87 | a = "\a", b = "\b", f = "\f", 88 | n = "\n", r = "\r", t = "\t", v = "\v", 89 | ["\\"] = "\\", ["'"] = "'", ['"'] = '"', ["\n"] = "\n" } 90 | return t[x] or error([[Unknown escape sequence '\]]..x..[[']]) 91 | end 92 | 93 | return s 94 | :gsub ("(\\+)([0-9][0-9]?[0-9]?)", unesc_digits) 95 | :gsub ("\\(%D)",unesc_letter) 96 | end 97 | 98 | lexer.extractors = { 99 | "skip_whitespaces_and_comments", 100 | "extract_short_string", "extract_word", "extract_number", 101 | "extract_long_string", "extract_symbol" } 102 | 103 | lexer.token_metatable = { 104 | -- __tostring = function(a) 105 | -- return string.format ("`%s{'%s'}",a.tag, a[1]) 106 | -- end 107 | } 108 | 109 | lexer.lineinfo_metatable = { } 110 | 111 | ---------------------------------------------------------------------- 112 | -- Really extract next token fron the raw string 113 | -- (and update the index). 114 | -- loc: offset of the position just after spaces and comments 115 | -- previous_i: offset in src before extraction began 116 | ---------------------------------------------------------------------- 117 | function lexer:extract () 118 | local previous_i = self.i 119 | local loc = self.i 120 | local eof, token 121 | 122 | -- Put line info, comments and metatable around the tag and content 123 | -- provided by extractors, thus returning a complete lexer token. 124 | -- first_line: line # at the beginning of token 125 | -- first_column_offset: char # of the last '\n' before beginning of token 126 | -- i: scans from beginning of prefix spaces/comments to end of token. 127 | local function build_token (tag, content) 128 | assert (tag and content) 129 | local i, first_line, first_column_offset, previous_line_length = 130 | previous_i, self.line, self.column_offset, nil 131 | 132 | -- update self.line and first_line. i := indexes of '\n' chars 133 | while true do 134 | i = self.src :find ("\n", i+1, true) 135 | if not i or i>self.i then break end -- no more '\n' until end of token 136 | previous_line_length = i - self.column_offset 137 | if loc and i <= loc then -- '\n' before beginning of token 138 | first_column_offset = i 139 | first_line = first_line+1 140 | end 141 | self.line = self.line+1 142 | self.column_offset = i 143 | end 144 | 145 | -- lineinfo entries: [1]=line, [2]=column, [3]=char, [4]=filename 146 | local fli = { first_line, loc-first_column_offset, loc, self.src_name } 147 | local lli = { self.line, self.i-self.column_offset-1, self.i-1, self.src_name } 148 | --Pluto barfes when the metatable is set:( 149 | setmetatable(fli, lexer.lineinfo_metatable) 150 | setmetatable(lli, lexer.lineinfo_metatable) 151 | local a = { tag = tag, lineinfo = { first=fli, last=lli }, content } 152 | if lli[2]==-1 then lli[1], lli[2] = lli[1]-1, previous_line_length-1 end 153 | if #self.attached_comments > 0 then 154 | a.lineinfo.comments = self.attached_comments 155 | fli.comments = self.attached_comments 156 | if self.lineinfo_last then 157 | self.lineinfo_last.comments = self.attached_comments 158 | end 159 | end 160 | self.attached_comments = { } 161 | return setmetatable (a, self.token_metatable) 162 | end -- 163 | 164 | for ext_idx, extractor in ipairs(self.extractors) do 165 | -- printf("method = %s", method) 166 | local tag, content = self [extractor] (self) 167 | -- [loc] is placed just after the leading whitespaces and comments; 168 | -- for this to work, the whitespace extractor *must be* at index 1. 169 | if ext_idx==1 then loc = self.i end 170 | 171 | if tag then 172 | --printf("`%s{ %q }\t%i", tag, content, loc); 173 | return build_token (tag, content) 174 | end 175 | end 176 | 177 | error "None of the lexer extractors returned anything!" 178 | end 179 | 180 | ---------------------------------------------------------------------- 181 | -- skip whites and comments 182 | -- FIXME: doesn't take into account: 183 | -- - unterminated long comments 184 | -- - short comments at last line without a final \n 185 | ---------------------------------------------------------------------- 186 | function lexer:skip_whitespaces_and_comments() 187 | local table_insert = _G.table.insert 188 | repeat -- loop as long as a space or comment chunk is found 189 | local _, j 190 | local again = false 191 | local last_comment_content = nil 192 | -- skip spaces 193 | self.i = self.src:match (self.patterns.spaces, self.i) 194 | -- skip a long comment if any 195 | _, last_comment_content, j = 196 | self.src :match (self.patterns.long_comment, self.i) 197 | if j then 198 | table_insert(self.attached_comments, 199 | {last_comment_content, self.i, j, "long"}) 200 | self.i=j; again=true 201 | end 202 | -- skip a short comment if any 203 | last_comment_content, j = self.src:match (self.patterns.short_comment, self.i) 204 | if j then 205 | table_insert(self.attached_comments, 206 | {last_comment_content, self.i, j, "short"}) 207 | self.i=j; again=true 208 | end 209 | if self.i>#self.src then return "Eof", "eof" end 210 | until not again 211 | 212 | if self.src:match (self.patterns.final_short_comment, self.i) then 213 | return "Eof", "eof" end 214 | --assert (not self.src:match(self.patterns.short_comment, self.i)) 215 | --assert (not self.src:match(self.patterns.long_comment, self.i)) 216 | -- --assert (not self.src:match(self.patterns.spaces, self.i)) 217 | return 218 | end 219 | 220 | ---------------------------------------------------------------------- 221 | -- extract a '...' or "..." short string 222 | ---------------------------------------------------------------------- 223 | function lexer:extract_short_string() 224 | -- [k] is the first unread char, [self.i] points to [k] in [self.src] 225 | local j, k = self.i, self.src :sub (self.i,self.i) 226 | if k~="'" and k~='"' then return end 227 | local i = self.i + 1 228 | local j = i 229 | while true do 230 | -- k = opening char: either simple-quote or double-quote 231 | -- i = index of beginning-of-string 232 | -- x = next "interesting" character 233 | -- j = position after interesting char 234 | -- y = char just after x 235 | local x, y 236 | x, j, y = self.src :match ("([\\\r\n"..k.."])()(.?)", j) 237 | if x == '\\' then j=j+1 -- don't parse escaped char 238 | elseif x == k then break -- unescaped end of string 239 | else -- eof or '\r' or '\n' reached before end of string 240 | assert (not x or x=="\r" or x=="\n") 241 | error "Unterminated string" 242 | end 243 | end 244 | self.i = j 245 | 246 | return "String", unescape_string (self.src:sub (i,j-2)) 247 | end 248 | 249 | ---------------------------------------------------------------------- 250 | -- 251 | ---------------------------------------------------------------------- 252 | function lexer:extract_word() 253 | -- Id / keyword 254 | local word, j = self.src:match (self.patterns.word, self.i) 255 | if word then 256 | self.i = j 257 | if self.alpha [word] then return "Keyword", word 258 | else return "Id", word end 259 | end 260 | end 261 | 262 | ---------------------------------------------------------------------- 263 | -- 264 | ---------------------------------------------------------------------- 265 | function lexer:extract_number() 266 | -- Number 267 | local j = self.src:match(self.patterns.number_hex, self.i) 268 | if not j then 269 | j = self.src:match (self.patterns.number_mantissa[1], self.i) or 270 | self.src:match (self.patterns.number_mantissa[2], self.i) 271 | if j then 272 | j = self.src:match (self.patterns.number_exponant, j) or j; 273 | end 274 | end 275 | if not j then return end 276 | -- Number found, interpret with tonumber() and return it 277 | local n = tonumber (self.src:sub (self.i, j-1)) 278 | self.i = j 279 | return "Number", n 280 | end 281 | 282 | ---------------------------------------------------------------------- 283 | -- 284 | ---------------------------------------------------------------------- 285 | function lexer:extract_long_string() 286 | -- Long string 287 | local _, content, j = self.src:match (self.patterns.long_string, self.i) 288 | if j then self.i = j; return "String", content end 289 | end 290 | 291 | ---------------------------------------------------------------------- 292 | -- 293 | ---------------------------------------------------------------------- 294 | function lexer:extract_symbol() 295 | -- compound symbol 296 | local k = self.src:sub (self.i,self.i) 297 | local symk = self.sym [k] 298 | if not symk then 299 | self.i = self.i + 1 300 | return "Keyword", k 301 | end 302 | for _, sym in pairs (symk) do 303 | if sym == self.src:sub (self.i, self.i + #sym - 1) then 304 | self.i = self.i + #sym; 305 | return "Keyword", sym 306 | end 307 | end 308 | -- single char symbol 309 | self.i = self.i+1 310 | return "Keyword", k 311 | end 312 | 313 | ---------------------------------------------------------------------- 314 | -- Add a keyword to the list of keywords recognized by the lexer. 315 | ---------------------------------------------------------------------- 316 | function lexer:add (w, ...) 317 | assert(not ..., "lexer:add() takes only one arg, although possibly a table") 318 | if type (w) == "table" then 319 | for _, x in ipairs (w) do self:add (x) end 320 | else 321 | if w:match (self.patterns.word .. "$") then self.alpha [w] = true 322 | elseif w:match "^%p%p+$" then 323 | local k = w:sub(1,1) 324 | local list = self.sym [k] 325 | if not list then list = { }; self.sym [k] = list end 326 | _G.table.insert (list, w) 327 | elseif w:match "^%p$" then return 328 | else error "Invalid keyword" end 329 | end 330 | end 331 | 332 | ---------------------------------------------------------------------- 333 | -- Return the [n]th next token, without consumming it. 334 | -- [n] defaults to 1. If it goes pass the end of the stream, an EOF 335 | -- token is returned. 336 | ---------------------------------------------------------------------- 337 | function lexer:peek (n) 338 | if not n then n=1 end 339 | if n > #self.peeked then 340 | for i = #self.peeked+1, n do 341 | self.peeked [i] = self:extract() 342 | end 343 | end 344 | return self.peeked [n] 345 | end 346 | 347 | ---------------------------------------------------------------------- 348 | -- Return the [n]th next token, removing it as well as the 0..n-1 349 | -- previous tokens. [n] defaults to 1. If it goes pass the end of the 350 | -- stream, an EOF token is returned. 351 | ---------------------------------------------------------------------- 352 | function lexer:next (n) 353 | n = n or 1 354 | self:peek (n) 355 | local a 356 | for i=1,n do 357 | a = _G.table.remove (self.peeked, 1) 358 | if a then 359 | --debugf ("lexer:next() ==> %s %s", 360 | -- table.tostring(a), tostring(a)) 361 | end 362 | self.lastline = a.lineinfo.last[1] 363 | end 364 | self.lineinfo_last = a.lineinfo.last 365 | return a or eof_token 366 | end 367 | 368 | ---------------------------------------------------------------------- 369 | -- Returns an object which saves the stream's current state. 370 | ---------------------------------------------------------------------- 371 | -- FIXME there are more fields than that to save 372 | function lexer:save () return { self.i; _G.table.cat(self.peeked) } end 373 | 374 | ---------------------------------------------------------------------- 375 | -- Restore the stream's state, as saved by method [save]. 376 | ---------------------------------------------------------------------- 377 | -- FIXME there are more fields than that to restore 378 | function lexer:restore (s) self.i=s[1]; self.peeked=s[2] end 379 | 380 | ---------------------------------------------------------------------- 381 | -- Resynchronize: cancel any token in self.peeked, by emptying the 382 | -- list and resetting the indexes 383 | ---------------------------------------------------------------------- 384 | function lexer:sync() 385 | local p1 = self.peeked[1] 386 | if p1 then 387 | li = p1.lineinfo.first 388 | self.line, self.i = li[1], li[3] 389 | self.column_offset = self.i - li[2] 390 | self.peeked = { } 391 | self.attached_comments = p1.lineinfo.first.comments or { } 392 | end 393 | end 394 | 395 | ---------------------------------------------------------------------- 396 | -- Take the source and offset of an old lexer. 397 | ---------------------------------------------------------------------- 398 | function lexer:takeover(old) 399 | self:sync() 400 | self.line, self.column_offset, self.i, self.src, self.attached_comments = 401 | old.line, old.column_offset, old.i, old.src, old.attached_comments 402 | return self 403 | end 404 | 405 | -- function lexer:lineinfo() 406 | -- if self.peeked[1] then return self.peeked[1].lineinfo.first 407 | -- else return { self.line, self.i-self.column_offset, self.i } end 408 | -- end 409 | 410 | 411 | ---------------------------------------------------------------------- 412 | -- Return the current position in the sources. This position is between 413 | -- two tokens, and can be within a space / comment area, and therefore 414 | -- have a non-null width. :lineinfo_left() returns the beginning of the 415 | -- separation area, :lineinfo_right() returns the end of that area. 416 | -- 417 | -- ____ last consummed token ____ first unconsummed token 418 | -- / / 419 | -- XXXXX YYYYY 420 | -- \____ \____ 421 | -- :lineinfo_left() :lineinfo_right() 422 | ---------------------------------------------------------------------- 423 | function lexer:lineinfo_right() 424 | return self:peek(1).lineinfo.first 425 | end 426 | 427 | function lexer:lineinfo_left() 428 | return self.lineinfo_last 429 | end 430 | 431 | ---------------------------------------------------------------------- 432 | -- Create a new lexstream. 433 | ---------------------------------------------------------------------- 434 | function lexer:newstream (src_or_stream, name) 435 | name = name or "?" 436 | if type(src_or_stream)=='table' then -- it's a stream 437 | return setmetatable ({ }, self) :takeover (src_or_stream) 438 | elseif type(src_or_stream)=='string' then -- it's a source string 439 | local src = src_or_stream 440 | local stream = { 441 | src_name = name; -- Name of the file 442 | src = src; -- The source, as a single string 443 | peeked = { }; -- Already peeked, but not discarded yet, tokens 444 | i = 1; -- Character offset in src 445 | line = 1; -- Current line number 446 | column_offset = 0; -- distance from beginning of file to last '\n' 447 | attached_comments = { },-- comments accumulator 448 | lineinfo_last = { 1, 1, 1, name } 449 | } 450 | setmetatable (stream, self) 451 | 452 | -- skip initial sharp-bang for unix scripts 453 | -- FIXME: redundant with mlp.chunk() 454 | if src and src :match "^#" then stream.i = src :find "\n" + 1 end 455 | return stream 456 | else 457 | assert(false, ":newstream() takes a source string or a stream, not a ".. 458 | type(src_or_stream)) 459 | end 460 | end 461 | 462 | ---------------------------------------------------------------------- 463 | -- if there's no ... args, return the token a (whose truth value is 464 | -- true) if it's a `Keyword{ }, or nil. If there are ... args, they 465 | -- have to be strings. if the token a is a keyword, and it's content 466 | -- is one of the ... args, then returns it (it's truth value is 467 | -- true). If no a keyword or not in ..., return nil. 468 | ---------------------------------------------------------------------- 469 | function lexer:is_keyword (a, ...) 470 | if not a or a.tag ~= "Keyword" then return false end 471 | local words = {...} 472 | if #words == 0 then return a[1] end 473 | for _, w in ipairs (words) do 474 | if w == a[1] then return w end 475 | end 476 | return false 477 | end 478 | 479 | ---------------------------------------------------------------------- 480 | -- Cause an error if the next token isn't a keyword whose content 481 | -- is listed among ... args (which have to be strings). 482 | ---------------------------------------------------------------------- 483 | function lexer:check (...) 484 | local words = {...} 485 | local a = self:next() 486 | local function err () 487 | error ("Got " .. tostring (a) .. 488 | ", expected one of these keywords : '" .. 489 | _G.table.concat (words,"', '") .. "'") end 490 | 491 | if not a or a.tag ~= "Keyword" then err () end 492 | if #words == 0 then return a[1] end 493 | for _, w in ipairs (words) do 494 | if w == a[1] then return w end 495 | end 496 | err () 497 | end 498 | 499 | ---------------------------------------------------------------------- 500 | -- 501 | ---------------------------------------------------------------------- 502 | function lexer:clone() 503 | local clone = { 504 | alpha = table.deep_copy(self.alpha), 505 | sym = table.deep_copy(self.sym) } 506 | setmetatable(clone, self) 507 | clone.__index = clone 508 | return clone 509 | end 510 | -------------------------------------------------------------------------------- /lib/lua2c/ast2cast.lua: -------------------------------------------------------------------------------- 1 | -- 2 | -- ast2cast.lua 3 | -- Converts Lua ASTs to C ASTs. 4 | -- 5 | -- Variable naming conventions: ast (Lua AST), cast (C AST). 6 | -- 7 | -- (c) 2008 David Manura. Licensed in the same terms as Lua (MIT license). 8 | -- See included LICENSE file for full licensing details. 9 | 10 | --luaanalyze checks 11 | --! local t_G = typeimport"luaanalyze.library.standard" 12 | --! t_G.add_field "mlc" 13 | --! t_G.add_field "mlp" 14 | --! local ast_t = require "luaanalyze.type.ast" 15 | --! ast_t.add_field "name" 16 | --! ast_t.add_field "upvalue" 17 | --! ast_t.add_field "idx" 18 | --! ast_t.add_field "type" 19 | --! typematch("[^c]ast$", ast_t) --FIX:match 20 | --! checkglobals() 21 | --! checktypes() 22 | 23 | local M = {} 24 | 25 | local _G = _G 26 | local assert = _G.assert 27 | local ipairs = _G.ipairs 28 | local math = _G.math 29 | local select = _G.select 30 | local setmetatable = _G.setmetatable 31 | local string = _G.string 32 | local tostring = _G.tostring 33 | local type = _G.type 34 | local table = _G.table 35 | local unpack = _G.unpack 36 | 37 | 38 | -- converts Metalua AST built from string 39 | -- to C AST . 40 | -- returns(cast) 41 | local function ast_to_cast(src, ast) 42 | --##------------------------------------------------------------------ 43 | --## Note: this is a large function nesting many closures; 44 | --## indentation of its contents is omitted. 45 | --##------------------------------------------------------------------ 46 | 47 | 48 | -- info on current scope. includes 49 | -- table of local variables. 50 | -- Maps name -> stack index 51 | local _currentscope = {['.closurelevel'] = 0} 52 | 53 | -- topmost stack index 54 | local _idxtop = 0 55 | 56 | local _names = {} 57 | 58 | local _varid = 0 59 | 60 | -- note: _is_created maps function name to boolean indicated whether 61 | -- function has been generated. 62 | local _is_created = {} 63 | 64 | 65 | -- Information on function currently being compiled. 66 | local _funcinfo = {is_vararg=false, nformalparams=0, 67 | is_lc_nextra_used=false, is_lc_nactualargs_used=false, 68 | is_lc_nextra_used_debug=false, is_lc_nactualargs_used_debug=false, 69 | idxtopmax = 0 70 | --old: outervars={} 71 | } 72 | 73 | 74 | -- LUA_MINSTACK value in luaconf.h 75 | local LUA_MINSTACK = 20 76 | 77 | 78 | -- Mutators for _idxtop. 79 | local function idxtop_change(n) 80 | _idxtop = _idxtop + n 81 | _funcinfo.idxtopmax = math.max(_funcinfo.idxtopmax, _idxtop) 82 | end 83 | local function idxtop_restore(idx) 84 | assert(idx <= _idxtop) 85 | _idxtop = idx 86 | end 87 | 88 | -- Builds set from elements in array t. 89 | local function makeset(t) 90 | local set = {} 91 | for _,v in ipairs(t) do set[v] = true end 92 | return set 93 | end 94 | 95 | 96 | -- Set of identifiers of binary operators 97 | -- (metamethod name without "__" prefix). 98 | local is_binopid = makeset { 99 | "add", "sub", "mul", "div", 100 | "mod", "pow", "concat", "eq", 101 | "lt", "le", "and", "or" 102 | } 103 | 104 | -- Set of identifiers of unary operators. 105 | local is_unopid = makeset { 106 | "not", "len", "unm" 107 | } 108 | 109 | -- Set of binary ops with metamethod behavior like "+". 110 | local is_arithbinop = makeset { 111 | "add", "sub", "mul", "div", "mod", "pow" 112 | } 113 | 114 | -- Maps operator identifier to function 115 | -- implementing that identifier (without metamethods). 116 | local fops = { 117 | ["add"] =function(a,b) return a+b end, 118 | ["sub"] =function(a,b) return a-b end, 119 | ["mul"] =function(a,b) return a*b end, 120 | ["div"] =function(a,b) return a/b end, 121 | ["mod"] =function(a,b) return a%b end, 122 | ["pow"] =function(a,b) return a^b end, 123 | ["concat"]=function(a,b) return a..b end, 124 | ["eq"] =function(a,b) return a==b end, 125 | ["lt"] =function(a,b) return a 0 then 209 | prepend_comment(cast2[1], cast2.comment) 210 | elseif #cast1 > 0 then 211 | append_comment_below(cast1[#cast1], cast2.comment) 212 | else 213 | assert(false) 214 | end 215 | end 216 | if cast2.aftercomment then 217 | if #cast2 > 0 then 218 | append_comment_below(cast2[#cast2], cast2.aftercomment) 219 | elseif #cast1 > 0 then 220 | append_comment_below(cast1[#cast1], cast2.aftercomment) 221 | else 222 | assert(false) 223 | end 224 | end 225 | append_array(cast1.pre, cast2.pre) 226 | return cast2 --FIX:improve style? 227 | end 228 | 229 | 230 | -- Constructor and type for C AST nodes. 231 | local cexpr_mt = {} 232 | cexpr_mt.__index = cexpr_mt 233 | function cexpr_mt:append(cast) 234 | return append_cast(self, cast) 235 | end 236 | local function cexpr(...) 237 | return setmetatable({idx=-1, pre={}, ...}, cexpr_mt) 238 | end 239 | 240 | local C_mt = {} 241 | function C_mt.__index(t,k,v) 242 | local f = function(...) return {tag=k, ...} end 243 | t.k = f -- cache 244 | return f 245 | end 246 | local C = setmetatable({}, C_mt) 247 | 248 | local function tag(o) 249 | return type(o) == 'table' and o.tag or nil 250 | end 251 | 252 | -- returns C AST of double for given C AST idx. 253 | local function todouble(idx) 254 | if type(idx) == 'string' then 255 | return C.Id(idx) 256 | else 257 | return C.lua_tonumber(idx) 258 | end 259 | end 260 | 261 | -- returns C AST of bool for given C AST idx. 262 | local function tobool(idx) 263 | if type(idx) == 'string' then 264 | return C.Id(idx) 265 | else 266 | return C.lua_toboolean(idx) 267 | end 268 | end 269 | 270 | 271 | -- Adjusts absolute index of local variable, 272 | -- given that in vararg (...) functions, base index depends on 273 | -- arguments passed in. Can return C AST. 274 | local function realidx(idx) 275 | -- pseudoindex 276 | if type(idx) == 'table' and idx.tag == 'C' and 277 | idx[1] == 'lua_upvalueindex(1)' 278 | then 279 | return idx 280 | end 281 | local idxreal = tag(idx) == 'Upval' and idx[1] or idx 282 | if _funcinfo.is_vararg and 283 | (tag(idxreal) == 'Id' or idxreal > _funcinfo.nformalparams) 284 | then 285 | if tag(idx) == 'Upval' then 286 | -- no adjustment 287 | else 288 | idx = C.Op('+', idx, C.Id'lc_nextra') 289 | _funcinfo.is_lc_nextra_used = true 290 | end 291 | end 292 | return idx 293 | end 294 | 295 | -- Gets real index of local var. 296 | local function localidx(name) 297 | local idx = _currentscope[name] 298 | if not idx then return end 299 | 300 | return realidx(idx) 301 | end 302 | 303 | 304 | --old: local function isupvalue(name) 305 | -- return not _currentscope[name] and _funcinfo.outervars[name] 306 | --end 307 | 308 | -- Adjusts stack index to relative (negative) position offset. note: 309 | -- use only when values inside offset are on the stack 310 | -- (non-psuedoindices). 311 | -- Note: allows C AST. 312 | local function adjustidx(offset, idx) 313 | if type(idx) ~= 'number' or idx > 0 then 314 | return idx 315 | else -- relative stack index or pseudoindex 316 | -- FIX:pseudoindices not supported? 317 | return idx + offset + 1 318 | end 319 | end 320 | 321 | -- Adjusts relative stack indicies 322 | -- Note: allows C ASTs. 323 | local function adjustidxs(...) 324 | local nused = 0 325 | local result = {...} 326 | for i=#result,1,-1 do 327 | local idx = result[i] 328 | if type(idx) == 'number' and idx < 0 then 329 | --FIX: and idx > LUA_REGISTRYINDEX ? 330 | result[i] = result[i] - nused -- adjust 331 | end 332 | if idx == -1 then 333 | nused = nused + 1 334 | end 335 | end 336 | return unpack(result) 337 | end 338 | 339 | -- Counts number of temporary values on stack. 340 | local function countstack(...) 341 | local n = select('#', ...) 342 | local ncount = 0 343 | for i=1,n do 344 | local idx = select(i, ...) 345 | if idx == -1 then ncount = ncount + 1 end 346 | end 347 | return ncount 348 | end 349 | 350 | -- Given AST ast taken from inside code string src, 351 | -- return code string representing AST. 352 | local function get_ast_string(src, ast, ...) 353 | local first = ast.lineinfo.first[3] 354 | local last = ast.lineinfo.last [3] 355 | local code = src:sub(first, last) 356 | return code 357 | end 358 | 359 | -- Gets next unique ID. Useful for generating unique variable names. 360 | -- orig_name is string of arbitrary text to base variable name 361 | -- on (optional and may be ignored) 362 | -- prefix is prefix string for variable (defaults to '') 363 | local MAX_IDENTIFIER_LENGTH = 60 -- note: not a hard limit 364 | local function nextid(orig_name, prefix) 365 | orig_name = orig_name or '' 366 | prefix = prefix or '' 367 | 368 | -- ensure will form a valid C identifier 369 | local name = orig_name:gsub('[^%w_]', '_') 370 | 371 | -- ensure uniqueness 372 | _names[name] = (_names[name] or 0) + 1 373 | 374 | local id = 375 | 'lc' .. prefix .. _names[name] .. (name == '' and '' or '_') .. name 376 | return id 377 | end 378 | 379 | -- Gets next unique ID for lexical variable. 380 | local function nextvarid() 381 | _varid = _varid + 1 382 | return _varid 383 | end 384 | 385 | local function newscope() 386 | local currentscope_old = _currentscope 387 | _currentscope = setmetatable({}, {__index = currentscope_old}) 388 | return currentscope_old 389 | end 390 | 391 | local function restore_scope(currentscope_save) 392 | _currentscope = currentscope_save 393 | end 394 | 395 | local function restore_stack_rel(cast, idx_old) 396 | local npushed = _idxtop - idx_old 397 | if npushed ~= 0 then 398 | cast:append(C.lua_pop(npushed)) 399 | append_comment(cast[#cast], 'internal: stack cleanup on scope exit') 400 | end 401 | idxtop_change(- npushed) 402 | end 403 | 404 | local function restore_stack_abs(cast, idx_old_cast, idx_old) 405 | cast:append(C.lua_settop(realidx(idx_old_cast))) 406 | idxtop_restore(idx_old) 407 | end 408 | 409 | 410 | -- Returns whether expression could possibly return a number of argument 411 | -- different from 1. 412 | -- note: expr_ast can be nil 413 | local function can_multi(expr_ast) 414 | return expr_ast and (expr_ast.tag == 'Call' or expr_ast.tag == 'Invoke' 415 | or expr_ast.tag == 'Dots') 416 | end 417 | 418 | 419 | -- Performs constant folding on AST. 420 | local function constant_fold(ast) 421 | if ast.tag == 'Op' then 422 | local opid = ast[1] 423 | local a_ast = constant_fold(ast[2]) 424 | local b_ast = ast[3] and constant_fold(ast[3]) 425 | if b_ast then -- binary op 426 | if a_ast.tag == 'Number' and b_ast.tag == 'Number' then 427 | return obj_to_ast(fops[opid](a_ast[1], b_ast[1])) 428 | end 429 | else -- unary op 430 | if a_ast.tag == 'Number' then 431 | return obj_to_ast(fops[opid](a_ast[1])) 432 | end 433 | end 434 | end 435 | return ast 436 | end 437 | 438 | 439 | local function get_closuretableidx_cast() 440 | return localidx('.closuretable') 441 | end 442 | 443 | -- Deduce names of functions by the variables they are assigned to. 444 | -- Given lists of LHS and RHS expressions, adds a "name" field 445 | -- to any applicable RHS expression. 446 | local function deduce_function_names(ls_ast, rs_ast) 447 | for i,r_ast in ipairs(rs_ast) do 448 | local l_ast = ls_ast[i] 449 | if ls_ast[i] and r_ast.tag == 'Function' then 450 | local name = get_ast_string(src, l_ast) 451 | r_ast.name = name 452 | end 453 | end 454 | end 455 | 456 | 457 | -- forward declarations 458 | local genexpr 459 | local genblock 460 | 461 | -- Converts any Lua arithmetic op AST to C AST. 462 | local function genarithbinop(ast, where) 463 | -- Returns C AST node definition arithmetic binary op with given 464 | -- identitifier. 465 | local function makebinop(opid) 466 | local ccode = string.format([[ 467 | /* __%s metamethod handler. 468 | * warning: assumes indices in range LUA_REGISTRYINDEX < x < 0 are relative. */ 469 | static void lc_%s(lua_State * L, int idxa, int idxb) { 470 | if (lua_isnumber(L,idxa) && lua_isnumber(L,idxb)) { 471 | lua_pushnumber(L,%s); 472 | } 473 | else { 474 | if (luaL_getmetafield(L,idxa,"__%s")||luaL_getmetafield(L,idxb,"__%s")) { 475 | lua_pushvalue(L,idxa < 0 && idxa > LUA_REGISTRYINDEX ? idxa-1 : idxa); 476 | lua_pushvalue(L,idxb < 0 && idxb > LUA_REGISTRYINDEX ? idxb-2 : idxb); 477 | lua_call(L,2,1); 478 | } 479 | else { 480 | luaL_error(L, "attempt to perform arithmetic"); 481 | } 482 | } 483 | } 484 | ]], opid, opid, 485 | assert(opid_to_c[opid])("lua_tonumber(L,idxa)", "lua_tonumber(L,idxb)"), 486 | opid, opid) 487 | 488 | return C.C(ccode) 489 | end 490 | 491 | local opid, a_ast, b_ast = ast[1], ast[2], ast[3] 492 | 493 | local cast = cexpr() 494 | 495 | if opid == 'pow' or opid == 'mod' then 496 | if not _is_created['math.h'] then 497 | append_array(cast.pre, {C.Include''}) 498 | _is_created['math.h'] = true 499 | end 500 | end 501 | 502 | local fname = "lc_" .. opid 503 | if not _is_created[fname] then 504 | append_array(cast.pre, {makebinop(opid)}) 505 | _is_created[fname] = true 506 | end 507 | 508 | local a_idx = cast:append(genexpr(a_ast, 'anywhere')).idx 509 | local b_idx = cast:append(genexpr(b_ast, 'anywhere')).idx 510 | local nstack = countstack(a_idx, b_idx) 511 | a_idx, b_idx = adjustidxs(a_idx, b_idx) 512 | cast:append(C.CallL('lc_'..opid, a_idx, b_idx)) 513 | for i=1,nstack do 514 | cast:append(C.lua_remove(-2)) 515 | end 516 | 517 | return cast 518 | end 519 | 520 | -- Converts Lua equality op AST to C AST. 521 | local function geneqop(ast, where) 522 | local a_ast, b_ast = ast[2], ast[3] 523 | local cast = cexpr() 524 | local a_idx = cast:append(genexpr(a_ast, where)).idx 525 | local b_idx = cast:append(genexpr(b_ast, where)).idx 526 | local nstack = (a_idx == -1 and 1 or 0) + (b_idx == -1 and 1 or 0) 527 | a_idx, b_idx = adjustidxs(a_idx, b_idx) 528 | local id = nextid(); cast:append(C.Let(id, C.lua_equal(a_idx, b_idx))) 529 | if nstack ~= 0 then 530 | cast:append(C.lua_pop(nstack)) 531 | end 532 | cast:append(C.lua_pushboolean(C.Id(id))) 533 | return cast 534 | end 535 | 536 | -- Converts Lua less-than op AST to C AST. 537 | local function genltop(ast, where) 538 | local opid, a_ast, b_ast = ast[1], ast[2], ast[3] 539 | local cast = cexpr() 540 | 541 | local a_idx = cast:append(genexpr(a_ast, 'anywhere')).idx 542 | local b_idx = cast:append(genexpr(b_ast, 'anywhere')).idx 543 | local nstack = countstack(a_idx, b_idx) 544 | a_idx, b_idx = adjustidxs(a_idx, b_idx) 545 | 546 | local id = nextid(); cast:append(C.Let(id, C.lua_lessthan(a_idx, b_idx))) 547 | 548 | if nstack ~= 0 then 549 | cast:append(C.lua_pop(nstack)) 550 | end 551 | 552 | cast:append(C.lua_pushboolean(C.Id(id))) 553 | 554 | return cast 555 | end 556 | 557 | 558 | -- Converts Lua less-than-or-equal op AST to C AST. 559 | -- Why does the Lua C API implement lua_lessthan but not lua_lessequal? 560 | -- (note: lessqual is defined in lvm.c). 561 | local function genleop(ast, where) 562 | local function makeop() 563 | local cast = C.C [[ 564 | /* warning: assumes indices in range LUA_REGISTRYINDEX < x < 0 are relative. */ 565 | static int lc_le(lua_State * L, int idxa, int idxb) { 566 | if (lua_type(L,idxa) == LUA_TNUMBER && lua_type(L,idxb) == LUA_TNUMBER) { 567 | return lua_tonumber(L,idxa) <= lua_tonumber(L,idxb); 568 | } 569 | else if (lua_type(L,idxa) == LUA_TSTRING && lua_type(L,idxb) == LUA_TSTRING) { 570 | /* result similar to lvm.c l_strcmp */ 571 | return lua_lessthan(L,idxa,idxb) || lua_rawequal(L,idxa,idxb); 572 | } 573 | else if (luaL_getmetafield(L,idxa,"__le")||luaL_getmetafield(L,idxb,"__le")) { 574 | lua_pushvalue(L,idxa < 0 && idxa > LUA_REGISTRYINDEX ? idxa-1 : idxa); 575 | lua_pushvalue(L,idxb < 0 && idxb > LUA_REGISTRYINDEX ? idxb-2 : idxb); 576 | lua_call(L,2,1); 577 | const int result = lua_toboolean(L,-1); 578 | lua_pop(L,1); 579 | return result; 580 | } 581 | else if (luaL_getmetafield(L,idxa,"__lt")||luaL_getmetafield(L,idxb,"__lt")) { 582 | lua_pushvalue(L,idxb < 0 && idxb > LUA_REGISTRYINDEX ? idxb-1 : idxb); 583 | lua_pushvalue(L,idxa < 0 && idxa > LUA_REGISTRYINDEX ? idxa-2 : idxa); 584 | lua_call(L,2,1); 585 | const int result = ! lua_toboolean(L,-1); 586 | lua_pop(L,1); 587 | return result; 588 | } 589 | else { 590 | return luaL_error(L, "attempt to compare"); 591 | } 592 | } 593 | ]] 594 | 595 | return cast 596 | end 597 | 598 | local opid, a_ast, b_ast = ast[1], ast[2], ast[3] 599 | local cast = cexpr() 600 | 601 | local fname = "lc_le" 602 | if not _is_created[fname] then 603 | append_array(cast.pre, {makeop(opid)}) 604 | _is_created[fname] = true 605 | end 606 | 607 | local a_idx = cast:append(genexpr(a_ast, 'anywhere')).idx 608 | local b_idx = cast:append(genexpr(b_ast, 'anywhere')).idx 609 | local nstack = countstack(a_idx, b_idx) 610 | a_idx, b_idx = adjustidxs(a_idx, b_idx) 611 | if nstack == 0 then 612 | cast:append(C.lua_pushboolean(C.CallL('lc_le', a_idx, b_idx))) 613 | else 614 | local id = nextid() 615 | cast:append(C.Let(id, C.CallL('lc_le', a_idx, b_idx))) 616 | cast:append(C.lua_pop(nstack)) 617 | cast:append(C.lua_pushboolean(C.Id(id))) 618 | end 619 | 620 | return cast 621 | end 622 | 623 | -- Converts Lua binary logical op AST to C AST. 624 | local function genlogbinop(ast, where) 625 | local opid, a_ast, b_ast = ast[1], ast[2], ast[3] 626 | local cast = cexpr() 627 | 628 | cast:append(genexpr(a_ast, 'onstack')) 629 | 630 | assert(opid == 'and' or opid == 'or') 631 | local expr_cast = C.lua_toboolean(-1) 632 | if opid == 'or' then expr_cast = C.Not(expr_cast) end 633 | local block_cast = cexpr() 634 | block_cast:append(C.lua_pop(1)) 635 | block_cast:append(genexpr(b_ast, 'onstack')) 636 | cast:append(C.If(expr_cast, block_cast)) 637 | append_array(cast.pre, block_cast.pre) 638 | 639 | return cast 640 | end 641 | 642 | -- Converts Lua unary logical (i.e. not) op AST to C AST. 643 | local function gennotop(ast, where) 644 | local opid, a_ast = ast[1], ast[2] 645 | local cast = cexpr() 646 | 647 | local a_idx = cast:append(genexpr(a_ast, 'anywhere')).idx 648 | 649 | cast:append(C.lua_pushboolean(C.Not(tobool(a_idx)))) 650 | if a_idx == -1 then 651 | cast:append(C.lua_remove(-2)) 652 | end 653 | 654 | return cast 655 | end 656 | 657 | -- Converts Lua length op AST to C AST. 658 | local function genlenop(ast, where) 659 | local opid, a_ast = ast[1], ast[2] 660 | 661 | local cast = cexpr() 662 | local a_idx = cast:append(genexpr(a_ast, 'anywhere')).idx 663 | 664 | --FIX:call metatable __len for userdata? 665 | local id = nextid() 666 | cast:append(C.LetDouble(id, C.lua_objlen(a_idx))) 667 | if a_idx == -1 then 668 | cast:append(C.lua_pop(1)) 669 | end 670 | cast:append(C.lua_pushnumber(C.Id(id))) 671 | return cast 672 | end 673 | 674 | 675 | -- Converts Lua unary minus op AST to C AST. 676 | local function genunmop(ast, where) 677 | -- Returns C AST node definition of op. 678 | local function makeop() 679 | local ccode = [[ 680 | /* __unm metamethod handler. 681 | * warning: assumes indices in range LUA_REGISTRYINDEX < x < 0 are relative. */ 682 | static void lc_unm(lua_State * L, int idxa) { 683 | if (lua_isnumber(L,idxa)) { 684 | lua_pushnumber(L,- lua_tonumber(L, idxa)); 685 | } 686 | else { 687 | if (luaL_getmetafield(L,idxa,"__unm")) { 688 | lua_pushvalue(L,idxa < 0 && idxa > LUA_REGISTRYINDEX ? idxa-1 : idxa); 689 | lua_call(L,1,1); 690 | } 691 | else { 692 | luaL_error(L, "attempt to perform arithmetic"); 693 | } 694 | } 695 | } 696 | ]] 697 | return C.C(ccode) 698 | end 699 | 700 | local opid, a_ast = ast[1], ast[2] 701 | assert(opid == 'unm') 702 | 703 | local cast = cexpr() 704 | 705 | local fname = "lc_" .. opid 706 | if not _is_created[fname] then 707 | append_array(cast.pre, {makeop()}) 708 | _is_created[fname] = true 709 | end 710 | 711 | local a_idx = cast:append(genexpr(a_ast, 'anywhere')).idx 712 | local nstack = countstack(a_idx) 713 | a_idx = adjustidxs(a_idx) 714 | cast:append(C.CallL('lc_'..opid, a_idx)) 715 | for i=1,nstack do 716 | cast:append(C.lua_remove(-2)) 717 | end 718 | 719 | return cast 720 | end 721 | 722 | -- Converts Lua concatenation op AST to C AST. 723 | local function genconcatop(ast, where) 724 | local a_ast, b_ast = ast[2], ast[3] 725 | local cast = cexpr() 726 | 727 | cast:append(genexpr(a_ast, 'onstack')) 728 | cast:append(genexpr(b_ast, 'onstack')) 729 | cast:append(C.lua_concat(2)) 730 | 731 | return cast 732 | end 733 | 734 | -- Creates C AST for table used to manage upvalues 735 | -- for a closure. 736 | -- note: call activate_closure_table() afterward. 737 | local function gennewclosuretable() 738 | local cast = cexpr() 739 | 740 | if not _is_created['lc_newclosuretable'] then 741 | -- note: index idx cannot be relative. 742 | -- note: key 0 points to parent table. 743 | local body_cast = cexpr() 744 | --IMPROVE: C.C usage 745 | body_cast:append(C.C[[ 746 | /* pushes new closure table onto the stack, using closure table at 747 | * given index as its parent */ 748 | static void lc_newclosuretable(lua_State * L, int idx) {]]) 749 | 750 | body_cast:append( cexpr( cexpr( 751 | C.lua_newtable(), 752 | C.lua_pushvalue(C.Id'idx'), 753 | C.lua_rawseti(-2,0) 754 | ))) 755 | body_cast:append(C.C'}') 756 | 757 | append_array(cast.pre, body_cast) 758 | 759 | _is_created['lc_newclosuretable'] = true 760 | end 761 | 762 | cast:append(C.CallL('lc_newclosuretable', get_closuretableidx_cast())) 763 | idxtop_change(1) 764 | 765 | local id = nextid() 766 | cast:append(C.Enum(id, _idxtop)) 767 | if not _is_created['assert.h'] then 768 | append_array(cast.pre, {C.Include''}) 769 | _is_created['assert.h'] = true 770 | end 771 | cast:append(C.Call('assert', C.Op('==', C.lua_gettop(), realidx(C.Id(id))))) 772 | 773 | cast.idx = C.Id(id) 774 | 775 | return cast 776 | end 777 | 778 | local function activate_closure_table(idx) 779 | _currentscope['.closuretable'] = idx 780 | _currentscope['.closurelevel'] = _currentscope['.closurelevel'] + 1 781 | end 782 | 783 | -- Convert Lua `Function AST to C AST. 784 | -- C AST defines the function's implementation. 785 | -- helper function to generate function definition 786 | -- is_main - whether this is the top-level "main" function. 787 | local function genfunctiondef(ast, ismain) 788 | local params_ast, body_ast = ast[1], ast[2] 789 | 790 | -- localize new function info. 791 | local funcinfo_old = _funcinfo 792 | local has_vararg = params_ast[#params_ast] 793 | and params_ast[#params_ast].tag == 'Dots' 794 | local nformalargs = #params_ast - (has_vararg and 1 or 0) 795 | _funcinfo = {is_vararg = has_vararg, nformalparams = nformalargs, 796 | is_lc_nextra_used = false, is_lc_nactualargs_used=false, 797 | is_lc_nextra_used_debug = false, is_lc_nactualargs_used_debug=false, 798 | idxtopmax = 0 799 | --old: outervars = _currentscope 800 | } 801 | 802 | -- note: special usage of _idxtop 803 | local idxtop_old = _idxtop 804 | _idxtop = 0 805 | 806 | -- localize code 807 | local currentscope_save = newscope() 808 | _currentscope['.closuretable'] = C.C'lua_upvalueindex(1)' 809 | 810 | -- define parameters as local variables. 811 | for i,var_ast in ipairs(params_ast) do 812 | if var_ast.tag ~= 'Dots' then 813 | assert(var_ast.tag == 'Id') 814 | local varname = var_ast[1] 815 | _currentscope[varname] = i 816 | idxtop_change(1) 817 | end 818 | end 819 | 820 | local body_cast = cexpr() 821 | 822 | local is_upvalue = false 823 | for i,params_ast in ipairs(params_ast) do 824 | is_upvalue = is_upvalue or params_ast.upvalue 825 | end 826 | if is_upvalue then 827 | local ct_idx = body_cast:append(gennewclosuretable()).idx 828 | activate_closure_table(ct_idx) 829 | 830 | for i=1,nformalargs do 831 | local param_ast = params_ast[i] 832 | 833 | if param_ast.upvalue then 834 | local name = param_ast[1] 835 | local varid = nextvarid() 836 | 837 | -- create local symbol 838 | _currentscope[name] = 839 | {tag='Upval', _idxtop, _currentscope['.closurelevel'], varid} 840 | 841 | body_cast:append(C.lua_pushvalue(i)) 842 | body_cast:append(C.lua_rawseti(-2, varid)) 843 | end 844 | end 845 | end 846 | 847 | -- generate body 848 | body_cast:append(genblock(body_ast)) 849 | 850 | local bodypre_cast = cexpr() 851 | 852 | -- Ensure stack space. 853 | local fudge = 10 -- allow some extra space, avoiding detailed 854 | -- accounting. FIX: is this always sufficient? 855 | if _funcinfo.idxtopmax + fudge >= LUA_MINSTACK then 856 | bodypre_cast:append(C.lua_checkstack(_funcinfo.idxtopmax + fudge)) 857 | end 858 | 859 | -- note: do after generating body do that _funcinfo params are set 860 | -- note: safety in case caller passes more or less arguments 861 | -- than parameters. IMPROVE-some of this is sometimes not necessary. 862 | bodypre_cast:append(C.Enum('lc_nformalargs', nformalargs)) 863 | if #params_ast > 0 and params_ast[#params_ast].tag == 'Dots' or 864 | ismain -- dots implicit in main chunk 865 | then 866 | if nformalargs > 0 then 867 | bodypre_cast:append( 868 | C.If(C.Op('<', C.lua_gettop(), C.Id'lc_nformalargs'), 869 | { C.lua_settop(C.Id'lc_nformalargs') } )) 870 | end 871 | -- note: measure nactualargs after adjustment above 872 | -- (important for genexpr of `Id) 873 | if _funcinfo.is_lc_nextra_used then 874 | _funcinfo.is_lc_nactualargs_used = true 875 | elseif _funcinfo.is_lc_nextra_used_debug then 876 | _funcinfo.is_lc_nactualargs_used_debug = true 877 | end 878 | 879 | if _funcinfo.is_lc_nactualargs_used then 880 | bodypre_cast:append(C.LetInt('lc_nactualargs', C.lua_gettop())) 881 | elseif _funcinfo.is_lc_nactualargs_used_debug then 882 | bodypre_cast:append(C.C'#ifndef NDEBUG') 883 | bodypre_cast:append(C.LetInt('lc_nactualargs', C.lua_gettop())) 884 | bodypre_cast:append(C.C'#endif') 885 | end 886 | if _funcinfo.is_lc_nextra_used then 887 | bodypre_cast:append(C.LetInt('lc_nextra', 888 | C.Op('-', C.Id'lc_nactualargs', C.Id'lc_nformalargs'))) 889 | elseif _funcinfo.is_lc_nextra_used_debug then 890 | bodypre_cast:append(C.C'#ifndef NDEBUG') 891 | bodypre_cast:append(C.LetInt('lc_nextra', 892 | C.Op('-', C.Id'lc_nactualargs', C.Id'lc_nformalargs'))) 893 | bodypre_cast:append(C.C'#endif') 894 | end 895 | else 896 | bodypre_cast:append(C.lua_settop(#params_ast)) 897 | end 898 | 899 | -- prepend bodypre_cast to body_cast (IMPROVE: add prepend method?) 900 | local body_old_cast = body_cast 901 | local body_cast = cexpr() 902 | body_cast:append(bodypre_cast) 903 | body_cast:append(body_old_cast) 904 | 905 | if #body_ast == 0 or body_ast[#body_ast].tag ~= 'Return' then 906 | body_cast:append(C.Return(0)) 907 | end 908 | 909 | local id = ast.is_main and 'lcf_main' 910 | or nextid(ast.name, 'f') 911 | local def_cast = cexpr() 912 | append_array(def_cast, body_cast.pre) 913 | local func_cast = C.Functiondef(id, body_cast) 914 | 915 | local comment = 916 | ast.is_main and '(...)' or 917 | trim(src:sub(ast.lineinfo.first[3], params_ast.lineinfo.last[3])) .. ')' 918 | if comment:sub(1,1) == '(' then 919 | comment = 'function' .. comment 920 | end 921 | if ast.name then 922 | comment = 'name: ' .. ast.name .. '\n' .. comment 923 | end 924 | func_cast.comment = comment 925 | 926 | func_cast.id = id 927 | append_array(def_cast, { func_cast }) 928 | 929 | restore_scope(currentscope_save) 930 | _idxtop = idxtop_old 931 | 932 | _funcinfo = funcinfo_old 933 | 934 | return def_cast 935 | end 936 | 937 | -- Converts Lua `Function AST to C AST. 938 | -- The C AST instantiates the function/closure (and includes code to define 939 | -- the function implementation also). 940 | local function genfunction(ast, where) 941 | local cast = cexpr() 942 | 943 | --## generate function definition 944 | local def_cast = genfunctiondef(ast) 945 | append_array(cast.pre, def_cast) 946 | 947 | --## generate function object 948 | if ast.uses_upvalue then 949 | cast:append(C.lua_pushvalue(get_closuretableidx_cast())) 950 | cast:append(C.lua_pushcclosure(C.Id(def_cast[#def_cast].id), 1)) 951 | else 952 | cast:append(C.lua_pushcfunction(C.Id(def_cast[#def_cast].id))) 953 | end 954 | 955 | return cast 956 | end 957 | 958 | -- Convert Lua top-level block AST (for top-level main function) 959 | -- to C AST. 960 | local function genmainchunk(ast) 961 | local astnew = {tag='Function', is_main=true, name='(main)', {{tag='Dots'}}, 962 | ast, lineinfo=ast.lineinfo} -- note: lineinfo not cloned. ok? 963 | 964 | return genfunctiondef(astnew, true) 965 | end 966 | 967 | 968 | -- Converts Lua `Table AST to C AST. 969 | local function gentable(ast, where) 970 | local cast = cexpr() 971 | local narr = 0 972 | local nrec = 0 973 | for _,e_ast in ipairs(ast) do 974 | if e_ast.tag == 'Pair' then -- Q: always true? 975 | nrec = nrec + 1 976 | else 977 | narr = narr + 1 978 | end 979 | end 980 | if narr + nrec == 0 then 981 | cast:append(C.lua_newtable()) 982 | else 983 | cast:append(C.lua_createtable(narr,nrec)) 984 | end 985 | 986 | local pos = 0 987 | for i,e_ast in ipairs(ast) do 988 | if e_ast.tag == 'Pair' then 989 | local k_ast, v_ast = e_ast[1], e_ast[2] 990 | cast:append(genexpr(k_ast, 'onstack')) 991 | cast:append(genexpr(v_ast, 'onstack')) 992 | cast:append(C.lua_rawset(-3)) 993 | else 994 | local can_multret = i == #ast and can_multi(e_ast) 995 | 996 | if can_multret then 997 | -- table marker 998 | local id = nextid() 999 | cast:append(C.Let(id, C.lua_gettop())) 1000 | 1001 | -- get list of values 1002 | cast:append(genexpr(e_ast, 'onstack', 'multret')) 1003 | 1004 | -- push list of values in table. 1005 | -- note: Lua spec does not prohibit right-to-left insertion. 1006 | -- IMPROVE? right-to-left insertion can cause needless placement 1007 | -- of values into the hash part of the table. 1008 | cast:append( 1009 | C.While(C.Op('>', C.lua_gettop(), C.Id(id)), { 1010 | C.lua_rawseti(C.Id(id), 1011 | C.Op('+', pos, C.Op('-', C.lua_gettop(), C.Id(id)))) } )) 1012 | else 1013 | cast:append(genexpr(e_ast, 'onstack')) 1014 | pos = pos + 1 1015 | cast:append(C.lua_rawseti(-2, pos)) 1016 | end 1017 | end 1018 | end 1019 | return cast 1020 | end 1021 | 1022 | -- Converts Lua `Call or `Invoke AST to C AST. 1023 | local function gencall(ast, where, nret) 1024 | local isinvoke = ast.tag == 'Invoke' -- method call 1025 | local cast = cexpr() 1026 | 1027 | -- whether last argument might possibly return multiple values 1028 | local can_multret = can_multi(ast[#ast]) 1029 | 1030 | -- push function 1031 | if isinvoke then 1032 | local obj_ast = ast[1] 1033 | cast:append(genexpr(obj_ast, 'onstack')) -- actually self 1034 | else 1035 | assert(ast.tag == 'Call') 1036 | local f_ast = ast[1] 1037 | cast:append(genexpr(f_ast, 'onstack')) 1038 | end 1039 | 1040 | -- argument marker 1041 | local id 1042 | if can_multret then 1043 | id = nextid(); cast:append(C.Let(id, C.lua_gettop())) 1044 | end 1045 | 1046 | -- push arguments 1047 | for i=2,#ast do 1048 | if i == 2 and isinvoke then 1049 | local methodname_ast = ast[i] 1050 | cast:append(genexpr(methodname_ast, 'onstack')) 1051 | cast:append(C.lua_gettable(-2)) 1052 | cast:append(C.lua_insert(-2)) -- swap self and function 1053 | else 1054 | cast:append(genexpr(ast[i], 'onstack', i == #ast and 'multret' or false)) 1055 | end 1056 | end 1057 | local narg = can_multret and C.Op('-', C.lua_gettop(), C.Id(id)) or #ast-1 1058 | 1059 | -- call 1060 | cast:append(C.lua_call(narg, 1061 | nret == 'multret' and C.C'LUA_MULTRET' or nret or 1)) 1062 | 1063 | return cast 1064 | end 1065 | 1066 | 1067 | -- helper 1068 | local function gen_lc_setupvalue(...) 1069 | local cast = cexpr(C.CallL('lc_setupvalue', ...)) 1070 | if not _is_created['lc_setupvalue'] then 1071 | append_array(cast.pre, {C.C[[ 1072 | static void lc_setupvalue(lua_State * L, int tidx, int level, int varid) { 1073 | if (level == 0) { 1074 | lua_rawseti(L,tidx,varid); 1075 | } 1076 | else { 1077 | lua_pushvalue(L,tidx); 1078 | while(--level >= 0) { 1079 | lua_rawgeti(L,tidx,0); /* 0 links to parent table */ 1080 | lua_remove(L,-2); 1081 | tidx = -1; 1082 | } 1083 | lua_insert(L,-2); 1084 | lua_rawseti(L,-2,varid); 1085 | lua_pop(L,1); 1086 | } 1087 | } 1088 | ]]}) 1089 | _is_created['lc_setupvalue'] = true 1090 | end 1091 | return cast 1092 | end 1093 | 1094 | -- helper 1095 | local function gen_lc_getupvalue(tidx, level, varid) 1096 | assert(tidx and level and varid) 1097 | local cast = cexpr(C.CallL('lc_getupvalue', tidx, level, varid)) 1098 | if not _is_created['lc_getupvalue'] then 1099 | append_array(cast.pre, {C.C[[ 1100 | /* gets upvalue with ID varid by consulting upvalue table at index 1101 | * tidx for the upvalue table at given nesting level. */ 1102 | static void lc_getupvalue(lua_State * L, int tidx, int level, int varid) { 1103 | if (level == 0) { 1104 | lua_rawgeti(L,tidx,varid); 1105 | } 1106 | else { 1107 | lua_pushvalue(L,tidx); 1108 | while (--level >= 0) { 1109 | lua_rawgeti(L,tidx,0); /* 0 links to parent table */ 1110 | lua_remove(L,-2); 1111 | tidx = -1; 1112 | } 1113 | lua_rawgeti(L,-1,varid); 1114 | lua_remove(L,-2); 1115 | } 1116 | } 1117 | ]]}) 1118 | _is_created['lc_getupvalue'] = true 1119 | end 1120 | return cast 1121 | end 1122 | 1123 | -- Converts Lua `Number AST to C C AST 1124 | local function gennumber(x, pre_cast) 1125 | if x == math.huge or x == -math.huge then 1126 | if not _is_created['math.h'] then 1127 | append_array(pre_cast, {C.Include''}) 1128 | _is_created['math.h'] = true 1129 | end 1130 | end 1131 | return x 1132 | end 1133 | 1134 | -- Converts any Lua expression AST to C AST. 1135 | -- (local) 1136 | function genexpr(ast, where, nret) 1137 | if ast.tag == 'Op' then 1138 | ast = constant_fold(ast) 1139 | end 1140 | 1141 | if ast.tag == 'Nil' then 1142 | return cexpr(C.lua_pushnil()) 1143 | elseif ast.tag == 'Dots' then 1144 | if nret == 'multret' then 1145 | _funcinfo.is_lc_nactualargs_used = true 1146 | return cexpr(C.C[[{int i; for (i=lc_nformalargs+1; i<=lc_nactualargs; i++) { lua_pushvalue(L, i); }}]]) 1147 | elseif nret and nret > 1 then 1148 | _funcinfo.is_lc_nextra_used = true 1149 | return cexpr(C.C([[ 1150 | /* ... */ 1151 | { int i; 1152 | const int idx = lua_gettop(L); 1153 | const int npush = (]] .. nret .. [[ <= lc_nextra) ? ]] .. nret .. [[ : lc_nextra; 1154 | for (i=lc_nformalargs+1; i<=lc_nformalargs+npush; i++) { lua_pushvalue(L, i); } 1155 | lua_settop(L, idx + ]] .. nret .. [[); }]])) 1156 | else 1157 | return cexpr(C.lua_pushvalue(C.Op('+', C.Id'lc_nformalargs', 1))); 1158 | end 1159 | elseif ast.tag == 'True' then 1160 | return cexpr(C.lua_pushboolean(1)) 1161 | elseif ast.tag == 'False' then 1162 | return cexpr(C.lua_pushboolean(0)) 1163 | elseif ast.tag == 'Number' then 1164 | local cast = cexpr() 1165 | cast:append(C.lua_pushnumber(gennumber(ast[1], cast.pre))) 1166 | return cast 1167 | elseif ast.tag == 'String' then 1168 | local s = ast[1] 1169 | return cexpr(C.lua_pushliteral(s)) 1170 | elseif ast.tag == 'Function' then 1171 | return genfunction(ast, where) 1172 | elseif ast.tag == 'Table' then 1173 | return gentable(ast, where) 1174 | elseif ast.tag == 'Op' then 1175 | local opid = ast[1] 1176 | if is_binopid[opid] then 1177 | if is_arithbinop[opid] then 1178 | return genarithbinop(ast, where) 1179 | elseif opid == 'eq' then 1180 | return geneqop(ast, where) 1181 | elseif opid == 'lt' then 1182 | return genltop(ast, where) 1183 | elseif opid == 'le' then 1184 | return genleop(ast, where) 1185 | elseif opid == 'or' or opid == 'and' then 1186 | return genlogbinop(ast, where) 1187 | elseif opid == 'concat' then 1188 | return genconcatop(ast, where) 1189 | else 1190 | assert(false, opid) 1191 | end 1192 | elseif is_unopid[opid] then 1193 | if opid == 'not' then 1194 | return gennotop(ast, where) 1195 | elseif opid == 'len' then 1196 | return genlenop(ast, where) 1197 | elseif opid == 'unm' then 1198 | return genunmop(ast, where) 1199 | else 1200 | assert(false, opid) 1201 | end 1202 | else 1203 | assert(false, opid) 1204 | end 1205 | elseif ast.tag == 'Paren' then 1206 | local ast2 = ast[1] 1207 | return genexpr(ast2, where) 1208 | -- note: metalua allows `Stat here too as an extension 1209 | elseif ast.tag == 'Call' or ast.tag == 'Invoke' then 1210 | return gencall(ast, where, nret) 1211 | elseif ast.tag == 'Id' then 1212 | local name = ast[1] 1213 | local cast = cexpr() 1214 | if _currentscope[name] then -- local 1215 | local idx = localidx(name) 1216 | if tag(idx) == 'Upval' then 1217 | local closuretable_idx, closurelevel, varid = idx[1], idx[2], idx[3] 1218 | cast:append(gen_lc_getupvalue( 1219 | get_closuretableidx_cast(), 1220 | _currentscope['.closurelevel'] - closurelevel, varid)) 1221 | elseif where == 'anywhere' then 1222 | cast.idx = idx 1223 | return cast 1224 | else 1225 | cast:append(C.lua_pushvalue(idx)) 1226 | end 1227 | else -- global 1228 | --old: cast:append(C.lua_getglobal(name)) 1229 | cast:append(C.lua_getfield(C.C'LUA_ENVIRONINDEX', name)) 1230 | end 1231 | return cast 1232 | elseif ast.tag == 'Index' then 1233 | local cast = cexpr() 1234 | local t_ast, k_ast = ast[1], ast[2] 1235 | local t_idx = cast:append(genexpr(t_ast, 'anywhere')).idx 1236 | cast:append(genexpr(k_ast, 'onstack')) 1237 | cast:append(C.lua_gettable(adjustidx(-2, t_idx))) 1238 | if t_idx == -1 then 1239 | cast:append(C.lua_remove(-2)) 1240 | end 1241 | return cast 1242 | else 1243 | assert(false, ast.tag) 1244 | end 1245 | end 1246 | 1247 | -- Converts Lua l-value expression AST to C AST. 1248 | local function genlvalueassign(l_ast) 1249 | local cast = cexpr() 1250 | if l_ast.tag == 'Id' then 1251 | local name = l_ast[1] 1252 | if _currentscope[name] then 1253 | local idx = localidx(name) 1254 | if tag(idx) == 'Upval' then 1255 | local closuretable_idx, closurelevel, varid = idx[1], idx[2], idx[3] 1256 | cast:append(gen_lc_setupvalue( 1257 | get_closuretableidx_cast(), 1258 | _currentscope['.closurelevel'] - closurelevel, varid)) 1259 | else 1260 | cast:append(C.lua_replace(idx)) 1261 | end 1262 | else -- global 1263 | --old:cast:append(C.lua_setglobal(name)) 1264 | cast:append(C.lua_setfield(C.C'LUA_ENVIRONINDEX', name)) 1265 | end 1266 | elseif l_ast.tag == 'Index' then 1267 | local t_ast, k_ast = l_ast[1], l_ast[2] 1268 | local t_idx = cast:append(genexpr(t_ast, 'anywhere')).idx 1269 | if t_idx == -1 then cast:append(C.lua_insert(-2)) end 1270 | cast:append(genexpr(k_ast, 'onstack')) 1271 | cast:append(C.lua_insert(-2)) 1272 | cast:append(C.lua_settable(adjustidx(-3, t_idx))) 1273 | if t_idx == -1 then 1274 | cast:append(C.lua_pop(1)) 1275 | end 1276 | else 1277 | assert(false, l_ast.tag) 1278 | end 1279 | return cast 1280 | end 1281 | 1282 | 1283 | -- Converts Lua `If AST to C AST. 1284 | local function genif(ast, i) 1285 | i = i or 1 -- i > 1 is index in AST node of elseif branch to generate 1286 | local cast = cexpr() 1287 | local if_args = {pre=cast.pre} 1288 | 1289 | local currentscope_save = _currentscope 1290 | 1291 | local base_idx = _idxtop 1292 | local base_id = nextid() 1293 | cast:append(C.Enum(base_id, base_idx)) 1294 | 1295 | do 1296 | local expr_ast, body_ast = ast[i], ast[i+1] 1297 | 1298 | idxtop_restore(base_idx) 1299 | 1300 | -- expression 1301 | local a_idx = cast:append(genexpr(expr_ast, 'anywhere')).idx 1302 | if a_idx ~= -1 then 1303 | if_args[#if_args+1] = tobool(a_idx) 1304 | else 1305 | local id = nextid(); 1306 | cast:append(C.Let(id, tobool(-1))) 1307 | cast:append(C.lua_pop(1)) 1308 | if_args[#if_args+1] = C.Id(id) 1309 | end 1310 | 1311 | -- block 1312 | newscope() 1313 | local block_cast = genblock(body_ast) 1314 | table.insert(if_args, block_cast) 1315 | append_array(cast.pre, block_cast.pre) 1316 | restore_scope(currentscope_save) 1317 | end 1318 | 1319 | if ast[i+2] then 1320 | idxtop_restore(base_idx) 1321 | 1322 | local currentscope_save = newscope() 1323 | local block_cast 1324 | if not ast[i+3] then 1325 | block_cast = genblock(ast[i+2]) 1326 | if block_cast[1] then 1327 | prepend_comment(block_cast[1], 'else') 1328 | end 1329 | else 1330 | block_cast = genif(ast, i+2) 1331 | end 1332 | table.insert(if_args, block_cast) 1333 | append_array(cast.pre, block_cast.pre) 1334 | restore_scope(currentscope_save) 1335 | end 1336 | -- note: idx is (in general) indeterminant now 1337 | -- due to the multiple branches. 1338 | 1339 | cast:append(C.If(unpack(if_args))) 1340 | 1341 | cast:append(C.lua_settop(realidx(C.Id(base_id)))) 1342 | idxtop_restore(base_idx) 1343 | 1344 | local start = 1345 | i == 1 and ast.lineinfo.first[3] or ast[i-1].lineinfo.last[3]+1 1346 | prepend_comment(cast, trim(src:sub(start, ast[i].lineinfo.last[3])) .. 1347 | ' then') 1348 | if i == 1 then 1349 | append_comment_below(cast, 'end') 1350 | end 1351 | local comment = false 1352 | 1353 | return cast, comment 1354 | end 1355 | 1356 | 1357 | -- Converts Lua `Fornum AST to C AST. 1358 | local function genfornum(ast) 1359 | local name_ast, e1_ast, e2_ast, e3_ast, block_ast = 1360 | ast[1], ast[2], ast[3], ast[5] and ast[4] or ast[5], ast[5] or ast[4] 1361 | local name_id = name_ast[1]; assert(name_ast.tag == 'Id') 1362 | local cast = cexpr() 1363 | 1364 | local var_id = nextid() .. '_var'; 1365 | local limit_id = nextid() .. '_limit'; 1366 | local step_id = nextid() .. '_step'; 1367 | local var_idx = cast:append(genexpr(e1_ast, 'anywhere')).idx 1368 | local limit_idx = cast:append(genexpr(e2_ast, 'anywhere')).idx 1369 | local step_idx = e3_ast and cast:append(genexpr(e3_ast, 'anywhere')).idx 1370 | 1371 | local nstack = (var_idx == -1 and 1 or 0) + 1372 | (limit_idx == -1 and 1 or 0) + 1373 | (step_idx == -1 and 1 or 0) 1374 | 1375 | var_idx, limit_idx, step_idx = adjustidxs(var_idx, limit_idx, step_idx) 1376 | 1377 | 1378 | local expr_cast = 1379 | C.Op('&&', C.lua_isnumber(var_idx), C.lua_isnumber(limit_idx)) 1380 | if step_idx then 1381 | expr_cast = C.Op('&&', expr_cast, C.lua_isnumber(step_idx)) 1382 | end 1383 | cast:append( 1384 | C.If(C.Not(expr_cast), 1385 | {C.CallL('luaL_error', "'for' limit must be a number")})) 1386 | cast:append(C.LetMutableDouble(var_id, todouble(var_idx))) 1387 | cast:append(C.LetDouble(limit_id, todouble(limit_idx))) 1388 | if e3_ast then 1389 | cast:append(C.LetDouble(step_id, todouble(step_idx))) 1390 | else 1391 | cast:append(C.LetDouble(step_id, 1.0)) 1392 | end 1393 | 1394 | cast:append(C.lua_pop(nstack)) 1395 | 1396 | local while_expr_cast = 1397 | C.Op('||', 1398 | C.Op('&&', 1399 | C.Op('>', C.Id(step_id), 0), 1400 | C.Op('<=', C.Id(var_id), C.Id(limit_id))), 1401 | C.Op('&&', 1402 | C.Op('<=', C.Id(step_id), 0), 1403 | C.Op('>=', C.Id(var_id), C.Id(limit_id)))) 1404 | 1405 | -- local scope to evaluate block 1406 | local currentscope_save = newscope() 1407 | 1408 | local block_cast = cexpr() 1409 | 1410 | local base_idx = _idxtop 1411 | local base_id = nextid() 1412 | cast:append(C.Enum(base_id, base_idx)) 1413 | 1414 | -- create local 1415 | if name_ast.upvalue then 1416 | local ct_idx = block_cast:append(gennewclosuretable()).idx 1417 | activate_closure_table(ct_idx) 1418 | 1419 | local varid = nextvarid() 1420 | block_cast:append(C.lua_pushnumber(C.Id(var_id))) 1421 | block_cast:append(C.lua_rawseti(-2, varid)) 1422 | _currentscope[name_id] = 1423 | {tag='Upval', ct_idx, _currentscope['.closurelevel'], varid} 1424 | else 1425 | block_cast:append(C.lua_pushnumber(C.Id(var_id))) 1426 | idxtop_change(1) 1427 | _currentscope[name_id] = _idxtop 1428 | end 1429 | block_cast[1].comment = 1430 | string.format("internal: local %s at index %d", name_id, _idxtop) 1431 | 1432 | block_cast:append(genblock(block_ast)) 1433 | 1434 | restore_stack_rel(block_cast, base_idx) 1435 | restore_scope(currentscope_save) 1436 | 1437 | block_cast:append(C.C(var_id .. ' += ' .. step_id .. ';')) --IMPROVE? 1438 | 1439 | cast:append(C.While(while_expr_cast, block_cast)) 1440 | append_array(cast.pre, block_cast.pre) 1441 | 1442 | -- note: mixed breaks and locals can leave the stack at an 1443 | -- unknown size, so absolute adjust here. 1444 | restore_stack_abs(cast, C.Id(base_id), base_idx) 1445 | 1446 | prepend_comment(cast, 1447 | trim(src:sub(ast.lineinfo.first[3], block_ast.lineinfo.first[3]-1))) 1448 | append_comment_below(cast, 'end') 1449 | local comment = false 1450 | 1451 | return cast, comment 1452 | end 1453 | 1454 | -- Converts Lua `Forin AST to C AST. 1455 | local function genforin(ast) 1456 | local names_ast, exprs_ast, block_ast = ast[1], ast[2], ast[3] 1457 | local cast = cexpr() 1458 | 1459 | local multi = can_multi(exprs_ast[1]) 1460 | 1461 | local base_idx = _idxtop 1462 | local base_id = nextid() 1463 | cast:append(C.Enum(base_id, base_idx)) 1464 | 1465 | -- loop invisible variables: var, limit, step 1466 | local nlast = multi and 3 + 1 - #exprs_ast or 1 1467 | for i,expr_ast in ipairs(exprs_ast) do 1468 | cast:append(genexpr(expr_ast, 'onstack', 1469 | i==#exprs_ast and math.max(0,nlast))) 1470 | end 1471 | if nlast < 0 then 1472 | cast:append(C.lua_pop(-nlast)) 1473 | end 1474 | idxtop_change(3) 1475 | append_comment(cast[1], 'internal: local f, s, var = explist') 1476 | 1477 | local base2_idx = _idxtop 1478 | 1479 | local block_cast = cexpr(); do 1480 | 1481 | -- local scope to evaluate block 1482 | local currentscope_save = newscope() 1483 | local extra = 0 1484 | local ct_idx 1485 | 1486 | local is_upvalue = false 1487 | for i,name_ast in ipairs(names_ast) do 1488 | if name_ast.upvalue then is_upvalue = true end 1489 | end 1490 | if is_upvalue then 1491 | ct_idx = block_cast:append(gennewclosuretable()).idx 1492 | activate_closure_table(ct_idx) 1493 | extra = 1 1494 | end 1495 | 1496 | -- loop variables and control 1497 | block_cast:append(C.lua_pushvalue(-3 - extra)) 1498 | append_comment(block_cast[#block_cast], 1499 | 'internal: local var_1, ..., var_n = f(s, var)\n' .. 1500 | ' if var_1 == nil then break end\n' .. 1501 | ' var = var_1') 1502 | block_cast:append(C.lua_pushvalue(-3 - extra)) 1503 | block_cast:append(C.lua_pushvalue(-3 - extra)) 1504 | block_cast:append(C.lua_call(2,#names_ast)) 1505 | idxtop_change(#names_ast) 1506 | block_cast:append(C.If(C.lua_isnil(- #names_ast), {C.Break()})) 1507 | block_cast:append(C.lua_pushvalue(- #names_ast)) 1508 | block_cast:append(C.lua_replace(- #names_ast - 2 - extra)) 1509 | -- loop variables 1510 | local pos1 = #block_cast 1511 | local idx1 = _idxtop - #names_ast 1512 | do -- locals used as upvalues 1513 | local varids = {} 1514 | local nlocals = 0 -- number of non-up-value-enabled locals found 1515 | for i=#names_ast,1,-1 do 1516 | local name_ast = names_ast[i] 1517 | if name_ast.upvalue then 1518 | local name_id = name_ast[1]; assert(name_ast.tag == 'Id') 1519 | varids[i] = nextvarid() 1520 | if nlocals == 0 then 1521 | block_cast:append(C.lua_rawseti(-1 - i, varids[i])) 1522 | else 1523 | block_cast:append(C.lua_pushvalue(-1 - nlocals)) 1524 | block_cast:append(C.lua_rawseti(realidx(ct_idx), varids[i])) 1525 | block_cast:append(C.lua_remove(-1 - nlocals)) 1526 | end 1527 | idxtop_change(- 1) 1528 | _currentscope[name_id] = 1529 | {tag='Upval', ct_idx, _currentscope['.closurelevel'], varids[i]} 1530 | else 1531 | nlocals = nlocals + 1 1532 | end 1533 | end 1534 | for i,name_ast in ipairs(names_ast) do if varids[i] then 1535 | append_comment(block_cast[pos1+1], 1536 | string.format("internal: upvalue-enabled local %s with id %d", 1537 | name_ast[1], varids[i])) 1538 | end end 1539 | end 1540 | if pos1 == #block_cast then 1541 | -- hack: ensure AST node exists in which to place comment. 1542 | block_cast:append(C.C'') 1543 | end 1544 | do -- locals 1545 | local count = 0 1546 | for i,name_ast in ipairs(names_ast) do 1547 | if not name_ast.upvalue then 1548 | local name_id = name_ast[1]; assert(name_ast.tag == 'Id') 1549 | count = count + 1 1550 | _currentscope[name_id] = idx1 + count 1551 | append_comment(block_cast[pos1+1], 1552 | string.format("internal: local %s with idx %d", 1553 | name_id, _currentscope[name_id])) 1554 | end 1555 | end 1556 | end 1557 | 1558 | -- block 1559 | block_cast:append(genblock(block_ast)) 1560 | 1561 | -- end scope 1562 | restore_stack_rel(block_cast, base2_idx) 1563 | restore_scope(currentscope_save) 1564 | end 1565 | cast:append(C.While(1, block_cast)) 1566 | append_array(cast.pre, block_cast.pre) 1567 | 1568 | restore_stack_abs(cast, C.Id(base_id), base_idx) 1569 | 1570 | prepend_comment(cast, 1571 | trim(src:sub(ast.lineinfo.first[3], block_ast.lineinfo.first[3]-1))) 1572 | append_comment_below(cast, 'end') 1573 | local comment = false 1574 | 1575 | return cast, comment 1576 | end 1577 | 1578 | 1579 | -- Converts Lua `While AST to C AST. 1580 | local function genwhile(ast) 1581 | local expr_ast, block_ast = ast[1], ast[2] 1582 | local cast = cexpr() 1583 | 1584 | local base_idx = _idxtop 1585 | local base_id = nextid() 1586 | cast:append(C.Enum(base_id, base_idx)) 1587 | 1588 | do 1589 | -- expression 1590 | local block_cast = cexpr(); do 1591 | local expr_idx = block_cast:append(genexpr(expr_ast, 'anywhere')).idx 1592 | block_cast:append( 1593 | C.If(C.Not(tobool(expr_idx)), {C.Break()})) 1594 | if expr_idx == -1 then 1595 | block_cast:append(C.lua_pop(1)) 1596 | end 1597 | 1598 | -- local scope to evaluate block 1599 | local currentscope_save = newscope() 1600 | 1601 | -- block 1602 | block_cast:append(genblock(block_ast)) 1603 | 1604 | restore_stack_rel(block_cast, base_idx) 1605 | restore_scope(currentscope_save) 1606 | end 1607 | 1608 | cast:append(C.While(1, block_cast)) 1609 | append_array(cast.pre, block_cast.pre) 1610 | end 1611 | 1612 | -- note: mixed breaks and locals can leave the stack at an 1613 | -- unknown size, so absolute adjust here. 1614 | restore_stack_abs(cast, C.Id(base_id), base_idx) 1615 | 1616 | prepend_comment(cast, 1617 | trim(src:sub(ast.lineinfo.first[3], block_ast.lineinfo.first[3]-1))) 1618 | append_comment_below(cast, 'end') 1619 | local comment = false 1620 | 1621 | return cast, comment 1622 | end 1623 | 1624 | 1625 | -- Converts Lua `Repeat AST to C AST. 1626 | local function genrepeat(ast) 1627 | local block_ast, expr_ast = ast[1], ast[2] 1628 | local cast = cexpr() 1629 | 1630 | local base_idx = _idxtop 1631 | local base_id = nextid() 1632 | cast:append(C.Enum(base_id, base_idx)) 1633 | 1634 | do 1635 | -- body 1636 | local block_cast = cexpr(); do 1637 | -- local scope to evaluate block and expression 1638 | local currentscope_save = newscope() 1639 | 1640 | -- block 1641 | block_cast:append(genblock(block_ast)) 1642 | 1643 | -- expression 1644 | local expr_idx = block_cast:append(genexpr(expr_ast, 'anywhere')).idx 1645 | idxtop_change(1) 1646 | block_cast:append( 1647 | C.If(tobool(expr_idx), {C.Break()})) 1648 | 1649 | restore_stack_rel(block_cast, base_idx) 1650 | restore_scope(currentscope_save) 1651 | end 1652 | cast:append(C.While(1, block_cast)) 1653 | append_array(cast.pre, block_cast.pre) 1654 | end 1655 | 1656 | -- note: mixed breaks and locals can leave the stack at an 1657 | -- unknown size, so absolute adjust here. 1658 | restore_stack_abs(cast, C.Id(base_id), base_idx) 1659 | 1660 | prepend_comment(cast, 'repeat') 1661 | append_comment_below(cast, 1662 | trim(src:sub(block_ast.lineinfo.last[3]+1, ast.lineinfo.last[3]))) 1663 | local comment = false 1664 | 1665 | return cast, comment 1666 | end 1667 | 1668 | 1669 | -- Converts Lua `Do AST to C AST. 1670 | local function gendo(ast) 1671 | local cast = cexpr() 1672 | 1673 | local base_idx = _idxtop 1674 | 1675 | -- local scope to evaluate block 1676 | local currentscope_save = newscope() 1677 | cast:append(genblock(ast)) 1678 | restore_scope(currentscope_save) 1679 | restore_stack_rel(cast, base_idx) 1680 | 1681 | prepend_comment(cast, 'do') 1682 | append_comment_below(cast, 'end') 1683 | local comment = false 1684 | 1685 | return cast, comment 1686 | end 1687 | 1688 | 1689 | -- Converts Lua `Local AST to C AST. 1690 | local function genlocalstat(ast) 1691 | local names_ast, vals_ast = ast[1], ast[2] 1692 | local cast = cexpr() 1693 | 1694 | local ct_idx 1695 | local is_upvalue = false 1696 | for i,name_ast in ipairs(names_ast) do 1697 | is_upvalue = is_upvalue or name_ast.upvalue 1698 | end 1699 | if is_upvalue then 1700 | ct_idx = cast:append(gennewclosuretable()).idx 1701 | end 1702 | 1703 | deduce_function_names(names_ast, vals_ast) 1704 | 1705 | -- create values 1706 | for i,val_ast in ipairs(vals_ast) do 1707 | cast:append(genexpr(val_ast, 'onstack', 1708 | #names_ast > #vals_ast and i == #vals_ast and can_multi(val_ast) and 1709 | (#names_ast - #vals_ast + 1))) 1710 | end 1711 | 1712 | -- cleanup if LHS and RHS lengths don't match 1713 | if #names_ast > #vals_ast and not can_multi(vals_ast[#vals_ast]) then 1714 | cast:append( 1715 | C.lua_settop( 1716 | C.Op('+', C.lua_gettop(), #names_ast - #vals_ast))) 1717 | elseif #names_ast < #vals_ast then 1718 | cast:append(C.lua_pop(#vals_ast - #names_ast)) 1719 | end 1720 | 1721 | if ct_idx then 1722 | -- activate closure scope (after having generated values) 1723 | activate_closure_table(ct_idx) 1724 | end 1725 | 1726 | -- store values in closure table and create local symbols 1727 | for i=#names_ast,1,-1 do local name_ast = names_ast[i] 1728 | local name = name_ast[1] 1729 | 1730 | if name_ast.upvalue then 1731 | local varid = nextvarid() 1732 | cast:append(C.lua_rawseti(realidx(ct_idx), varid)) 1733 | -- create local symbol 1734 | _currentscope[name] = 1735 | {tag='Upval', ct_idx, _currentscope['.closurelevel'], varid} 1736 | end 1737 | end 1738 | -- create local symbols 1739 | for i,name_ast in ipairs(names_ast) do 1740 | local name = name_ast[1]; assert(name_ast.tag == 'Id') 1741 | if not name_ast.upvalue then 1742 | idxtop_change(1) 1743 | _currentscope[name] = _idxtop 1744 | end 1745 | end 1746 | return cast 1747 | end 1748 | 1749 | -- Converts Lua `Set AST to C AST. 1750 | local function genset(stat_ast) 1751 | -- note: supports x,y=y,x 1752 | local ls_ast, rs_ast = stat_ast[1], stat_ast[2] 1753 | local cast = cexpr() 1754 | 1755 | deduce_function_names(ls_ast, rs_ast) 1756 | 1757 | -- create values (r_ast) 1758 | for i,r_ast in ipairs(rs_ast) do 1759 | cast:append(genexpr(r_ast, 'onstack', 1760 | #ls_ast > #rs_ast and i == #rs_ast and can_multi(r_ast) and 1761 | (#ls_ast - #rs_ast + 1))) 1762 | end 1763 | 1764 | -- cleanup if LHS and RHS lengths don't match 1765 | if #ls_ast > #rs_ast and not can_multi(rs_ast[#rs_ast]) then 1766 | cast:append( 1767 | C.lua_settop( 1768 | C.Op('+', C.lua_gettop(), #ls_ast - #rs_ast))) 1769 | elseif #ls_ast < #rs_ast then 1770 | cast:append(C.lua_pop(#rs_ast - #ls_ast)) 1771 | end 1772 | 1773 | for i=#ls_ast,1,-1 do 1774 | cast:append(genlvalueassign(ls_ast[i])) 1775 | end 1776 | 1777 | return cast 1778 | end 1779 | 1780 | -- Converts Lua `Localrec AST to C AST. 1781 | local function genlocalrec(ast) 1782 | local names_ast, vals_ast = ast[1], ast[2] 1783 | assert(#names_ast == 1) 1784 | assert(#vals_ast == 1) 1785 | local name_ast = names_ast[1] 1786 | local val_ast = vals_ast[1] 1787 | assert(val_ast.tag == 'Function') 1788 | 1789 | local cast = cexpr() 1790 | -- activate scope and symbol (prior to generating value) 1791 | local ct_idx 1792 | local varid 1793 | local name = name_ast[1] 1794 | if name_ast.upvalue then 1795 | ct_idx = cast:append(gennewclosuretable()).idx 1796 | activate_closure_table(ct_idx) 1797 | -- create local symbol 1798 | varid = nextvarid() 1799 | _currentscope[name] = 1800 | {tag='Upval', ct_idx, _currentscope['.closurelevel'], varid} 1801 | else 1802 | _currentscope[name] = _idxtop + 1 1803 | end 1804 | 1805 | deduce_function_names(names_ast, vals_ast) 1806 | 1807 | -- create value 1808 | cast:append(genexpr(val_ast, 'onstack')) 1809 | 1810 | -- store value 1811 | if name_ast.upvalue then 1812 | cast:append(C.lua_rawseti(realidx(ct_idx), varid)) 1813 | else 1814 | idxtop_change(1) 1815 | end 1816 | 1817 | return cast 1818 | end 1819 | 1820 | -- Converts any Lua statement AST to C AST. 1821 | local function genstatement(stat_ast) 1822 | local cast 1823 | local comment 1824 | if stat_ast.tag == 'Set' then 1825 | cast = genset(stat_ast) 1826 | elseif stat_ast.tag == 'Return' then 1827 | cast = cexpr() 1828 | local can_multi = #stat_ast >= 1 and can_multi(stat_ast[#stat_ast]) 1829 | local id 1830 | if can_multi then 1831 | id = nextid(); cast:append(C.Let(id, C.lua_gettop())) 1832 | end 1833 | for i,e_ast in ipairs(stat_ast) do 1834 | cast:append(genexpr(e_ast, 'onstack', 1835 | i==#stat_ast and can_multi and 'multret' or 1)) 1836 | end 1837 | if id then 1838 | cast:append(C.Return(C.Op('-', C.lua_gettop(), C.Id(id)))) 1839 | else 1840 | cast:append(C.Return(#stat_ast)) 1841 | end 1842 | elseif stat_ast.tag == 'Fornum' then 1843 | cast, comment = genfornum(stat_ast) 1844 | elseif stat_ast.tag == 'Forin' then 1845 | cast, comment = genforin(stat_ast) 1846 | elseif stat_ast.tag == 'While' then 1847 | cast, comment = genwhile(stat_ast) 1848 | elseif stat_ast.tag == 'Repeat' then 1849 | cast, comment = genrepeat(stat_ast) 1850 | elseif stat_ast.tag == 'Do' then 1851 | cast, comment = gendo(stat_ast) 1852 | elseif stat_ast.tag == 'If' then 1853 | cast, comment = genif(stat_ast) 1854 | elseif stat_ast.tag == 'Call' or stat_ast.tag == 'Invoke' then 1855 | cast = genexpr(stat_ast, 'onstack', 0) 1856 | elseif stat_ast.tag == 'Local' then 1857 | cast = genlocalstat(stat_ast) 1858 | elseif stat_ast.tag == 'Localrec' then 1859 | cast = genlocalrec(stat_ast) 1860 | elseif stat_ast.tag == 'Break' then 1861 | cast = cexpr(C.Break()) 1862 | else 1863 | assert(false, stat_ast.tag) 1864 | end 1865 | if comment ~= false then 1866 | comment = comment or get_ast_string(src, stat_ast) 1867 | prepend_comment(cast, comment) 1868 | end 1869 | return cast 1870 | end 1871 | 1872 | -- Converts Lua block AST to C AST. 1873 | -- (local) 1874 | function genblock(ast) 1875 | local cast = cexpr() 1876 | for _,stat_ast in ipairs(ast) do 1877 | local stat_cast = genstatement(stat_ast) 1878 | 1879 | local comments = stat_ast.lineinfo.first.comments 1880 | if comments then 1881 | for i=#comments,1,-1 do 1882 | local comment = src:sub(comments[i][2], comments[i][3]):gsub('\n$','') 1883 | prepend_comment(stat_cast, comment) 1884 | end 1885 | end 1886 | 1887 | cast:append(stat_cast) 1888 | 1889 | -- DEBUG 1890 | if true then 1891 | if _funcinfo.is_vararg then 1892 | _funcinfo.is_lc_nextra_used_debug = true 1893 | end 1894 | if not _is_created['assert.h'] then 1895 | append_array(cast.pre, {C.Include''}) 1896 | _is_created['assert.h'] = true 1897 | end 1898 | cast:append(C.C(string.format([[assert(lua_gettop(L) %s== %d);]], 1899 | _funcinfo.is_lc_nextra_used_debug and "- lc_nextra " or "", _idxtop))) 1900 | end 1901 | 1902 | end 1903 | 1904 | if #ast > 0 then 1905 | local stat_ast = ast[#ast] 1906 | local comments = stat_ast.lineinfo.last.comments 1907 | if comments then 1908 | for i=1,#comments do 1909 | local comment = src:sub(comments[i][2], comments[i][3]):gsub('\n$','') 1910 | append_comment_below(cast[#cast], comment) 1911 | end 1912 | end 1913 | else 1914 | local comments = ast.lineinfo.first.comments 1915 | if comments then 1916 | for i=1,#comments do 1917 | local comment = src:sub(comments[i][2], comments[i][3]):gsub('\n$','') 1918 | append_comment_below(cast, comment) 1919 | end 1920 | end 1921 | end 1922 | 1923 | return cast 1924 | end 1925 | 1926 | local function gendefinitions(ast) 1927 | local cast = C.Def() 1928 | for _,def_ast in ipairs(ast) do 1929 | cast:append(genstatement(def_ast)) 1930 | end 1931 | return cast 1932 | end 1933 | 1934 | 1935 | -- Converts Lua top-level function to C AST, 1936 | -- including prelude. 1937 | local function genfull(ast) 1938 | -- support LUA_INIT environment variable 1939 | local enable_lua_init = true 1940 | 1941 | local cast = cexpr(); cast.tag = 'Def' 1942 | 1943 | cast:append(C.C [[ 1944 | /* WARNING: This file was automatically generated by lua2c. */ 1945 | 1946 | #ifdef __cplusplus 1947 | extern "C" { 1948 | #endif 1949 | #include 1950 | #include 1951 | #include 1952 | #ifdef __cplusplus 1953 | } 1954 | #endif 1955 | #include 1956 | #include 1957 | ]]) 1958 | 1959 | if enable_lua_init then 1960 | cast:append(C.C [[ 1961 | #include 1962 | ]]) 1963 | end 1964 | 1965 | cast:append(genmainchunk(ast)) 1966 | 1967 | cast:append(C.C [[ 1968 | /* from lua.c */ 1969 | static int traceback (lua_State *L) { 1970 | if (!lua_isstring(L, 1)) /* 'message' not a string? */ 1971 | return 1; /* keep it intact */ 1972 | lua_getfield(L, LUA_GLOBALSINDEX, "debug"); 1973 | if (!lua_istable(L, -1)) { 1974 | lua_pop(L, 1); 1975 | return 1; 1976 | } 1977 | lua_getfield(L, -1, "traceback"); 1978 | if (!lua_isfunction(L, -1)) { 1979 | lua_pop(L, 2); 1980 | return 1; 1981 | } 1982 | lua_pushvalue(L, 1); /* pass error message */ 1983 | lua_pushinteger(L, 2); /* skip this function and traceback */ 1984 | lua_call(L, 2, 1); /* call debug.traceback */ 1985 | return 1; 1986 | } 1987 | ]]) 1988 | 1989 | if enable_lua_init then 1990 | cast:append(C.C [[ 1991 | static void lc_l_message (const char *pname, const char *msg) { 1992 | if (pname) fprintf(stderr, "%s: ", pname); 1993 | fprintf(stderr, "%s\n", msg); 1994 | fflush(stderr); 1995 | } 1996 | 1997 | static int lc_report (lua_State *L, int status) { 1998 | if (status && !lua_isnil(L, -1)) { 1999 | const char *msg = lua_tostring(L, -1); 2000 | if (msg == NULL) msg = "(error object is not a string)"; 2001 | /*FIX-IMROVE:progname*/ 2002 | lc_l_message("lua", msg); 2003 | lua_pop(L, 1); 2004 | } 2005 | return status; 2006 | } 2007 | 2008 | static int lc_docall (lua_State *L, int narg, int clear) { 2009 | int status; 2010 | int base = lua_gettop(L) - narg; /* function index */ 2011 | lua_pushcfunction(L, traceback); /* push traceback function */ 2012 | lua_insert(L, base); /* put it under chunk and args */ 2013 | /*FIX? signal(SIGINT, laction); */ 2014 | status = lua_pcall(L, narg, (clear ? 0 : LUA_MULTRET), base); 2015 | /*FIX? signal(SIGINT, SIG_DFL); */ 2016 | lua_remove(L, base); /* remove traceback function */ 2017 | /* force a complete garbage collection in case of errors */ 2018 | if (status != 0) lua_gc(L, LUA_GCCOLLECT, 0); 2019 | return status; 2020 | } 2021 | 2022 | static int lc_dofile (lua_State *L, const char *name) { 2023 | int status = luaL_loadfile(L, name) || lc_docall(L, 0, 1); 2024 | return lc_report(L, status); 2025 | } 2026 | 2027 | static int lc_dostring (lua_State *L, const char *s, const char *name) { 2028 | int status = luaL_loadbuffer(L, s, strlen(s), name) || lc_docall(L, 0, 1); 2029 | return lc_report(L, status); 2030 | } 2031 | 2032 | static int lc_handle_luainit (lua_State *L) { 2033 | const char *init = getenv(LUA_INIT); 2034 | if (init == NULL) return 0; /* status OK */ 2035 | else if (init[0] == '@') 2036 | return lc_dofile(L, init+1); 2037 | else 2038 | return lc_dostring(L, init, "=" LUA_INIT); 2039 | } 2040 | ]]) 2041 | end 2042 | 2043 | cast:append(C.C [[ 2044 | typedef struct { 2045 | int c; 2046 | const char ** v; 2047 | } lc_args_t; 2048 | ]]) 2049 | 2050 | cast:append(C.C [[ 2051 | /* create global arg table */ 2052 | static void lc_createarg(lua_State * L, const lc_args_t * const args) { 2053 | int i; 2054 | lua_newtable(L); 2055 | for (i=0; i < args->c; i++) { 2056 | lua_pushstring(L, args->v[i]); 2057 | lua_rawseti(L, -2, i); 2058 | } 2059 | lua_setglobal(L, "arg"); 2060 | } 2061 | ]]) 2062 | 2063 | cast:append(C.C([[ 2064 | static int lc_pmain(lua_State * L) { 2065 | luaL_openlibs(L); 2066 | 2067 | const lc_args_t * const args = (lc_args_t*)lua_touserdata(L, 1); 2068 | lc_createarg(L, args); 2069 | 2070 | lua_pushcfunction(L, traceback); 2071 | 2072 | ]] .. ( 2073 | enable_lua_init and [[ 2074 | const int status1 = lc_handle_luainit(L); 2075 | if (status1 != 0) return 0; 2076 | ]] or '') .. 2077 | [[ 2078 | 2079 | /* note: IMPROVE: closure not always needed here */ 2080 | lua_newtable(L); /* closure table */ 2081 | lua_pushcclosure(L, lcf_main, 1); 2082 | int i; 2083 | for (i=1; i < args->c; i++) { 2084 | lua_pushstring(L, args->v[i]); 2085 | } 2086 | int status2 = lua_pcall(L, args->c-1, 0, -2); 2087 | if (status2 != 0) { 2088 | const char * msg = lua_tostring(L,-1); 2089 | if (msg == NULL) msg = "(error object is not a string)"; 2090 | fputs(msg, stderr); 2091 | } 2092 | return 0; 2093 | } 2094 | ]])) 2095 | 2096 | cast:append(C.C [[ 2097 | int main(int argc, const char ** argv) { 2098 | lc_args_t args = {argc, argv}; 2099 | lua_State * L = luaL_newstate(); 2100 | if (! L) { fputs("Failed creating Lua state.", stderr); exit(1); } 2101 | 2102 | int status = lua_cpcall(L, lc_pmain, &args); 2103 | if (status != 0) { 2104 | fputs(lua_tostring(L,-1), stderr); 2105 | } 2106 | 2107 | lua_close(L); 2108 | return 0; 2109 | } 2110 | ]]) 2111 | 2112 | return cast 2113 | end 2114 | M.genfull = genfull 2115 | 2116 | 2117 | -- First pass through AST . 2118 | -- Each `Id node is marked with field if it is used as an 2119 | -- upvalue. 2120 | -- Each `Function node is marked with field if it uses 2121 | -- at least one upvalue. 2122 | local function first_pass(ast) 2123 | -- Represents current lexical scope. 2124 | -- Maps variable name to variable info (see newvar). 2125 | local scope = {} 2126 | 2127 | local functions = {} 2128 | 2129 | local function newscope() 2130 | local saved_scope = scope 2131 | scope = setmetatable({}, {__index=scope}) 2132 | return saved_scope 2133 | end 2134 | 2135 | local function endscope(saved_scope) 2136 | scope = assert(saved_scope) 2137 | end 2138 | 2139 | local function newvar(id_ast) 2140 | assert(id_ast.tag == 'Id', id_ast) 2141 | local name = id_ast[1] 2142 | scope[name] = {function_level = #functions, ast = id_ast} 2143 | end 2144 | 2145 | local function usevar(name) 2146 | local varinfo = scope[name] 2147 | if varinfo and varinfo.function_level < #functions then 2148 | --DEBUG('upval:', varinfo.ast) 2149 | varinfo.ast.upvalue = true 2150 | for i=varinfo.function_level+1,#functions do 2151 | functions[i].uses_upvalue = true 2152 | end 2153 | end 2154 | end 2155 | 2156 | local function process(ast) 2157 | if ast.tag == nil or ast.tag == 'Do' then -- block 2158 | local saved_scope = newscope() 2159 | for _,stat_ast in ipairs(ast) do 2160 | process(stat_ast) 2161 | end 2162 | endscope(saved_scope) 2163 | elseif ast.tag == 'Set' then 2164 | for i=1,#ast[1] do process(ast[1][i]) end 2165 | for i=1,#ast[2] do process(ast[2][i]) end 2166 | elseif ast.tag == 'While' then 2167 | process(ast[1]) 2168 | local saved_scope = newscope() 2169 | process(ast[2]) 2170 | endscope(saved_scope) 2171 | elseif ast.tag == 'Repeat' then 2172 | local saved_scope = newscope() 2173 | process(ast[1]) 2174 | process(ast[2]) 2175 | endscope(saved_scope) 2176 | elseif ast.tag == 'If' then 2177 | for i=1,#ast do 2178 | if i % 2 == 0 or i == #ast then 2179 | local saved_scope = newscope() 2180 | process(ast[i]) 2181 | endscope(saved_scope) 2182 | else 2183 | process(ast[i]) 2184 | end 2185 | end 2186 | elseif ast.tag == 'Fornum' then 2187 | local saved_scope = newscope() 2188 | newvar(ast[1]) 2189 | for i=2,#ast do process(ast[i]) end 2190 | endscope(saved_scope) 2191 | elseif ast.tag == 'Forin' then 2192 | local saved_scope = newscope() 2193 | for i=1,#ast[1] do newvar(ast[1][i]) end 2194 | for i=1,#ast[2] do process(ast[2][i]) end 2195 | process(ast[#ast]) 2196 | endscope(saved_scope) 2197 | elseif ast.tag == 'Local' then 2198 | if ast[2] then 2199 | for i=1,#ast[2] do process(ast[2][i]) end 2200 | end 2201 | for i=1,#ast[1] do newvar(ast[1][i]) end 2202 | elseif ast.tag == 'Localrec' then 2203 | for i=1,#ast[1] do newvar(ast[1][i]) end 2204 | if ast[2] then 2205 | for i=1,#ast[2] do process(ast[2][i]) end 2206 | end 2207 | --metalua: elseif ast.tag == 'Goto' or ast.tag == 'Label' then 2208 | elseif ast.tag == 'Return' then 2209 | for i=1,#ast do process(ast[i]) end 2210 | elseif ast.tag == 'Break' then 2211 | elseif ast.tag == 'Nil' or ast.tag == 'Dots' or ast.tag == 'True' 2212 | or ast.tag == 'False' or ast.tag == 'Number' or ast.tag == 'String' 2213 | then 2214 | elseif ast.tag == 'Function' then 2215 | local saved_scope = newscope() 2216 | table.insert(functions, ast) 2217 | for i=1,#ast[1] do 2218 | if ast[1][i].tag ~= 'Dots' then newvar(ast[1][i]) end 2219 | end 2220 | process(ast[2]) 2221 | table.remove(functions) 2222 | endscope(saved_scope) 2223 | elseif ast.tag == 'Table' then 2224 | for i=1,#ast do process(ast[i]) end 2225 | elseif ast.tag == 'Pair' then 2226 | for i=1,2 do process(ast[i]) end 2227 | elseif ast.tag == 'Op' then 2228 | for i=2,#ast do process(ast[i]) end 2229 | elseif ast.tag == 'Paren' then 2230 | process(ast[1]) 2231 | -- metalua: elseif ast.tag == 'Stat' then 2232 | elseif ast.tag == 'Call' then 2233 | for i=1,#ast do process(ast[i]) end 2234 | elseif ast.tag == 'Invoke' then 2235 | process(ast[1]) 2236 | for i=3,#ast do process(ast[i]) end 2237 | elseif ast.tag == 'Index' then 2238 | for i=1,2 do process(ast[i]) end 2239 | elseif ast.tag == 'Id' then 2240 | usevar(ast[1]) 2241 | else 2242 | assert(false, ast.tag) 2243 | end 2244 | end 2245 | 2246 | 2247 | process(ast) 2248 | end 2249 | 2250 | 2251 | first_pass(ast) 2252 | 2253 | local cast = genfull(ast) 2254 | 2255 | return cast 2256 | 2257 | 2258 | --##------------------------------------------------------------------ 2259 | --## Note: this is a large function nesting many closures; 2260 | --## indentation of its contents is omitted. 2261 | --##------------------------------------------------------------------ 2262 | end 2263 | -- end of ast_to_cast 2264 | 2265 | 2266 | M.ast_to_cast = ast_to_cast 2267 | 2268 | return M 2269 | -------------------------------------------------------------------------------- /lib/lua2c/cast2string.lua: -------------------------------------------------------------------------------- 1 | -- 2 | -- cast2string.lua 3 | -- Converts C AST to C code string. 4 | -- 5 | -- (c) 2008 David Manura. Licensed in the same terms as Lua (MIT license). 6 | -- See included LICENSE file for full licensing details. 7 | 8 | local M = {} 9 | 10 | local _G = _G 11 | local assert = _G.assert 12 | local ipairs = _G.ipairs 13 | local math = _G.math 14 | local string = _G.string 15 | local table = _G.table 16 | local tostring = _G.tostring 17 | local type = _G.type 18 | local unpack = _G.unpack 19 | 20 | -- Indents lines of code. 21 | local function indentcode(code) 22 | local indent = ' ' 23 | code = code:gsub('\n', '\n' .. indent) 24 | code = code:gsub(indent .. '$', '') 25 | if code ~= "" then code = indent .. code end 26 | return code 27 | end 28 | 29 | 30 | -- Makes C comment of string s. 31 | local function ccomment(s) 32 | s = s:gsub('%*%/', '* /') 33 | s = s:gsub('%/%*', '/ *') 34 | s = '/* ' .. s:gsub('\n', '\n' .. ' * ') .. ' */' 35 | return s 36 | end 37 | 38 | 39 | -- Tests whether C AST node of given type should not have semicolon 40 | -- appended. 41 | local no_semicolon = { 42 | ['If'] = true, 43 | ['While'] = true, 44 | ['Functiondef'] = true, 45 | ['C'] = true 46 | } 47 | 48 | -- Converts C AST to C code string. 49 | local function cast_to_string(cast) 50 | -- DEBUG(type(cast) == 'table' and cast.tag or cast) 51 | if type(cast) ~= 'table' then 52 | if type(cast) =='number' then -- convenience function 53 | local s = 54 | (cast ~= cast) and '(0.0/0.0)' or 55 | (cast == math.huge) and 'HUGE_VAL' or 56 | (cast == -math.huge) and '-HUGE_VAL' or 57 | tostring(cast) 58 | --note: HUGE_VAL/-HUGE_VAL defined in math.h 59 | 60 | --IMPROVE: avoid 'warning: integer constant is too large for 61 | -- "long" type', at least in gcc. Make distinction between 62 | -- doubles and integers? 63 | --if not s:find('[Ee%.]') then 64 | -- s = s .. '.0' 65 | --end 66 | return s 67 | elseif type(cast) == 'string' then -- convenience function 68 | return string.format("%q", cast):gsub('\\\n', '\\n') 69 | else 70 | assert(false, type(cast)) 71 | end 72 | elseif cast.tag == 'C' or cast.tag == 'Id' then 73 | local ccode = cast[1] 74 | assert(type(ccode) == 'string', tostring(ccode)) 75 | return ccode 76 | elseif cast.tag == 'Op' then 77 | local opid, a_cast, b_cast = cast[1], cast[2], cast[3] 78 | local pa,pz = '(', ')' -- improve: sometimes can be avoided 79 | return pa .. cast_to_string(a_cast) .. 80 | ' ' .. opid .. ' ' .. cast_to_string(b_cast) .. pz 81 | elseif cast.tag == 'Include' then 82 | local name = cast[1] 83 | return '#include ' .. name 84 | elseif cast.tag == 'Let' then 85 | local id, val_cast = cast[1], cast[2] 86 | return "const int " .. id .. " = " .. cast_to_string(val_cast) 87 | elseif cast.tag == 'LetDouble' then 88 | local id, val_cast = cast[1], cast[2] 89 | return "const double " .. id .. " = " .. cast_to_string(val_cast) 90 | elseif cast.tag == 'LetMutableDouble' then 91 | local id, val_cast = cast[1], cast[2] 92 | return "double " .. id .. " = " .. cast_to_string(val_cast) 93 | elseif cast.tag == 'LetInt' then 94 | local id, val_cast = cast[1], cast[2] 95 | return "const int " .. id .. " = " .. cast_to_string(val_cast) 96 | elseif cast.tag == 'Enum' then 97 | local id, val_cast = cast[1], cast[2] 98 | return "enum { " .. id .. " = " .. tostring(val_cast) .. " }" 99 | elseif cast.tag == 'Not' then 100 | local a_ast = cast[1] 101 | local pa,pz = '(', ')' -- improve: sometimes can be avoided 102 | return '!' .. pa .. cast_to_string(a_ast) .. pz 103 | elseif cast.tag == 'Return' then 104 | local a_ast = cast[1] 105 | return 'return' .. (a_ast and ' ' .. cast_to_string(a_ast) or '') 106 | elseif cast.tag == 'Break' then 107 | return 'break' 108 | elseif cast.tag == 'Call' then 109 | local args = {tag='C'} 110 | for i=2,#cast do 111 | args[#args+1] = cast_to_string(cast[i]) 112 | end 113 | return cast[1] .. '(' .. table.concat(args, ',') .. ')' 114 | elseif cast.tag == 'CallL' then 115 | local args = {tag='C', 'L'} 116 | for i=2,#cast do 117 | args[#args+1] = cast_to_string(cast[i]) 118 | end 119 | return cast[1] .. '(' .. table.concat(args, ',') .. ')' 120 | elseif cast.tag == 'Def' then 121 | local ts = {} 122 | for i,stat_cast in ipairs(cast) do 123 | ts[i] = cast_to_string(stat_cast) .. '\n\n' 124 | end 125 | local ccode = table.concat(ts) 126 | return ccode 127 | elseif cast.tag == nil then -- block 128 | local ts = {} 129 | if cast.comment then 130 | ts[#ts+1] = '\n' .. ccomment(cast.comment) .. '\n' 131 | end 132 | for i,stat_cast in ipairs(cast) do 133 | local comment = '' 134 | if stat_cast.comment then 135 | comment = '\n' .. ccomment(stat_cast.comment) .. '\n' 136 | end 137 | local postcomment = '' 138 | if stat_cast.postcomment then 139 | postcomment = ccomment(stat_cast.postcomment) .. '\n' 140 | end 141 | local semi = no_semicolon[stat_cast.tag] and '' or ';' 142 | ts[#ts+1] = comment .. cast_to_string(stat_cast) .. semi .. '\n' .. 143 | postcomment 144 | end 145 | if cast.postcomment then 146 | ts[#ts+1] = '\n' .. ccomment(cast.postcomment) .. '\n' 147 | end 148 | 149 | local ccode = indentcode(table.concat(ts)) 150 | return ccode 151 | elseif cast.tag == 'If' then 152 | local ccode = '' 153 | for i=2,#cast,2 do 154 | if i ~= 2 then ccode = ccode .. 'else ' end 155 | ccode = ccode .. 'if (' .. cast_to_string(cast[i-1]) .. ') {\n' .. 156 | cast_to_string(cast[i]) .. '}' 157 | end 158 | if #cast % 2 == 1 then 159 | ccode = ccode .. '\nelse {\n' .. cast_to_string(cast[#cast]) .. '}' 160 | end 161 | return ccode 162 | elseif cast.tag == 'While' then 163 | local expr_cast, block_cast = cast[1], cast[2] 164 | local ccode = 'while (' .. cast_to_string(expr_cast) .. ') {\n' .. 165 | cast_to_string(block_cast) .. '}' 166 | return ccode 167 | elseif cast.tag == 'Functiondef' then 168 | local id, body_cast = cast[1], cast[2] 169 | local comment = '' 170 | if cast.comment then 171 | comment = ccomment(cast.comment) .. '\n' 172 | end 173 | local postcomment = '' 174 | if cast.postcomment then 175 | postcomment = ccomment(cast.postcomment) .. '\n' 176 | end 177 | local ccode = 178 | comment .. 179 | 'static int ' .. id .. ' (lua_State * L) {\n' .. 180 | cast_to_string(body_cast) .. '}\n' .. postcomment 181 | return ccode 182 | elseif cast.tag:find'lua_' == 1 then -- convenience function 183 | return cast_to_string{tag='CallL', cast.tag, unpack(cast)} 184 | else 185 | assert(false, cast.tag) 186 | end 187 | end 188 | M.cast_to_string = cast_to_string 189 | 190 | return M 191 | 192 | 193 | -------------------------------------------------------------------------------- /lib/metalua/base.lua: -------------------------------------------------------------------------------- 1 | ---------------------------------------------------------------------- 2 | ---------------------------------------------------------------------- 3 | -- 4 | -- Base library extension 5 | -- 6 | ---------------------------------------------------------------------- 7 | ---------------------------------------------------------------------- 8 | 9 | if not metalua then rawset(getfenv(), 'metalua', { }) end 10 | metalua.version = "v-0.5" 11 | 12 | if not rawpairs then 13 | rawpairs, rawipairs, rawtype = pairs, ipairs, type 14 | end 15 | 16 | function pairs(x) 17 | assert(type(x)=='table', 'pairs() expects a table') 18 | local mt = getmetatable(x) 19 | if mt then 20 | local mtp = mt.__pairs 21 | if mtp then return mtp(x) end 22 | end 23 | return rawpairs(x) 24 | end 25 | 26 | function ipairs(x) 27 | assert(type(x)=='table', 'ipairs() expects a table') 28 | local mt = getmetatable(x) 29 | if mt then 30 | local mti = mt.__ipairs 31 | if mti then return mti(x) end 32 | end 33 | return rawipairs(x) 34 | end 35 | 36 | --[[ 37 | function type(x) 38 | local mt = getmetatable(x) 39 | if mt then 40 | local mtt = mt.__type 41 | if mtt then return mtt end 42 | end 43 | return rawtype(x) 44 | end 45 | ]] 46 | 47 | function min (a, ...) 48 | for n in values{...} do if na then a=n end end 54 | return a 55 | end 56 | 57 | function o (...) 58 | local args = {...} 59 | local function g (...) 60 | local result = {...} 61 | for i=#args, 1, -1 do result = {args[i](unpack(result))} end 62 | return unpack (result) 63 | end 64 | return g 65 | end 66 | 67 | function id (...) return ... end 68 | function const (k) return function () return k end end 69 | 70 | function printf(...) return print(string.format(...)) end 71 | function eprintf(...) 72 | io.stderr:write(string.format(...).."\n") 73 | end 74 | 75 | function ivalues (x) 76 | assert(type(x)=='table', 'ivalues() expects a table') 77 | local i = 1 78 | local function iterator () 79 | local r = x[i]; i=i+1; return r 80 | end 81 | return iterator 82 | end 83 | 84 | 85 | function values (x) 86 | assert(type(x)=='table', 'values() expects a table') 87 | local function iterator (state) 88 | local it 89 | state.content, it = next(state.list, state.content) 90 | return it 91 | end 92 | return iterator, { list = x } 93 | end 94 | 95 | function keys (x) 96 | assert(type(x)=='table', 'keys() expects a table') 97 | local function iterator (state) 98 | local it = next(state.list, state.content) 99 | state.content = it 100 | return it 101 | end 102 | return iterator, { list = x } 103 | end 104 | 105 | -------------------------------------------------------------------------------- /lib/metalua/runtime.lua: -------------------------------------------------------------------------------- 1 | require 'metalua.base' 2 | require 'metalua.table2' 3 | require 'metalua.string2' 4 | -------------------------------------------------------------------------------- /lib/metalua/string2.lua: -------------------------------------------------------------------------------- 1 | 2 | ---------------------------------------------------------------------- 3 | ---------------------------------------------------------------------- 4 | -- 5 | -- String module extension 6 | -- 7 | ---------------------------------------------------------------------- 8 | ---------------------------------------------------------------------- 9 | 10 | -- Courtesy of lua-users.org 11 | function string.split(str, pat) 12 | local t = {} 13 | local fpat = "(.-)" .. pat 14 | local last_end = 1 15 | local s, e, cap = string.find(str, fpat, 1) 16 | while s do 17 | if s ~= 1 or cap ~= "" then 18 | table.insert(t,cap) 19 | end 20 | last_end = e+1 21 | s, e, cap = string.find(str, fpat, last_end) 22 | end 23 | if last_end <= string.len(str) then 24 | cap = string.sub(str, last_end) 25 | table.insert(t, cap) 26 | end 27 | return t 28 | end 29 | 30 | -- "match" is regularly used as a keyword for pattern matching, 31 | -- so here is an always available substitute. 32 | string.strmatch = string["match"] 33 | 34 | -- change a compiled string into a function 35 | function string.undump(str) 36 | if str:strmatch '^\027LuaQ' or str:strmatch '^#![^\n]+\n\027LuaQ' then 37 | local f = (lua_loadstring or loadstring)(str) 38 | return f 39 | else 40 | error "Not a chunk dump" 41 | end 42 | end 43 | 44 | return string -------------------------------------------------------------------------------- /lib/metalua/table2.lua: -------------------------------------------------------------------------------- 1 | --------------------------------------------------------------------- 2 | ---------------------------------------------------------------------- 3 | -- 4 | -- Table module extension 5 | -- 6 | ---------------------------------------------------------------------- 7 | ---------------------------------------------------------------------- 8 | 9 | -- todo: table.scan (scan1?) fold1? flip? 10 | 11 | function table.transpose(t) 12 | local tt = { } 13 | for a, b in pairs(t) do tt[b] = a end 14 | return tt 15 | end 16 | 17 | function table.iforeach(f, ...) 18 | -- assert (type (f) == "function") [wouldn't allow metamethod __call] 19 | local nargs = select("#", ...) 20 | if nargs==1 then -- Quick iforeach (most common case), just one table arg 21 | local t = ... 22 | assert (type (t) == "table") 23 | for i = 1, #t do 24 | local result = f (t[i]) 25 | -- If the function returns non-false, stop iteration 26 | if result then return result end 27 | end 28 | else -- advanced case: boundaries and/or multiple tables 29 | -- 1 - find boundaries if any 30 | local args, fargs, first, last, arg1 = {...}, { } 31 | if type(args[1]) ~= "number" then first, arg1 = 1, 1 32 | elseif type(args[2]) ~= "number" then first, last, arg1 = 1, args[1], 2 33 | else first, last, i = args[1], args[2], 3 end 34 | assert (nargs > arg1) 35 | -- 2 - determine upper boundary if not given 36 | if not last then for i = arg1, nargs do 37 | assert (type (args[i]) == "table") 38 | last = max (#args[i], last) 39 | end end 40 | -- 3 - perform the iteration 41 | for i = first, last do 42 | for j = arg1, nargs do fargs[j] = args[j][i] end -- build args list 43 | local result = f (unpack (fargs)) -- here is the call 44 | -- If the function returns non-false, stop iteration 45 | if result then return result end 46 | end 47 | end 48 | end 49 | 50 | function table.imap (f, ...) 51 | local result, idx = { }, 1 52 | local function g(...) result[idx] = f(...); idx=idx+1 end 53 | table.iforeach(g, ...) 54 | return result 55 | end 56 | 57 | function table.ifold (f, acc, ...) 58 | local function g(...) acc = f (acc,...) end 59 | table.iforeach (g, ...) 60 | return acc 61 | end 62 | 63 | -- function table.ifold1 (f, ...) 64 | -- return table.ifold (f, acc, 2, false, ...) 65 | -- end 66 | 67 | function table.izip(...) 68 | local function g(...) return {...} end 69 | return table.imap(g, ...) 70 | end 71 | 72 | function table.ifilter(f, t) 73 | local yes, no = { }, { } 74 | for i=1,#t do table.insert (f(t[i]) and yes or no, t[i]) end 75 | return yes, no 76 | end 77 | 78 | function table.icat(...) 79 | local result = { } 80 | for t in values {...} do 81 | for x in values (t) do 82 | table.insert (result, x) 83 | end 84 | end 85 | return result 86 | end 87 | 88 | function table.iflatten (x) return table.icat (unpack (x)) end 89 | 90 | function table.irev (t) 91 | local result, nt = { }, #t 92 | for i=0, nt-1 do result[nt-i] = t[i+1] end 93 | return result 94 | end 95 | 96 | function table.isub (t, ...) 97 | local ti, u = table.insert, { } 98 | local args, nargs = {...}, select("#", ...) 99 | for i=1, nargs/2 do 100 | local a, b = args[2*i-1], args[2*i] 101 | for i=a, b, a<=b and 1 or -1 do ti(u, t[i]) end 102 | end 103 | return u 104 | end 105 | 106 | function table.iall (f, ...) 107 | local result = true 108 | local function g(...) return not f(...) end 109 | return not table.iforeach(g, ...) 110 | --return result 111 | end 112 | 113 | function table.iany (f, ...) 114 | local function g(...) return not f(...) end 115 | return not table.iall(g, ...) 116 | end 117 | 118 | function table.shallow_copy(x) 119 | local y={ } 120 | for k, v in pairs(x) do y[k]=v end 121 | return y 122 | end 123 | 124 | -- Warning, this is implementation dependent: it relies on 125 | -- the fact the [next()] enumerates the array-part before the hash-part. 126 | function table.cat(...) 127 | local y={ } 128 | for x in values{...} do 129 | -- cat array-part 130 | for _, v in ipairs(x) do table.insert(y,v) end 131 | -- cat hash-part 132 | local lx, k = #x 133 | if lx>0 then k=next(x,lx) else k=next(x) end 134 | while k do y[k]=x[k]; k=next(x,k) end 135 | end 136 | return y 137 | end 138 | 139 | function table.deep_copy(x) 140 | local tracker = { } 141 | local function aux (x) 142 | if type(x) == "table" then 143 | local y=tracker[x] 144 | if y then return y end 145 | y = { }; tracker[x] = y 146 | setmetatable (y, getmetatable (x)) 147 | for k,v in pairs(x) do y[aux(k)] = aux(v) end 148 | return y 149 | else return x end 150 | end 151 | return aux(x) 152 | end 153 | 154 | function table.override(dst, src) 155 | for k, v in pairs(src) do dst[k] = v end 156 | for i = #src+1, #dst do dst[i] = nil end 157 | return dst 158 | end 159 | 160 | 161 | function table.range(a,b,c) 162 | if not b then assert(not(c)); b=a; a=1 163 | elseif not c then c = (b>=a) and 1 or -1 end 164 | local result = { } 165 | for i=a, b, c do table.insert(result, i) end 166 | return result 167 | end 168 | 169 | -- FIXME: new_indent seems to be always nil?! 170 | -- FIXME: accumulator function should be configurable, 171 | -- so that print() doesn't need to bufferize the whole string 172 | -- before starting to print. 173 | function table.tostring(t, ...) 174 | local PRINT_HASH, HANDLE_TAG, FIX_INDENT, LINE_MAX, INITIAL_INDENT = true, true 175 | for _, x in ipairs {...} do 176 | if type(x) == "number" then 177 | if not LINE_MAX then LINE_MAX = x 178 | else INITIAL_INDENT = x end 179 | elseif x=="nohash" then PRINT_HASH = false 180 | elseif x=="notag" then HANDLE_TAG = false 181 | else 182 | local n = string['match'](x, "^indent%s*(%d*)$") 183 | if n then FIX_INDENT = tonumber(n) or 3 end 184 | end 185 | end 186 | LINE_MAX = LINE_MAX or math.huge 187 | INITIAL_INDENT = INITIAL_INDENT or 1 188 | 189 | local current_offset = 0 -- indentation level 190 | local xlen_cache = { } -- cached results for xlen() 191 | local acc_list = { } -- Generated bits of string 192 | local function acc(...) -- Accumulate a bit of string 193 | local x = table.concat{...} 194 | current_offset = current_offset + #x 195 | table.insert(acc_list, x) 196 | end 197 | local function valid_id(x) 198 | -- FIXME: we should also reject keywords; but the list of 199 | -- current keywords is not fixed in metalua... 200 | return type(x) == "string" 201 | and string['match'](x, "^[a-zA-Z_][a-zA-Z0-9_]*$") 202 | end 203 | 204 | -- Compute the number of chars it would require to display the table 205 | -- on a single line. Helps to decide whether some carriage returns are 206 | -- required. Since the size of each sub-table is required many times, 207 | -- it's cached in [xlen_cache]. 208 | local xlen_type = { } 209 | local function xlen(x, nested) 210 | nested = nested or { } 211 | if x==nil then return #"nil" end 212 | --if nested[x] then return #tostring(x) end -- already done in table 213 | local len = xlen_cache[x] 214 | if len then return len end 215 | local f = xlen_type[type(x)] 216 | if not f then return #tostring(x) end 217 | len = f (x, nested) 218 | xlen_cache[x] = len 219 | return len 220 | end 221 | 222 | -- optim: no need to compute lengths if I'm not going to use them 223 | -- anyway. 224 | if LINE_MAX == math.huge then xlen = function() return 0 end end 225 | 226 | xlen_type["nil"] = function () return 3 end 227 | function xlen_type.number (x) return #tostring(x) end 228 | function xlen_type.boolean (x) return x and 4 or 5 end 229 | function xlen_type.string (x) return #string.format("%q",x) end 230 | function xlen_type.table (adt, nested) 231 | 232 | -- Circular references detection 233 | if nested [adt] then return #tostring(adt) end 234 | nested [adt] = true 235 | 236 | local has_tag = HANDLE_TAG and valid_id(adt.tag) 237 | local alen = #adt 238 | local has_arr = alen>0 239 | local has_hash = false 240 | local x = 0 241 | 242 | if PRINT_HASH then 243 | -- first pass: count hash-part 244 | for k, v in pairs(adt) do 245 | if k=="tag" and has_tag then 246 | -- this is the tag -> do nothing! 247 | elseif type(k)=="number" and k<=alen and math.fmod(k,1)==0 then 248 | -- array-part pair -> do nothing! 249 | else 250 | has_hash = true 251 | if valid_id(k) then x=x+#k 252 | else x = x + xlen (k, nested) + 2 end -- count surrounding brackets 253 | x = x + xlen (v, nested) + 5 -- count " = " and ", " 254 | end 255 | end 256 | end 257 | 258 | for i = 1, alen do x = x + xlen (adt[i], nested) + 2 end -- count ", " 259 | 260 | nested[adt] = false -- No more nested calls 261 | 262 | if not (has_tag or has_arr or has_hash) then return 3 end 263 | if has_tag then x=x+#adt.tag+1 end 264 | if not (has_arr or has_hash) then return x end 265 | if not has_hash and alen==1 and type(adt[1])~="table" then 266 | return x-2 -- substract extraneous ", " 267 | end 268 | return x+2 -- count "{ " and " }", substract extraneous ", " 269 | end 270 | 271 | -- Recursively print a (sub) table at given indentation level. 272 | -- [newline] indicates whether newlines should be inserted. 273 | local function rec (adt, nested, indent) 274 | if not FIX_INDENT then indent = current_offset end 275 | local function acc_newline() 276 | acc ("\n"); acc (string.rep (" ", indent)) 277 | current_offset = indent 278 | end 279 | local x = { } 280 | x["nil"] = function() acc "nil" end 281 | function x.number() acc (tostring (adt)) end 282 | --function x.string() acc (string.format ("%q", adt)) end 283 | function x.string() acc ((string.format ("%q", adt):gsub("\\\n", "\\n"))) end 284 | function x.boolean() acc (adt and "true" or "false") end 285 | function x.table() 286 | if nested[adt] then acc(tostring(adt)); return end 287 | nested[adt] = true 288 | 289 | 290 | local has_tag = HANDLE_TAG and valid_id(adt.tag) 291 | local alen = #adt 292 | local has_arr = alen>0 293 | local has_hash = false 294 | 295 | if has_tag then acc("`"); acc(adt.tag) end 296 | 297 | -- First pass: handle hash-part 298 | if PRINT_HASH then 299 | for k, v in pairs(adt) do 300 | -- pass if the key belongs to the array-part or is the "tag" field 301 | if not (k=="tag" and HANDLE_TAG) and 302 | not (type(k)=="number" and k<=alen and math.fmod(k,1)==0) then 303 | 304 | -- Is it the first time we parse a hash pair? 305 | if not has_hash then 306 | acc "{ " 307 | if not FIX_INDENT then indent = current_offset end 308 | else acc ", " end 309 | 310 | -- Determine whether a newline is required 311 | local is_id, expected_len = valid_id(k) 312 | if is_id then expected_len = #k + xlen (v, nested) + #" = , " 313 | else expected_len = xlen (k, nested) + 314 | xlen (v, nested) + #"[] = , " end 315 | if has_hash and expected_len + current_offset > LINE_MAX 316 | then acc_newline() end 317 | 318 | -- Print the key 319 | if is_id then acc(k); acc " = " 320 | else acc "["; rec (k, nested, indent+(FIX_INDENT or 0)); acc "] = " end 321 | 322 | -- Print the value 323 | rec (v, nested, indent+(FIX_INDENT or 0)) 324 | has_hash = true 325 | end 326 | end 327 | end 328 | 329 | -- Now we know whether there's a hash-part, an array-part, and a tag. 330 | -- Tag and hash-part are already printed if they're present. 331 | if not has_tag and not has_hash and not has_arr then acc "{ }"; 332 | elseif has_tag and not has_hash and not has_arr then -- nothing, tag already in acc 333 | else 334 | assert (has_hash or has_arr) 335 | local no_brace = false 336 | if has_hash and has_arr then acc ", " 337 | elseif has_tag and not has_hash and alen==1 and type(adt[1])~="table" then 338 | -- No brace required; don't print "{", remember not to print "}" 339 | acc (" "); rec (adt[1], nested, indent+(FIX_INDENT or 0)) 340 | no_brace = true 341 | elseif not has_hash then 342 | -- Braces required, but not opened by hash-part handler yet 343 | acc "{ " 344 | if not FIX_INDENT then indent = current_offset end 345 | end 346 | 347 | -- 2nd pass: array-part 348 | if not no_brace and has_arr then 349 | rec (adt[1], nested, indent+(FIX_INDENT or 0)) 350 | for i=2, alen do 351 | acc ", "; 352 | if current_offset + xlen (adt[i], { }) > LINE_MAX 353 | then acc_newline() end 354 | rec (adt[i], nested, indent+(FIX_INDENT or 0)) 355 | end 356 | end 357 | if not no_brace then acc " }" end 358 | end 359 | nested[adt] = false -- No more nested calls 360 | end 361 | local y = x[type(adt)] 362 | if y then y() else acc(tostring(adt)) end 363 | end 364 | --printf("INITIAL_INDENT = %i", INITIAL_INDENT) 365 | current_offset = INITIAL_INDENT or 0 366 | rec(t, { }, 0) 367 | return table.concat (acc_list) 368 | end 369 | 370 | function table.print(...) return print(table.tostring(...)) end 371 | 372 | return table -------------------------------------------------------------------------------- /lib/mlp_expr.lua: -------------------------------------------------------------------------------- 1 | ---------------------------------------------------------------------- 2 | -- Metalua: $Id: mlp_expr.lua,v 1.7 2006/11/15 09:07:50 fab13n Exp $ 3 | -- 4 | -- Summary: metalua parser, expression parser. This is part of the 5 | -- definition of module [mlp]. 6 | -- 7 | ---------------------------------------------------------------------- 8 | -- 9 | -- Copyright (c) 2006, Fabien Fleutot . 10 | -- 11 | -- This software is released under the MIT Licence, see licence.txt 12 | -- for details. 13 | -- 14 | ---------------------------------------------------------------------- 15 | -- History: 16 | -- $Log: mlp_expr.lua,v $ 17 | -- Revision 1.7 2006/11/15 09:07:50 fab13n 18 | -- debugged meta operators. 19 | -- Added command line options handling. 20 | -- 21 | -- Revision 1.6 2006/11/10 02:11:17 fab13n 22 | -- compiler faithfulness to 5.1 improved 23 | -- gg.expr extended 24 | -- mlp.expr refactored 25 | -- 26 | -- Revision 1.5 2006/11/09 09:39:57 fab13n 27 | -- some cleanup 28 | -- 29 | -- Revision 1.4 2006/11/07 21:29:02 fab13n 30 | -- improved quasi-quoting 31 | -- 32 | -- Revision 1.3 2006/11/07 04:38:00 fab13n 33 | -- first bootstrapping version. 34 | -- 35 | -- Revision 1.2 2006/11/05 15:08:34 fab13n 36 | -- updated code generation, to be compliant with 5.1 37 | -- 38 | ---------------------------------------------------------------------- 39 | 40 | -------------------------------------------------------------------------------- 41 | -- 42 | -- Exported API: 43 | -- * [mlp.expr()] 44 | -- * [mlp.expr_list()] 45 | -- * [mlp.func_val()] 46 | -- 47 | -------------------------------------------------------------------------------- 48 | 49 | --require "gg" 50 | --require "mlp_misc" 51 | --require "mlp_table" 52 | --require "mlp_meta" 53 | 54 | -------------------------------------------------------------------------------- 55 | -- These function wrappers (eta-expansions ctually) are just here to break 56 | -- some circular dependencies between mlp_xxx.lua files. 57 | -------------------------------------------------------------------------------- 58 | local function _expr (lx) return mlp.expr (lx) end 59 | local function _table_content (lx) return mlp.table_content (lx) end 60 | local function block (lx) return mlp.block (lx) end 61 | local function stat (lx) return mlp.stat (lx) end 62 | 63 | module ("mlp", package.seeall) 64 | 65 | -------------------------------------------------------------------------------- 66 | -- Non-empty expression list. Actually, this isn't used here, but that's 67 | -- handy to give to users. 68 | -------------------------------------------------------------------------------- 69 | expr_list = gg.list{ _expr, separators = "," } 70 | 71 | -------------------------------------------------------------------------------- 72 | -- Helpers for function applications / method applications 73 | -------------------------------------------------------------------------------- 74 | func_args_content = gg.list { 75 | name = "function arguments", 76 | _expr, separators = ",", terminators = ")" } 77 | 78 | -- Used to parse methods 79 | method_args = gg.multisequence{ 80 | name = "function argument(s)", 81 | { "{", table_content, "}" }, 82 | { "(", func_args_content, ")", builder = fget(1) }, 83 | default = function(lx) local r = opt_string(lx); return r and {r} or { } end } 84 | 85 | -------------------------------------------------------------------------------- 86 | -- [func_val] parses a function, from opening parameters parenthese to 87 | -- "end" keyword included. Used for anonymous functions as well as 88 | -- function declaration statements (both local and global). 89 | -- 90 | -- It's wrapped in a [_func_val] eta expansion, so that when expr 91 | -- parser uses the latter, they will notice updates of [func_val] 92 | -- definitions. 93 | -------------------------------------------------------------------------------- 94 | func_params_content = gg.list{ name="function parameters", 95 | gg.multisequence{ { "...", builder = "Dots" }, default = id }, 96 | separators = ",", terminators = {")", "|"} } 97 | 98 | local _func_params_content = function (lx) return func_params_content(lx) end 99 | 100 | func_val = gg.sequence { name="function body", 101 | "(", func_params_content, ")", block, "end", builder = "Function" } 102 | 103 | local _func_val = function (lx) return func_val(lx) end 104 | 105 | -------------------------------------------------------------------------------- 106 | -- Default parser for primary expressions 107 | -------------------------------------------------------------------------------- 108 | function id_or_literal (lx) 109 | local a = lx:next() 110 | if a.tag~="Id" and a.tag~="String" and a.tag~="Number" then 111 | gg.parse_error (lx, "Unexpected expr token %s", 112 | _G.table.tostring (a, 'nohash')) 113 | end 114 | return a 115 | end 116 | 117 | 118 | -------------------------------------------------------------------------------- 119 | -- Builder generator for operators. Wouldn't be worth it if "|x|" notation 120 | -- were allowed, but then lua 5.1 wouldn't compile it 121 | -------------------------------------------------------------------------------- 122 | 123 | -- opf1 = |op| |_,a| `Op{ op, a } 124 | local function opf1 (op) return 125 | function (_,a) return { tag="Op", op, a } end end 126 | 127 | -- opf2 = |op| |a,_,b| `Op{ op, a, b } 128 | local function opf2 (op) return 129 | function (a,_,b) return { tag="Op", op, a, b } end end 130 | 131 | -- opf2r = |op| |a,_,b| `Op{ op, b, a } -- (args reversed) 132 | local function opf2r (op) return 133 | function (a,_,b) return { tag="Op", op, b, a } end end 134 | 135 | local function op_ne(a, _, b) 136 | -- The first version guarantees to return the same code as Lua, 137 | -- but it relies on the non-standard 'ne' operator, which has been 138 | -- suppressed from the official AST grammar (although still supported 139 | -- in practice by the compiler). 140 | -- return { tag="Op", "ne", a, b } 141 | return { tag="Op", "not", { tag="Op", "eq", a, b, lineinfo= { 142 | first = a.lineinfo.first, last = b.lineinfo.last } } } 143 | end 144 | 145 | 146 | -------------------------------------------------------------------------------- 147 | -- 148 | -- complete expression 149 | -- 150 | -------------------------------------------------------------------------------- 151 | 152 | -- FIXME: set line number. In [expr] transformers probably 153 | 154 | expr = gg.expr { name = "expression", 155 | 156 | primary = gg.multisequence{ name="expr primary", 157 | { "(", _expr, ")", builder = "Paren" }, 158 | { "function", _func_val, builder = fget(1) }, 159 | { "-{", splice_content, "}", builder = fget(1) }, 160 | { "+{", quote_content, "}", builder = fget(1) }, 161 | { "nil", builder = "Nil" }, 162 | { "true", builder = "True" }, 163 | { "false", builder = "False" }, 164 | { "...", builder = "Dots" }, 165 | table, 166 | default = id_or_literal }, 167 | 168 | infix = { name="expr infix op", 169 | { "+", prec = 60, builder = opf2 "add" }, 170 | { "-", prec = 60, builder = opf2 "sub" }, 171 | { "*", prec = 70, builder = opf2 "mul" }, 172 | { "/", prec = 70, builder = opf2 "div" }, 173 | { "%", prec = 70, builder = opf2 "mod" }, 174 | { "^", prec = 90, builder = opf2 "pow", assoc = "right" }, 175 | { "..", prec = 40, builder = opf2 "concat", assoc = "right" }, 176 | { "==", prec = 30, builder = opf2 "eq" }, 177 | { "~=", prec = 30, builder = op_ne }, 178 | { "<", prec = 30, builder = opf2 "lt" }, 179 | { "<=", prec = 30, builder = opf2 "le" }, 180 | { ">", prec = 30, builder = opf2r "lt" }, 181 | { ">=", prec = 30, builder = opf2r "le" }, 182 | { "and",prec = 20, builder = opf2 "and" }, 183 | { "or", prec = 10, builder = opf2 "or" } }, 184 | 185 | prefix = { name="expr prefix op", 186 | { "not", prec = 80, builder = opf1 "not" }, 187 | { "#", prec = 80, builder = opf1 "len" }, 188 | { "-", prec = 80, builder = opf1 "unm" } }, 189 | 190 | suffix = { name="expr suffix op", 191 | { "[", _expr, "]", builder = function (tab, idx) 192 | return {tag="Index", tab, idx[1]} end}, 193 | { ".", id, builder = function (tab, field) 194 | return {tag="Index", tab, id2string(field[1])} end }, 195 | { "(", func_args_content, ")", builder = function(f, args) 196 | return {tag="Call", f, unpack(args[1])} end }, 197 | { "{", _table_content, "}", builder = function (f, arg) 198 | return {tag="Call", f, arg[1]} end}, 199 | { ":", id, method_args, builder = function (obj, post) 200 | return {tag="Invoke", obj, id2string(post[1]), unpack(post[2])} end}, 201 | { "+{", quote_content, "}", builder = function (f, arg) 202 | return {tag="Call", f, arg[1] } end }, 203 | default = { name="opt_string_arg", parse = mlp.opt_string, builder = function(f, arg) 204 | return {tag="Call", f, arg } end } } } 205 | -------------------------------------------------------------------------------- /lib/mlp_ext.lua: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- 2 | -- 3 | -- Non-Lua syntax extensions 4 | -- 5 | -------------------------------------------------------------------------------- 6 | 7 | module ("mlp", package.seeall) 8 | 9 | -------------------------------------------------------------------------------- 10 | -- Alebraic Datatypes 11 | -------------------------------------------------------------------------------- 12 | local function adt (lx) 13 | local tagval = id (lx) [1] 14 | local tagkey = {tag="Pair", {tag="String", "tag"}, {tag="String", tagval} } 15 | if lx:peek().tag == "String" or lx:peek().tag == "Number" then 16 | return { tag="Table", tagkey, lx:next() } 17 | elseif lx:is_keyword (lx:peek(), "{") then 18 | local x = table (lx) 19 | _G.table.insert (x, 1, tagkey) 20 | return x 21 | else return { tag="Table", tagkey } end 22 | end 23 | 24 | expr:add{ "`", adt, builder = fget(1) } 25 | 26 | -------------------------------------------------------------------------------- 27 | -- Anonymous lambda 28 | -------------------------------------------------------------------------------- 29 | local lambda_expr = gg.sequence{ 30 | "|", func_params_content, "|", expr, 31 | builder= function (x) 32 | local li = x[2].lineinfo 33 | return { tag="Function", x[1], 34 | { {tag="Return", x[2], lineinfo=li }, lineinfo=li } } 35 | end } 36 | 37 | -- In an earlier version, lambda_expr took an expr_list rather than an expr 38 | -- after the 2nd bar. However, it happened to be much more of a burden than an 39 | -- help, So finally I disabled it. If you want to return several results, 40 | -- use the long syntax. 41 | -------------------------------------------------------------------------------- 42 | -- local lambda_expr = gg.sequence{ 43 | -- "|", func_params_content, "|", expr_list, 44 | -- builder= function (x) 45 | -- return {tag="Function", x[1], { {tag="Return", unpack(x[2]) } } } end } 46 | 47 | expr:add (lambda_expr) 48 | 49 | -------------------------------------------------------------------------------- 50 | -- Allows to write "a `f` b" instead of "f(a, b)". Taken from Haskell. 51 | -- This is not part of Lua 5.1 syntax, so it's added to the expression 52 | -- afterwards, so that it's easier to disable. 53 | -------------------------------------------------------------------------------- 54 | local function expr_in_backquotes (lx) return expr(lx, 35) end 55 | 56 | expr.infix:add{ name = "infix function", 57 | "`", expr_in_backquotes, "`", prec = 35, assoc="left", 58 | builder = function(a, op, b) return {tag="Call", op[1], a, b} end } 59 | 60 | 61 | -------------------------------------------------------------------------------- 62 | -- table.override assignment 63 | -------------------------------------------------------------------------------- 64 | 65 | mlp.lexer:add "<-" 66 | stat.assignments["<-"] = function (a, b) 67 | assert( #a==1 and #b==1, "No multi-args for '<-'") 68 | return { tag="Call", { tag="Index", { tag="Id", "table" }, 69 | { tag="String", "override" } }, 70 | a[1], b[1]} 71 | end 72 | 73 | -------------------------------------------------------------------------------- 74 | -- C-style op+assignments 75 | -------------------------------------------------------------------------------- 76 | local function op_assign(kw, op) 77 | local function rhs(a, b) 78 | return { tag="Op", op, a, b } 79 | end 80 | local function f(a,b) 81 | return { tag="Set", a, _G.table.imap(rhs, a, b) } 82 | end 83 | mlp.lexer:add (kw) 84 | mlp.stat.assignments[kw] = f 85 | end 86 | 87 | _G.table.iforeach (op_assign, 88 | {"+=", "-=", "*=", "/="}, 89 | {"add", "sub", "mul", "div"}) -------------------------------------------------------------------------------- /lib/mlp_lexer.lua: -------------------------------------------------------------------------------- 1 | ---------------------------------------------------------------------- 2 | -- Metalua: $Id: mll.lua,v 1.3 2006/11/15 09:07:50 fab13n Exp $ 3 | -- 4 | -- Summary: Source file lexer. ~~Currently only works on strings. 5 | -- Some API refactoring is needed. 6 | -- 7 | ---------------------------------------------------------------------- 8 | -- 9 | -- Copyright (c) 2006-2007, Fabien Fleutot . 10 | -- 11 | -- This software is released under the MIT Licence, see licence.txt 12 | -- for details. 13 | -- 14 | ---------------------------------------------------------------------- 15 | 16 | module ("mlp", package.seeall) 17 | 18 | require "lexer" 19 | 20 | local mlp_lexer = lexer.lexer:clone() 21 | 22 | local keywords = { 23 | "and", "break", "do", "else", "elseif", 24 | "end", "false", "for", "function", "if", 25 | "in", "local", "nil", "not", "or", "repeat", 26 | "return", "then", "true", "until", "while", 27 | "...", "..", "==", ">=", "<=", "~=", 28 | "+{", "-{" } 29 | 30 | for w in values(keywords) do mlp_lexer:add(w) end 31 | 32 | _M.lexer = mlp_lexer 33 | -------------------------------------------------------------------------------- /lib/mlp_meta.lua: -------------------------------------------------------------------------------- 1 | ---------------------------------------------------------------------- 2 | -- Metalua: $Id: mlp_meta.lua,v 1.4 2006/11/15 09:07:50 fab13n Exp $ 3 | -- 4 | -- Summary: Meta-operations: AST quasi-quoting and splicing 5 | -- 6 | ---------------------------------------------------------------------- 7 | -- 8 | -- Copyright (c) 2006, Fabien Fleutot . 9 | -- 10 | -- This software is released under the MIT Licence, see licence.txt 11 | -- for details. 12 | -- 13 | ---------------------------------------------------------------------- 14 | 15 | 16 | -------------------------------------------------------------------------------- 17 | -- 18 | -- Exported API: 19 | -- * [mlp.splice_content()] 20 | -- * [mlp.quote_content()] 21 | -- 22 | -------------------------------------------------------------------------------- 23 | 24 | module ("mlp", package.seeall) 25 | 26 | -------------------------------------------------------------------------------- 27 | -- External splicing: compile an AST into a chunk, load and evaluate 28 | -- that chunk, and replace the chunk by its result (which must also be 29 | -- an AST). 30 | -------------------------------------------------------------------------------- 31 | 32 | function splice (ast) 33 | local f = mlc.function_of_ast(ast, '=splice') 34 | local result=f() 35 | return result 36 | end 37 | 38 | -------------------------------------------------------------------------------- 39 | -- Going from an AST to an AST representing that AST 40 | -- the only key being lifted in this version is ["tag"] 41 | -------------------------------------------------------------------------------- 42 | function quote (t) 43 | --print("QUOTING:", _G.table.tostring(t, 60)) 44 | local cases = { } 45 | function cases.table (t) 46 | local mt = { tag = "Table" } 47 | --_G.table.insert (mt, { tag = "Pair", quote "quote", { tag = "True" } }) 48 | if t.tag == "Splice" then 49 | assert (#t==1, "Invalid splice") 50 | local sp = t[1] 51 | return sp 52 | elseif t.tag then 53 | _G.table.insert (mt, { tag = "Pair", quote "tag", quote (t.tag) }) 54 | end 55 | for _, v in ipairs (t) do 56 | _G.table.insert (mt, quote(v)) 57 | end 58 | return mt 59 | end 60 | function cases.number (t) return { tag = "Number", t, quote = true } end 61 | function cases.string (t) return { tag = "String", t, quote = true } end 62 | return cases [ type (t) ] (t) 63 | end 64 | 65 | -------------------------------------------------------------------------------- 66 | -- when this variable is false, code inside [-{...}] is compiled and 67 | -- avaluated immediately. When it's true (supposedly when we're 68 | -- parsing data inside a quasiquote), [-{foo}] is replaced by 69 | -- [`Splice{foo}], which will be unpacked by [quote()]. 70 | -------------------------------------------------------------------------------- 71 | in_a_quote = false 72 | 73 | -------------------------------------------------------------------------------- 74 | -- Parse the inside of a "-{ ... }" 75 | -------------------------------------------------------------------------------- 76 | function splice_content (lx) 77 | local parser_name = "expr" 78 | if lx:is_keyword (lx:peek(2), ":") then 79 | local a = lx:next() 80 | lx:next() -- skip ":" 81 | assert (a.tag=="Id", "Invalid splice parser name") 82 | parser_name = a[1] 83 | end 84 | local ast = mlp[parser_name](lx) 85 | if in_a_quote then 86 | --printf("SPLICE_IN_QUOTE:\n%s", _G.table.tostring(ast, "nohash", 60)) 87 | return { tag="Splice", ast } 88 | else 89 | if parser_name == "expr" then ast = { { tag="Return", ast } } 90 | elseif parser_name == "stat" then ast = { ast } 91 | elseif parser_name ~= "block" then 92 | error ("splice content must be an expr, stat or block") end 93 | --printf("EXEC THIS SPLICE:\n%s", _G.table.tostring(ast, "nohash", 60)) 94 | return splice (ast) 95 | end 96 | end 97 | 98 | -------------------------------------------------------------------------------- 99 | -- Parse the inside of a "+{ ... }" 100 | -------------------------------------------------------------------------------- 101 | function quote_content (lx) 102 | local parser 103 | if lx:is_keyword (lx:peek(2), ":") then -- +{parser: content } 104 | parser = mlp[id(lx)[1]] 105 | lx:next() 106 | else -- +{ content } 107 | parser = mlp.expr 108 | end 109 | 110 | local prev_iq = in_a_quote 111 | in_a_quote = true 112 | --print("IN_A_QUOTE") 113 | local content = parser (lx) 114 | local q_content = quote (content) 115 | in_a_quote = prev_iq 116 | return q_content 117 | end 118 | 119 | -------------------------------------------------------------------------------- /lib/mlp_misc.lua: -------------------------------------------------------------------------------- 1 | ---------------------------------------------------------------------- 2 | -- Metalua: $Id: mlp_misc.lua,v 1.6 2006/11/15 09:07:50 fab13n Exp $ 3 | -- 4 | -- Summary: metalua parser, miscellaneous utility functions. 5 | -- 6 | ---------------------------------------------------------------------- 7 | -- 8 | -- Copyright (c) 2006, Fabien Fleutot . 9 | -- 10 | -- This software is released under the MIT Licence, see licence.txt 11 | -- for details. 12 | -- 13 | ---------------------------------------------------------------------- 14 | -- History: 15 | -- $Log: mlp_misc.lua,v $ 16 | -- Revision 1.6 2006/11/15 09:07:50 fab13n 17 | -- debugged meta operators. 18 | -- Added command line options handling. 19 | -- 20 | -- Revision 1.5 2006/11/10 02:11:17 fab13n 21 | -- compiler faithfulness to 5.1 improved 22 | -- gg.expr extended 23 | -- mlp.expr refactored 24 | -- 25 | -- Revision 1.4 2006/11/09 09:39:57 fab13n 26 | -- some cleanup 27 | -- 28 | -- Revision 1.3 2006/11/07 04:38:00 fab13n 29 | -- first bootstrapping version. 30 | -- 31 | -- Revision 1.2 2006/11/05 15:08:34 fab13n 32 | -- updated code generation, to be compliant with 5.1 33 | -- 34 | ---------------------------------------------------------------------- 35 | 36 | -------------------------------------------------------------------------------- 37 | -- 38 | -- Exported API: 39 | -- * [mlp.fget()] 40 | -- * [mlp.id()] 41 | -- * [mlp.opt_id()] 42 | -- * [mlp.id_list()] 43 | -- * [mlp.gensym()] 44 | -- * [mlp.string()] 45 | -- * [mlp.opt_string()] 46 | -- * [mlp.id2string()] 47 | -- 48 | -------------------------------------------------------------------------------- 49 | 50 | --require "gg" 51 | --require "mll" 52 | 53 | module ("mlp", package.seeall) 54 | 55 | -------------------------------------------------------------------------------- 56 | -- returns a function that takes the [n]th element of a table. 57 | -- if [tag] is provided, then this element is expected to be a 58 | -- table, and this table receives a "tag" field whose value is 59 | -- set to [tag]. 60 | -- 61 | -- The primary purpose of this is to generate builders for 62 | -- grammar generators. It has little purpose in metalua, as lambda has 63 | -- a lightweight syntax. 64 | -------------------------------------------------------------------------------- 65 | 66 | function fget (n, tag) 67 | assert (type (n) == "number") 68 | if tag then 69 | assert (type (tag) == "string") 70 | return function (x) 71 | assert (type (x[n]) == "table") 72 | return {tag=tag, unpack(x[n])} end 73 | else 74 | return function (x) return x[n] end 75 | end 76 | end 77 | 78 | 79 | -------------------------------------------------------------------------------- 80 | -- Try to read an identifier (possibly as a splice), or return [false] if no 81 | -- id is found. 82 | -------------------------------------------------------------------------------- 83 | function opt_id (lx) 84 | local a = lx:peek(); 85 | if lx:is_keyword (a, "-{") then 86 | local v = gg.sequence{ "-{", splice_content, "}" } (lx) [1] 87 | if v.tag ~= "Id" and v.tag ~= "Splice" then 88 | gg.parse_error(lx,"Bad id splice") 89 | end 90 | return v 91 | elseif a.tag == "Id" then return lx:next() 92 | else return false end 93 | end 94 | 95 | -------------------------------------------------------------------------------- 96 | -- Mandatory reading of an id: causes an error if it can't read one. 97 | -------------------------------------------------------------------------------- 98 | function id (lx) 99 | return opt_id (lx) or gg.parse_error(lx,"Identifier expected") 100 | end 101 | 102 | -------------------------------------------------------------------------------- 103 | -- Common helper function 104 | -------------------------------------------------------------------------------- 105 | id_list = gg.list { primary = mlp.id, separators = "," } 106 | 107 | -------------------------------------------------------------------------------- 108 | -- Symbol generator: [gensym()] returns a guaranteed-to-be-unique identifier. 109 | -- The main purpose is to avoid variable capture in macros. 110 | -- 111 | -- If a string is passed as an argument, theis string will be part of the 112 | -- id name (helpful for macro debugging) 113 | -------------------------------------------------------------------------------- 114 | local gensymidx = 0 115 | 116 | function gensym (arg) 117 | gensymidx = gensymidx + 1 118 | return { tag="Id", _G.string.format(".%i.%s", gensymidx, arg or "")} 119 | end 120 | 121 | -------------------------------------------------------------------------------- 122 | -- Converts an identifier into a string. Hopefully one day it'll handle 123 | -- splices gracefully, but that proves quite tricky. 124 | -------------------------------------------------------------------------------- 125 | function id2string (id) 126 | --print("id2string:", disp.ast(id)) 127 | if id.tag == "Id" then id.tag = "String"; return id 128 | elseif id.tag == "Splice" then 129 | assert (in_a_quote, "can't do id2string on an outermost splice") 130 | error ("id2string on splice not implemented") 131 | -- Evaluating id[1] will produce `Id{ xxx }, 132 | -- and we want it to produce `String{ xxx } 133 | -- Morally, this is what I want: 134 | -- return `String{ `Index{ `Splice{ id[1] }, `Number 1 } } 135 | -- That is, without sugar: 136 | return {tag="String", {tag="Index", {tag="Splice", id[1] }, 137 | {tag="Number", 1 } } } 138 | else error ("Identifier expected: "..table.tostring(id)) end 139 | end 140 | 141 | -------------------------------------------------------------------------------- 142 | -- Read a string, possibly spliced, or return an error if it can't 143 | -------------------------------------------------------------------------------- 144 | function string (lx) 145 | local a = lx:peek() 146 | if lx:is_keyword (a, "-{") then 147 | local v = gg.sequence{ "-{", splice_content, "}" } (lx) [1] 148 | if v.tag ~= "" and v.tag ~= "Splice" then 149 | gg.parse_error(lx,"Bad string splice") 150 | end 151 | return v 152 | elseif a.tag == "String" then return lx:next() 153 | else error "String expected" end 154 | end 155 | 156 | -------------------------------------------------------------------------------- 157 | -- Try to read a string, or return false if it can't. No splice allowed. 158 | -------------------------------------------------------------------------------- 159 | function opt_string (lx) 160 | return lx:peek().tag == "String" and lx:next() 161 | end 162 | 163 | -------------------------------------------------------------------------------- 164 | -- Chunk reader: block + Eof 165 | -------------------------------------------------------------------------------- 166 | function skip_initial_sharp_comment (lx) 167 | -- Dirty hack: I'm happily fondling lexer's private parts 168 | -- FIXME: redundant with lexer:newstream() 169 | lx :sync() 170 | local i = lx.src:match ("^#.-\n()", lx.i) 171 | if i then lx.i, lx.column_offset, lx.line = i, i, lx.line+1 end 172 | end 173 | 174 | local function _chunk (lx) 175 | if lx:peek().tag == 'Eof' then return { } -- handle empty files 176 | else 177 | skip_initial_sharp_comment (lx) 178 | local chunk = block (lx) 179 | if lx:peek().tag ~= "Eof" then error "End-of-file expected" end 180 | return chunk 181 | end 182 | end 183 | 184 | -- chunk is wrapped in a sequence so that it has a "transformer" field. 185 | chunk = gg.sequence { _chunk, builder = unpack } -------------------------------------------------------------------------------- /lib/mlp_stat.lua: -------------------------------------------------------------------------------- 1 | ---------------------------------------------------------------------- 2 | -- Metalua: $Id: mlp_stat.lua,v 1.7 2006/11/15 09:07:50 fab13n Exp $ 3 | -- 4 | -- Summary: metalua parser, statement/block parser. This is part of 5 | -- the definition of module [mlp]. 6 | -- 7 | ---------------------------------------------------------------------- 8 | -- 9 | -- Copyright (c) 2006, Fabien Fleutot . 10 | -- 11 | -- This software is released under the MIT Licence, see licence.txt 12 | -- for details. 13 | -- 14 | ---------------------------------------------------------------------- 15 | -- 16 | ---------------------------------------------------------------------- 17 | 18 | -------------------------------------------------------------------------------- 19 | -- 20 | -- Exports API: 21 | -- * [mlp.stat()] 22 | -- * [mlp.block()] 23 | -- * [mlp.for_header()] 24 | -- 25 | -------------------------------------------------------------------------------- 26 | 27 | -------------------------------------------------------------------------------- 28 | -- eta-expansions to break circular dependency 29 | -------------------------------------------------------------------------------- 30 | local expr = function (lx) return mlp.expr (lx) end 31 | local func_val = function (lx) return mlp.func_val (lx) end 32 | local expr_list = function (lx) return mlp.expr_list(lx) end 33 | 34 | module ("mlp", package.seeall) 35 | 36 | -------------------------------------------------------------------------------- 37 | -- List of all keywords that indicate the end of a statement block. Users are 38 | -- likely to extend this list when designing extensions. 39 | -------------------------------------------------------------------------------- 40 | 41 | 42 | local block_terminators = { "else", "elseif", "end", "until", ")", "}", "]" } 43 | 44 | -- FIXME: this must be handled from within GG!!! 45 | function block_terminators:add(x) 46 | if type (x) == "table" then for _, y in ipairs(x) do self:add (y) end 47 | else _G.table.insert (self, x) end 48 | end 49 | 50 | -------------------------------------------------------------------------------- 51 | -- list of statements, possibly followed by semicolons 52 | -------------------------------------------------------------------------------- 53 | block = gg.list { 54 | name = "statements block", 55 | terminators = block_terminators, 56 | primary = function (lx) 57 | -- FIXME use gg.optkeyword() 58 | local x = stat (lx) 59 | if lx:is_keyword (lx:peek(), ";") then lx:next() end 60 | return x 61 | end } 62 | 63 | -------------------------------------------------------------------------------- 64 | -- Helper function for "return " parsing. 65 | -- Called when parsing return statements. 66 | -- The specific test for initial ";" is because it's not a block terminator, 67 | -- so without itgg.list would choke on "return ;" statements. 68 | -- We don't make a modified copy of block_terminators because this list 69 | -- is sometimes modified at runtime, and the return parser would get out of 70 | -- sync if it was relying on a copy. 71 | -------------------------------------------------------------------------------- 72 | local return_expr_list_parser = gg.multisequence{ 73 | { ";" , builder = function() return { } end }, 74 | default = gg.list { 75 | expr, separators = ",", terminators = block_terminators } } 76 | 77 | -------------------------------------------------------------------------------- 78 | -- for header, between [for] and [do] (exclusive). 79 | -- Return the `Forxxx{...} AST, without the body element (the last one). 80 | -------------------------------------------------------------------------------- 81 | function for_header (lx) 82 | local var = mlp.id (lx) 83 | if lx:is_keyword (lx:peek(), "=") then 84 | -- Fornum: only 1 variable 85 | lx:next() -- skip "=" 86 | local e = expr_list (lx) 87 | assert (2 <= #e and #e <= 3, "2 or 3 values in a fornum") 88 | return { tag="Fornum", var, unpack (e) } 89 | else 90 | -- Forin: there might be several vars 91 | local a = lx:is_keyword (lx:next(), ",", "in") 92 | if a=="in" then var_list = { var, lineinfo = var.lineinfo } else 93 | -- several vars; first "," skipped, read other vars 94 | var_list = gg.list{ 95 | primary = id, separators = ",", terminators = "in" } (lx) 96 | _G.table.insert (var_list, 1, var) -- put back the first variable 97 | lx:next() -- skip "in" 98 | end 99 | local e = expr_list (lx) 100 | return { tag="Forin", var_list, e } 101 | end 102 | end 103 | 104 | -------------------------------------------------------------------------------- 105 | -- Function def parser helper: id ( . id ) * 106 | -------------------------------------------------------------------------------- 107 | local function fn_builder (list) 108 | local r = list[1] 109 | for i = 2, #list do r = { tag="Index", r, id2string(list[i]) } end 110 | return r 111 | end 112 | local func_name = gg.list{ id, separators = ".", builder = fn_builder } 113 | 114 | -------------------------------------------------------------------------------- 115 | -- Function def parser helper: ( : id )? 116 | -------------------------------------------------------------------------------- 117 | local method_name = gg.onkeyword{ name = "method invocation", ":", id, 118 | transformers = { function(x) return x and id2string(x) end } } 119 | 120 | -------------------------------------------------------------------------------- 121 | -- Function def builder 122 | -------------------------------------------------------------------------------- 123 | local function funcdef_builder(x) 124 | local name, method, func = x[1], x[2], x[3] 125 | if method then 126 | name = { tag="Index", name, method, lineinfo = { 127 | first = name.lineinfo.first, 128 | last = method.lineinfo.last } } 129 | _G.table.insert (func[1], 1, {tag="Id", "self"}) 130 | end 131 | local r = { tag="Set", {name}, {func} } 132 | r[1].lineinfo = name.lineinfo 133 | r[2].lineinfo = func.lineinfo 134 | return r 135 | end 136 | 137 | 138 | -------------------------------------------------------------------------------- 139 | -- if statement builder 140 | -------------------------------------------------------------------------------- 141 | local function if_builder (x) 142 | local cb_pairs, else_block, r = x[1], x[2], {tag="If"} 143 | for i=1,#cb_pairs do r[2*i-1]=cb_pairs[i][1]; r[2*i]=cb_pairs[i][2] end 144 | if else_block then r[#r+1] = else_block end 145 | return r 146 | end 147 | 148 | -------------------------------------------------------------------------------- 149 | -- produce a list of (expr,block) pairs 150 | -------------------------------------------------------------------------------- 151 | local elseifs_parser = gg.list { 152 | gg.sequence { expr, "then", block }, 153 | separators = "elseif", 154 | terminators = { "else", "end" } } 155 | 156 | -------------------------------------------------------------------------------- 157 | -- assignments and calls: statements that don't start with a keyword 158 | -------------------------------------------------------------------------------- 159 | local function assign_or_call_stat_parser (lx) 160 | local e = expr_list (lx) 161 | local a = lx:is_keyword(lx:peek()) 162 | local op = a and stat.assignments[a] 163 | if op then 164 | --FIXME: check that [e] is a LHS 165 | lx:next() 166 | local v = expr_list (lx) 167 | if type(op)=="string" then return { tag=op, e, v } 168 | else return op (e, v) end 169 | else 170 | assert (#e > 0) 171 | if #e > 1 then 172 | gg.parse_error (lx, "comma is not a valid statement separator") end 173 | if e[1].tag ~= "Call" and e[1].tag ~= "Invoke" then 174 | gg.parse_error (lx, "This expression is of type '%s'; ".. 175 | "only function and method calls make valid statements", 176 | e[1].tag or "") 177 | end 178 | return e[1] 179 | end 180 | end 181 | 182 | local_stat_parser = gg.multisequence{ 183 | -- local function 184 | { "function", id, func_val, builder = 185 | function(x) 186 | local vars = { x[1], lineinfo = x[1].lineinfo } 187 | local vals = { x[2], lineinfo = x[2].lineinfo } 188 | return { tag="Localrec", vars, vals } 189 | end }, 190 | -- local ( = )? 191 | default = gg.sequence{ id_list, gg.onkeyword{ "=", expr_list }, 192 | builder = function(x) return {tag="Local", x[1], x[2] or { } } end } } 193 | 194 | -------------------------------------------------------------------------------- 195 | -- statement 196 | -------------------------------------------------------------------------------- 197 | stat = gg.multisequence { 198 | name="statement", 199 | { "do", block, "end", builder = 200 | function (x) return { tag="Do", unpack (x[1]) } end }, 201 | { "for", for_header, "do", block, "end", builder = 202 | function (x) x[1][#x[1]+1] = x[2]; return x[1] end }, 203 | { "function", func_name, method_name, func_val, builder=funcdef_builder }, 204 | { "while", expr, "do", block, "end", builder = "While" }, 205 | { "repeat", block, "until", expr, builder = "Repeat" }, 206 | { "local", local_stat_parser, builder = fget (1) }, 207 | { "return", return_expr_list_parser, builder = fget (1, "Return") }, 208 | { "break", builder = function() return { tag="Break" } end }, 209 | { "-{", splice_content, "}", builder = fget(1) }, 210 | { "if", elseifs_parser, gg.onkeyword{ "else", block }, "end", 211 | builder = if_builder }, 212 | default = assign_or_call_stat_parser } 213 | 214 | stat.assignments = { 215 | ["="] = "Set" } 216 | 217 | function stat.assignments:add(k, v) self[k] = v end 218 | -------------------------------------------------------------------------------- /lib/mlp_table.lua: -------------------------------------------------------------------------------- 1 | ---------------------------------------------------------------------- 2 | -- Metalua: $Id: mlp_table.lua,v 1.5 2006/11/10 02:11:17 fab13n Exp $ 3 | -- 4 | -- Summary: metalua parser, table constructor parser. This is part 5 | -- of thedefinition of module [mlp]. 6 | -- 7 | ---------------------------------------------------------------------- 8 | -- 9 | -- Copyright (c) 2006, Fabien Fleutot . 10 | -- 11 | -- This software is released under the MIT Licence, see licence.txt 12 | -- for details. 13 | -- 14 | ---------------------------------------------------------------------- 15 | -- History: 16 | -- $Log: mlp_table.lua,v $ 17 | -- Revision 1.5 2006/11/10 02:11:17 fab13n 18 | -- compiler faithfulness to 5.1 improved 19 | -- gg.expr extended 20 | -- mlp.expr refactored 21 | -- 22 | -- Revision 1.4 2006/11/09 09:39:57 fab13n 23 | -- some cleanup 24 | -- 25 | -- Revision 1.3 2006/11/07 04:38:00 fab13n 26 | -- first bootstrapping version. 27 | -- 28 | -- Revision 1.2 2006/11/05 15:08:34 fab13n 29 | -- updated code generation, to be compliant with 5.1 30 | -- 31 | ---------------------------------------------------------------------- 32 | 33 | -------------------------------------------------------------------------------- 34 | -- 35 | -- Exported API: 36 | -- * [mlp.table_field()] 37 | -- * [mlp.table_content()] 38 | -- * [mlp.table()] 39 | -- 40 | -- KNOWN BUG: doesn't handle final ";" or "," before final "}" 41 | -- 42 | -------------------------------------------------------------------------------- 43 | 44 | --require "gg" 45 | --require "mll" 46 | --require "mlp_misc" 47 | 48 | module ("mlp", package.seeall) 49 | 50 | -------------------------------------------------------------------------------- 51 | -- eta expansion to break circular dependencies: 52 | -------------------------------------------------------------------------------- 53 | local function _expr (lx) return expr(lx) end 54 | 55 | -------------------------------------------------------------------------------- 56 | -- [[key] = value] table field definition 57 | -------------------------------------------------------------------------------- 58 | local bracket_field = gg.sequence{ "[", _expr, "]", "=", _expr, builder = "Pair" } 59 | 60 | -------------------------------------------------------------------------------- 61 | -- [id = value] or [value] table field definition; 62 | -- [[key]=val] are delegated to [bracket_field()] 63 | -------------------------------------------------------------------------------- 64 | function table_field (lx) 65 | if lx:is_keyword (lx:peek(), "[") then return bracket_field (lx) end 66 | local e = _expr (lx) 67 | if lx:is_keyword (lx:peek(), "=") then 68 | lx:next(); -- skip the "=" 69 | local key = id2string(e) 70 | local val = _expr(lx) 71 | local r = { tag="Pair", key, val } 72 | r.lineinfo = { first = key.lineinfo.first, last = val.lineinfo.last } 73 | return r 74 | else return e end 75 | end 76 | 77 | local function _table_field(lx) return table_field(lx) end 78 | 79 | -------------------------------------------------------------------------------- 80 | -- table constructor, without enclosing braces; returns a full table object 81 | -------------------------------------------------------------------------------- 82 | table_content = gg.list { _table_field, 83 | separators = { ",", ";" }, terminators = "}", builder = "Table" } 84 | 85 | local function _table_content(lx) return table_content(lx) end 86 | 87 | -------------------------------------------------------------------------------- 88 | -- complete table constructor including [{...}] 89 | -------------------------------------------------------------------------------- 90 | table = gg.sequence{ "{", _table_content, "}", builder = fget(1) } 91 | 92 | 93 | -------------------------------------------------------------------------------- /lua2c.lua: -------------------------------------------------------------------------------- 1 | -- lua2c.lua - Driver for lua2c - converts Lua 5.1 source to C code. 2 | -- 3 | -- STATUS: 4 | -- WARNING: This code passes much of the Lua 5.1 test suite, 5 | -- but there could still be errors. In particular, a few 6 | -- language features (e.g. coroutines) are not implemented. 7 | -- 8 | -- Unimplemented Lua language features: 9 | -- - deprecated old style vararg (arg) table inside vararg functions 10 | -- (LUA_COMPAT_VARARG) 11 | -- - debug.getinfo(f, 'n').name for C-based functions 12 | -- - setfenv does not permit C-based functions 13 | -- - how well do tail call optimizations work? 14 | -- - how to handle coroutines? (see README) 15 | -- Note: A few things (coroutines) might remain 16 | -- unimplemented--see README file file for details. 17 | -- 18 | -- Possible improvements: 19 | -- - Fix: large numerical literals can give gcc warnings such as 20 | -- 'warning: integer constant is too large for "long" type'). 21 | -- Literal numbers are rendered as C integers literals (e.g. 123) 22 | -- rather than C double literals (eg. 123.0). 23 | -- - improved debug tracebacks on exceptions 24 | -- - See items marked FIX in below code. 25 | -- 26 | -- SOURCE: 27 | -- 28 | -- http://lua-users.org/wiki/LuaToCee 29 | -- 30 | -- (c) 2008 David Manura. Licensed in the same terms as Lua (MIT license). 31 | -- See included LICENSE file for full licensing details. 32 | -- Please post any patches/improvements. 33 | -- 34 | 35 | local _G = _G 36 | local assert = _G.assert 37 | local error = _G.error 38 | local io = _G.io 39 | local ipairs = _G.ipairs 40 | local os = _G.os 41 | local package = _G.package 42 | local require = _G.require 43 | local string = _G.string 44 | local table = _G.table 45 | 46 | -- package.path = './lib/?.lua;' .. package.path 47 | 48 | -- note: includes gg/mlp Lua parsing Libraries taken from Metalua. 49 | require "lexer" 50 | require "gg" 51 | require "mlp_lexer" 52 | require "mlp_misc" 53 | require "mlp_table" 54 | require "mlp_meta" 55 | require "mlp_expr" 56 | require "mlp_stat" 57 | require "mlp_ext" 58 | _G.mlc = {} -- make gg happy 59 | local mlp = assert(_G.mlp) 60 | local A2C = require "lua2c.ast2cast" 61 | local C2S = require "lua2c.cast2string" 62 | 63 | local function NOTIMPL(s) 64 | error('FIX - NOT IMPLEMENTED: ' .. s, 2) 65 | end 66 | 67 | local function DEBUG(...) 68 | local ts = {...} 69 | for i,v in ipairs(ts) do 70 | ts[i] = table.tostring(v,'nohash',60) 71 | end 72 | io.stderr:write(table.concat(ts, ' ') .. '\n') 73 | end 74 | 75 | -- Converts Lua source string to Lua AST (via mlp/gg) 76 | local function string_to_ast(src) 77 | local lx = mlp.lexer:newstream (src) 78 | local ast = mlp.chunk (lx) 79 | return ast 80 | end 81 | 82 | local src_filename = ... 83 | 84 | if not src_filename then 85 | io.stderr:write("usage: lua2c filename.lua\n") 86 | os.exit(1) 87 | end 88 | 89 | local src_file = assert(io.open (src_filename, 'r')) 90 | local src = src_file:read '*a'; src_file:close() 91 | src = src:gsub('^#[^\r\n]*', '') -- remove any shebang 92 | 93 | local ast = string_to_ast(src) 94 | 95 | local cast = A2C.ast_to_cast(src, ast) 96 | -- DEBUG(cast) 97 | 98 | io.stdout:write(C2S.cast_to_string(cast)) 99 | 100 | --------------------------------------------------------------------------------