rley 0.2.14 → 0.2.15
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 +4 -4
- data/CHANGELOG.md +3 -0
- data/LICENSE.txt +1 -1
- data/README.md +4 -6
- data/examples/parsers/parsing_another.rb +70 -0
- data/examples/parsers/parsing_right_recursive.rb +70 -0
- data/lib/rley/constants.rb +1 -1
- data/lib/rley/gfg/edge.rb +19 -0
- data/lib/rley/gfg/end_vertex.rb +24 -0
- data/lib/rley/gfg/epsilon_edge.rb +25 -0
- data/lib/rley/gfg/grm_flow_graph.rb +167 -0
- data/lib/rley/gfg/item_vertex.rb +26 -0
- data/lib/rley/gfg/non_terminal_vertex.rb +21 -0
- data/lib/rley/gfg/scan_edge.rb +25 -0
- data/lib/rley/gfg/start_vertex.rb +24 -0
- data/lib/rley/gfg/vertex.rb +24 -0
- data/lib/rley/parser/earley_parser.rb +4 -18
- data/lib/rley/parser/grm_items_builder.rb +23 -0
- data/lib/rley/parser/parsing.rb +1 -1
- data/spec/rley/formatter/json_spec.rb +0 -1
- data/spec/rley/gfg/edge_spec.rb +33 -0
- data/spec/rley/gfg/end_vertex_spec.rb +26 -0
- data/spec/rley/gfg/epsilon_edge_spec.rb +25 -0
- data/spec/rley/gfg/grm_flow_graph_spec.rb +139 -0
- data/spec/rley/gfg/item_vertex_spec.rb +50 -0
- data/spec/rley/gfg/non_terminal_vertex_spec.rb +25 -0
- data/spec/rley/gfg/scan_edge_spec.rb +30 -0
- data/spec/rley/gfg/start_vertex_spec.rb +26 -0
- data/spec/rley/gfg/vertex_spec.rb +38 -0
- data/spec/rley/parser/grm_items_builder_spec.rb +43 -0
- metadata +34 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9a8824f31c8801d99d12566267a5e8cd95bb26c1
|
4
|
+
data.tar.gz: d5a0887f857d5b0efe154f10a202932960bc2e9e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cbf13dc6ac58dced26074ed1b2f9a4aab99b2834ea720743621d0cec7f0e0b17b7dc89e19956765b4921f4e1f4601b1858da92bcf0260b86c41de5c1c040579d
|
7
|
+
data.tar.gz: fed291494d3178e6813f561ef301ac60fc88604c81c0f8b40ee8fa8639e20873cef29fc79d90b47993e228134b1dcf0a8502ad1c3336b7e91659cb3a0c006345
|
data/CHANGELOG.md
CHANGED
data/LICENSE.txt
CHANGED
data/README.md
CHANGED
@@ -7,6 +7,7 @@ Rley
|
|
7
7
|
[](https://coveralls.io/r/famished-tiger/Rley?branch=master)
|
8
8
|
[](http://badge.fury.io/rb/rley)
|
9
9
|
[](https://gemnasium.com/famished-tiger/Rley)
|
10
|
+
[](http://inch-ci.org/github/famished-tiger/Rley)
|
10
11
|
[](https://github.com/famished-tiger/Rley/blob/master/LICENSE.txt)
|
11
12
|
|
12
13
|
__Rley__ is a Ruby implementation of a parser using the [Earley](http://en.wikipedia.org/wiki/Earley_parser) algorithm.
|
@@ -29,13 +30,10 @@ The Earley's algorithm being more general is able to parse input that conforms t
|
|
29
30
|
|
30
31
|
This project is in "earley" stage.
|
31
32
|
####Roadmap:
|
33
|
+
- Rewrite the parser using the GFG (Grammar Flow Graph) approach
|
34
|
+
- Replace parse trees by shared packed parse forests
|
32
35
|
- Document the parser API
|
33
36
|
- Add more validation tests and sample grammars
|
34
|
-
- Add AST generation (and semantic actions?)
|
35
|
-
- Add DSL for grammar specification
|
36
|
-
- Add grammar validations
|
37
|
-
- Add error reporting
|
38
|
-
|
39
37
|
- Add a command-line interface
|
40
38
|
- Provide documentation and examples
|
41
39
|
|
@@ -59,5 +57,5 @@ Here are a few other ones:
|
|
59
57
|
|
60
58
|
Copyright
|
61
59
|
---------
|
62
|
-
Copyright (c) 2014-
|
60
|
+
Copyright (c) 2014-2016, Dimitri Geshef.
|
63
61
|
__Rley__ is released under the MIT License see [LICENSE.txt](https://github.com/famished-tiger/Rley/blob/master/LICENSE.txt) for details.
|
@@ -0,0 +1,70 @@
|
|
1
|
+
# Purpose: to demonstrate how to build and render a parse tree
|
2
|
+
|
3
|
+
require 'pp' # TODO remove this dependency
|
4
|
+
require 'rley' # Load the gem
|
5
|
+
|
6
|
+
# Steps to render a parse tree (of a valid parsed input):
|
7
|
+
# 1. Define a grammar
|
8
|
+
# 2. Create a tokenizer for the language
|
9
|
+
# 3. Create a parser for that grammar
|
10
|
+
# 4. Tokenize the input
|
11
|
+
# 5. Let the parser process the input
|
12
|
+
# 6. Generate a parse tree from the parse result
|
13
|
+
# 7. Render the parse tree (in JSON)
|
14
|
+
|
15
|
+
########################################
|
16
|
+
# Step 1. Define a problematic grammar
|
17
|
+
# Grammar Z: A grammar with hidden left recursion and a cycle
|
18
|
+
# (based on example 2 in article of Elizabeth Scott, "SPPF-Style Parsing From Earley Recognisers"
|
19
|
+
# Electronic Notes in Theoretical Computer Science 203 (2008) 53–67
|
20
|
+
# Let's create the grammar step-by-step with the grammar builder:
|
21
|
+
builder = Rley::Syntax::GrammarBuilder.new
|
22
|
+
builder.add_terminals('b')
|
23
|
+
builder.add_production('S' => %w(S S))
|
24
|
+
builder.add_production('S' => 'b')
|
25
|
+
|
26
|
+
# And now build the grammar...
|
27
|
+
grammar_tricky = builder.grammar
|
28
|
+
|
29
|
+
|
30
|
+
########################################
|
31
|
+
# 2. Create a tokenizer for the language
|
32
|
+
# The tokenizer transforms the input into an array of tokens
|
33
|
+
def tokenizer(aText, aGrammar)
|
34
|
+
tokens = aText.chars.map do |lexeme|
|
35
|
+
case lexeme
|
36
|
+
when 'b'
|
37
|
+
terminal = aGrammar.name2symbol[lexeme]
|
38
|
+
else
|
39
|
+
msg = "Unknown input text '#{lexeme}'"
|
40
|
+
fail StandardError, msg
|
41
|
+
end
|
42
|
+
Rley::Parser::Token.new(lexeme, terminal)
|
43
|
+
end
|
44
|
+
|
45
|
+
return tokens
|
46
|
+
end
|
47
|
+
|
48
|
+
########################################
|
49
|
+
# Step 3. Create a parser for that grammar
|
50
|
+
parser = Rley::Parser::EarleyParser.new(grammar_tricky)
|
51
|
+
|
52
|
+
########################################
|
53
|
+
# Step 3. Tokenize the input
|
54
|
+
valid_input = 'bbb'
|
55
|
+
tokens = tokenizer(valid_input, grammar_tricky)
|
56
|
+
|
57
|
+
########################################
|
58
|
+
# Step 5. Let the parser process the input
|
59
|
+
result = parser.parse(tokens)
|
60
|
+
puts "Parsing success? #{result.success?}"
|
61
|
+
puts "Parsing ambiguous? #{result.ambiguous?}"
|
62
|
+
#pp result
|
63
|
+
|
64
|
+
result.chart.state_sets.each_with_index do |aStateSet, index|
|
65
|
+
puts "State[#{index}]"
|
66
|
+
puts "========"
|
67
|
+
aStateSet.states.each { |aState| puts aState.to_s }
|
68
|
+
end
|
69
|
+
|
70
|
+
# End of file
|
@@ -0,0 +1,70 @@
|
|
1
|
+
# Purpose: to demonstrate how to build and render a parse tree
|
2
|
+
|
3
|
+
require 'pp' # TODO remove this dependency
|
4
|
+
require 'rley' # Load the gem
|
5
|
+
|
6
|
+
# Steps to render a parse tree (of a valid parsed input):
|
7
|
+
# 1. Define a grammar
|
8
|
+
# 2. Create a tokenizer for the language
|
9
|
+
# 3. Create a parser for that grammar
|
10
|
+
# 4. Tokenize the input
|
11
|
+
# 5. Let the parser process the input
|
12
|
+
# 6. Generate a parse tree from the parse result
|
13
|
+
# 7. Render the parse tree (in JSON)
|
14
|
+
|
15
|
+
########################################
|
16
|
+
# Step 1. Define a problematic grammar
|
17
|
+
# Grammar Z: A grammar with hidden left recursion and a cycle
|
18
|
+
# (based on example in book of D. Grune, C JH. Jacobs,
|
19
|
+
# "Parsing Techniques: A Practical Guide"
|
20
|
+
# Springer, ISBN: 978-1-4419-1901-4, (2010) p. 224
|
21
|
+
# Let's create the grammar step-by-step with the grammar builder:
|
22
|
+
builder = Rley::Syntax::GrammarBuilder.new
|
23
|
+
builder.add_terminals('a')
|
24
|
+
builder.add_production('S' => %w(a S))
|
25
|
+
builder.add_production('S' => []) # Empty RHS
|
26
|
+
|
27
|
+
# And now build the grammar...
|
28
|
+
right_recursive_gram = builder.grammar
|
29
|
+
|
30
|
+
|
31
|
+
########################################
|
32
|
+
# 2. Create a tokenizer for the language
|
33
|
+
# The tokenizer transforms the input into an array of tokens
|
34
|
+
def tokenizer(aText, aGrammar)
|
35
|
+
tokens = aText.chars.map do |lexeme|
|
36
|
+
case lexeme
|
37
|
+
when 'a'
|
38
|
+
terminal = aGrammar.name2symbol[lexeme]
|
39
|
+
else
|
40
|
+
msg = "Unknown input text '#{lexeme}'"
|
41
|
+
fail StandardError, msg
|
42
|
+
end
|
43
|
+
Rley::Parser::Token.new(lexeme, terminal)
|
44
|
+
end
|
45
|
+
|
46
|
+
return tokens
|
47
|
+
end
|
48
|
+
|
49
|
+
########################################
|
50
|
+
# Step 3. Create a parser for that grammar
|
51
|
+
parser = Rley::Parser::EarleyParser.new(right_recursive_gram)
|
52
|
+
|
53
|
+
########################################
|
54
|
+
# Step 3. Tokenize the input
|
55
|
+
valid_input = 'aaaa'
|
56
|
+
tokens = tokenizer(valid_input, right_recursive_gram)
|
57
|
+
|
58
|
+
########################################
|
59
|
+
# Step 5. Let the parser process the input
|
60
|
+
result = parser.parse(tokens)
|
61
|
+
puts "Parsing success? #{result.success?}"
|
62
|
+
#pp result
|
63
|
+
|
64
|
+
result.chart.state_sets.each_with_index do |aStateSet, index|
|
65
|
+
puts "State[#{index}]"
|
66
|
+
puts "========"
|
67
|
+
aStateSet.states.each { |aState| puts aState.to_s }
|
68
|
+
end
|
69
|
+
|
70
|
+
# End of file
|
data/lib/rley/constants.rb
CHANGED
@@ -0,0 +1,19 @@
|
|
1
|
+
module Rley # This module is used as a namespace
|
2
|
+
module GFG # This module is used as a namespace
|
3
|
+
# Abstract class. Represents an edge in a grammar flow graph.
|
4
|
+
# Responsibilities:
|
5
|
+
# - To know the successor vertex
|
6
|
+
class Edge
|
7
|
+
# The destination vertex of the edge .
|
8
|
+
attr_reader :successor
|
9
|
+
|
10
|
+
def initialize(thePredecessor, theSuccessor)
|
11
|
+
@successor = theSuccessor
|
12
|
+
thePredecessor.add_edge(self)
|
13
|
+
end
|
14
|
+
|
15
|
+
end # class
|
16
|
+
end # module
|
17
|
+
end # module
|
18
|
+
|
19
|
+
# End of file
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require_relative 'non_terminal_vertex'
|
2
|
+
|
3
|
+
module Rley # This module is used as a namespace
|
4
|
+
module GFG # This module is used as a namespace
|
5
|
+
# TODO: change definition.
|
6
|
+
# Represents a specialized vertex in a grammar flow graph
|
7
|
+
# that is associated to a given non-terminal symbol.
|
8
|
+
# Responsibilities (in addition to inherited ones):
|
9
|
+
# - Know its related non-terminal symbol
|
10
|
+
class EndVertex < NonTerminalVertex
|
11
|
+
|
12
|
+
def initialize(aNonTerminal)
|
13
|
+
super(aNonTerminal)
|
14
|
+
end
|
15
|
+
|
16
|
+
def label()
|
17
|
+
return "#{non_terminal}."
|
18
|
+
end
|
19
|
+
|
20
|
+
end # class
|
21
|
+
end # module
|
22
|
+
end # module
|
23
|
+
|
24
|
+
# End of file
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require_relative 'edge'
|
2
|
+
|
3
|
+
module Rley # This module is used as a namespace
|
4
|
+
module GFG # This module is used as a namespace
|
5
|
+
# Represents an edge in a grammar flow graph
|
6
|
+
# without change of the position in the input stream.
|
7
|
+
# Responsibilities:
|
8
|
+
# - To know the successor vertex
|
9
|
+
class EpsilonEdge < Edge
|
10
|
+
# The destination vertex of the edge .
|
11
|
+
attr_reader :successor
|
12
|
+
|
13
|
+
def initialize(thePredecessor, theSuccessor)
|
14
|
+
super(thePredecessor, theSuccessor)
|
15
|
+
end
|
16
|
+
|
17
|
+
def to_s()
|
18
|
+
" --> #{successor.label}"
|
19
|
+
end
|
20
|
+
|
21
|
+
end # class
|
22
|
+
end # module
|
23
|
+
end # module
|
24
|
+
|
25
|
+
# End of file
|
@@ -0,0 +1,167 @@
|
|
1
|
+
require_relative 'start_vertex'
|
2
|
+
require_relative 'end_vertex'
|
3
|
+
require_relative 'item_vertex'
|
4
|
+
require_relative 'epsilon_edge'
|
5
|
+
require_relative 'scan_edge'
|
6
|
+
|
7
|
+
module Rley # This module is used as a namespace
|
8
|
+
module GFG # This module is used as a namespace
|
9
|
+
# TODO: add definition
|
10
|
+
class GrmFlowGraph
|
11
|
+
# The set of all vertices in the graph
|
12
|
+
attr_reader :vertices
|
13
|
+
|
14
|
+
# The vertex marked as start node of the graph
|
15
|
+
attr_reader :start_vertex
|
16
|
+
|
17
|
+
# A Hash with pairs of the form: non-terminal symbol => start node
|
18
|
+
attr_reader :start_vertex_for
|
19
|
+
|
20
|
+
# A Hash with pairs of the form: non-terminal symbol => end node
|
21
|
+
attr_reader :end_vertex_for
|
22
|
+
|
23
|
+
def initialize(theDottedItems)
|
24
|
+
@vertices = []
|
25
|
+
@start_vertex_for = {}
|
26
|
+
@end_vertex_for = {}
|
27
|
+
|
28
|
+
build_graph(theDottedItems)
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
def add_vertex(aVertex)
|
33
|
+
# TODO: make setting of start vertex more robust
|
34
|
+
start_vertex = aVertex if vertices.empty?
|
35
|
+
vertices << aVertex
|
36
|
+
end
|
37
|
+
|
38
|
+
def build_graph(theDottedItems)
|
39
|
+
build_all_starts_ends(theDottedItems)
|
40
|
+
|
41
|
+
curr_production = nil
|
42
|
+
theDottedItems.each_with_index do |d_item, index_item|
|
43
|
+
if curr_production.nil? || curr_production != d_item.production
|
44
|
+
# Another production found...
|
45
|
+
curr_production = d_item.production
|
46
|
+
lhs = curr_production.lhs
|
47
|
+
# build_start_end_for(lhs) #Build start/end vertex per non-terminal
|
48
|
+
if curr_production.empty?
|
49
|
+
add_single_item(d_item)
|
50
|
+
else
|
51
|
+
# Add vertices and edges for dotted items of production
|
52
|
+
augment_graph(theDottedItems, index_item)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def build_all_starts_ends(theDottedItems)
|
59
|
+
productions_raw = theDottedItems.map(&:production)
|
60
|
+
productions = productions_raw.uniq
|
61
|
+
productions.each { |prod| build_start_end_for(prod.lhs) }
|
62
|
+
end
|
63
|
+
|
64
|
+
# if there is not yet a start vertex labelled .N in the GFG:
|
65
|
+
# add a start vertex labelled .N
|
66
|
+
# add an end vertex labelled N.
|
67
|
+
def build_start_end_for(aNonTerminal)
|
68
|
+
unless start_vertex_for.include?(aNonTerminal)
|
69
|
+
new_start_vertex = StartVertex.new(aNonTerminal)
|
70
|
+
start_vertex_for[aNonTerminal] = new_start_vertex
|
71
|
+
add_vertex(new_start_vertex)
|
72
|
+
|
73
|
+
new_end_vertex = EndVertex.new(aNonTerminal)
|
74
|
+
end_vertex_for[aNonTerminal] = new_end_vertex
|
75
|
+
add_vertex(new_end_vertex)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
|
80
|
+
def add_single_item(aDottedItem)
|
81
|
+
new_vertex = ItemVertex.new(aDottedItem)
|
82
|
+
add_vertex(new_vertex)
|
83
|
+
build_entry_edge(new_vertex)
|
84
|
+
build_exit_edge(new_vertex)
|
85
|
+
end
|
86
|
+
|
87
|
+
# Add vertices and edges for dotted items derived from same production
|
88
|
+
# production of the form: N => α[1] ... α[n] (non-empty rhs):
|
89
|
+
# # Rule 3
|
90
|
+
# create n+1 nodes labelled: N => . α[1] ... α[n], N => α[1] . ... α[n], ..., N => α[1] ... α[n] .
|
91
|
+
# add an epsilon entry edge: .N -> N => . α[1] ... α[n]
|
92
|
+
# add an epsilon exit edge: N => α[1] ... α[n] . -> N.
|
93
|
+
# For each symbol in rhs:
|
94
|
+
# if it is a terminal, say a:
|
95
|
+
# Rule 4
|
96
|
+
# add a scan edge: N => α[1] .a α[n] -> N => α[1] a. α[n]
|
97
|
+
# else # non-terminal symbol A in rhs:
|
98
|
+
# Rule 5
|
99
|
+
# add a call edge: N => α[1] .A α[n] -> .A
|
100
|
+
# add a return edge: A. -> N => α[1] A. α[n]
|
101
|
+
def augment_graph(theDottedItems, firstItemPos)
|
102
|
+
production = theDottedItems[firstItemPos].production
|
103
|
+
max_index = production.rhs.size + 1
|
104
|
+
(0...max_index).each do |index|
|
105
|
+
current_item = theDottedItems[firstItemPos+index]
|
106
|
+
new_vertex = ItemVertex.new(current_item)
|
107
|
+
add_vertex(new_vertex)
|
108
|
+
build_exit_edge(new_vertex) if current_item.reduce_item? # At end?
|
109
|
+
if current_item.at_start?
|
110
|
+
build_entry_edge(new_vertex)
|
111
|
+
else
|
112
|
+
# At least one symbol before the dot
|
113
|
+
# Retrieve the symbol before the dot...
|
114
|
+
prev_symbol = current_item.prev_symbol
|
115
|
+
if prev_symbol.kind_of?(Syntax::Terminal)
|
116
|
+
build_scan_edge(vertices[-2], new_vertex)
|
117
|
+
else
|
118
|
+
# ...non-terminal
|
119
|
+
build_call_return_edges(vertices[-2], new_vertex)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
# Create an entry edge for the given vertex
|
126
|
+
def build_entry_edge(theVertex)
|
127
|
+
# Retrieve corresponding start vertex (for lhs non-terminal)
|
128
|
+
start_vertex = start_vertex_for[theVertex.dotted_item.production.lhs]
|
129
|
+
|
130
|
+
# Create an edge start_vertex -> the vertex
|
131
|
+
EpsilonEdge.new(start_vertex, theVertex)
|
132
|
+
end
|
133
|
+
|
134
|
+
# Create an exit edge for the given vertex
|
135
|
+
def build_exit_edge(theVertex)
|
136
|
+
# Retrieve corresponding end vertex (for lhs non-terminal)
|
137
|
+
end_vertex = end_vertex_for[theVertex.dotted_item.production.lhs]
|
138
|
+
|
139
|
+
# Create an edge the vertex -> end_vertex
|
140
|
+
EpsilonEdge.new(theVertex, end_vertex)
|
141
|
+
end
|
142
|
+
|
143
|
+
# Create a scan edge between two vertices.
|
144
|
+
# These two vertices are assumed to correspond to two
|
145
|
+
# consecutive dot positions separated by a terminal symbol
|
146
|
+
def build_scan_edge(fromVertex, toVertex)
|
147
|
+
ScanEdge.new(fromVertex, toVertex, fromVertex.dotted_item.next_symbol)
|
148
|
+
end
|
149
|
+
|
150
|
+
def build_call_return_edges(call_vertex, return_vertex)
|
151
|
+
nt_symbol = call_vertex.dotted_item.next_symbol
|
152
|
+
|
153
|
+
# Retrieve corresponding start vertex
|
154
|
+
start_vertex = start_vertex_for[nt_symbol]
|
155
|
+
# Create an edge call vertex -> start vertex
|
156
|
+
EpsilonEdge.new(call_vertex, start_vertex)
|
157
|
+
|
158
|
+
# Retrieve corresponding end vertex
|
159
|
+
end_vertex = end_vertex_for[nt_symbol]
|
160
|
+
# Create an edge end vertex -> return vertex
|
161
|
+
EpsilonEdge.new(end_vertex, return_vertex)
|
162
|
+
end
|
163
|
+
end # class
|
164
|
+
end # module
|
165
|
+
end # module
|
166
|
+
|
167
|
+
# End of file
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require_relative 'vertex'
|
2
|
+
|
3
|
+
module Rley # This module is used as a namespace
|
4
|
+
module GFG # This module is used as a namespace
|
5
|
+
# TODO: modify definition
|
6
|
+
# Represents a specialized vertex in a grammar flow graph
|
7
|
+
# that is associated to a given dotted item.
|
8
|
+
# Responsibilities (in addition to inherited ones):
|
9
|
+
# - Know its related non-terminal symbol
|
10
|
+
class ItemVertex < Vertex
|
11
|
+
attr_reader :dotted_item
|
12
|
+
|
13
|
+
def initialize(aDottedItem)
|
14
|
+
super()
|
15
|
+
@dotted_item = aDottedItem
|
16
|
+
end
|
17
|
+
|
18
|
+
def label()
|
19
|
+
return "#{dotted_item}"
|
20
|
+
end
|
21
|
+
|
22
|
+
end # class
|
23
|
+
end # module
|
24
|
+
end # module
|
25
|
+
|
26
|
+
# End of file
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require_relative 'vertex'
|
2
|
+
|
3
|
+
module Rley # This module is used as a namespace
|
4
|
+
module GFG # This module is used as a namespace
|
5
|
+
# Represents a specialized vertex in a grammar flow graph
|
6
|
+
# that is associated to a given non-terminal symbol.
|
7
|
+
# Responsibilities (in addition to inherited ones):
|
8
|
+
# - Know its related non-terminal symbol
|
9
|
+
class NonTerminalVertex < Vertex
|
10
|
+
attr_reader :non_terminal
|
11
|
+
|
12
|
+
def initialize(aNonTerminal)
|
13
|
+
super()
|
14
|
+
@non_terminal = aNonTerminal
|
15
|
+
end
|
16
|
+
|
17
|
+
end # class
|
18
|
+
end # module
|
19
|
+
end # module
|
20
|
+
|
21
|
+
# End of file
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require_relative 'edge'
|
2
|
+
|
3
|
+
module Rley # This module is used as a namespace
|
4
|
+
module GFG # This module is used as a namespace
|
5
|
+
# Abstract class. Represents an edge in a grammar flow graph
|
6
|
+
# Responsibilities:
|
7
|
+
# - To know the successor vertex
|
8
|
+
class ScanEdge < Edge
|
9
|
+
# The terminal symbol expected from the input stream
|
10
|
+
attr_reader :terminal
|
11
|
+
|
12
|
+
def initialize(thePredecessor, theSuccessor, aTerminal)
|
13
|
+
super(thePredecessor, theSuccessor)
|
14
|
+
@terminal = aTerminal
|
15
|
+
end
|
16
|
+
|
17
|
+
def to_s()
|
18
|
+
" -#{terminal}-> #{successor.label}"
|
19
|
+
end
|
20
|
+
|
21
|
+
end # class
|
22
|
+
end # module
|
23
|
+
end # module
|
24
|
+
|
25
|
+
# End of file
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require_relative 'non_terminal_vertex'
|
2
|
+
|
3
|
+
module Rley # This module is used as a namespace
|
4
|
+
module GFG # This module is used as a namespace
|
5
|
+
# TODO: change definition.
|
6
|
+
# Represents a specialized vertex in a grammar flow graph
|
7
|
+
# that is associated to a given non-terminal symbol.
|
8
|
+
# Responsibilities (in addition to inherited ones):
|
9
|
+
# - Know its related non-terminal symbol
|
10
|
+
class StartVertex < NonTerminalVertex
|
11
|
+
|
12
|
+
def initialize(aNonTerminal)
|
13
|
+
super(aNonTerminal)
|
14
|
+
end
|
15
|
+
|
16
|
+
def label()
|
17
|
+
return ".#{non_terminal}"
|
18
|
+
end
|
19
|
+
|
20
|
+
end # class
|
21
|
+
end # module
|
22
|
+
end # module
|
23
|
+
|
24
|
+
# End of file
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Rley # This module is used as a namespace
|
2
|
+
module GFG # This module is used as a namespace
|
3
|
+
# Abstract class. Represents a vertex in a grammar flow graph
|
4
|
+
# Responsibilities:
|
5
|
+
# - To know its outgoing edges
|
6
|
+
# - To know its label
|
7
|
+
class Vertex
|
8
|
+
# The edges linking the successor vertices to this one.
|
9
|
+
attr_reader :edges
|
10
|
+
|
11
|
+
def initialize()
|
12
|
+
@edges = []
|
13
|
+
end
|
14
|
+
|
15
|
+
# Add an graph edge to this vertex
|
16
|
+
def add_edge(anEdge)
|
17
|
+
edges << anEdge
|
18
|
+
end
|
19
|
+
|
20
|
+
end # class
|
21
|
+
end # module
|
22
|
+
end # module
|
23
|
+
|
24
|
+
# End of file
|
@@ -1,12 +1,14 @@
|
|
1
1
|
require_relative '../syntax/grammar'
|
2
|
+
require_relative 'grm_items_builder' # Use mix-in module
|
2
3
|
require_relative 'parse_tracer'
|
3
|
-
require_relative 'dotted_item'
|
4
4
|
require_relative 'parsing'
|
5
5
|
|
6
6
|
module Rley # This module is used as a namespace
|
7
7
|
module Parser # This module is used as a namespace
|
8
8
|
# Implementation of a parser that uses the Earley parsing algorithm.
|
9
9
|
class EarleyParser
|
10
|
+
include GrmItemsBuilder # Mix-in module for created dotted items of given grammar
|
11
|
+
|
10
12
|
# The grammar of the language.
|
11
13
|
attr_reader(:grammar)
|
12
14
|
|
@@ -23,7 +25,7 @@ module Rley # This module is used as a namespace
|
|
23
25
|
|
24
26
|
def initialize(aGrammar)
|
25
27
|
@grammar = aGrammar
|
26
|
-
@dotted_items = build_dotted_items(grammar)
|
28
|
+
@dotted_items = build_dotted_items(grammar) # Method from mixin
|
27
29
|
@start_mapping = build_start_mapping(dotted_items)
|
28
30
|
@next_mapping = build_next_mapping(dotted_items)
|
29
31
|
end
|
@@ -31,8 +33,6 @@ module Rley # This module is used as a namespace
|
|
31
33
|
# Parse a sequence of input tokens.
|
32
34
|
# @param aTokenSequence [Array] Array of Tokens objects returned by a
|
33
35
|
# tokenizer/scanner/lexer.
|
34
|
-
# @param aGrammar [Grammar] The grammar of the language
|
35
|
-
# (to use by the parser).
|
36
36
|
# @param aTraceLevel [Fixnum] The specified trace level.
|
37
37
|
# The possible values are:
|
38
38
|
# 0: No trace output (default case)
|
@@ -69,20 +69,6 @@ module Rley # This module is used as a namespace
|
|
69
69
|
|
70
70
|
private
|
71
71
|
|
72
|
-
def build_dotted_items(aGrammar)
|
73
|
-
items = []
|
74
|
-
aGrammar.rules.each do |prod|
|
75
|
-
rhs_size = prod.rhs.size
|
76
|
-
if rhs_size == 0
|
77
|
-
items << DottedItem.new(prod, 0)
|
78
|
-
else
|
79
|
-
items += (0..rhs_size).map { |i| DottedItem.new(prod, i) }
|
80
|
-
end
|
81
|
-
end
|
82
|
-
|
83
|
-
return items
|
84
|
-
end
|
85
|
-
|
86
72
|
# Create a Hash with pairs of the kind:
|
87
73
|
# non-terminal => [start dotted items]
|
88
74
|
def build_start_mapping(theDottedItems)
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require_relative 'dotted_item'
|
2
|
+
|
3
|
+
module Rley # This module is used as a namespace
|
4
|
+
module Parser # This module is used as a namespace
|
5
|
+
# Mix-in module. Builds the dotted items for a given grammar
|
6
|
+
module GrmItemsBuilder
|
7
|
+
# Build an array of dotted items from the productions in passed grammar.
|
8
|
+
def build_dotted_items(aGrammar)
|
9
|
+
items = []
|
10
|
+
aGrammar.rules.each do |prod|
|
11
|
+
rhs_size = prod.rhs.size
|
12
|
+
if rhs_size == 0
|
13
|
+
items << DottedItem.new(prod, 0)
|
14
|
+
else
|
15
|
+
items += (0..rhs_size).map { |i| DottedItem.new(prod, i) }
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
return items
|
20
|
+
end
|
21
|
+
end # module
|
22
|
+
end # module
|
23
|
+
end # module
|
data/lib/rley/parser/parsing.rb
CHANGED
@@ -11,7 +11,7 @@ module Rley # This module is used as a namespace
|
|
11
11
|
# The sequence of input token to parse
|
12
12
|
attr_reader(:tokens)
|
13
13
|
|
14
|
-
# @param
|
14
|
+
# @param aTracer [ParseTracer] An object that traces the parsing.
|
15
15
|
# The possible values are:
|
16
16
|
# 0: No trace output (default case)
|
17
17
|
# 1: Show trace of scanning and completion rules
|
@@ -1,7 +1,6 @@
|
|
1
1
|
require_relative '../../spec_helper'
|
2
2
|
require 'stringio'
|
3
3
|
|
4
|
-
# require_relative '../../../lib/rley/dynamic_grammar'
|
5
4
|
require_relative '../support/grammar_abc_helper'
|
6
5
|
require_relative '../../../lib/rley/parser/token'
|
7
6
|
require_relative '../../../lib/rley/parser/earley_parser'
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require_relative '../../spec_helper'
|
2
|
+
|
3
|
+
require_relative '../../../lib/rley/gfg/start_vertex'
|
4
|
+
require_relative '../../../lib/rley/gfg/end_vertex'
|
5
|
+
|
6
|
+
# Load the class under test
|
7
|
+
require_relative '../../../lib/rley/gfg/edge'
|
8
|
+
|
9
|
+
module Rley # Open this namespace to avoid module qualifier prefixes
|
10
|
+
module GFG # Open this namespace to avoid module qualifier prefixes
|
11
|
+
describe Edge do
|
12
|
+
let(:vertex1) { StartVertex.new('from') }
|
13
|
+
let(:vertex2) { StartVertex.new('to') }
|
14
|
+
subject { Edge.new(vertex1, vertex2) }
|
15
|
+
|
16
|
+
context 'Initialization:' do
|
17
|
+
it 'should be created with two vertice arguments' do
|
18
|
+
expect { Edge.new(vertex1, vertex2) }.not_to raise_error
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'should be registered by the predecessor vertex' do
|
22
|
+
expect(subject).to eq(vertex1.edges.last)
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'should know the successor vertex' do
|
26
|
+
expect(subject.successor).to eq(vertex2)
|
27
|
+
end
|
28
|
+
end # context
|
29
|
+
end # describe
|
30
|
+
end # module
|
31
|
+
end # module
|
32
|
+
|
33
|
+
# End of file
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require_relative '../../spec_helper'
|
2
|
+
|
3
|
+
# Load the class under test
|
4
|
+
require_relative '../../../lib/rley/gfg/end_vertex'
|
5
|
+
|
6
|
+
module Rley # Open this namespace to avoid module qualifier prefixes
|
7
|
+
module GFG # Open this namespace to avoid module qualifier prefixes
|
8
|
+
describe EndVertex do
|
9
|
+
let(:sample_nt) { double('NT') }
|
10
|
+
subject { EndVertex.new(sample_nt) }
|
11
|
+
|
12
|
+
context 'Initialization:' do
|
13
|
+
it 'should be created with a non-terminal symbol' do
|
14
|
+
expect { EndVertex.new(sample_nt) }.not_to raise_error
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'should know its label' do
|
18
|
+
allow(sample_nt).to receive(:to_s).and_return('NT')
|
19
|
+
expect(subject.label).to eq('NT.')
|
20
|
+
end
|
21
|
+
end # context
|
22
|
+
end # describe
|
23
|
+
end # module
|
24
|
+
end # module
|
25
|
+
|
26
|
+
# End of file
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require_relative '../../spec_helper'
|
2
|
+
|
3
|
+
require_relative '../../../lib/rley/gfg/start_vertex'
|
4
|
+
require_relative '../../../lib/rley/gfg/end_vertex'
|
5
|
+
|
6
|
+
# Load the class under test
|
7
|
+
require_relative '../../../lib/rley/gfg/epsilon_edge'
|
8
|
+
|
9
|
+
module Rley # Open this namespace to avoid module qualifier prefixes
|
10
|
+
module GFG # Open this namespace to avoid module qualifier prefixes
|
11
|
+
describe Edge do
|
12
|
+
let(:vertex1) { StartVertex.new('from') }
|
13
|
+
let(:vertex2) { StartVertex.new('to') }
|
14
|
+
subject { EpsilonEdge.new(vertex1, vertex2) }
|
15
|
+
|
16
|
+
context 'Initialization:' do
|
17
|
+
it 'should be created with two vertice arguments' do
|
18
|
+
expect { EpsilonEdge.new(vertex1, vertex2) }.not_to raise_error
|
19
|
+
end
|
20
|
+
end # context
|
21
|
+
end # describe
|
22
|
+
end # module
|
23
|
+
end # module
|
24
|
+
|
25
|
+
# End of file
|
@@ -0,0 +1,139 @@
|
|
1
|
+
require_relative '../../spec_helper'
|
2
|
+
require 'pp'
|
3
|
+
|
4
|
+
require_relative '../support/grammar_abc_helper'
|
5
|
+
require_relative '../../../lib/rley/parser/grm_items_builder'
|
6
|
+
|
7
|
+
# Load the module under test
|
8
|
+
require_relative '../../../lib/rley/gfg/grm_flow_graph'
|
9
|
+
|
10
|
+
module Rley # Open this namespace to avoid module qualifier prefixes
|
11
|
+
module GFG # Open this namespace to avoid module qualifier prefixes
|
12
|
+
describe GrmFlowGraph do
|
13
|
+
include GrammarABCHelper # Mix-in module with builder for grammar abc
|
14
|
+
|
15
|
+
# Helper method. Create an array of dotted items
|
16
|
+
# from the given grammar
|
17
|
+
def build_items_for_grammar(aGrammar)
|
18
|
+
helper = Object.new
|
19
|
+
helper.extend(Parser::GrmItemsBuilder)
|
20
|
+
return helper.build_dotted_items(aGrammar)
|
21
|
+
end
|
22
|
+
|
23
|
+
# Check that the passed graph has exactly the same
|
24
|
+
# vertices and edges as expected
|
25
|
+
def compare_graph_expectations(aGraph, expectations)
|
26
|
+
i = 0
|
27
|
+
aGraph.vertices.each do |vertex|
|
28
|
+
vertex.edges.each do |edge|
|
29
|
+
representation = vertex.label + edge.to_s
|
30
|
+
expect(representation).to eq(expectations[i])
|
31
|
+
i += 1
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# Factory method. Build a production with the given sequence
|
37
|
+
# of symbols as its rhs.
|
38
|
+
let(:grammar_abc) do
|
39
|
+
builder = grammar_abc_builder
|
40
|
+
builder.grammar
|
41
|
+
end
|
42
|
+
|
43
|
+
# Helper method. Create an array of dotted items
|
44
|
+
# from the abc grammar
|
45
|
+
let(:items_from_grammar) { build_items_for_grammar(grammar_abc) }
|
46
|
+
|
47
|
+
# Default instantiation rule
|
48
|
+
subject { GrmFlowGraph.new(items_from_grammar) }
|
49
|
+
|
50
|
+
|
51
|
+
context 'Initialization:' do
|
52
|
+
it 'should be created with an array of dotted items' do
|
53
|
+
expect { GrmFlowGraph.new(items_from_grammar) }.not_to raise_error
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'should have the correct number of vertices' do
|
57
|
+
# Number of vertices = count of dotted items +...
|
58
|
+
# ... 2 * count of non-terminals
|
59
|
+
count_vertices = 2 * grammar_abc.non_terminals.size
|
60
|
+
count_vertices += items_from_grammar.size
|
61
|
+
expect(subject.vertices.size).to eq(count_vertices)
|
62
|
+
end
|
63
|
+
|
64
|
+
it 'should have for each non-terminal one start and end vertex' do
|
65
|
+
# Iterate over all non-terminals of grammar...
|
66
|
+
grammar_abc.non_terminals.each do |nterm|
|
67
|
+
# ...to each non-terminal there should be a start vertex
|
68
|
+
start_vertex = subject.start_vertex_for[nterm]
|
69
|
+
expect(start_vertex).to be_kind_of(StartVertex)
|
70
|
+
expect(start_vertex.label).to eq(".#{nterm}")
|
71
|
+
|
72
|
+
# ...to each non-terminal there should be an end vertex
|
73
|
+
end_vertex = subject.end_vertex_for[nterm]
|
74
|
+
expect(end_vertex).to be_kind_of(EndVertex)
|
75
|
+
expect(end_vertex.label).to eq("#{nterm}.")
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
it 'should have one or more entry edges per start vertex' do
|
80
|
+
subject.start_vertex_for.values.each do |a_start|
|
81
|
+
expect(a_start.edges.size >= 1).to be_truthy
|
82
|
+
a_start.edges.each do |edge|
|
83
|
+
expect(edge.successor.dotted_item.at_start?).to be_truthy
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
it 'should have the correct graph structure' do
|
89
|
+
# We use the abc grammar
|
90
|
+
expected = [
|
91
|
+
'.S --> S => . A',
|
92
|
+
'.A --> A => . a A c',
|
93
|
+
'.A --> A => . b',
|
94
|
+
'A. --> S => A .',
|
95
|
+
'A. --> A => a A . c',
|
96
|
+
'S => . A --> .A',
|
97
|
+
'S => A . --> S.',
|
98
|
+
'A => . a A c -a-> A => a . A c',
|
99
|
+
'A => a . A c --> .A',
|
100
|
+
'A => a A . c -c-> A => a A c .',
|
101
|
+
'A => a A c . --> A.',
|
102
|
+
'A => . b -b-> A => b .',
|
103
|
+
'A => b . --> A.'
|
104
|
+
]
|
105
|
+
|
106
|
+
compare_graph_expectations(subject, expected)
|
107
|
+
end
|
108
|
+
|
109
|
+
it 'should handle empty productions' do
|
110
|
+
builder = Rley::Syntax::GrammarBuilder.new
|
111
|
+
builder.add_terminals('a')
|
112
|
+
builder.add_production('S' => 'A')
|
113
|
+
builder.add_production('A' => 'a')
|
114
|
+
builder.add_production('A' => []) # empty rhs
|
115
|
+
|
116
|
+
grammar = builder.grammar
|
117
|
+
items = build_items_for_grammar(grammar)
|
118
|
+
|
119
|
+
graph = GrmFlowGraph.new(items)
|
120
|
+
expected = [
|
121
|
+
'.S --> S => . A',
|
122
|
+
'.A --> A => . a',
|
123
|
+
'.A --> A => .',
|
124
|
+
'A. --> S => A .',
|
125
|
+
'S => . A --> .A',
|
126
|
+
'S => A . --> S.',
|
127
|
+
'A => . a -a-> A => a .',
|
128
|
+
'A => a . --> A.',
|
129
|
+
'A => . --> A.'
|
130
|
+
]
|
131
|
+
|
132
|
+
compare_graph_expectations(graph, expected)
|
133
|
+
end
|
134
|
+
end # context
|
135
|
+
end # describe
|
136
|
+
end # module
|
137
|
+
end # module
|
138
|
+
|
139
|
+
# End of file
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require_relative '../../spec_helper'
|
2
|
+
require_relative '../../../lib/rley/syntax/terminal'
|
3
|
+
require_relative '../../../lib/rley/syntax/non_terminal'
|
4
|
+
require_relative '../../../lib/rley/syntax/production'
|
5
|
+
require_relative '../../../lib/rley/parser/dotted_item'
|
6
|
+
|
7
|
+
# Load the class under test
|
8
|
+
require_relative '../../../lib/rley/gfg/item_vertex'
|
9
|
+
|
10
|
+
module Rley # Open this namespace to avoid module qualifier prefixes
|
11
|
+
module GFG # Open this namespace to avoid module qualifier prefixes
|
12
|
+
describe ItemVertex do
|
13
|
+
# Factory method. Builds a production with given left-hand side (LHS)
|
14
|
+
# and given RHS (right-hand side)
|
15
|
+
def build_prod(theLHS, *theRHSSymbols)
|
16
|
+
return Syntax::Production.new(theLHS, theRHSSymbols)
|
17
|
+
end
|
18
|
+
|
19
|
+
let(:t_a) { Rley::Syntax::Terminal.new('A') }
|
20
|
+
let(:t_b) { Rley::Syntax::Terminal.new('B') }
|
21
|
+
let(:t_c) { Rley::Syntax::Terminal.new('C') }
|
22
|
+
let(:nt_sentence) { Rley::Syntax::NonTerminal.new('sentence') }
|
23
|
+
let(:sample_prod) { build_prod(nt_sentence, t_a, t_b, t_c) }
|
24
|
+
let(:other_prod) { build_prod(nt_sentence, t_a) }
|
25
|
+
let(:empty_prod) { build_prod(nt_sentence) }
|
26
|
+
let(:sample_item) { Parser::DottedItem.new(sample_prod, 1) }
|
27
|
+
subject { ItemVertex.new(sample_item) }
|
28
|
+
|
29
|
+
context 'Initialization:' do
|
30
|
+
it 'should be created with a dotted item' do
|
31
|
+
expect { ItemVertex.new(sample_item) }.not_to raise_error
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'should know its dotted item' do
|
35
|
+
expect(subject.dotted_item).to eq(sample_item)
|
36
|
+
end
|
37
|
+
end # context
|
38
|
+
|
39
|
+
context 'Provided services:' do
|
40
|
+
it 'should know its label' do
|
41
|
+
expect(subject.label).to eq(sample_item.to_s)
|
42
|
+
end
|
43
|
+
|
44
|
+
|
45
|
+
end # context
|
46
|
+
end # describe
|
47
|
+
end # module
|
48
|
+
end # module
|
49
|
+
|
50
|
+
# End of file
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require_relative '../../spec_helper'
|
2
|
+
|
3
|
+
# Load the class under test
|
4
|
+
require_relative '../../../lib/rley/gfg/non_terminal_vertex'
|
5
|
+
|
6
|
+
module Rley # Open this namespace to avoid module qualifier prefixes
|
7
|
+
module GFG # Open this namespace to avoid module qualifier prefixes
|
8
|
+
describe NonTerminalVertex do
|
9
|
+
let(:sample_nt) { double('fake-non-terminal') }
|
10
|
+
subject { NonTerminalVertex.new(sample_nt) }
|
11
|
+
|
12
|
+
context 'Initialization:' do
|
13
|
+
it 'should be created with a non-terminal symbol' do
|
14
|
+
expect { NonTerminalVertex.new(sample_nt) }.not_to raise_error
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'should know its non-terminal' do
|
18
|
+
expect(subject.non_terminal).to eq(sample_nt)
|
19
|
+
end
|
20
|
+
end # context
|
21
|
+
end # describe
|
22
|
+
end # module
|
23
|
+
end # module
|
24
|
+
|
25
|
+
# End of file
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require_relative '../../spec_helper'
|
2
|
+
|
3
|
+
require_relative '../../../lib/rley/gfg/start_vertex'
|
4
|
+
require_relative '../../../lib/rley/gfg/end_vertex'
|
5
|
+
|
6
|
+
# Load the class under test
|
7
|
+
require_relative '../../../lib/rley/gfg/scan_edge'
|
8
|
+
|
9
|
+
module Rley # Open this namespace to avoid module qualifier prefixes
|
10
|
+
module GFG # Open this namespace to avoid module qualifier prefixes
|
11
|
+
describe Edge do
|
12
|
+
let(:vertex1) { StartVertex.new('from') }
|
13
|
+
let(:vertex2) { StartVertex.new('to') }
|
14
|
+
let(:sample_terminal) { double('fake-terminal') }
|
15
|
+
subject { ScanEdge.new(vertex1, vertex2, sample_terminal) }
|
16
|
+
|
17
|
+
context 'Initialization:' do
|
18
|
+
it 'should be created with two vertice arguments & a terminal' do
|
19
|
+
expect { ScanEdge.new(vertex1, vertex2, sample_terminal) }.not_to raise_error
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'should know the related terminal' do
|
23
|
+
expect(subject.terminal).to eq(sample_terminal)
|
24
|
+
end
|
25
|
+
end # context
|
26
|
+
end # describe
|
27
|
+
end # module
|
28
|
+
end # module
|
29
|
+
|
30
|
+
# End of file
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require_relative '../../spec_helper'
|
2
|
+
|
3
|
+
# Load the class under test
|
4
|
+
require_relative '../../../lib/rley/gfg/start_vertex'
|
5
|
+
|
6
|
+
module Rley # Open this namespace to avoid module qualifier prefixes
|
7
|
+
module GFG # Open this namespace to avoid module qualifier prefixes
|
8
|
+
describe StartVertex do
|
9
|
+
let(:sample_nt) { double('NT') }
|
10
|
+
subject { StartVertex.new(sample_nt) }
|
11
|
+
|
12
|
+
context 'Initialization:' do
|
13
|
+
it 'should be created with a non-terminal symbol' do
|
14
|
+
expect { StartVertex.new(sample_nt) }.not_to raise_error
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'should know its label' do
|
18
|
+
allow(sample_nt).to receive(:to_s).and_return('NT')
|
19
|
+
expect(subject.label).to eq('.NT')
|
20
|
+
end
|
21
|
+
end # context
|
22
|
+
end # describe
|
23
|
+
end # module
|
24
|
+
end # module
|
25
|
+
|
26
|
+
# End of file
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require_relative '../../spec_helper'
|
2
|
+
|
3
|
+
# Load the class under test
|
4
|
+
require_relative '../../../lib/rley/gfg/vertex'
|
5
|
+
|
6
|
+
module Rley # Open this namespace to avoid module qualifier prefixes
|
7
|
+
module GFG # Open this namespace to avoid module qualifier prefixes
|
8
|
+
describe Vertex do
|
9
|
+
subject { Vertex.new() }
|
10
|
+
|
11
|
+
context 'Initialization:' do
|
12
|
+
it 'should be created without argument' do
|
13
|
+
expect { Vertex.new() }.not_to raise_error
|
14
|
+
end
|
15
|
+
|
16
|
+
it "shouldn't have edges at start" do
|
17
|
+
expect(subject.edges.empty?).to eq(true)
|
18
|
+
end
|
19
|
+
end # context
|
20
|
+
|
21
|
+
context 'Provided services:' do
|
22
|
+
it 'should accept new edges' do
|
23
|
+
edge1 = double('fake-edge1')
|
24
|
+
edge2 = double('fake-edge2')
|
25
|
+
|
26
|
+
expect { subject.add_edge(edge1) }.not_to raise_error
|
27
|
+
expect(subject.edges.size).to eq(1)
|
28
|
+
expect(subject.edges.last).to eq(edge1)
|
29
|
+
expect { subject.add_edge(edge2) }.not_to raise_error
|
30
|
+
expect(subject.edges.size).to eq(2)
|
31
|
+
expect(subject.edges.last).to eq(edge2)
|
32
|
+
end
|
33
|
+
end # context
|
34
|
+
end # describe
|
35
|
+
end # module
|
36
|
+
end # module
|
37
|
+
|
38
|
+
# End of file
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require_relative '../../spec_helper'
|
2
|
+
|
3
|
+
require_relative '../support/grammar_abc_helper'
|
4
|
+
|
5
|
+
# Load the module under test
|
6
|
+
require_relative '../../../lib/rley/parser/grm_items_builder'
|
7
|
+
|
8
|
+
module Rley # Open this namespace to avoid module qualifier prefixes
|
9
|
+
module Parser # Open this namespace to avoid module qualifier prefixes
|
10
|
+
describe 'Testing GrmItemsBuilder' do
|
11
|
+
include GrmItemsBuilder # Use mix-in to test
|
12
|
+
include GrammarABCHelper # Mix-in module with builder for grammar abc
|
13
|
+
|
14
|
+
# Factory method. Build a production with the given sequence
|
15
|
+
# of symbols as its rhs.
|
16
|
+
let(:grammar_abc) do
|
17
|
+
builder = grammar_abc_builder
|
18
|
+
builder.grammar
|
19
|
+
end
|
20
|
+
|
21
|
+
context 'Builder pattern behaviour' do
|
22
|
+
it 'should create dotted items for a grammar' do
|
23
|
+
# Next line calls method from mixin module under test
|
24
|
+
items = build_dotted_items(grammar_abc)
|
25
|
+
expect(items.size).to eq(8)
|
26
|
+
expectations = [
|
27
|
+
'S => . A',
|
28
|
+
'S => A .',
|
29
|
+
'A => . a A c',
|
30
|
+
'A => a . A c',
|
31
|
+
'A => a A . c',
|
32
|
+
'A => a A c .',
|
33
|
+
'A => . b',
|
34
|
+
'A => b .',
|
35
|
+
]
|
36
|
+
expect(items.map(&:to_s)).to eq(expectations)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end # describe
|
40
|
+
end # module
|
41
|
+
end # module
|
42
|
+
|
43
|
+
# End of file
|
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.2.
|
4
|
+
version: 0.2.15
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dimitri Geshef
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2016-02-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|
@@ -139,9 +139,11 @@ files:
|
|
139
139
|
- examples/parsers/parsing_L1.rb
|
140
140
|
- examples/parsers/parsing_abc.rb
|
141
141
|
- examples/parsers/parsing_ambig.rb
|
142
|
+
- examples/parsers/parsing_another.rb
|
142
143
|
- examples/parsers/parsing_b_expr.rb
|
143
144
|
- examples/parsers/parsing_err_expr.rb
|
144
145
|
- examples/parsers/parsing_groucho.rb
|
146
|
+
- examples/parsers/parsing_right_recursive.rb
|
145
147
|
- examples/parsers/parsing_tricky.rb
|
146
148
|
- examples/parsers/tracing_parser.rb
|
147
149
|
- examples/recognizers/recognizer_abc.rb
|
@@ -150,10 +152,20 @@ files:
|
|
150
152
|
- lib/rley/formatter/base_formatter.rb
|
151
153
|
- lib/rley/formatter/debug.rb
|
152
154
|
- lib/rley/formatter/json.rb
|
155
|
+
- lib/rley/gfg/edge.rb
|
156
|
+
- lib/rley/gfg/end_vertex.rb
|
157
|
+
- lib/rley/gfg/epsilon_edge.rb
|
158
|
+
- lib/rley/gfg/grm_flow_graph.rb
|
159
|
+
- lib/rley/gfg/item_vertex.rb
|
160
|
+
- lib/rley/gfg/non_terminal_vertex.rb
|
161
|
+
- lib/rley/gfg/scan_edge.rb
|
162
|
+
- lib/rley/gfg/start_vertex.rb
|
163
|
+
- lib/rley/gfg/vertex.rb
|
153
164
|
- lib/rley/parse_tree_visitor.rb
|
154
165
|
- lib/rley/parser/chart.rb
|
155
166
|
- lib/rley/parser/dotted_item.rb
|
156
167
|
- lib/rley/parser/earley_parser.rb
|
168
|
+
- lib/rley/parser/grm_items_builder.rb
|
157
169
|
- lib/rley/parser/parse_state.rb
|
158
170
|
- lib/rley/parser/parse_state_tracker.rb
|
159
171
|
- lib/rley/parser/parse_tracer.rb
|
@@ -177,10 +189,20 @@ files:
|
|
177
189
|
- lib/rley/syntax/verbatim_symbol.rb
|
178
190
|
- spec/rley/formatter/debug_spec.rb
|
179
191
|
- spec/rley/formatter/json_spec.rb
|
192
|
+
- spec/rley/gfg/edge_spec.rb
|
193
|
+
- spec/rley/gfg/end_vertex_spec.rb
|
194
|
+
- spec/rley/gfg/epsilon_edge_spec.rb
|
195
|
+
- spec/rley/gfg/grm_flow_graph_spec.rb
|
196
|
+
- spec/rley/gfg/item_vertex_spec.rb
|
197
|
+
- spec/rley/gfg/non_terminal_vertex_spec.rb
|
198
|
+
- spec/rley/gfg/scan_edge_spec.rb
|
199
|
+
- spec/rley/gfg/start_vertex_spec.rb
|
200
|
+
- spec/rley/gfg/vertex_spec.rb
|
180
201
|
- spec/rley/parse_tree_visitor_spec.rb
|
181
202
|
- spec/rley/parser/chart_spec.rb
|
182
203
|
- spec/rley/parser/dotted_item_spec.rb
|
183
204
|
- spec/rley/parser/earley_parser_spec.rb
|
205
|
+
- spec/rley/parser/grm_items_builder_spec.rb
|
184
206
|
- spec/rley/parser/parse_state_spec.rb
|
185
207
|
- spec/rley/parser/parse_tracer_spec.rb
|
186
208
|
- spec/rley/parser/parse_tree_builder_spec.rb
|
@@ -236,9 +258,19 @@ summary: Ruby implementation of the Earley's parsing algorithm
|
|
236
258
|
test_files:
|
237
259
|
- spec/rley/formatter/debug_spec.rb
|
238
260
|
- spec/rley/formatter/json_spec.rb
|
261
|
+
- spec/rley/gfg/edge_spec.rb
|
262
|
+
- spec/rley/gfg/end_vertex_spec.rb
|
263
|
+
- spec/rley/gfg/epsilon_edge_spec.rb
|
264
|
+
- spec/rley/gfg/grm_flow_graph_spec.rb
|
265
|
+
- spec/rley/gfg/item_vertex_spec.rb
|
266
|
+
- spec/rley/gfg/non_terminal_vertex_spec.rb
|
267
|
+
- spec/rley/gfg/scan_edge_spec.rb
|
268
|
+
- spec/rley/gfg/start_vertex_spec.rb
|
269
|
+
- spec/rley/gfg/vertex_spec.rb
|
239
270
|
- spec/rley/parser/chart_spec.rb
|
240
271
|
- spec/rley/parser/dotted_item_spec.rb
|
241
272
|
- spec/rley/parser/earley_parser_spec.rb
|
273
|
+
- spec/rley/parser/grm_items_builder_spec.rb
|
242
274
|
- spec/rley/parser/parse_state_spec.rb
|
243
275
|
- spec/rley/parser/parse_tracer_spec.rb
|
244
276
|
- spec/rley/parser/parse_tree_builder_spec.rb
|