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