1 ---------------------------------------------------------------------------
2 -- Copyright (c) 2006-2013 Fabien Fleutot and others.
4 -- All rights reserved.
6 -- This program and the accompanying materials are made available
7 -- under the terms of the Eclipse Public License v1.0 which
8 -- accompanies this distribution, and is available at
9 -- http://www.eclipse.org/legal/epl-v10.html
11 -- This program and the accompanying materials are also made available
12 -- under the terms of the MIT public license which accompanies this
13 -- distribution, and is available at http://www.lua.org/license.html
16 -- Fabien Fleutot - API and implementation
18 --------------------------------------------------------------------------------
20 --------------------------------------------------------------------------------
22 -- Convert between various code representation formats. Atomic
23 -- converters are written in extenso, others are composed automatically
24 -- by chaining the atomic ones together in a closure.
26 -- Supported formats are:
28 -- * srcfile: the name of a file containing sources.
29 -- * src: these sources as a single string.
30 -- * lexstream: a stream of lexemes.
31 -- * ast: an abstract syntax tree.
32 -- * proto: a (Yueliang) struture containing a high level
33 -- representation of bytecode. Largely based on the
34 -- Proto structure in Lua's VM
35 -- * bytecode: a string dump of the function, as taken by
36 -- loadstring() and produced by string.dump().
37 -- * function: an executable lua function in RAM.
39 --------------------------------------------------------------------------------
45 --------------------------------------------------------------------------------
46 -- Order of the transformations. if 'a' is on the left of 'b', then a 'a' can
47 -- be transformed into a 'b' (but not the other way around).
48 -- M.sequence goes for numbers to format names, M.order goes from format
50 --------------------------------------------------------------------------------
52 'srcfile', 'src', 'lexstream', 'ast', 'proto', 'bytecode', 'function' }
55 srcfile = { 'string', '?string' },
56 src = { 'string', '?string' },
57 lexstream = { 'lexer.stream', '?string' },
58 ast = { 'table', '?string' },
59 proto = { 'table', '?string' },
60 bytecode = { 'string', '?string' },
64 -- if defined, runs on every newly-generated AST
65 function M.check_ast(ast)
66 local function rec(x, n, parent)
67 if not x.lineinfo and parent.lineinfo then
68 local pp = require 'metalua.pprint'
69 pp.printf("WARNING: Missing lineinfo in child #%s `%s{...} of node at %s",
70 n, x.tag or '', tostring(parent.lineinfo))
72 for i, child in ipairs(x) do
73 if type(child)=='table' then rec(child, i, x) end
81 M.order= { }; for a,b in pairs(M.sequence) do M.order[b]=a end
83 local CONV = { } -- conversion metatable __index
85 function CONV :srcfile_to_src(x, name)
86 checks('metalua.compiler', 'string', '?string')
88 local f, msg = io.open (x, 'rb')
89 if not f then error(msg) end
90 local r, msg = f :read '*a'
91 if not r then error("Cannot read file '"..x.."': "..msg) end
96 function CONV :src_to_lexstream(src, name)
97 checks('metalua.compiler', 'string', '?string')
98 local r = self.parser.lexer :newstream (src, name)
102 function CONV :lexstream_to_ast(lx, name)
103 checks('metalua.compiler', 'lexer.stream', '?string')
104 local r = self.parser.chunk(lx)
106 if M.check_ast then M.check_ast (r) end
110 local bytecode_compiler = nil -- cache to avoid repeated `pcall(require(...))`
111 local function get_bytecode_compiler()
112 if bytecode_compiler then return bytecode_compiler else
113 local status, result = pcall(require, 'metalua.compiler.bytecode')
115 bytecode_compiler = result
117 elseif string.match(result, "not found") then
118 error "Compilation only available with full Metalua"
119 else error (result) end
123 function CONV :ast_to_proto(ast, name)
124 checks('metalua.compiler', 'table', '?string')
125 return get_bytecode_compiler().ast_to_proto(ast, name), name
128 function CONV :proto_to_bytecode(proto, name)
129 return get_bytecode_compiler().proto_to_bytecode(proto), name
132 function CONV :bytecode_to_function(bc, name)
133 checks('metalua.compiler', 'string', '?string')
134 return loadstring(bc, name)
137 -- Create all sensible combinations
138 for i=1,#M.sequence do
139 local src = M.sequence[i]
140 for j=i+2, #M.sequence do
141 local dst = M.sequence[j]
142 local dst_name = src.."_to_"..dst
143 local my_arg_types = arg_types[src]
144 local functions = { }
146 local name = M.sequence[k].."_to_"..M.sequence[k+1]
147 local f = assert(CONV[name], name)
148 table.insert (functions, f)
150 CONV[dst_name] = function(self, a, b)
151 checks('metalua.compiler', unpack(my_arg_types))
152 for _, f in ipairs(functions) do
157 --printf("Created M.%s out of %s", dst_name, table.concat(n, ', '))
162 --------------------------------------------------------------------------------
163 -- This one goes in the "wrong" direction, cannot be composed.
164 --------------------------------------------------------------------------------
165 function CONV :function_to_bytecode(...) return string.dump(...) end
167 function CONV :ast_to_src(...)
168 require 'metalua.loader' -- ast_to_string isn't written in plain lua
169 return require 'metalua.compiler.ast_to_src' (...)
172 local MT = { __index=CONV, __type='metalua.compiler' }
175 local parser = require 'metalua.compiler.parser' .new()
176 local self = { parser = parser }
177 setmetatable(self, MT)