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.
- 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
@@ -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
|