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 CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- ZTI1YmQ5MTEwOTcyYmZjZmVjMzE0Y2NmNjBhNzA0MzE3NTgzYWE1Yw==
4
+ NDY5YjhjZjdmMGRlZTVhYzk4NWI1NjlkNTQ4N2MzMDMwZjEyMmRhYg==
5
5
  data.tar.gz: !binary |-
6
- MjA1YTQ4NWVjNTNjNjdjY2NlN2M2NTQ5NDQzNWQxZWU4NGEwOWU1OA==
6
+ ZjM3OTdhY2VhMzhjYzA5ZDMwYzQyNDM5ZjIxZGJmNzMyMjljMGU1ZQ==
7
7
  !binary "U0hBNTEy":
8
8
  metadata.gz: !binary |-
9
- OGQwOTA2NjE0MzM2NjA0MzlkZTAxYzE0ZTUzZDY0MTMyMGRhNzQzYTg2YmIy
10
- NWExNGI4NGQ0OGQ0NGEwMDkyNTJkMjRkMDM5ODZhZmY5NmIxYWVkNzA4YmVl
11
- YjMxMjNjMGRjOTE5NDJmZmI4YjYzYjFhYTNhYmI3NDMxZGYxNjM=
9
+ Mzg2MjBhOTllMWUwMjZiN2U0YWE3MGMwNzg5MGJmZmJhNzcyN2UyNDk5NDdh
10
+ YjgwYmRlMDBhODQwZTAyYmU2ZWVkZTlhZTVlZjUxNzJmMzM4ZDYwOWUwNGM2
11
+ ZjEzZDMwYjk2Y2Y5MGVhYTk2Mzg4MGY5NDNkOThiOTg2NmViMzM=
12
12
  data.tar.gz: !binary |-
13
- NWM3NGY1ODJlM2JjYTJjMGVmYmJiODMyOWUzYzEzZGNmYjMxYjZiMTJmMGNh
14
- NTY4ZGZmMWY4ZjIzODA3ZjVjYmVhODYwOWYwMWQ2NmRjNWFkNTEzNzUwZTMy
15
- YTcwMDAzN2RkMDg1ZWFjNWYyNmI1OWM1MzgyYzNjMmJjNWQ5OTU=
13
+ NGZhM2RmYmQzYzZlMGMzZjQ3MWRlNGZmOGRlMzUyMGYwOWI2NjVkZWQzOThm
14
+ NDg3OWU4MmMyNmVhNzBkMmZlZTEwYWU1MTE0YjA4YjQxZjJiOWJiNjc2ZTFk
15
+ MGE2YWJlZDEzMDNkZDMxNTEyYzU5MDg0YTgzNGJjMGFmMDY2Y2Q=
@@ -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
 
@@ -3,7 +3,7 @@
3
3
 
4
4
  module Rley # Module used as a namespace
5
5
  # The version number of the gem.
6
- Version = '0.1.02'
6
+ Version = '0.1.03'
7
7
 
8
8
  # Brief description of the gem.
9
9
  Description = "Ruby implementation of the Earley's parsing algorithm"
@@ -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
@@ -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
@@ -35,6 +35,7 @@ module Rley # Open this namespace to avoid module qualifier prefixes
35
35
  end
36
36
  =end
37
37
 
38
+
38
39
  # Grammar 1: A very simple language
39
40
  # (based on example in N. Wirth "Compiler Construction" book, p. 6)
40
41
  # S ::= A.
@@ -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
- let(:start_dotted_rule) { DottedItem.new(prod_S, 0) }
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.02
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-06 00:00:00.000000000 Z
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