rley 0.1.02 → 0.1.03
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +8 -8
- data/CHANGELOG.md +4 -0
- data/lib/rley/constants.rb +1 -1
- data/lib/rley/parse_tree_visitor.rb +102 -0
- data/lib/rley/parser/parsing.rb +1 -0
- data/lib/rley/ptree/non_terminal_node.rb +8 -0
- data/lib/rley/ptree/parse_tree.rb +13 -1
- data/lib/rley/ptree/terminal_node.rb +6 -0
- data/spec/rley/parse_tree_visitor_spec.rb +174 -0
- data/spec/rley/parser/earley_parser_spec.rb +1 -0
- data/spec/rley/parser/parsing_spec.rb +5 -2
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
NDY5YjhjZjdmMGRlZTVhYzk4NWI1NjlkNTQ4N2MzMDMwZjEyMmRhYg==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
ZjM3OTdhY2VhMzhjYzA5ZDMwYzQyNDM5ZjIxZGJmNzMyMjljMGU1ZQ==
|
7
7
|
!binary "U0hBNTEy":
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
Mzg2MjBhOTllMWUwMjZiN2U0YWE3MGMwNzg5MGJmZmJhNzcyN2UyNDk5NDdh
|
10
|
+
YjgwYmRlMDBhODQwZTAyYmU2ZWVkZTlhZTVlZjUxNzJmMzM4ZDYwOWUwNGM2
|
11
|
+
ZjEzZDMwYjk2Y2Y5MGVhYTk2Mzg4MGY5NDNkOThiOTg2NmViMzM=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
NGZhM2RmYmQzYzZlMGMzZjQ3MWRlNGZmOGRlMzUyMGYwOWI2NjVkZWQzOThm
|
14
|
+
NDg3OWU4MmMyNmVhNzBkMmZlZTEwYWU1MTE0YjA4YjQxZjJiOWJiNjc2ZTFk
|
15
|
+
MGE2YWJlZDEzMDNkZDMxNTEyYzU5MDg0YTgzNGJjMGFmMDY2Y2Q=
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,7 @@
|
|
1
|
+
### 0.1.03 / 2014-12-08
|
2
|
+
* [NEW] `ParseTreeVisitor` class. A class that walks through a parse tree.
|
3
|
+
* [NEW] Method `accept` added to `ParseTree`, `TerminalNode`, `NonTerminalNode` classes.
|
4
|
+
|
1
5
|
### 0.1.02 / 2014-12-06
|
2
6
|
* [CHANGE] Upgraded code & spec files to reach 100% code coverage again.
|
3
7
|
|
data/lib/rley/constants.rb
CHANGED
@@ -0,0 +1,102 @@
|
|
1
|
+
module Rley # This module is used as a namespace
|
2
|
+
# A visitor class dedicated in the visit of ParseTree objects.
|
3
|
+
class ParseTreeVisitor
|
4
|
+
# Link to the parse tree to visit
|
5
|
+
attr_reader(:ptree)
|
6
|
+
|
7
|
+
# List of objects that subscribed to the visit event notification.
|
8
|
+
attr_reader(:subscribers)
|
9
|
+
|
10
|
+
# Build a visitor for the given ptree.
|
11
|
+
# @param aParseTree [ParseTree] the parse tree to visit.
|
12
|
+
def initialize(aParseTree)
|
13
|
+
@ptree = aParseTree
|
14
|
+
@subscribers = []
|
15
|
+
end
|
16
|
+
|
17
|
+
public
|
18
|
+
|
19
|
+
# Add a subscriber for the visit event notifications.
|
20
|
+
# @param aSubscriber [Object]
|
21
|
+
def subscribe(aSubscriber)
|
22
|
+
subscribers << aSubscriber
|
23
|
+
end
|
24
|
+
|
25
|
+
# Remove the given object from the subscription list.
|
26
|
+
# The object won't be notified of visit events.
|
27
|
+
# @param aSubscriber [Object]
|
28
|
+
def unsubscribe(aSubscriber)
|
29
|
+
subscribers.delete_if { |entry| entry == aSubscriber }
|
30
|
+
end
|
31
|
+
|
32
|
+
# The signal to begin the visit of the parse tree.
|
33
|
+
def start()
|
34
|
+
ptree.accept(self)
|
35
|
+
end
|
36
|
+
|
37
|
+
|
38
|
+
# Visit event. The visitor is about to visit the ptree.
|
39
|
+
# @param aParseTree [ParseTree] the ptree to visit.
|
40
|
+
def start_visit_ptree(aParseTree)
|
41
|
+
broadcast(:before_ptree, aParseTree)
|
42
|
+
end
|
43
|
+
|
44
|
+
|
45
|
+
# Visit event. The visitor is about to visit the given non terminal node.
|
46
|
+
# @param aNonTerminal [NonTerminalNode] the node to visit.
|
47
|
+
def start_visit_nonterminal(aNonTerminalNode)
|
48
|
+
broadcast(:before_non_terminal, aNonTerminalNode)
|
49
|
+
end
|
50
|
+
|
51
|
+
# Visit event. The visitor is about to visit the children of a non
|
52
|
+
# terminal node.
|
53
|
+
# @param aParentNode [NonTeminalNode] the (non-terminal) parent node.
|
54
|
+
def visit_children(aParentNode)
|
55
|
+
children = aParentNode.children
|
56
|
+
broadcast(:before_children, aParentNode, children)
|
57
|
+
|
58
|
+
# Let's proceed with the visit of children
|
59
|
+
children.each { |a_node| a_node.accept(self) }
|
60
|
+
|
61
|
+
broadcast(:after_children, aParentNode, children)
|
62
|
+
end
|
63
|
+
|
64
|
+
# Visit event. The visitor is visiting the
|
65
|
+
# given terminal node.
|
66
|
+
# @param aTerminal [TerminalNode] the terminal to visit.
|
67
|
+
def visit_terminal(aTerminalNode)
|
68
|
+
broadcast(:before_terminal, aTerminalNode)
|
69
|
+
broadcast(:after_terminal, aTerminalNode)
|
70
|
+
end
|
71
|
+
|
72
|
+
|
73
|
+
# Visit event. The visitor has completed its visit of the given
|
74
|
+
# non-terminal node.
|
75
|
+
# @param aNonTerminal [NonTerminalNode] the node to visit.
|
76
|
+
def end_visit_nonterminal(aNonTerminalNode)
|
77
|
+
broadcast(:after_non_terminal, aNonTerminalNode)
|
78
|
+
end
|
79
|
+
|
80
|
+
# Visit event. The visitor has completed the visit of the ptree.
|
81
|
+
# @param aParseTree [ParseTree] the ptree to visit.
|
82
|
+
def end_visit_ptree(aParseTree)
|
83
|
+
broadcast(:after_ptree, aParseTree)
|
84
|
+
end
|
85
|
+
|
86
|
+
private
|
87
|
+
|
88
|
+
# Send a notification to all subscribers.
|
89
|
+
# @param msg [Symbol] event to notify
|
90
|
+
# @param args [Array] arguments of the notification.
|
91
|
+
def broadcast(msg, *args)
|
92
|
+
subscribers.each do |a_subscriber|
|
93
|
+
next unless a_subscriber.respond_to?(msg)
|
94
|
+
a_subscriber.send(msg, *args)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
|
99
|
+
end # class
|
100
|
+
end # module
|
101
|
+
|
102
|
+
# End of file
|
data/lib/rley/parser/parsing.rb
CHANGED
@@ -42,6 +42,7 @@ module Rley # This module is used as a namespace
|
|
42
42
|
when Syntax::Terminal
|
43
43
|
state_set_index -= 1
|
44
44
|
ptree.step_back(state_set_index)
|
45
|
+
#ptree.current_node.token = tokens[state_set_index]
|
45
46
|
parse_state = chart[state_set_index].predecessor_state(parse_state)
|
46
47
|
curr_dotted_item = parse_state.dotted_rule
|
47
48
|
|
@@ -15,6 +15,14 @@ module Rley # This module is used as a namespace
|
|
15
15
|
def add_child(aChildNode)
|
16
16
|
children << aChildNode
|
17
17
|
end
|
18
|
+
|
19
|
+
# Part of the 'visitee' role in Visitor design pattern.
|
20
|
+
# @param aVisitor[ParseTreeVisitor] the visitor
|
21
|
+
def accept(aVisitor)
|
22
|
+
aVisitor.start_visit_nonterminal(self)
|
23
|
+
aVisitor.visit_children(self)
|
24
|
+
aVisitor.end_visit_nonterminal(self)
|
25
|
+
end
|
18
26
|
end # class
|
19
27
|
end # module
|
20
28
|
end # module
|
@@ -21,6 +21,18 @@ module Rley # This module is used as a namespace
|
|
21
21
|
return current_path.last
|
22
22
|
end
|
23
23
|
|
24
|
+
# Part of the 'visitee' role in the Visitor design pattern.
|
25
|
+
# A visitee is expected to accept the visit from a visitor object
|
26
|
+
# @param aVisitor [ParseTreeVisitor] the visitor object
|
27
|
+
def accept(aVisitor)
|
28
|
+
aVisitor.start_visit_ptree(self)
|
29
|
+
|
30
|
+
# Let's proceed with the visit of nodes
|
31
|
+
root.accept(aVisitor) if root
|
32
|
+
|
33
|
+
aVisitor.end_visit_ptree(self)
|
34
|
+
end
|
35
|
+
|
24
36
|
|
25
37
|
def add_children(aProduction, aRange)
|
26
38
|
aProduction.rhs.each do |symb|
|
@@ -47,7 +59,6 @@ module Rley # This module is used as a namespace
|
|
47
59
|
# @param tokenPos [Fixnum] position of the matching input token
|
48
60
|
def step_up(tokenPos)
|
49
61
|
(pos, last_node) = current_path.pop(2)
|
50
|
-
#last_node.range = low_bound({low: tokenPos})
|
51
62
|
end
|
52
63
|
|
53
64
|
|
@@ -69,6 +80,7 @@ module Rley # This module is used as a namespace
|
|
69
80
|
end
|
70
81
|
|
71
82
|
private
|
83
|
+
|
72
84
|
def low_bound(aRange)
|
73
85
|
result = case aRange
|
74
86
|
when Hash then aRange[:low]
|
@@ -9,6 +9,12 @@ module Rley # This module is used as a namespace
|
|
9
9
|
def initialize(aTerminalSymbol, aRange)
|
10
10
|
super(aTerminalSymbol, aRange)
|
11
11
|
end
|
12
|
+
|
13
|
+
# Part of the 'visitee' role in Visitor design pattern.
|
14
|
+
# @param aVisitor[ParseTreeVisitor] the visitor
|
15
|
+
def accept(aVisitor)
|
16
|
+
aVisitor.visit_terminal(self)
|
17
|
+
end
|
12
18
|
|
13
19
|
end # class
|
14
20
|
end # module
|
@@ -0,0 +1,174 @@
|
|
1
|
+
require_relative '../spec_helper'
|
2
|
+
|
3
|
+
require_relative '../../lib/rley/syntax/grammar_builder'
|
4
|
+
require_relative '../../lib/rley/parser/token'
|
5
|
+
require_relative '../../lib/rley/parser/earley_parser'
|
6
|
+
# Load the class under test
|
7
|
+
require_relative '../../lib/rley/parse_tree_visitor'
|
8
|
+
|
9
|
+
module Rley # Open this namespace to avoid module qualifier prefixes
|
10
|
+
describe ParseTreeVisitor do
|
11
|
+
let(:grammar_abc) do
|
12
|
+
builder = Syntax::GrammarBuilder.new
|
13
|
+
builder.add_terminals('a', 'b', 'c')
|
14
|
+
builder.add_production('S' => ['A'])
|
15
|
+
builder.add_production('A' => %w(a A c))
|
16
|
+
builder.add_production('A' => ['b'])
|
17
|
+
builder.grammar
|
18
|
+
end
|
19
|
+
|
20
|
+
let(:a_) { grammar_abc.name2symbol['a'] }
|
21
|
+
let(:b_) { grammar_abc.name2symbol['b'] }
|
22
|
+
let(:c_) { grammar_abc.name2symbol['c'] }
|
23
|
+
|
24
|
+
|
25
|
+
# Helper method that mimicks the output of a tokenizer
|
26
|
+
# for the language specified by gramma_abc
|
27
|
+
let(:grm_abc_tokens1) do
|
28
|
+
[
|
29
|
+
Parser::Token.new('a', a_),
|
30
|
+
Parser::Token.new('a', a_),
|
31
|
+
Parser::Token.new('b', b_),
|
32
|
+
Parser::Token.new('c', c_),
|
33
|
+
Parser::Token.new('c', c_)
|
34
|
+
]
|
35
|
+
end
|
36
|
+
|
37
|
+
# Factory method that builds a sample parse tree.
|
38
|
+
# Generated tree has the following structure:
|
39
|
+
# S[0,5]
|
40
|
+
# +- A[0,5]
|
41
|
+
# +- a[0,0]
|
42
|
+
# +- A[1,4]
|
43
|
+
# | +- a[1,1]
|
44
|
+
# | +- A[2,3]
|
45
|
+
# | | +- b[2,3]
|
46
|
+
# | +- c[3,4]
|
47
|
+
# +- c[4,5]
|
48
|
+
# Capital letters represent non-terminal nodes
|
49
|
+
let(:grm_abc_ptree1) do
|
50
|
+
parser = Parser::EarleyParser.new(grammar_abc)
|
51
|
+
parse_result = parser.parse(grm_abc_tokens1)
|
52
|
+
parse_result.parse_tree
|
53
|
+
end
|
54
|
+
|
55
|
+
|
56
|
+
# Default instantiation rule
|
57
|
+
subject { ParseTreeVisitor.new(grm_abc_ptree1) }
|
58
|
+
|
59
|
+
|
60
|
+
context 'Standard creation & initialization:' do
|
61
|
+
|
62
|
+
it 'should be initialized with a parse tree argument' do
|
63
|
+
expect { ParseTreeVisitor.new(grm_abc_ptree1) }.not_to raise_error
|
64
|
+
end
|
65
|
+
|
66
|
+
it 'should know the parse tree to visit' do
|
67
|
+
expect(subject.ptree).to eq(grm_abc_ptree1)
|
68
|
+
end
|
69
|
+
|
70
|
+
it "shouldn't have subscribers at start" do
|
71
|
+
expect(subject.subscribers).to be_empty
|
72
|
+
end
|
73
|
+
end # context
|
74
|
+
|
75
|
+
|
76
|
+
context 'Subscribing:' do
|
77
|
+
let(:listener1) { double('fake-subscriber1') }
|
78
|
+
let(:listener2) { double('fake-subscriber2') }
|
79
|
+
|
80
|
+
it 'should allow subscriptions' do
|
81
|
+
subject.subscribe(listener1)
|
82
|
+
expect(subject.subscribers.size).to eq(1)
|
83
|
+
expect(subject.subscribers).to eq([listener1])
|
84
|
+
|
85
|
+
subject.subscribe(listener2)
|
86
|
+
expect(subject.subscribers.size).to eq(2)
|
87
|
+
expect(subject.subscribers).to eq([listener1, listener2])
|
88
|
+
end
|
89
|
+
|
90
|
+
it 'should allow un-subcriptions' do
|
91
|
+
subject.subscribe(listener1)
|
92
|
+
subject.subscribe(listener2)
|
93
|
+
subject.unsubscribe(listener2)
|
94
|
+
expect(subject.subscribers.size).to eq(1)
|
95
|
+
expect(subject.subscribers).to eq([listener1])
|
96
|
+
subject.unsubscribe(listener1)
|
97
|
+
expect(subject.subscribers).to be_empty
|
98
|
+
end
|
99
|
+
|
100
|
+
end # context
|
101
|
+
|
102
|
+
|
103
|
+
context 'Notifying visit events:' do
|
104
|
+
# Default instantiation rule
|
105
|
+
subject do
|
106
|
+
instance = ParseTreeVisitor.new(grm_abc_ptree1)
|
107
|
+
instance.subscribe(listener1)
|
108
|
+
instance
|
109
|
+
end
|
110
|
+
|
111
|
+
# Use doubles/mocks to simulate formatters
|
112
|
+
let(:listener1) { double('fake-subscriber1') }
|
113
|
+
let(:listener2) { double('fake-subscriber2') }
|
114
|
+
|
115
|
+
# Sample non-terminal node
|
116
|
+
let(:nterm_node) do
|
117
|
+
first_big_a = grm_abc_ptree1.root.children[0]
|
118
|
+
second_big_a = first_big_a.children[1]
|
119
|
+
second_big_a.children[1]
|
120
|
+
end
|
121
|
+
|
122
|
+
# Sample terminal node
|
123
|
+
let(:term_node) { nterm_node.children[0] }
|
124
|
+
|
125
|
+
it 'should react to the start_visit_ptree message' do
|
126
|
+
# Notify subscribers when start the visit of the ptree
|
127
|
+
expect(listener1).to receive(:before_ptree).with(grm_abc_ptree1)
|
128
|
+
subject.start_visit_ptree(grm_abc_ptree1)
|
129
|
+
end
|
130
|
+
|
131
|
+
it 'should react to the start_visit_nonterminal message' do
|
132
|
+
# Notify subscribers when start the visit of a non-terminal node
|
133
|
+
expect(listener1).to receive(:before_non_terminal).with(nterm_node)
|
134
|
+
subject.start_visit_nonterminal(nterm_node)
|
135
|
+
end
|
136
|
+
|
137
|
+
it 'should react to the visit_children message' do
|
138
|
+
# Notify subscribers when start the visit of children nodes
|
139
|
+
children = nterm_node.children
|
140
|
+
expect(listener1).to receive(:before_children).with(nterm_node, children)
|
141
|
+
expect(listener1).to receive(:before_terminal).with(children[0])
|
142
|
+
expect(listener1).to receive(:after_terminal).with(children[0])
|
143
|
+
expect(listener1).to receive(:after_children).with(nterm_node, children)
|
144
|
+
subject.visit_children(nterm_node)
|
145
|
+
end
|
146
|
+
|
147
|
+
it 'should react to the end_visit_nonterminal message' do
|
148
|
+
# Notify subscribers when ending the visit of a non-terminal node
|
149
|
+
expect(listener1).to receive(:after_non_terminal).with(nterm_node)
|
150
|
+
subject.end_visit_nonterminal(nterm_node)
|
151
|
+
end
|
152
|
+
|
153
|
+
it 'should react to the visit_terminal message' do
|
154
|
+
# Notify subscribers when start & ending the visit of a terminal node
|
155
|
+
expect(listener1).to receive(:before_terminal).with(term_node)
|
156
|
+
expect(listener1).to receive(:after_terminal).with(term_node)
|
157
|
+
subject.visit_terminal(term_node)
|
158
|
+
end
|
159
|
+
|
160
|
+
it 'should react to the end_visit_ptree message' do
|
161
|
+
# Notify subscribers when ending the visit of the ptree
|
162
|
+
expect(listener1).to receive(:after_ptree).with(grm_abc_ptree1)
|
163
|
+
subject.end_visit_ptree(grm_abc_ptree1)
|
164
|
+
end
|
165
|
+
|
166
|
+
it 'should begin the visit when requested' do
|
167
|
+
subject.start
|
168
|
+
end
|
169
|
+
|
170
|
+
end # context
|
171
|
+
end # describe
|
172
|
+
end # module
|
173
|
+
|
174
|
+
# End of file
|
@@ -26,7 +26,7 @@ module Rley # Open this namespace to avoid module qualifier prefixes
|
|
26
26
|
let(:prod_S) { Syntax::Production.new(nt_S, [nt_A]) }
|
27
27
|
let(:prod_A1) { Syntax::Production.new(nt_A, [a_, nt_A, c_]) }
|
28
28
|
let(:prod_A2) { Syntax::Production.new(nt_A, [b_]) }
|
29
|
-
|
29
|
+
|
30
30
|
|
31
31
|
# Helper method that mimicks the output of a tokenizer
|
32
32
|
# for the language specified by gramma_abc
|
@@ -40,6 +40,9 @@ module Rley # Open this namespace to avoid module qualifier prefixes
|
|
40
40
|
]
|
41
41
|
end
|
42
42
|
|
43
|
+
|
44
|
+
let(:start_dotted_rule) { DottedItem.new(prod_S, 0) }
|
45
|
+
|
43
46
|
# Default instantiation rule
|
44
47
|
subject { Parsing.new(start_dotted_rule, grm1_tokens) }
|
45
48
|
|
@@ -128,7 +131,7 @@ module Rley # Open this namespace to avoid module qualifier prefixes
|
|
128
131
|
end
|
129
132
|
end
|
130
133
|
|
131
|
-
|
134
|
+
|
132
135
|
it 'should build the parse tree for a non-ambiguous grammar' do
|
133
136
|
parser = EarleyParser.new(sample_grammar1)
|
134
137
|
instance = parser.parse(token_seq1)
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rley
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.03
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dimitri Geshef
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-12-
|
11
|
+
date: 2014-12-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|
@@ -96,6 +96,7 @@ files:
|
|
96
96
|
- lib/rley/parser/parsing.rb
|
97
97
|
- lib/rley/parser/state_set.rb
|
98
98
|
- lib/rley/parser/token.rb
|
99
|
+
- lib/rley/parse_tree_visitor.rb
|
99
100
|
- lib/rley/ptree/non_terminal_node.rb
|
100
101
|
- lib/rley/ptree/parse_tree.rb
|
101
102
|
- lib/rley/ptree/parse_tree_node.rb
|
@@ -117,6 +118,7 @@ files:
|
|
117
118
|
- spec/rley/parser/parsing_spec.rb
|
118
119
|
- spec/rley/parser/state_set_spec.rb
|
119
120
|
- spec/rley/parser/token_spec.rb
|
121
|
+
- spec/rley/parse_tree_visitor_spec.rb
|
120
122
|
- spec/rley/ptree/non_terminal_node_spec.rb
|
121
123
|
- spec/rley/ptree/parse_tree_node_spec.rb
|
122
124
|
- spec/rley/ptree/parse_tree_spec.rb
|
@@ -170,6 +172,7 @@ test_files:
|
|
170
172
|
- spec/rley/parser/parsing_spec.rb
|
171
173
|
- spec/rley/parser/state_set_spec.rb
|
172
174
|
- spec/rley/parser/token_spec.rb
|
175
|
+
- spec/rley/parse_tree_visitor_spec.rb
|
173
176
|
- spec/rley/ptree/non_terminal_node_spec.rb
|
174
177
|
- spec/rley/ptree/parse_tree_node_spec.rb
|
175
178
|
- spec/rley/ptree/parse_tree_spec.rb
|