maccro 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/lib/maccro.rb ADDED
@@ -0,0 +1,147 @@
1
+ require_relative "./maccro/version"
2
+
3
+ require_relative "./maccro/dsl"
4
+ require_relative "./maccro/rule"
5
+ require_relative "./maccro/code_util"
6
+
7
+ require_relative "./maccro/kernel_ext"
8
+
9
+ module Maccro
10
+ @@dic = {}
11
+ @@trace_global = nil
12
+
13
+ def self.register(name, before, after, under: nil, safe_reference: false)
14
+ # Maccro.register(:double_less_than, 'e1 < e2 < e3', 'e1 < e2 && e2 < e3')
15
+ # Maccro.register(:double_greater_than, 'e1 > e2 > e3', 'e1 > e2 && e2 > e3')
16
+ # Maccro.register(:double_greater_than, 'e1 < e2 < e3', 'e1 < e2 && e2 < e3', safe_reference: true)
17
+ # Maccro.register(:activerecord_where_equal, 'v1 = v2', 'v1 => v2', under: 'e.where($TARGET)')
18
+ if safe_reference
19
+ raise NotImplementedError, "TODO: implement it"
20
+ end
21
+ @@dic[name] = Rule.new(name, before, after, under: under, safe_reference: safe_reference)
22
+ end
23
+
24
+ # TODO: apply_to_proc (that supports the list of local variables)
25
+
26
+ def self.apply(mojule, method, rules: @@dic, verbose: false, from_trace: false, get_code: false)
27
+ # Maccro.apply(X, X.instance_method(:yay), verbose: true)
28
+ if !method.source_location
29
+ raise "Native method can't be redefined"
30
+ end
31
+
32
+ ast = CodeUtil.proc_to_ast(method)
33
+ if !ast
34
+ if from_trace
35
+ # unknown and unexpected loaded ruby code (which many not have visible source)
36
+ return
37
+ else
38
+ raise "Failed to load AST nodes - source file may be invisible: #{method}"
39
+ end
40
+ end
41
+ # This node should be SCOPE node (just under DEFN or DEFS)
42
+ # But its code range is equal to code range of DEFN/DEFS
43
+ CodeUtil.extend_tree_with_wrapper(ast)
44
+
45
+ is_singleton_method = (mojule != method.owner)
46
+
47
+ first_lineno = ast.first_lineno
48
+ first_column = ast.first_column
49
+
50
+ iseq = nil
51
+ path = nil
52
+ source = nil
53
+ rewrite_method_code_range = nil
54
+
55
+ rewrite_happens = false
56
+ first_time = true
57
+
58
+ while rewrite_happens || first_time
59
+ rewrite_happens = false
60
+ first_time = false
61
+
62
+ rules.each_pair do |_name, this_rule|
63
+ try_once = ->(rule) {
64
+ matched = rule.match(ast)
65
+ next unless matched
66
+
67
+ if !source || !path || !iseq
68
+ source, path, iseq = CodeUtil.get_source_path_iseq(method)
69
+ end
70
+
71
+ source = matched.rewrite(source)
72
+ ast = CodeUtil.get_method_node(CodeUtil.parse_to_ast(source), method.name, first_lineno, first_column, singleton_method: is_singleton_method)
73
+ CodeUtil.extend_tree_with_wrapper(ast)
74
+ rewrite_method_code_range = CodeRange.from_node(ast)
75
+ rewrite_happens = true
76
+ try_once.call(this_rule)
77
+ }
78
+ try_once.call(this_rule)
79
+
80
+ break if rewrite_happens # to retry all rules
81
+ end
82
+ end
83
+
84
+ if source && path && rewrite_method_code_range
85
+ eval_source = (" " * first_column) + rewrite_method_code_range.get(source) # restore the original indentation
86
+ return eval_source if get_code
87
+ puts eval_source if verbose
88
+ CodeUtil.suppress_warning do
89
+ mojule.module_eval(eval_source, path, first_lineno)
90
+ end
91
+ end
92
+ end
93
+
94
+ # TODO: check visibility: private method is still private method even after module_eval?
95
+
96
+ def self.enable(target: nil, path: nil, rules: nil)
97
+ if target || path
98
+ enable_trace(target: target, path: path, rule_names: rules)
99
+ else
100
+ if rules
101
+ raise "Cannot enable globally with specific rules"
102
+ end
103
+ enable_trace(globally: true)
104
+ end
105
+ end
106
+
107
+ def self.enable_trace(target: nil, path: nil, globally: false, rule_names: nil)
108
+ if globally && @@trace_global
109
+ return nil
110
+ end
111
+
112
+ if rule_names
113
+ rules = rule_names.map{|n| [n, @@dic[n]] }.to_h
114
+ else
115
+ rules = @@dic
116
+ end
117
+
118
+ trace = TracePoint.new(:end) do |tp|
119
+ current_location = tp.path
120
+ next unless globally || target == tp.self || path == current_location
121
+
122
+ this = tp.self
123
+
124
+ methods = (
125
+ this.instance_methods(false).map{|m| this.instance_method(m) } +
126
+ this.private_instance_methods(false).map{|m| this.instance_method(m) } +
127
+
128
+ # NameError: undefined singleton method `provides?' for `Bundler::RubygemsIntegration::Legacy'
129
+ this.singleton_methods.map{|m| this.singleton_method(m) rescue nil }.compact
130
+ )
131
+
132
+ methods.each do |method|
133
+ source_location = method.source_location
134
+ next if !source_location # native method
135
+ next if source_location.first == '-e'
136
+ next if source_location.first != current_location # methods defined in other file
137
+ Maccro.apply(this, method, rules: rules, from_trace: true)
138
+ end
139
+ end
140
+
141
+ if globally
142
+ @@trace_global = trace
143
+ end
144
+ trace.enable
145
+ nil
146
+ end
147
+ end
@@ -0,0 +1,105 @@
1
+ require "maccro"
2
+
3
+ module Maccro
4
+ module Builtin
5
+ RULES = {
6
+ # [before, after (, options)]
7
+ # options:
8
+ # * under
9
+ # * safe_reference
10
+
11
+ # continuing less/greater-than or equal-to
12
+ less_than_2: ['e1 < e2 < e3', '(e1 < e2 && e2 < e3)'],
13
+ less_than_or_equal_to_2: ['e1 <= e2 <= e3', '(e1 <= e2 && e2 <= e3)'],
14
+ less_than_and_equal_to_a: ['e1 <= e2 < e3', '(e1 <= e2 && e2 < e3)'],
15
+ less_than_and_equal_to_b: ['e1 < e2 <= e3', '(e1 < e2 && e2 <= e3)'],
16
+ greater_than_2: ['e1 > e2 > e3', '(e1 > e2 && e2 > e3)'],
17
+ greater_than_or_equal_to_2: ['e1 >= e2 >= e3', '(e1 >= e2 && e2 >= e3)'],
18
+ greater_than_and_equal_to_a: ['e1 >= e2 > e3', '(e1 >= e2 && e2 > e3)'],
19
+ greater_than_and_equal_to_b: ['e1 > e2 >= e3', '(e1 > e2 && e2 >= e3)'],
20
+
21
+ # mathematic intervals
22
+ open_interval: ['e1 < e2 < e3', '(e1 < e2 && e2 < e3)'],
23
+ closed_interval: ['e1 <= e2 <= e3', '(e1 <= e2 && e2 <= e3)'],
24
+ left_closed_interval: ['e1 <= e2 < e3', '(e1 <= e2 && e2 < e3)'],
25
+ right_closed_interval: ['e1 < e2 <= e3', '(e1 < e2 && e2 <= e3)'],
26
+
27
+ # ActiveRecord utilities
28
+ ar_where_in_range_exclusive: ['e1 <= y1 < e2', '{y1 => [(e1)...(e2)]}', {under: 'e1.where($TARGET)'}],
29
+ ar_where_in_range_inclusive: ['e1 <= y1 <= e2', '{y1 => [(e1)..(e2)]}', {under: 'e1.where($TARGET)'}],
30
+ ar_where_equal_to: ['y1 == e1', '{y1 => e1}', {under: 'e1.where($TARGET)'}],
31
+ ar_where_not_equal_to: ['y1 != e1', '["#{y1} != ?", e1]', {under: 'e1.where($TARGET)'}],
32
+ ar_where_larger_than: ['y1 > e1', '["#{y1} > ?", e1]', {under: 'e1.where($TARGET)'}],
33
+ ar_where_larger_than_or_equal_to: ['y1 >= e1', '["#{y1} >= ?", e1]', {under: 'e1.where($TARGET)'}],
34
+ ar_where_less_than: ['y1 < e1', '["#{y1} < ?", e1]', {under: 'e1.where($TARGET)'}],
35
+ ar_where_less_than_or_equal_to: ['y1 <= e1', '["#{y1} <= ?", e1]', {under: 'e1.where($TARGET)'}],
36
+ # TODO: and-or mixed query
37
+ ar_and_chain_5: ['e1.where((e2 && e3 and e4 and e5 and e6 and e7))', 'e1.where(e2).where(e3).where(e4).where(e5).where(e6).where(e7)'],
38
+ ar_and_chain_4: ['e1.where((e2 and e3 and e4 and e5 and e6))', 'e1.where(e2).where(e3).where(e4).where(e5).where(e6)'],
39
+ ar_and_chain_3: ['e1.where((e2 and e3 and e4 and e5))', 'e1.where(e2).where(e3).where(e4).where(e5)'],
40
+ ar_and_chain_2: ['e1.where((e2 and e3 and e4))', 'e1.where(e2).where(e3).where(e4)'],
41
+ ar_and_chain_1: ['e1.where((e2 and e3))', 'e1.where(e2).where(e3)'],
42
+ ar_or_chain_5: ['e1.where((e2 or e3 or e4 or e5 or e6 or e7))', 'e1.where(e2).or(e1.where(e3)).or(e1.where(e4)).or(e1.where(e5)).or(e1.where(e6)).or(e1.where(e7))'],
43
+ ar_or_chain_4: ['e1.where((e2 or e3 or e4 or e5 or e6))', 'e1.where(e2).or(e1.where(e3)).or(e1.where(e4)).or(e1.where(e5)).or(e1.where(e6))'],
44
+ ar_or_chain_3: ['e1.where((e2 or e3 or e4 or e5))', 'e1.where(e2).or(e1.where(e3)).or(e1.where(e4)).or(e1.where(e5))'],
45
+ ar_or_chain_2: ['e1.where((e2 or e3 or e4))', 'e1.where(e2).or(e1.where(e3)).or(e1.where(e4))'],
46
+ ar_or_chain_1: ['e1.where((e2 or e3))', 'e1.where(e2).or(e1.where(e3))'],
47
+ }.freeze
48
+
49
+ RULE_GROUPS = {
50
+ inequality_operators: [
51
+ :less_than_2, :less_than_or_equal_to_2, :less_than_and_equal_to_a, :less_than_and_equal_to_b,
52
+ :greater_than_2, :greater_than_or_equal_to_2, :greater_than_and_equal_to_a, :greater_than_and_equal_to_b,
53
+ ],
54
+
55
+ mathematic_intervals: [:open_interval, :closed_interval, :left_closed_interval, :right_closed_interval],
56
+
57
+ activerecord_utilities: [
58
+ :ar_where_in_range_exclusive, :ar_where_in_range_inclusive,
59
+ :ar_where_equal_to, :ar_where_not_equal_to,
60
+ :ar_where_larger_than, :ar_where_larger_than_or_equal_to, :ar_where_less_than, :ar_where_less_than_or_equal_to,
61
+ :ar_and_chain_5, :ar_and_chain_4, :ar_and_chain_3, :ar_and_chain_2, :ar_and_chain_1,
62
+ :ar_or_chain_5, :ar_or_chain_4, :ar_or_chain_3, :ar_or_chain_2, :ar_or_chain_1,
63
+ ]
64
+ }.freeze
65
+
66
+ def self.rule(name)
67
+ return nil unless RULES.has_key?(name)
68
+ before, after, options = RULES.fetch(name)
69
+ options ||= {}
70
+ Rule.new(name, before, after, under: options.fetch(:under, nil), safe_reference: options.fetch(:safe_reference, false))
71
+ end
72
+
73
+ def self.rules(*names)
74
+ rules = {}
75
+ names.each do |name|
76
+ if RULES.has_key?(name)
77
+ rules[name] = rule(name)
78
+ elsif RULE_GROUPS.has_key?(name)
79
+ RULE_GROUPS[name].each do |n|
80
+ rules[n] = rule(n)
81
+ end
82
+ end
83
+ end
84
+ rules
85
+ end
86
+
87
+ def self.register(name)
88
+ if RULES.has_key?(name)
89
+ before, after, options = RULES.fetch(name)
90
+ options ||= {}
91
+ Maccro.register(name, before, after, under: options.fetch(:under, nil), safe_reference: options.fetch(:safe_reference, false))
92
+ elsif RULE_GROUPS.has_key?(name)
93
+ RULE_GROUPS[name].each do |rule_name|
94
+ register(rule_name)
95
+ end
96
+ end
97
+ end
98
+
99
+ def self.register_all
100
+ RULES.each_key do |name|
101
+ register(name)
102
+ end
103
+ end
104
+ end
105
+ end
@@ -0,0 +1,63 @@
1
+ require_relative 'code_util'
2
+
3
+ module Maccro
4
+ class CodeRange
5
+ def self.from_node(ast)
6
+ CodeRange.new(ast.first_lineno, ast.first_column, ast.last_lineno, ast.last_column)
7
+ end
8
+
9
+ attr_reader :first_lineno, :first_column, :last_lineno, :last_column
10
+
11
+ def initialize(first_lineno, first_column, last_lineno, last_column)
12
+ @first_lineno = first_lineno
13
+ @first_column = first_column
14
+ @last_lineno = last_lineno
15
+ @last_column = last_column
16
+ end
17
+
18
+ def ==(other)
19
+ @first_lineno == other.first_lineno &&
20
+ @first_column == other.first_column &&
21
+ @last_lineno == other.last_lineno &&
22
+ @last_column == other.last_column
23
+ end
24
+
25
+ def <=>(other)
26
+ if @first_lineno < other.first_lineno
27
+ -1
28
+ elsif @first_lineno == other.first_lineno
29
+ if @first_column < other.first_column
30
+ -1
31
+ elsif @first_column == other.first_column
32
+ if @last_lineno < other.last_lineno
33
+ -1
34
+ elsif @last_lineno == other.last_lineno
35
+ if @last_column < other.last_column
36
+ -1
37
+ elsif @last_column == other.last_column
38
+ 0
39
+ else # @last_column > other.last_column
40
+ 1
41
+ end
42
+ else # @last_lineno > other.last_lineno
43
+ 1
44
+ end
45
+ else # @first_column > other.first_column
46
+ 1
47
+ end
48
+ else # @first_lineno > other.first_lineno
49
+ 1
50
+ end
51
+ end
52
+
53
+ def source(path)
54
+ source = File.open(path){|f| f.read } # open as binary?
55
+ get(source)
56
+ end
57
+
58
+ def get(source)
59
+ range = CodeUtil.code_range_to_range(source, self)
60
+ source[range]
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,106 @@
1
+ module Maccro
2
+ module CodeUtil
3
+ def self.code_position_to_index(source, lineno, column)
4
+ source_lines = source.lines # including newline at the end of line
5
+ if source_lines.size < lineno
6
+ raise "too few lines for specified position: lineno:#{lineno}, column:#{column}"
7
+ end
8
+ counter = 1
9
+ index = 0
10
+ while counter < lineno
11
+ index += source_lines.shift.size
12
+ counter += 1
13
+ end
14
+ if source_lines.empty?
15
+ raise "too few lines for specified position: lineno:#{lineno}, column:#{column}"
16
+ end
17
+ # column is 0 origin
18
+ if source_lines.first.size < 1
19
+ raise "empty line at the end of source"
20
+ end
21
+ if source_lines.first.size < column
22
+ raise "too few chars in the line for specified position: lineno:#{lineno}, column:#{column}"
23
+ end
24
+ return index + column
25
+ end
26
+
27
+ def self.code_range_to_range(source, code_range)
28
+ begin_index = code_position_to_index(source, code_range.first_lineno, code_range.first_column)
29
+ end_index = code_position_to_index(source, code_range.last_lineno, code_range.last_column)
30
+ Range.new(begin_index, end_index, true) # exclude end char
31
+ end
32
+
33
+ def self.code_range_to_code(source, code_range)
34
+ source[code_range_to_range(source, code_range)]
35
+ end
36
+
37
+ def self.suppress_warning
38
+ v = $VERBOSE
39
+ $VERBOSE = nil
40
+ yield
41
+ ensure
42
+ $VERBOSE = v
43
+ end
44
+
45
+ def self.parse_to_ast(code)
46
+ suppress_warning do
47
+ RubyVM::AbstractSyntaxTree.parse(code)
48
+ end
49
+ end
50
+
51
+ def self.proc_to_ast(block)
52
+ suppress_warning do
53
+ RubyVM::AbstractSyntaxTree.of(block)
54
+ end
55
+ end
56
+
57
+ def self.proc_to_iseq(block)
58
+ RubyVM::InstructionSequence.of(block)
59
+ end
60
+
61
+ def self.extend_tree_with_wrapper(tree)
62
+ return unless tree.is_a?(RubyVM::AbstractSyntaxTree::Node)
63
+ tree.extend Maccro::DSL::ASTNodeWrapper unless tree.is_a?(Maccro::DSL::ASTNodeWrapper)
64
+ tree.children.each do |c|
65
+ extend_tree_with_wrapper(c)
66
+ end
67
+ end
68
+
69
+ def self.get_source_path_iseq(method)
70
+ iseq ||= CodeUtil.proc_to_iseq(method)
71
+ if !iseq
72
+ raise "Native methods can't be redefined"
73
+ end
74
+ path ||= iseq.absolute_path
75
+ if !path # STDIN or -e
76
+ raise "Methods from stdin or -e can't be redefined"
77
+ end
78
+ source ||= File.read(path)
79
+
80
+ return source, path, iseq
81
+ end
82
+
83
+ def self.get_method_node(node, method_name, lineno, column, singleton_method: false)
84
+ if singleton_method
85
+ # TODO: consider receiver filter
86
+ # 0: (SELF@57:6-57:10)
87
+ dig_method_node(node, :DEFS, 1, method_name, lineno, column)
88
+ else
89
+ dig_method_node(node, :DEFN, 0, method_name, lineno, column)
90
+ end
91
+ end
92
+
93
+ def self.dig_method_node(node, def_type, method_name_index, method_name, lineno, column)
94
+ return nil unless node.is_a?(RubyVM::AbstractSyntaxTree::Node)
95
+ if node.type == def_type && node.children[method_name_index] == method_name && node.first_lineno == lineno && node.first_column == column
96
+ return node
97
+ elsif node.respond_to?(:children)
98
+ node.children.each do |n|
99
+ r = dig_method_node(n, def_type, method_name_index, method_name, lineno, column)
100
+ return r if r
101
+ end
102
+ end
103
+ nil
104
+ end
105
+ end
106
+ end
data/lib/maccro/dsl.rb ADDED
@@ -0,0 +1,182 @@
1
+ require_relative 'dsl/node'
2
+ require_relative 'dsl/literal'
3
+ require_relative 'dsl/value'
4
+ require_relative 'dsl/assign'
5
+ require_relative 'dsl/expression'
6
+ require_relative 'code_util'
7
+
8
+ module Maccro
9
+ module DSL
10
+ def self.matcher(code_snippet)
11
+ ast = CodeUtil.parse_to_ast(code_snippet)
12
+ # Top level node should be SCOPE, and children[2] will be the first expression node
13
+ return ast_node_to_dsl_node(ast.children[2])
14
+ end
15
+
16
+ def self.ast_node_to_dsl_node(ast_node)
17
+ unless ast_node.is_a?(RubyVM::AbstractSyntaxTree::Node)
18
+ if ast_node.is_a?(Array)
19
+ ast_node.times do |i|
20
+ ast_node[i] = ast_node_to_dsl_node(ast_node[i])
21
+ end
22
+ end
23
+ return ast_node
24
+ end
25
+
26
+ # ast_node.is_a?(RubyVM::AbstractSyntaxTree::Node)
27
+ ast_node.extend ASTNodeWrapper
28
+ if is_placeholder?(ast_node)
29
+ return placeholder_to_matcher_node(ast_node)
30
+ end
31
+
32
+ ast_node.children.each_with_index do |n, i|
33
+ ast_node.children[i] = ast_node_to_dsl_node(n)
34
+ end
35
+ ast_node
36
+ end
37
+
38
+ def self.is_placeholder?(node)
39
+ if node.type == :VCALL && placeholder_name?(node.children.first)
40
+ true
41
+ elsif node.type == :GVAR && node.children.first == :'$TARGET'
42
+ true
43
+ else
44
+ false
45
+ end
46
+ end
47
+
48
+ def self.placeholder_name?(sym)
49
+ # Expression: "eN"
50
+ # Value: "vN"
51
+ # String: "sN"
52
+ # Symbol: "yN"
53
+ # Number: "nN"
54
+ # Regular expression: "rN"
55
+ # N index is 1 origin
56
+ (sym.to_s =~ /^[evsynr][1-9]\d*$/).!.!
57
+ end
58
+
59
+ def self.placeholder_to_matcher_node(placeholder_node)
60
+ name = placeholder_node.children.first.to_s
61
+ nodeClass = case name
62
+ when '$TARGET' then AnyNode
63
+ when /^s([1-9]\d*)$/ then String
64
+ when /^y([1-9]\d*)$/ then Symbol
65
+ when /^n([1-9]\d*)$/ then Number
66
+ when /^r([1-9]\d*)$/ then RegularExpression
67
+ when /^v([1-9]\d*)$/ then Value
68
+ when /^e([1-9]\d*)$/ then Expression
69
+ else
70
+ raise "BUG: unregistered placeholder name `#{name}`"
71
+ end
72
+ nodeClass.new(name, placeholder_node.to_code_range)
73
+ end
74
+ end
75
+ end
76
+
77
+ ###
78
+ # List of AST nodes: from node_children() in ast.c
79
+
80
+ # ASGN means Assignment
81
+
82
+ # BLOCK
83
+ # IF (expression)
84
+ # UNLESS (expression)
85
+ # CASE (expression)
86
+ # CASE2 (expression)
87
+ # WHEN
88
+ # WHILE
89
+ # UNTIL
90
+ # ITER
91
+ # FOR
92
+ # FOR_MASGN
93
+ # BREAK
94
+ # NEXT
95
+ # RETURN
96
+ # REDO
97
+ # RETRY
98
+ # BEGIN (expression)
99
+ # RESCUE
100
+ # RESBODY
101
+ # ENSURE
102
+ # AND (expression)
103
+ # OR (expression)
104
+ # MASGN (assign)
105
+ # LASGN (assign)
106
+ # DASGN (assign)
107
+ # DASGN_CUPR (assign)
108
+ # IASGN (assign)
109
+ # CVASGN (assign)
110
+ # GASGN (assign)
111
+ # CDECL (assign)
112
+ # OP_ASGN1 (assign)
113
+ # OP_ASGN2 (assign)
114
+ # OP_ASGN_AND (assign)
115
+ # OP_ASGN_OR (assign)
116
+ # OP_CDECL (assign)
117
+ # CALL (expression)
118
+ # OPCALL (expression)
119
+ # QCALL (expression)
120
+ # FCALL (expression)
121
+ # VCALL (expression)
122
+ # SUPER (expression)
123
+ # ZSUPER (expression)
124
+ # ARRAY (value)
125
+ # VALUES [return arguments]
126
+ # ZARRAY (value)
127
+ # HASH (value)
128
+ # YIELD (expression)
129
+ # LVAR (value)
130
+ # DVAR (value)
131
+ # IVAR (value)
132
+ # CONST (value)
133
+ # CVAR (value)
134
+ # GVAR (value)
135
+ # NTH_REF (value)
136
+ # BACK_REF (value)
137
+ # MATCH (expression)
138
+ # MATCH2 (expression)
139
+ # MATCH3 (expression)
140
+ # LIT (value)
141
+ # STR (value)
142
+ # XSTR (value)
143
+ # ONCE (value)
144
+ # DSTR (value)
145
+ # DXSTR (value)
146
+ # DREGX (value)
147
+ # DSYM (value)
148
+ # EVSTR [String interpolation (in string literals)]
149
+ # ARGSCAT
150
+ # AGSPUSH
151
+ # SPLAT
152
+ # BLOCK_PASS
153
+ # DEFN (expression)
154
+ # DEFS (expression)
155
+ # ALIAS
156
+ # VALIAS
157
+ # UNDEF
158
+ # CLASS
159
+ # MODULE
160
+ # SCLASS
161
+ # COLON2 (expression)
162
+ # COLON3 (expression)
163
+ # DOT2 (expression)
164
+ # DOT3 (expression)
165
+ # FLIP2 (expression)
166
+ # FLIP3 (expression)
167
+ # SELF (value)
168
+ # NIL (value)
169
+ # TRUE (value)
170
+ # FALSE (value)
171
+ # ERRINFO
172
+ # DEFINED (expression)
173
+ # POSTEXE
174
+ # ATTRASGN (assign)
175
+ # LAMBDA (value)
176
+ # OPT_ARG
177
+ # KW_ARG
178
+ # POSTARG
179
+ # ARGS
180
+ # SCOPE
181
+ # ARGS_AUX
182
+ # LAST