maccro 0.1.0
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 +7 -0
- data/.gitignore +10 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +359 -0
- data/Rakefile +12 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/lib/maccro.rb +147 -0
- data/lib/maccro/builtin.rb +105 -0
- data/lib/maccro/code_range.rb +63 -0
- data/lib/maccro/code_util.rb +106 -0
- data/lib/maccro/dsl.rb +182 -0
- data/lib/maccro/dsl/assign.rb +100 -0
- data/lib/maccro/dsl/expression.rb +166 -0
- data/lib/maccro/dsl/literal.rb +121 -0
- data/lib/maccro/dsl/node.rb +89 -0
- data/lib/maccro/dsl/value.rb +85 -0
- data/lib/maccro/kernel_ext.rb +15 -0
- data/lib/maccro/matched.rb +34 -0
- data/lib/maccro/rewrite_the_world.rb +4 -0
- data/lib/maccro/rule.rb +106 -0
- data/lib/maccro/version.rb +3 -0
- data/maccro.gemspec +27 -0
- metadata +108 -0
@@ -0,0 +1,100 @@
|
|
1
|
+
require_relative "node"
|
2
|
+
|
3
|
+
module Maccro
|
4
|
+
module DSL
|
5
|
+
class MultiAssignment < Node
|
6
|
+
def type; :MASGN; end
|
7
|
+
def self.match?(node); node.type == :MASGN ; end
|
8
|
+
end
|
9
|
+
|
10
|
+
class LocalVariableAssignment < Node
|
11
|
+
def type; :LASGN; end
|
12
|
+
def self.match?(node); node.type == :LASGN ; end
|
13
|
+
end
|
14
|
+
|
15
|
+
class DynamicVariableAssignmentOutOfScope < Node
|
16
|
+
# x = 1; 1.times{ x = 1 }
|
17
|
+
def type; :DASGN; end
|
18
|
+
def self.match?(node); node.type == :DASGN ; end
|
19
|
+
end
|
20
|
+
|
21
|
+
class DynamicVariableAssignmentCurrentScope < Node
|
22
|
+
# 1.times{ x = 1 }
|
23
|
+
def type; :DASGN_CUPR; end
|
24
|
+
def self.match?(node); node.type == :DASGN_CUPR ; end
|
25
|
+
end
|
26
|
+
|
27
|
+
class InstanceVariableAssignment < Node
|
28
|
+
def type; :IASGN; end
|
29
|
+
def self.match?(node); node.type == :IASGN ; end
|
30
|
+
end
|
31
|
+
|
32
|
+
class ClassVariableAssignment < Node
|
33
|
+
def type; :CVASGN; end
|
34
|
+
def self.match?(node); node.type == :CVASGN ; end
|
35
|
+
end
|
36
|
+
|
37
|
+
class GlobalVariableAssignment < Node
|
38
|
+
def type; :GASGN; end
|
39
|
+
def self.match?(node); node.type == :GASGN ; end
|
40
|
+
end
|
41
|
+
|
42
|
+
class AttributeAssignment < Node
|
43
|
+
# x[1] = 1
|
44
|
+
# x.a = 1
|
45
|
+
def type; :ATTRASGN; end
|
46
|
+
def self.match?(node); node.type == :ATTRASGN ; end
|
47
|
+
end
|
48
|
+
|
49
|
+
class ArrayAssignmentWithOperator < Node
|
50
|
+
# x[1] += 1
|
51
|
+
def type; :OP_ASGN1; end
|
52
|
+
def self.match?(node); node.type == :OP_ASGN1 ; end
|
53
|
+
end
|
54
|
+
|
55
|
+
class AttributeAssignmentWithOperator < Node
|
56
|
+
# x.a += 1
|
57
|
+
def type; :OP_ASGN2; end
|
58
|
+
def self.match?(node); node.type == :OP_ASGN2 ; end
|
59
|
+
end
|
60
|
+
|
61
|
+
class AndAssignment < Node
|
62
|
+
# x &&= y
|
63
|
+
def type; :OP_ASGN_AND; end
|
64
|
+
def self.match?(node); node.type == :OP_ASGN_AND ; end
|
65
|
+
end
|
66
|
+
|
67
|
+
class OrAssignment < Node
|
68
|
+
# x ||= y
|
69
|
+
def type; :OP_ASGN_OR; end
|
70
|
+
def self.match?(node); node.type == :OP_ASGN_OR ; end
|
71
|
+
end
|
72
|
+
|
73
|
+
class ConstantDeclaration < Node
|
74
|
+
def type; :CDECL; end
|
75
|
+
def self.match?(node); node.type == :CDECL ; end
|
76
|
+
end
|
77
|
+
|
78
|
+
class ConstantDeclarationWithOperator < Node
|
79
|
+
# A::B ||= 1
|
80
|
+
# A::B += 1
|
81
|
+
def type; :OP_CDECL; end
|
82
|
+
def self.match?(node); node.type == :OP_CDECL ; end
|
83
|
+
end
|
84
|
+
|
85
|
+
class Assignment < Node
|
86
|
+
SUB_TYPES = [
|
87
|
+
MultiAssignment, LocalVariableAssignment,
|
88
|
+
DynamicVariableAssignmentOutOfScope, DynamicVariableAssignmentCurrentScope,
|
89
|
+
InstanceVariableAssignment, ClassVariableAssignment, GlobalVariableAssignment,
|
90
|
+
AttributeAssignment, ArrayAssignmentWithOperator, AttributeAssignmentWithOperator,
|
91
|
+
AndAssignment, OrAssignment,
|
92
|
+
ConstantDeclaration, ConstantDeclarationWithOperator,
|
93
|
+
].freeze
|
94
|
+
|
95
|
+
def self.match?(node)
|
96
|
+
SUB_TYPES.any?{|s| s.match?(node) }
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
@@ -0,0 +1,166 @@
|
|
1
|
+
require_relative "node"
|
2
|
+
require_relative "value"
|
3
|
+
require_relative "assign"
|
4
|
+
|
5
|
+
module Maccro
|
6
|
+
module DSL
|
7
|
+
class IfExp < Node
|
8
|
+
def type; :IF; end
|
9
|
+
def self.match?(node); node.type == :IF; end
|
10
|
+
end
|
11
|
+
|
12
|
+
class UnlessExp < Node
|
13
|
+
def type; :UNLESS; end
|
14
|
+
def self.match?(node); node.type == :UNLESS; end
|
15
|
+
end
|
16
|
+
|
17
|
+
class CaseExp < Node
|
18
|
+
def type; :MACCRO_CASE; end
|
19
|
+
def self.match?(node)
|
20
|
+
node.type == :CASE || node.type == :CASE2
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# class BeginExp < Node
|
25
|
+
# # TODO: getting source of begin node DOES drop the beggining "begin" and tailing "end"
|
26
|
+
# def type; :"BEGIN"; end
|
27
|
+
# def self.match?(node); node.type == :"BEGIN"; end
|
28
|
+
# end
|
29
|
+
|
30
|
+
# class RescueExp < Node
|
31
|
+
# # TODO: getting source of rescue node DOES drop the beggining "begin" and tailing "end"
|
32
|
+
# def type; :"RESCUE"; end
|
33
|
+
# def self.match?(node); node.type == :"RESCUE"; end
|
34
|
+
# end
|
35
|
+
|
36
|
+
class AndExp < Node
|
37
|
+
def type; :AND; end
|
38
|
+
def self.match?(node); node.type == :AND; end
|
39
|
+
end
|
40
|
+
|
41
|
+
class OrExp < Node
|
42
|
+
def type; :OR; end
|
43
|
+
def self.match?(node); node.type == :OR; end
|
44
|
+
end
|
45
|
+
|
46
|
+
class CallExp < Node
|
47
|
+
def type; :CALL; end
|
48
|
+
def self.match?(node); node.type == :CALL; end
|
49
|
+
end
|
50
|
+
|
51
|
+
class OperatorCallExp < Node
|
52
|
+
def type; :OPCALL; end
|
53
|
+
def self.match?(node); node.type == :OPCALL; end
|
54
|
+
end
|
55
|
+
|
56
|
+
class SafeCallExp < Node
|
57
|
+
# x&.foo(1)
|
58
|
+
def type; :QCALL; end
|
59
|
+
def self.match?(node); node.type == :QCALL; end
|
60
|
+
end
|
61
|
+
|
62
|
+
class FunctionCallExp < Node
|
63
|
+
def type; :FCALL; end
|
64
|
+
def self.match?(node); node.type == :FCALL; end
|
65
|
+
end
|
66
|
+
|
67
|
+
class VCallExp < Node
|
68
|
+
# function call without arguments
|
69
|
+
def type; :VCALL; end
|
70
|
+
def self.match?(node); node.type == :VCALL; end
|
71
|
+
end
|
72
|
+
|
73
|
+
class SuperExp < Node
|
74
|
+
# super or super without arguments
|
75
|
+
def type; :MACCRO_SUPER; end
|
76
|
+
def self.match?(node)
|
77
|
+
node.type == :SUPER || node.type == :ZSUPER
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
class YieldExp < Node
|
82
|
+
def type; :YIELD; end
|
83
|
+
def self.match?(node); node.type == :YIELD; end
|
84
|
+
end
|
85
|
+
|
86
|
+
class MatchExp < Node
|
87
|
+
# MATCH: /.../ without `=~` (matches to $_ implicitly)
|
88
|
+
# MATCH2: /.../ =~ "..." (left side)
|
89
|
+
# MATCH3: "..." =~ /.../ (right side)
|
90
|
+
def type; :MACCRO_MATCH; end
|
91
|
+
def self.match?(node)
|
92
|
+
t = node.type
|
93
|
+
t == :MATCH || t == :MATCH2 || t == :MATCH3
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
class DefineMethod < Node
|
98
|
+
def type; :DEFN; end
|
99
|
+
def self.match?(node); node.type == :DEFN; end
|
100
|
+
end
|
101
|
+
|
102
|
+
class DefineSingletonMethod < Node
|
103
|
+
def type; :DEFS; end
|
104
|
+
def self.match?(node); node.type == :DEFS; end
|
105
|
+
end
|
106
|
+
|
107
|
+
class Colon2Exp < Node
|
108
|
+
def type; :COLON2; end
|
109
|
+
def self.match?(node); node.type == :COLON2; end
|
110
|
+
end
|
111
|
+
|
112
|
+
class Colon3Exp < Node
|
113
|
+
# top level reference (::A)
|
114
|
+
def type; :COLON3; end
|
115
|
+
def self.match?(node); node.type == :COLON3; end
|
116
|
+
end
|
117
|
+
|
118
|
+
class Dot2Exp < Node
|
119
|
+
# range constructor (inclusive)
|
120
|
+
def type; :DOT2; end
|
121
|
+
def self.match?(node); node.type == :DOT2; end
|
122
|
+
end
|
123
|
+
|
124
|
+
class Dot3Exp < Node
|
125
|
+
# range constructor (exclusive)
|
126
|
+
def type; :DOT3; end
|
127
|
+
def self.match?(node); node.type == :DOT3; end
|
128
|
+
end
|
129
|
+
|
130
|
+
class Flip2Exp < Node
|
131
|
+
# flip-flop (inclusive)
|
132
|
+
def type; :FLIP2; end
|
133
|
+
def self.match?(node); node.type == :FLIP2; end
|
134
|
+
end
|
135
|
+
|
136
|
+
class Flip3Exp < Node
|
137
|
+
# flip-flop (exclusive)
|
138
|
+
def type; :FLIP3; end
|
139
|
+
def self.match?(node); node.type == :FLIP3; end
|
140
|
+
end
|
141
|
+
|
142
|
+
class DefinedExp < Node
|
143
|
+
def type; :DEFINED; end
|
144
|
+
def self.match?(node); node.type == :DEFINED; end
|
145
|
+
end
|
146
|
+
|
147
|
+
class Expression < Node
|
148
|
+
SUB_TYPES = [
|
149
|
+
Value, Assignment,
|
150
|
+
IfExp, UnlessExp, CaseExp,
|
151
|
+
# BeginExp, RescueExp,
|
152
|
+
AndExp, OrExp,
|
153
|
+
CallExp, OperatorCallExp, SafeCallExp, FunctionCallExp, VCallExp,
|
154
|
+
SuperExp, YieldExp,
|
155
|
+
MatchExp,
|
156
|
+
DefineMethod, DefineSingletonMethod,
|
157
|
+
Colon2Exp, Colon3Exp, Dot2Exp, Dot3Exp, Flip2Exp, Flip3Exp,
|
158
|
+
DefinedExp,
|
159
|
+
].freeze
|
160
|
+
|
161
|
+
def self.match?(node)
|
162
|
+
SUB_TYPES.any?{|s| s.match?(node) }
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
@@ -0,0 +1,121 @@
|
|
1
|
+
require_relative "node"
|
2
|
+
|
3
|
+
module Maccro
|
4
|
+
module DSL
|
5
|
+
class Literal < Node
|
6
|
+
# integer, float, symbol, regex, ...
|
7
|
+
def type; :LIT; end
|
8
|
+
def self.match?(node); node.type == :LIT; end
|
9
|
+
end
|
10
|
+
|
11
|
+
class DSymbol < Node
|
12
|
+
# symbol with string interpolation
|
13
|
+
def type; :DSYM; end
|
14
|
+
def self.match?(node); node.type == :DSYM; end
|
15
|
+
end
|
16
|
+
|
17
|
+
class Symbol < Node
|
18
|
+
def type; :LIT; end
|
19
|
+
def self.match?(node)
|
20
|
+
(node.type == :LIT && node.children[0].is_a?(::Symbol)) || DSymbol.match?(node)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
class Number < Node
|
25
|
+
def type; :LIT; end
|
26
|
+
def self.match?(node)
|
27
|
+
node.type == :LIT && node.children[0].is_a?(::Numeric)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
class RegexpCompiledOnce < Node
|
32
|
+
# regex with RE_OPTION_ONCE (see new_regexp() in parse.y)
|
33
|
+
def type; :ONCE; end
|
34
|
+
def self.match?(node); node.type == :ONCE; end
|
35
|
+
end
|
36
|
+
|
37
|
+
class DRegexp < Node
|
38
|
+
# regex with string interpolation
|
39
|
+
def type; :DREGX; end
|
40
|
+
def self.match?(node); node.type == :DREGX; end
|
41
|
+
end
|
42
|
+
|
43
|
+
class RegularExpression < Node
|
44
|
+
def type; :LIT; end
|
45
|
+
def self.match?(node)
|
46
|
+
node.type == :LIT && node.children[0].is_a?(Regexp)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
class FString < Node
|
51
|
+
# string literal without interpolation
|
52
|
+
def type; :STR; end
|
53
|
+
def self.match?(node); node.type == :STR; end
|
54
|
+
end
|
55
|
+
|
56
|
+
class DString < Node
|
57
|
+
# string literal with interpolation ?
|
58
|
+
def type; :DSTR; end
|
59
|
+
def self.match?(node); node.type == :DSTR; end
|
60
|
+
end
|
61
|
+
|
62
|
+
class XString < Node
|
63
|
+
# string from back quote: `...`
|
64
|
+
# is it a literal?
|
65
|
+
def type; :XSTR; end
|
66
|
+
def self.match?(node); node.type == :XSTR; end
|
67
|
+
end
|
68
|
+
|
69
|
+
class DXString < Node
|
70
|
+
# string from back quote with string interpolation: `... #{..} ...`
|
71
|
+
# is it a literal?
|
72
|
+
def type; :DXSTR; end
|
73
|
+
def self.match?(node); node.type == :DXSTR; end
|
74
|
+
end
|
75
|
+
|
76
|
+
class String < Node
|
77
|
+
SUB_TYPES = [FString, DString, XString, DXString].freeze
|
78
|
+
|
79
|
+
def type; :MACCRO_STRING; end
|
80
|
+
|
81
|
+
def self.match?(node)
|
82
|
+
SUB_TYPES.any?{|s| s.match?(node) }
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
class NilValue < Node
|
87
|
+
def type; :NIL; end
|
88
|
+
def self.match?(node); node.type == :NIL; end
|
89
|
+
end
|
90
|
+
|
91
|
+
class TrueValue < Node
|
92
|
+
def type; :TRUE; end
|
93
|
+
def self.match?(node); node.type == :TRUE; end
|
94
|
+
end
|
95
|
+
|
96
|
+
class FalseValue < Node
|
97
|
+
def type; :FALSE; end
|
98
|
+
def self.match?(node); node.type == :FALSE; end
|
99
|
+
end
|
100
|
+
|
101
|
+
class Lambda < Node
|
102
|
+
def type; :LAMBDA; end
|
103
|
+
def self.match?(node); node.type == :LAMBDA; end
|
104
|
+
end
|
105
|
+
|
106
|
+
class Array < Node
|
107
|
+
# ARRAY and ZARRAY
|
108
|
+
def type; :MACCRO_ARRAY; end
|
109
|
+
def self.match?(node)
|
110
|
+
node.type == :ARRAY || node.type == :ZARRAY
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
class Hash < Node
|
115
|
+
# HASH may be a hash literal, or keyword argument assignment `"a: 1" of f(a: 1)`
|
116
|
+
# HASH(nd_alen == 0) is a keyword argument assignment, but no way to get it via AST::Node
|
117
|
+
def type; :HASH; end
|
118
|
+
def self.match?(node); node.type == :HASH; end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
require_relative '../code_range'
|
2
|
+
|
3
|
+
module Maccro
|
4
|
+
module DSL
|
5
|
+
module ASTNodeWrapper
|
6
|
+
def children
|
7
|
+
@child_nodes ||= super
|
8
|
+
end
|
9
|
+
|
10
|
+
def to_code_range
|
11
|
+
CodeRange.from_node(self)
|
12
|
+
end
|
13
|
+
|
14
|
+
def match?(node)
|
15
|
+
return false unless node.is_a?(ASTNodeWrapper)
|
16
|
+
return false if node.type != self.type
|
17
|
+
self.children.each_with_index do |c, i|
|
18
|
+
if c.is_a?(ASTNodeWrapper)
|
19
|
+
return false unless c.match?(node.children[i])
|
20
|
+
else
|
21
|
+
return false unless c == node.children[i]
|
22
|
+
end
|
23
|
+
end
|
24
|
+
# same type, all children match with others
|
25
|
+
true
|
26
|
+
end
|
27
|
+
|
28
|
+
def capture(ast, placeholders)
|
29
|
+
self.children.each_with_index do |c, i|
|
30
|
+
if c.is_a?(ASTNodeWrapper)
|
31
|
+
c.capture(ast.children[i], placeholders)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
class Node
|
38
|
+
attr_reader :name, :first_lineno, :first_column, :last_lineno, :last_column
|
39
|
+
|
40
|
+
include ASTNodeWrapper
|
41
|
+
|
42
|
+
def initialize(name, code_range)
|
43
|
+
@name = name
|
44
|
+
@code_range = code_range
|
45
|
+
@first_lineno = code_range.first_lineno
|
46
|
+
@first_column = code_range.first_column
|
47
|
+
@last_lineno = code_range.last_lineno
|
48
|
+
@last_column = code_range.last_column
|
49
|
+
end
|
50
|
+
|
51
|
+
def to_code_range
|
52
|
+
@code_range
|
53
|
+
end
|
54
|
+
|
55
|
+
def type
|
56
|
+
:MACCRO_NODE
|
57
|
+
end
|
58
|
+
|
59
|
+
def children
|
60
|
+
[]
|
61
|
+
end
|
62
|
+
|
63
|
+
def match?(node)
|
64
|
+
# literals are not Node in any cases
|
65
|
+
return false unless node.respond_to?(:type)
|
66
|
+
|
67
|
+
if self.class.respond_to?(:match?)
|
68
|
+
self.class.match?(node)
|
69
|
+
else
|
70
|
+
super
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def capture(ast, placeholders)
|
75
|
+
placeholders[@name] = ast.to_code_range
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
class AnyNode < Node
|
80
|
+
def match?(node)
|
81
|
+
true
|
82
|
+
end
|
83
|
+
|
84
|
+
def capture(ast, placeholders)
|
85
|
+
placeholders[:__target__] = ast
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|