aurum 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (28) hide show
  1. data/example/expression/expression.rb +29 -0
  2. data/lib/aurum.rb +10 -0
  3. data/lib/aurum/engine.rb +173 -0
  4. data/lib/aurum/grammar.rb +234 -0
  5. data/lib/aurum/lexical_table_generator.rb +423 -0
  6. data/lib/aurum/parsing_table_generator.rb +445 -0
  7. data/test/engine/lexer_test.rb +52 -0
  8. data/test/engine/semantic_attributes_test.rb +15 -0
  9. data/test/grammar_definition/character_class_definition_test.rb +28 -0
  10. data/test/grammar_definition/grammar_definition_test.rb +54 -0
  11. data/test/grammar_definition/lexical_definition_test.rb +56 -0
  12. data/test/grammar_definition/operator_precedence_definition_test.rb +35 -0
  13. data/test/grammar_definition/production_definition_test.rb +60 -0
  14. data/test/lexical_table_generator/automata_test.rb +74 -0
  15. data/test/lexical_table_generator/character_set_test.rb +73 -0
  16. data/test/lexical_table_generator/interval_test.rb +36 -0
  17. data/test/lexical_table_generator/pattern_test.rb +109 -0
  18. data/test/lexical_table_generator/subset_determinizer_test.rb +19 -0
  19. data/test/lexical_table_generator/table_generator_test.rb +126 -0
  20. data/test/parsing_table_generator/augmented_grammar_test.rb +45 -0
  21. data/test/parsing_table_generator/lalr_n_computation_test.rb +89 -0
  22. data/test/parsing_table_generator/lr_0_automata_test.rb +91 -0
  23. data/test/parsing_table_generator/lr_item_test.rb +33 -0
  24. data/test/parsing_table_generator/parsing_table_state_test.rb +39 -0
  25. data/test/parsing_table_generator/precedence_table_test.rb +28 -0
  26. data/test/parsing_table_generator/production_test.rb +9 -0
  27. data/test/test_helper.rb +103 -0
  28. metadata +78 -0
