dendroid 0.1.00 → 0.2.00
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 +3 -0
- data/lib/dendroid/formatters/ascii_tree.rb +142 -0
- data/lib/dendroid/formatters/base_formatter.rb +25 -0
- data/lib/dendroid/formatters/bracket_notation.rb +50 -0
- data/lib/dendroid/grm_analysis/dotted_item.rb +46 -30
- data/lib/dendroid/grm_analysis/grm_analyzer.rb +2 -4
- data/lib/dendroid/grm_analysis/{choice_items.rb → rule_items.rb} +10 -10
- data/lib/dendroid/grm_dsl/base_grm_builder.rb +3 -4
- data/lib/dendroid/parsing/and_node.rb +56 -0
- data/lib/dendroid/parsing/chart_walker.rb +293 -0
- data/lib/dendroid/parsing/composite_parse_node.rb +21 -0
- data/lib/dendroid/parsing/empty_rule_node.rb +28 -0
- data/lib/dendroid/parsing/or_node.rb +51 -0
- data/lib/dendroid/parsing/parse_node.rb +26 -0
- data/lib/dendroid/parsing/parse_tree_visitor.rb +127 -0
- data/lib/dendroid/parsing/parser.rb +185 -0
- data/lib/dendroid/parsing/terminal_node.rb +32 -0
- data/lib/dendroid/parsing/walk_progress.rb +117 -0
- data/lib/dendroid/recognizer/chart.rb +8 -0
- data/lib/dendroid/recognizer/e_item.rb +21 -2
- data/lib/dendroid/recognizer/item_set.rb +7 -2
- data/lib/dendroid/recognizer/recognizer.rb +33 -20
- data/lib/dendroid/syntax/grammar.rb +1 -1
- data/lib/dendroid/syntax/rule.rb +71 -13
- data/spec/dendroid/grm_analysis/dotted_item_spec.rb +59 -47
- data/spec/dendroid/grm_analysis/{choice_items_spec.rb → rule_items_spec.rb} +5 -6
- data/spec/dendroid/parsing/chart_walker_spec.rb +223 -0
- data/spec/dendroid/parsing/terminal_node_spec.rb +36 -0
- data/spec/dendroid/recognizer/e_item_spec.rb +5 -5
- data/spec/dendroid/recognizer/item_set_spec.rb +16 -8
- data/spec/dendroid/recognizer/recognizer_spec.rb +57 -5
- data/spec/dendroid/support/sample_grammars.rb +2 -0
- data/spec/dendroid/syntax/grammar_spec.rb +16 -21
- data/spec/dendroid/syntax/rule_spec.rb +56 -7
- data/version.txt +1 -1
- metadata +20 -13
- data/lib/dendroid/grm_analysis/alternative_item.rb +0 -70
- data/lib/dendroid/grm_analysis/production_items.rb +0 -55
- data/lib/dendroid/syntax/choice.rb +0 -95
- data/lib/dendroid/syntax/production.rb +0 -82
- data/spec/dendroid/grm_analysis/alternative_item_spec.rb +0 -12
- data/spec/dendroid/grm_analysis/production_items_spec.rb +0 -68
- data/spec/dendroid/syntax/choice_spec.rb +0 -68
- data/spec/dendroid/syntax/production_spec.rb +0 -92
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bb2bba5cca25fb6a95bfa820891edb4882595062be6a3a439a0fee8c2515efb1
|
4
|
+
data.tar.gz: 52643afdeded2e2944e93124267c009d6c496ab6d645d4878087072f24685e67
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 47db9f266723dc8d292bdc8cbb7006bd8e9da8471bd89e933312231bf26ba90e5c40e7bf3e6eead665918a968f476705936507b66b753debde18391a4fa6936a
|
7
|
+
data.tar.gz: 2fdc6b316b2f250644818af70ea40bf68c263776fcbb751ac2b05e03555194a3d86344f28574d3d6efd1a303f0d89c619f7ac199d57468c5e8eddff9f397f123
|
data/CHANGELOG.md
CHANGED
@@ -0,0 +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 = (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
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
|
4
|
+
# Superclass for parse tree formatters.
|
5
|
+
class BaseFormatter
|
6
|
+
# The IO output stream in which the formatter's result will be sent.
|
7
|
+
# @return [IO] The output stream for the formatter.
|
8
|
+
attr_reader(:output)
|
9
|
+
|
10
|
+
# Constructor.
|
11
|
+
# @param anIO [IO] an output IO where the formatter's result will
|
12
|
+
# be placed.
|
13
|
+
def initialize(anIO)
|
14
|
+
@output = anIO
|
15
|
+
end
|
16
|
+
|
17
|
+
# Given a parse tree visitor, perform the visit
|
18
|
+
# and render the visit events in the output stream.
|
19
|
+
# @param aVisitor [ParseTreeVisitor]
|
20
|
+
def render(aVisitor)
|
21
|
+
aVisitor.subscribe(self)
|
22
|
+
aVisitor.start
|
23
|
+
aVisitor.unsubscribe(self)
|
24
|
+
end
|
25
|
+
end # class
|
@@ -0,0 +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,8 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'weakref'
|
4
|
+
|
3
5
|
module Dendroid
|
4
|
-
# This module contains classes that from the analysis of grammar rules help to build objects
|
5
|
-
# needed by a recognizer or a parser for the language.
|
6
6
|
module GrmAnalysis
|
7
7
|
# For a given production rule, a dotted item represents a recognition state.
|
8
8
|
# The dot partitions the rhs of the rule in two parts:
|
@@ -20,42 +20,40 @@ module Dendroid
|
|
20
20
|
# An item with a dot in front of a terminal is called a shift item.
|
21
21
|
# An item with the dot not at the beginning is sometimes referred to as a kernel item
|
22
22
|
class DottedItem
|
23
|
-
#
|
23
|
+
# (Weak) reference to the production rule
|
24
24
|
# @return [Dendroid::Syntax::Production]
|
25
25
|
attr_reader :rule
|
26
26
|
|
27
27
|
# @return [Integer] the dot position
|
28
28
|
attr_reader :position
|
29
29
|
|
30
|
+
# @return [Integer] the alternative number
|
31
|
+
attr_reader :alt_index
|
32
|
+
|
30
33
|
# Constructor.
|
31
|
-
# @param
|
34
|
+
# @param aChoice [Dendroid::Syntax::Rule]
|
32
35
|
# @param aPosition [Integer] Position of the dot in rhs of production.
|
33
|
-
|
34
|
-
|
36
|
+
# @param index [Integer] the rank of the alternative at hand
|
37
|
+
def initialize(aChoice, aPosition, index)
|
38
|
+
@alt_index = index
|
39
|
+
@rule = WeakRef.new(aChoice)
|
35
40
|
@position = valid_position(aPosition)
|
36
41
|
end
|
37
42
|
|
38
|
-
# Return a String representation of the
|
43
|
+
# Return a String representation of the alternative item.
|
39
44
|
# @return [String]
|
40
45
|
def to_s
|
41
|
-
rhs_names = rule.
|
46
|
+
rhs_names = rule.alternatives[alt_index].members.map(&:to_s)
|
42
47
|
dotted_rhs = rhs_names.insert(position, '.')
|
43
48
|
"#{rule.head} => #{dotted_rhs.join(' ')}"
|
44
49
|
end
|
45
50
|
|
46
|
-
|
51
|
+
alias inspect to_s
|
52
|
+
|
53
|
+
# Indicate whether the rhs of the alternative is empty
|
47
54
|
# @return [Boolean]
|
48
55
|
def empty?
|
49
|
-
rule.empty?
|
50
|
-
end
|
51
|
-
|
52
|
-
# Terminology inspired from Luger's book
|
53
|
-
# @return [Symbol] one of: :initial, :initial_and_completed, :partial, :completed
|
54
|
-
def state
|
55
|
-
return :initial_and_completed if empty?
|
56
|
-
return :initial if position.zero?
|
57
|
-
|
58
|
-
position == rule.body.size ? :completed : :partial
|
56
|
+
rule.alternatives[alt_index].empty?
|
59
57
|
end
|
60
58
|
|
61
59
|
# Indicate whether the dot is at the start of rhs
|
@@ -64,28 +62,46 @@ module Dendroid
|
|
64
62
|
position.zero? || empty?
|
65
63
|
end
|
66
64
|
|
67
|
-
# Indicate
|
65
|
+
# Indicate the dot isn't at start nor at end position
|
66
|
+
# @return [Boolean]
|
67
|
+
def intermediate_pos?
|
68
|
+
return false if empty? || position.zero?
|
69
|
+
|
70
|
+
position < rule.alternatives[alt_index].size
|
71
|
+
end
|
72
|
+
|
73
|
+
# Indicate whether the dot is at the start of rhs
|
68
74
|
# @return [Boolean]
|
69
75
|
def final_pos?
|
70
|
-
empty? || position == rule.
|
76
|
+
empty? || position == rule.alternatives[alt_index].size
|
71
77
|
end
|
72
78
|
|
73
79
|
alias completed? final_pos?
|
74
80
|
|
75
|
-
#
|
76
|
-
# @return [
|
77
|
-
def
|
78
|
-
return
|
81
|
+
# Terminology inspired from Luger's book
|
82
|
+
# @return [Symbol] one of: :initial, :initial_and_completed, :partial, :completed
|
83
|
+
def state
|
84
|
+
return :initial_and_completed if empty?
|
85
|
+
return :initial if position.zero?
|
79
86
|
|
80
|
-
position
|
87
|
+
position == rule.alternatives[alt_index].size ? :completed : :partial
|
81
88
|
end
|
82
89
|
|
90
|
+
|
83
91
|
# Return the symbol right after the dot (if any)
|
84
92
|
# @return [Dendroid::Syntax::GrmSymbol, NilClass]
|
85
93
|
def next_symbol
|
86
94
|
return nil if empty? || completed?
|
87
95
|
|
88
|
-
rule.
|
96
|
+
rule.alternatives[alt_index].members[position]
|
97
|
+
end
|
98
|
+
|
99
|
+
# Return the symbol right before the dot (if any)
|
100
|
+
# @return [Dendroid::Syntax::GrmSymbol, NilClass]
|
101
|
+
def prev_symbol
|
102
|
+
return nil if empty? || position.zero?
|
103
|
+
|
104
|
+
rule.alternatives[alt_index].members[position - 1]
|
89
105
|
end
|
90
106
|
|
91
107
|
# Check whether the given symbol is the same as after the dot.
|
@@ -99,7 +115,7 @@ module Dendroid
|
|
99
115
|
end
|
100
116
|
|
101
117
|
# Check whether the dotted item is a shift item.
|
102
|
-
# In other words, it expects a terminal to be next symbol
|
118
|
+
# In other words, it expects a terminal to be the next symbol
|
103
119
|
# @return [Boolean]
|
104
120
|
def pre_scan?
|
105
121
|
next_symbol&.terminal?
|
@@ -112,13 +128,13 @@ module Dendroid
|
|
112
128
|
def ==(other)
|
113
129
|
return true if eql?(other)
|
114
130
|
|
115
|
-
(position == other.position) && rule.eql?(other.rule)
|
131
|
+
(position == other.position) && rule.eql?(other.rule) && (alt_index == other.alt_index)
|
116
132
|
end
|
117
133
|
|
118
134
|
private
|
119
135
|
|
120
136
|
def valid_position(aPosition)
|
121
|
-
raise StandardError if aPosition.negative? || aPosition > rule.
|
137
|
+
raise StandardError if aPosition.negative? || aPosition > rule.alternatives[alt_index].size
|
122
138
|
|
123
139
|
aPosition
|
124
140
|
end
|
@@ -1,7 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative '../grm_analysis/
|
4
|
-
require_relative '../grm_analysis/choice_items'
|
3
|
+
require_relative '../grm_analysis/rule_items.rb'
|
5
4
|
|
6
5
|
module Dendroid
|
7
6
|
module GrmAnalysis
|
@@ -62,8 +61,7 @@ module Dendroid
|
|
62
61
|
|
63
62
|
def build_dotted_items
|
64
63
|
grammar.rules.each do |prod|
|
65
|
-
|
66
|
-
prod.extend(mixin)
|
64
|
+
prod.extend(RuleItems)
|
67
65
|
prod.build_items
|
68
66
|
rule_items = prod.items.flatten
|
69
67
|
items.concat(rule_items)
|
@@ -1,24 +1,24 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative '
|
3
|
+
require_relative 'dotted_item.rb'
|
4
4
|
|
5
5
|
module Dendroid
|
6
6
|
module GrmAnalysis
|
7
7
|
# Mix-in module for extending the Syntax::Choice class
|
8
8
|
# with dotted items manipulation methods
|
9
|
-
module
|
9
|
+
module RuleItems
|
10
10
|
# Build the alternative items for this choice and assign them
|
11
11
|
# to the `items` attributes
|
12
|
-
# @return [Array<Array<GrmAnalysis::
|
12
|
+
# @return [Array<Array<GrmAnalysis::Dottedtem>>]
|
13
13
|
def build_items
|
14
14
|
# AlternativeItem
|
15
15
|
@items = Array.new(alternatives.size) { |_| [] }
|
16
16
|
alternatives.each_with_index do |alt_seq, index|
|
17
17
|
if alt_seq.empty?
|
18
|
-
@items[index] <<
|
18
|
+
@items[index] << DottedItem.new(self, 0, index)
|
19
19
|
else
|
20
20
|
(0..alt_seq.size).each do |pos|
|
21
|
-
@items[index] <<
|
21
|
+
@items[index] << DottedItem.new(self, pos, index)
|
22
22
|
end
|
23
23
|
end
|
24
24
|
end
|
@@ -26,29 +26,29 @@ module Dendroid
|
|
26
26
|
|
27
27
|
# Read accessor for the `items` attribute.
|
28
28
|
# Return the dotted items for this production
|
29
|
-
# @return [Array<Array<GrmAnalysis::
|
29
|
+
# @return [Array<Array<GrmAnalysis::Dottedtem>>]
|
30
30
|
def items
|
31
31
|
@items
|
32
32
|
end
|
33
33
|
|
34
34
|
# Return the predicted items (i.e. the alternative items with the dot at start)
|
35
35
|
# for this choice.
|
36
|
-
# @return [Array<GrmAnalysis::
|
36
|
+
# @return [Array<GrmAnalysis::Dottedtem>]
|
37
37
|
def predicted_items
|
38
38
|
@items.map(&:first)
|
39
39
|
end
|
40
40
|
|
41
41
|
# Return the reduce items (i.e. the alternative items with the dot at end)
|
42
42
|
# for this choice.
|
43
|
-
# @return [Array<GrmAnalysis::
|
43
|
+
# @return [Array<GrmAnalysis::Dottedtem>]
|
44
44
|
def reduce_items
|
45
45
|
@items.map(&:last)
|
46
46
|
end
|
47
47
|
|
48
48
|
# Return the next item given the provided item.
|
49
49
|
# In other words, advance the dot by one position.
|
50
|
-
# @param anItem [GrmAnalysis::
|
51
|
-
# @return [GrmAnalysis::
|
50
|
+
# @param anItem [GrmAnalysis::Dottedtem]
|
51
|
+
# @return [GrmAnalysis::Dottedtem|NilClass]
|
52
52
|
def next_item(anItem)
|
53
53
|
items_arr = items[anItem.alt_index]
|
54
54
|
return nil if anItem == items_arr.last
|
@@ -3,8 +3,7 @@
|
|
3
3
|
require_relative '..\syntax\terminal'
|
4
4
|
require_relative '..\syntax\non_terminal'
|
5
5
|
require_relative '..\syntax\symbol_seq'
|
6
|
-
require_relative '..\syntax\
|
7
|
-
require_relative '..\syntax\choice'
|
6
|
+
require_relative '..\syntax\rule'
|
8
7
|
require_relative '..\syntax\grammar'
|
9
8
|
|
10
9
|
module Dendroid
|
@@ -90,10 +89,10 @@ module Dendroid
|
|
90
89
|
raw_rhs = productionRuleRepr.values.first
|
91
90
|
|
92
91
|
if raw_rhs.is_a? String
|
93
|
-
new_prod = Dendroid::Syntax::
|
92
|
+
new_prod = Dendroid::Syntax::Rule.new(lhs, [build_symbol_seq(raw_rhs)])
|
94
93
|
else
|
95
94
|
rhs = raw_rhs.map { |raw| build_symbol_seq(raw) }
|
96
|
-
new_prod = Dendroid::Syntax::
|
95
|
+
new_prod = Dendroid::Syntax::Rule.new(lhs, rhs)
|
97
96
|
end
|
98
97
|
rules << new_prod
|
99
98
|
new_prod
|
@@ -0,0 +1,56 @@
|
|
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
|
+
if children[index].nil? # Is slot available?
|
20
|
+
super(child_node, index)
|
21
|
+
else
|
22
|
+
raise StandardError
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def match(anEItem)
|
27
|
+
return false if range[0] != anEItem.origin
|
28
|
+
|
29
|
+
dotted = anEItem.dotted_item
|
30
|
+
same_rule = (rule.lhs == dotted.rule.lhs) && (alt_index == dotted.alt_index)
|
31
|
+
return false unless same_rule
|
32
|
+
|
33
|
+
dotted.initial_pos? ? true : partial?
|
34
|
+
end
|
35
|
+
|
36
|
+
def expecting?(symbol, position)
|
37
|
+
symb_seq = rule.rhs[alt_index]
|
38
|
+
symb_seq[position] == symbol
|
39
|
+
end
|
40
|
+
|
41
|
+
def partial?
|
42
|
+
children.any?(&:nil?)
|
43
|
+
end
|
44
|
+
|
45
|
+
def to_s
|
46
|
+
"#{rule.lhs} => #{rule.rhs[alt_index]} #{range}"
|
47
|
+
end
|
48
|
+
|
49
|
+
# Part of the 'visitee' role in Visitor design pattern.
|
50
|
+
# @param aVisitor[ParseTreeVisitor] the visitor
|
51
|
+
def accept(aVisitor)
|
52
|
+
aVisitor.visit_and_node(self)
|
53
|
+
end
|
54
|
+
end # class
|
55
|
+
end # module
|
56
|
+
end # module
|