Commit bdefad39 authored by Jamie A. Jennings's avatar Jamie A. Jennings

Now producing prefix exps (in tree form). And noticed that we do need a...

Now producing prefix exps (in tree form).  And noticed that we do need a converter to the rpl_1_2 parse tree format. That is because rpl_1_3 grammar does not have choice, and_exp, or sequence nodes.  We need to convert the "operator" nodes to those.
parent 1dfb8587
Pipeline #60927716 passed with stage
in 1 minute and 33 seconds
......@@ -113,21 +113,21 @@ operator = slash / and
grammar
syntax_error = { rest_of_line }
-- infix (and other) expression forms
exp = {atmos
!binding_prefix
atmos
term quantifier?
{ atmos
operator?
exp}*
}
exp = infix -- capture
alias infix = {atmos
!binding_prefix
atmos
term quantifier?
{ atmos
operator?
infix}*
}
grammar_exp = "grammar" identifier atmos (bindings "in")? exp
let_exp = "let" atmos (bindings "in")? exp
application = { identifier ":" } atmos (arg / arglist / rawarglist )
arglist = { atmos open int_or_exp { atmos "," int_or_exp }* close }
rawarglist = { atmos openraw int_or_exp { atmos "," int_or_exp }* closeraw }
arg = term / int
arg = term / int -- must be term, not exp
alias int_or_exp = atmos int / exp
alias term = {grammar_exp /
let_exp /
......
......@@ -55,7 +55,12 @@ local function new_stack()
end,
is_empty = function()
return top==0
end}
end,
to_list = function()
local ls = {}
for i=1,top do ls[i] = data[i]; end
return ls
end}
end
local function is_atmosphere(pt)
......@@ -96,7 +101,8 @@ function insert_seq_operators(exps)
for i, item in ipairs(exps) do
table.insert(result, item)
local succ = exps[i+1]
if item.type=="operator" then
if item.type=="operator" or is_atmosphere(item) then
-- no op
elseif succ and succ.type~="operator" then
table.insert(result,
{type = "operator",
......@@ -112,49 +118,38 @@ local shunting_yard_explist;
local function shunting_yard_exp(exp, attribute_table)
if DEBUG then print("*** entering shunting_yard_exp with: ", exp.type, " ", exp.data) end
if exp.type == "form.exp" or
exp.type == "form.cooked" or
local function map_shunting_yard_exp(ls)
return map(function(e) return shunting_yard_exp(e, attribute_table) end, ls)
end
if exp.type == "form.exp" then
return {type=exp.type,
s=exp.s,
e=exp.e,
data=exp.data,
subs=shunting_yard_explist(exp.subs)}
elseif exp.type == "form.cooked" or
exp.type == "form.raw" or
exp.type == "form.bracket" then
exp.type == "form.bracket" or
exp.type == "form.predicate" then
return {type=exp.type,
s=exp.s,
e=exp.e,
data=exp.data,
subs=shunting_yard_explist(exp.subs, attribute_table)}
subs=map_shunting_yard_exp(exp.subs)}
elseif exp.type == "form.grammar_exp" then
assert(false, "unhandled expression type")
elseif exp.type == "form.let_exp" then
assert(false, "unhandled expression type")
elseif exp.type == "form.application" then
local n = #exp.subs
local args = exp.subs[n]
local arg_form = exp.subs[n]
local newsubs = {}
for i=1,n-1 do newsubs[i] = exp.subs[i] end
if args.type=="form.arg" then
newsubs[n] = {type=args.type,
s=args.s,
e=args.e,
data=args.data,
subs=shunting_yard_explist(exp.subs[n].subs, attribute_table)}
elseif args.type=="form.arglist" or args.type=="form.rawarglist" then
newsubs[n] = {type=args.type,
s=args.s,
e=args.e,
data=args.data,
subs=map(shunting_yard_exp, args.subs)}
else
error("unexpected subform in application: " .. tostring(args.type))
end
return {type=exp.type,
s=exp.s,
e=exp.e,
data=exp.data,
subs=newsubs}
elseif exp.type == "form.predicate" then
local newsubs = { exp.subs[1] } -- predicate
for i=2, #exp.subs do
newsubs[i] = shunting_yard_exp(exp.subs[i], attribute_table)
end
newsubs[n] = {type=arg_form.type,
s=arg_form.s,
e=arg_form.e,
data=arg_form.data,
subs=map_shunting_yard_exp(arg_form.subs)}
return {type=exp.type,
s=exp.s,
e=exp.e,
......@@ -210,6 +205,32 @@ local function should_push(op, stack_top, attribute_table)
assert(false, "should not get here")
end
-- The stack of expressions in the original shunting yard algorithm contains
-- only expressions. In Rosie, it also contains items of atmosphere,
-- i.e. comments and newlines. We will collect those that come after an
-- expression and associate them with that expression.
local function pop_exp(exps)
local items = {}
while (not exps.is_empty()) do
local item = exps.pop()
table.insert(items, item)
if not is_atmosphere(item) then break; end
end
return items
end
local function make_prefix_tree(op, exps)
local right = pop_exp(exps)
local left = pop_exp(exps)
if DEBUG then
print("right operand: "); table.print(right)
print("left operand: "); table.print(left)
end
assert( right[1] and left[1] )
op.subs = list.append(left, right)
return op
end
--------------------------------------------------------------------------------
-- This is the shunting yard algorithm implementation, shunting_yard_explist().
-- Its first argument is a list containing expressions and operators. It is
......@@ -219,37 +240,42 @@ end
function shunting_yard_explist(subs, attribute_table)
if not attribute_table then attribute_table = OPERATOR_ATTRIBUTES end
if DEBUG then
io.stdout:write("entering: ")
io.stdout:write(" entering: ")
for _,v in ipairs(subs) do io.stdout:write(v.type, " ") end
print()
subs = attach_quantifiers(subs)
io.stdout:write("after attaching quantifiers: ")
io.stdout:write(" after attaching quantifiers: ")
for _,v in ipairs(subs) do io.stdout:write(v.type, " ") end
print()
subs = insert_seq_operators(subs)
io.stdout:write("after inserting seq operators: ")
io.stdout:write(" after inserting seq operators: ")
for _,v in ipairs(subs) do io.stdout:write(v.type, " ", v.data, " ") end
print()
end
local exps = {}
local exps = new_stack()
local opstack = new_stack()
for _, e in ipairs(subs) do
if is_atmosphere(e) then
table.insert(exps, e)
exps.push(e)
elseif e.type == "operator" then
while not should_push(e.data, opstack.peek(), attribute_table) do
table.insert(exps, opstack.pop())
-- To produce a postfix sequence of exps and ops:
-- exps.push(opstack.pop())
-- Instead, we will convert to prefix in tree form. Would be
-- simpler except that there's atmosphere on the exps stack.
exps.push(make_prefix_tree(opstack.pop(), exps))
end
opstack.push(e)
else
table.insert(exps, shunting_yard_exp(e, attribute_table))
exps.push(shunting_yard_exp(e, attribute_table))
end
end
while not opstack.is_empty() do
local op = opstack.pop()
table.insert(exps, op)
-- To produce a postfix sequence of exps and ops:
-- exps.push(opstack.pop())
exps.push(make_prefix_tree(opstack.pop(), exps))
end
return exps
return exps.to_list()
end
function shunting_yard(ls)
......@@ -348,39 +374,7 @@ function infix.to_prefix(pt)
end
--------------------------------------------------------------------------------
-- RPL 1.2 and RPL 1.3 are intended to accept the same language, though they
-- parse differently. The to_prefix() function restructures a parse tree
-- produced by the rpl_1_3 grammar so that it has a traditional prefix form,
-- where each operator, even if infix, is the root of a tree whose children are
-- the operands.
--
-- While modifying rpl_1_2 to produce rpl_1_3, we made a couple of minor changes
-- to make the to_prefix() algorithm easier to write. Specifically, form.exp
-- replaces the use of the alias form.infix (previously called form.exp_) in the
-- following productions:
-- (1) form.simple
-- (2) form.int_or_exp
--
-- The to_rpl_1_2 function alters a parse tree (AFTER to_prefix has been used)
-- to remove the capture node of type form.exp from the productions listed
-- above, because the RPL compiler does not expect them. In other words, we do
-- not want to change the compiler as a result of the change in parsing strategy.
function infix.to_rpl_1_2(pt)
if pt.type=="form.simple" or pt.type=="form.int_or_exp" then
assert(pt.subs and pt.subs[1] and pt.subs[1].type and
pt.subs[1].type=="form.exp" and not pt.subs[2])
return {type=pt.type,
s=pt.s,
e=pt.e,
data=pt.data,
subs=map(infix.to_rpl_1_2, pt.subs[1].subs)}
else
return pt
end
end
--------------------------------------------------------------------------------
-- FOR TESTING DURING DEVELOPMENT
function slurp(filename)
local f, err = io.open(filename)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment