aurum 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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