aurum 0.1.1 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +29 -0
- data/examples/dangling_else/grammar.rb +23 -0
- data/examples/expression/grammar.rb +28 -0
- data/examples/smalltalk/grammar.rb +151 -0
- data/examples/smalltalk/interpreter.rb +70 -0
- data/examples/yacc/grammar.rb +72 -0
- data/lib/aurum.rb +1 -9
- data/lib/aurum/engine.rb +39 -175
- data/lib/aurum/engine/parsing_facility.rb +107 -0
- data/lib/aurum/engine/tokenization_facility.rb +86 -0
- data/lib/aurum/grammar.rb +52 -219
- data/lib/aurum/grammar/automata.rb +194 -0
- data/lib/aurum/grammar/builder/augmented_grammar.rb +83 -0
- data/lib/aurum/grammar/builder/dot_logger.rb +66 -0
- data/lib/aurum/grammar/builder/lexical_table_builder.rb +55 -0
- data/lib/aurum/grammar/builder/parsing_table_builder.rb +238 -0
- data/lib/aurum/grammar/builder/set_of_items.rb +190 -0
- data/lib/aurum/grammar/compiled_tables.rb +20 -0
- data/lib/aurum/grammar/dsl/lexical_definition.rb +94 -0
- data/lib/aurum/grammar/dsl/syntax_definition.rb +79 -0
- data/lib/aurum/grammar/lexical_rules.rb +224 -0
- data/lib/aurum/grammar/metalang/grammar.rb +47 -0
- data/lib/aurum/grammar/syntax_rules.rb +95 -0
- data/spec/builder/dsl_definition/aurum_grammar_spec.rb +33 -0
- data/spec/engine/lexer_spec.rb +59 -0
- data/spec/engine/parser_spec.rb +90 -0
- data/spec/examples/dangling_else_example.rb +30 -0
- data/spec/examples/expression_example.rb +48 -0
- data/spec/examples/smalltalk_example.rb +50 -0
- data/spec/examples/yacc_spec.rb +30 -0
- data/spec/grammar/builder/lexical_table/automata_spec.rb +55 -0
- data/spec/grammar/builder/lexical_table/builder_spec.rb +78 -0
- data/spec/grammar/builder/lexical_table/character_set_spec.rb +100 -0
- data/spec/grammar/builder/lexical_table/pattern_spec.rb +11 -0
- data/spec/grammar/builder/lexical_table/regular_expression.rb +40 -0
- data/spec/grammar/builder/parsing_table/augmented_grammar_spec.rb +36 -0
- data/spec/grammar/builder/parsing_table/builder_spec.rb +152 -0
- data/spec/grammar/builder/parsing_table/digraph_traverser_spec.rb +42 -0
- data/spec/grammar/builder/parsing_table/item_spec.rb +51 -0
- data/spec/grammar/builder/parsing_table/sources_spec.rb +66 -0
- data/spec/grammar/builder/parsing_table/state_spec.rb +82 -0
- data/spec/grammar/dsl/character_classes_builder_spec.rb +50 -0
- data/spec/grammar/dsl/lexical_rules_builder_spec.rb +181 -0
- data/spec/grammar/dsl/precedence_builder_spec.rb +64 -0
- data/spec/grammar/dsl/productions_builder_spec.rb +78 -0
- data/spec/grammar/metalang/metalang_spec.rb +0 -0
- data/spec/grammar/precedence_spec.rb +42 -0
- data/spec/grammar/syntax_rules_spec.rb +31 -0
- data/spec/parser_matcher.rb +69 -0
- data/spec/pattern_matcher.rb +123 -0
- data/spec/spec_helper.rb +133 -0
- metadata +70 -36
- data/example/expression/expression.rb +0 -35
- data/example/expression/lisp.rb +0 -26
- data/lib/aurum/lexical_table_generator.rb +0 -429
- data/lib/aurum/parsing_table_generator.rb +0 -464
- data/test/engine/lexer_test.rb +0 -59
- data/test/engine/semantic_attributes_test.rb +0 -15
- data/test/grammar_definition/character_class_definition_test.rb +0 -28
- data/test/grammar_definition/grammar_definition_test.rb +0 -55
- data/test/grammar_definition/lexical_definition_test.rb +0 -56
- data/test/grammar_definition/operator_precedence_definition_test.rb +0 -35
- data/test/grammar_definition/production_definition_test.rb +0 -60
- data/test/lexical_table_generator/automata_test.rb +0 -74
- data/test/lexical_table_generator/character_set_test.rb +0 -73
- data/test/lexical_table_generator/interval_test.rb +0 -36
- data/test/lexical_table_generator/pattern_test.rb +0 -115
- data/test/lexical_table_generator/subset_determinizer_test.rb +0 -19
- data/test/lexical_table_generator/table_generator_test.rb +0 -126
- data/test/parsing_table_generator/augmented_grammar_test.rb +0 -45
- data/test/parsing_table_generator/lalr_n_computation_test.rb +0 -92
- data/test/parsing_table_generator/lr_0_automata_test.rb +0 -94
- data/test/parsing_table_generator/lr_item_test.rb +0 -27
- data/test/parsing_table_generator/parsing_table_state_test.rb +0 -39
- data/test/parsing_table_generator/precedence_table_test.rb +0 -28
- data/test/parsing_table_generator/production_test.rb +0 -9
- data/test/test_helper.rb +0 -103
@@ -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
|