4 require
"fa_algorithms"
5 require
"bootstrap/rtn"
8 function find_state(rule
, slotnum
)
13 for s
in each(rule
:states()) do
14 for edge_val
, dest_state
, properties
in s
:transitions() do
15 if properties
.slotnum
== slotnum
then
21 error(string.format("No such slotnum (%d) for rule %s", slotnum
, rule_str
))
24 function get_target_for_slotnum(state
, slotnum
)
28 for edge_val
, dest_state
, properties
in state
:transitions() do
29 if properties
.slotnum
== slotnum
then
30 return {edge_val
, dest_state
}
34 error(string.format("Slotnum %d not found for state %s", slotnum
, serialize(state
, 4, " ")))
37 function parse_gla(str
, rtn_state
)
38 local stream
= CharStream
:new(str
)
39 stream
:ignore("whitespace")
40 local gla
= fa
.GLA
:new()
41 gla
.rtn_state
= rtn_state
42 local states
= {[1]=gla
.start
}
43 while not stream
:eof() do
44 local statenum
= tonumber(stream
:consume_pattern("%d+"))
45 local state
= states
[statenum
]
47 error(string.format("GLA refers to state %d before it is used as a target",
50 while stream
:lookahead(1) ~= ";" do
52 local term
= stream
:consume_pattern("%w+")
54 local dest_state_num
= tonumber(stream
:consume_pattern("%d+"))
55 states
[dest_state_num
] = states
[dest_state_num
] or fa
.GLAState
:new()
56 local dest_state
= states
[dest_state_num
]
58 state
:add_transition(fa
.eof
, dest_state
)
60 state
:add_transition(term
, dest_state
)
62 if stream
:lookahead(1) == "(" then
64 local final_state_slotnum
= tonumber(stream
:consume_pattern("%d+"))
66 dest_state
.final
= get_target_for_slotnum(rtn_state
, final_state_slotnum
)
76 function assert_lookahead(grammar_str
, rule_str
, slotnum
, expected_gla_str
)
77 grammar
= parse_grammar(CharStream
:new(grammar_str
))
78 grammar
:determinize_rtns()
79 grammar
:minimize_rtns()
81 local rule
= grammar
.rtns
:get(rule_str
)
82 state
= find_state(rule
, slotnum
)
83 expected_gla
= parse_gla(expected_gla_str
, state
)
85 compute_lookahead(grammar
)
87 if not fa_isequal(expected_gla
, state
.gla
) then
88 local bad
= io
.open("bad.dot", "w")
89 bad
:write("digraph untitled {\n")
90 bad
:write(state
.gla
:to_dot())
94 local good
= io
.open("good.dot", "w")
95 good
:write("digraph untitled {\n")
96 good
:write(expected_gla
:to_dot())
100 os
.execute("dot -Tpng -o good.png good.dot")
101 os
.execute("dot -Tpng -o bad.png bad.dot")
103 error("GLAs were not equal: expected and actual are " ..
104 "in good.png and bad.png, respectively")
109 function TestSimple
:test1()
123 function TestSimple
:test_multiple_recursions()
139 function TestEpsilon
:test1()
154 function TestEpsilon
:test2()
169 function TestEpsilon
:test3()
185 function TestEpsilon
:test4()
202 function TestEpsilon
:test5()
220 TODO
: add this test when GLAs that tell RTNs to
return are supported
.
221 function TestEpsilon
:test5()
236 TestMultipleNonterms
= {}
237 function TestMultipleNonterms
:test1()
254 function TestMultipleNonterms
:test22()
274 function TestLL2
:test1()
277 s -> "X" "Y" | "X" "Z";
288 function TestLL2
:test2()
303 function TestLL2
:test3()
319 function TestLL2
:test3()
322 s -> "X"? "Y" | "X" "Z";
335 function TestLL3
:test1()
339 a -> ("P" | "Q") ("P" | "Q");
353 function TestLL3
:test2()
357 a -> ("P" | "Q")? ("P" | "Q")?;
375 -- This is equivalent to the grammar on page 271 of The Definitive ANTLR Reference.
376 function TestLL3
:test3()
379 s -> "X" s "Y" | "X" "X" "Z";
391 function TestEOF
:test1()
405 function TestEOF
:test2()
406 -- this is the example used by Terence Parr in his discussion of ANTLR 3.0's
407 -- lookahead analysis: http://www.antlr.org/blog/antlr3/lookahead.tml
425 function TestLLStar
:test1()
440 -- Test lookahead that we can only compute correctly if we apply the
441 -- tail-recursion optimization.
442 function TestLLStar
:test2()
458 function TestFollow
:test1()
473 function TestFollow
:test2()
487 function assert_fails_with_error(grammar_str
, error_string
)
488 grammar
= parse_grammar(CharStream
:new(grammar_str
))
489 grammar
:determinize_rtns()
490 grammar
:minimize_rtns()
492 local success
, message
= pcall(compute_lookahead
, grammar
)
494 error("Failed to fail!")
495 elseif not message
:find(error_string
) then
496 error("Failed with wrong message! Message was supposed to start with "
497 .. error_string
.. ", instead it was: " .. message
)
501 function assert_left_recursive(grammar_str
)
502 assert_fails_with_error(grammar_str
, "Grammar is not LL%(%*%): it is left%-recursive!")
505 TestDetectNonLLStar
= {}
506 function TestDetectNonLLStar
:test_left_recursive()
507 assert_left_recursive(
514 function TestDetectNonLLStar
:test_left_recursive2()
515 assert_left_recursive(
523 LuaUnit
:run(unpack(arg
))