dendroid 0.2.00 → 0.2.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/.rubocop.yml +3 -0
- data/CHANGELOG.md +16 -0
- data/lib/dendroid/formatters/ascii_tree.rb +142 -142
- data/lib/dendroid/formatters/base_formatter.rb +24 -25
- data/lib/dendroid/formatters/bracket_notation.rb +50 -50
- data/lib/dendroid/grm_analysis/dotted_item.rb +0 -1
- data/lib/dendroid/grm_analysis/grm_analyzer.rb +9 -1
- data/lib/dendroid/grm_analysis/rule_items.rb +1 -1
- data/lib/dendroid/parsing/and_node.rb +54 -56
- data/lib/dendroid/parsing/chart_walker.rb +301 -293
- data/lib/dendroid/parsing/composite_parse_node.rb +20 -21
- data/lib/dendroid/parsing/empty_rule_node.rb +28 -28
- data/lib/dendroid/parsing/or_node.rb +46 -51
- data/lib/dendroid/parsing/parse_node.rb +26 -26
- data/lib/dendroid/parsing/parse_tree_visitor.rb +127 -127
- data/lib/dendroid/parsing/terminal_node.rb +32 -32
- data/lib/dendroid/parsing/walk_progress.rb +121 -117
- data/lib/dendroid/recognizer/chart.rb +1 -6
- data/lib/dendroid/recognizer/recognizer.rb +12 -6
- data/lib/dendroid/syntax/grammar.rb +3 -5
- data/lib/dendroid/syntax/rule.rb +1 -1
- data/lib/dendroid.rb +13 -0
- data/spec/dendroid/grm_analysis/dotted_item_spec.rb +1 -1
- data/spec/dendroid/parsing/chart_walker_spec.rb +250 -223
- data/spec/dendroid/parsing/terminal_node_spec.rb +36 -36
- data/spec/dendroid/recognizer/recognizer_spec.rb +0 -1
- data/spec/dendroid/support/sample_grammars.rb +0 -2
- data/version.txt +1 -1
- metadata +3 -4
- data/lib/dendroid/parsing/parser.rb +0 -185
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bb58d7b275f7eb65ccfef37ee213fc53319c5634cf8dbd79020451c79769a3c3
|
4
|
+
data.tar.gz: f075b751545df13bda67d3e2df1c3861dfdaffa8fc416a7a735d0c4fc2da01bb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1f3598bbc4f19ff183e655a92ca86908615a10b2f142982c09eb11b01b9fd05296db1ce5b9fa14be9d5f71c8358fd60535a0eb8b0feea8fcddafae5100fe8df4
|
7
|
+
data.tar.gz: c3227a5e8cb2675d2de6d011091e3b665e0217e62401619db36e43967a9d407c06a50205031c03dacc43f72b01cd1b0ebf66132240194d61de1ae1e167594113
|
data/.rubocop.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -2,9 +2,25 @@
|
|
2
2
|
|
3
3
|
## [Unreleased]
|
4
4
|
|
5
|
+
## [0.2.02] - 2023-12-18
|
6
|
+
Code re-styling to fix most Rubocop offenses.
|
7
|
+
|
8
|
+
### Added
|
9
|
+
- Directory 'modeling' for PlantUML text files.
|
10
|
+
|
11
|
+
### Changed
|
12
|
+
- File `dendroid.rb`: added `relative_require` to load key dependencies.
|
13
|
+
|
14
|
+
## [0.2.01] - 2023-12-17
|
15
|
+
Code re-styling to fix most Rubocop offenses.
|
16
|
+
|
5
17
|
## [0.2.00] - 2023-12-16
|
6
18
|
Version bump: Very crude parser implementation (generate shared parse forests in case of ambiguity).
|
7
19
|
|
20
|
+
### Added
|
21
|
+
- Directory `parsing`: module implementing a parser
|
22
|
+
- Directory `formatters`: module with classes for rendering parse tree/forest
|
23
|
+
|
8
24
|
## [0.1.00] - 2023-11-03
|
9
25
|
Version bump: the Earley recognizer is functional.
|
10
26
|
|
@@ -1,142 +1,142 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative 'base_formatter'
|
4
|
-
|
5
|
-
# A formatter class that draws parse trees by using characters
|
6
|
-
class Asciitree < BaseFormatter
|
7
|
-
# TODO
|
8
|
-
attr_reader(:curr_path)
|
9
|
-
|
10
|
-
# For each node in curr_path, there is a corresponding string value.
|
11
|
-
# Allowed string values are: 'first', 'last', 'first_and_last', 'other'
|
12
|
-
attr_reader(:ranks)
|
13
|
-
|
14
|
-
# @return [String] The character pattern used for rendering
|
15
|
-
# a parent - child nesting
|
16
|
-
attr_reader(:and_nesting_prefix)
|
17
|
-
|
18
|
-
# TODO: comment
|
19
|
-
attr_reader(:or_nesting_prefix)
|
20
|
-
|
21
|
-
# @return [String] The character pattern used for a blank indentation
|
22
|
-
attr_reader(:blank_indent)
|
23
|
-
|
24
|
-
# @return [String] The character pattern for indentation and nesting
|
25
|
-
# continuation.
|
26
|
-
attr_reader(:continuation_indent)
|
27
|
-
|
28
|
-
# Constructor.
|
29
|
-
# @param anIO [IO] The output stream to which the parse tree
|
30
|
-
# is written.
|
31
|
-
def initialize(anIO)
|
32
|
-
super(anIO)
|
33
|
-
@curr_path = []
|
34
|
-
@ranks = []
|
35
|
-
|
36
|
-
@and_nesting_prefix = '+-- '
|
37
|
-
@or_nesting_prefix = '/-- '
|
38
|
-
@blank_indent = ' '
|
39
|
-
@continuation_indent = '| '
|
40
|
-
end
|
41
|
-
|
42
|
-
# Method called by a ParseTreeVisitor to which the formatter subscribed.
|
43
|
-
# Notification of a visit event: the visitor is about to visit
|
44
|
-
# the children of a non-terminal node
|
45
|
-
# @param parent [NonTerminalNode]
|
46
|
-
# @param _children [Array<ParseTreeNode>] array of children nodes
|
47
|
-
def before_subnodes(parent, _children)
|
48
|
-
rank_of(parent)
|
49
|
-
curr_path << parent
|
50
|
-
end
|
51
|
-
|
52
|
-
# Method called by a ParseTreeVisitor to which the formatter subscribed.
|
53
|
-
# Notification of a visit event: the visitor is about to visit
|
54
|
-
# a non-terminal node
|
55
|
-
# @param aNonTerm [NonTerminalNode]
|
56
|
-
def before_and_node(aNonTerm)
|
57
|
-
emit_and(aNonTerm)
|
58
|
-
end
|
59
|
-
|
60
|
-
def before_or_node(aNonTerm)
|
61
|
-
emit_or(aNonTerm)
|
62
|
-
end
|
63
|
-
|
64
|
-
def before_empty_rule_node(anEmptyRuleNode)
|
65
|
-
emit_and(anEmptyRuleNode, ': .')
|
66
|
-
end
|
67
|
-
|
68
|
-
# Method called by a ParseTreeVisitor to which the formatter subscribed.
|
69
|
-
# Notification of a visit event: the visitor is about to visit
|
70
|
-
# a terminal node
|
71
|
-
# @param aTerm [TerminalNode]
|
72
|
-
def before_terminal(aTerm)
|
73
|
-
emit_terminal(aTerm, ": '#{aTerm.token.source}'")
|
74
|
-
end
|
75
|
-
|
76
|
-
# Method called by a ParseTreeVisitor to which the formatter subscribed.
|
77
|
-
# Notification of a visit event: the visitor completed the visit of
|
78
|
-
# the children of a non-terminal node.
|
79
|
-
# @param _parent [NonTerminalNode]
|
80
|
-
# @param _children [Array] array of children nodes
|
81
|
-
def after_subnodes(_parent, _children)
|
82
|
-
curr_path.pop
|
83
|
-
ranks.pop
|
84
|
-
end
|
85
|
-
|
86
|
-
private
|
87
|
-
|
88
|
-
# Parent node is last node in current path
|
89
|
-
# or current path is empty (then aChild is root node)
|
90
|
-
def rank_of(aChild)
|
91
|
-
if curr_path.empty?
|
92
|
-
rank = 'root'
|
93
|
-
elsif curr_path[-1].children.size == 1
|
94
|
-
rank = 'first_and_last'
|
95
|
-
else
|
96
|
-
parent = curr_path[-1]
|
97
|
-
siblings = parent.children
|
98
|
-
siblings_last_index = siblings.size - 1
|
99
|
-
rank = case siblings.find_index(aChild)
|
100
|
-
when 0 then 'first'
|
101
|
-
when siblings_last_index then 'last'
|
102
|
-
else
|
103
|
-
'other'
|
104
|
-
end
|
105
|
-
end
|
106
|
-
ranks << rank
|
107
|
-
end
|
108
|
-
|
109
|
-
# 'root', 'first', 'first_and_last', 'last', 'other'
|
110
|
-
def path_prefix(connector)
|
111
|
-
return '' if ranks.empty?
|
112
|
-
|
113
|
-
prefix = +''
|
114
|
-
@ranks.each_with_index do |rank, i|
|
115
|
-
next if i.zero?
|
116
|
-
|
117
|
-
case rank
|
118
|
-
when 'first', 'other'
|
119
|
-
prefix << continuation_indent
|
120
|
-
|
121
|
-
when 'last', 'first_and_last', 'root'
|
122
|
-
prefix << blank_indent
|
123
|
-
end
|
124
|
-
end
|
125
|
-
|
126
|
-
nesting =
|
127
|
-
prefix << nesting
|
128
|
-
prefix
|
129
|
-
end
|
130
|
-
|
131
|
-
def emit_and(aNode, aSuffix = '')
|
132
|
-
output.puts("#{path_prefix(:and)}#{aNode.rule.lhs.name}#{aSuffix}")
|
133
|
-
end
|
134
|
-
|
135
|
-
def emit_or(aNode, aSuffix = '')
|
136
|
-
output.puts("#{path_prefix(:or)}OR #{aNode.symbol.name}#{aSuffix}")
|
137
|
-
end
|
138
|
-
|
139
|
-
def emit_terminal(aNode, aSuffix = '')
|
140
|
-
output.puts("#{path_prefix(:and)}#{aNode.symbol.name}#{aSuffix}")
|
141
|
-
end
|
142
|
-
end # class
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'base_formatter'
|
4
|
+
|
5
|
+
# A formatter class that draws parse trees by using characters
|
6
|
+
class Asciitree < BaseFormatter
|
7
|
+
# TODO
|
8
|
+
attr_reader(:curr_path)
|
9
|
+
|
10
|
+
# For each node in curr_path, there is a corresponding string value.
|
11
|
+
# Allowed string values are: 'first', 'last', 'first_and_last', 'other'
|
12
|
+
attr_reader(:ranks)
|
13
|
+
|
14
|
+
# @return [String] The character pattern used for rendering
|
15
|
+
# a parent - child nesting
|
16
|
+
attr_reader(:and_nesting_prefix)
|
17
|
+
|
18
|
+
# TODO: comment
|
19
|
+
attr_reader(:or_nesting_prefix)
|
20
|
+
|
21
|
+
# @return [String] The character pattern used for a blank indentation
|
22
|
+
attr_reader(:blank_indent)
|
23
|
+
|
24
|
+
# @return [String] The character pattern for indentation and nesting
|
25
|
+
# continuation.
|
26
|
+
attr_reader(:continuation_indent)
|
27
|
+
|
28
|
+
# Constructor.
|
29
|
+
# @param anIO [IO] The output stream to which the parse tree
|
30
|
+
# is written.
|
31
|
+
def initialize(anIO)
|
32
|
+
super(anIO)
|
33
|
+
@curr_path = []
|
34
|
+
@ranks = []
|
35
|
+
|
36
|
+
@and_nesting_prefix = '+-- '
|
37
|
+
@or_nesting_prefix = '/-- '
|
38
|
+
@blank_indent = ' '
|
39
|
+
@continuation_indent = '| '
|
40
|
+
end
|
41
|
+
|
42
|
+
# Method called by a ParseTreeVisitor to which the formatter subscribed.
|
43
|
+
# Notification of a visit event: the visitor is about to visit
|
44
|
+
# the children of a non-terminal node
|
45
|
+
# @param parent [NonTerminalNode]
|
46
|
+
# @param _children [Array<ParseTreeNode>] array of children nodes
|
47
|
+
def before_subnodes(parent, _children)
|
48
|
+
rank_of(parent)
|
49
|
+
curr_path << parent
|
50
|
+
end
|
51
|
+
|
52
|
+
# Method called by a ParseTreeVisitor to which the formatter subscribed.
|
53
|
+
# Notification of a visit event: the visitor is about to visit
|
54
|
+
# a non-terminal node
|
55
|
+
# @param aNonTerm [NonTerminalNode]
|
56
|
+
def before_and_node(aNonTerm)
|
57
|
+
emit_and(aNonTerm)
|
58
|
+
end
|
59
|
+
|
60
|
+
def before_or_node(aNonTerm)
|
61
|
+
emit_or(aNonTerm)
|
62
|
+
end
|
63
|
+
|
64
|
+
def before_empty_rule_node(anEmptyRuleNode)
|
65
|
+
emit_and(anEmptyRuleNode, ': .')
|
66
|
+
end
|
67
|
+
|
68
|
+
# Method called by a ParseTreeVisitor to which the formatter subscribed.
|
69
|
+
# Notification of a visit event: the visitor is about to visit
|
70
|
+
# a terminal node
|
71
|
+
# @param aTerm [TerminalNode]
|
72
|
+
def before_terminal(aTerm)
|
73
|
+
emit_terminal(aTerm, ": '#{aTerm.token.source}'")
|
74
|
+
end
|
75
|
+
|
76
|
+
# Method called by a ParseTreeVisitor to which the formatter subscribed.
|
77
|
+
# Notification of a visit event: the visitor completed the visit of
|
78
|
+
# the children of a non-terminal node.
|
79
|
+
# @param _parent [NonTerminalNode]
|
80
|
+
# @param _children [Array] array of children nodes
|
81
|
+
def after_subnodes(_parent, _children)
|
82
|
+
curr_path.pop
|
83
|
+
ranks.pop
|
84
|
+
end
|
85
|
+
|
86
|
+
private
|
87
|
+
|
88
|
+
# Parent node is last node in current path
|
89
|
+
# or current path is empty (then aChild is root node)
|
90
|
+
def rank_of(aChild)
|
91
|
+
if curr_path.empty?
|
92
|
+
rank = 'root'
|
93
|
+
elsif curr_path[-1].children.size == 1
|
94
|
+
rank = 'first_and_last'
|
95
|
+
else
|
96
|
+
parent = curr_path[-1]
|
97
|
+
siblings = parent.children
|
98
|
+
siblings_last_index = siblings.size - 1
|
99
|
+
rank = case siblings.find_index(aChild)
|
100
|
+
when 0 then 'first'
|
101
|
+
when siblings_last_index then 'last'
|
102
|
+
else
|
103
|
+
'other'
|
104
|
+
end
|
105
|
+
end
|
106
|
+
ranks << rank
|
107
|
+
end
|
108
|
+
|
109
|
+
# 'root', 'first', 'first_and_last', 'last', 'other'
|
110
|
+
def path_prefix(connector)
|
111
|
+
return '' if ranks.empty?
|
112
|
+
|
113
|
+
prefix = +''
|
114
|
+
@ranks.each_with_index do |rank, i|
|
115
|
+
next if i.zero?
|
116
|
+
|
117
|
+
case rank
|
118
|
+
when 'first', 'other'
|
119
|
+
prefix << continuation_indent
|
120
|
+
|
121
|
+
when 'last', 'first_and_last', 'root'
|
122
|
+
prefix << blank_indent
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
nesting = connector == :and ? and_nesting_prefix : or_nesting_prefix
|
127
|
+
prefix << nesting
|
128
|
+
prefix
|
129
|
+
end
|
130
|
+
|
131
|
+
def emit_and(aNode, aSuffix = '')
|
132
|
+
output.puts("#{path_prefix(:and)}#{aNode.rule.lhs.name}#{aSuffix}")
|
133
|
+
end
|
134
|
+
|
135
|
+
def emit_or(aNode, aSuffix = '')
|
136
|
+
output.puts("#{path_prefix(:or)}OR #{aNode.symbol.name}#{aSuffix}")
|
137
|
+
end
|
138
|
+
|
139
|
+
def emit_terminal(aNode, aSuffix = '')
|
140
|
+
output.puts("#{path_prefix(:and)}#{aNode.symbol.name}#{aSuffix}")
|
141
|
+
end
|
142
|
+
end # class
|
@@ -1,25 +1,24 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
#
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
#
|
11
|
-
#
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
#
|
18
|
-
#
|
19
|
-
|
20
|
-
|
21
|
-
aVisitor.
|
22
|
-
aVisitor.
|
23
|
-
|
24
|
-
|
25
|
-
end # class
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Superclass for parse tree formatters.
|
4
|
+
class BaseFormatter
|
5
|
+
# The IO output stream in which the formatter's result will be sent.
|
6
|
+
# @return [IO] The output stream for the formatter.
|
7
|
+
attr_reader(:output)
|
8
|
+
|
9
|
+
# Constructor.
|
10
|
+
# @param anIO [IO] an output IO where the formatter's result will
|
11
|
+
# be placed.
|
12
|
+
def initialize(anIO)
|
13
|
+
@output = anIO
|
14
|
+
end
|
15
|
+
|
16
|
+
# Given a parse tree visitor, perform the visit
|
17
|
+
# and render the visit events in the output stream.
|
18
|
+
# @param aVisitor [ParseTreeVisitor]
|
19
|
+
def render(aVisitor)
|
20
|
+
aVisitor.subscribe(self)
|
21
|
+
aVisitor.start
|
22
|
+
aVisitor.unsubscribe(self)
|
23
|
+
end
|
24
|
+
end # class
|
@@ -1,50 +1,50 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative 'base_formatter'
|
4
|
-
|
5
|
-
class BracketNotation < BaseFormatter
|
6
|
-
# Method called by a ParseTreeVisitor to which the formatter subscribed.
|
7
|
-
# Notification of a visit event: the visitor is about to visit
|
8
|
-
# a non-terminal node
|
9
|
-
# @param and_node [ANDNode]
|
10
|
-
def before_and_node(and_node)
|
11
|
-
write("[#{and_node.rule.lhs.name} ")
|
12
|
-
end
|
13
|
-
|
14
|
-
def before_empty_rule_node(anEmptyRuleNode)
|
15
|
-
write("[#{anEmptyRuleNode.rule.lhs.name}]")
|
16
|
-
end
|
17
|
-
|
18
|
-
# Method called by a ParseTreeVisitor to which the formatter subscribed.
|
19
|
-
# Notification of a visit event: the visitor is about to visit
|
20
|
-
# a terminal node
|
21
|
-
# @param aTerm [TerminalNode]
|
22
|
-
def before_terminal(aTerm)
|
23
|
-
write("[#{aTerm.symbol.name} ")
|
24
|
-
end
|
25
|
-
|
26
|
-
# Method called by a ParseTreeVisitor to which the formatter subscribed.
|
27
|
-
# Notification of a visit event: the visitor completed the visit of
|
28
|
-
# a terminal node.
|
29
|
-
# @param aTerm [TerminalNode]
|
30
|
-
def after_terminal(aTerm)
|
31
|
-
# Escape all opening and closing square brackets
|
32
|
-
escape_lbrackets = aTerm.token.source.gsub(/\[/, '\[')
|
33
|
-
escaped = escape_lbrackets.gsub(/\]/, '\]')
|
34
|
-
write("#{escaped}]")
|
35
|
-
end
|
36
|
-
|
37
|
-
# Method called by a ParseTreeVisitor to which the formatter subscribed.
|
38
|
-
# Notification of a visit event: the visitor completed the visit of
|
39
|
-
# a non-terminal node
|
40
|
-
# @param _nonterm [NonTerminalNode]
|
41
|
-
def after_and_node(_nonterm)
|
42
|
-
write(']')
|
43
|
-
end
|
44
|
-
|
45
|
-
private
|
46
|
-
|
47
|
-
def write(aText)
|
48
|
-
output.write(aText)
|
49
|
-
end
|
50
|
-
end # class
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'base_formatter'
|
4
|
+
|
5
|
+
class BracketNotation < BaseFormatter
|
6
|
+
# Method called by a ParseTreeVisitor to which the formatter subscribed.
|
7
|
+
# Notification of a visit event: the visitor is about to visit
|
8
|
+
# a non-terminal node
|
9
|
+
# @param and_node [ANDNode]
|
10
|
+
def before_and_node(and_node)
|
11
|
+
write("[#{and_node.rule.lhs.name} ")
|
12
|
+
end
|
13
|
+
|
14
|
+
def before_empty_rule_node(anEmptyRuleNode)
|
15
|
+
write("[#{anEmptyRuleNode.rule.lhs.name}]")
|
16
|
+
end
|
17
|
+
|
18
|
+
# Method called by a ParseTreeVisitor to which the formatter subscribed.
|
19
|
+
# Notification of a visit event: the visitor is about to visit
|
20
|
+
# a terminal node
|
21
|
+
# @param aTerm [TerminalNode]
|
22
|
+
def before_terminal(aTerm)
|
23
|
+
write("[#{aTerm.symbol.name} ")
|
24
|
+
end
|
25
|
+
|
26
|
+
# Method called by a ParseTreeVisitor to which the formatter subscribed.
|
27
|
+
# Notification of a visit event: the visitor completed the visit of
|
28
|
+
# a terminal node.
|
29
|
+
# @param aTerm [TerminalNode]
|
30
|
+
def after_terminal(aTerm)
|
31
|
+
# Escape all opening and closing square brackets
|
32
|
+
escape_lbrackets = aTerm.token.source.gsub(/\[/, '\[')
|
33
|
+
escaped = escape_lbrackets.gsub(/\]/, '\]')
|
34
|
+
write("#{escaped}]")
|
35
|
+
end
|
36
|
+
|
37
|
+
# Method called by a ParseTreeVisitor to which the formatter subscribed.
|
38
|
+
# Notification of a visit event: the visitor completed the visit of
|
39
|
+
# a non-terminal node
|
40
|
+
# @param _nonterm [NonTerminalNode]
|
41
|
+
def after_and_node(_nonterm)
|
42
|
+
write(']')
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def write(aText)
|
48
|
+
output.write(aText)
|
49
|
+
end
|
50
|
+
end # class
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative '../grm_analysis/rule_items
|
3
|
+
require_relative '../grm_analysis/rule_items'
|
4
4
|
|
5
5
|
module Dendroid
|
6
6
|
module GrmAnalysis
|
@@ -111,6 +111,10 @@ module Dendroid
|
|
111
111
|
result
|
112
112
|
end
|
113
113
|
|
114
|
+
# rubocop: disable Metrics/AbcSize
|
115
|
+
# rubocop: disable Metrics/CyclomaticComplexity
|
116
|
+
# rubocop: disable Metrics/PerceivedComplexity
|
117
|
+
|
114
118
|
# FOLLOW(A): is the set of terminals (+ end marker) that may come after the
|
115
119
|
# non-terminal A.
|
116
120
|
def build_follow_sets
|
@@ -155,6 +159,10 @@ module Dendroid
|
|
155
159
|
end
|
156
160
|
end
|
157
161
|
|
162
|
+
# rubocop: enable Metrics/AbcSize
|
163
|
+
# rubocop: enable Metrics/CyclomaticComplexity
|
164
|
+
# rubocop: enable Metrics/PerceivedComplexity
|
165
|
+
|
158
166
|
def initialize_follow_sets
|
159
167
|
grammar.symbols.each do |symb|
|
160
168
|
next if symb.terminal?
|
@@ -1,56 +1,54 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative 'composite_parse_node'
|
4
|
-
|
5
|
-
module Dendroid
|
6
|
-
module Parsing
|
7
|
-
class ANDNode < CompositeParseNode
|
8
|
-
attr_reader :rule
|
9
|
-
attr_reader :alt_index
|
10
|
-
|
11
|
-
def initialize(anEItem, rank)
|
12
|
-
@rule = WeakRef.new(anEItem.dotted_item.rule)
|
13
|
-
@alt_index = anEItem.dotted_item.alt_index
|
14
|
-
upper_bound = rank
|
15
|
-
super(anEItem.origin, upper_bound, rule.rhs[alt_index].size)
|
16
|
-
end
|
17
|
-
|
18
|
-
def add_child(child_node, index)
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
end # module
|
56
|
-
end # module
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'composite_parse_node'
|
4
|
+
|
5
|
+
module Dendroid
|
6
|
+
module Parsing
|
7
|
+
class ANDNode < CompositeParseNode
|
8
|
+
attr_reader :rule
|
9
|
+
attr_reader :alt_index
|
10
|
+
|
11
|
+
def initialize(anEItem, rank)
|
12
|
+
@rule = WeakRef.new(anEItem.dotted_item.rule)
|
13
|
+
@alt_index = anEItem.dotted_item.alt_index
|
14
|
+
upper_bound = rank
|
15
|
+
super(anEItem.origin, upper_bound, rule.rhs[alt_index].size)
|
16
|
+
end
|
17
|
+
|
18
|
+
def add_child(child_node, index)
|
19
|
+
raise StandardError unless children[index].nil? # Is slot available?
|
20
|
+
|
21
|
+
super(child_node, index)
|
22
|
+
end
|
23
|
+
|
24
|
+
def match(anEItem)
|
25
|
+
return false if range[0] != anEItem.origin
|
26
|
+
|
27
|
+
dotted = anEItem.dotted_item
|
28
|
+
same_rule = (rule.lhs == dotted.rule.lhs) && (alt_index == dotted.alt_index)
|
29
|
+
return false unless same_rule
|
30
|
+
|
31
|
+
dotted.initial_pos? ? true : partial?
|
32
|
+
end
|
33
|
+
|
34
|
+
def expecting?(symbol, position)
|
35
|
+
symb_seq = rule.rhs[alt_index]
|
36
|
+
symb_seq[position] == symbol
|
37
|
+
end
|
38
|
+
|
39
|
+
def partial?
|
40
|
+
children.any?(&:nil?)
|
41
|
+
end
|
42
|
+
|
43
|
+
def to_s
|
44
|
+
"#{rule.lhs} => #{rule.rhs[alt_index]} #{range}"
|
45
|
+
end
|
46
|
+
|
47
|
+
# Part of the 'visitee' role in Visitor design pattern.
|
48
|
+
# @param aVisitor[ParseTreeVisitor] the visitor
|
49
|
+
def accept(aVisitor)
|
50
|
+
aVisitor.visit_and_node(self)
|
51
|
+
end
|
52
|
+
end # class
|
53
|
+
end # module
|
54
|
+
end # module
|