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,42 @@
1
+ helper_dir = File.join(File.dirname(__FILE__), '..', '..', '..')
2
+ $:.unshift(helper_dir) unless $:.include?(helper_dir)
3
+
4
+ require 'spec_helper'
5
+
6
+ def construct_automata grammar, start
7
+ syntax_rules = grammar.instance_eval {@syntax_rules}
8
+ augmented_grammar = Aurum::Builder::AugmentedGrammar.new(syntax_rules, Aurum::Grammar.nonterminal(start))
9
+ Aurum::Builder::ParsingTableBuilder.new(augmented_grammar, nil).instance_eval do
10
+ construct_automata
11
+ @augmented_grammar.compute_nullables
12
+ @augmented_grammar.compute_first_sets
13
+ @states
14
+ end
15
+ end
16
+
17
+ describe Aurum::Builder::DigraphTraverser, '(follow set calculator)' do
18
+ before :each do
19
+ @follow_set_calculator = Aurum::Builder::DigraphTraverser.new do |config|
20
+ (config.symbol == Aurum::Builder::StartSymbol) ? [false, [Aurum::Grammar::EOF], 65535] : [true, nil, nil]
21
+ end
22
+ end
23
+
24
+ it 'should calculate follow set for grammar 4.11 in dragon book' do
25
+ @states = construct_automata(Grammar411InDragonBook, 'expression')
26
+ right = Aurum::Grammar.terminal('$literal_)')
27
+ plus, mul = Aurum::Grammar.terminal('$literal_+'), Aurum::Grammar.terminal('$literal_*')
28
+ follow_set(Aurum::Grammar.nonterminal('expression')).should == [right, Aurum::Grammar::EOF].to_set
29
+ follow_set(Aurum::Grammar.nonterminal('expression1')).should == [right, Aurum::Grammar::EOF].to_set
30
+ follow_set(Aurum::Grammar.nonterminal('term')).should == [plus, right, Aurum::Grammar::EOF].to_set
31
+ follow_set(Aurum::Grammar.nonterminal('term1')).should == [plus, right, Aurum::Grammar::EOF].to_set
32
+ follow_set(Aurum::Grammar.nonterminal('factor')).should == [plus, mul, right, Aurum::Grammar::EOF].to_set
33
+ end
34
+
35
+ def follow_set symbol
36
+ follow_set = [].to_set
37
+ for state in @states
38
+ follow_set |= @follow_set_calculator.traverse(Aurum::Builder::Configuration.new(state, symbol))
39
+ end
40
+ follow_set
41
+ end
42
+ end
@@ -0,0 +1,51 @@
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::LRItem do
7
+ before :all do
8
+ @E, @T, @id = Aurum::Grammar.nonterminal('E'), Aurum::Grammar.nonterminal('T'), Aurum::Grammar.nonterminal('id')
9
+ @production = Aurum::Grammar.production(@E, [@T, @id])
10
+ end
11
+
12
+ it 'dot symbol of [E -> .T id] should be T' do
13
+ Aurum::Builder::LRItem.new(@production, 0).dot_symbol.should == @T
14
+ end
15
+ it 'dot symbol of [E -> T .id] should be id' do
16
+ Aurum::Builder::LRItem.new(@production, 1).dot_symbol.should == @id
17
+ end
18
+ it 'dot symbol of [E -> T id.] should be nil' do
19
+ Aurum::Builder::LRItem.new(@production, 2).dot_symbol.should be_nil
20
+ end
21
+ it '[E -> .T id] should not be kernel item' do
22
+ Aurum::Builder::LRItem.new(@production, 0).should_not be_kernel
23
+ end
24
+ it '[E -> T .id] should be kernel item' do
25
+ Aurum::Builder::LRItem.new(@production, 1).should be_kernel
26
+ end
27
+ it '[E -> T id.] should be kernel item' do
28
+ Aurum::Builder::LRItem.new(@production, 2).should be_kernel
29
+ end
30
+ it '[E -> .] should be kernel item' do
31
+ Aurum::Builder::LRItem.new(Aurum::Grammar.production(@E, []), 0).should be_kernel
32
+ end
33
+ it '[E -> T id.] should be handle item' do
34
+ Aurum::Builder::LRItem.new(@production, 2).should be_handle
35
+ end
36
+ it '[E -> .] should be handle item' do
37
+ Aurum::Builder::LRItem.new(Aurum::Grammar.production(@E, []), 0).should be_handle
38
+ end
39
+ it '[$start -> .E] should be kernel item' do
40
+ Aurum::Builder::LRItem.new(Aurum::Grammar.production(Aurum::Builder::StartSymbol, [@E]), 0).should be_kernel
41
+ end
42
+ it 'remaining part of [E -> .T id] should be [T id]' do
43
+ Aurum::Builder::LRItem.new(@production, 0).remaining.should == [@T, @id]
44
+ end
45
+ it 'remaining part of [E -> T .id] should be [id]' do
46
+ Aurum::Builder::LRItem.new(@production, 1).remaining.should == [@id]
47
+ end
48
+ it 'remaining part of [E -> T id .] should be []' do
49
+ Aurum::Builder::LRItem.new(@production, 2).remaining.should == []
50
+ end
51
+ end
@@ -0,0 +1,66 @@
1
+ helper_dir = File.join(File.dirname(__FILE__), '..', '..', '..')
2
+ $:.unshift(helper_dir) unless $:.include?(helper_dir)
3
+
4
+ require 'spec_helper'
5
+
6
+ def determine_lookaheads grammar, start
7
+ syntax_rules = grammar.instance_eval {@syntax_rules}
8
+ @augmented_grammar = Aurum::Builder::AugmentedGrammar.new(syntax_rules, Aurum::Grammar.nonterminal(start))
9
+ Aurum::Builder::ParsingTableBuilder.new(@augmented_grammar, nil).instance_eval do
10
+ construct_automata
11
+ unless @inconsistent_states.empty?
12
+ @augmented_grammar.compute_nullables
13
+ @augmented_grammar.compute_first_sets
14
+ @conflict_states = [].to_set
15
+ @follow_set_calculator = Aurum::Builder::DigraphTraverser.new do |config|
16
+ (config.symbol == Aurum::Builder::StartSymbol) ? [false, [Aurum::Grammar::EOF], 65535] : [true, nil, nil]
17
+ end
18
+ for inconsistent_state in @inconsistent_states do
19
+ determine_lookaheads_for(inconsistent_state)
20
+ @conflict_states << inconsistent_state if inconsistent_state.conflict?
21
+ end
22
+ @states
23
+ end
24
+ end
25
+ end
26
+
27
+ describe Aurum::Builder::Sources, ' for conflict state' do
28
+ before :all do
29
+ @slist, @rule = Aurum::Grammar.nonterminal('slist'), Aurum::Grammar.nonterminal('rule')
30
+ @s, @generate = Aurum::Grammar.terminal('_s'), Aurum::Grammar.terminal('$literal_->')
31
+ @states = determine_lookaheads(BnfGrammarInCharlesThesis, 'bnf')
32
+ end
33
+
34
+ it 'should use current state as configuration stack for read reduce action' do
35
+ state = @states.find {|state| state.conflict?}
36
+ sources = Aurum::Builder::Sources.new(state, @s)
37
+ stacks = sources[Aurum::ParsingTable::Action.new(:read_reduce, Aurum::Grammar.production(@slist, [@slist, @s]))]
38
+ stacks.should == [[@states.last]].to_set
39
+ end
40
+
41
+ it 'should use follow source as configuration stack for reduce action' do
42
+ state = @states.find {|state| state.conflict?}
43
+ sources = Aurum::Builder::Sources.new(state, @s)
44
+ stacks = sources[Aurum::ParsingTable::Action.new(:reduce, Aurum::Grammar.production(@rule, [@s, @generate, @slist]))]
45
+ stacks.should == [[@states[1]]].to_set
46
+ end
47
+ end
48
+
49
+ describe Aurum::Builder::Sources, ' for lookahead state' do
50
+ before :each do
51
+ @x, @w = Aurum::Grammar.terminal('_x'), Aurum::Grammar.terminal('_w')
52
+ @B, @C = Aurum::Grammar.nonterminal('b'), Aurum::Grammar.nonterminal('c')
53
+ @states = determine_lookaheads(LALR4Grammar, 's')
54
+ end
55
+
56
+ it 'should construct sources using previous one' do
57
+ previous_sources = Aurum::Builder::Sources.new(@states[4], @x)
58
+ lookahead = Aurum::Builder::State.new(@augmented_grammar, [])
59
+ lookahead.actions[@x] = @states[4].actions[@x]
60
+ sources = Aurum::Builder::Sources.new(lookahead, @x, previous_sources, @x)
61
+ action = Aurum::ParsingTable::Action.new(:reduce, Aurum::Grammar.production(@B, [@w]))
62
+ sources[action].should == [[@states[14]]].to_set
63
+ action = Aurum::ParsingTable::Action.new(:reduce, Aurum::Grammar.production(@C, [@w]))
64
+ sources[action].should == [[@states[10]]].to_set
65
+ end
66
+ end
@@ -0,0 +1,82 @@
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::State do
7
+
8
+ before :all do
9
+ @syntax_rules = Grammar419InDragonBook.instance_eval {@syntax_rules}
10
+ @E = Aurum::Grammar.nonterminal('expression')
11
+ @e_productions = @syntax_rules.productions(@E).to_a
12
+ @f_productions = @syntax_rules.productions(Aurum::Grammar.nonterminal('factor')).to_a
13
+ @augmented_grammar = Aurum::Builder::AugmentedGrammar.new(@syntax_rules, @E)
14
+ @production = @augmented_grammar.instance_eval {@start_production}
15
+ end
16
+
17
+ it 'should be represented by kernel items' do
18
+ kernel_item = Aurum::Builder::LRItem.new(@production)
19
+ non_kernel_item = Aurum::Builder::LRItem.new(@e_productions[0])
20
+ Aurum::Builder::State.new(@augmented_grammar, [kernel_item, non_kernel_item]).should == [kernel_item]
21
+ end
22
+
23
+ it 'should goto new state on X when A -> a.Xb in current state' do
24
+ state = Aurum::Builder::State.new(@augmented_grammar, [Aurum::Builder::LRItem.new(@production)])
25
+ state.goto(@E).inspect.should == '[$start -> expression ., expression -> expression .+ term ]'
26
+ end
27
+
28
+ it 'should return nil when try to goto on X but A -> a.Xb not in current state' do
29
+ state = Aurum::Builder::State.new(@augmented_grammar, [Aurum::Builder::LRItem.new(@production)])
30
+ state.goto(Aurum::Grammar.terminal('other')).should be_nil
31
+ end
32
+
33
+ it 'should be consistent if contains one and only one handle item' do
34
+ state = Aurum::Builder::State.new(@augmented_grammar, [Aurum::Builder::LRItem.new(@production, 1)])
35
+ state.inconsistent?.should be_false
36
+ end
37
+
38
+ it 'should be consistent if does not contain any handle item' do
39
+ state = Aurum::Builder::State.new(@augmented_grammar, [Aurum::Builder::LRItem.new(@e_productions[1]),
40
+ Aurum::Builder::LRItem.new(@e_productions[1], 2)])
41
+ state.inconsistent?.should be_false
42
+ end
43
+
44
+ it 'should be inconsistent if contains handle and other kernels' do
45
+ state = Aurum::Builder::State.new(@augmented_grammar, [Aurum::Builder::LRItem.new(@e_productions[1], 1),
46
+ Aurum::Builder::LRItem.new(@production, 1)])
47
+ state.inconsistent?.should be_true
48
+ end
49
+
50
+ it 'should be inconsistent if contains multi-handles' do
51
+ state = Aurum::Builder::State.new(@augmented_grammar, [Aurum::Builder::LRItem.new(@e_productions[1], 3),
52
+ Aurum::Builder::LRItem.new(@production, 1)])
53
+ state.inconsistent?.should be_true
54
+ end
55
+
56
+ it 'should be read reducable if contains one and only one handle item' do
57
+ state = Aurum::Builder::State.new(@augmented_grammar, [Aurum::Builder::LRItem.new(@f_productions[1], 1)])
58
+ state.read_reduce.should == @f_productions[1]
59
+ end
60
+ =begin
61
+ it 'should return predecessors according to given symbol list' do
62
+ state1 = Aurum::Builder::State.new([Aurum::Builder::LRItem.new(@production, 0)])
63
+ state2 = Aurum::Builder::State.new([Aurum::Builder::LRItem.new(@production, 1)])
64
+ state1.add_shift(@E, state2)
65
+ state2.predecessors([@E]).should == [state1]
66
+ end
67
+
68
+ it 'should return all including configs' do
69
+ production = @E.productions[0]
70
+ state0 = Aurum::ParsingTableBuilder::State.new([Aurum::ParsingTableBuilder::LRItem.new(production, 0)])
71
+ state1 = Aurum::ParsingTableBuilder::State.new([Aurum::ParsingTableBuilder::LRItem.new(production, 1)])
72
+ state2 = Aurum::ParsingTableBuilder::State.new([Aurum::ParsingTableBuilder::LRItem.new(production, 2)])
73
+ state3 = Aurum::ParsingTableBuilder::State.new([Aurum::ParsingTableBuilder::LRItem.new(production, 3)])
74
+ state0.add_shift(@E, state1)
75
+ state1.add_shift(@plus, state2)
76
+ state2.add_shift(@T, state3)
77
+ include_configs = []
78
+ state2.include_each(@T) {|state, symbol| include_configs << [state, symbol]}
79
+ include_configs.should == [[state0, @E]]
80
+ end
81
+ =end
82
+ end
@@ -0,0 +1,50 @@
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::DSL::CharacterClassesBuilder do
7
+ before :each do
8
+ @builder = Aurum::Grammar::DSL::CharacterClassesBuilder.new
9
+ end
10
+
11
+ it 'should define named character classes' do
12
+ @builder.instance_eval do
13
+ number range(?0, ?9)
14
+ number_enum enum('0123456789')
15
+ end
16
+ char_set = @builder.__named_character_classes__['number']
17
+ char_set.should == Aurum::Grammar::LexicalRules::CharacterSet.range(?0, ?9)
18
+ char_set = @builder.__named_character_classes__['number_enum']
19
+ char_set.should == Aurum::Grammar::LexicalRules::CharacterSet.enum('0123456789')
20
+ end
21
+
22
+ it 'should define character class by expression' do
23
+ @builder.instance_eval do
24
+ number range(?0, ?9)
25
+ alpha range(?a, ?z)
26
+ number_and_alpha number + alpha
27
+ end
28
+ char_set = @builder.__named_character_classes__['number_and_alpha']
29
+ char_set.should == (Aurum::Grammar::LexicalRules::CharacterSet.range(?0, ?9) + Aurum::Grammar::LexicalRules::CharacterSet.range(?a, ?z))
30
+ end
31
+
32
+ it 'should contain pre-definied classes' do
33
+ @builder.__named_character_classes__.should have_key('any')
34
+ @builder.__named_character_classes__.should have_key('underscore')
35
+ @builder.__named_character_classes__.should have_key('single_quote')
36
+ @builder.__named_character_classes__.should have_key('double_quote')
37
+ @builder.__named_character_classes__.should have_key('ascii')
38
+ @builder.__named_character_classes__.should have_key('ascii_punctuation')
39
+ @builder.__named_character_classes__.should have_key('decimal_number')
40
+ end
41
+
42
+ it 'should not re-define character class' do
43
+ lambda do
44
+ @builder.instance_eval do
45
+ number range(?0, ?9)
46
+ number range(?1, ?9)
47
+ end
48
+ end.should raise_error(RuntimeError, 'already defined character class: number')
49
+ end
50
+ end
@@ -0,0 +1,181 @@
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::DSL::LexicalRulesBuilder, ' pattern definition' do
7
+ include PatternMatcher
8
+ before :each do
9
+ @lexical_rules = Aurum::Grammar::LexicalRules.new
10
+ @char_classes = Aurum::Grammar::DSL::CharacterClassesBuilder.new
11
+ @builder = Aurum::Grammar::DSL::LexicalRulesBuilder.new(@lexical_rules, @char_classes)
12
+ end
13
+
14
+ it 'should create pattern using range' do
15
+ pattern = @builder.range(?a, ?c)
16
+ 'a'.should be_matched_by('[a-c]', pattern)
17
+ 'b'.should be_matched_by('[a-c]', pattern)
18
+ 'c'.should be_matched_by('[a-c]', pattern)
19
+ end
20
+
21
+ it 'should create pattern using enum' do
22
+ pattern = @builder.enum('qwe')
23
+ 'q'.should be_matched_by('q|w|e', pattern)
24
+ 'w'.should be_matched_by('q|w|e', pattern)
25
+ 'e'.should be_matched_by('q|w|e', pattern)
26
+ end
27
+
28
+ it 'should create pattern using string literal' do
29
+ pattern = @builder.string('string')
30
+ 'string'.should be_matched_by('string', pattern)
31
+ end
32
+
33
+ it 'should create pattern using pre-defined character classes' do
34
+ @char_classes.instance_eval {qwe enum('qwe')}
35
+ pattern = @builder.qwe
36
+ 'q'.should be_matched_by('q|w|e', pattern)
37
+ 'w'.should be_matched_by('q|w|e', pattern)
38
+ 'e'.should be_matched_by('q|w|e', pattern)
39
+ end
40
+
41
+ it 'should raise error if can not find pattern with specified name' do
42
+ lambda {@builder.inexist}.should raise_error(RuntimeError, "can not find pattern named 'inexist'")
43
+ end
44
+
45
+ it 'should create pattern by concatenating other patterns' do
46
+ string_a = @builder.string('string_a')
47
+ string_b = @builder.string('string_b')
48
+ pattern = @builder.concat(string_a, string_b)
49
+ 'string_astring_b'.should be_matched_by('string_astring_b', pattern)
50
+ end
51
+
52
+ it 'should create pattern by concatenating patterns and string literals' do
53
+ string_a = @builder.string('string_a')
54
+ pattern = @builder.concat(string_a, 'string_b')
55
+ 'string_astring_b'.should be_matched_by('string_astring_b', pattern)
56
+ end
57
+
58
+ it 'should add named pattern to pattern list' do
59
+ @builder.instance_eval do
60
+ pattern_a 'abc', string('def').zero_or_more
61
+ end
62
+ pattern = @builder.instance_eval {@named_patterns['pattern_a']}
63
+ 'abc'.should be_matched_by('abc(def)*', pattern)
64
+ 'abcdefdef'.should be_matched_by('abc(def)*', pattern)
65
+ end
66
+
67
+ it 'should add state local named pattern' do
68
+ @builder.instance_eval do
69
+ pattern_a 'abc', string('def').zero_or_more
70
+ within :state_a do
71
+ pattern_a 'def'
72
+ end
73
+ end
74
+ pattern = @builder.instance_eval {@named_patterns['pattern_a']}
75
+ 'abcdefdef'.should be_matched_by('abc(def)*', pattern)
76
+ end
77
+ end
78
+
79
+ describe Aurum::Grammar::DSL::LexicalRulesBuilder, ' leixcal action definition' do
80
+ before :each do
81
+ @lexical_rules = Aurum::Grammar::LexicalRules.new
82
+ @char_classes = Aurum::Grammar::DSL::CharacterClassesBuilder.new
83
+ @builder = Aurum::Grammar::DSL::LexicalRulesBuilder.new(@lexical_rules, @char_classes)
84
+ end
85
+
86
+ it 'should add pattern to initial state' do
87
+ @builder._id 'string'
88
+ actions = @lexical_rules.patterns['initial'].values
89
+ actions.should == [Aurum::LexicalTable::Action.new('_id', nil, nil, nil)]
90
+ end
91
+
92
+ it 'should add pattern to new lexical states' do
93
+ @builder.within :state_a, :state_b do
94
+ _id 'string'
95
+ end
96
+ actions = @lexical_rules.patterns['state_a'].values
97
+ actions.should == [Aurum::LexicalTable::Action.new( '_id', nil, nil, nil)]
98
+ actions = @lexical_rules.patterns['state_b'].values
99
+ actions.should == [Aurum::LexicalTable::Action.new('_id', nil, nil, nil)]
100
+ end
101
+
102
+ it 'should add pattern to exist lexical states' do
103
+ @builder.within :state_a, :state_b do
104
+ _id 'string'
105
+ end
106
+ @builder.within :state_a do
107
+ _id_2 'string_2'
108
+ end
109
+ actions = @lexical_rules.patterns['state_a'].values
110
+ actions.to_set.should == [Aurum::LexicalTable::Action.new('_id', nil, nil, nil), Aurum::LexicalTable::Action.new('_id_2', nil, nil, nil)].to_set
111
+ end
112
+
113
+ it "should define 'recognize' lexical action" do
114
+ pattern = @builder.pattern 'string'
115
+ @builder.instance_eval do
116
+ match(pattern, :recognize => :id)
117
+ end
118
+ action = @lexical_rules.patterns['initial'][pattern]
119
+ action.should == Aurum::LexicalTable::Action.new('id', nil, nil, nil)
120
+ end
121
+
122
+ it "should define 'shift to' lexical action" do
123
+ pattern = @builder.pattern 'string'
124
+ @builder.instance_eval do
125
+ match(pattern, :shift_to => :state)
126
+ end
127
+ action = @lexical_rules.patterns['initial'][pattern]
128
+ action.should == Aurum::LexicalTable::Action.new(nil,'state', nil, nil)
129
+ end
130
+
131
+ it "should define 'recognize and shift to' lexical action" do
132
+ pattern = @builder.pattern 'string'
133
+ @builder.instance_eval do
134
+ match(pattern, :recognize => :id, :shift_to => :state)
135
+ end
136
+ action = @lexical_rules.patterns['initial'][pattern]
137
+ action.should == Aurum::LexicalTable::Action.new('id', 'state', nil, nil)
138
+ end
139
+
140
+ it "should store add lexical event id to lexical action" do
141
+ pattern = @builder.pattern 'string'
142
+ @builder.instance_eval do
143
+ match(pattern, :event => :identifier_recongized)
144
+ end
145
+ action = @lexical_rules.patterns['initial'][pattern]
146
+ action.should == Aurum::LexicalTable::Action.new(nil, nil, 'identifier_recongized', nil)
147
+ end
148
+
149
+ it 'should store semantic action to lexical action' do
150
+ semantic_action = lambda { ruby action }
151
+ @builder._id 'string', &semantic_action
152
+ actions = @lexical_rules.patterns['initial'].values
153
+ actions.should == [Aurum::LexicalTable::Action.new('_id', nil, nil, semantic_action)]
154
+ end
155
+
156
+ it "should define 'ignore' lexical action" do
157
+ pattern = @builder.pattern 'string'
158
+ @builder.instance_eval do
159
+ ignore(pattern)
160
+ end
161
+ actions = @lexical_rules.patterns['initial'].values
162
+ actions.should == [Aurum::LexicalTable::Action.new('$ignored', nil, nil, nil)]
163
+ end
164
+
165
+ it "should define 'ignore and shif to' lexical action" do
166
+ pattern = @builder.pattern 'string'
167
+ @builder.instance_eval do
168
+ ignore(pattern, :event => :id_ignore, :shift_to => :state)
169
+ end
170
+ actions = @lexical_rules.patterns['initial'].values
171
+ actions.should == [Aurum::LexicalTable::Action.new('$ignored', 'state', 'id_ignore', nil)]
172
+ end
173
+
174
+ it 'should store semantic action to ignored lexical action' do
175
+ semantic_action = lambda { ruby action }
176
+ pattern = @builder.pattern 'string'
177
+ @builder.ignore pattern, &semantic_action
178
+ actions = @lexical_rules.patterns['initial'].values
179
+ actions.should == [Aurum::LexicalTable::Action.new('$ignored', nil, nil, semantic_action)]
180
+ end
181
+ end