herb 0.7.2-x86-linux-musl → 0.7.3-x86-linux-musl
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 +4 -4
- data/Makefile +2 -0
- data/README.md +1 -1
- data/Rakefile +46 -1
- data/config.yml +714 -0
- data/ext/herb/extconf.rb +2 -1
- data/ext/herb/nodes.c +1 -1
- data/herb.gemspec +3 -0
- data/lib/herb/3.0/herb.so +0 -0
- data/lib/herb/3.1/herb.so +0 -0
- data/lib/herb/3.2/herb.so +0 -0
- data/lib/herb/3.3/herb.so +0 -0
- data/lib/herb/3.4/herb.so +0 -0
- data/lib/herb/version.rb +1 -1
- data/src/analyze.c +5 -9
- data/src/analyze_helpers.c +17 -6
- data/src/include/pretty_print.h +1 -1
- data/src/include/version.h +1 -1
- data/src/parser.c +1 -0
- data/src/pretty_print.c +1 -1
- data/templates/ext/herb/error_helpers.c.erb +85 -0
- data/templates/ext/herb/error_helpers.h.erb +12 -0
- data/templates/ext/herb/nodes.c.erb +90 -0
- data/templates/ext/herb/nodes.h.erb +9 -0
- data/templates/javascript/packages/core/src/errors.ts.erb +193 -0
- data/templates/javascript/packages/core/src/node-type-guards.ts.erb +325 -0
- data/templates/javascript/packages/core/src/nodes.ts.erb +414 -0
- data/templates/javascript/packages/core/src/visitor.ts.erb +29 -0
- data/templates/javascript/packages/node/extension/error_helpers.cpp.erb +113 -0
- data/templates/javascript/packages/node/extension/error_helpers.h.erb +17 -0
- data/templates/javascript/packages/node/extension/nodes.cpp.erb +111 -0
- data/templates/javascript/packages/node/extension/nodes.h.erb +17 -0
- data/templates/lib/herb/ast/nodes.rb.erb +117 -0
- data/templates/lib/herb/errors.rb.erb +106 -0
- data/templates/lib/herb/visitor.rb.erb +28 -0
- data/templates/sig/serialized_ast_errors.rbs.erb +10 -0
- data/templates/sig/serialized_ast_nodes.rbs.erb +10 -0
- data/templates/src/ast_nodes.c.erb +145 -0
- data/templates/src/ast_pretty_print.c.erb +97 -0
- data/templates/src/errors.c.erb +245 -0
- data/templates/src/include/ast_nodes.h.erb +46 -0
- data/templates/src/include/ast_pretty_print.h.erb +14 -0
- data/templates/src/include/errors.h.erb +58 -0
- data/templates/src/visitor.c.erb +47 -0
- data/templates/template.rb +406 -0
- data/templates/wasm/error_helpers.cpp.erb +93 -0
- data/templates/wasm/error_helpers.h.erb +15 -0
- data/templates/wasm/nodes.cpp.erb +79 -0
- data/templates/wasm/nodes.h.erb +15 -0
- data/vendor/prism/Rakefile +75 -0
- data/vendor/prism/config.yml +4713 -0
- data/vendor/prism/include/prism/ast.h +8190 -0
- data/vendor/prism/include/prism/defines.h +260 -0
- data/vendor/prism/include/prism/diagnostic.h +455 -0
- data/vendor/prism/include/prism/encoding.h +283 -0
- data/vendor/prism/include/prism/node.h +129 -0
- data/vendor/prism/include/prism/options.h +482 -0
- data/vendor/prism/include/prism/pack.h +163 -0
- data/vendor/prism/include/prism/parser.h +933 -0
- data/vendor/prism/include/prism/prettyprint.h +34 -0
- data/vendor/prism/include/prism/regexp.h +43 -0
- data/vendor/prism/include/prism/static_literals.h +121 -0
- data/vendor/prism/include/prism/util/pm_buffer.h +236 -0
- data/vendor/prism/include/prism/util/pm_char.h +204 -0
- data/vendor/prism/include/prism/util/pm_constant_pool.h +218 -0
- data/vendor/prism/include/prism/util/pm_integer.h +130 -0
- data/vendor/prism/include/prism/util/pm_list.h +103 -0
- data/vendor/prism/include/prism/util/pm_memchr.h +29 -0
- data/vendor/prism/include/prism/util/pm_newline_list.h +113 -0
- data/vendor/prism/include/prism/util/pm_string.h +200 -0
- data/vendor/prism/include/prism/util/pm_strncasecmp.h +32 -0
- data/vendor/prism/include/prism/util/pm_strpbrk.h +46 -0
- data/vendor/prism/include/prism/version.h +29 -0
- data/vendor/prism/include/prism.h +408 -0
- data/vendor/prism/src/diagnostic.c +848 -0
- data/vendor/prism/src/encoding.c +5235 -0
- data/vendor/prism/src/node.c +8676 -0
- data/vendor/prism/src/options.c +328 -0
- data/vendor/prism/src/pack.c +509 -0
- data/vendor/prism/src/prettyprint.c +8941 -0
- data/vendor/prism/src/prism.c +23302 -0
- data/vendor/prism/src/regexp.c +790 -0
- data/vendor/prism/src/serialize.c +2268 -0
- data/vendor/prism/src/static_literals.c +617 -0
- data/vendor/prism/src/token_type.c +703 -0
- data/vendor/prism/src/util/pm_buffer.c +357 -0
- data/vendor/prism/src/util/pm_char.c +318 -0
- data/vendor/prism/src/util/pm_constant_pool.c +342 -0
- data/vendor/prism/src/util/pm_integer.c +670 -0
- data/vendor/prism/src/util/pm_list.c +49 -0
- data/vendor/prism/src/util/pm_memchr.c +35 -0
- data/vendor/prism/src/util/pm_newline_list.c +125 -0
- data/vendor/prism/src/util/pm_string.c +383 -0
- data/vendor/prism/src/util/pm_strncasecmp.c +36 -0
- data/vendor/prism/src/util/pm_strpbrk.c +206 -0
- data/vendor/prism/templates/ext/prism/api_node.c.erb +282 -0
- data/vendor/prism/templates/include/prism/ast.h.erb +226 -0
- data/vendor/prism/templates/include/prism/diagnostic.h.erb +130 -0
- data/vendor/prism/templates/java/org/prism/AbstractNodeVisitor.java.erb +22 -0
- data/vendor/prism/templates/java/org/prism/Loader.java.erb +434 -0
- data/vendor/prism/templates/java/org/prism/Nodes.java.erb +403 -0
- data/vendor/prism/templates/javascript/src/deserialize.js.erb +448 -0
- data/vendor/prism/templates/javascript/src/nodes.js.erb +197 -0
- data/vendor/prism/templates/javascript/src/visitor.js.erb +78 -0
- data/vendor/prism/templates/lib/prism/compiler.rb.erb +43 -0
- data/vendor/prism/templates/lib/prism/dispatcher.rb.erb +103 -0
- data/vendor/prism/templates/lib/prism/dot_visitor.rb.erb +189 -0
- data/vendor/prism/templates/lib/prism/dsl.rb.erb +133 -0
- data/vendor/prism/templates/lib/prism/inspect_visitor.rb.erb +131 -0
- data/vendor/prism/templates/lib/prism/mutation_compiler.rb.erb +19 -0
- data/vendor/prism/templates/lib/prism/node.rb.erb +515 -0
- data/vendor/prism/templates/lib/prism/reflection.rb.erb +136 -0
- data/vendor/prism/templates/lib/prism/serialize.rb.erb +602 -0
- data/vendor/prism/templates/lib/prism/visitor.rb.erb +55 -0
- data/vendor/prism/templates/rbi/prism/dsl.rbi.erb +68 -0
- data/vendor/prism/templates/rbi/prism/node.rbi.erb +164 -0
- data/vendor/prism/templates/rbi/prism/visitor.rbi.erb +18 -0
- data/vendor/prism/templates/sig/prism/_private/dot_visitor.rbs.erb +45 -0
- data/vendor/prism/templates/sig/prism/dsl.rbs.erb +31 -0
- data/vendor/prism/templates/sig/prism/mutation_compiler.rbs.erb +7 -0
- data/vendor/prism/templates/sig/prism/node.rbs.erb +132 -0
- data/vendor/prism/templates/sig/prism/visitor.rbs.erb +17 -0
- data/vendor/prism/templates/sig/prism.rbs.erb +89 -0
- data/vendor/prism/templates/src/diagnostic.c.erb +523 -0
- data/vendor/prism/templates/src/node.c.erb +333 -0
- data/vendor/prism/templates/src/prettyprint.c.erb +166 -0
- data/vendor/prism/templates/src/serialize.c.erb +406 -0
- data/vendor/prism/templates/src/token_type.c.erb +369 -0
- data/vendor/prism/templates/template.rb +689 -0
- 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
|