rley 0.7.08 → 0.8.03
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 +29 -5
- data/CHANGELOG.md +28 -4
- data/README.md +4 -5
- data/examples/NLP/nano_eng/nano_en_demo.rb +7 -11
- data/examples/NLP/nano_eng/nano_grammar.rb +18 -18
- data/examples/data_formats/JSON/json_ast_builder.rb +9 -18
- data/examples/data_formats/JSON/json_demo.rb +1 -2
- data/examples/data_formats/JSON/json_grammar.rb +11 -11
- data/examples/general/calc_iter1/calc_grammar.rb +5 -4
- data/examples/general/calc_iter2/calc_grammar.rb +9 -9
- data/examples/general/left.rb +1 -1
- data/examples/general/right.rb +1 -1
- data/lib/rley/base/dotted_item.rb +5 -0
- data/lib/rley/base/grm_items_builder.rb +6 -0
- data/lib/rley/constants.rb +1 -1
- data/lib/rley/engine.rb +2 -2
- data/lib/rley/interface.rb +16 -0
- data/lib/rley/notation/all_notation_nodes.rb +4 -0
- data/lib/rley/notation/ast_builder.rb +185 -0
- data/lib/rley/notation/ast_node.rb +44 -0
- data/lib/rley/notation/ast_visitor.rb +115 -0
- data/lib/rley/notation/grammar.rb +49 -0
- data/lib/rley/notation/grammar_builder.rb +505 -0
- data/lib/rley/notation/grouping_node.rb +23 -0
- data/lib/rley/notation/parser.rb +56 -0
- data/lib/rley/notation/sequence_node.rb +35 -0
- data/lib/rley/notation/symbol_node.rb +29 -0
- data/lib/rley/notation/tokenizer.rb +180 -0
- data/lib/rley/parse_rep/ast_base_builder.rb +44 -0
- data/lib/rley/parser/gfg_chart.rb +101 -6
- data/lib/rley/parser/gfg_earley_parser.rb +1 -1
- data/lib/rley/parser/gfg_parsing.rb +5 -3
- data/lib/rley/parser/parse_entry_set.rb +1 -1
- data/lib/rley/syntax/{grammar_builder.rb → base_grammar_builder.rb} +53 -15
- data/lib/rley/syntax/grm_symbol.rb +1 -1
- data/lib/rley/syntax/match_closest.rb +43 -0
- data/lib/rley/syntax/production.rb +6 -0
- data/lib/rley.rb +1 -1
- data/spec/rley/engine_spec.rb +6 -6
- data/spec/rley/gfg/grm_flow_graph_spec.rb +2 -2
- data/spec/rley/notation/grammar_builder_spec.rb +302 -0
- data/spec/rley/notation/parser_spec.rb +183 -0
- data/spec/rley/notation/tokenizer_spec.rb +364 -0
- data/spec/rley/parse_rep/ast_builder_spec.rb +0 -1
- data/spec/rley/parse_rep/groucho_spec.rb +1 -1
- data/spec/rley/parse_rep/parse_forest_builder_spec.rb +1 -1
- data/spec/rley/parse_rep/parse_forest_factory_spec.rb +2 -2
- data/spec/rley/parse_rep/parse_tree_factory_spec.rb +1 -1
- data/spec/rley/parser/dangling_else_spec.rb +447 -0
- data/spec/rley/parser/gfg_earley_parser_spec.rb +118 -10
- data/spec/rley/parser/gfg_parsing_spec.rb +2 -1
- data/spec/rley/parser/parse_walker_factory_spec.rb +2 -2
- data/spec/rley/support/ambiguous_grammar_helper.rb +2 -2
- data/spec/rley/support/grammar_abc_helper.rb +2 -2
- data/spec/rley/support/grammar_ambig01_helper.rb +2 -2
- data/spec/rley/support/grammar_arr_int_helper.rb +2 -2
- data/spec/rley/support/grammar_b_expr_helper.rb +2 -2
- data/spec/rley/support/grammar_int_seq_helper.rb +51 -0
- data/spec/rley/support/grammar_l0_helper.rb +2 -2
- data/spec/rley/support/grammar_pb_helper.rb +2 -2
- data/spec/rley/support/grammar_sppf_helper.rb +2 -2
- data/spec/rley/syntax/{grammar_builder_spec.rb → base_grammar_builder_spec.rb} +29 -11
- data/spec/rley/syntax/match_closest_spec.rb +46 -0
- data/spec/rley/syntax/production_spec.rb +4 -0
- metadata +29 -14
- data/lib/rley/parser/parse_state.rb +0 -78
- data/lib/rley/parser/parse_state_tracker.rb +0 -59
- data/lib/rley/parser/state_set.rb +0 -100
- data/spec/rley/parser/parse_state_spec.rb +0 -125
- data/spec/rley/parser/parse_tracer_spec.rb +0 -200
- data/spec/rley/parser/state_set_spec.rb +0 -130
data/examples/general/left.rb
CHANGED
data/examples/general/right.rb
CHANGED
@@ -28,6 +28,11 @@ module Rley # This module is used as a namespace
|
|
28
28
|
# @return [Integer]
|
29
29
|
attr_reader :position
|
30
30
|
|
31
|
+
# A possible constraint between symbol on left of dot and
|
32
|
+
# the closest preceding given terminal
|
33
|
+
# @return [NilClass, Syntax::MatchClosest]
|
34
|
+
attr_accessor :constraint
|
35
|
+
|
31
36
|
# @param aProduction [Syntax::Production]
|
32
37
|
# @param aPosition [Integer] Position of the dot in rhs of production.
|
33
38
|
def initialize(aProduction, aPosition)
|
@@ -12,12 +12,18 @@ module Rley # This module is used as a namespace
|
|
12
12
|
def build_dotted_items(aGrammar)
|
13
13
|
items = []
|
14
14
|
aGrammar.rules.each do |prod|
|
15
|
+
index_prev = items.size
|
15
16
|
rhs_size = prod.rhs.size
|
16
17
|
if rhs_size.zero?
|
17
18
|
items << DottedItem.new(prod, 0)
|
18
19
|
else
|
19
20
|
items += (0..rhs_size).map { |i| DottedItem.new(prod, i) }
|
20
21
|
end
|
22
|
+
|
23
|
+
prod.constraints.each do |cs|
|
24
|
+
# Attach constraint to dotted item n + 1
|
25
|
+
items[index_prev + cs.idx_symbol + 1].constraint = cs
|
26
|
+
end
|
21
27
|
end
|
22
28
|
|
23
29
|
return items
|
data/lib/rley/constants.rb
CHANGED
data/lib/rley/engine.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative './
|
3
|
+
require_relative './notation/grammar_builder'
|
4
4
|
require_relative './parser/gfg_earley_parser'
|
5
5
|
require_relative './parse_tree_visitor'
|
6
6
|
require_relative './parse_forest_visitor'
|
@@ -61,7 +61,7 @@ module Rley # This module is used as a namespace
|
|
61
61
|
# add_production('elements' => 'INTEGER')
|
62
62
|
# end
|
63
63
|
def build_grammar(&aBlock)
|
64
|
-
builder = Rley::
|
64
|
+
builder = Rley::Notation::GrammarBuilder.new(&aBlock)
|
65
65
|
@grammar = builder.grammar
|
66
66
|
end
|
67
67
|
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative './notation/grammar_builder'
|
4
|
+
|
5
|
+
module Rley # Module used as a namespace
|
6
|
+
# Factory method.
|
7
|
+
# A grammar builder constructs a Rley grammar piece by piece
|
8
|
+
# from DSL instructions in a provided code block.
|
9
|
+
# @param aBlock [Proc] a code block
|
10
|
+
# @return [Rley::Notation::GrammarBuilder] An object that builds a grammar.
|
11
|
+
def self.grammar_builder(&aBlock)
|
12
|
+
Rley::Notation::GrammarBuilder.new(&aBlock)
|
13
|
+
end
|
14
|
+
end # module
|
15
|
+
|
16
|
+
# End of file
|
@@ -0,0 +1,185 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../parse_rep/ast_base_builder'
|
4
|
+
require_relative '../engine'
|
5
|
+
require_relative 'all_notation_nodes'
|
6
|
+
|
7
|
+
module Rley
|
8
|
+
module Notation
|
9
|
+
# The purpose of ASTBuilder is to build piece by piece an AST
|
10
|
+
# (Abstract Syntax Tree) from a sequence of input tokens and
|
11
|
+
# visit events produced by walking over a GFGParsing object.
|
12
|
+
class ASTBuilder < Rley::ParseRep::ASTBaseBuilder
|
13
|
+
unless defined?(Name2special)
|
14
|
+
# Mapping Token name => operator | separator | delimiter characters
|
15
|
+
# @return [Hash{String => String}]
|
16
|
+
Name2special = {
|
17
|
+
'COMMA' => ',',
|
18
|
+
'ELLIPSIS' => '..',
|
19
|
+
'LEFT_BRACE' => '{',
|
20
|
+
'LEFT_PAREN' => '(',
|
21
|
+
'PLUS' => '+',
|
22
|
+
'QUESTION_MARK' => '?',
|
23
|
+
'RIGHT_BRACE' => '}',
|
24
|
+
'RIGHT_PAREN' => ')',
|
25
|
+
'STAR' => '*'
|
26
|
+
}.freeze
|
27
|
+
end
|
28
|
+
|
29
|
+
protected
|
30
|
+
|
31
|
+
def terminal2node
|
32
|
+
Terminal2NodeClass
|
33
|
+
end
|
34
|
+
|
35
|
+
# Method override
|
36
|
+
def new_leaf_node(_production, _terminal, aTokenPosition, aToken)
|
37
|
+
Rley::PTree::TerminalNode.new(aToken, aTokenPosition)
|
38
|
+
end
|
39
|
+
|
40
|
+
# Factory method for creating a parent node object.
|
41
|
+
# @param aProduction [Production] Production rule
|
42
|
+
# @param aRange [Range] Range of tokens matched by the rule
|
43
|
+
# @param theTokens [Array] The input tokens
|
44
|
+
# @param theChildren [Array] Children nodes (one per rhs symbol)
|
45
|
+
def new_parent_node(aProduction, aRange, theTokens, theChildren)
|
46
|
+
mth_name = method_name(aProduction.name)
|
47
|
+
if respond_to?(mth_name, true)
|
48
|
+
node = send(mth_name, aProduction, aRange, theTokens, theChildren)
|
49
|
+
else
|
50
|
+
# Default action...
|
51
|
+
node = case aProduction.rhs.size
|
52
|
+
when 0
|
53
|
+
return_epsilon(aRange, theTokens, theChildren)
|
54
|
+
when 1
|
55
|
+
return_first_child(aRange, theTokens, theChildren)
|
56
|
+
else
|
57
|
+
node = Rley::PTree::NonTerminalNode.new(aProduction.lhs, aRange)
|
58
|
+
theChildren&.reverse_each do |child|
|
59
|
+
node.add_subnode(child) if child
|
60
|
+
end
|
61
|
+
|
62
|
+
node
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
node
|
67
|
+
end
|
68
|
+
|
69
|
+
# Return the AST node corresponding to the second symbol in the rhs
|
70
|
+
def reduce_to_2nd_symbol(_production, _range, _tokens, theChildren)
|
71
|
+
theChildren[1]
|
72
|
+
end
|
73
|
+
|
74
|
+
#####################################
|
75
|
+
# SEMANTIC ACTIONS
|
76
|
+
#####################################
|
77
|
+
|
78
|
+
# rule('rhs' => 'member_seq').tag 'sequence'
|
79
|
+
def reduce_sequence(_production, _range, _tokens, theChildren)
|
80
|
+
if theChildren[0].size == 1
|
81
|
+
theChildren[0].first
|
82
|
+
else
|
83
|
+
SequenceNode.new(theChildren[0].first.position, theChildren[0], nil)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
# rule('member_seq' => 'member_seq member').tag 'more_members'
|
88
|
+
def reduce_more_members(_production, _range, _tokens, theChildren)
|
89
|
+
theChildren[0] << theChildren[1]
|
90
|
+
end
|
91
|
+
|
92
|
+
# rule('member_seq' => 'member')
|
93
|
+
def reduce_one_member(_production, _range, _tokens, theChildren)
|
94
|
+
[theChildren[0]]
|
95
|
+
end
|
96
|
+
|
97
|
+
# rule('strait_member' => 'base_member annotation')
|
98
|
+
def reduce_annotated_member(_production, _range, _tokens, theChildren)
|
99
|
+
theChildren[0].annotation = theChildren[1]
|
100
|
+
|
101
|
+
theChildren[0]
|
102
|
+
end
|
103
|
+
|
104
|
+
# rule('base_member' => 'SYMBOL')
|
105
|
+
def reduce_symbol(_production, _range, _tokens, theChildren)
|
106
|
+
SymbolNode.new(theChildren[0].token.position, theChildren[0].token.lexeme)
|
107
|
+
end
|
108
|
+
|
109
|
+
# rule('base_member' => 'LEFT_PAREN member_seq RIGHT_PAREN')
|
110
|
+
def reduce_grouping(_production, _range, tokens, theChildren)
|
111
|
+
if theChildren[1].size == 1
|
112
|
+
theChildren[1].first
|
113
|
+
else
|
114
|
+
rank = theChildren[0].range.high
|
115
|
+
pos = tokens[rank].position
|
116
|
+
GroupingNode.new(pos, theChildren[1], nil)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
# rule('quantified_member' => 'base_member quantifier')
|
121
|
+
def reduce_quantified_member(_production, _range, _tokens, theChildren)
|
122
|
+
theChildren[0].repetition = theChildren[1]
|
123
|
+
theChildren[0]
|
124
|
+
end
|
125
|
+
|
126
|
+
# rule('quantifier' => 'QUESTION_MARK')
|
127
|
+
def reduce_question_mark(_production, _range, _tokens, _theChildren)
|
128
|
+
:zero_or_one
|
129
|
+
end
|
130
|
+
|
131
|
+
# rule('quantifier' => 'STAR')
|
132
|
+
def reduce_star(_production, _range, _tokens, _theChildren)
|
133
|
+
:zero_or_more
|
134
|
+
end
|
135
|
+
|
136
|
+
# rule('quantifier' => 'PLUS')
|
137
|
+
def reduce_plus(_production, _range, _tokens, _theChildren)
|
138
|
+
:one_or_more
|
139
|
+
end
|
140
|
+
|
141
|
+
# rule('annotation' => 'LEFT_BRACE mapping RIGHT_BRACE').tag ''
|
142
|
+
def reduce_annotation(_production, _range, _tokens, theChildren)
|
143
|
+
theChildren[1]
|
144
|
+
end
|
145
|
+
|
146
|
+
# rule('mapping' => 'mapping COMMA key_value')
|
147
|
+
def reduce_more_pairs(_production, _range, _tokens, theChildren)
|
148
|
+
hsh = theChildren[0]
|
149
|
+
hsh[theChildren[2].first] = theChildren[2].last
|
150
|
+
|
151
|
+
hsh
|
152
|
+
end
|
153
|
+
|
154
|
+
# rule('mapping' => 'key_value').tag 'one_pair'
|
155
|
+
def reduce_one_pair(_production, _range, _tokens, theChildren)
|
156
|
+
{ theChildren[0].first => theChildren[0].last }
|
157
|
+
end
|
158
|
+
|
159
|
+
# rule('key_value' => 'KEY value')
|
160
|
+
def reduce_raw_pair(_production, _range, _tokens, theChildren)
|
161
|
+
key = theChildren[0].token.lexeme
|
162
|
+
value = if theChildren[1].kind_of?(Rley::PTree::TerminalNode)
|
163
|
+
theChildren[1].token.lexeme
|
164
|
+
else
|
165
|
+
theChildren[1]
|
166
|
+
end
|
167
|
+
[key, value]
|
168
|
+
end
|
169
|
+
|
170
|
+
# rule('range' => 'INT_LIT ELLIPSIS INT_LIT')
|
171
|
+
def reduce_bound_range(_production, _range, _tokens, theChildren)
|
172
|
+
low = theChildren[0].token.lexeme
|
173
|
+
high = theChildren[2].token.lexeme
|
174
|
+
case [low, high]
|
175
|
+
when %w[0 1]
|
176
|
+
:zero_or_one
|
177
|
+
when %w[1 1]
|
178
|
+
:exactly_one
|
179
|
+
else
|
180
|
+
Range.new(low.to_i, high.to_i)
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end # class
|
184
|
+
end # module
|
185
|
+
end # module
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Rley
|
4
|
+
module Notation
|
5
|
+
# Abstract class.
|
6
|
+
# Instances of its subclasses represent nodes of an abstract syntax tree
|
7
|
+
# that is the product of the parse of an input text.
|
8
|
+
class ASTNode
|
9
|
+
# @return [Rley::Lexical::Position] Position of the entry in the input stream.
|
10
|
+
attr_reader :position
|
11
|
+
|
12
|
+
# @return [Symbol]
|
13
|
+
attr_accessor :repetition
|
14
|
+
|
15
|
+
# @return [Hash]
|
16
|
+
attr_reader :annotation
|
17
|
+
|
18
|
+
# @param aPosition [Rley::Lexical::Position] Position of the entry in the input stream.
|
19
|
+
def initialize(aPosition)
|
20
|
+
@position = aPosition
|
21
|
+
@repetition = :exactly_one
|
22
|
+
@annotation = {}
|
23
|
+
end
|
24
|
+
|
25
|
+
def annotation=(aMapping)
|
26
|
+
repeat_key = 'repeat'
|
27
|
+
@repetition = aMapping.delete(repeat_key) if aMapping.include?(repeat_key)
|
28
|
+
@annotation = aMapping
|
29
|
+
end
|
30
|
+
|
31
|
+
# Notification that the parsing has successfully completed
|
32
|
+
def done!
|
33
|
+
# Default: do nothing ...
|
34
|
+
end
|
35
|
+
|
36
|
+
# Abstract method (must be overriden in subclasses).
|
37
|
+
# Part of the 'visitee' role in Visitor design pattern.
|
38
|
+
# @param _visitor [LoxxyTreeVisitor] the visitor
|
39
|
+
def accept(_visitor)
|
40
|
+
raise NotImplementedError
|
41
|
+
end
|
42
|
+
end # class
|
43
|
+
end # module
|
44
|
+
end # module
|
@@ -0,0 +1,115 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Rley
|
4
|
+
module Notation
|
5
|
+
class ASTVisitor
|
6
|
+
# Link to the top node to visit
|
7
|
+
attr_reader(:top)
|
8
|
+
|
9
|
+
# List of objects that subscribed to the visit event notification.
|
10
|
+
attr_reader(:subscribers)
|
11
|
+
|
12
|
+
# Build a visitor for the given top.
|
13
|
+
# @param aTop [Notation::ASTNode] the parse tree to visit.
|
14
|
+
def initialize(aTop)
|
15
|
+
raise StandardError if aTop.nil?
|
16
|
+
|
17
|
+
@top = aTop
|
18
|
+
@subscribers = []
|
19
|
+
end
|
20
|
+
|
21
|
+
# Add a subscriber for the visit event notifications.
|
22
|
+
# @param aSubscriber [Object]
|
23
|
+
def subscribe(aSubscriber)
|
24
|
+
subscribers << aSubscriber
|
25
|
+
end
|
26
|
+
|
27
|
+
# Remove the given object from the subscription list.
|
28
|
+
# The object won't be notified of visit events.
|
29
|
+
# @param aSubscriber [Object]
|
30
|
+
def unsubscribe(aSubscriber)
|
31
|
+
subscribers.delete_if { |entry| entry == aSubscriber }
|
32
|
+
end
|
33
|
+
|
34
|
+
# The signal to begin the visit of the top.
|
35
|
+
def start
|
36
|
+
top.accept(self)
|
37
|
+
end
|
38
|
+
|
39
|
+
# Visit event. The visitor is about to visit the ptree.
|
40
|
+
# @param aParseTree [Rley::PTree::ParseTree] the ptree to visit.
|
41
|
+
def start_visit_ptree(aParseTree)
|
42
|
+
broadcast(:before_ptree, aParseTree)
|
43
|
+
end
|
44
|
+
|
45
|
+
# Visit event. The visitor has completed the visit of the ptree.
|
46
|
+
# @param aParseTree [Rley::PTree::ParseTree] the visited ptree.
|
47
|
+
def end_visit_ptree(aParseTree)
|
48
|
+
broadcast(:after_ptree, aParseTree)
|
49
|
+
end
|
50
|
+
|
51
|
+
# Visit event. The visitor is about to visit a symbol node.
|
52
|
+
# @param aSymbolNode [Notation::SymbolNode] the symbol node to visit
|
53
|
+
def visit_symbol_node(aSymbolNode)
|
54
|
+
broadcast(:before_symbol_node, aSymbolNode, self)
|
55
|
+
broadcast(:after_symbol_node, aSymbolNode, self)
|
56
|
+
end
|
57
|
+
|
58
|
+
# Visit event. The visitor is about to visit a sequence node.
|
59
|
+
# @param aSequenceNode [Notation::SequenceNode] the sequence node to visit
|
60
|
+
def visit_sequence_node(aSequenceNode)
|
61
|
+
broadcast(:before_sequence_node, aSequenceNode, self)
|
62
|
+
traverse_subnodes(aSequenceNode)
|
63
|
+
broadcast(:after_sequence_node, aSequenceNode, self)
|
64
|
+
end
|
65
|
+
|
66
|
+
# Visit event. The visitor is about to visit a grouping node.
|
67
|
+
# @param aGroupingNode [Notation::GroupingNode] the grouping node to visit
|
68
|
+
def visit_grouping_node(aGroupingNode)
|
69
|
+
broadcast(:before_grouping_node, aGroupingNode, self)
|
70
|
+
traverse_subnodes(aGroupingNode) if aGroupingNode.repetition == :exactly_one
|
71
|
+
broadcast(:after_grouping_node, aGroupingNode, self)
|
72
|
+
end
|
73
|
+
|
74
|
+
private
|
75
|
+
|
76
|
+
# Visit event. The visitor is about to visit the subnodes of a non
|
77
|
+
# terminal node.
|
78
|
+
# @param aParentNode [Ast::LocCompoundExpr] the parent node.
|
79
|
+
def traverse_subnodes(aParentNode)
|
80
|
+
subnodes = aParentNode.subnodes
|
81
|
+
broadcast(:before_subnodes, aParentNode, subnodes)
|
82
|
+
|
83
|
+
# Let's proceed with the visit of subnodes
|
84
|
+
subnodes.each { |a_node| a_node.accept(self) }
|
85
|
+
|
86
|
+
broadcast(:after_subnodes, aParentNode, subnodes)
|
87
|
+
end
|
88
|
+
|
89
|
+
# Visit event. The visitor is about to visit one given subnode of a non
|
90
|
+
# terminal node.
|
91
|
+
# @param aParentNode [Ast::LocCompoundExpr] the parent node.
|
92
|
+
# @param index [integer] index of child subnode
|
93
|
+
def traverse_given_subnode(aParentNode, index)
|
94
|
+
subnode = aParentNode.subnodes[index]
|
95
|
+
broadcast(:before_given_subnode, aParentNode, subnode)
|
96
|
+
|
97
|
+
# Now, let's proceed with the visit of that subnode
|
98
|
+
subnode.accept(self)
|
99
|
+
|
100
|
+
broadcast(:after_given_subnode, aParentNode, subnode)
|
101
|
+
end
|
102
|
+
|
103
|
+
# Send a notification to all subscribers.
|
104
|
+
# @param msg [Symbol] event to notify
|
105
|
+
# @param args [Array] arguments of the notification.
|
106
|
+
def broadcast(msg, *args)
|
107
|
+
subscribers.each do |subscr|
|
108
|
+
next unless subscr.respond_to?(msg) || subscr.respond_to?(:accept_all)
|
109
|
+
|
110
|
+
subscr.send(msg, *args)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end # class
|
114
|
+
end # module
|
115
|
+
end # module
|