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,64 @@
|
|
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::PrecedencesBuilder do
|
7
|
+
before :each do
|
8
|
+
@syntax_rules = Aurum::Grammar::SyntaxRules.new
|
9
|
+
@builder = Aurum::Grammar::DSL::PrecedencesBuilder.new(@syntax_rules)
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'string should be declared as literal non-associative operator' do
|
13
|
+
@builder.nonassoc '+'
|
14
|
+
precedence = @syntax_rules.operator_precedence('$literal_+')
|
15
|
+
precedence.should eql?(Aurum::Grammar.precedence(:non_associative, 0))
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'symbol should be declared as terminal non-associative operator' do
|
19
|
+
@builder.nonassoc :_id
|
20
|
+
precedence = @syntax_rules.operator_precedence('_id')
|
21
|
+
precedence.should eql?(Aurum::Grammar.precedence(:non_associative, 0))
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'string should be declared as literal left-associative operator' do
|
25
|
+
@builder.left '+'
|
26
|
+
precedence = @syntax_rules.operator_precedence('$literal_+')
|
27
|
+
precedence.should eql?(Aurum::Grammar.precedence(:left_associative, 0))
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'symbol should be declared as terminal left-associative operator' do
|
31
|
+
@builder.left :_id
|
32
|
+
precedence = @syntax_rules.operator_precedence('_id')
|
33
|
+
precedence.should eql?(Aurum::Grammar.precedence(:left_associative, 0))
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'string should be declared as literal right-associative operator' do
|
37
|
+
@builder.right '+'
|
38
|
+
precedence = @syntax_rules.operator_precedence('$literal_+')
|
39
|
+
precedence.should eql?(Aurum::Grammar.precedence(:right_associative, 0))
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'symbol should be declared as terminal right-associative operator' do
|
43
|
+
@builder.right :_id
|
44
|
+
precedence = @syntax_rules.operator_precedence('_id')
|
45
|
+
precedence.should eql?(Aurum::Grammar.precedence(:right_associative, 0))
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'operators declared in the same time should have same precedence' do
|
49
|
+
@builder.nonassoc '+', '-'
|
50
|
+
precedence = @syntax_rules.operator_precedence('$literal_+')
|
51
|
+
precedence.should eql?(Aurum::Grammar.precedence(:non_associative, 0))
|
52
|
+
precedence = @syntax_rules.operator_precedence('$literal_-')
|
53
|
+
precedence.should eql?(Aurum::Grammar.precedence(:non_associative, 0))
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'operators declared earlier should have higher precedence' do
|
57
|
+
@builder.nonassoc '+'
|
58
|
+
@builder.nonassoc '-'
|
59
|
+
precedence = @syntax_rules.operator_precedence('$literal_+')
|
60
|
+
precedence.should eql?(Aurum::Grammar.precedence(:non_associative, 0))
|
61
|
+
precedence = @syntax_rules.operator_precedence('$literal_-')
|
62
|
+
precedence.should eql?(Aurum::Grammar.precedence(:non_associative, -1))
|
63
|
+
end
|
64
|
+
end
|
@@ -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
|
+
describe Aurum::Grammar::DSL::ProductionsBuilder do
|
7
|
+
before :each do
|
8
|
+
@syntax_rules, @actions = Aurum::Grammar::SyntaxRules.new, {}
|
9
|
+
@builder = Aurum::Grammar::DSL::ProductionsBuilder.new(@syntax_rules, @actions)
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'should add terminal to terminal list' do
|
13
|
+
@builder.instance_eval do
|
14
|
+
nonterminal _terminal
|
15
|
+
end
|
16
|
+
terminals = @syntax_rules.instance_eval {@terminals}
|
17
|
+
terminals.size.should == 1
|
18
|
+
terminals['_terminal'].should == Aurum::Grammar.terminal('_terminal')
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'should add literal to terminal and literal list' do
|
22
|
+
@builder.instance_eval do
|
23
|
+
nonterminal '+'
|
24
|
+
end
|
25
|
+
terminals, literals = @syntax_rules.instance_eval {[@terminals, @literals]}
|
26
|
+
terminals.size.should == 1
|
27
|
+
terminals['$literal_+'].should == Aurum::Grammar.terminal('$literal_+')
|
28
|
+
literals.should == ['+'].to_set
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'should add production to nonterminal' do
|
32
|
+
@builder.instance_eval do
|
33
|
+
nonterminal _terminal
|
34
|
+
end
|
35
|
+
productions = @syntax_rules.productions(Aurum::Grammar.nonterminal('nonterminal'))
|
36
|
+
productions.map {|prod| prod.inspect}.join(' , ').should == 'nonterminal -> _terminal'
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'should add empty production to nonterminal' do
|
40
|
+
@builder.instance_eval do
|
41
|
+
nonterminal _
|
42
|
+
end
|
43
|
+
productions = @syntax_rules.productions(Aurum::Grammar.nonterminal('nonterminal'))
|
44
|
+
productions.map {|prod| prod.inspect}.join(' , ').should == 'nonterminal -> '
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'should use object_id as anonymouse production name' do
|
48
|
+
@builder.instance_eval do
|
49
|
+
nonterminal _terminal
|
50
|
+
end
|
51
|
+
production = @syntax_rules.productions(Aurum::Grammar.nonterminal('nonterminal')).to_a[0]
|
52
|
+
production.name.should == "$production_#{production.object_id}"
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'should use last symbol as production name' do
|
56
|
+
@builder.instance_eval do
|
57
|
+
nonterminal _terminal, :production_name
|
58
|
+
end
|
59
|
+
production = @syntax_rules.productions(Aurum::Grammar.nonterminal('nonterminal')).to_a[0]
|
60
|
+
production.name.should == 'production_name'
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'should add action to actions table for anonymouse production' do
|
64
|
+
@builder.instance_eval do
|
65
|
+
nonterminal _terminal { 'action' }
|
66
|
+
end
|
67
|
+
production = @syntax_rules.productions(Aurum::Grammar.nonterminal('nonterminal')).to_a[0]
|
68
|
+
@actions[production.name].should_not be_nil
|
69
|
+
end
|
70
|
+
|
71
|
+
it 'should add action to actions table for named production' do
|
72
|
+
@builder.instance_eval do
|
73
|
+
nonterminal _terminal, :name do 'action' end
|
74
|
+
end
|
75
|
+
production = @syntax_rules.productions(Aurum::Grammar.nonterminal('nonterminal')).to_a[0]
|
76
|
+
@actions[production.name].should_not be_nil
|
77
|
+
end
|
78
|
+
end
|
File without changes
|
@@ -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
|
+
describe Aurum::Grammar::SyntaxRules::Precedence do
|
7
|
+
it 'precedence2 should greater than precedence2 if operator1 has higher precedence' do
|
8
|
+
precedence1 = Aurum::Grammar::SyntaxRules::Precedence.new(:left_associative, 0)
|
9
|
+
precedence2 = Aurum::Grammar::SyntaxRules::Precedence.new(:left_associative, -1)
|
10
|
+
precedence1.should > precedence2
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'precedence2 should less than precedence1 if precedence1 has higher precedence' do
|
14
|
+
precedence1 = Aurum::Grammar::SyntaxRules::Precedence.new(:left_associative, 0)
|
15
|
+
precedence2 = Aurum::Grammar::SyntaxRules::Precedence.new(:left_associative, -1)
|
16
|
+
precedence2.should < precedence1
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'precedence1 should greater then precedence2 if operators are left-associative and same precedence' do
|
20
|
+
precedence1 = Aurum::Grammar::SyntaxRules::Precedence.new(:left_associative, 0)
|
21
|
+
precedence2 = Aurum::Grammar::SyntaxRules::Precedence.new(:left_associative, 0)
|
22
|
+
precedence2.should > precedence1
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'precedence2 should greater then precedence1 if operators are left-associative and same precedence' do
|
26
|
+
precedence1 = Aurum::Grammar::SyntaxRules::Precedence.new(:left_associative, 0)
|
27
|
+
precedence2 = Aurum::Grammar::SyntaxRules::Precedence.new(:left_associative, 0)
|
28
|
+
precedence1.should > precedence2
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'precedence1 should less then precedence2 if operators are left-associative and same precedence' do
|
32
|
+
precedence1 = Aurum::Grammar::SyntaxRules::Precedence.new(:right_associative, 0)
|
33
|
+
precedence2 = Aurum::Grammar::SyntaxRules::Precedence.new(:right_associative, 0)
|
34
|
+
precedence1.should < precedence2
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'precedence2 should less then precedence1 if operators are left-associative and same precedence' do
|
38
|
+
precedence1 = Aurum::Grammar::SyntaxRules::Precedence.new(:right_associative, 0)
|
39
|
+
precedence2 = Aurum::Grammar::SyntaxRules::Precedence.new(:right_associative, 0)
|
40
|
+
precedence2.should < precedence1
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,31 @@
|
|
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::SyntaxRules do
|
7
|
+
before :all do
|
8
|
+
@syntax_rules = Aurum::Grammar::SyntaxRules.new
|
9
|
+
@E, @T, @plus = Aurum::Grammar.nonterminal('E'), Aurum::Grammar.nonterminal('T'), Aurum::Grammar.terminal('$literal_+')
|
10
|
+
@syntax_rules.add_syntax_rule Aurum::Grammar.production(@E, [@E, @plus, @E])
|
11
|
+
@syntax_rules.add_syntax_rule Aurum::Grammar.production(@E, [@T, @plus, @T])
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'should add productions to nonterminal' do
|
15
|
+
productions = @syntax_rules.productions(@E).map {|production| production.inspect}.to_set
|
16
|
+
productions.should == ['E -> T + T', 'E -> E + E'].to_set
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'should not add duplicated productions' do
|
20
|
+
@syntax_rules.add_syntax_rule Aurum::Grammar.production(@E, [@T, @plus, @T])
|
21
|
+
productions = @syntax_rules.productions(@E).map {|production| production.inspect}.to_set
|
22
|
+
productions.size.should == 2
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'should assign precedence to operator symbol' do
|
26
|
+
precedence = Aurum::Grammar.precedence(:left_associative, 1)
|
27
|
+
@syntax_rules.add_operator_precedence('$literal_+', precedence)
|
28
|
+
@syntax_rules.assign_operator_precedence_to_symbols
|
29
|
+
@syntax_rules.instance_eval {@terminals['$literal_+'].precedence}.should == precedence
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
module ParserMatcher
|
2
|
+
def token(terminal, value = terminal)
|
3
|
+
Aurum::Engine::Token.new(terminal.to_s, value)
|
4
|
+
end
|
5
|
+
|
6
|
+
def literal(terminal, value = terminal)
|
7
|
+
Aurum::Engine::Token.new("$literal_#{terminal}", value)
|
8
|
+
end
|
9
|
+
|
10
|
+
def be_parsed_as(nonterminal, value=nil)
|
11
|
+
parser_creator = Aurum::Parser.new(@grammar, nonterminal)
|
12
|
+
BeParsedAs.new(parser_creator.new, nonterminal, value)
|
13
|
+
end
|
14
|
+
|
15
|
+
class BeParsedAs
|
16
|
+
def initialize parser, nonterminal, value
|
17
|
+
@parser, @nonterminal, @value = parser, nonterminal, value
|
18
|
+
class << @parser
|
19
|
+
def error_recovery
|
20
|
+
raise 'error_recover'
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def matches?(source)
|
26
|
+
@source = source
|
27
|
+
(result = @parser.parse(Lexer.new(source))) rescue return false
|
28
|
+
return true unless @value
|
29
|
+
@value.all? {|attr, value| result.__send__(attr) == value}
|
30
|
+
end
|
31
|
+
|
32
|
+
def failure_message
|
33
|
+
"[#{@source.join(', ')}] can't be parsed as #{@nonterminal}"
|
34
|
+
end
|
35
|
+
|
36
|
+
def negative_failure_message
|
37
|
+
"[#{@source.join(', ')}] can be parsed as #{@nonterminal}"
|
38
|
+
end
|
39
|
+
|
40
|
+
def description
|
41
|
+
"be parsed as #{@nonterminal} if [#{@source.join(', ')}] given"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
class Lexer
|
46
|
+
def initialize source
|
47
|
+
@source = source.map do |token|
|
48
|
+
case token
|
49
|
+
when String : Aurum::Engine::Token.new("$literal_#{token}", token)
|
50
|
+
when Symbol : Aurum::Engine::Token.new(token.to_s, token)
|
51
|
+
when Aurum::Engine::Token : token
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def next_symbol
|
57
|
+
symbol = @source.shift()
|
58
|
+
symbol ? symbol : Aurum::Engine::Token.new('$eof', '')
|
59
|
+
end
|
60
|
+
|
61
|
+
def pushback symbol
|
62
|
+
@source.unshift(symbol)
|
63
|
+
end
|
64
|
+
|
65
|
+
def inspect
|
66
|
+
@source.inspect
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,123 @@
|
|
1
|
+
module PatternMatcher
|
2
|
+
class BeMatchedBy
|
3
|
+
def initialize pattern_string, pattern
|
4
|
+
@pattern_string, @pattern = pattern_string, pattern
|
5
|
+
end
|
6
|
+
|
7
|
+
def matches?(lexeme)
|
8
|
+
@lexeme = lexeme
|
9
|
+
states = closure(@pattern.automata.table, [0])
|
10
|
+
lexeme.each_byte {|char| states = move(@pattern.automata.table, states, char)}
|
11
|
+
states.include?(@pattern.accept)
|
12
|
+
end
|
13
|
+
|
14
|
+
def failure_message
|
15
|
+
"'#@lexeme' is not matched by /#@pattern_string/"
|
16
|
+
end
|
17
|
+
|
18
|
+
def negative_failure_message
|
19
|
+
"'#@lexeme' is matched by /#@pattern_string/"
|
20
|
+
end
|
21
|
+
|
22
|
+
def description
|
23
|
+
"match '#@lexeme' by /#@pattern_string/"
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
def move automata, states, char
|
28
|
+
result = []
|
29
|
+
states.each {|state| automata[state].each {|tran| result.concat(closure(automata, [tran.destination])) if tran.character_set.include?(char)} }
|
30
|
+
result.uniq
|
31
|
+
end
|
32
|
+
|
33
|
+
def closure automata, states
|
34
|
+
closure, unvisited = Set.new(states.dup), states.dup
|
35
|
+
while !unvisited.empty?
|
36
|
+
automata[unvisited.pop].each do |tran|
|
37
|
+
[closure, unvisited].each {|x| x << tran.destination} if tran.character_set == Aurum::Grammar::LexicalRules::Epsilon && !closure.include?(tran.destination)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
closure.to_a
|
41
|
+
end
|
42
|
+
end
|
43
|
+
def be_matched_by pattern_string, pattern
|
44
|
+
BeMatchedBy.new(pattern_string, pattern)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
module DeterminizedPatternMatcher
|
49
|
+
class BeMatchedBy
|
50
|
+
def initialize pattern_string, pattern
|
51
|
+
@pattern_string, = pattern_string
|
52
|
+
@automata, @accepts = Aurum::Grammar::LexicalRules::SubsetDeterminizer.new(pattern.automata, [pattern.accept]).determinize
|
53
|
+
end
|
54
|
+
|
55
|
+
def matches?(lexeme)
|
56
|
+
@lexeme = lexeme
|
57
|
+
state = 0
|
58
|
+
lexeme.each_byte {|char| state = move(state, char)} rescue return false
|
59
|
+
@accepts.include?(state)
|
60
|
+
end
|
61
|
+
|
62
|
+
def failure_message
|
63
|
+
"'#@lexeme' is not matched by /#@pattern_string/"
|
64
|
+
end
|
65
|
+
|
66
|
+
def negative_failure_message
|
67
|
+
"'#@lexeme' is matched by /#@pattern_string/"
|
68
|
+
end
|
69
|
+
|
70
|
+
def description
|
71
|
+
"match '#@lexeme' by /#@pattern_string/"
|
72
|
+
end
|
73
|
+
|
74
|
+
private
|
75
|
+
def move state, char
|
76
|
+
@automata.table[state].each {|tran| return tran.destination if tran.character_set.include?(char)}
|
77
|
+
raise 'not matched'
|
78
|
+
end
|
79
|
+
end
|
80
|
+
def be_matched_by pattern_string, pattern
|
81
|
+
BeMatchedBy.new(pattern_string, pattern)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
module MinimizedPatternMatcher
|
86
|
+
class BeMatchedBy
|
87
|
+
def initialize pattern_string, pattern
|
88
|
+
@pattern_string, = pattern_string
|
89
|
+
automata, accepts = Aurum::Grammar::LexicalRules::SubsetDeterminizer.new(pattern.automata, [pattern.accept]).determinize
|
90
|
+
accept_actions = {}
|
91
|
+
accepts.keys.each {|state| accept_actions[state] = 'action'}
|
92
|
+
@automata, @accepts = Aurum::Grammar::LexicalRules::HopcroftMinimizer.new(automata, accept_actions).minimize
|
93
|
+
end
|
94
|
+
|
95
|
+
def matches?(lexeme)
|
96
|
+
@lexeme = lexeme
|
97
|
+
state = 0
|
98
|
+
lexeme.each_byte {|char| state = move(state, char)} rescue return false
|
99
|
+
@accepts.include?(state)
|
100
|
+
end
|
101
|
+
|
102
|
+
def failure_message
|
103
|
+
"'#@lexeme' is not matched by /#@pattern_string/"
|
104
|
+
end
|
105
|
+
|
106
|
+
def negative_failure_message
|
107
|
+
"'#@lexeme' is matched by /#@pattern_string/"
|
108
|
+
end
|
109
|
+
|
110
|
+
def description
|
111
|
+
"match '#@lexeme' by /#@pattern_string/"
|
112
|
+
end
|
113
|
+
|
114
|
+
private
|
115
|
+
def move state, char
|
116
|
+
@automata.table[state].each {|tran| return tran.destination if tran.character_set.include?(char)}
|
117
|
+
raise 'not matched'
|
118
|
+
end
|
119
|
+
end
|
120
|
+
def be_matched_by pattern_string, pattern
|
121
|
+
BeMatchedBy.new(pattern_string, pattern)
|
122
|
+
end
|
123
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,133 @@
|
|
1
|
+
lib_dir = File.join(File.dirname(__FILE__), '..', 'lib')
|
2
|
+
examples_dir = File.join(File.dirname(__FILE__), '..', 'examples')
|
3
|
+
$:.unshift(lib_dir) unless $:.include?(lib_dir)
|
4
|
+
$:.unshift(examples_dir) unless $:.include?(examples_dir)
|
5
|
+
|
6
|
+
require 'delegate'
|
7
|
+
require 'aurum/grammar'
|
8
|
+
require 'aurum/engine'
|
9
|
+
|
10
|
+
require File.join(File.dirname(__FILE__), 'pattern_matcher')
|
11
|
+
require File.join(File.dirname(__FILE__), 'parser_matcher')
|
12
|
+
|
13
|
+
class SimpleLR0Grammar < Aurum::Grammar
|
14
|
+
syntax_rules do
|
15
|
+
expression term, '+', term
|
16
|
+
term _id
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
class Grammar419InDragonBook < Aurum::Grammar
|
21
|
+
syntax_rules do
|
22
|
+
expression expression, '+', term
|
23
|
+
expression term
|
24
|
+
term term, '*', factor
|
25
|
+
term factor
|
26
|
+
factor '(', expression, ')'
|
27
|
+
factor _id
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
class ExpressionGrammar < Aurum::Grammar
|
32
|
+
syntax_rules do
|
33
|
+
expression expression, '+', expression
|
34
|
+
expression expression, '*', expression
|
35
|
+
expression '(', expression, ')'
|
36
|
+
expression _id
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
class ExpressionGrammarWithOperatorPrecedence < Aurum::Grammar
|
41
|
+
precedences do
|
42
|
+
left '*'
|
43
|
+
left '+'
|
44
|
+
end
|
45
|
+
|
46
|
+
productions do
|
47
|
+
expression expression, '+', expression
|
48
|
+
expression expression, '*', expression
|
49
|
+
expression '(', expression, ')'
|
50
|
+
expression _id
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
class BnfGrammarInCharlesThesis < Aurum::Grammar
|
55
|
+
productions do
|
56
|
+
bnf rlist
|
57
|
+
rlist rlist, rule
|
58
|
+
rlist _
|
59
|
+
rule _s, '->', slist
|
60
|
+
slist slist, _s
|
61
|
+
slist _
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
class Grammar411InDragonBook < Aurum::Grammar
|
66
|
+
productions do
|
67
|
+
expression term, expression1
|
68
|
+
expression1 '+', term, expression1
|
69
|
+
expression1 _
|
70
|
+
term factor, term1
|
71
|
+
term1 '*', factor, term1
|
72
|
+
term1 _
|
73
|
+
factor '(', expression, ')'
|
74
|
+
factor _id
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
class GrammarWithNullableSymbols < Aurum::Grammar
|
79
|
+
productions do
|
80
|
+
expression term
|
81
|
+
expression factor
|
82
|
+
term _
|
83
|
+
factor _id
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
class LALR4Grammar < Aurum::Grammar
|
88
|
+
productions do
|
89
|
+
s b, _x, _x, _x, _y, _z
|
90
|
+
s c, _x, _x, _x, _x, _z
|
91
|
+
s _x, b, _x, _x, _x, _x, _y
|
92
|
+
b _w
|
93
|
+
c _w
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
class BnfGrammarWithActions < Aurum::Grammar
|
98
|
+
productions do
|
99
|
+
bnf rlist
|
100
|
+
rlist rlist, rule {rlist.s_exp = rlist1.s_exp + [rule.s_exp]}
|
101
|
+
rlist _ {rlist.s_exp = []}
|
102
|
+
rule _s, '->', slist {rule.s_exp = [:rule, _s.value, slist.s_exp]}
|
103
|
+
slist slist, _s {slist.s_exp = slist1.s_exp + [_s.value]}
|
104
|
+
slist _ {slist.s_exp = []}
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
class GrammarForLexerTesting < Aurum::Grammar
|
109
|
+
tokens do
|
110
|
+
_numeric range(?0, ?9).one_or_more
|
111
|
+
match 'state_a_begin', :shift_to => :state_a
|
112
|
+
match 'state_b_begin', :shift_to => :state_b, :recognize => :_state_b_begin
|
113
|
+
match 'state_b_beg', :recognize => :_state_b_beg
|
114
|
+
ignore 'ignore'
|
115
|
+
within :state_a, :state_b do
|
116
|
+
_numeric range(?0, ?9).one_or_more
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
class BNFGrammar < Aurum::Grammar
|
122
|
+
productions do
|
123
|
+
bnf bnf, rules
|
124
|
+
bnf _
|
125
|
+
rules rules, '|', symbol_list
|
126
|
+
rules rule
|
127
|
+
rule _nonterminal, '->', symbol_list
|
128
|
+
symbol_list symbol_list, _nonterminal
|
129
|
+
symbol_list _
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
$pattern = Aurum::Grammar::LexicalRules::Pattern
|