@@ -0,0 +1,36 @@
1
+ $:.unshift(File.dirname(__FILE__) + '/../')
2
+ require 'test_helper'
3
+
4
+ class IntervalTest < Test::Unit::TestCase
5
+ def test_should_include_character_in_interval
6
+ interval = Aurum::CharacterSet::Interval.new ?a, ?c
7
+ assert interval.include?('b')
8
+ assert !interval.include?('d')
9
+ end
10
+
11
+ def test_should_use_lowest_as_first_and_highest_as_end
12
+ interval_a = Aurum::CharacterSet::Interval.new ?a, ?c
13
+ interval_b = Aurum::CharacterSet::Interval.new ?b, ?d
14
+ assert interval_a.merge!(interval_b)
15
+ assert_equal Aurum::CharacterSet::Interval.new(?a, ?d), interval_a
16
+ end
17
+
18
+ def test_should_cat_two_intervals
19
+ interval_a = Aurum::CharacterSet::Interval.new ?a, ?c
20
+ interval_b = Aurum::CharacterSet::Interval.new ?d, ?f
21
+ assert interval_a.merge!(interval_b)
22
+ assert_equal Aurum::CharacterSet::Interval.new(?a, ?f), interval_a
23
+ end
24
+
25
+ def test_should_return_nil_if_two_intervls_do_not_have_any_char_in_common
26
+ interval_a = Aurum::CharacterSet::Interval.new ?a, ?c
27
+ interval_b = Aurum::CharacterSet::Interval.new ?e, ?f
28
+ assert !interval_a.merge!(interval_b)
29
+ end
30
+
31
+ def test_should_def_interval_for_single_char
32
+ interval = Aurum::CharacterSet::Interval.new ?a
33
+ assert interval.include?('a')
34
+ assert !interval.include?('b')
35
+ end
36
+ end
@@ -0,0 +1,109 @@
1
+ $:.unshift(File.dirname(__FILE__) + '/../')
2
+ require 'test_helper'
3
+
4
+ class PatternTest < Test::Unit::TestCase
5
+ Epsilon = Aurum::Epsilon
6
+
7
+ def test_should_match_single_string
8
+ pattern = Aurum::Pattern.from_string 'a'
9
+ assert match?('a', pattern)
10
+ assert !match?('b', pattern)
11
+ end
12
+
13
+ def test_should_match_string_literal
14
+ pattern = Aurum::Pattern.from_string 'abc'
15
+ assert match?('abc', pattern)
16
+ assert !match?('abcabc', pattern)
17
+ assert !match?('bcd', pattern)
18
+ end
19
+
20
+ def test_should_match_character_set
21
+ char_set = Aurum::CharacterSet::Interval.new(?A, ?Z).to_char_set
22
+ pattern = Aurum::Pattern.from_char_set char_set
23
+ ('A'..'Z').each {|x| assert match?(x, pattern)}
24
+ end
25
+
26
+ def test_should_match_string_literal_zero_or_more_times
27
+ pattern = Aurum::Pattern.from_string('abc').kleene
28
+ assert match?('', pattern)
29
+ assert match?('abc' * 10, pattern)
30
+ assert !match?('ab', pattern)
31
+ end
32
+
33
+ def test_should_match_string_literal_one_or_more_times
34
+ pattern = Aurum::Pattern.from_string('abc').iterate
35
+ assert match?('abc', pattern)
36
+ assert match?('abc' * 10, pattern)
37
+ assert !match?('', pattern)
38
+ end
39
+
40
+ def test_should_match_string_literal_zero_or_one_time
41
+ pattern = Aurum::Pattern.from_string('abc').opt
42
+ assert match?('', pattern)
43
+ assert match?('abc', pattern)
44
+ assert !match?('abc' * 2, pattern)
45
+ end
46
+
47
+ def test_should_match_concate_string
48
+ first = Aurum::Pattern.from_string 'first'
49
+ second = Aurum::Pattern.from_string 'second'
50
+ pattern = Aurum::Pattern.concat(first, second)
51
+ assert match?('firstsecond', pattern)
52
+ assert !match?('first', pattern)
53
+ assert !match?('second', pattern)
54
+ end
55
+
56
+ def test_should_match_pattern_a_or_pattern_b
57
+ pattern_a = Aurum::Pattern.from_string 'patterna'
58
+ pattern_b = Aurum::Pattern.from_string 'patternb'
59
+ pattern = pattern_a | pattern_b
60
+ ['patterna', 'patternb'].each {|x| assert match?(x, pattern)}
61
+ end
62
+
63
+ def test_should_match_pattern_n_times
64
+ pattern = Aurum::Pattern.from_string 'pattern'
65
+ assert match?('pattern' * 5, pattern[5])
66
+ assert match?('pattern' * 10, pattern[10])
67
+ end
68
+
69
+ def test_should_match_pattern_n_to_m_times
70
+ pattern = Aurum::Pattern.from_string('pattern')[5, 7]
71
+ (5..7).each {|x| assert match?('pattern' * x, pattern)}
72
+ assert !match?('pattern' * 4, pattern)
73
+ assert !match?('pattern' * 8, pattern)
74
+ end
75
+
76
+ def test_should_match_everything_but_the_strings_matched_by_pattern
77
+ pattern = Aurum::Pattern.from_string 'pattern'
78
+ negative_pattern = pattern.negate
79
+ assert !match?('pattern', negative_pattern)
80
+ assert match?('anything', negative_pattern)
81
+ end
82
+
83
+ def test_should_match_everything_upto_first_occurrence_of_a_text_matched_by_pattern
84
+ pattern = ~ Aurum::Pattern.from_string('*/')
85
+ assert match?('comments */', pattern)
86
+ assert !match?('everything', pattern)
87
+ end
88
+
89
+ def match? expected_string, pattern
90
+ states = closure pattern.automata.table, [0]
91
+ expected_string.each_byte {|char| states = move(pattern.automata.table, states, char)}
92
+ states.include?(pattern.accept)
93
+ end
94
+
95
+ def move automata, states, char
96
+ result = []
97
+ states.each {|state| automata[state].each {|tran| result.concat(closure(automata, [tran.destination])) if tran.symbols.include? char} }
98
+ result.uniq;
99
+ end
100
+
101
+ def closure automata, states
102
+ closure, unvisited = Set.new(states.dup), states.dup
103
+ filter = lambda {|x| x.symbols == Epsilon && !closure.include?(x.destination)}
104
+ while !unvisited.empty? do
105
+ automata[unvisited.pop].grep_each(filter){|tran| [closure, unvisited].each {|x| x << tran.destination}}
106
+ end
107
+ closure.to_a
108
+ end
109
+ end
@@ -0,0 +1,19 @@
1
+ $:.unshift(File.dirname(__FILE__) + '/../')
2
+ require 'test_helper'
3
+ require 'set'
4
+
5
+ class SubsetDeterminizerTest < Test::Unit::TestCase
6
+ def test_should_create_equivalentDFA()
7
+ a, b, abb = Aurum::Pattern.from_string('a'), Aurum::Pattern.from_string('b'), Aurum::Pattern.from_string('abb')
8
+ pattern = Aurum::Pattern.concat((a | b).kleene, abb)
9
+ automata, accepts = pattern.automata.determinize [pattern.accept]
10
+ final = move automata.table, 'aaabbbaaabb'
11
+ assert accepts.include?(final)
12
+ end
13
+
14
+ def move table, source
15
+ state = 0
16
+ source.each_byte {|char| state = (table[state].find {|tran| tran.symbols.include? char}).destination}
17
+ state
18
+ end
19
+ end
@@ -0,0 +1,126 @@
1
+ $:.unshift(File.dirname(__FILE__) + '/../')
2
+ require 'test_helper'
3
+
4
+ Aurum::LexicalTableGenerator.class_eval do
5
+ attr_reader :accept_states, :partitions, :lexical_automata
6
+
7
+ public :construct_automata, :make_initial_partitions, :refine_partitions
8
+
9
+ def table
10
+ @lexical_automata.table
11
+ end
12
+ end
13
+
14
+ class LexicalTableGeneratorTest < Test::Unit::TestCase
15
+ def test_should_construct_sub_automata_for_lexical_states
16
+ specification = {:initial => {PATTERN_A => 'recognize'},
17
+ :state_a => {PATTERN_B => 'recognize'}}
18
+
19
+ generator = Aurum::LexicalTableGenerator.new specification
20
+ generator.construct_automata
21
+ @table, @accepts, @lexical_states = generator.table, generator.accept_states, generator.lexical_states
22
+
23
+ assert recognize?(:initial, 'pattern_a')
24
+ assert !recognize?(:initial, 'pattern_b')
25
+ assert !recognize?(:state_a, 'pattern_a')
26
+ assert recognize?(:state_a, 'pattern_b')
27
+ end
28
+
29
+ def test_should_add_common_patterns_to_all_lexical_states
30
+ specification = {:initial => {PATTERN_A => 'recognize'},
31
+ :state_a => {PATTERN_B => 'recognize'},
32
+ :all => {PATTERN_C => 'recognize'}}
33
+
34
+ generator = Aurum::LexicalTableGenerator.new specification
35
+ generator.construct_automata
36
+ @table, @accepts, @lexical_states = generator.table, generator.accept_states, generator.lexical_states
37
+
38
+ assert recognize?(:initial, 'pattern_c')
39
+ assert recognize?(:state_a, 'pattern_b')
40
+ assert !recognize?(:all, 'pattern_b')
41
+ end
42
+
43
+ def test_initial_partiations_should_be_start_accepts_and_non_accepts
44
+ specification = {:initial => {PATTERN_A => 'recognize'},
45
+ :state_a => {PATTERN_B => 'recognize'}}
46
+
47
+ generator = Aurum::LexicalTableGenerator.new specification
48
+ generator.construct_automata
49
+ generator.make_initial_partitions
50
+ partitions = generator.partitions
51
+
52
+ assert_equal 3, partitions.size
53
+ assert partitions.include?([0])
54
+ assert partitions.include?(generator.accept_states.keys)
55
+ assert partitions.include?(generator.lexical_automata.all_states - generator.accept_states.keys - [0])
56
+ end
57
+
58
+ def test_should_not_split_accept_states_if_has_same_action
59
+ specification = {:initial => {PATTERN_A => 'recognize', PATTERN_B => 'recognize'}}
60
+ generator = Aurum::LexicalTableGenerator.new specification
61
+ generator.construct_automata
62
+ generator.make_initial_partitions
63
+
64
+ assert generator.partitions.include?(generator.accept_states.keys)
65
+ end
66
+
67
+ def test_should_split_accept_states_if_has_different_actions
68
+ specification = {:initial => {PATTERN_A => 'recognizeA', PATTERN_B => 'recognizeB'}}
69
+ generator = Aurum::LexicalTableGenerator.new specification
70
+ generator.construct_automata
71
+ generator.make_initial_partitions
72
+
73
+ assert !generator.partitions.include?(generator.accept_states.keys)
74
+ end
75
+
76
+ def test_should_partition_size_should_equal_to_state_size_if_min_dfa_given
77
+ specification = {:initial => {PATTERN_A => 'recognize'}}
78
+ generator = Aurum::LexicalTableGenerator.new specification
79
+ generator.construct_automata
80
+ generator.make_initial_partitions
81
+ generator.refine_partitions
82
+ assert_equal generator.table.size, generator.partitions.size
83
+ end
84
+
85
+ def test_should_partition_size_should_less_than_state_size
86
+ specification = {:initial => {ABABB => 'recognize'}}
87
+ generator = Aurum::LexicalTableGenerator.new specification
88
+ generator.construct_automata
89
+ generator.make_initial_partitions
90
+ generator.refine_partitions
91
+ assert generator.table.size > generator.partitions.size
92
+ end
93
+
94
+ def test_should_return_original_automata_if_min_dfa_given
95
+ specification = {:initial => {PATTERN_A => 'recognize'}}
96
+ generator = Aurum::LexicalTableGenerator.new specification
97
+ lexical_table, accepts = generator.lexical_table
98
+ assert generator.table.eql?(lexical_table)
99
+ end
100
+
101
+ def test_should_recognize_same_lexeme
102
+ specification = {:initial => {ABABB => 'recognize'},
103
+ :state_a => {PATTERN_B => 'recognize'},
104
+ :all => {PATTERN_C => 'recognize'}}
105
+
106
+ generator = Aurum::LexicalTableGenerator.new specification
107
+ @table, @accepts = generator.lexical_table
108
+ @lexical_states = generator.lexical_states
109
+
110
+ assert recognize?(:initial, 'aabaabaabb')
111
+ assert !recognize?(:initial, 'patterna')
112
+ assert recognize?(:state_a, 'pattern_b')
113
+ assert !recognize?(:all, 'pattern_b')
114
+ end
115
+
116
+ def recognize? lexical_state, source
117
+ begin
118
+ lexical_state = - @lexical_states.index(lexical_state) - 1
119
+ state = (@table[0].find {|tran| tran.symbols.include?(lexical_state)}).destination
120
+ source.each_byte {|char| state = (@table[state].find {|tran| tran.symbols.include? char}).destination}
121
+ @accepts.keys.include?(state)
122
+ rescue
123
+ false
124
+ end
125
+ end
126
+ end
@@ -0,0 +1,45 @@
1
+ $:.unshift(File.dirname(__FILE__) + '/../')
2
+ require 'test_helper'
3
+ require 'set'
4
+
5
+ Aurum::ParsingTableGenerator.class_eval do
6
+ attr_reader :nullables, :first_sets
7
+ end
8
+
9
+ class AugmentedGrammarTest < Test::Unit::TestCase
10
+ def atest_should_find_all_used_symbols
11
+ generator = parser_generator E=>[production(E, T)], T=>[production(T, F)], F=>[production(F, ID)]
12
+ generator.start_from E
13
+ assert_equal [E, T, F, ID], generator.symbols
14
+ generator.start_from T
15
+ assert_equal [T, F, ID], generator.symbols
16
+ end
17
+
18
+ def atest_should_find_all_used_productions
19
+ generator = parser_generator E=>[production(E, T)], T=>[production(T, F)], F=>[production(F, ID)]
20
+ generator.start_from E
21
+ assert_equal [production(START, E), production(E, T), production(T, F), production(F, ID)].to_set, generator.productions.to_set
22
+ generator.start_from T
23
+ assert_equal [production(START, T), production(T, F), production(F, ID)].to_set, generator.productions.to_set
24
+ end
25
+
26
+ def test_should_compute_nullable_nonterminals
27
+ generator = parser_generator E=>[production(E, T)], T=>[production(T)], F=>[production(F, T, ID)]
28
+ generator.start_from E
29
+ assert_equal [T, E, START].to_set, generator.nullables.to_set
30
+ generator.start_from F
31
+ assert_equal [T].to_set, generator.nullables.to_set
32
+ end
33
+
34
+ def atest_first_set_should_contain_terminals_left_depends_on_nt_dirctly
35
+ generator = parser_generator E=>[production(E, T, ID), production(E, T, T, T, terminal('other'))], T=>[production(T)]
36
+ generator.start_from E
37
+ assert_equal [ID, terminal('other')].to_set, generator.first_sets[E].to_set
38
+ end
39
+
40
+ def atest_should_contain_fist_set_of_nt_which_left_depends_on_nt_dirctly
41
+ generator = parser_generator E=>[production(E, T, ID), production(E, T, T, T, terminal('other'))], T=>[production(T)], F=>[production(F, T, E)]
42
+ generator.start_from F
43
+ assert_equal generator.first_sets[F].to_set, generator.first_sets[E].to_set
44
+ end
45
+ end
@@ -0,0 +1,89 @@
1
+ $:.unshift(File.dirname(__FILE__) + '/../')
2
+ require 'test_helper'
3
+
4
+ Aurum::ParsingTableGenerator.class_eval do
5
+ attr_reader :states
6
+ public :construct_LR0_automata, :compute_LALR_1_lookahead, :compute_LALR_n_lookahead, :default_action
7
+ end
8
+ class LALRLookaheadComputationTest < Test::Unit::TestCase
9
+
10
+ def test_should_compute_reduce_action_for_inconsistent_states
11
+ generator = parser_generator EXPRESSION_GRAMMAR_LALR1
12
+ generator.start_from E
13
+ generator.construct_LR0_automata
14
+ states = generator.states.find_all {|x| !x.consistent?}
15
+ generator.compute_LALR_1_lookahead
16
+ assert_equal [reduce(0)].to_set, states[0][terminal('$eof')]
17
+ assert_equal [reduce(2)].to_set, states[1][terminal('+')]
18
+ assert_equal [reduce(2)].to_set, states[1][terminal(')')]
19
+ assert_equal [reduce(1)].to_set, states[2][terminal('+')]
20
+ assert_equal [reduce(1)].to_set, states[2][terminal(')')]
21
+ end
22
+
23
+ def test_should_replace_conficted_state_actions_with_lookahead_action
24
+ generator = parser_generator BNF_GRAMMAR_LALR2
25
+ generator.start_from BNF
26
+ generator.construct_LR0_automata
27
+ generator.compute_LALR_1_lookahead
28
+ conflicted_state = (generator.states.find_all {|x| x.conflicted? })[0]
29
+ generator.compute_LALR_n_lookahead
30
+ assert_equal [lookahead_shift(generator.states.length - 1)].to_set, conflicted_state[terminal('s')]
31
+ assert !conflicted_state.conflicted?
32
+ end
33
+
34
+ def test_should_add_reduce_action_to_lookahead_state
35
+ generator = parser_generator BNF_GRAMMAR_LALR2
36
+ generator.start_from BNF
37
+ generator.construct_LR0_automata
38
+ generator.compute_LALR_1_lookahead
39
+ generator.compute_LALR_n_lookahead
40
+ states = generator.states
41
+ assert_equal [read_reduce(6)].to_set, states[-1][terminal('s')]
42
+ assert_equal [read_reduce(6)].to_set, states[-1][terminal('$eof')]
43
+ assert_equal [reduce(4)].to_set, states[-1][terminal('->')]
44
+ end
45
+
46
+ def test_should_return_default_reduce_if_no_action_for_given_symbol
47
+ generator = parser_generator BNF_GRAMMAR_LALR2
48
+ generator.start_from BNF
49
+ generator.construct_LR0_automata
50
+ generator.compute_LALR_1_lookahead
51
+ generator.compute_LALR_n_lookahead
52
+ default_actions = generator.states.map {|x| generator.default_action x}
53
+ assert_equal [reduce(2), reduce(1), nil, reduce(5), reduce(4), reduce(4)], default_actions
54
+ end
55
+
56
+ def test_should_return_lookahead_level
57
+ generator = parser_generator SIMPLE_GRAMMAR_LR0
58
+ table, level = generator.start_from(E).parsing_table
59
+ assert_equal 0, level
60
+ generator = parser_generator EXPRESSION_GRAMMAR_LALR1
61
+ table, level = generator.start_from(E).parsing_table
62
+ assert_equal 1, level
63
+ generator = parser_generator IF_GRAMMAR_LALR2
64
+ table, level = generator.start_from(STATEMENT).parsing_table
65
+ assert_equal 2, level
66
+ end
67
+
68
+ def test_should_raise_error_if_grammar_not_lalr_n
69
+ begin
70
+ parser_generator(NOT_LALR_GRAMMAR).start_from(E).parsing_table
71
+ assert false
72
+ rescue RuntimeError => error
73
+ assert_equal 'not LALR(n)', error.message
74
+ end
75
+ end
76
+
77
+ def test_should_resolve_conflicts_for_expression
78
+ op_a, op_b = terminal('+'), terminal('*')
79
+ generator = parser_generator EXPRESSION_GRAMMAR, [[op_b], [op_a]]
80
+ generator.start_from E
81
+ generator.construct_LR0_automata
82
+ generator.compute_LALR_1_lookahead
83
+ state = generator.states.last
84
+ assert_equal 1, state[terminal('+')].size
85
+ assert state[terminal('+')].to_a.first.kind_of?(Aurum::ReduceAction)
86
+ assert_equal 1, state[terminal('*')].size
87
+ assert state[terminal('*')].to_a.first.kind_of?(Aurum::ShiftAction)
88
+ end
89
+ end
@@ -0,0 +1,91 @@
1
+ $:.unshift(File.dirname(__FILE__) + '/../')
2
+ require 'test_helper'
3
+
4
+ Aurum::ParsingTableGenerator.class_eval do
5
+ attr_reader :states
6
+ public :closure, :goto, :read_set, :construct_LR0_automata
7
+ end
8
+
9
+ class LR0AutomataTest < Test::Unit::TestCase
10
+ def test_closure_should_contain_items_themselves
11
+ generator = parser_generator E=>[production(E, ID)]
12
+ assert_equal [LR_item(0, E, ID)], generator.closure([LR_item(0, E, ID)])
13
+ end
14
+
15
+ def test_closure_should_contain_all_right_most_lr_items_of_dot_symbol
16
+ generator = parser_generator E=>[production(E, T)], T=>[production(T, ID), production(T, terminal('other'))]
17
+ closure = generator.closure [LR_item(0, E, T)]
18
+ [LR_item(0, T, ID), LR_item(0, T, terminal('other'))].each {|x| assert closure.include?(x)}
19
+ end
20
+
21
+ def test_should_return_goto_items_if_expected_symbol_given
22
+ generator = parser_generator E=>[production(E, T)], T=>[production(T, ID), production(T, terminal('other'))]
23
+ assert_equal [LR_item(1, E, T)], generator.goto([LR_item(0, E, T)], T)
24
+ end
25
+
26
+ def test_goto_items_should_be_closured_if_expected_symbol_given
27
+ generator = parser_generator E=>[production(E, T, T)], T=>[production(T, ID), production(T, terminal('other'))]
28
+ goto = generator.goto [LR_item(0, E, T, T)], T
29
+ [LR_item(0, T, ID), LR_item(0, T, terminal('other'))].each {|x| assert goto.include?(x)}
30
+ end
31
+
32
+ def test_should_use_LR0_items_of_collection_as_state
33
+ generator = parser_generator SIMPLE_GRAMMAR_LR0
34
+ generator.start_from E
35
+ generator.construct_LR0_automata
36
+ states = generator.states
37
+ assert 3, states.length
38
+ assert [LR_item(0, START, E), LR_item(0, T, terminal('+'), T), LR_item(0, T, ID)], states[0]
39
+ assert [LR_item(1, T, terminal('+'), T)], states[1]
40
+ assert [LR_item(2, T, terminal('+'), T), LR_item(0, T, ID)], states[2]
41
+ end
42
+
43
+ def test_should_add_shift_action_to_states
44
+ generator = parser_generator SIMPLE_GRAMMAR_LR0
45
+ generator.start_from E
46
+ generator.construct_LR0_automata
47
+ states = generator.states
48
+ assert_equal [shift(1)].to_set, states[0][T]
49
+ assert_equal [shift(2)].to_set, states[1][terminal('+')]
50
+ end
51
+
52
+ def test_should_add_read_reduce_action_to_states
53
+ generator = parser_generator SIMPLE_GRAMMAR_LR0
54
+ generator.start_from E
55
+ generator.construct_LR0_automata
56
+ states = generator.states
57
+ assert_equal [read_reduce(0)].to_set, states[0][E]
58
+ assert_equal [read_reduce(2)].to_set, states[0][ID]
59
+ assert_equal [read_reduce(2)].to_set, states[2][ID]
60
+ assert_equal [read_reduce(1)].to_set, states[2][T]
61
+ end
62
+
63
+ def test_should_return_all_predsucceors
64
+ generator = parser_generator EXPRESSION_GRAMMAR_LALR1
65
+ generator.start_from E
66
+ generator.construct_LR0_automata
67
+ states = generator.states
68
+ assert_equal [LR_item(0, START, E),
69
+ LR_item(0, E, E, terminal('+'), T),
70
+ LR_item(0, E, T),
71
+ LR_item(0, T, T, terminal('*'), F),
72
+ LR_item(0, T, F),
73
+ LR_item(0, F, terminal('('), E, terminal(')')),
74
+ LR_item(0, F, ID)], states[2].predsucceors([T])[0]
75
+ assert_equal [LR_item(1, F, terminal('('), E, terminal(')')),
76
+ LR_item(0, E, E, terminal('+'), T),
77
+ LR_item(0, E, T),
78
+ LR_item(0, T, T, terminal('*'), F),
79
+ LR_item(0, T, F),
80
+ LR_item(0, F, terminal('('), E, terminal(')')),
81
+ LR_item(0, F, ID)], states[2].predsucceors([T])[1]
82
+ end
83
+
84
+ def test_should_return_read_set_for_state
85
+ generator = parser_generator EXPRESSION_GRAMMAR_LALR1
86
+ generator.start_from E
87
+ generator.construct_LR0_automata
88
+ states = generator.states
89
+ assert_equal [terminal('id'), terminal('(')].to_set, generator.read_set(states[2], terminal('*')).to_set
90
+ end
91
+ end