rley 0.4.01 → 0.4.02
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +4 -0
- data/LICENSE.txt +2 -2
- data/README.md +3 -3
- data/examples/NLP/mini_en_demo.rb +1 -1
- data/examples/data_formats/JSON/JSON_demo.rb +1 -0
- data/examples/data_formats/JSON/JSON_lexer.rb +4 -4
- data/examples/general/calc/calc_lexer.rb +2 -2
- data/lib/rley.rb +1 -1
- data/lib/rley/constants.rb +1 -1
- data/lib/rley/formatter/debug.rb +2 -2
- data/lib/rley/formatter/json.rb +4 -4
- data/lib/rley/parse_tree_visitor.rb +9 -9
- data/lib/rley/parser/base_parser.rb +1 -1
- data/lib/rley/parser/gfg_parsing.rb +9 -0
- data/lib/rley/parser/parse_tree_builder.rb +176 -126
- data/lib/rley/parser/parse_tree_factory.rb +57 -0
- data/lib/rley/ptree/non_terminal_node.rb +10 -9
- data/lib/rley/ptree/parse_tree_node.rb +10 -5
- data/lib/rley/ptree/terminal_node.rb +14 -6
- data/lib/rley/sppf/sppf_node.rb +2 -2
- data/lib/rley/{parser → tokens}/token.rb +1 -4
- data/lib/rley/{ptree → tokens}/token_range.rb +1 -1
- data/spec/rley/formatter/debug_spec.rb +16 -16
- data/spec/rley/formatter/json_spec.rb +8 -8
- data/spec/rley/parse_forest_visitor_spec.rb +1 -1
- data/spec/rley/parse_tree_visitor_spec.rb +28 -28
- data/spec/rley/parser/error_reason_spec.rb +3 -3
- data/spec/rley/parser/gfg_chart_spec.rb +2 -2
- data/spec/rley/parser/gfg_earley_parser_spec.rb +2 -2
- data/spec/rley/parser/gfg_parsing_spec.rb +2 -2
- data/spec/rley/parser/groucho_spec.rb +1 -1
- data/spec/rley/parser/parse_tracer_spec.rb +2 -2
- data/spec/rley/parser/parse_tree_builder_spec.rb +213 -140
- data/spec/rley/parser/parse_tree_factory_spec.rb +85 -0
- data/spec/rley/parser/parse_walker_factory_spec.rb +11 -10
- data/spec/rley/ptree/non_terminal_node_spec.rb +23 -20
- data/spec/rley/ptree/terminal_node_spec.rb +7 -12
- data/spec/rley/sppf/alternative_node_spec.rb +2 -2
- data/spec/rley/sppf/non_terminal_node_spec.rb +2 -2
- data/spec/rley/support/ambiguous_grammar_helper.rb +2 -2
- data/spec/rley/support/expectation_helper.rb +1 -1
- data/spec/rley/support/grammar_ambig01_helper.rb +2 -2
- data/spec/rley/support/grammar_b_expr_helper.rb +2 -2
- data/spec/rley/support/grammar_helper.rb +3 -3
- data/spec/rley/support/grammar_l0_helper.rb +2 -2
- data/spec/rley/support/grammar_pb_helper.rb +2 -2
- data/spec/rley/{ptree → tokens}/token_range_spec.rb +2 -2
- data/spec/rley/{parser → tokens}/token_spec.rb +2 -2
- metadata +11 -17
- data/lib/rley/parser/chart.rb +0 -82
- data/lib/rley/parser/earley_parser.rb +0 -203
- data/lib/rley/parser/parsing.rb +0 -265
- data/spec/rley/parser/chart_spec.rb +0 -120
- data/spec/rley/parser/earley_parser_spec.rb +0 -710
- data/spec/rley/parser/parsing_spec.rb +0 -408
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cd7c7f9e941ac7139080960dca0cb2a16665bcbd
|
4
|
+
data.tar.gz: dd4e2a8dae9fd1958427beab6c63b0e9206834e7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7611109ca8b7d99a090e88de7eea31cc08c22250d790c2befd3b3aaa9e222450ac900d10ab733d0ee72e831da0bf7a7ad07da6f6e33e20724879659a2e45d620
|
7
|
+
data.tar.gz: f81cc80909844e4cbd50ec85ad803b08483e714f78734b234de13b544f891f77759cdb8df4349dea1c68a8de250d44ef860e0a8bf27dd6313da08f0cc3c49b34
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,7 @@
|
|
1
|
+
### 0.4.02 / 2017-04-09
|
2
|
+
* [NEW] Module re-organization for clearer dependencies: Classes `Token` and `TokenRange` are moved to a separate module `Tokens`.
|
3
|
+
* [CHANGE] Code, specs, examples and `README.md` adapted to reflect the module re-organization.
|
4
|
+
|
1
5
|
### 0.4.01 / 2016-12-21
|
2
6
|
* [NEW] File `appveyor.yml`. Add AppVeyor CI to Github commits. AppVeyor complements Travis by running builds under Windows OS.
|
3
7
|
This permits to test the portability across operating systems.
|
data/LICENSE.txt
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
Copyright (c) 2014-
|
1
|
+
Copyright (c) 2014-2017 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
|
@@ -16,4 +16,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
16
16
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
17
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
18
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
-
THE SOFTWARE.
|
19
|
+
THE SOFTWARE.
|
data/README.md
CHANGED
@@ -75,7 +75,7 @@ Installing the latest stable version is simple:
|
|
75
75
|
## A whirlwind tour of Rley
|
76
76
|
The purpose of this section is show how to create a parser for a minimalistic
|
77
77
|
English language subset.
|
78
|
-
The tour is organized
|
78
|
+
The tour is organized as follows:
|
79
79
|
1. [Defining the language grammar](#defining-the-language-grammar)
|
80
80
|
2. [Creating a lexicon](#creating-a-lexicon)
|
81
81
|
3. [Creating a tokenizer](#creating-a-tokenizer)
|
@@ -152,7 +152,7 @@ The subset of English grammar is based on an example from the NLTK book.
|
|
152
152
|
raise StandardError, "Word '#{word}' not found in lexicon"
|
153
153
|
end
|
154
154
|
terminal = aGrammar.name2symbol[term_name]
|
155
|
-
Rley::
|
155
|
+
Rley::Tokens::Token.new(word, terminal)
|
156
156
|
end
|
157
157
|
|
158
158
|
return tokens
|
@@ -283,5 +283,5 @@ Here are a few other ones:
|
|
283
283
|
|
284
284
|
Copyright
|
285
285
|
---------
|
286
|
-
Copyright (c) 2014-
|
286
|
+
Copyright (c) 2014-2017, Dimitri Geshef.
|
287
287
|
__Rley__ is released under the MIT License see [LICENSE.txt](https://github.com/famished-tiger/Rley/blob/master/LICENSE.txt) for details.
|
@@ -64,7 +64,7 @@ def tokenizer(aTextToParse, aGrammar)
|
|
64
64
|
raise StandardError, "Word '#{word}' not found in lexicon"
|
65
65
|
end
|
66
66
|
terminal = aGrammar.name2symbol[term_name]
|
67
|
-
Rley::
|
67
|
+
Rley::Tokens::Token.new(word, terminal)
|
68
68
|
end
|
69
69
|
|
70
70
|
return tokens
|
@@ -51,7 +51,7 @@ private
|
|
51
51
|
when '{', '}', '[', ']', ',', ':'
|
52
52
|
type_name = @@lexeme2name[curr_ch]
|
53
53
|
token_type = name2symbol[type_name]
|
54
|
-
token = Rley::
|
54
|
+
token = Rley::Tokens::Token.new(curr_ch, token_type)
|
55
55
|
|
56
56
|
# LITERALS
|
57
57
|
when '"' # Start string delimiter found
|
@@ -59,7 +59,7 @@ private
|
|
59
59
|
end_delimiter = scanner.getch()
|
60
60
|
raise ScanError.new('No closing quotes (") found') if end_delimiter.nil?
|
61
61
|
token_type = name2symbol['JSON_STRING']
|
62
|
-
token = Rley::
|
62
|
+
token = Rley::Tokens::Token.new(value, token_type)
|
63
63
|
|
64
64
|
when /[ftn]/ # First letter of keywords
|
65
65
|
@scanner.pos = scanner.pos - 1 # Simulate putback
|
@@ -69,7 +69,7 @@ private
|
|
69
69
|
raise ScanError.new("Invalid keyword: #{invalid_keyw}")
|
70
70
|
else
|
71
71
|
token_type = name2symbol['KEYWORD']
|
72
|
-
token = Rley::
|
72
|
+
token = Rley::Tokens::Token.new(keyw, token_type)
|
73
73
|
end
|
74
74
|
|
75
75
|
|
@@ -77,7 +77,7 @@ private
|
|
77
77
|
@scanner.pos = scanner.pos - 1 # Simulate putback
|
78
78
|
value = scanner.scan(/-?[0-9]+(\.[0-9]+)?([eE][-+]?[0-9])?/)
|
79
79
|
token_type = name2symbol['JSON_NUMBER']
|
80
|
-
token = Rley::
|
80
|
+
token = Rley::Tokens::Token.new(value, token_type)
|
81
81
|
|
82
82
|
|
83
83
|
else # Unknown token
|
@@ -51,14 +51,14 @@ private
|
|
51
51
|
when '(', ')', '+', '-', '*', '/'
|
52
52
|
type_name = @@lexeme2name[curr_ch]
|
53
53
|
token_type = name2symbol[type_name]
|
54
|
-
token = Rley::
|
54
|
+
token = Rley::Tokens::Token.new(curr_ch, token_type)
|
55
55
|
|
56
56
|
# LITERALS
|
57
57
|
when /[-0-9]/ # Start character of number literal found
|
58
58
|
@scanner.pos = scanner.pos - 1 # Simulate putback
|
59
59
|
value = scanner.scan(/-?[0-9]+(\.[0-9]+)?([eE][-+]?[0-9])?/)
|
60
60
|
token_type = name2symbol['NUMBER']
|
61
|
-
token = Rley::
|
61
|
+
token = Rley::Tokens::Token.new(value, token_type)
|
62
62
|
|
63
63
|
|
64
64
|
else # Unknown token
|
data/lib/rley.rb
CHANGED
@@ -4,7 +4,7 @@
|
|
4
4
|
|
5
5
|
require_relative './rley/constants'
|
6
6
|
require_relative './rley/syntax/grammar_builder'
|
7
|
-
require_relative './rley/
|
7
|
+
require_relative './rley/tokens/token'
|
8
8
|
require_relative './rley/parser/earley_parser'
|
9
9
|
require_relative './rley/parser/gfg_earley_parser'
|
10
10
|
require_relative './rley/parse_tree_visitor'
|
data/lib/rley/constants.rb
CHANGED
data/lib/rley/formatter/debug.rb
CHANGED
@@ -41,7 +41,7 @@ module Rley # This module is used as a namespace
|
|
41
41
|
# the children of a non-terminal node
|
42
42
|
# @param _parent [NonTerminalNode]
|
43
43
|
# @param _children [Array] array of children nodes
|
44
|
-
def
|
44
|
+
def before_subnodes(_parent, _children)
|
45
45
|
output_event(__method__, indentation)
|
46
46
|
indent
|
47
47
|
end
|
@@ -76,7 +76,7 @@ module Rley # This module is used as a namespace
|
|
76
76
|
# the children of a non-terminal node.
|
77
77
|
# @param _parent [NonTerminalNode]
|
78
78
|
# @param _children [Array] array of children nodes
|
79
|
-
def
|
79
|
+
def after_subnodes(_parent, _children)
|
80
80
|
dedent
|
81
81
|
output_event(__method__, indentation)
|
82
82
|
end
|
data/lib/rley/formatter/json.rb
CHANGED
@@ -48,8 +48,8 @@ module Rley # This module is used as a namespace
|
|
48
48
|
# Notification of a visit event: the visitor is about to visit
|
49
49
|
# the children of a non-terminal node
|
50
50
|
# @param _parent [NonTerminalNode]
|
51
|
-
# @param
|
52
|
-
def
|
51
|
+
# @param _subnodes [Array] array of children nodes
|
52
|
+
def before_subnodes(_parent, _subnodes)
|
53
53
|
print_text('[', nil)
|
54
54
|
indent
|
55
55
|
sibling_flags.push(false)
|
@@ -73,8 +73,8 @@ module Rley # This module is used as a namespace
|
|
73
73
|
# Notification of a visit event: the visitor completed the visit of
|
74
74
|
# the children of a non-terminal node.
|
75
75
|
# @param _parent [NonTerminalNode]
|
76
|
-
# @param
|
77
|
-
def
|
76
|
+
# @param _subnodes [Array] array of children nodes
|
77
|
+
def after_subnodes(_parent, _subnodes)
|
78
78
|
sibling_flags.pop
|
79
79
|
print_text("\n", ']')
|
80
80
|
dedent
|
@@ -50,9 +50,9 @@ module Rley # This module is used as a namespace
|
|
50
50
|
def visit_nonterminal(aNonTerminalNode)
|
51
51
|
if @traversal == :post_order
|
52
52
|
broadcast(:before_non_terminal, aNonTerminalNode)
|
53
|
-
|
53
|
+
traverse_subnodes(aNonTerminalNode)
|
54
54
|
else
|
55
|
-
|
55
|
+
traverse_subnodes(aNonTerminalNode)
|
56
56
|
broadcast(:before_non_terminal, aNonTerminalNode)
|
57
57
|
end
|
58
58
|
broadcast(:after_non_terminal, aNonTerminalNode)
|
@@ -82,17 +82,17 @@ module Rley # This module is used as a namespace
|
|
82
82
|
|
83
83
|
private
|
84
84
|
|
85
|
-
# Visit event. The visitor is about to visit the
|
85
|
+
# Visit event. The visitor is about to visit the subnodes of a non
|
86
86
|
# terminal node.
|
87
87
|
# @param aParentNode [NonTeminalNode] the (non-terminal) parent node.
|
88
|
-
def
|
89
|
-
|
90
|
-
broadcast(:
|
88
|
+
def traverse_subnodes(aParentNode)
|
89
|
+
subnodes = aParentNode.subnodes
|
90
|
+
broadcast(:before_subnodes, aParentNode, subnodes)
|
91
91
|
|
92
|
-
# Let's proceed with the visit of
|
93
|
-
|
92
|
+
# Let's proceed with the visit of subnodes
|
93
|
+
subnodes.each { |a_node| a_node.accept(self) }
|
94
94
|
|
95
|
-
broadcast(:
|
95
|
+
broadcast(:after_subnodes, aParentNode, subnodes)
|
96
96
|
end
|
97
97
|
|
98
98
|
# Send a notification to all subscribers.
|
@@ -1,7 +1,7 @@
|
|
1
1
|
require_relative '../syntax/grammar'
|
2
2
|
require_relative 'grm_items_builder' # Use mix-in module
|
3
3
|
require_relative 'parse_tracer'
|
4
|
-
|
4
|
+
|
5
5
|
|
6
6
|
module Rley # This module is used as a namespace
|
7
7
|
module Parser # This module is used as a namespace
|
@@ -2,6 +2,7 @@ require_relative 'gfg_chart'
|
|
2
2
|
require_relative 'error_reason'
|
3
3
|
require_relative 'parse_entry_tracker'
|
4
4
|
require_relative 'parse_forest_factory'
|
5
|
+
require_relative 'parse_tree_factory'
|
5
6
|
|
6
7
|
|
7
8
|
module Rley # This module is used as a namespace
|
@@ -148,6 +149,14 @@ module Rley # This module is used as a namespace
|
|
148
149
|
|
149
150
|
return factory.build_parse_forest
|
150
151
|
end
|
152
|
+
|
153
|
+
# Factory method. Builds a ParseTree from the parse result.
|
154
|
+
# @return [ParseTree]
|
155
|
+
def parse_tree()
|
156
|
+
factory = ParseTreeFactory.new(self)
|
157
|
+
|
158
|
+
return factory.build_parse_tree
|
159
|
+
end
|
151
160
|
|
152
161
|
# Retrieve the very first parse entry added to the chart.
|
153
162
|
# This entry corresponds to the start vertex of the GF graph
|
@@ -1,174 +1,224 @@
|
|
1
|
-
require_relative '../
|
1
|
+
require_relative '../syntax/terminal'
|
2
|
+
require_relative '../syntax/non_terminal'
|
3
|
+
require_relative '../gfg/end_vertex'
|
4
|
+
require_relative '../gfg/item_vertex'
|
5
|
+
require_relative '../gfg/start_vertex'
|
2
6
|
require_relative '../ptree/non_terminal_node'
|
7
|
+
require_relative '../ptree/terminal_node'
|
3
8
|
require_relative '../ptree/parse_tree'
|
4
9
|
|
5
|
-
|
6
10
|
module Rley # This module is used as a namespace
|
7
11
|
module Parser # This module is used as a namespace
|
8
12
|
# Builder GoF pattern. Builder pattern builds a complex object
|
9
13
|
# (say, a parse tree) from simpler objects (terminal and non-terminal
|
10
14
|
# nodes) and using a step by step approach.
|
11
15
|
class ParseTreeBuilder
|
12
|
-
|
13
|
-
attr_reader(:
|
14
|
-
|
15
|
-
def initialize(aStartProduction, aRange)
|
16
|
-
@current_path = []
|
17
|
-
start_symbol = aStartProduction.lhs
|
18
|
-
add_node(start_symbol, aRange)
|
19
|
-
use_production(aStartProduction, aRange)
|
20
|
-
move_down
|
21
|
-
end
|
16
|
+
# The sequence of input tokens
|
17
|
+
attr_reader(:tokens)
|
22
18
|
|
23
|
-
#
|
24
|
-
|
25
|
-
return current_path.last
|
26
|
-
end
|
19
|
+
# Link to tree object
|
20
|
+
attr_reader(:tree)
|
27
21
|
|
28
|
-
#
|
29
|
-
|
30
|
-
return PTree::ParseTree.new(root)
|
31
|
-
end
|
22
|
+
# Link to current path
|
23
|
+
attr_reader(:curr_path)
|
32
24
|
|
25
|
+
# The last parse entry visited
|
26
|
+
attr_reader(:last_visitee)
|
33
27
|
|
34
|
-
#
|
35
|
-
|
36
|
-
# Then add the rhs constituents as child nodes of the current node.
|
37
|
-
# Assumption: current node is lhs of the production association
|
38
|
-
# with the parse state.
|
39
|
-
# @param aCompleteState [ParseState] A complete parse state
|
40
|
-
# (dot is at end of rhs)
|
41
|
-
def use_complete_state(aCompleteState)
|
42
|
-
prod = aCompleteState.dotted_rule.production
|
43
|
-
use_production(prod, low: aCompleteState.origin)
|
44
|
-
end
|
28
|
+
# A hash with pairs of the form: visited parse entry => tree node
|
29
|
+
attr_reader(:entry2node)
|
45
30
|
|
46
|
-
# Given that the current node is a non-terminal
|
47
|
-
# Make its last child node the current node.
|
48
|
-
def move_down()
|
49
|
-
curr_node = current_node
|
50
|
-
unless curr_node.is_a?(PTree::NonTerminalNode)
|
51
|
-
msg = "Current node isn't a non-terminal node #{curr_node.class}"
|
52
|
-
raise StandardError, msg
|
53
|
-
end
|
54
|
-
children = curr_node.children
|
55
|
-
path_increment = [children.size - 1, children.last]
|
56
|
-
@current_path.concat(path_increment)
|
57
|
-
end
|
58
31
|
|
32
|
+
def initialize(theTokens)
|
33
|
+
@tokens = theTokens
|
34
|
+
@curr_path = []
|
35
|
+
@entry2node = {}
|
36
|
+
end
|
59
37
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
(
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
current_path << new_pos
|
71
|
-
current_path << new_curr_node
|
72
|
-
end
|
73
|
-
break if pos > 0 || new_curr_node.is_a?(PTree::TerminalNode)
|
38
|
+
def receive_event(anEvent, anEntry, anIndex)
|
39
|
+
# puts "Event: #{anEvent} #{anEntry} #{anIndex}"
|
40
|
+
if anEntry.dotted_entry?
|
41
|
+
process_item_entry(anEvent, anEntry, anIndex)
|
42
|
+
elsif anEntry.start_entry?
|
43
|
+
process_start_entry(anEvent, anEntry, anIndex)
|
44
|
+
elsif anEntry.end_entry?
|
45
|
+
process_end_entry(anEvent, anEntry, anIndex)
|
46
|
+
else
|
47
|
+
raise NotImplementedError
|
74
48
|
end
|
49
|
+
|
50
|
+
@last_visitee = anEntry
|
75
51
|
end
|
76
52
|
|
53
|
+
# Return the current_parent node
|
54
|
+
def curr_parent()
|
55
|
+
return curr_path.last
|
56
|
+
end
|
77
57
|
|
78
|
-
|
79
|
-
def add_node(aSymbol, aRange)
|
80
|
-
# Create the node
|
81
|
-
a_node = new_node(aSymbol, aRange)
|
58
|
+
private
|
82
59
|
|
83
|
-
|
84
|
-
|
60
|
+
def process_start_entry(_anEvent, _anEntry, _anIndex)
|
61
|
+
curr_path.pop
|
85
62
|
end
|
86
63
|
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
64
|
+
def process_end_entry(anEvent, anEntry, anIndex)
|
65
|
+
case anEvent
|
66
|
+
when :visit
|
67
|
+
# create a node with the non-terminal
|
68
|
+
# with same right extent as curr_entry_set_index
|
69
|
+
# add the new node as first child of current_parent
|
70
|
+
# append the new node to the curr_path
|
71
|
+
range = { low: anEntry.origin, high: anIndex }
|
72
|
+
non_terminal = anEntry.vertex.non_terminal
|
73
|
+
create_non_terminal_node(anEntry, range, non_terminal)
|
74
|
+
@tree = create_tree(curr_parent) unless @last_visitee
|
75
|
+
else
|
76
|
+
raise NotImplementedError
|
98
77
|
end
|
99
|
-
upper = high_bound(aRange)
|
100
|
-
current_node.range = upper unless upper.nil?
|
101
78
|
end
|
102
79
|
|
103
|
-
private
|
104
80
|
|
105
|
-
def
|
106
|
-
case
|
107
|
-
when
|
108
|
-
|
109
|
-
|
110
|
-
|
81
|
+
def process_item_entry(anEvent, anEntry, anIndex)
|
82
|
+
case anEvent
|
83
|
+
when :visit
|
84
|
+
if anEntry.exit_entry?
|
85
|
+
# Previous entry was an end entry (X. pattern)
|
86
|
+
# Does the previous entry have multiple antecedent?
|
87
|
+
if last_visitee.end_entry? && last_visitee.antecedents.size > 1
|
88
|
+
# Store current path for later backtracking
|
89
|
+
# puts "Store backtrack context #{last_visitee}"
|
90
|
+
# puts "path [#{curr_path.map{|e|e.to_string(0)}.join(', ')}]"
|
91
|
+
entry2path_to_alt[last_visitee] = curr_path.dup
|
92
|
+
curr_parent.refinement = :or
|
93
|
+
|
94
|
+
create_alternative_node(anEntry)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
# Does this entry have multiple antecedent?
|
99
|
+
if anEntry.antecedents.size > 1
|
100
|
+
# Store current path for later backtracking
|
101
|
+
# puts "Store backtrack context #{anEntry}"
|
102
|
+
# puts "path [#{curr_path.map{|e|e.to_string(0)}.join(', ')}]"
|
103
|
+
entry2path_to_alt[anEntry] = curr_path.dup
|
104
|
+
# curr_parent.refinement = :or
|
105
|
+
|
106
|
+
create_alternative_node(anEntry)
|
107
|
+
end
|
108
|
+
|
109
|
+
# Retrieve the grammar symbol before the dot (if any)
|
110
|
+
prev_symbol = anEntry.prev_symbol
|
111
|
+
case prev_symbol
|
112
|
+
when Syntax::Terminal
|
113
|
+
# Add node without changing current path
|
114
|
+
create_token_node(anEntry, anIndex)
|
115
|
+
|
116
|
+
when NilClass # Dot at the beginning of production
|
117
|
+
if anEntry.vertex.dotted_item.production.empty?
|
118
|
+
# Empty rhs => create an epsilon node ...
|
119
|
+
# ... without changing current path
|
120
|
+
create_epsilon_node(anEntry, anIndex)
|
121
|
+
end
|
122
|
+
curr_path.pop if curr_parent.kind_of?(SPPF::AlternativeNode)
|
123
|
+
end
|
124
|
+
|
125
|
+
when :backtrack
|
126
|
+
# # Restore path
|
127
|
+
# @curr_path = entry2path_to_alt[anEntry].dup
|
128
|
+
# # puts "Special restore path [#{curr_path.map{|e|e.to_string(0)}.join(', ')}]"
|
129
|
+
# antecedent_index = curr_parent.subnodes.size
|
130
|
+
# # puts "Current parent #{curr_parent.to_string(0)}"
|
131
|
+
# # puts "Antecedent index #{antecedent_index}"
|
132
|
+
|
133
|
+
# create_alternative_node(anEntry)
|
134
|
+
|
135
|
+
when :revisit
|
136
|
+
# # Retrieve the grammar symbol before the dot (if any)
|
137
|
+
# prev_symbol = anEntry.prev_symbol
|
138
|
+
# case prev_symbol
|
139
|
+
# when Syntax::Terminal
|
140
|
+
# # Add node without changing current path
|
141
|
+
# create_token_node(anEntry, anIndex)
|
142
|
+
|
143
|
+
# when NilClass # Dot at the beginning of production
|
144
|
+
# if anEntry.vertex.dotted_item.production.empty?
|
145
|
+
# # Empty rhs => create an epsilon node ...
|
146
|
+
# # ... without changing current path
|
147
|
+
# create_epsilon_node(anEntry, anIndex)
|
148
|
+
# end
|
149
|
+
# curr_path.pop if curr_parent.kind_of?(PTree::AlternativeNode)
|
150
|
+
# end
|
111
151
|
end
|
152
|
+
end
|
112
153
|
|
113
|
-
|
154
|
+
# Create an empty parse tree
|
155
|
+
def create_tree(aRootNode)
|
156
|
+
return Rley::PTree::ParseTree.new(aRootNode)
|
114
157
|
end
|
115
158
|
|
116
|
-
# Add children nodes to current one.
|
117
|
-
# The children correspond to the members of the rhs of the production.
|
118
|
-
def use_production(aProduction, aRange)
|
119
|
-
prod = aProduction
|
120
|
-
curr_node = current_node
|
121
159
|
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
160
|
+
# Factory method. Build and return an PTree non-terminal node.
|
161
|
+
def create_non_terminal_node(anEntry, aRange, nonTSymb = nil)
|
162
|
+
non_terminal = nonTSymb.nil? ? anEntry.vertex.non_terminal : nonTSymb
|
163
|
+
new_node = Rley::PTree::NonTerminalNode.new(non_terminal, aRange)
|
164
|
+
entry2node[anEntry] = new_node
|
165
|
+
add_subnode(new_node)
|
166
|
+
# puts "FOREST ADD #{curr_parent.key if curr_parent}/#{new_node.key}"
|
129
167
|
|
130
|
-
return
|
131
|
-
curr_node.children.first.range.assign(low: curr_node.range.low)
|
132
|
-
curr_node.children.last.range.assign(high: curr_node.range.high)
|
168
|
+
return new_node
|
133
169
|
end
|
134
170
|
|
135
|
-
# Add the given node as child node of current node
|
136
|
-
def add_child(aNode)
|
137
|
-
curr_node = current_node
|
138
171
|
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
172
|
+
# Add an alternative node to the tree
|
173
|
+
def create_alternative_node(anEntry)
|
174
|
+
vertex = anEntry.vertex
|
175
|
+
range = curr_parent.range
|
176
|
+
alternative = Rley::PTree::AlternativeNode.new(vertex, range)
|
177
|
+
add_subnode(alternative)
|
178
|
+
tree.is_ambiguous = true
|
179
|
+
# puts "FOREST ADD #{alternative.key}"
|
180
|
+
|
181
|
+
return alternative
|
144
182
|
end
|
145
183
|
|
146
|
-
#
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
184
|
+
# create a token node,
|
185
|
+
# with same origin as token,
|
186
|
+
# with same right extent = origin + 1
|
187
|
+
# add the new node as first child of current_parent
|
188
|
+
def create_token_node(anEntry, anIndex)
|
189
|
+
token_position = anIndex - 1
|
190
|
+
curr_token = tokens[token_position]
|
191
|
+
new_node = PTree::TerminalNode.new(curr_token, token_position)
|
192
|
+
candidate = add_node_to_tree(new_node)
|
193
|
+
entry2node[anEntry] = candidate
|
194
|
+
|
195
|
+
return candidate
|
151
196
|
end
|
152
197
|
|
153
198
|
|
154
|
-
def
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
199
|
+
def create_epsilon_node(anEntry, anIndex)
|
200
|
+
new_node = PTree::EpsilonNode.new(anIndex)
|
201
|
+
candidate = add_node_to_tree(new_node)
|
202
|
+
entry2node[anEntry] = candidate
|
203
|
+
|
204
|
+
return candidate
|
205
|
+
end
|
206
|
+
|
207
|
+
# Add the given node if not yet present in parse tree
|
208
|
+
def add_node_to_tree(aNode)
|
209
|
+
new_node = aNode
|
210
|
+
# puts "FOREST ADD #{key_node}"
|
211
|
+
add_subnode(new_node, false)
|
160
212
|
|
161
|
-
return
|
213
|
+
return new_node
|
162
214
|
end
|
163
215
|
|
164
|
-
def high_bound(aRange)
|
165
|
-
result = case aRange
|
166
|
-
when Integer then aRange
|
167
|
-
when Hash then aRange[:high]
|
168
|
-
when PTree::TokenRange then aRange.high
|
169
|
-
end
|
170
216
|
|
171
|
-
|
217
|
+
# Add the given node as sub-node of current parent node
|
218
|
+
# Optionally add the node to the current path
|
219
|
+
def add_subnode(aNode, addToPath = true)
|
220
|
+
curr_parent.add_subnode(aNode) unless curr_path.empty?
|
221
|
+
curr_path << aNode if addToPath
|
172
222
|
end
|
173
223
|
end # class
|
174
224
|
end # module
|