resyma 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.rspec +3 -0
- data/.rubocop.yml +31 -0
- data/CHANGELOG.md +5 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +69 -0
- data/LICENSE +674 -0
- data/README.md +167 -0
- data/Rakefile +8 -0
- data/lib/resyma/core/algorithm/engine.rb +189 -0
- data/lib/resyma/core/algorithm/matcher.rb +48 -0
- data/lib/resyma/core/algorithm/tuple.rb +25 -0
- data/lib/resyma/core/algorithm.rb +5 -0
- data/lib/resyma/core/automaton/builder.rb +78 -0
- data/lib/resyma/core/automaton/definition.rb +32 -0
- data/lib/resyma/core/automaton/epsilon_NFA.rb +115 -0
- data/lib/resyma/core/automaton/matchable.rb +16 -0
- data/lib/resyma/core/automaton/regexp.rb +175 -0
- data/lib/resyma/core/automaton/state.rb +22 -0
- data/lib/resyma/core/automaton/transition.rb +58 -0
- data/lib/resyma/core/automaton/visualize.rb +23 -0
- data/lib/resyma/core/automaton.rb +9 -0
- data/lib/resyma/core/parsetree/builder.rb +89 -0
- data/lib/resyma/core/parsetree/converter.rb +61 -0
- data/lib/resyma/core/parsetree/default_converter.rb +331 -0
- data/lib/resyma/core/parsetree/definition.rb +77 -0
- data/lib/resyma/core/parsetree/source.rb +73 -0
- data/lib/resyma/core/parsetree/traversal.rb +26 -0
- data/lib/resyma/core/parsetree.rb +8 -0
- data/lib/resyma/core/utilities.rb +30 -0
- data/lib/resyma/language.rb +290 -0
- data/lib/resyma/nise/date.rb +53 -0
- data/lib/resyma/nise/rubymoji.rb +13 -0
- data/lib/resyma/nise/toml.rb +63 -0
- data/lib/resyma/parsetree.rb +163 -0
- data/lib/resyma/program/automaton.rb +84 -0
- data/lib/resyma/program/parsetree.rb +79 -0
- data/lib/resyma/program/traverse.rb +77 -0
- data/lib/resyma/version.rb +5 -0
- data/lib/resyma.rb +12 -0
- data/resyma.gemspec +47 -0
- data/sig/resyma.rbs +4 -0
- metadata +184 -0
@@ -0,0 +1,331 @@
|
|
1
|
+
require "resyma/core/parsetree/definition"
|
2
|
+
require "resyma/core/parsetree/converter"
|
3
|
+
|
4
|
+
module Resyma
|
5
|
+
module Core
|
6
|
+
DEFAULT_CONVERTER = Converter.new
|
7
|
+
|
8
|
+
CONST_TOKEN_TABLE = {
|
9
|
+
"(" => :round_left,
|
10
|
+
")" => :round_right,
|
11
|
+
"begin" => :kwd_begin,
|
12
|
+
"end" => :kwd_end,
|
13
|
+
"," => :comma,
|
14
|
+
"[" => :square_left,
|
15
|
+
"]" => :square_right,
|
16
|
+
"*" => :star,
|
17
|
+
"**" => :star2,
|
18
|
+
":" => :colon,
|
19
|
+
"=>" => :arrow,
|
20
|
+
"{" => :curly_left,
|
21
|
+
"}" => :curly_right,
|
22
|
+
".." => :dot2,
|
23
|
+
"..." => :dot3,
|
24
|
+
"defined?" => :kwd_defined?,
|
25
|
+
"." => :dot,
|
26
|
+
"&." => :and_dot,
|
27
|
+
"do" => :kwd_do,
|
28
|
+
"&" => :ampersand,
|
29
|
+
"|" => :tube,
|
30
|
+
"=" => :eq,
|
31
|
+
"true" => :the_true,
|
32
|
+
"false" => :the_false,
|
33
|
+
"nil" => :the_nil
|
34
|
+
}.freeze
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
Resyma::Core::DEFAULT_CONVERTER.instance_eval do
|
39
|
+
|
40
|
+
def make_token(type, value, parent, index, ast)
|
41
|
+
Resyma::Core::ParseTree.new(type, [value], parent, index, true, ast)
|
42
|
+
end
|
43
|
+
|
44
|
+
def_fallback do |ast, parent, index|
|
45
|
+
make_token(:any, ast.type.to_s, parent, index, ast)
|
46
|
+
end
|
47
|
+
|
48
|
+
simple_literal = {
|
49
|
+
true: :the_true,
|
50
|
+
false: :the_false,
|
51
|
+
nil: :the_nil,
|
52
|
+
complex: :complex,
|
53
|
+
rational: :rational,
|
54
|
+
str: :str,
|
55
|
+
regexp: :regexp,
|
56
|
+
sym: :sym,
|
57
|
+
self: :the_self,
|
58
|
+
lvar: :id,
|
59
|
+
ivar: :ivar,
|
60
|
+
cvar: :cvar,
|
61
|
+
gvar: :gvar,
|
62
|
+
nth_ref: :nth_ref,
|
63
|
+
back_ref: :back_ref
|
64
|
+
}
|
65
|
+
|
66
|
+
def_rule simple_literal.keys do |ast, parent, index|
|
67
|
+
make_token simple_literal[ast.type], ast.loc.expression.source,
|
68
|
+
parent, index, ast
|
69
|
+
end
|
70
|
+
|
71
|
+
number_regexp = /^\s*(\+|-)?\s*([0-9.]+)\s*$/
|
72
|
+
def_rule %i[int float] do |ast, parent, index|
|
73
|
+
m = number_regexp.match(ast.loc.expression.source)
|
74
|
+
if m.nil?
|
75
|
+
raise Resyma::Core::ConversionError,
|
76
|
+
"Internal error: Number pattern [#{ast.loc.expression}] is invalid"
|
77
|
+
end
|
78
|
+
Resyma::Core::ParseTreeBuilder.root(ast.type, nil, index, ast) do
|
79
|
+
leaf :numop, m[1] unless m[1].nil?
|
80
|
+
leaf :numbase, m[2]
|
81
|
+
end.build(parent)
|
82
|
+
end
|
83
|
+
|
84
|
+
def check_boundary(boundary, pt_builder)
|
85
|
+
return if boundary.nil?
|
86
|
+
|
87
|
+
ctt = Resyma::Core::CONST_TOKEN_TABLE
|
88
|
+
value = boundary.source
|
89
|
+
type = ctt[value]
|
90
|
+
if type.nil?
|
91
|
+
raise Resyma::Core::ConversionError,
|
92
|
+
"Unknwon boundary-token of AST with type <begin>: #{value}"
|
93
|
+
end
|
94
|
+
pt_builder.add_child!(type, nil, true, [value])
|
95
|
+
end
|
96
|
+
|
97
|
+
def_rule %i[begin kwbegin] do |ast, parent, index|
|
98
|
+
ptb = Resyma::Core::ParseTreeBuilder.new(:begin, index, false, [], ast)
|
99
|
+
check_boundary ast.loc.begin, ptb
|
100
|
+
ast.children.each do |sub|
|
101
|
+
ptb.add_parsetree_child!(convert(sub), sub)
|
102
|
+
end
|
103
|
+
check_boundary ast.loc.end, ptb
|
104
|
+
ptb.build(parent)
|
105
|
+
end
|
106
|
+
|
107
|
+
def_rule %i[dstr dsym xstr] do |ast, parent, index|
|
108
|
+
ptb = Resyma::Core::ParseTreeBuilder.new(ast.type, index, false, [], ast)
|
109
|
+
ast.children.each do |sub|
|
110
|
+
ptb.add_parsetree_child!(convert(sub), sub)
|
111
|
+
end
|
112
|
+
ptb.build(parent)
|
113
|
+
end
|
114
|
+
|
115
|
+
def def_rule_for_seq(type, open, sep, close)
|
116
|
+
ctt = Resyma::Core::CONST_TOKEN_TABLE
|
117
|
+
def_rule type do |ast, parent, index|
|
118
|
+
ptb = Resyma::Core::ParseTreeBuilder.new(ast.type, index, false, [], ast)
|
119
|
+
ptb.add_child!(ctt[open], nil, true, [open]) unless open.nil?
|
120
|
+
unless ast.children.empty?
|
121
|
+
first = ast.children.first
|
122
|
+
ptb.add_parsetree_child!(convert(first), first)
|
123
|
+
ast.children[1..].each do |sub|
|
124
|
+
ptb.add_child!(ctt[sep], nil, true, [sep]) unless sep.nil?
|
125
|
+
ptb.add_parsetree_child!(convert(sub), sub)
|
126
|
+
end
|
127
|
+
end
|
128
|
+
ptb.add_child!(ctt[close], nil, true, [close]) unless close.nil?
|
129
|
+
ptb.build(parent)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
def_rule_for_seq(:array, "[", ",", "]")
|
134
|
+
def_rule_for_seq(:hash, "{", ",", "}")
|
135
|
+
def_rule_for_seq(:kwargs, nil, ",", nil)
|
136
|
+
|
137
|
+
def_rule %i[splat kwsplat] do |ast, parent, index|
|
138
|
+
ctt = Resyma::Core::CONST_TOKEN_TABLE
|
139
|
+
star = ast.loc.operator.source
|
140
|
+
ptb = Resyma::Core::ParseTreeBuilder.new(ast.type, index, false, [], ast)
|
141
|
+
ptb.add_child!(ctt[star], nil, true, [star])
|
142
|
+
first = ast.children.first
|
143
|
+
ptb.add_parsetree_child!(convert(first), first)
|
144
|
+
ptb.build(parent)
|
145
|
+
end
|
146
|
+
|
147
|
+
def_rule :pair do |ast, parent, index|
|
148
|
+
ctt = Resyma::Core::CONST_TOKEN_TABLE
|
149
|
+
left, right = ast.children
|
150
|
+
op_value = ast.loc.operator.source
|
151
|
+
op_type = ctt[op_value]
|
152
|
+
if op_type.nil?
|
153
|
+
raise Resyma::Core::ConversionError,
|
154
|
+
"Unknown operator for hash pair: #{op_value}"
|
155
|
+
end
|
156
|
+
ptb = Resyma::Core::ParseTreeBuilder.new(ast.type, index, false, [], ast)
|
157
|
+
ptb.add_parsetree_child!(convert(left), left)
|
158
|
+
ptb.add_child!(op_type, nil, true, [op_value])
|
159
|
+
ptb.add_parsetree_child!(convert(right), right)
|
160
|
+
ptb.build(parent)
|
161
|
+
end
|
162
|
+
|
163
|
+
def_rule %i[erange irange] do |ast, parent, index|
|
164
|
+
ctt = Resyma::Core::CONST_TOKEN_TABLE
|
165
|
+
ptb = Resyma::Core::ParseTreeBuilder.new(ast.type, index, false, [], ast)
|
166
|
+
left, right = ast.children
|
167
|
+
op_value = ast.loc.operator.source
|
168
|
+
ptb.add_parsetree_child!(convert(left), left) unless left.nil?
|
169
|
+
ptb.add_child!(ctt[op_value], nil, true, [op_value])
|
170
|
+
ptb.add_parsetree_child!(convert(right), right) unless right.nil?
|
171
|
+
ptb.build(parent)
|
172
|
+
end
|
173
|
+
|
174
|
+
def_rule :const do |ast, parent, index|
|
175
|
+
ctt = Resyma::Core::CONST_TOKEN_TABLE
|
176
|
+
scope, sym = ast.children
|
177
|
+
maybe_colon = ast.loc.double_colon
|
178
|
+
ptb = Resyma::Core::ParseTreeBuilder.new(:const, index, false, [], ast)
|
179
|
+
unless scope.nil? || scope.type == :cbase
|
180
|
+
ptb.add_parsetree_child!(convert(scope), scope)
|
181
|
+
end
|
182
|
+
unless maybe_colon.nil?
|
183
|
+
op_value = maybe_colon.source
|
184
|
+
ptb.add_child!(ctt[op_value], nil, true, [op_value])
|
185
|
+
end
|
186
|
+
ptb.add_child!(:id, nil, true, [sym.to_s])
|
187
|
+
ptb.build(parent)
|
188
|
+
end
|
189
|
+
|
190
|
+
def try_token!(ptb, optional_range)
|
191
|
+
ctt = Resyma::Core::CONST_TOKEN_TABLE
|
192
|
+
return if optional_range.nil?
|
193
|
+
|
194
|
+
token_value = if optional_range.is_a?(String)
|
195
|
+
optional_range
|
196
|
+
else
|
197
|
+
optional_range.source
|
198
|
+
end
|
199
|
+
ptb.add_child!(ctt[token_value], nil, true, [token_value])
|
200
|
+
end
|
201
|
+
|
202
|
+
def_rule :defined? do |ast, parent, index|
|
203
|
+
ptb = Resyma::Core::ParseTreeBuilder.new(:defined?, index, false, [], ast)
|
204
|
+
try_token! ptb, ast.loc.source.keyword
|
205
|
+
try_token! ptb, ast.loc.source.begin
|
206
|
+
expr, = ast.children
|
207
|
+
ptb.add_parsetree_child!(convert(expr), expr)
|
208
|
+
try_token! ptb, ast.loc.source.end
|
209
|
+
ptb.build(parent)
|
210
|
+
end
|
211
|
+
|
212
|
+
asgn_table = {
|
213
|
+
lvasgn: :id,
|
214
|
+
ivasgn: :ivar,
|
215
|
+
cvasgn: :cvar,
|
216
|
+
gvasgn: :gvar
|
217
|
+
}
|
218
|
+
|
219
|
+
def_rule %i[lvasgn ivasgn cvasgn gvasgn] do |ast, parent, index|
|
220
|
+
name = ast.loc.name.source
|
221
|
+
value = ast.children[1]
|
222
|
+
ptb = Resyma::Core::ParseTreeBuilder.new(ast.type, index, false, [], ast)
|
223
|
+
name_tkn = make_token(asgn_table[ast.type], name, nil, 0, nil)
|
224
|
+
ptb.add_parsetree_child!(name_tkn)
|
225
|
+
try_token!(ptb, ast.loc.operator)
|
226
|
+
ptb.add_parsetree_child!(convert(value), value)
|
227
|
+
ptb.build(parent)
|
228
|
+
end
|
229
|
+
|
230
|
+
#
|
231
|
+
# @yieldparam [Resyma::Core::ParseTreeBuilder]
|
232
|
+
#
|
233
|
+
def with_ptb(ast, parent, index, type = ast.type)
|
234
|
+
ptb = Resyma::Core::ParseTreeBuilder.new(type, index, false, [], ast)
|
235
|
+
yield ptb
|
236
|
+
ptb.build(parent)
|
237
|
+
end
|
238
|
+
|
239
|
+
def add_ast!(ptb, ast)
|
240
|
+
ptb.add_parsetree_child!(convert(ast), ast) unless ast.nil?
|
241
|
+
end
|
242
|
+
|
243
|
+
def_rule :casgn do |ast, parent, index|
|
244
|
+
with_ptb ast, parent, index do |ptb|
|
245
|
+
base, sym, value_ast = ast.children
|
246
|
+
add_ast! ptb, base unless base.nil? || base.type == :cbase
|
247
|
+
try_token! ptb, ast.loc.double_colon
|
248
|
+
name = ast.loc.name.source
|
249
|
+
name_tkn = make_token(:id, name, nil, 0, nil)
|
250
|
+
ptb.add_parsetree_child!(name_tkn)
|
251
|
+
try_token! ptb, ast.loc.operator
|
252
|
+
add_ast! ptb, value_ast
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
256
|
+
def add_id!(ptb, name)
|
257
|
+
tkn = make_token(:id, name.to_s, nil, 0, nil)
|
258
|
+
ptb.add_parsetree_child!(tkn)
|
259
|
+
end
|
260
|
+
|
261
|
+
def_rule %i[send csend] do |ast, parent, index|
|
262
|
+
with_ptb ast, parent, index do |ptb|
|
263
|
+
if ast.loc.respond_to? :operator
|
264
|
+
rec, _, rhs = ast.children
|
265
|
+
add_ast! ptb, rec
|
266
|
+
try_token! ptb, ast.loc.dot
|
267
|
+
selector = ast.loc.selector.source
|
268
|
+
add_id! ptb, selector
|
269
|
+
try_token! ptb, ast.loc.operator
|
270
|
+
add_ast! ptb, rhs
|
271
|
+
end
|
272
|
+
end
|
273
|
+
end
|
274
|
+
|
275
|
+
def_rule :block do |ast, parent, index|
|
276
|
+
with_ptb ast, parent, index do |ptb|
|
277
|
+
send, args, block = ast.children
|
278
|
+
add_ast! ptb, send
|
279
|
+
try_token! ptb, ast.loc.begin
|
280
|
+
add_ast! ptb, args
|
281
|
+
add_ast! ptb, block
|
282
|
+
try_token! ptb, ast.loc.end
|
283
|
+
end
|
284
|
+
end
|
285
|
+
|
286
|
+
def_rule :args do |ast, parent, index|
|
287
|
+
with_ptb ast, parent, index do |ptb|
|
288
|
+
try_token! ptb, ast.loc.begin
|
289
|
+
unless ast.children.empty?
|
290
|
+
add_ast! ptb, ast.children.first
|
291
|
+
ast.children[1..].each do |arg|
|
292
|
+
try_token! ptb, ","
|
293
|
+
add_ast! ptb, arg
|
294
|
+
end
|
295
|
+
end
|
296
|
+
try_token! ptb, ast.loc.end
|
297
|
+
end
|
298
|
+
end
|
299
|
+
|
300
|
+
def_rule :arg do |ast, parent, index|
|
301
|
+
with_ptb ast, parent, index do |ptb|
|
302
|
+
add_id! ptb, ast.loc.name.source
|
303
|
+
end
|
304
|
+
end
|
305
|
+
|
306
|
+
def_rule :optarg do |ast, parent, index|
|
307
|
+
with_ptb ast, parent, index do |ptb|
|
308
|
+
add_id! ptb, ast.loc.name.source
|
309
|
+
try_token! ptb, ast.loc.operator
|
310
|
+
add_ast! ptb, ast.children[1]
|
311
|
+
end
|
312
|
+
end
|
313
|
+
|
314
|
+
def def_rule_with_ptb(types)
|
315
|
+
def_rule types do |ast, parent, index|
|
316
|
+
with_ptb ast, parent, index do |ptb|
|
317
|
+
yield ptb, ast, parent, index
|
318
|
+
end
|
319
|
+
end
|
320
|
+
end
|
321
|
+
|
322
|
+
def_rule_with_ptb :restarg do |ptb, ast|
|
323
|
+
try_token! ptb, "*"
|
324
|
+
add_id! ptb, ast.loc.name.source unless ast.loc.name.nil?
|
325
|
+
end
|
326
|
+
|
327
|
+
def_rule_with_ptb :blockarg do |ptb, ast|
|
328
|
+
try_token! ptb, "&"
|
329
|
+
add_id! ptb, ast.loc.name.source unless ast.loc.name.nil?
|
330
|
+
end
|
331
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
require "set"
|
2
|
+
|
3
|
+
module Resyma
|
4
|
+
module Core
|
5
|
+
class Field
|
6
|
+
#
|
7
|
+
# Create an instance of Field, which is set and used by the matching
|
8
|
+
# algorithm
|
9
|
+
#
|
10
|
+
# @param [Integer] id ID of the node
|
11
|
+
# @param [Hash<Integer, Set<Resyma::Core::Tuple2>>] start Sets of 2
|
12
|
+
# tuples, corresponding to different automata
|
13
|
+
# @param [Hash<Integer, Set<Resyma::Core::Tuple4>>] trans Sets of 4
|
14
|
+
# tuples, corresponding to different automata
|
15
|
+
#
|
16
|
+
def initialize(id, start, trans)
|
17
|
+
@id = id
|
18
|
+
@start = start
|
19
|
+
@trans = trans
|
20
|
+
end
|
21
|
+
|
22
|
+
attr_accessor :id, :start, :trans
|
23
|
+
|
24
|
+
def self.clean_field
|
25
|
+
start = Hash.new { |hash, key| hash[key] = Set[] }
|
26
|
+
trans = Hash.new { |hash, key| hash[key] = Set[] }
|
27
|
+
new(-1, start, trans)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
#
|
32
|
+
# Parse tree with fields used by the matching algorithm
|
33
|
+
#
|
34
|
+
class ParseTree
|
35
|
+
attr_accessor :symbol, :children, :parent, :index, :field, :ast, :cache
|
36
|
+
|
37
|
+
#
|
38
|
+
# Create an instance of parse tree
|
39
|
+
#
|
40
|
+
# @param [Symbol] symbol Symbol associating to the node
|
41
|
+
# @param [Array] children Subtrees of current node, or an array with a
|
42
|
+
# single element if it is a leaf
|
43
|
+
# @param [Resyma::Core::ParseTree, nil] parent Parent tree, or nil if the
|
44
|
+
# current node is the root
|
45
|
+
# @param [Integer] index There are `index` brother preceding to the
|
46
|
+
# current node
|
47
|
+
# @param [true,false] is_leaf Whether or not the current node is a leaf
|
48
|
+
# @param [Parser::AST::Node,nil] ast Its corresponding abstract syntax
|
49
|
+
# tree
|
50
|
+
#
|
51
|
+
def initialize(symbol, children, parent, index, is_leaf, ast = nil)
|
52
|
+
@symbol = symbol
|
53
|
+
@children = children
|
54
|
+
@parent = parent
|
55
|
+
@index = index
|
56
|
+
@field = Field.clean_field
|
57
|
+
@is_leaf = is_leaf
|
58
|
+
@ast = ast
|
59
|
+
@cache = {}
|
60
|
+
end
|
61
|
+
|
62
|
+
def clear!
|
63
|
+
@field = Field.clean_field
|
64
|
+
@cache = {}
|
65
|
+
@children.each(&:clear!) unless leaf?
|
66
|
+
end
|
67
|
+
|
68
|
+
def root?
|
69
|
+
@parent.nil?
|
70
|
+
end
|
71
|
+
|
72
|
+
def leaf?
|
73
|
+
@is_leaf
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require "set"
|
2
|
+
require "parser/current"
|
3
|
+
|
4
|
+
module Resyma
|
5
|
+
module Core
|
6
|
+
SourceLocator = Struct.new("SourceDetector", :matcher, :locate)
|
7
|
+
|
8
|
+
SOURCE_LOCATORS = []
|
9
|
+
|
10
|
+
def self.def_source_locator(regexp, &block)
|
11
|
+
SOURCE_LOCATORS.unshift SourceLocator.new(regexp, block)
|
12
|
+
end
|
13
|
+
|
14
|
+
#
|
15
|
+
# Locate the AST of a callable object
|
16
|
+
#
|
17
|
+
# @param [#source_location] procedure A callable object, particular a
|
18
|
+
# instance of Proc or Method
|
19
|
+
#
|
20
|
+
# @return [nil, Parser::AST::Node] AST of the procedure, or nil if cannot
|
21
|
+
# locate its source
|
22
|
+
#
|
23
|
+
def self.locate(procedure)
|
24
|
+
if procedure.respond_to? :source_location
|
25
|
+
filename, lino = procedure.source_location
|
26
|
+
SOURCE_LOCATORS.each do |locator|
|
27
|
+
if locator.matcher.match?(filename)
|
28
|
+
return locator.locate.call(procedure, filename, lino)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
nil
|
34
|
+
end
|
35
|
+
|
36
|
+
CALLABLE_TYPES = Set[:def, :defs, :block]
|
37
|
+
|
38
|
+
def self.line_number_of_callable(ast)
|
39
|
+
case ast.type
|
40
|
+
when Set[:def, :defs] then ast.loc.keyword.line
|
41
|
+
when :block then ast.loc.begin.line
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.locate_possible_procedures(ast, lino)
|
46
|
+
return [] unless ast.is_a?(Parser::AST::Node)
|
47
|
+
|
48
|
+
procs = []
|
49
|
+
if CALLABLE_TYPES.include?(ast.type) &&
|
50
|
+
line_number_of_callable(ast) == lino
|
51
|
+
procs.push ast
|
52
|
+
end
|
53
|
+
ast.children.each do |sub|
|
54
|
+
procs += locate_possible_procedures(sub, lino)
|
55
|
+
end
|
56
|
+
procs
|
57
|
+
end
|
58
|
+
|
59
|
+
class MultipleProcedureError < Resyma::Error; end
|
60
|
+
|
61
|
+
def_source_locator(/^.*$/) do |_, filename, lino|
|
62
|
+
tree = Parser::CurrentRuby.parse_file(filename)
|
63
|
+
procs = locate_possible_procedures(tree, lino)
|
64
|
+
case procs.size
|
65
|
+
when 1 then procs[0]
|
66
|
+
when 0 then nil
|
67
|
+
else raise MultipleProcedureError,
|
68
|
+
"Detected multiple procedures in [#{filename}:#{lino}], " +
|
69
|
+
"which is unsupported currently"
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require "resyma/core/parsetree/definition"
|
2
|
+
|
3
|
+
module Resyma
|
4
|
+
module Core
|
5
|
+
class ParseTree
|
6
|
+
#
|
7
|
+
# Depth-firstly traverse the tree
|
8
|
+
#
|
9
|
+
# @yieldparam [Resyma::Core::ParseTree] A parse tree
|
10
|
+
#
|
11
|
+
# @return [nil] Nothing
|
12
|
+
#
|
13
|
+
def depth_first_each(&block)
|
14
|
+
yield self
|
15
|
+
|
16
|
+
return if leaf?
|
17
|
+
|
18
|
+
@children.each do |child|
|
19
|
+
child.depth_first_each(&block)
|
20
|
+
end
|
21
|
+
|
22
|
+
nil
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,8 @@
|
|
1
|
+
require "resyma/core/parsetree/source"
|
2
|
+
require "resyma/core/parsetree/definition"
|
3
|
+
require "resyma/core/parsetree/traversal"
|
4
|
+
require "resyma/core/parsetree/builder"
|
5
|
+
require "resyma/core/parsetree/converter"
|
6
|
+
require "resyma/core/parsetree/default_converter"
|
7
|
+
|
8
|
+
__END__
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require "set"
|
2
|
+
|
3
|
+
module Resyma
|
4
|
+
module Core
|
5
|
+
module Utils
|
6
|
+
def self.big_union(sets)
|
7
|
+
union = Set[]
|
8
|
+
sets.each { |set| union.merge(set) }
|
9
|
+
union
|
10
|
+
end
|
11
|
+
|
12
|
+
#
|
13
|
+
# Whether an automaton accepts the input
|
14
|
+
#
|
15
|
+
# @param [Resyma::Core::Automaton] automaton An well-formed automaton
|
16
|
+
# @param [Array] input_array A list of input tokens
|
17
|
+
#
|
18
|
+
# @return [true,false] Result
|
19
|
+
#
|
20
|
+
def self.automaton_accept?(automaton, input_array)
|
21
|
+
current_state = automaton.start
|
22
|
+
input_array.each do |word|
|
23
|
+
current_state = automaton.destination(current_state, word)
|
24
|
+
return false if current_state.nil?
|
25
|
+
end
|
26
|
+
automaton.accept? current_state
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|