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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 3487e06363e33e6a4e0c0e5c05a74379913589ab
4
- data.tar.gz: f0907aa318f914f1ae63082b4e7158f929b2e746
3
+ metadata.gz: 9a8824f31c8801d99d12566267a5e8cd95bb26c1
4
+ data.tar.gz: d5a0887f857d5b0efe154f10a202932960bc2e9e
5
5
  SHA512:
6
- metadata.gz: bdda4a8211470a1290befe63adfd65c278a3376a706ca367542f7ed1a7bbacc072a0167f08f539d94272ca3145e8b4821ac1f9912dc0d2d8824e01768689ea10
7
- data.tar.gz: a23fe29e4573e412275e5ae8fcc5da35763954e130085628353cfce78651f30b0bc5a08ce0130c6edfd317ed952d57029fbfec1842c6fd46f895475061289750
6
+ metadata.gz: cbf13dc6ac58dced26074ed1b2f9a4aab99b2834ea720743621d0cec7f0e0b17b7dc89e19956765b4921f4e1f4601b1858da92bcf0260b86c41de5c1c040579d
7
+ data.tar.gz: fed291494d3178e6813f561ef301ac60fc88604c81c0f8b40ee8fa8639e20873cef29fc79d90b47993e228134b1dcf0a8502ad1c3336b7e91659cb3a0c006345
@@ -1,3 +1,6 @@
1
+ ### 0.2.15 / 2016-02-14
2
+ * [CHANGE] A lot of internal changes. This is the last version before a Grammar Flow Graph-based parsing implementation.
3
+
1
4
  ### 0.2.14 / 2015-11-25
2
5
  * [FIX] Method `StateSet#ambiguities` overlooked some ambiguities in parse sets.
3
6
 
@@ -1,4 +1,4 @@
1
- Copyright (c) 2014-2015 Dimitri Geshef
1
+ Copyright (c) 2014-2016 Dimitri Geshef
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining a copy
4
4
  of this software and associated documentation files (the "Software"), to deal
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-2015, Dimitri Geshef.
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
@@ -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.2.14'
6
+ Version = '0.2.15'
7
7
 
8
8
  # Brief description of the gem.
9
9
  Description = "Ruby implementation of the Earley's parsing algorithm"
@@ -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
@@ -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 aTraceLevel [Fixnum] The specified trace level.
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.14
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: 2015-11-26 00:00:00.000000000 Z
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