herb 0.7.2 → 0.7.3

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 (125) hide show
  1. checksums.yaml +4 -4
  2. data/Makefile +2 -0
  3. data/README.md +1 -1
  4. data/Rakefile +46 -1
  5. data/config.yml +714 -0
  6. data/ext/herb/extconf.rb +2 -1
  7. data/ext/herb/nodes.c +1 -1
  8. data/herb.gemspec +3 -0
  9. data/lib/herb/version.rb +1 -1
  10. data/src/analyze.c +5 -9
  11. data/src/analyze_helpers.c +17 -6
  12. data/src/include/pretty_print.h +1 -1
  13. data/src/include/version.h +1 -1
  14. data/src/parser.c +1 -0
  15. data/src/pretty_print.c +1 -1
  16. data/templates/ext/herb/error_helpers.c.erb +85 -0
  17. data/templates/ext/herb/error_helpers.h.erb +12 -0
  18. data/templates/ext/herb/nodes.c.erb +90 -0
  19. data/templates/ext/herb/nodes.h.erb +9 -0
  20. data/templates/javascript/packages/core/src/errors.ts.erb +193 -0
  21. data/templates/javascript/packages/core/src/node-type-guards.ts.erb +325 -0
  22. data/templates/javascript/packages/core/src/nodes.ts.erb +414 -0
  23. data/templates/javascript/packages/core/src/visitor.ts.erb +29 -0
  24. data/templates/javascript/packages/node/extension/error_helpers.cpp.erb +113 -0
  25. data/templates/javascript/packages/node/extension/error_helpers.h.erb +17 -0
  26. data/templates/javascript/packages/node/extension/nodes.cpp.erb +111 -0
  27. data/templates/javascript/packages/node/extension/nodes.h.erb +17 -0
  28. data/templates/lib/herb/ast/nodes.rb.erb +117 -0
  29. data/templates/lib/herb/errors.rb.erb +106 -0
  30. data/templates/lib/herb/visitor.rb.erb +28 -0
  31. data/templates/sig/serialized_ast_errors.rbs.erb +10 -0
  32. data/templates/sig/serialized_ast_nodes.rbs.erb +10 -0
  33. data/templates/src/ast_nodes.c.erb +145 -0
  34. data/templates/src/ast_pretty_print.c.erb +97 -0
  35. data/templates/src/errors.c.erb +245 -0
  36. data/templates/src/include/ast_nodes.h.erb +46 -0
  37. data/templates/src/include/ast_pretty_print.h.erb +14 -0
  38. data/templates/src/include/errors.h.erb +58 -0
  39. data/templates/src/visitor.c.erb +47 -0
  40. data/templates/template.rb +406 -0
  41. data/templates/wasm/error_helpers.cpp.erb +93 -0
  42. data/templates/wasm/error_helpers.h.erb +15 -0
  43. data/templates/wasm/nodes.cpp.erb +79 -0
  44. data/templates/wasm/nodes.h.erb +15 -0
  45. data/vendor/prism/Rakefile +75 -0
  46. data/vendor/prism/config.yml +4713 -0
  47. data/vendor/prism/include/prism/ast.h +8190 -0
  48. data/vendor/prism/include/prism/defines.h +260 -0
  49. data/vendor/prism/include/prism/diagnostic.h +455 -0
  50. data/vendor/prism/include/prism/encoding.h +283 -0
  51. data/vendor/prism/include/prism/node.h +129 -0
  52. data/vendor/prism/include/prism/options.h +482 -0
  53. data/vendor/prism/include/prism/pack.h +163 -0
  54. data/vendor/prism/include/prism/parser.h +933 -0
  55. data/vendor/prism/include/prism/prettyprint.h +34 -0
  56. data/vendor/prism/include/prism/regexp.h +43 -0
  57. data/vendor/prism/include/prism/static_literals.h +121 -0
  58. data/vendor/prism/include/prism/util/pm_buffer.h +236 -0
  59. data/vendor/prism/include/prism/util/pm_char.h +204 -0
  60. data/vendor/prism/include/prism/util/pm_constant_pool.h +218 -0
  61. data/vendor/prism/include/prism/util/pm_integer.h +130 -0
  62. data/vendor/prism/include/prism/util/pm_list.h +103 -0
  63. data/vendor/prism/include/prism/util/pm_memchr.h +29 -0
  64. data/vendor/prism/include/prism/util/pm_newline_list.h +113 -0
  65. data/vendor/prism/include/prism/util/pm_string.h +200 -0
  66. data/vendor/prism/include/prism/util/pm_strncasecmp.h +32 -0
  67. data/vendor/prism/include/prism/util/pm_strpbrk.h +46 -0
  68. data/vendor/prism/include/prism/version.h +29 -0
  69. data/vendor/prism/include/prism.h +408 -0
  70. data/vendor/prism/src/diagnostic.c +848 -0
  71. data/vendor/prism/src/encoding.c +5235 -0
  72. data/vendor/prism/src/node.c +8676 -0
  73. data/vendor/prism/src/options.c +328 -0
  74. data/vendor/prism/src/pack.c +509 -0
  75. data/vendor/prism/src/prettyprint.c +8941 -0
  76. data/vendor/prism/src/prism.c +23302 -0
  77. data/vendor/prism/src/regexp.c +790 -0
  78. data/vendor/prism/src/serialize.c +2268 -0
  79. data/vendor/prism/src/static_literals.c +617 -0
  80. data/vendor/prism/src/token_type.c +703 -0
  81. data/vendor/prism/src/util/pm_buffer.c +357 -0
  82. data/vendor/prism/src/util/pm_char.c +318 -0
  83. data/vendor/prism/src/util/pm_constant_pool.c +342 -0
  84. data/vendor/prism/src/util/pm_integer.c +670 -0
  85. data/vendor/prism/src/util/pm_list.c +49 -0
  86. data/vendor/prism/src/util/pm_memchr.c +35 -0
  87. data/vendor/prism/src/util/pm_newline_list.c +125 -0
  88. data/vendor/prism/src/util/pm_string.c +383 -0
  89. data/vendor/prism/src/util/pm_strncasecmp.c +36 -0
  90. data/vendor/prism/src/util/pm_strpbrk.c +206 -0
  91. data/vendor/prism/templates/ext/prism/api_node.c.erb +282 -0
  92. data/vendor/prism/templates/include/prism/ast.h.erb +226 -0
  93. data/vendor/prism/templates/include/prism/diagnostic.h.erb +130 -0
  94. data/vendor/prism/templates/java/org/prism/AbstractNodeVisitor.java.erb +22 -0
  95. data/vendor/prism/templates/java/org/prism/Loader.java.erb +434 -0
  96. data/vendor/prism/templates/java/org/prism/Nodes.java.erb +403 -0
  97. data/vendor/prism/templates/javascript/src/deserialize.js.erb +448 -0
  98. data/vendor/prism/templates/javascript/src/nodes.js.erb +197 -0
  99. data/vendor/prism/templates/javascript/src/visitor.js.erb +78 -0
  100. data/vendor/prism/templates/lib/prism/compiler.rb.erb +43 -0
  101. data/vendor/prism/templates/lib/prism/dispatcher.rb.erb +103 -0
  102. data/vendor/prism/templates/lib/prism/dot_visitor.rb.erb +189 -0
  103. data/vendor/prism/templates/lib/prism/dsl.rb.erb +133 -0
  104. data/vendor/prism/templates/lib/prism/inspect_visitor.rb.erb +131 -0
  105. data/vendor/prism/templates/lib/prism/mutation_compiler.rb.erb +19 -0
  106. data/vendor/prism/templates/lib/prism/node.rb.erb +515 -0
  107. data/vendor/prism/templates/lib/prism/reflection.rb.erb +136 -0
  108. data/vendor/prism/templates/lib/prism/serialize.rb.erb +602 -0
  109. data/vendor/prism/templates/lib/prism/visitor.rb.erb +55 -0
  110. data/vendor/prism/templates/rbi/prism/dsl.rbi.erb +68 -0
  111. data/vendor/prism/templates/rbi/prism/node.rbi.erb +164 -0
  112. data/vendor/prism/templates/rbi/prism/visitor.rbi.erb +18 -0
  113. data/vendor/prism/templates/sig/prism/_private/dot_visitor.rbs.erb +45 -0
  114. data/vendor/prism/templates/sig/prism/dsl.rbs.erb +31 -0
  115. data/vendor/prism/templates/sig/prism/mutation_compiler.rbs.erb +7 -0
  116. data/vendor/prism/templates/sig/prism/node.rbs.erb +132 -0
  117. data/vendor/prism/templates/sig/prism/visitor.rbs.erb +17 -0
  118. data/vendor/prism/templates/sig/prism.rbs.erb +89 -0
  119. data/vendor/prism/templates/src/diagnostic.c.erb +523 -0
  120. data/vendor/prism/templates/src/node.c.erb +333 -0
  121. data/vendor/prism/templates/src/prettyprint.c.erb +166 -0
  122. data/vendor/prism/templates/src/serialize.c.erb +406 -0
  123. data/vendor/prism/templates/src/token_type.c.erb +369 -0
  124. data/vendor/prism/templates/template.rb +689 -0
  125. metadata +112 -2
