]> git.lizzy.rs Git - metalua.git/blob - metalua/compiler.lua
Merge branch 'master' of ssh://git.eclipse.org/gitroot/koneki/org.eclipse.koneki...
[metalua.git] / metalua / compiler.lua
1 ---------------------------------------------------------------------------
2 -- Copyright (c) 2006-2013 Fabien Fleutot and others.
3 --
4 -- All rights reserved.
5 --
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
10 --
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
14 --
15 -- Contributors:
16 --     Fabien Fleutot - API and implementation
17 --
18 --------------------------------------------------------------------------------
19
20 --------------------------------------------------------------------------------
21 --
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.
25 --
26 -- Supported formats are:
27 --
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.
38 --
39 --------------------------------------------------------------------------------
40
41 require 'checks'
42
43 local M  = { }
44
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
49 -- names to numbers.
50 --------------------------------------------------------------------------------
51 M.sequence = {
52         'srcfile',  'src', 'lexstream', 'ast', 'proto', 'bytecode', 'function' }
53
54 local arg_types = {
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' },
61 }
62
63 if false then
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))
71             end
72             for i, child in ipairs(x) do
73                 if type(child)=='table' then rec(child, i, x) end
74             end
75         end
76         rec(ast, -1, { })
77     end
78 end
79
80
81 M.order= { }; for a,b in pairs(M.sequence) do M.order[b]=a end
82
83 local CONV = { } -- conversion metatable __index
84
85 function CONV :srcfile_to_src(x, name)
86         checks('metalua.compiler', 'string', '?string')
87         name = name or '@'..x
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
92         f :close()
93         return r, name
94 end
95
96 function CONV :src_to_lexstream(src, name)
97         checks('metalua.compiler', 'string', '?string')
98         local r = self.parser.lexer :newstream (src, name)
99         return r, name
100 end
101
102 function CONV :lexstream_to_ast(lx, name)
103         checks('metalua.compiler', 'lexer.stream', '?string')
104         local r = self.parser.chunk(lx)
105         r.source = name
106     if M.check_ast then M.check_ast (r) end
107         return r, name
108 end
109
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')
114         if status then
115             bytecode_compiler = result
116             return result
117         elseif string.match(result, "not found") then
118             error "Compilation only available with full Metalua"
119         else error (result) end
120     end
121 end
122
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
126 end
127
128 function CONV :proto_to_bytecode(proto, name)
129     return get_bytecode_compiler().proto_to_bytecode(proto), name
130 end
131
132 function CONV :bytecode_to_function(bc, name)
133         checks('metalua.compiler', 'string', '?string')
134         return loadstring(bc, name)
135 end
136
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 = { }
145                 for k=i, j-1 do
146                         local name =  M.sequence[k].."_to_"..M.sequence[k+1]
147                         local f = assert(CONV[name], name)
148                         table.insert (functions, f)
149                 end
150                 CONV[dst_name] = function(self, a, b)
151                         checks('metalua.compiler', unpack(my_arg_types))
152                         for _, f in ipairs(functions) do
153                                 a, b = f(self, a, b)
154                         end
155                         return a, b
156                 end
157                 --printf("Created M.%s out of %s", dst_name, table.concat(n, ', '))
158         end
159 end
160
161
162 --------------------------------------------------------------------------------
163 -- This one goes in the "wrong" direction, cannot be composed.
164 --------------------------------------------------------------------------------
165 function CONV :function_to_bytecode(...) return string.dump(...) end
166
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' (...)
170 end
171
172 local MT = { __index=CONV, __type='metalua.compiler' }
173
174 function M.new()
175         local parser = require 'metalua.compiler.parser' .new()
176         local self = { parser = parser }
177         setmetatable(self, MT)
178         return self
179 end
180
181 return M