Add stuff that doesn't work

This commit is contained in:
SoniEx2 2019-04-15 22:38:40 -03:00
parent e5e6533401
commit af3acfbb80
8 changed files with 396 additions and 41 deletions

6
autotest.sh Executable file
View File

@ -0,0 +1,6 @@
#!/bin/sh
until inotifywait -e modify compiler.lua testc.lua; do
date '+%s'
lua testc.lua
done

239
compiler.lua Normal file
View File

@ -0,0 +1,239 @@
--[[
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,
}

71
cratera.lua Normal file
View File

@ -0,0 +1,71 @@
--[[
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 code is highly experimental and not very good
local parser = require "parser"
local luatokens = require "luatokens"
local compiler = require "compiler"
local CRATERA_SEED = nil -- TODO
local function cratera_load(reader)
local f, s, i = parser.stream(luatokens.defs, reader)
local nl = 1
local otherstate = {}
local f, s, i = parser.stream(compiler.defs, function()
local tokens
repeat
local pos, state, transemsg, etoken, estate = f(s, i)
otherstate.line = state.line
i = pos
if not i then return nil end
if not state then error(transemsg) end
tokens = {}
for i,v in ipairs(state) do
state[i] = nil
tokens[i] = v
end
until #tokens > 0 or not transemsg
return tokens
end, otherstate)
local function fn()
function fn()
local tokens
repeat
local pos, state, transemsg, etoken, estate, est = f(s, i)
i = pos
if not i then return nil end
if not state then error(transemsg .. " " .. tostring(etoken)) end
tokens = {""}
for i,v in ipairs(state) do
state[i] = nil
tokens[i+1] = v
end
until #tokens > 1 or not transemsg
return table.concat(tokens, " ")
end
local ret = fn()
return string.sub(ret, 2)
end
return load(function()
return fn()
end)
end
return {load = cratera_load, CRATERA_SEED = CRATERA_SEED}

View File

@ -272,7 +272,7 @@ do local tstring = selfify({})
end
return "string"
end
tsescapes.digitc = setmetatable(selfify({[""] = tsescapes.digit, string = tstring}, "digitc"), {__index=tstring})
tsescapes.digitc = setmetatable(selfify({[parser.FALLBACK] = tsescapes.digit, string = tstring}, "digitc"), {__index=tstring})
tsescapes.digitc[1]=function(state, token, rule)
if rule == nil then
collect_fallback(state, string.char(state.in_digit))
@ -346,7 +346,7 @@ do local tstring = selfify({})
do local tseskipwhitespace = selfify(mknewline({
string = defs.string,
whitespace = "self",
[""] = "string",
[parser.FALLBACK] = "string",
[1] = collect_fallback,
}, 2))
--tseskipwhitespace["\n"] = setmetatable({[2] = countline, ["\r"] = setmetatable({}, {__index=tseskipwhitespace})}, {__index=tseskipwhitespace})
@ -366,7 +366,7 @@ do local tstring = selfify({})
tstring['\n'] = false
tstring['\r'] = false
tstring[""] = "self"
tstring[parser.FALLBACK] = "self"
tstring[1] = collect_fallback
@ -385,7 +385,7 @@ end
do local tlongstring = {}
defs.longstring = tlongstring
do local tllongstring_proper = selfify({[""] = "self", ["]"] = function(state, token) state.longstring_close = 0 return "maybe_end" end})
do local tllongstring_proper = selfify({[parser.FALLBACK] = "self", ["]"] = function(state, token) state.longstring_close = 0 return "maybe_end" end})
tllongstring_proper[1] = false -- placeholder for newline handling
tllongstring_proper[2] = collect_fallback
@ -412,7 +412,7 @@ do local tlongstring = {}
return "maybe_end"
end
end
tllmaybe_end[""] = "longstring_proper"
tllmaybe_end[parser.FALLBACK] = "longstring_proper"
tllmaybe_end[1] = collect_fallback
tllmaybe_end[-1] = function(state, token, rule)
if not rule then
@ -473,6 +473,7 @@ mknewline(defs, 1)
defs["-"] = "maybe_comment"
do local tmaybe_comment = setmetatable({["-"] = "comment"}, {__index=defs})
defs.maybe_comment = tmaybe_comment
tmaybe_comment[parser.EOZ] = "self" -- defs
tmaybe_comment[-1] = function(state, token, rule)
if rule ~= "comment" then
state[#state+1] = "-"
@ -480,12 +481,12 @@ do local tmaybe_comment = setmetatable({["-"] = "comment"}, {__index=defs})
end
do local tmcomment = {comment_proper = selfify({})}
tmaybe_comment.comment = tmcomment
tmcomment[""] = "comment_proper"
tmcomment[parser.FALLBACK] = "comment_proper"
tmcomment["["] = "maybe_longcomment"
mknewline(tmcomment, 1, defs)
mknewline(tmcomment.comment_proper, 1, defs)
tmcomment.comment_proper[""] = "self"
do local tllongcomment_proper = selfify({[""] = "self", ["]"] = function(state, token) state.longcomment_close = 0 return "maybe_end" end})
tmcomment.comment_proper[parser.FALLBACK] = "self"
do local tllongcomment_proper = selfify({[parser.FALLBACK] = "self", ["]"] = function(state, token) state.longcomment_close = 0 return "maybe_end" end})
tmcomment.longcomment = tllongcomment_proper
do local tllmaybe_end = selfify({defs = defs}, "maybe_end")
tllongcomment_proper.maybe_end = tllmaybe_end
@ -504,7 +505,7 @@ do local tmaybe_comment = setmetatable({["-"] = "comment"}, {__index=defs})
return "maybe_end"
end
end
tllmaybe_end[""] = "longcomment_proper"
tllmaybe_end[parser.FALLBACK] = "longcomment_proper"
tllmaybe_end[-1] = function(state, token, rule)
if not rule then
state.longcomment_close = nil
@ -543,6 +544,7 @@ end
local STATE = parser.STATE
defs.multitokens = setmetatable({
[parser.EOZ] = "self",
[-1] = function(state, token, rule)
if not state[STATE].multitoken[token] then
state[#state+1] = state[STATE].first
@ -736,9 +738,7 @@ function defs.string_open(state, token)
assert("this shouldn't happen")
end
return {
defs = defs,
tokens = {
local tokens = {
TK_AND = TK_AND, TK_BREAK = TK_BREAK,
TK_DO = TK_DO, TK_ELSE = TK_ELSE, TK_ELSEIF = TK_ELSEIF, TK_END = TK_END, TK_FALSE = TK_FALSE, TK_FOR = TK_FOR, TK_FUNCTION = TK_FUNCTION,
TK_GOTO = TK_GOTO, TK_IF = TK_IF, TK_IN = TK_IN, TK_LOCAL = TK_LOCAL, TK_NIL = TK_NIL, TK_NOT = TK_NOT, TK_OR = TK_OR, TK_REPEAT = TK_REPEAT,
@ -747,7 +747,14 @@ return {
TK_SHL = TK_SHL, TK_SHR = TK_SHR,
TK_DBCOLON = TK_DBCOLON, TK_EOS = TK_EOS,
TK_FLT = TK_FLT, TK_INT = TK_INT, TK_NAME = TK_NAME, TK_STRING = TK_STRING
},
}
for k,v in pairs(tokens) do
setmetatable(v, {__name=k, __tostring=function(self) return getmetatable(self).__name end})
end
return {
defs = defs,
tokens = tokens,
reverse_keywords = reverse_keywords,
reverse_tokens = {
[TK_IDIV] = "//", [TK_CONCAT] = "..", [TK_DOTS] = "...", [TK_EQ] = "==", [TK_GE] = ">=", [TK_LE] = "<=", [TK_NE] = "~=",

View File

@ -16,34 +16,43 @@
along with this program. If not, see <https://www.gnu.org/licenses/>.
--]]
local function ts(self) return getmetatable(self).__name end
-- key for STATE
local STATE = {}
local STATE = setmetatable({}, {__name="STATE", __tostring=ts})
-- key for DATA
local DATA = {}
local DATA = setmetatable({}, {__name="DATA", __tostring=ts})
-- key for GENERATOR
local GEN = {}
local GEN = setmetatable({}, {__name="GEN", __tostring=ts})
-- key for DATA OFFSET
local OFFDATA = {}
local OFFDATA = setmetatable({}, {__name="OFFDATA", __tostring=ts})
-- key for End of Stream
local EOZ = {}
local EOZ = setmetatable({}, {__name="EOZ", __tostring=ts})
-- key for number rules (prevent conflict with hooks)
local NUMBER = setmetatable({}, {__name="NUMBER", __tostring=ts})
-- key for fallback rules (prevent conflict with empty string)
local FALLBACK = setmetatable({}, {__name="FALLBACK", __tostring=ts})
local optimize_lookups = {}
for i=0, 255 do
optimize_lookups[i] = string.char(i)
end
local type, tostring
= type, tostring
local type, tostring, string_byte
= type, tostring, string.byte
local function get_next_common(state, in_pos, token)
-- note: must preserve "token" - do not call recursively with a different token
local transition
if state[STATE] then
local transition, retry
local st = state[STATE]
if st then
local rule = st[token]
if not rule and token == EOZ then
return in_pos, state
end
if type(token) == "number" then
rule = st[NUMBER]
end
do -- pre-hooks
local pos = -1
local hook = st[pos]
@ -57,7 +66,7 @@ local function get_next_common(state, in_pos, token)
end
transition = rule
if transition == nil then
transition = st[""]
transition = st[FALLBACK]
end
local recheck = true
while recheck do
@ -67,7 +76,10 @@ local function get_next_common(state, in_pos, token)
transition = st[transition]
recheck = true
elseif tytrans == "function" then
transition = transition(state, token)
transition, retry = transition(state, token)
recheck = true
elseif tytrans == "table" and st[transition] ~= nil then
transition = st[transition]
recheck = true
end
end
@ -88,8 +100,9 @@ local function get_next_common(state, in_pos, token)
if not state[STATE] then
-- unexpected token. stream consumer may attempt to recover,
-- but we do this mostly to differentiate it from "end of stream" condition.
return in_pos - 1, nil, "unexpected token", token, state
return in_pos - 1, nil, "unexpected token", token, state, st
end
if retry then in_pos = in_pos - 1 end
return in_pos, state, transition -- TODO is this what we should be returning?
end
@ -120,7 +133,7 @@ local function get_next_string(state, in_pos)
end
end
in_pos = in_pos + 1
local token = optimize_lookups[string.byte(state[DATA], in_pos - state[OFFDATA], in_pos - state[OFFDATA])]
local token = optimize_lookups[string_byte(state[DATA], in_pos - state[OFFDATA], in_pos - state[OFFDATA])]
if token == nil then
state[OFFDATA] = in_pos - 1
state[DATA] = state[GEN]()
@ -129,8 +142,8 @@ local function get_next_string(state, in_pos)
return get_next_common(state, in_pos, token)
end
local function stream(defs, data)
local state = {}
local function stream(defs, data, state)
local state = state or {}
local fn
state[STATE] = defs
if type(data) == "function" then
@ -145,8 +158,8 @@ local function stream(defs, data)
return fn, state, state[OFFDATA]
end
local function parse(defs, data)
for pos, state, transemsg, etoken, estate in stream(defs, data) do
local function parse(defs, data, state)
for pos, state, transemsg, etoken, estate in stream(defs, data, state) do
if not state then
-- parse error
return nil, transemsg, etoken, estate
@ -165,6 +178,8 @@ return {
STATE = STATE,
COLLECT = COLLECT,
EOZ = EOZ,
FALLBACK = FALLBACK,
NUMBER = NUMBER,
stream = stream,
parse = parse,
-- common utility function

View File

@ -28,7 +28,7 @@ defs['-'] = function(state, token)
state.file = io.stdin
return "self"
end
defs[""] = function(state, token)
defs[parser.FALLBACK] = function(state, token)
if state.filename then
error("Must specify only one filename")
end
@ -47,7 +47,7 @@ defs[-1] = function(state, token, rule)
error("Unknown option: " .. token)
end
end
defs['--'] = parser.selfify({[""] = defs[""], [parser.EOZ] = defs[parser.EOZ]})
defs['--'] = parser.selfify({[parser.FALLBACK] = defs[parser.FALLBACK], [parser.EOZ] = defs[parser.EOZ]})
local state = parser.parse(defs, arg)
local luatokens = require "luatokens"

17
testc.lua Normal file
View File

@ -0,0 +1,17 @@
local function printr(...)
print(...)
return ...
end
local realload = load
load = function(target, ...)
if type(target) == "function" then
return realload(function() return printr(target()) end, ...)
else
return realload(printr(target), ...)
end
end
local cratera = require "cratera"
assert(printr(cratera.load("")))()

View File

@ -42,7 +42,7 @@ do -- trim left spaces
defs['\f'] = "whitespace"
defs['\v'] = "whitespace"
defs.whitespace = "self"
defs[''] = function(state, token)
defs[parser.FALLBACK] = function(state, token)
state[#state + 1] = token
if #state > 20 then
state[1] = table.concat(state)
@ -54,7 +54,7 @@ do -- trim left spaces
end
defs.start = {}
defs.start.self = defs.start
defs.start[''] = function(state, token)
defs.start[parser.FALLBACK] = function(state, token)
state[#state + 1] = token
if #state > 20 then
state[1] = table.concat(state)