]> git.lizzy.rs Git - metalua.git/blob - src/compiler/mlc.mlua
going on reverting metalua as a regular set of lua libraries.
[metalua.git] / src / compiler / mlc.mlua
1 --*-lua-*-----------------------------------------------------------------------
2 -- This module is written in a more hackish way than necessary, just
3 -- because I can.  Its core feature is to dynamically generate a
4 -- function that converts from a source format to a destination
5 -- format; these formats are the various ways to represent a piece of
6 -- program, from the source file to the executable function. Legal
7 -- formats are:
8 --
9 -- * luafile:    the name of a file containing sources.
10 -- * luastring:  these sources as a single string.
11 -- * lexstream:  a stream of lexemes.
12 -- * ast:        an abstract syntax tree.
13 -- * proto:      a (Yueliang) struture containing a high level 
14 --               representation of bytecode. Largely based on the 
15 --               Proto structure in Lua's VM.
16 -- * luacstring: a string dump of the function, as taken by 
17 --               loadstring() and produced by string.dump().
18 -- * function:   an executable lua function in RAM.
19 --
20 --------------------------------------------------------------------------------
21
22 require 'bytecode'
23 require 'mlp'
24
25 mlc = { }
26 setmetatable(mlc, mlc)
27 mlc.metabugs = false
28
29 --------------------------------------------------------------------------------
30 -- Order of the transformations. if 'a' is on the left of 'b', then a 'a' can
31 -- be transformed into a 'b' (but not the other way around). Since the table
32 -- is transposed, the test is 'if mlc.order.a > mlc.order.b then error(...) end'
33 --------------------------------------------------------------------------------
34 mlc.order = table.transpose{ 
35    'luafile',  'luastring', 'lexstream', 'ast', 'proto', 
36    'luacstring', 'function' }
37
38 --------------------------------------------------------------------------------
39 -- The macro 'POINT(point_name, expected_type)' creates an entry point in the
40 -- 'mlc.convert' function. When we convert a 'a' into a 'b', FIXME
41 --------------------------------------------------------------------------------
42 -{ block:
43    jump_to_point = `If{ }
44    function point_builder(args)
45       local name, point_type, code = unpack(args)
46       table.insert(jump_to_point, +{src_fmt == -{name}}) -- if source format is 'name'
47       table.insert(jump_to_point, { `Goto{name} })       -- then jump to label  'name'
48       return {
49          ---------------------------------------------------
50          -- Stop if this is the destination format
51          ---------------------------------------------------
52          +{stat: if dst_fmt == -{name} then return x end },
53          ---------------------------------------------------
54          -- Start here if the source format matches
55          ---------------------------------------------------
56          `Label{ name },
57          -- +{print(" *** point "..-{name})}, -- debug trace
58          ---------------------------------------------------
59          -- Check that the type matches
60          ---------------------------------------------------
61          +{stat: assert (-{point_type} == type(x), "Invalid source type") },
62          -- perform transformation operations to the next type
63          }
64    end
65    mlp.lexer:add 'POINT'
66    mlp.stat:add{ 'POINT', mlp.string, ',', mlp.string, builder = point_builder } 
67 } -- end of meta-block
68
69 function mlc.convert (x, src_fmt, dst_fmt, name)
70    -- printf(" *** Convert a %s into a %s", src_fmt, dst_fmt)
71    -{ jump_to_point }
72
73    error "Can't perform this conversion (bad src name)"
74
75    POINT 'luafile', 'string' -- x is the src file's name
76
77    if not name then name = x end
78    local f, msg = io.open(x, "rb")
79    if not f then error(msg) end
80    x = f:read'*a'
81    f:close()
82    
83    POINT 'luastring', 'string' -- x is the source
84
85    x = mlp.lexer:newstream(x)      
86    
87    POINT 'lexstream', 'table' -- x is the lexeme stream
88
89    local status -- status = compilation success
90    local lx=x
91    if SHOW_METABUGS
92    -- If SHOW_METABUGS is true, errors should be attributed to a parser bug.
93    then status, x = true, mlp.chunk (lx)
94    -- If SHOW_METABUGS is false, errors should be attributed to an invalid entry.
95    else status, x = pcall (mlp.chunk, lx) end
96    -- FIXME: this test seems wrong ???
97    if status and lx:peek().tag ~= "Eof"
98    then status, x = false, "Premature Eof" 
99    elseif status and lx:peek().tag == "End"
100    then status, x = false, "Unexpected 'end' keyword" end
101    if not status and x then 
102       -- x = error msg; get rid of ???
103       x = x:match "[^:]+:[0-9]+: (.*)" or x
104       printf("Parsing error in %s line %s, char %s: \n%s",
105              filename or "?", lx.line, lx.i, x)
106       return nil
107    end
108    
109    POINT 'ast', 'table' -- x is the AST
110    x = bytecode.metalua_compile(x) 
111    x.source = name
112    POINT 'proto', 'table' 
113    x = bytecode.dump_string (x)
114    POINT 'luacstring', 'string' -- normally x is a bytecode dump
115    x = string.undump(x, name)
116    POINT 'function', 'function' 
117    error "Can't perform this conversion (bad dst name)"
118 end
119
120 -- Dynamically compose a conversion function from a function name
121 -- xxx_of_yyy() or yyy_to_xxx().
122 function mlc.__index(_, name)   
123    local dst, src = name:strmatch '^([a-z]+)_of_([a-z]+)$'
124    if not dst then src, dst = name:strmatch '^([a-z]+)_to_([a-z]+)$' end
125    if not src and dst then error "Bad converter name" end
126    local osrc, odst = mlc.order[src], mlc.order[dst]   
127    if not src or not dst then error ("malformed mlc function "..name) end
128    if not osrc then error ("unknown source format "..src) end
129    if not odst then error ("unknown destination format "..src) end
130    if osrc > odst then error "Can't convert in this direction" end
131    return |x, name| mlc.convert(x, src, dst, name) 
132 end
133
134 -- This case isn't handled by the __index method, as it goes "in the wrong direction"
135 mlc.function_to_luacstring = string.dump
136 mlc.luacstring_of_function = string.dump
137
138 -- These are drop-in replacement for loadfile() and loadstring(). The
139 -- C functions will call them instead of the original versions if
140 -- they're referenced in the registry.
141
142 function loadstring(str, name)
143    if type(str) ~= 'string' then error 'string expected' end
144    if str:match '^\027LuaQ' then return string.undump(str) end
145    local n = str:match '^#![^\n]*\n()'
146    if n then str=str:sub(n, -1) end
147    -- FIXME: handle erroneous returns (return nil + error msg)
148    return mlc.function_of_luastring(str, name)
149 end
150
151 function loadfile(filename)
152    local f = io.open(filename, 'rb')
153    local src = f:read '*a'
154    f:close()
155    return loadstring(src, '@'..filename)
156 end
157
158 function load(f, name)
159    while true do
160       local x = f()
161       if not x then break end
162       assert(type(x)=='string', "function passed to load() must return strings")
163       table.insert(acc, x)
164    end
165    return loadstring(table.concat(x))
166 end
167
168 function dostring(src)
169    local f, msg = loadstring(src)
170    if not f then error(msg) end
171    return f()
172 end
173
174 function dofile(name)
175    local f, msg = loadfile(name)
176    if not f then error(msg) end
177    return f()
178 end
179
180
181
182 debug.getregistry().loadstring = metalua_loadstring
183 debug.getregistry().loadfile = metalua_loadfile
184