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 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