rley 0.2.14 → 0.2.15
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
[![Coverage Status](https://img.shields.io/coveralls/famished-tiger/Rley.svg)](https://coveralls.io/r/famished-tiger/Rley?branch=master)
|
8
8
|
[![Gem Version](https://badge.fury.io/rb/rley.svg)](http://badge.fury.io/rb/rley)
|
9
9
|
[![Dependency Status](https://gemnasium.com/famished-tiger/Rley.svg)](https://gemnasium.com/famished-tiger/Rley)
|
10
|
+
[![Inline docs](http://inch-ci.org/github/famished-tiger/Rley.svg?branch=master)](http://inch-ci.org/github/famished-tiger/Rley)
|
10
11
|
[![License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat)](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
|