1
Jianw
9 天以前 70f29da38121b9a467841253e3268feb5df02902
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
---------------------------------------------------------------------------
-- Copyright (c) 2006-2013 Fabien Fleutot and others.
--
-- All rights reserved.
--
-- This program and the accompanying materials are made available
-- under the terms of the Eclipse Public License v1.0 which
-- accompanies this distribution, and is available at
-- http://www.eclipse.org/legal/epl-v10.html
--
-- This program and the accompanying materials are also made available
-- under the terms of the MIT public license which accompanies this
-- distribution, and is available at http://www.lua.org/license.html
--
-- Contributors:
--     Fabien Fleutot - API and implementation
--
--------------------------------------------------------------------------------
 
--------------------------------------------------------------------------------
--
-- Convert between various code representation formats. Atomic
-- converters are written in extenso, others are composed automatically
-- by chaining the atomic ones together in a closure.
--
-- Supported formats are:
--
-- * srcfile:    the name of a file containing sources.
-- * src:        these sources as a single string.
-- * lexstream:  a stream of lexemes.
-- * ast:        an abstract syntax tree.
-- * proto:      a (Yueliang) struture containing a high level
--               representation of bytecode. Largely based on the
--               Proto structure in Lua's VM
-- * bytecode:   a string dump of the function, as taken by
--               loadstring() and produced by string.dump().
-- * function:   an executable lua function in RAM.
--
--------------------------------------------------------------------------------
 
require 'checks'
 
local M  = { }
local unpack = table.unpack or unpack
 
--------------------------------------------------------------------------------
-- Order of the transformations. if 'a' is on the left of 'b', then a 'a' can
-- be transformed into a 'b' (but not the other way around).
-- M.sequence goes for numbers to format names, M.order goes from format
-- names to numbers.
--------------------------------------------------------------------------------
M.sequence = {
    'srcfile',  'src', 'lexstream', 'ast', 'proto', 'bytecode', 'function' }
 
local arg_types = {
    srcfile    = { 'string', '?string' },
    src        = { 'string', '?string' },
    lexstream  = { 'lexer.stream', '?string' },
    ast        = { 'table', '?string' },
    proto      = { 'table', '?string' },
    bytecode   = { 'string', '?string' },
}
 
M.order= { }; for a,b in pairs(M.sequence) do M.order[b]=a end
 
local CONV = { } -- conversion metatable __index
 
function CONV :srcfile_to_src(x, name)
    checks('metalua.compiler', 'string', '?string')
    name = name or '@'..x
    local f, msg = io.open (x, 'rb')
    if not f then error(msg) end
    local r, msg = f :read '*a'
    if not r then error("Cannot read file '"..x.."': "..msg) end
    f :close()
    return r, name
end
 
function CONV :src_to_lexstream(src, name)
    checks('metalua.compiler', 'string', '?string')
    local r = self.parser.lexer :newstream (src, name)
    return r, name
end
 
function CONV :lexstream_to_ast(lx, name)
    checks('metalua.compiler', 'lexer.stream', '?string')
    local r = self.parser.chunk(lx)
    r.source = name
    return r, name
end
 
local bytecode_compiler = nil -- cache to avoid repeated `pcall(require(...))`
local function get_bytecode_compiler()
    if bytecode_compiler then return bytecode_compiler else
        local status, result = pcall(require, 'metalua.compiler.bytecode')
        if status then
            bytecode_compiler = result
            return result
        elseif string.match(result, "not found") then
            error "Compilation only available with full Metalua"
        else error (result) end
    end
end
 
function CONV :ast_to_proto(ast, name)
    checks('metalua.compiler', 'table', '?string')
    return get_bytecode_compiler().ast_to_proto(ast, name), name
end
 
function CONV :proto_to_bytecode(proto, name)
    return get_bytecode_compiler().proto_to_bytecode(proto), name
end
 
function CONV :bytecode_to_function(bc, name)
    checks('metalua.compiler', 'string', '?string')
    return loadstring(bc, name)
end
 
-- Create all sensible combinations
for i=1,#M.sequence do
    local src = M.sequence[i]
    for j=i+2, #M.sequence do
        local dst = M.sequence[j]
        local dst_name = src.."_to_"..dst
        local my_arg_types = arg_types[src]
        local functions = { }
        for k=i, j-1 do
            local name =  M.sequence[k].."_to_"..M.sequence[k+1]
            local f = assert(CONV[name], name)
            table.insert (functions, f)
        end
        CONV[dst_name] = function(self, a, b)
            checks('metalua.compiler', unpack(my_arg_types))
            for _, f in ipairs(functions) do
                a, b = f(self, a, b)
            end
            return a, b
        end
        --printf("Created M.%s out of %s", dst_name, table.concat(n, ', '))
    end
end
 
 
--------------------------------------------------------------------------------
-- This one goes in the "wrong" direction, cannot be composed.
--------------------------------------------------------------------------------
function CONV :function_to_bytecode(...) return string.dump(...) end
 
function CONV :ast_to_src(...)
    require 'metalua.loader' -- ast_to_string isn't written in plain lua
    return require 'metalua.compiler.ast_to_src' (...)
end
 
local MT = { __index=CONV, __type='metalua.compiler' }
 
function M.new()
    local parser = require 'metalua.compiler.parser' .new()
    local self = { parser = parser }
    setmetatable(self, MT)
    return self
end
 
return M