lua.cratera/compiler.lua

240 lines
9.9 KiB
Lua

--[[
This file is part of cratera.lua - pure-Lua Cratera-to-Lua transpiler
Copyright (C) 2019 Soni L.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
--]]
--[[
This software is based on Lua 5.1 and Lua 5.3
Lua 5.1 license:
/******************************************************************************
* Copyright (C) 1994-2012 Lua.org, PUC-Rio. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
******************************************************************************/
Lua 5.3 license:
/******************************************************************************
* Copyright (C) 1994-2018 Lua.org, PUC-Rio.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
******************************************************************************/
--]]
-- a parser.lua-based cratera compiler
-- a few notes:
-- * all "next" should be tables. as well as all "super" (which should be "next").
-- (it wouldn't work properly without this)
-- * when calling into a deeper level, remember to use the second return value "retry"
-- (i.e. set it to true)
local parser = require "parser"
local selfify = parser.selfify
local STATE = parser.STATE
local l = require "luatokens".tokens
local assert, type, setmetatable = assert, type, setmetatable
local function tostring__name(self)
return getmetatable(self).__name
end
local function Upvaldesc() return {
name = nil, -- TString -- upvalue name (for debug information)
instack = false, -- lu_byte -- whether it is in stack (register)
idx = 0, -- lu_byte -- index of upvalue (in stack or in outer function's list)
} end
local function LocVar() return {
varname = nil, -- TString
startpc = 0, -- int -- first point where variable is active
endpc = 0, -- int -- first point where variable is dead
} end
local function Proto() return { -- is a GC object
numparams = 0, -- lu_byte -- number of fixed parameters
is_vararg = false, -- lu_byte but boolean
maxstacksize = 0, -- lu_byte -- number of registers needed by this function
k = {}, -- TValue * -- constants used by the function
code = {}, -- Instruction * -- opcodes
p = {}, -- Proto ** -- functions defined inside the function
lineinfo = {}, -- int * -- map from opcodes to source lines (debug information)
locvars = {}, -- LocVar * -- information about local variables (debug information)
uvalues = {}, -- Upvaldesc * -- upvalue information
} end
local function FuncState() return {
f = nil, -- Proto -- current function header
prev = nil, -- FuncState -- enclosing function
ls = nil, -- LexState -- lexical state
bl = nil, -- BlockCnt -- chain of current blocks
pc = 0, -- int -- next position to code (equivalent to 'ncode')
lasttarget = 0, -- int -- 'label' of last 'jump label'
jpc = 0, -- int -- number of pending jumps to 'pc'
--nk = 0, -- int -- number of elements in 'k'
--np = 0, -- int -- number of elements in 'p'
firstlocal = 0, -- int -- index of first local var (in Dyndata array)
nlocvars = 0, -- short -- number of elements in 'f->locvars'
nactvar = 0, -- lu_byte -- number of active local variables
nups = 0, -- lu_byte -- number of upvalues
freereg = 0, -- lu_byte -- first free register
} end
local function Labeldesc() return {
name = nil, -- TString -- label identifier
pc = nil, -- int -- position in code
line = nil, -- int -- line where it appeared
nactvar = nil, -- lu_byte -- local level where it appears in current block
} end
local function Dyndata() return {
actvar = {}, -- ArrayList of Vardesc (short) -- list of active local variables
gt = {}, -- Labellist (ArrayList of Labeldesc) -- list of pending gotos
label = {}, -- Labellist (ArrayList of Labeldesc) -- list of active labels
} end
local function ParserState() return { -- LexState
fs = nil, -- FuncState *
dyd = nil, -- Dyndata *
} end
local gotostatname = {[parser.EOZ] = false}
local gotostatnamemt = {__index=gotostatname, __name="gotostatname", __tostring=tostring__name}
gotostatname[parser.FALLBACK] = function(state, token)
assert(type(token) == "string")
state[#state+1] = "goto"
state[#state+1] = token
return state[STATE].next
end
local gotostat = {[parser.EOZ] = false}
local gotostatmt = {__index=gotostat, __name="gotostat", __tostring=tostring__name}
gotostat[l.TK_NAME] = function(state, token)
return setmetatable({next = state[STATE].next}, gotostatnamemt)
end
local singlevar = {[parser.EOZ] = false}
local singlevarmt = {__index=singlevar, __name="singlevar", __tostring=tostring__name}
singlevar[parser.FALLBACK] = function(state, token)
assert(type(token) == "string")
state[#state+1] = token
return state[STATE].next
end
local primaryexp = {[parser.EOZ] = false}
local primaryexpmt = {__name="primaryexp", __tostring=tostring__name}
primaryexp['('] = function(state, token) end
primaryexp[l.TK_NAME] = function(state, token)
return setmetatable({next=state[STATE].next}, singlevarmt)
end
local suffixedexp = {}
local suffixedexpmt = {__name="suffixedexp", __tostring=tostring__name}
suffixedexp.next = function() end
local exprstat = {}
local exprstatmt = {__index=exprstat, __name="exprstat", __tostring=tostring__name}
exprstat.next = {}
local statementt = {[parser.EOZ] = false}
local statementmt = {__index=statementt, __name="statement", __tostring=tostring__name}
local function statement(state, token)
local cur = state[STATE]
return setmetatable({next = cur.next}, statementmt), true
end
statementt[";"] = function(state, token)
state[#state+1] = token
return "next"
end
statementt[l.TK_IF] = function(state, token) end
statementt[l.TK_WHILE] = function(state, token) end
statementt[l.TK_DO] = function(state, token) end
statementt[l.TK_FOR] = function(state, token) end
statementt[l.TK_REPEAT] = function(state, token) end
statementt[l.TK_FUNCTION] = function(state, token) end
statementt[l.TK_LOCAL] = function(state, token) end
statementt[l.TK_DBCOLON] = function(state, token) end
statementt[l.TK_RETURN] = function(state, token) end
statementt[l.TK_BREAK] = function(state, token)
state[#state+1] = "break"
return "next"
end
statementt[l.TK_GOTO] = function(state, token)
return setmetatable({next = state[STATE].next}, gotostatmt)
end
statementt[parser.FALLBACK] = function(state, token)
return setmetatable({super = state[STATE].next}, exprstatmt), true
end
local statlistt = {}
local statlistmt = {__index=statlistt, __name="statlist", __tostring=tostring__name}
local function statlist(state, token)
local cur = state[STATE]
return setmetatable(selfify({super = cur.next, withuntil = cur.withuntil}, "next"), statlistmt), true
end
statlistt[l.TK_ELSE] = function() return "super", true end
statlistt[l.TK_ELSEIF] = function() return "super", true end
statlistt[l.TK_END] = function() return "super", true end
statlistt[parser.EOZ] = function() return "super", true end
statlistt[l.TK_UNTIL] = function() return "withuntil", true end
statlistt[parser.FALLBACK] = statement
local mainfunc = setmetatable({}, {__name="mainfunc", __tostring=tostring__name})
mainfunc.withuntil = "super"
mainfunc[parser.EOZ] = parser.FALLBACK
mainfunc[parser.FALLBACK] = statlist
mainfunc.next = {
[parser.EOZ] = {}
}
local defs = setmetatable({}, {__name="defs", __tostring=tostring__name})
defs[parser.EOZ] = parser.FALLBACK
defs[parser.FALLBACK] = function(state, token) return mainfunc, true end
return {
defs = defs,
}