summary refs log blame commit diff stats
path: root/compiler.lua
blob: d67f9ec4b64e1b59e23ebd3f12d53eddcf45106a (plain) (tree)














































































































































































































































                                                                                                         
--[[
    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,
}