@@ -0,0 +1,43 @@
1
+ module Prism
2
+ # A compiler is a visitor that returns the value of each node as it visits.
3
+ # This is as opposed to a visitor which will only walk the tree. This can be
4
+ # useful when you are trying to compile a tree into a different format.
5
+ #
6
+ # For example, to build a representation of the tree as s-expressions, you
7
+ # could write:
8
+ #
9
+ # class SExpressions < Prism::Compiler
10
+ # def visit_arguments_node(node) = [:arguments, super]
11
+ # def visit_call_node(node) = [:call, super]
12
+ # def visit_integer_node(node) = [:integer]
13
+ # def visit_program_node(node) = [:program, super]
14
+ # end
15
+ #
16
+ # Prism.parse("1 + 2").value.accept(SExpressions.new)
17
+ # # => [:program, [[[:call, [[:integer], [:arguments, [[:integer]]]]]]]]
18
+ #
19
+ class Compiler < Visitor
20
+ # Visit an individual node.
21
+ def visit(node)
22
+ node&.accept(self)
23
+ end
24
+
25
+ # Visit a list of nodes.
26
+ def visit_all(nodes)
27
+ nodes.map { |node| node&.accept(self) }
28
+ end
29
+
30
+ # Visit the child nodes of the given node.
31
+ def visit_child_nodes(node)
32
+ node.compact_child_nodes.map { |node| node.accept(self) }
33
+ end
34
+
35
+ <%- nodes.each_with_index do |node, index| -%>
36
+ <%= "\n" if index != 0 -%>
37
+ # Compile a <%= node.name %> node
38
+ def visit_<%= node.human %>(node)
39
+ node.compact_child_nodes.map { |node| node.accept(self) }
40
+ end
41
+ <%- end -%>
42
+ end
43
+ end
@@ -0,0 +1,103 @@
1
+ module Prism
2
+ # The dispatcher class fires events for nodes that are found while walking an
3
+ # AST to all registered listeners. It's useful for performing different types
4
+ # of analysis on the AST while only having to walk the tree once.
5
+ #
6
+ # To use the dispatcher, you would first instantiate it and register listeners
7
+ # for the events you're interested in:
8
+ #
9
+ # class OctalListener
10
+ # def on_integer_node_enter(node)
11
+ # if node.octal? && !node.slice.start_with?("0o")
12
+ # warn("Octal integers should be written with the 0o prefix")
13
+ # end
14
+ # end
15
+ # end
16
+ #
17
+ # listener = OctalListener.new
18
+ # dispatcher = Prism::Dispatcher.new
19
+ # dispatcher.register(listener, :on_integer_node_enter)
20
+ #
21
+ # Then, you can walk any number of trees and dispatch events to the listeners:
22
+ #
23
+ # result = Prism.parse("001 + 002 + 003")
24
+ # dispatcher.dispatch(result.value)
25
+ #
26
+ # Optionally, you can also use `#dispatch_once` to dispatch enter and leave
27
+ # events for a single node without recursing further down the tree. This can
28
+ # be useful in circumstances where you want to reuse the listeners you already
29
+ # have registers but want to stop walking the tree at a certain point.
30
+ #
31
+ # integer = result.value.statements.body.first.receiver.receiver
32
+ # dispatcher.dispatch_once(integer)
33
+ #
34
+ class Dispatcher < Visitor
35
+ # attr_reader listeners: Hash[Symbol, Array[Listener]]
36
+ attr_reader :listeners
37
+
38
+ # Initialize a new dispatcher.
39
+ def initialize
40
+ @listeners = {}
41
+ end
42
+
43
+ # Register a listener for one or more events.
44
+ #
45
+ # def register: (Listener, *Symbol) -> void
46
+ def register(listener, *events)
47
+ register_events(listener, events)
48
+ end
49
+
50
+ # Register all public methods of a listener that match the pattern
51
+ # `on_<node_name>_(enter|leave)`.
52
+ #
53
+ # def register_public_methods: (Listener) -> void
54
+ def register_public_methods(listener)
55
+ register_events(listener, listener.public_methods(false).grep(/\Aon_.+_(?:enter|leave)\z/))
56
+ end
57
+
58
+ # Register a listener for the given events.
59
+ private def register_events(listener, events)
60
+ events.each { |event| (listeners[event] ||= []) << listener }
61
+ end
62
+
63
+ # Walks `root` dispatching events to all registered listeners.
64
+ #
65
+ # def dispatch: (Node) -> void
66
+ alias dispatch visit
67
+
68
+ # Dispatches a single event for `node` to all registered listeners.
69
+ #
70
+ # def dispatch_once: (Node) -> void
71
+ def dispatch_once(node)
72
+ node.accept(DispatchOnce.new(listeners))
73
+ end
74
+ <%- nodes.each do |node| -%>
75
+
76
+ # Dispatch enter and leave events for <%= node.name %> nodes and continue
77
+ # walking the tree.
78
+ def visit_<%= node.human %>(node)
79
+ listeners[:on_<%= node.human %>_enter]&.each { |listener| listener.on_<%= node.human %>_enter(node) }
80
+ super
81
+ listeners[:on_<%= node.human %>_leave]&.each { |listener| listener.on_<%= node.human %>_leave(node) }
82
+ end
83
+ <%- end -%>
84
+
85
+ class DispatchOnce < Visitor # :nodoc:
86
+ attr_reader :listeners
87
+
88
+ def initialize(listeners)
89
+ @listeners = listeners
90
+ end
91
+ <%- nodes.each do |node| -%>
92
+
93
+ # Dispatch enter and leave events for <%= node.name %> nodes.
94
+ def visit_<%= node.human %>(node)
95
+ listeners[:on_<%= node.human %>_enter]&.each { |listener| listener.on_<%= node.human %>_enter(node) }
96
+ listeners[:on_<%= node.human %>_leave]&.each { |listener| listener.on_<%= node.human %>_leave(node) }
97
+ end
98
+ <%- end -%>
99
+ end
100
+
101
+ private_constant :DispatchOnce
102
+ end
103
+ end
@@ -0,0 +1,189 @@
1
+ require "cgi/escape"
2
+ require "cgi/util" unless defined?(CGI::EscapeExt)
3
+
4
+ module Prism
5
+ # This visitor provides the ability to call Node#to_dot, which converts a
6
+ # subtree into a graphviz dot graph.
7
+ class DotVisitor < Visitor
8
+ class Field # :nodoc:
9
+ attr_reader :name, :value, :port
10
+
11
+ def initialize(name, value, port)
12
+ @name = name
13
+ @value = value
14
+ @port = port
15
+ end
16
+
17
+ def to_dot
18
+ if port
19
+ "<tr><td align=\"left\" colspan=\"2\" port=\"#{name}\">#{name}</td></tr>"
20
+ else
21
+ "<tr><td align=\"left\">#{name}</td><td>#{CGI.escapeHTML(value || raise)}</td></tr>"
22
+ end
23
+ end
24
+ end
25
+
26
+ class Table # :nodoc:
27
+ attr_reader :name, :fields
28
+
29
+ def initialize(name)
30
+ @name = name
31
+ @fields = []
32
+ end
33
+
34
+ def field(name, value = nil, port: false)
35
+ fields << Field.new(name, value, port)
36
+ end
37
+
38
+ def to_dot
39
+ dot = <<~DOT
40
+ <table border="0" cellborder="1" cellspacing="0" cellpadding="4">
41
+ <tr><td colspan="2"><b>#{name}</b></td></tr>
42
+ DOT
43
+
44
+ if fields.any?
45
+ "#{dot} #{fields.map(&:to_dot).join("\n ")}\n</table>"
46
+ else
47
+ "#{dot}</table>"
48
+ end
49
+ end
50
+ end
51
+
52
+ class Digraph # :nodoc:
53
+ attr_reader :nodes, :waypoints, :edges
54
+
55
+ def initialize
56
+ @nodes = []
57
+ @waypoints = []
58
+ @edges = []
59
+ end
60
+
61
+ def node(value)
62
+ nodes << value
63
+ end
64
+
65
+ def waypoint(value)
66
+ waypoints << value
67
+ end
68
+
69
+ def edge(value)
70
+ edges << value
71
+ end
72
+
73
+ def to_dot
74
+ <<~DOT
75
+ digraph "Prism" {
76
+ node [
77
+ fontname=\"Courier New\"
78
+ shape=plain
79
+ style=filled
80
+ fillcolor=gray95
81
+ ];
82
+
83
+ #{nodes.map { |node| node.gsub(/\n/, "\n ") }.join("\n ")}
84
+ node [shape=point];
85
+ #{waypoints.join("\n ")}
86
+
87
+ #{edges.join("\n ")}
88
+ }
89
+ DOT
90
+ end
91
+ end
92
+
93
+ private_constant :Field, :Table, :Digraph
94
+
95
+ # The digraph that is being built.
96
+ attr_reader :digraph
97
+
98
+ # Initialize a new dot visitor.
99
+ def initialize
100
+ @digraph = Digraph.new
101
+ end
102
+
103
+ # Convert this visitor into a graphviz dot graph string.
104
+ def to_dot
105
+ digraph.to_dot
106
+ end
107
+ <%- nodes.each do |node| -%>
108
+
109
+ # Visit a <%= node.name %> node.
110
+ def visit_<%= node.human %>(node)
111
+ table = Table.new("<%= node.name %>")
112
+ id = node_id(node)
113
+ <%- if (node_flags = node.flags) -%>
114
+
115
+ # flags
116
+ table.field("flags", <%= node_flags.human %>_inspect(node))
117
+ <%- end -%>
118
+ <%- node.fields.each do |field| -%>
119
+
120
+ # <%= field.name %>
121
+ <%- case field -%>
122
+ <%- when Prism::Template::NodeField -%>
123
+ table.field("<%= field.name %>", port: true)
124
+ digraph.edge("#{id}:<%= field.name %> -> #{node_id(node.<%= field.name %>)};")
125
+ <%- when Prism::Template::OptionalNodeField -%>
126
+ unless (<%= field.name %> = node.<%= field.name %>).nil?
127
+ table.field("<%= field.name %>", port: true)
128
+ digraph.edge("#{id}:<%= field.name %> -> #{node_id(<%= field.name %>)};")
129
+ end
130
+ <%- when Prism::Template::NodeListField -%>
131
+ if node.<%= field.name %>.any?
132
+ table.field("<%= field.name %>", port: true)
133
+
134
+ waypoint = "#{id}_<%= field.name %>"
135
+ digraph.waypoint("#{waypoint};")
136
+
137
+ digraph.edge("#{id}:<%= field.name %> -> #{waypoint};")
138
+ node.<%= field.name %>.each { |child| digraph.edge("#{waypoint} -> #{node_id(child)};") }
139
+ else
140
+ table.field("<%= field.name %>", "[]")
141
+ end
142
+ <%- when Prism::Template::StringField, Prism::Template::ConstantField, Prism::Template::OptionalConstantField, Prism::Template::UInt8Field, Prism::Template::UInt32Field, Prism::Template::ConstantListField, Prism::Template::IntegerField, Prism::Template::DoubleField -%>
143
+ table.field("<%= field.name %>", node.<%= field.name %>.inspect)
144
+ <%- when Prism::Template::LocationField -%>
145
+ table.field("<%= field.name %>", location_inspect(node.<%= field.name %>))
146
+ <%- when Prism::Template::OptionalLocationField -%>
147
+ unless (<%= field.name %> = node.<%= field.name %>).nil?
148
+ table.field("<%= field.name %>", location_inspect(<%= field.name %>))
149
+ end
150
+ <%- else -%>
151
+ <%- raise -%>
152
+ <%- end -%>
153
+ <%- end -%>
154
+
155
+ digraph.nodes << <<~DOT
156
+ #{id} [
157
+ label=<#{table.to_dot.gsub(/\n/, "\n ")}>
158
+ ];
159
+ DOT
160
+
161
+ super
162
+ end
163
+ <%- end -%>
164
+
165
+ private
166
+
167
+ # Generate a unique node ID for a node throughout the digraph.
168
+ def node_id(node)
169
+ "Node_#{node.object_id}"
170
+ end
171
+
172
+ # Inspect a location to display the start and end line and column numbers.
173
+ def location_inspect(location)
174
+ "(#{location.start_line},#{location.start_column})-(#{location.end_line},#{location.end_column})"
175
+ end
176
+ <%- flags.each do |flag| -%>
177
+
178
+ # Inspect a node that has <%= flag.human %> flags to display the flags as a
179
+ # comma-separated list.
180
+ def <%= flag.human %>_inspect(node)
181
+ flags = [] #: Array[String]
182
+ <%- flag.values.each do |value| -%>
183
+ flags << "<%= value.name.downcase %>" if node.<%= value.name.downcase %>?
184
+ <%- end -%>
185
+ flags.join(", ")
186
+ end
187
+ <%- end -%>
188
+ end
189
+ end
@@ -0,0 +1,133 @@
1
+ module Prism
2
+ # The DSL module provides a set of methods that can be used to create prism
3
+ # nodes in a more concise manner. For example, instead of writing:
4
+ #
5
+ # source = Prism::Source.for("[1]")
6
+ #
7
+ # Prism::ArrayNode.new(
8
+ # source,
9
+ # 0,
10
+ # Prism::Location.new(source, 0, 3),
11
+ # 0,
12
+ # [
13
+ # Prism::IntegerNode.new(
14
+ # source,
15
+ # 0,
16
+ # Prism::Location.new(source, 1, 1),
17
+ # Prism::IntegerBaseFlags::DECIMAL,
18
+ # 1
19
+ # )
20
+ # ],
21
+ # Prism::Location.new(source, 0, 1),
22
+ # Prism::Location.new(source, 2, 1)
23
+ # )
24
+ #
25
+ # you could instead write:
26
+ #
27
+ # class Builder
28
+ # include Prism::DSL
29
+ #
30
+ # attr_reader :default_source
31
+ #
32
+ # def initialize
33
+ # @default_source = source("[1]")
34
+ # end
35
+ #
36
+ # def build
37
+ # array_node(
38
+ # location: location(start_offset: 0, length: 3),
39
+ # elements: [
40
+ # integer_node(
41
+ # location: location(start_offset: 1, length: 1),
42
+ # flags: integer_base_flag(:decimal),
43
+ # value: 1
44
+ # )
45
+ # ],
46
+ # opening_loc: location(start_offset: 0, length: 1),
47
+ # closing_loc: location(start_offset: 2, length: 1)
48
+ # )
49
+ # end
50
+ # end
51
+ #
52
+ # This is mostly helpful in the context of generating trees programmatically.
53
+ module DSL
54
+ # Provide all of these methods as module methods as well, to allow for
55
+ # building nodes like Prism::DSL.nil_node.
56
+ extend self
57
+
58
+ # Create a new Source object.
59
+ def source(string)
60
+ Source.for(string)
61
+ end
62
+
63
+ # Create a new Location object.
64
+ def location(source: default_source, start_offset: 0, length: 0)
65
+ Location.new(source, start_offset, length)
66
+ end
67
+ <%- nodes.each do |node| -%>
68
+
69
+ # Create a new <%= node.name %> node.
70
+ def <%= node.human %>(<%= ["source: default_source", "node_id: 0", "location: default_location", "flags: 0", *node.fields.map { |field|
71
+ case field
72
+ when Prism::Template::NodeField
73
+ kind = field.specific_kind || field.union_kind&.first
74
+ if kind.nil?
75
+ "#{field.name}: default_node(source, location)"
76
+ else
77
+ "#{field.name}: #{kind.gsub(/(?<=.)[A-Z]/, "_\\0").downcase}(source: source)"
78
+ end
79
+ when Prism::Template::ConstantField
80
+ "#{field.name}: :\"\""
81
+ when Prism::Template::OptionalNodeField, Prism::Template::OptionalConstantField, Prism::Template::OptionalLocationField
82
+ "#{field.name}: nil"
83
+ when Prism::Template::NodeListField, Prism::Template::ConstantListField
84
+ "#{field.name}: []"
85
+ when Prism::Template::StringField
86
+ "#{field.name}: \"\""
87
+ when Prism::Template::LocationField
88
+ "#{field.name}: location"
89
+ when Prism::Template::UInt8Field, Prism::Template::UInt32Field, Prism::Template::IntegerField
90
+ "#{field.name}: 0"
91
+ when Prism::Template::DoubleField
92
+ "#{field.name}: 0.0"
93
+ else
94
+ raise
95
+ end
96
+ }].join(", ") %>)
97
+ <%= node.name %>.new(<%= ["source", "node_id", "location", "flags", *node.fields.map(&:name)].join(", ") %>)
98
+ end
99
+ <%- end -%>
100
+ <%- flags.each do |flag| -%>
101
+
102
+ # Retrieve the value of one of the <%= flag.name %> flags.
103
+ def <%= flag.human.chomp("s") %>(name)
104
+ case name
105
+ <%- flag.values.each do |value| -%>
106
+ when :<%= value.name.downcase %> then <%= flag.name %>::<%= value.name %>
107
+ <%- end -%>
108
+ else Kernel.raise ArgumentError, "invalid <%= flag.name %> flag: #{name.inspect}"
109
+ end
110
+ end
111
+ <%- end -%>
112
+
113
+ private
114
+
115
+ # The default source object that gets attached to nodes and locations if no
116
+ # source is specified.
117
+ def default_source
118
+ Source.for("")
119
+ end
120
+
121
+ # The default location object that gets attached to nodes if no location is
122
+ # specified, which uses the given source.
123
+ def default_location
124
+ Location.new(default_source, 0, 0)
125
+ end
126
+
127
+ # The default node that gets attached to nodes if no node is specified for a
128
+ # required node field.
129
+ def default_node(source, location)
130
+ MissingNode.new(source, -1, location, 0)
131
+ end
132
+ end
133
+ end
@@ -0,0 +1,131 @@
1
+ module Prism
2
+ # This visitor is responsible for composing the strings that get returned by
3
+ # the various #inspect methods defined on each of the nodes.
4
+ class InspectVisitor < Visitor
5
+ # Most of the time, we can simply pass down the indent to the next node.
6
+ # However, when we are inside a list we want some extra special formatting
7
+ # when we hit an element in that list. In this case, we have a special
8
+ # command that replaces the subsequent indent with the given value.
9
+ class Replace # :nodoc:
10
+ attr_reader :value
11
+
12
+ def initialize(value)
13
+ @value = value
14
+ end
15
+ end
16
+
17
+ private_constant :Replace
18
+
19
+ # The current prefix string.
20
+ attr_reader :indent
21
+
22
+ # The list of commands that we need to execute in order to compose the
23
+ # final string.
24
+ attr_reader :commands
25
+
26
+ # Initializes a new instance of the InspectVisitor.
27
+ def initialize(indent = +"")
28
+ @indent = indent
29
+ @commands = []
30
+ end
31
+
32
+ # Compose an inspect string for the given node.
33
+ def self.compose(node)
34
+ visitor = new
35
+ node.accept(visitor)
36
+ visitor.compose
37
+ end
38
+
39
+ # Compose the final string.
40
+ def compose
41
+ buffer = +""
42
+ replace = nil
43
+
44
+ until commands.empty?
45
+ # @type var command: String | node | Replace
46
+ # @type var indent: String
47
+ command, indent = *commands.shift
48
+
49
+ case command
50
+ when String
51
+ buffer << (replace || indent)
52
+ buffer << command
53
+ replace = nil
54
+ when Node
55
+ visitor = InspectVisitor.new(indent)
56
+ command.accept(visitor)
57
+ @commands = [*visitor.commands, *@commands]
58
+ when Replace
59
+ replace = command.value
60
+ else
61
+ raise "Unknown command: #{command.inspect}"
62
+ end
63
+ end
64
+
65
+ buffer
66
+ end
67
+ <%- nodes.each do |node| -%>
68
+
69
+ # Inspect a <%= node.name %> node.
70
+ def visit_<%= node.human %>(node)
71
+ commands << [inspect_node(<%= node.name.inspect %>, node), indent]
72
+ <%- (fields = [node.flags || Prism::Template::Flags.empty, *node.fields]).each_with_index do |field, index| -%>
73
+ <%- pointer = index == fields.length - 1 ? "└── " : "├── " -%>
74
+ <%- preadd = index == fields.length - 1 ? " " : "│ " -%>
75
+ <%- case field -%>
76
+ <%- when Prism::Template::Flags -%>
77
+ flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), <%= field.values.map { |value| "(\"#{value.name.downcase}\" if node.#{value.name.downcase}?)" }.join(", ") %>].compact
78
+ commands << ["<%= pointer %>flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent]
79
+ <%- when Prism::Template::NodeListField -%>
80
+ commands << ["<%= pointer %><%= field.name %>: (length: #{(<%= field.name %> = node.<%= field.name %>).length})\n", indent]
81
+ if <%= field.name %>.any?
82
+ <%= field.name %>[0...-1].each do |child|
83
+ commands << [Replace.new("#{indent}<%= preadd %>├── "), indent]
84
+ commands << [child, "#{indent}<%= preadd %>│ "]
85
+ end
86
+ commands << [Replace.new("#{indent}<%= preadd %>└── "), indent]
87
+ commands << [<%= field.name %>[-1], "#{indent}<%= preadd %> "]
88
+ end
89
+ <%- when Prism::Template::NodeField -%>
90
+ commands << ["<%= pointer %><%= field.name %>:\n", indent]
91
+ commands << [node.<%= field.name %>, "#{indent}<%= preadd %>"]
92
+ <%- when Prism::Template::OptionalNodeField -%>
93
+ if (<%= field.name %> = node.<%= field.name %>).nil?
94
+ commands << ["<%= pointer %><%= field.name %>: ∅\n", indent]
95
+ else
96
+ commands << ["<%= pointer %><%= field.name %>:\n", indent]
97
+ commands << [<%= field.name %>, "#{indent}<%= preadd %>"]
98
+ end
99
+ <%- when Prism::Template::ConstantField, Prism::Template::ConstantListField, Prism::Template::StringField, Prism::Template::UInt8Field, Prism::Template::UInt32Field, Prism::Template::IntegerField, Prism::Template::DoubleField -%>
100
+ commands << ["<%= pointer %><%= field.name %>: #{node.<%= field.name %>.inspect}\n", indent]
101
+ <%- when Prism::Template::OptionalConstantField -%>
102
+ if (<%= field.name %> = node.<%= field.name %>).nil?
103
+ commands << ["<%= pointer %><%= field.name %>: ∅\n", indent]
104
+ else
105
+ commands << ["<%= pointer %><%= field.name %>: #{<%= field.name %>.inspect}\n", indent]
106
+ end
107
+ <%- when Prism::Template::LocationField, Prism::Template::OptionalLocationField -%>
108
+ commands << ["<%= pointer %><%= field.name %>: #{inspect_location(node.<%= field.name %>)}\n", indent]
109
+ <%- end -%>
110
+ <%- end -%>
111
+ end
112
+ <%- end -%>
113
+
114
+ private
115
+
116
+ # Compose a header for the given node.
117
+ def inspect_node(name, node)
118
+ location = node.location
119
+ "@ #{name} (location: (#{location.start_line},#{location.start_column})-(#{location.end_line},#{location.end_column}))\n"
120
+ end
121
+
122
+ # Compose a string representing the given inner location field.
123
+ def inspect_location(location)
124
+ if location
125
+ "(#{location.start_line},#{location.start_column})-(#{location.end_line},#{location.end_column}) = #{location.slice.inspect}"
126
+ else
127
+ "∅"
128
+ end
129
+ end
130
+ end
131
+ end
@@ -0,0 +1,19 @@
1
+ module Prism
2
+ # This visitor walks through the tree and copies each node as it is being
3
+ # visited. This is useful for consumers that want to mutate the tree, as you
4
+ # can change subtrees in place without effecting the rest of the tree.
5
+ class MutationCompiler < Compiler
6
+ <%- nodes.each_with_index do |node, index| -%>
7
+ <%= "\n" if index != 0 -%>
8
+ # Copy a <%= node.name %> node
9
+ def visit_<%= node.human %>(node)
10
+ <%- fields = node.fields.select { |field| [Prism::Template::NodeField, Prism::Template::OptionalNodeField, Prism::Template::NodeListField].include?(field.class) } -%>
11
+ <%- if fields.any? -%>
12
+ node.copy(<%= fields.map { |field| "#{field.name}: #{field.is_a?(Prism::Template::NodeListField) ? "visit_all" : "visit"}(node.#{field.name})" }.join(", ") %>)
13
+ <%- else -%>
14
+ node.copy
15
+ <%- end -%>
16
+ end
17
+ <%- end -%>
18
+ end
19
+ end