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.
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