dendroid 0.2.00 → 0.2.01

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bb2bba5cca25fb6a95bfa820891edb4882595062be6a3a439a0fee8c2515efb1
4
- data.tar.gz: 52643afdeded2e2944e93124267c009d6c496ab6d645d4878087072f24685e67
3
+ metadata.gz: 8a6ce20fae6c5c7d07a9995e8adf97affcddc654edd7c72ff82b63b7fab42aff
4
+ data.tar.gz: e3b5dbf5ebc3468a11349a809914fd0178603f926f74a36f8848f128916f9b5f
5
5
  SHA512:
6
- metadata.gz: 47db9f266723dc8d292bdc8cbb7006bd8e9da8471bd89e933312231bf26ba90e5c40e7bf3e6eead665918a968f476705936507b66b753debde18391a4fa6936a
7
- data.tar.gz: 2fdc6b316b2f250644818af70ea40bf68c263776fcbb751ac2b05e03555194a3d86344f28574d3d6efd1a303f0d89c619f7ac199d57468c5e8eddff9f397f123
6
+ metadata.gz: 865063f7b0f5234871956ec35fb2cf9279804cef999d1d8041a9dde92776c3fae8d96b2aeb56bf3f1b8889a10348eef33f39e8321d8c47cd8a8db6c8a4d637f1
7
+ data.tar.gz: 83f74f28495fe104c7ea9ed7e58031017636b0eedf090ab19175bb5d5e5281329307c7e06fb7e47c1f43705bfb1b65a42c441a84b352de527bf4470212862572
data/.rubocop.yml CHANGED
@@ -37,3 +37,6 @@ Style/AccessorGrouping:
37
37
 
38
38
  Style/CommentedKeyword:
39
39
  Enabled: false
40
+
41
+ Style/ParallelAssignment:
42
+ Enabled: false
data/CHANGELOG.md CHANGED
@@ -2,6 +2,9 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [0.2.01] - 2023-12-17
6
+ Code re-styling to fix most Rubocop offenses.
7
+
5
8
  ## [0.2.00] - 2023-12-16
6
9
  Version bump: Very crude parser implementation (generate shared parse forests in case of ambiguity).
7
10
 
@@ -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 = (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
+ # 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
- # 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
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
@@ -87,7 +87,6 @@ module Dendroid
87
87
  position == rule.alternatives[alt_index].size ? :completed : :partial
88
88
  end
89
89
 
90
-
91
90
  # Return the symbol right after the dot (if any)
92
91
  # @return [Dendroid::Syntax::GrmSymbol, NilClass]
93
92
  def next_symbol
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative '../grm_analysis/rule_items.rb'
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,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'dotted_item.rb'
3
+ require_relative 'dotted_item'
4
4
 
5
5
  module Dendroid
6
6
  module GrmAnalysis
@@ -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
- 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
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