resyma 0.1.1

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.
Files changed (43) hide show
  1. checksums.yaml +7 -0
  2. data/.rspec +3 -0
  3. data/.rubocop.yml +31 -0
  4. data/CHANGELOG.md +5 -0
  5. data/Gemfile +6 -0
  6. data/Gemfile.lock +69 -0
  7. data/LICENSE +674 -0
  8. data/README.md +167 -0
  9. data/Rakefile +8 -0
  10. data/lib/resyma/core/algorithm/engine.rb +189 -0
  11. data/lib/resyma/core/algorithm/matcher.rb +48 -0
  12. data/lib/resyma/core/algorithm/tuple.rb +25 -0
  13. data/lib/resyma/core/algorithm.rb +5 -0
  14. data/lib/resyma/core/automaton/builder.rb +78 -0
  15. data/lib/resyma/core/automaton/definition.rb +32 -0
  16. data/lib/resyma/core/automaton/epsilon_NFA.rb +115 -0
  17. data/lib/resyma/core/automaton/matchable.rb +16 -0
  18. data/lib/resyma/core/automaton/regexp.rb +175 -0
  19. data/lib/resyma/core/automaton/state.rb +22 -0
  20. data/lib/resyma/core/automaton/transition.rb +58 -0
  21. data/lib/resyma/core/automaton/visualize.rb +23 -0
  22. data/lib/resyma/core/automaton.rb +9 -0
  23. data/lib/resyma/core/parsetree/builder.rb +89 -0
  24. data/lib/resyma/core/parsetree/converter.rb +61 -0
  25. data/lib/resyma/core/parsetree/default_converter.rb +331 -0
  26. data/lib/resyma/core/parsetree/definition.rb +77 -0
  27. data/lib/resyma/core/parsetree/source.rb +73 -0
  28. data/lib/resyma/core/parsetree/traversal.rb +26 -0
  29. data/lib/resyma/core/parsetree.rb +8 -0
  30. data/lib/resyma/core/utilities.rb +30 -0
  31. data/lib/resyma/language.rb +290 -0
  32. data/lib/resyma/nise/date.rb +53 -0
  33. data/lib/resyma/nise/rubymoji.rb +13 -0
  34. data/lib/resyma/nise/toml.rb +63 -0
  35. data/lib/resyma/parsetree.rb +163 -0
  36. data/lib/resyma/program/automaton.rb +84 -0
  37. data/lib/resyma/program/parsetree.rb +79 -0
  38. data/lib/resyma/program/traverse.rb +77 -0
  39. data/lib/resyma/version.rb +5 -0
  40. data/lib/resyma.rb +12 -0
  41. data/resyma.gemspec +47 -0
  42. data/sig/resyma.rbs +4 -0
  43. 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