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 end
return "string" return "string"
end 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) tsescapes.digitc[1]=function(state, token, rule)
if rule == nil then if rule == nil then
collect_fallback(state, string.char(state.in_digit)) collect_fallback(state, string.char(state.in_digit))
@ -346,7 +346,7 @@ do local tstring = selfify({})
do local tseskipwhitespace = selfify(mknewline({ do local tseskipwhitespace = selfify(mknewline({
string = defs.string, string = defs.string,
whitespace = "self", whitespace = "self",
[""] = "string", [parser.FALLBACK] = "string",
[1] = collect_fallback, [1] = collect_fallback,
}, 2)) }, 2))
--tseskipwhitespace["\n"] = setmetatable({[2] = countline, ["\r"] = setmetatable({}, {__index=tseskipwhitespace})}, {__index=tseskipwhitespace}) --tseskipwhitespace["\n"] = setmetatable({[2] = countline, ["\r"] = setmetatable({}, {__index=tseskipwhitespace})}, {__index=tseskipwhitespace})
@ -366,7 +366,7 @@ do local tstring = selfify({})
tstring['\n'] = false tstring['\n'] = false
tstring['\r'] = false tstring['\r'] = false
tstring[""] = "self" tstring[parser.FALLBACK] = "self"
tstring[1] = collect_fallback tstring[1] = collect_fallback
@ -385,7 +385,7 @@ end
do local tlongstring = {} do local tlongstring = {}
defs.longstring = 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[1] = false -- placeholder for newline handling
tllongstring_proper[2] = collect_fallback tllongstring_proper[2] = collect_fallback
@ -412,7 +412,7 @@ do local tlongstring = {}
return "maybe_end" return "maybe_end"
end end
end end
tllmaybe_end[""] = "longstring_proper" tllmaybe_end[parser.FALLBACK] = "longstring_proper"
tllmaybe_end[1] = collect_fallback tllmaybe_end[1] = collect_fallback
tllmaybe_end[-1] = function(state, token, rule) tllmaybe_end[-1] = function(state, token, rule)
if not rule then if not rule then
@ -473,6 +473,7 @@ mknewline(defs, 1)
defs["-"] = "maybe_comment" defs["-"] = "maybe_comment"
do local tmaybe_comment = setmetatable({["-"] = "comment"}, {__index=defs}) do local tmaybe_comment = setmetatable({["-"] = "comment"}, {__index=defs})
defs.maybe_comment = tmaybe_comment defs.maybe_comment = tmaybe_comment
tmaybe_comment[parser.EOZ] = "self" -- defs
tmaybe_comment[-1] = function(state, token, rule) tmaybe_comment[-1] = function(state, token, rule)
if rule ~= "comment" then if rule ~= "comment" then
state[#state+1] = "-" state[#state+1] = "-"
@ -480,12 +481,12 @@ do local tmaybe_comment = setmetatable({["-"] = "comment"}, {__index=defs})
end end
do local tmcomment = {comment_proper = selfify({})} do local tmcomment = {comment_proper = selfify({})}
tmaybe_comment.comment = tmcomment tmaybe_comment.comment = tmcomment
tmcomment[""] = "comment_proper" tmcomment[parser.FALLBACK] = "comment_proper"
tmcomment["["] = "maybe_longcomment" tmcomment["["] = "maybe_longcomment"
mknewline(tmcomment, 1, defs) mknewline(tmcomment, 1, defs)
mknewline(tmcomment.comment_proper, 1, defs) mknewline(tmcomment.comment_proper, 1, defs)
tmcomment.comment_proper[""] = "self" tmcomment.comment_proper[parser.FALLBACK] = "self"
do local tllongcomment_proper = selfify({[""] = "self", ["]"] = function(state, token) state.longcomment_close = 0 return "maybe_end" end}) do local tllongcomment_proper = selfify({[parser.FALLBACK] = "self", ["]"] = function(state, token) state.longcomment_close = 0 return "maybe_end" end})
tmcomment.longcomment = tllongcomment_proper tmcomment.longcomment = tllongcomment_proper
do local tllmaybe_end = selfify({defs = defs}, "maybe_end") do local tllmaybe_end = selfify({defs = defs}, "maybe_end")
tllongcomment_proper.maybe_end = tllmaybe_end tllongcomment_proper.maybe_end = tllmaybe_end
@ -504,7 +505,7 @@ do local tmaybe_comment = setmetatable({["-"] = "comment"}, {__index=defs})
return "maybe_end" return "maybe_end"
end end
end end
tllmaybe_end[""] = "longcomment_proper" tllmaybe_end[parser.FALLBACK] = "longcomment_proper"
tllmaybe_end[-1] = function(state, token, rule) tllmaybe_end[-1] = function(state, token, rule)
if not rule then if not rule then
state.longcomment_close = nil state.longcomment_close = nil
@ -543,6 +544,7 @@ end
local STATE = parser.STATE local STATE = parser.STATE
defs.multitokens = setmetatable({ defs.multitokens = setmetatable({
[parser.EOZ] = "self",
[-1] = function(state, token, rule) [-1] = function(state, token, rule)
if not state[STATE].multitoken[token] then if not state[STATE].multitoken[token] then
state[#state+1] = state[STATE].first state[#state+1] = state[STATE].first
@ -736,18 +738,23 @@ function defs.string_open(state, token)
assert("this shouldn't happen") assert("this shouldn't happen")
end end
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,
TK_RETURN = TK_RETURN, TK_THEN = TK_THEN, TK_TRUE = TK_TRUE, TK_UNTIL = TK_UNTIL, TK_WHILE = TK_WHILE,
TK_IDIV = TK_IDIV, TK_CONCAT = TK_CONCAT, TK_DOTS = TK_DOTS, TK_EQ = TK_EQ, TK_GE = TK_GE, TK_LE = TK_LE, TK_NE = TK_NE,
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 { return {
defs = defs, defs = defs,
tokens = { tokens = 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,
TK_RETURN = TK_RETURN, TK_THEN = TK_THEN, TK_TRUE = TK_TRUE, TK_UNTIL = TK_UNTIL, TK_WHILE = TK_WHILE,
TK_IDIV = TK_IDIV, TK_CONCAT = TK_CONCAT, TK_DOTS = TK_DOTS, TK_EQ = TK_EQ, TK_GE = TK_GE, TK_LE = TK_LE, TK_NE = TK_NE,
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
},
reverse_keywords = reverse_keywords, reverse_keywords = reverse_keywords,
reverse_tokens = { reverse_tokens = {
[TK_IDIV] = "//", [TK_CONCAT] = "..", [TK_DOTS] = "...", [TK_EQ] = "==", [TK_GE] = ">=", [TK_LE] = "<=", [TK_NE] = "~=", [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/>. along with this program. If not, see <https://www.gnu.org/licenses/>.
--]] --]]
local function ts(self) return getmetatable(self).__name end
-- key for STATE -- key for STATE
local STATE = {} local STATE = setmetatable({}, {__name="STATE", __tostring=ts})
-- key for DATA -- key for DATA
local DATA = {} local DATA = setmetatable({}, {__name="DATA", __tostring=ts})
-- key for GENERATOR -- key for GENERATOR
local GEN = {} local GEN = setmetatable({}, {__name="GEN", __tostring=ts})
-- key for DATA OFFSET -- key for DATA OFFSET
local OFFDATA = {} local OFFDATA = setmetatable({}, {__name="OFFDATA", __tostring=ts})
-- key for End of Stream -- 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 = {} local optimize_lookups = {}
for i=0, 255 do for i=0, 255 do
optimize_lookups[i] = string.char(i) optimize_lookups[i] = string.char(i)
end end
local type, tostring local type, tostring, string_byte
= type, tostring = type, tostring, string.byte
local function get_next_common(state, in_pos, token) local function get_next_common(state, in_pos, token)
-- note: must preserve "token" - do not call recursively with a different token -- note: must preserve "token" - do not call recursively with a different token
local transition local transition, retry
if state[STATE] then local st = state[STATE]
local st = state[STATE] if st then
local rule = st[token] local rule = st[token]
if not rule and token == EOZ then if not rule and token == EOZ then
return in_pos, state return in_pos, state
end end
if type(token) == "number" then
rule = st[NUMBER]
end
do -- pre-hooks do -- pre-hooks
local pos = -1 local pos = -1
local hook = st[pos] local hook = st[pos]
@ -57,7 +66,7 @@ local function get_next_common(state, in_pos, token)
end end
transition = rule transition = rule
if transition == nil then if transition == nil then
transition = st[""] transition = st[FALLBACK]
end end
local recheck = true local recheck = true
while recheck do while recheck do
@ -67,7 +76,10 @@ local function get_next_common(state, in_pos, token)
transition = st[transition] transition = st[transition]
recheck = true recheck = true
elseif tytrans == "function" then 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 recheck = true
end end
end end
@ -88,8 +100,9 @@ local function get_next_common(state, in_pos, token)
if not state[STATE] then if not state[STATE] then
-- unexpected token. stream consumer may attempt to recover, -- unexpected token. stream consumer may attempt to recover,
-- but we do this mostly to differentiate it from "end of stream" condition. -- 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 end
if retry then in_pos = in_pos - 1 end
return in_pos, state, transition -- TODO is this what we should be returning? return in_pos, state, transition -- TODO is this what we should be returning?
end end
@ -120,7 +133,7 @@ local function get_next_string(state, in_pos)
end end
end end
in_pos = in_pos + 1 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 if token == nil then
state[OFFDATA] = in_pos - 1 state[OFFDATA] = in_pos - 1
state[DATA] = state[GEN]() state[DATA] = state[GEN]()
@ -129,8 +142,8 @@ local function get_next_string(state, in_pos)
return get_next_common(state, in_pos, token) return get_next_common(state, in_pos, token)
end end
local function stream(defs, data) local function stream(defs, data, state)
local state = {} local state = state or {}
local fn local fn
state[STATE] = defs state[STATE] = defs
if type(data) == "function" then if type(data) == "function" then
@ -145,8 +158,8 @@ local function stream(defs, data)
return fn, state, state[OFFDATA] return fn, state, state[OFFDATA]
end end
local function parse(defs, data) local function parse(defs, data, state)
for pos, state, transemsg, etoken, estate in stream(defs, data) do for pos, state, transemsg, etoken, estate in stream(defs, data, state) do
if not state then if not state then
-- parse error -- parse error
return nil, transemsg, etoken, estate return nil, transemsg, etoken, estate
@ -165,6 +178,8 @@ return {
STATE = STATE, STATE = STATE,
COLLECT = COLLECT, COLLECT = COLLECT,
EOZ = EOZ, EOZ = EOZ,
FALLBACK = FALLBACK,
NUMBER = NUMBER,
stream = stream, stream = stream,
parse = parse, parse = parse,
-- common utility function -- common utility function

View File

@ -28,7 +28,7 @@ defs['-'] = function(state, token)
state.file = io.stdin state.file = io.stdin
return "self" return "self"
end end
defs[""] = function(state, token) defs[parser.FALLBACK] = function(state, token)
if state.filename then if state.filename then
error("Must specify only one filename") error("Must specify only one filename")
end end
@ -47,7 +47,7 @@ defs[-1] = function(state, token, rule)
error("Unknown option: " .. token) error("Unknown option: " .. token)
end end
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 state = parser.parse(defs, arg)
local luatokens = require "luatokens" 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['\f'] = "whitespace"
defs['\v'] = "whitespace" defs['\v'] = "whitespace"
defs.whitespace = "self" defs.whitespace = "self"
defs[''] = function(state, token) defs[parser.FALLBACK] = function(state, token)
state[#state + 1] = token state[#state + 1] = token
if #state > 20 then if #state > 20 then
state[1] = table.concat(state) state[1] = table.concat(state)
@ -54,7 +54,7 @@ do -- trim left spaces
end end
defs.start = {} defs.start = {}
defs.start.self = defs.start defs.start.self = defs.start
defs.start[''] = function(state, token) defs.start[parser.FALLBACK] = function(state, token)
state[#state + 1] = token state[#state + 1] = token
if #state > 20 then if #state > 20 then
state[1] = table.concat(state) state[1] = table.concat(state)