aurum 0.1.1 → 0.2.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 (77) hide show
  1. data/Rakefile +29 -0
  2. data/examples/dangling_else/grammar.rb +23 -0
  3. data/examples/expression/grammar.rb +28 -0
  4. data/examples/smalltalk/grammar.rb +151 -0
  5. data/examples/smalltalk/interpreter.rb +70 -0
  6. data/examples/yacc/grammar.rb +72 -0
  7. data/lib/aurum.rb +1 -9
  8. data/lib/aurum/engine.rb +39 -175
  9. data/lib/aurum/engine/parsing_facility.rb +107 -0
  10. data/lib/aurum/engine/tokenization_facility.rb +86 -0
  11. data/lib/aurum/grammar.rb +52 -219
  12. data/lib/aurum/grammar/automata.rb +194 -0
  13. data/lib/aurum/grammar/builder/augmented_grammar.rb +83 -0
  14. data/lib/aurum/grammar/builder/dot_logger.rb +66 -0
  15. data/lib/aurum/grammar/builder/lexical_table_builder.rb +55 -0
  16. data/lib/aurum/grammar/builder/parsing_table_builder.rb +238 -0
  17. data/lib/aurum/grammar/builder/set_of_items.rb +190 -0
  18. data/lib/aurum/grammar/compiled_tables.rb +20 -0
  19. data/lib/aurum/grammar/dsl/lexical_definition.rb +94 -0
  20. data/lib/aurum/grammar/dsl/syntax_definition.rb +79 -0
  21. data/lib/aurum/grammar/lexical_rules.rb +224 -0
  22. data/lib/aurum/grammar/metalang/grammar.rb +47 -0
  23. data/lib/aurum/grammar/syntax_rules.rb +95 -0
  24. data/spec/builder/dsl_definition/aurum_grammar_spec.rb +33 -0
  25. data/spec/engine/lexer_spec.rb +59 -0
  26. data/spec/engine/parser_spec.rb +90 -0
  27. data/spec/examples/dangling_else_example.rb +30 -0
  28. data/spec/examples/expression_example.rb +48 -0
  29. data/spec/examples/smalltalk_example.rb +50 -0
  30. data/spec/examples/yacc_spec.rb +30 -0
  31. data/spec/grammar/builder/lexical_table/automata_spec.rb +55 -0
  32. data/spec/grammar/builder/lexical_table/builder_spec.rb +78 -0
  33. data/spec/grammar/builder/lexical_table/character_set_spec.rb +100 -0
  34. data/spec/grammar/builder/lexical_table/pattern_spec.rb +11 -0
  35. data/spec/grammar/builder/lexical_table/regular_expression.rb +40 -0
  36. data/spec/grammar/builder/parsing_table/augmented_grammar_spec.rb +36 -0
  37. data/spec/grammar/builder/parsing_table/builder_spec.rb +152 -0
  38. data/spec/grammar/builder/parsing_table/digraph_traverser_spec.rb +42 -0
  39. data/spec/grammar/builder/parsing_table/item_spec.rb +51 -0
  40. data/spec/grammar/builder/parsing_table/sources_spec.rb +66 -0
  41. data/spec/grammar/builder/parsing_table/state_spec.rb +82 -0
  42. data/spec/grammar/dsl/character_classes_builder_spec.rb +50 -0
  43. data/spec/grammar/dsl/lexical_rules_builder_spec.rb +181 -0
  44. data/spec/grammar/dsl/precedence_builder_spec.rb +64 -0
  45. data/spec/grammar/dsl/productions_builder_spec.rb +78 -0
  46. data/spec/grammar/metalang/metalang_spec.rb +0 -0
  47. data/spec/grammar/precedence_spec.rb +42 -0
  48. data/spec/grammar/syntax_rules_spec.rb +31 -0
  49. data/spec/parser_matcher.rb +69 -0
  50. data/spec/pattern_matcher.rb +123 -0
  51. data/spec/spec_helper.rb +133 -0
  52. metadata +70 -36
  53. data/example/expression/expression.rb +0 -35
  54. data/example/expression/lisp.rb +0 -26
  55. data/lib/aurum/lexical_table_generator.rb +0 -429
  56. data/lib/aurum/parsing_table_generator.rb +0 -464
  57. data/test/engine/lexer_test.rb +0 -59
  58. data/test/engine/semantic_attributes_test.rb +0 -15
  59. data/test/grammar_definition/character_class_definition_test.rb +0 -28
  60. data/test/grammar_definition/grammar_definition_test.rb +0 -55
  61. data/test/grammar_definition/lexical_definition_test.rb +0 -56
  62. data/test/grammar_definition/operator_precedence_definition_test.rb +0 -35
  63. data/test/grammar_definition/production_definition_test.rb +0 -60
  64. data/test/lexical_table_generator/automata_test.rb +0 -74
  65. data/test/lexical_table_generator/character_set_test.rb +0 -73
  66. data/test/lexical_table_generator/interval_test.rb +0 -36
  67. data/test/lexical_table_generator/pattern_test.rb +0 -115
  68. data/test/lexical_table_generator/subset_determinizer_test.rb +0 -19
  69. data/test/lexical_table_generator/table_generator_test.rb +0 -126
  70. data/test/parsing_table_generator/augmented_grammar_test.rb +0 -45
  71. data/test/parsing_table_generator/lalr_n_computation_test.rb +0 -92
  72. data/test/parsing_table_generator/lr_0_automata_test.rb +0 -94
  73. data/test/parsing_table_generator/lr_item_test.rb +0 -27
  74. data/test/parsing_table_generator/parsing_table_state_test.rb +0 -39
  75. data/test/parsing_table_generator/precedence_table_test.rb +0 -28
  76. data/test/parsing_table_generator/production_test.rb +0 -9
  77. data/test/test_helper.rb +0 -103
