aurum 0.1.1 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +29 -0
- data/examples/dangling_else/grammar.rb +23 -0
- data/examples/expression/grammar.rb +28 -0
- data/examples/smalltalk/grammar.rb +151 -0
- data/examples/smalltalk/interpreter.rb +70 -0
- data/examples/yacc/grammar.rb +72 -0
- data/lib/aurum.rb +1 -9
- data/lib/aurum/engine.rb +39 -175
- data/lib/aurum/engine/parsing_facility.rb +107 -0
- data/lib/aurum/engine/tokenization_facility.rb +86 -0
- data/lib/aurum/grammar.rb +52 -219
- data/lib/aurum/grammar/automata.rb +194 -0
- data/lib/aurum/grammar/builder/augmented_grammar.rb +83 -0
- data/lib/aurum/grammar/builder/dot_logger.rb +66 -0
- data/lib/aurum/grammar/builder/lexical_table_builder.rb +55 -0
- data/lib/aurum/grammar/builder/parsing_table_builder.rb +238 -0
- data/lib/aurum/grammar/builder/set_of_items.rb +190 -0
- data/lib/aurum/grammar/compiled_tables.rb +20 -0
- data/lib/aurum/grammar/dsl/lexical_definition.rb +94 -0
- data/lib/aurum/grammar/dsl/syntax_definition.rb +79 -0
- data/lib/aurum/grammar/lexical_rules.rb +224 -0
- data/lib/aurum/grammar/metalang/grammar.rb +47 -0
- data/lib/aurum/grammar/syntax_rules.rb +95 -0
- data/spec/builder/dsl_definition/aurum_grammar_spec.rb +33 -0
- data/spec/engine/lexer_spec.rb +59 -0
- data/spec/engine/parser_spec.rb +90 -0
- data/spec/examples/dangling_else_example.rb +30 -0
- data/spec/examples/expression_example.rb +48 -0
- data/spec/examples/smalltalk_example.rb +50 -0
- data/spec/examples/yacc_spec.rb +30 -0
- data/spec/grammar/builder/lexical_table/automata_spec.rb +55 -0
- data/spec/grammar/builder/lexical_table/builder_spec.rb +78 -0
- data/spec/grammar/builder/lexical_table/character_set_spec.rb +100 -0
- data/spec/grammar/builder/lexical_table/pattern_spec.rb +11 -0
- data/spec/grammar/builder/lexical_table/regular_expression.rb +40 -0
- data/spec/grammar/builder/parsing_table/augmented_grammar_spec.rb +36 -0
- data/spec/grammar/builder/parsing_table/builder_spec.rb +152 -0
- data/spec/grammar/builder/parsing_table/digraph_traverser_spec.rb +42 -0
- data/spec/grammar/builder/parsing_table/item_spec.rb +51 -0
- data/spec/grammar/builder/parsing_table/sources_spec.rb +66 -0
- data/spec/grammar/builder/parsing_table/state_spec.rb +82 -0
- data/spec/grammar/dsl/character_classes_builder_spec.rb +50 -0
- data/spec/grammar/dsl/lexical_rules_builder_spec.rb +181 -0
- data/spec/grammar/dsl/precedence_builder_spec.rb +64 -0
- data/spec/grammar/dsl/productions_builder_spec.rb +78 -0
- data/spec/grammar/metalang/metalang_spec.rb +0 -0
- data/spec/grammar/precedence_spec.rb +42 -0
- data/spec/grammar/syntax_rules_spec.rb +31 -0
- data/spec/parser_matcher.rb +69 -0
- data/spec/pattern_matcher.rb +123 -0
- data/spec/spec_helper.rb +133 -0
- metadata +70 -36
- data/example/expression/expression.rb +0 -35
- data/example/expression/lisp.rb +0 -26
- data/lib/aurum/lexical_table_generator.rb +0 -429
- data/lib/aurum/parsing_table_generator.rb +0 -464
- data/test/engine/lexer_test.rb +0 -59
- data/test/engine/semantic_attributes_test.rb +0 -15
- data/test/grammar_definition/character_class_definition_test.rb +0 -28
- data/test/grammar_definition/grammar_definition_test.rb +0 -55
- data/test/grammar_definition/lexical_definition_test.rb +0 -56
- data/test/grammar_definition/operator_precedence_definition_test.rb +0 -35
- data/test/grammar_definition/production_definition_test.rb +0 -60
- data/test/lexical_table_generator/automata_test.rb +0 -74
- data/test/lexical_table_generator/character_set_test.rb +0 -73
- data/test/lexical_table_generator/interval_test.rb +0 -36
- data/test/lexical_table_generator/pattern_test.rb +0 -115
- data/test/lexical_table_generator/subset_determinizer_test.rb +0 -19
- data/test/lexical_table_generator/table_generator_test.rb +0 -126
- data/test/parsing_table_generator/augmented_grammar_test.rb +0 -45
- data/test/parsing_table_generator/lalr_n_computation_test.rb +0 -92
- data/test/parsing_table_generator/lr_0_automata_test.rb +0 -94
- data/test/parsing_table_generator/lr_item_test.rb +0 -27
- data/test/parsing_table_generator/parsing_table_state_test.rb +0 -39
- data/test/parsing_table_generator/precedence_table_test.rb +0 -28
- data/test/parsing_table_generator/production_test.rb +0 -9
- data/test/test_helper.rb +0 -103
@@ -1,36 +0,0 @@
|
|
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
|
@@ -1,115 +0,0 @@
|
|
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 test_should_match_every_char_in_enum_literal
|
90
|
-
pattern = Aurum::Pattern.from_enum('*/')
|
91
|
-
assert match?('*', pattern)
|
92
|
-
assert match?('/', pattern)
|
93
|
-
end
|
94
|
-
|
95
|
-
def match? expected_string, pattern
|
96
|
-
states = closure pattern.automata.table, [0]
|
97
|
-
expected_string.each_byte {|char| states = move(pattern.automata.table, states, char)}
|
98
|
-
states.include?(pattern.accept)
|
99
|
-
end
|
100
|
-
|
101
|
-
def move automata, states, char
|
102
|
-
result = []
|
103
|
-
states.each {|state| automata[state].each {|tran| result.concat(closure(automata, [tran.destination])) if tran.symbols.include? char} }
|
104
|
-
result.uniq;
|
105
|
-
end
|
106
|
-
|
107
|
-
def closure automata, states
|
108
|
-
closure, unvisited = Set.new(states.dup), states.dup
|
109
|
-
filter = lambda {|x| x.symbols == Epsilon && !closure.include?(x.destination)}
|
110
|
-
while !unvisited.empty? do
|
111
|
-
automata[unvisited.pop].grep_each(filter){|tran| [closure, unvisited].each {|x| x << tran.destination}}
|
112
|
-
end
|
113
|
-
closure.to_a
|
114
|
-
end
|
115
|
-
end
|
@@ -1,19 +0,0 @@
|
|
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
|
@@ -1,126 +0,0 @@
|
|
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
|
@@ -1,45 +0,0 @@
|
|
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 test_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 test_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 test_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 test_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
|
@@ -1,92 +0,0 @@
|
|
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.inconsistent?}
|
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.conflict? })[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.conflict?
|
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
|
-
generator = parser_generator BNF_GRAMMAR_LALR2
|
67
|
-
table, level = generator.start_from(BNF).parsing_table
|
68
|
-
assert_equal 2, level
|
69
|
-
end
|
70
|
-
|
71
|
-
def test_should_raise_error_if_grammar_not_lalr_n
|
72
|
-
begin
|
73
|
-
parser_generator(NOT_LALR_GRAMMAR).start_from(E).parsing_table
|
74
|
-
assert false
|
75
|
-
rescue RuntimeError => error
|
76
|
-
assert_equal 'not LALR(n)', error.message
|
77
|
-
end
|
78
|
-
end
|
79
|
-
|
80
|
-
def test_should_resolve_conflicts_for_expression
|
81
|
-
op_a, op_b = terminal('+'), terminal('*')
|
82
|
-
generator = parser_generator EXPRESSION_GRAMMAR, [[op_b], [op_a]]
|
83
|
-
generator.start_from E
|
84
|
-
generator.construct_LR0_automata
|
85
|
-
generator.compute_LALR_1_lookahead
|
86
|
-
state = generator.states.last
|
87
|
-
assert_equal 1, state[terminal('+')].size
|
88
|
-
assert state[terminal('+')].to_a.first.kind_of?(Aurum::ReduceAction)
|
89
|
-
assert_equal 1, state[terminal('*')].size
|
90
|
-
assert state[terminal('*')].to_a.first.kind_of?(Aurum::ShiftAction)
|
91
|
-
end
|
92
|
-
end
|
@@ -1,94 +0,0 @@
|
|
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
|
-
generator.start_from E
|
18
|
-
closure = generator.closure [LR_item(0, E, T)]
|
19
|
-
[LR_item(0, T, ID), LR_item(0, T, terminal('other'))].each {|x| assert closure.include?(x)}
|
20
|
-
end
|
21
|
-
|
22
|
-
def test_should_return_goto_items_if_expected_symbol_given
|
23
|
-
generator = parser_generator E=>[production(E, T)], T=>[production(T, ID), production(T, terminal('other'))]
|
24
|
-
generator.start_from E
|
25
|
-
assert_equal [LR_item(1, E, T)], generator.goto([LR_item(0, E, T)], T)
|
26
|
-
end
|
27
|
-
|
28
|
-
def test_goto_items_should_be_closured_if_expected_symbol_given
|
29
|
-
generator = parser_generator E=>[production(E, T, T)], T=>[production(T, ID), production(T, terminal('other'))]
|
30
|
-
generator.start_from E
|
31
|
-
goto = generator.goto [LR_item(0, E, T, T)], T
|
32
|
-
[LR_item(0, T, ID), LR_item(0, T, terminal('other'))].each {|x| assert goto.include?(x)}
|
33
|
-
end
|
34
|
-
|
35
|
-
def test_should_use_LR0_items_of_collection_as_state
|
36
|
-
generator = parser_generator SIMPLE_GRAMMAR_LR0
|
37
|
-
generator.start_from E
|
38
|
-
generator.construct_LR0_automata
|
39
|
-
states = generator.states
|
40
|
-
assert 3, states.length
|
41
|
-
assert [LR_item(0, START, E), LR_item(0, T, terminal('+'), T), LR_item(0, T, ID)], states[0]
|
42
|
-
assert [LR_item(1, T, terminal('+'), T)], states[1]
|
43
|
-
assert [LR_item(2, T, terminal('+'), T), LR_item(0, T, ID)], states[2]
|
44
|
-
end
|
45
|
-
|
46
|
-
def test_should_add_shift_action_to_states
|
47
|
-
generator = parser_generator SIMPLE_GRAMMAR_LR0
|
48
|
-
generator.start_from E
|
49
|
-
generator.construct_LR0_automata
|
50
|
-
states = generator.states
|
51
|
-
assert_equal [shift(1)].to_set, states[0][T]
|
52
|
-
assert_equal [shift(2)].to_set, states[1][terminal('+')]
|
53
|
-
end
|
54
|
-
|
55
|
-
def test_should_add_read_reduce_action_to_states
|
56
|
-
generator = parser_generator SIMPLE_GRAMMAR_LR0
|
57
|
-
generator.start_from E
|
58
|
-
generator.construct_LR0_automata
|
59
|
-
states = generator.states
|
60
|
-
assert_equal [read_reduce(0)].to_set, states[0][E]
|
61
|
-
assert_equal [read_reduce(2)].to_set, states[0][ID]
|
62
|
-
assert_equal [read_reduce(2)].to_set, states[2][ID]
|
63
|
-
assert_equal [read_reduce(1)].to_set, states[2][T]
|
64
|
-
end
|
65
|
-
|
66
|
-
def test_should_return_all_predsucceors
|
67
|
-
generator = parser_generator EXPRESSION_GRAMMAR_LALR1
|
68
|
-
generator.start_from E
|
69
|
-
generator.construct_LR0_automata
|
70
|
-
states = generator.states
|
71
|
-
assert_equal [LR_item(0, START, E),
|
72
|
-
LR_item(0, E, E, terminal('+'), T),
|
73
|
-
LR_item(0, E, T),
|
74
|
-
LR_item(0, T, T, terminal('*'), F),
|
75
|
-
LR_item(0, T, F),
|
76
|
-
LR_item(0, F, terminal('('), E, terminal(')')),
|
77
|
-
LR_item(0, F, ID)], states[2].predsucceors([T])[0]
|
78
|
-
assert_equal [LR_item(1, F, terminal('('), E, terminal(')')),
|
79
|
-
LR_item(0, E, E, terminal('+'), T),
|
80
|
-
LR_item(0, E, T),
|
81
|
-
LR_item(0, T, T, terminal('*'), F),
|
82
|
-
LR_item(0, T, F),
|
83
|
-
LR_item(0, F, terminal('('), E, terminal(')')),
|
84
|
-
LR_item(0, F, ID)], states[2].predsucceors([T])[1]
|
85
|
-
end
|
86
|
-
|
87
|
-
def test_should_return_read_set_for_state
|
88
|
-
generator = parser_generator EXPRESSION_GRAMMAR_LALR1
|
89
|
-
generator.start_from E
|
90
|
-
generator.construct_LR0_automata
|
91
|
-
states = generator.states
|
92
|
-
assert_equal [terminal('id'), terminal('(')].to_set, generator.read_set(states[2], terminal('*')).to_set
|
93
|
-
end
|
94
|
-
end
|