rley 0.1.02 → 0.1.03
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.
- 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
|