@@ -0,0 +1,30 @@
1
+ helper_dir = File.join(File.dirname(__FILE__), '..')
2
+ $:.unshift(helper_dir) unless $:.include?(helper_dir)
3
+
4
+ require 'spec_helper'
5
+ require 'yacc/grammar'
6
+
7
+ describe 'Yacc Grammar' do
8
+ before :all do
9
+ YaccLexer = Aurum::Lexer.new(Aurum::Examples::YaccGrammar)
10
+ #YaccParser = Aurum::Parser.new(Aurum::Examples::YaccGrammar, :grammar)
11
+ end
12
+
13
+ it 'should ' do
14
+ lines = File.open('ruby.y').readlines
15
+ content = lines.join("\n")
16
+ scanner = scanner(content)
17
+ while (a = scanner.next_symbol)
18
+ p a
19
+ break if a.terminal == '$unknown' || a.terminal == '$eof'
20
+ end
21
+ end
22
+
23
+ def scanner source
24
+ YaccLexer.new(source)
25
+ end
26
+
27
+ def parse source
28
+ YaccParser.new.parse(YaccLexer.new(source)).s_exp
29
+ end
30
+ end
@@ -0,0 +1,55 @@
1
+ helper_dir = File.join(File.dirname(__FILE__), '..', '..', '..')
2
+ $:.unshift(helper_dir) unless $:.include?(helper_dir)
3
+
4
+ require 'spec_helper'
5
+
6
+ require File.dirname(__FILE__) + '/regular_expression'
7
+
8
+ describe Aurum::Grammar::LexicalRules::Automata, ' alphabet' do
9
+ before :each do
10
+ @automata = Aurum::Grammar::LexicalRules::Automata.new(4)
11
+ end
12
+
13
+ it 'should return ac and mq' do
14
+ @automata.connect(0, interval(?a, ?c), 1)
15
+ @automata.connect(2, interval(?m, ?q), 3)
16
+ result = []
17
+ @automata.alphabet([0, 2]) {|states, character_set| result << [states, character_set]}
18
+ result.should == [[[1], interval(?a, ?c)], [[3], interval(?m, ?q)]]
19
+ end
20
+
21
+ it 'should return ac df and g' do
22
+ @automata.connect(0, interval(?a, ?f), 1)
23
+ @automata.connect(2, interval(?d, ?g), 3)
24
+ result = []
25
+ @automata.alphabet([0, 2]) {|states, character_set| result << [states, character_set]}
26
+ result.should == [[[1], interval(?a, ?c)], [[1, 3], interval(?d, ?f)], [[3], interval(?g, ?g)]]
27
+ end
28
+
29
+ it 'should return ac dg and hz' do
30
+ @automata.connect(0, interval(?a, ?z), 1)
31
+ @automata.connect(2, interval(?d, ?g), 3)
32
+ result = []
33
+ @automata.alphabet([0, 2]) {|states, character_set| result << [states, character_set]}
34
+ result.should == [[[1], interval(?a, ?c)], [[1, 3], interval(?d, ?g)], [[1], interval(?h, ?z)]]
35
+ end
36
+
37
+ def interval first, last
38
+ Aurum::Grammar::LexicalRules::CharacterSet::Interval.new(first, last).to_char_set
39
+ end
40
+ end
41
+
42
+ describe Aurum::Grammar::LexicalRules::Automata, ' subset determinizer' do
43
+ include DeterminizedPatternMatcher
44
+ it_should_behave_like 'regular expression'
45
+ end
46
+
47
+ describe Aurum::Grammar::LexicalRules::Automata, ' hopcroft minimizer' do
48
+ include MinimizedPatternMatcher
49
+ it_should_behave_like 'regular expression'
50
+ end
51
+
52
+
53
+
54
+
55
+
@@ -0,0 +1,78 @@
1
+ helper_dir = File.join(File.dirname(__FILE__), '..', '..', '..')
2
+ $:.unshift(helper_dir) unless $:.include?(helper_dir)
3
+
4
+ require 'spec_helper'
5
+
6
+ def scan(lexical_state, lexeme)
7
+ begin
8
+ state = - @table.lexical_states.index(lexical_state.to_s) - 1
9
+ state = (@table.states[0].find {|tran| tran.character_set.include?(state)}).destination
10
+ lexeme.each_byte {|char| state = (@table.states[state].find {|tran| tran.character_set.include?(char)}).destination}
11
+ @table.actions[state]
12
+ rescue
13
+ raise 'error'
14
+ end
15
+ end
16
+
17
+ describe Aurum::Builder::LexicalTableBuilder, ' no reserved literal' do
18
+ before :all do
19
+ @pattern_a, @pattern_b = $pattern.string('pattern_a'), $pattern.string('pattern_b')
20
+ @action_a, @action_b = Aurum::LexicalTable::Action.new('pattern_a'), Aurum::LexicalTable::Action.new('pattern_b')
21
+ @lexical_rules = Aurum::Grammar::LexicalRules.new
22
+ @lexical_rules.add_lexical_action(:initial, @pattern_a, @action_a)
23
+ @lexical_rules.add_lexical_action(:state_a, @pattern_b, @action_b)
24
+ end
25
+
26
+ before :each do
27
+ @builder = Aurum::Builder::LexicalTableBuilder.new(@lexical_rules, [], [])
28
+ @table = @builder.build
29
+ end
30
+
31
+ it 'should recognize pattern_a in initial state' do
32
+ scan(:initial, 'pattern_a').should == @action_a
33
+ end
34
+
35
+ it 'should not recognize pattern_b in initial state' do
36
+ lambda {scan(:initial, 'pattern_b')}.should raise_error
37
+ end
38
+
39
+ it 'should recognize pattern_b in state_a' do
40
+ scan(:state_a, 'pattern_b').should == @action_b
41
+ end
42
+
43
+ it 'should not recognize pattern_a in state_a' do
44
+ lambda {scan(:state_a, 'pattern_a')}.should raise_error
45
+ end
46
+ end
47
+
48
+ describe Aurum::Builder::LexicalTableBuilder, ' reserved literal in all states' do
49
+ before :all do
50
+ @pattern_a, @pattern_b = $pattern.enum('abcdefghijklmnopqrstuvwxyz').one_or_more, $pattern.string('pattern_b')
51
+ @action_a, @action_b = Aurum::LexicalTable::Action.new('pattern_a'), Aurum::LexicalTable::Action.new('pattern_b')
52
+ @lexical_rules = Aurum::Grammar::LexicalRules.new
53
+ @lexical_rules.add_lexical_action(:initial, @pattern_a, @action_a)
54
+ @lexical_rules.add_lexical_action(:state_a, @pattern_b, @action_b)
55
+ @action_keyword = Aurum::LexicalTable::Action.new('$literal_keyword')
56
+ end
57
+
58
+ before :each do
59
+ @builder = Aurum::Builder::LexicalTableBuilder.new(@lexical_rules, ['all'], ['keyword'])
60
+ @table = @builder.build
61
+ end
62
+
63
+ it 'should recognize keyword in initial state' do
64
+ scan(:initial, 'keyword').should == @action_keyword
65
+ end
66
+
67
+ it 'should recognize pattern_a in initial state' do
68
+ scan(:initial, 'keywore').should == @action_a
69
+ end
70
+
71
+ it 'should recognize keyword in state_a' do
72
+ scan(:state_a, 'keyword').should == @action_keyword
73
+ end
74
+
75
+ it 'should recognize pattern_a in initial state' do
76
+ scan(:state_a, 'pattern_b').should == @action_b
77
+ end
78
+ end
@@ -0,0 +1,100 @@
1
+ helper_dir = File.join(File.dirname(__FILE__), '..', '..', '..')
2
+ $:.unshift(helper_dir) unless $:.include?(helper_dir)
3
+
4
+ require 'spec_helper'
5
+
6
+ describe Aurum::Grammar::LexicalRules::CharacterSet::Interval do
7
+ before :each do
8
+ @interval = Aurum::Grammar::LexicalRules::CharacterSet::Interval.new ?a, ?c
9
+ end
10
+
11
+ it 'should contain character within interval' do
12
+ (?a..?c).each {|character| @interval.should be_include(character)}
13
+ end
14
+
15
+ it 'should not contain character out of interval' do
16
+ @interval.should_not be_include(?d)
17
+ end
18
+
19
+ it 'should not merge intervals if they do not have any character in common' do
20
+ interval = Aurum::Grammar::LexicalRules::CharacterSet::Interval.new ?e, ?f
21
+ @interval.merge!(interval).should be_nil
22
+ end
23
+
24
+ it 'should merge two intervals if they have characters in common' do
25
+ interval = Aurum::Grammar::LexicalRules::CharacterSet::Interval.new ?b, ?e
26
+ @interval.merge!(interval).should_not be_nil
27
+ @interval.should == Aurum::Grammar::LexicalRules::CharacterSet::Interval.new(?a, ?e)
28
+ end
29
+
30
+ it 'should merge [b-e] and [a-c]' do
31
+ interval = Aurum::Grammar::LexicalRules::CharacterSet::Interval.new ?b, ?e
32
+ interval.merge!(@interval).should_not be_nil
33
+ interval.should == Aurum::Grammar::LexicalRules::CharacterSet::Interval.new(?a, ?e)
34
+ end
35
+
36
+ it 'should merge two intervals if they are adjacent' do
37
+ interval = Aurum::Grammar::LexicalRules::CharacterSet::Interval.new ?d, ?e
38
+ @interval.merge!(interval).should_not be_nil
39
+ @interval.should == Aurum::Grammar::LexicalRules::CharacterSet::Interval.new(?a, ?e)
40
+ end
41
+
42
+ it 'should merge [d-e] and [a-c]' do
43
+ interval = Aurum::Grammar::LexicalRules::CharacterSet::Interval.new ?d, ?e
44
+ interval.merge!(@interval).should_not be_nil
45
+ interval.should == Aurum::Grammar::LexicalRules::CharacterSet::Interval.new(?a, ?e)
46
+ end
47
+ end
48
+
49
+ describe Aurum::Grammar::LexicalRules::CharacterSet do
50
+ it 'should construct character set for string literal' do
51
+ character_set = Aurum::Grammar::LexicalRules::CharacterSet.enum('age')
52
+ [?a, ?g, ?e].each {|char | character_set.should be_include(char)}
53
+ end
54
+
55
+ it 'should merge intervals as many as possible' do
56
+ character_set = Aurum::Grammar::LexicalRules::CharacterSet.enum('abc')
57
+ character_set.intervals.size.should == 1
58
+ character_set.intervals.should be_include(Aurum::Grammar::LexicalRules::CharacterSet::Interval.new(?a, ?c))
59
+ end
60
+
61
+ it '[aec] + [dbf] should be [a-f]' do
62
+ char_set_aec, char_set_dbf = Aurum::Grammar::LexicalRules::CharacterSet.enum('aec'), Aurum::Grammar::LexicalRules::CharacterSet.enum('dbf')
63
+ result = char_set_aec + char_set_dbf
64
+ result.intervals.size.should == 1
65
+ result.intervals.should be_include(Aurum::Grammar::LexicalRules::CharacterSet::Interval.new(?a, ?f))
66
+ end
67
+
68
+ it '[a-d] - [bc] should be [a] and [d]' do
69
+ char_set_ad, char_set_bc = Aurum::Grammar::LexicalRules::CharacterSet.enum('abcd'), Aurum::Grammar::LexicalRules::CharacterSet.enum('bc')
70
+ result = char_set_ad - char_set_bc
71
+ result.intervals.size.should == 2
72
+ result.intervals.should be_include(Aurum::Grammar::LexicalRules::CharacterSet::Interval.new(?a))
73
+ result.intervals.should be_include(Aurum::Grammar::LexicalRules::CharacterSet::Interval.new(?d))
74
+ end
75
+
76
+ it '[a-d] - [e-f] should be [a-d]' do
77
+ char_set_ad, char_set_ef = Aurum::Grammar::LexicalRules::CharacterSet.enum('abcd'), Aurum::Grammar::LexicalRules::CharacterSet.enum('ef')
78
+ result = char_set_ad - char_set_ef
79
+ result.intervals.size.should == 1
80
+ result.intervals.should be_include(Aurum::Grammar::LexicalRules::CharacterSet::Interval.new(?a, ?d))
81
+ end
82
+
83
+ it '[a-d] - [a-d] should be []' do
84
+ char_set_ad = Aurum::Grammar::LexicalRules::CharacterSet.enum('abcd')
85
+ result = char_set_ad - char_set_ad
86
+ result.intervals.size.should == 0
87
+ end
88
+ end
89
+
90
+ describe Aurum::Grammar::LexicalRules::Point do
91
+ before :each do
92
+ @point = Aurum::Grammar::LexicalRules::Point.new(?a, true, 1)
93
+ end
94
+
95
+ it {@point.should == @point}
96
+ it {@point.should < Aurum::Grammar::LexicalRules::Point.new(?b, true, 1)}
97
+ it {@point.should > Aurum::Grammar::LexicalRules::Point.new(?A, true, 1)}
98
+ it {@point.should < Aurum::Grammar::LexicalRules::Point.new(?a, false, 1)}
99
+ it {Aurum::Grammar::LexicalRules::Point.new(?a, false, 1).should > @point}
100
+ end
@@ -0,0 +1,11 @@
1
+ helper_dir = File.join(File.dirname(__FILE__), '..', '..', '..')
2
+ $:.unshift(helper_dir) unless $:.include?(helper_dir)
3
+
4
+ require 'spec_helper'
5
+
6
+ require File.dirname(__FILE__) + '/regular_expression'
7
+
8
+ describe Aurum::Grammar::LexicalRules::Pattern do
9
+ include PatternMatcher
10
+ it_should_behave_like 'regular expression'
11
+ end
@@ -0,0 +1,40 @@
1
+ describe 'regular expression', :shared => true do
2
+ it {'string'.should be_matched_by('string', $pattern.string('string'))}
3
+ it {'string'.should_not be_matched_by('other_string', $pattern.string('other_string'))}
4
+ it {'a'.should be_matched_by('a|b|c', $pattern.enum('abc'))}
5
+ it {'b'.should be_matched_by('a|b|c', $pattern.enum('abc'))}
6
+ it {'c'.should be_matched_by('a|b|c', $pattern.enum('abc'))}
7
+ it {'d'.should_not be_matched_by('a|b|c', $pattern.enum('abc'))}
8
+ it {''.should be_matched_by('(abc)*', $pattern.string('abc').zero_or_more)}
9
+ it {'abc'.should be_matched_by('(abc)*', $pattern.string('abc').zero_or_more)}
10
+ it {'abcabcabcabc'.should be_matched_by('(abc)*', $pattern.string('abc').zero_or_more)}
11
+ it {''.should_not be_matched_by('(abc)+', $pattern.string('abc').one_or_more)}
12
+ it {'abc'.should be_matched_by('(abc)+', $pattern.string('abc').one_or_more)}
13
+ it {'abcabcabcabc'.should be_matched_by('(abc)+', $pattern.string('abc').one_or_more)}
14
+ it {''.should be_matched_by('(abc)+', $pattern.string('abc').zero_or_one)}
15
+ it {'abc'.should be_matched_by('(abc)+', $pattern.string('abc').zero_or_one)}
16
+ it {'abcabc'.should_not be_matched_by('(abc)+', $pattern.string('abc').zero_or_one)}
17
+ it {'abcdef'.should be_matched_by('(abc)(def)', $pattern.concat([$pattern.string('abc'), $pattern.string('def')]))}
18
+ it {'abcdeg'.should_not be_matched_by('(abc)(def)', $pattern.concat([$pattern.string('abc'), $pattern.string('def')]))}
19
+ it {'abcabc'.should be_matched_by('(abc){2}', $pattern.string('abc')[2])}
20
+ it {'abc'.should_not be_matched_by('(abc){2}', $pattern.string('abc')[2])}
21
+ it {'abcabcabc'.should_not be_matched_by('(abc){2}', $pattern.string('abc')[2])}
22
+ it {'abcabc'.should be_matched_by('(abc){2,4}', $pattern.string('abc')[2, 4])}
23
+ it {'abcabcabc'.should be_matched_by('(abc){2,4}', $pattern.string('abc')[2, 4])}
24
+ it {'abcabcabcabc'.should be_matched_by('(abc){2,4}', $pattern.string('abc')[2, 4])}
25
+ it {'abc'.should_not be_matched_by('(abc){2,4}', $pattern.string('abc')[2, 4])}
26
+ it {'abcabcabcabcabc'.should_not be_matched_by('(abc){2,4}', $pattern.string('abc')[2, 4])}
27
+ it {'abc'.should be_matched_by('(abc)|(def)', $pattern.string('abc') | $pattern.string('def'))}
28
+ it {'def'.should be_matched_by('(abc)|(def)', $pattern.string('abc') | $pattern.string('def'))}
29
+ it {'deg'.should_not be_matched_by('(abc)|(def)', $pattern.string('abc') | $pattern.string('def'))}
30
+ it {'abc'.should_not be_matched_by('!abc', $pattern.string('abc').not)}
31
+ it {'ade'.should be_matched_by('!abc', $pattern.string('abc').not)}
32
+ it {'a'.should be_matched_by('!abc', $pattern.string('abc').not)}
33
+ it {'abcdefhijk'.should be_matched_by('!abc', $pattern.string('abc').not)}
34
+ it {''.should be_matched_by('!abc', $pattern.string('abc').not)}
35
+ it {'abcdef */'.should be_matched_by('~*/', ~ $pattern.string('*/'))}
36
+ it {'/* abcdef */'.should be_matched_by('/*~*/', $pattern.concat([$pattern.string('/*'), ~ $pattern.string('*/')]))}
37
+ it {'abcdef */ /'.should_not be_matched_by('~*/', ~ $pattern.string('*/'))}
38
+ it {'abcdef'.should_not be_matched_by('~*/', ~ $pattern.string('*/'))}
39
+ it {'abcdef *'.should_not be_matched_by('~*/', ~ $pattern.string('*/'))}
40
+ end
@@ -0,0 +1,36 @@
1
+ helper_dir = File.join(File.dirname(__FILE__), '..', '..', '..')
2
+ $:.unshift(helper_dir) unless $:.include?(helper_dir)
3
+
4
+ require 'spec_helper'
5
+
6
+ describe Aurum::Builder::AugmentedGrammar do
7
+ before :all do
8
+ @syntax_rules = Aurum::Grammar::SyntaxRules.new
9
+ @E, @T = Aurum::Grammar.nonterminal('E'), Aurum::Grammar.nonterminal('T')
10
+ @F, @id = Aurum::Grammar.nonterminal('F'), Aurum::Grammar.terminal('id')
11
+ @syntax_rules.add_syntax_rule Aurum::Grammar.production(@E, [@T])
12
+ @syntax_rules.add_syntax_rule Aurum::Grammar.production(@E, [@F])
13
+ @syntax_rules.add_syntax_rule Aurum::Grammar.production(@T, [])
14
+ @syntax_rules.add_syntax_rule Aurum::Grammar.production(@F, [@id])
15
+ @augmented_grammar = Aurum::Builder::AugmentedGrammar.new(@syntax_rules, @E)
16
+ end
17
+
18
+ it 'should compute all nullable symbols' do
19
+ @augmented_grammar.compute_nullables
20
+ @augmented_grammar.should be_nullable(@E)
21
+ @augmented_grammar.should be_nullable(@T)
22
+ @augmented_grammar.should_not be_nullable(@F)
23
+ end
24
+
25
+ it 'first set of terminal should be itself' do
26
+ @augmented_grammar.compute_first_sets
27
+ @augmented_grammar.first_set(@id).should == [@id].to_set
28
+ end
29
+
30
+ it 'first set of symbols should be included to nonterminal' do
31
+ @augmented_grammar.compute_first_sets
32
+ @augmented_grammar.first_set(@E).should == [@id].to_set
33
+ @augmented_grammar.first_set(@F).should == [@id].to_set
34
+ @augmented_grammar.first_set(@T).should == [].to_set
35
+ end
36
+ end
@@ -0,0 +1,152 @@
1
+ helper_dir = File.join(File.dirname(__FILE__), '..', '..', '..')
2
+ $:.unshift(helper_dir) unless $:.include?(helper_dir)
3
+
4
+ require 'spec_helper'
5
+
6
+ describe 'LR(0)', :shared => true do
7
+ it 'should not have any inconsistent state' do
8
+ @builder.instance_eval {@inconsistent_states}.should be_empty
9
+ end
10
+
11
+ it 'lookahead level should be 0' do
12
+ @parsing_table.lookahead.should == 0
13
+ end
14
+ end
15
+
16
+ describe 'LALR(1)', :shared => true do
17
+ it 'should have inconsistent states' do
18
+ @builder.instance_eval {@inconsistent_states}.should_not be_empty
19
+ end
20
+
21
+ it 'should not have any conflict state' do
22
+ @builder.instance_eval {@conflict_states}.should be_empty
23
+ end
24
+
25
+ it 'lookahead level should be 1' do
26
+ @parsing_table.lookahead.should == 1
27
+ end
28
+ end
29
+
30
+ describe 'LALR(n)', :shared => true do
31
+ it 'should have inconsistent states' do
32
+ @builder.instance_eval {@inconsistent_states}.should_not be_empty
33
+ end
34
+
35
+ it 'should have conflict states in LALR(1) automata' do
36
+ @builder.instance_eval {@conflict_states}.should_not be_empty
37
+ end
38
+
39
+ it 'should not have any conflict state in LALR(n) automata' do
40
+ @builder.instance_eval {@states}.all? {|state| !state.conflict?}.should be_true
41
+ end
42
+
43
+ it 'should have lookahead states' do
44
+ @builder.instance_eval {@states}.any? {|state| state.empty?}.should be_true
45
+ end
46
+
47
+ it "lookahead level should be n" do
48
+ @parsing_table.lookahead.should == @lookahead_level
49
+ end
50
+ end
51
+
52
+ describe Aurum::Builder::ParsingTableBuilder, ' LR(0) : Simple LR(0) Grammar' do
53
+ before :each do
54
+ syntax_rules = SimpleLR0Grammar.instance_eval {@syntax_rules}
55
+ augmented_grammar = Aurum::Builder::AugmentedGrammar.new(syntax_rules, Aurum::Grammar.nonterminal('expression'))
56
+ @builder = Aurum::Builder::ParsingTableBuilder.new(augmented_grammar, nil)
57
+ @parsing_table = @builder.build
58
+ end
59
+
60
+ it 'should use set of items as parsing state' do
61
+ states = @builder.instance_eval {@states}
62
+ states.size.should == 3
63
+ states[0].inspect.should == '[$start -> .expression ]'
64
+ states[1].inspect.should == '[expression -> term .+ term ]'
65
+ states[2].inspect.should == '[expression -> term + .term ]'
66
+ end
67
+
68
+ it_should_behave_like 'LR(0)'
69
+ end
70
+
71
+ describe Aurum::Builder::ParsingTableBuilder, ' LALR(1) : Grammar 4.19 in Dragon book' do
72
+ before :each do
73
+ syntax_rules = Grammar419InDragonBook.instance_eval {@syntax_rules}
74
+ augmented_grammar = Aurum::Builder::AugmentedGrammar.new(syntax_rules, Aurum::Grammar.nonterminal('expression'))
75
+ @builder = Aurum::Builder::ParsingTableBuilder.new(augmented_grammar, nil)
76
+ @parsing_table = @builder.build
77
+ end
78
+
79
+ it 'should compute lookahead symbol for inconsistent states' do
80
+ @parsing_table.actions[1]['$eof'].should_not be_read_reduce
81
+ @parsing_table.actions[2]['$eof'].should_not be_read_reduce
82
+ @parsing_table.actions[2]['$literal_)'].should_not be_read_reduce
83
+ @parsing_table.actions[2]['$literal_+'].should_not be_read_reduce
84
+ @parsing_table.actions[6]['$eof'].should_not be_read_reduce
85
+ @parsing_table.actions[6]['$literal_)'].should_not be_read_reduce
86
+ @parsing_table.actions[6]['$literal_+'].should_not be_read_reduce
87
+ end
88
+
89
+ it_should_behave_like 'LALR(1)'
90
+ end
91
+
92
+ describe Aurum::Builder::ParsingTableBuilder, ' LALR(1) : Expression Grammar with operator precedences' do
93
+ before :each do
94
+ syntax_rules = ExpressionGrammarWithOperatorPrecedence.instance_eval {@syntax_rules}
95
+ syntax_rules.assign_operator_precedence_to_symbols
96
+ augmented_grammar = Aurum::Builder::AugmentedGrammar.new(syntax_rules, Aurum::Grammar.nonterminal('expression'))
97
+ @builder = Aurum::Builder::ParsingTableBuilder.new(augmented_grammar, nil)
98
+ @parsing_table = @builder.build
99
+ end
100
+
101
+ it 'should be reduce action if equal precedence operator given' do
102
+ @parsing_table.actions[7]['$literal_+'].should be_reduce
103
+ end
104
+
105
+ it 'should be reduce action if ) given' do
106
+ @parsing_table.actions[7]['$literal_)'].should be_reduce
107
+ end
108
+
109
+ it 'should be reduce action if EOF given' do
110
+ @parsing_table.actions[7]['$eof'].should be_reduce
111
+ end
112
+
113
+ it 'should be shift action if higer precedence operator given' do
114
+ @parsing_table.actions[7]['$literal_*'].should be_shift
115
+ end
116
+
117
+ it_should_behave_like 'LALR(1)'
118
+ end
119
+
120
+ describe Aurum::Builder::ParsingTableBuilder, ' LALR(2) : BNF grammar in Philippe Charles\' thesis' do
121
+ before :each do
122
+ syntax_rules = BnfGrammarInCharlesThesis.instance_eval {@syntax_rules}
123
+ augmented_grammar = Aurum::Builder::AugmentedGrammar.new(syntax_rules, Aurum::Grammar.nonterminal('bnf'))
124
+ @builder = Aurum::Builder::ParsingTableBuilder.new(augmented_grammar, nil)
125
+ @parsing_table = @builder.build
126
+ @lookahead_level = 2
127
+ end
128
+
129
+ it_should_behave_like 'LALR(n)'
130
+ end
131
+
132
+
133
+ describe Aurum::Builder::ParsingTableBuilder, ' LALR(4) : simple LALR(4) grammar' do
134
+ before :each do
135
+ syntax_rules = LALR4Grammar.instance_eval {@syntax_rules}
136
+ augmented_grammar = Aurum::Builder::AugmentedGrammar.new(syntax_rules, Aurum::Grammar.nonterminal('s'))
137
+ @builder = Aurum::Builder::ParsingTableBuilder.new(augmented_grammar, nil)
138
+ @parsing_table = @builder.build
139
+ @lookahead_level = 4
140
+ end
141
+
142
+ it_should_behave_like 'LALR(n)'
143
+ end
144
+
145
+ describe Aurum::Builder::ParsingTableBuilder, ' not LR(n) : Expression Grammar without operator precedences' do
146
+ it 'should raise not LR(n) error' do
147
+ syntax_rules = ExpressionGrammar.instance_eval {@syntax_rules}
148
+ augmented_grammar = Aurum::Builder::AugmentedGrammar.new(syntax_rules, Aurum::Grammar.nonterminal('expression'))
149
+ builder = Aurum::Builder::ParsingTableBuilder.new(augmented_grammar, nil)
150
+ lambda {builder.build}.should raise_error(RuntimeError, 'not LALR(n)')
151
+ end
152
+ end