herb 0.7.2-x86_64-linux-gnu → 0.7.4-x86_64-linux-gnu

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 (133) 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/3.0/herb.so +0 -0
  10. data/lib/herb/3.1/herb.so +0 -0
  11. data/lib/herb/3.2/herb.so +0 -0
  12. data/lib/herb/3.3/herb.so +0 -0
  13. data/lib/herb/3.4/herb.so +0 -0
  14. data/lib/herb/engine.rb +8 -1
  15. data/lib/herb/version.rb +1 -1
  16. data/src/analyze.c +5 -9
  17. data/src/analyze_helpers.c +17 -6
  18. data/src/herb.c +2 -2
  19. data/src/include/parser.h +2 -2
  20. data/src/include/pretty_print.h +1 -1
  21. data/src/include/version.h +1 -1
  22. data/src/parser.c +3 -2
  23. data/src/pretty_print.c +1 -1
  24. data/templates/ext/herb/error_helpers.c.erb +85 -0
  25. data/templates/ext/herb/error_helpers.h.erb +12 -0
  26. data/templates/ext/herb/nodes.c.erb +90 -0
  27. data/templates/ext/herb/nodes.h.erb +9 -0
  28. data/templates/javascript/packages/core/src/errors.ts.erb +193 -0
  29. data/templates/javascript/packages/core/src/node-type-guards.ts.erb +325 -0
  30. data/templates/javascript/packages/core/src/nodes.ts.erb +414 -0
  31. data/templates/javascript/packages/core/src/visitor.ts.erb +29 -0
  32. data/templates/javascript/packages/node/extension/error_helpers.cpp.erb +113 -0
  33. data/templates/javascript/packages/node/extension/error_helpers.h.erb +17 -0
  34. data/templates/javascript/packages/node/extension/nodes.cpp.erb +111 -0
  35. data/templates/javascript/packages/node/extension/nodes.h.erb +17 -0
  36. data/templates/lib/herb/ast/nodes.rb.erb +117 -0
  37. data/templates/lib/herb/errors.rb.erb +106 -0
  38. data/templates/lib/herb/visitor.rb.erb +28 -0
  39. data/templates/sig/serialized_ast_errors.rbs.erb +10 -0
  40. data/templates/sig/serialized_ast_nodes.rbs.erb +10 -0
  41. data/templates/src/ast_nodes.c.erb +145 -0
  42. data/templates/src/ast_pretty_print.c.erb +97 -0
  43. data/templates/src/errors.c.erb +245 -0
  44. data/templates/src/include/ast_nodes.h.erb +46 -0
  45. data/templates/src/include/ast_pretty_print.h.erb +14 -0
  46. data/templates/src/include/errors.h.erb +58 -0
  47. data/templates/src/visitor.c.erb +47 -0
  48. data/templates/template.rb +406 -0
  49. data/templates/wasm/error_helpers.cpp.erb +93 -0
  50. data/templates/wasm/error_helpers.h.erb +15 -0
  51. data/templates/wasm/nodes.cpp.erb +79 -0
  52. data/templates/wasm/nodes.h.erb +15 -0
  53. data/vendor/prism/Rakefile +75 -0
  54. data/vendor/prism/config.yml +4713 -0
  55. data/vendor/prism/include/prism/ast.h +8190 -0
  56. data/vendor/prism/include/prism/defines.h +260 -0
  57. data/vendor/prism/include/prism/diagnostic.h +455 -0
  58. data/vendor/prism/include/prism/encoding.h +283 -0
  59. data/vendor/prism/include/prism/node.h +129 -0
  60. data/vendor/prism/include/prism/options.h +482 -0
  61. data/vendor/prism/include/prism/pack.h +163 -0
  62. data/vendor/prism/include/prism/parser.h +933 -0
  63. data/vendor/prism/include/prism/prettyprint.h +34 -0
  64. data/vendor/prism/include/prism/regexp.h +43 -0
  65. data/vendor/prism/include/prism/static_literals.h +121 -0
  66. data/vendor/prism/include/prism/util/pm_buffer.h +236 -0
  67. data/vendor/prism/include/prism/util/pm_char.h +204 -0
  68. data/vendor/prism/include/prism/util/pm_constant_pool.h +218 -0
  69. data/vendor/prism/include/prism/util/pm_integer.h +130 -0
  70. data/vendor/prism/include/prism/util/pm_list.h +103 -0
  71. data/vendor/prism/include/prism/util/pm_memchr.h +29 -0
  72. data/vendor/prism/include/prism/util/pm_newline_list.h +113 -0
  73. data/vendor/prism/include/prism/util/pm_string.h +200 -0
  74. data/vendor/prism/include/prism/util/pm_strncasecmp.h +32 -0
  75. data/vendor/prism/include/prism/util/pm_strpbrk.h +46 -0
  76. data/vendor/prism/include/prism/version.h +29 -0
  77. data/vendor/prism/include/prism.h +408 -0
  78. data/vendor/prism/src/diagnostic.c +848 -0
  79. data/vendor/prism/src/encoding.c +5235 -0
  80. data/vendor/prism/src/node.c +8676 -0
  81. data/vendor/prism/src/options.c +328 -0
  82. data/vendor/prism/src/pack.c +509 -0
  83. data/vendor/prism/src/prettyprint.c +8941 -0
  84. data/vendor/prism/src/prism.c +23302 -0
  85. data/vendor/prism/src/regexp.c +790 -0
  86. data/vendor/prism/src/serialize.c +2268 -0
  87. data/vendor/prism/src/static_literals.c +617 -0
  88. data/vendor/prism/src/token_type.c +703 -0
  89. data/vendor/prism/src/util/pm_buffer.c +357 -0
  90. data/vendor/prism/src/util/pm_char.c +318 -0
  91. data/vendor/prism/src/util/pm_constant_pool.c +342 -0
  92. data/vendor/prism/src/util/pm_integer.c +670 -0
  93. data/vendor/prism/src/util/pm_list.c +49 -0
  94. data/vendor/prism/src/util/pm_memchr.c +35 -0
  95. data/vendor/prism/src/util/pm_newline_list.c +125 -0
  96. data/vendor/prism/src/util/pm_string.c +383 -0
  97. data/vendor/prism/src/util/pm_strncasecmp.c +36 -0
  98. data/vendor/prism/src/util/pm_strpbrk.c +206 -0
  99. data/vendor/prism/templates/ext/prism/api_node.c.erb +282 -0
  100. data/vendor/prism/templates/include/prism/ast.h.erb +226 -0
  101. data/vendor/prism/templates/include/prism/diagnostic.h.erb +130 -0
  102. data/vendor/prism/templates/java/org/prism/AbstractNodeVisitor.java.erb +22 -0
  103. data/vendor/prism/templates/java/org/prism/Loader.java.erb +434 -0
  104. data/vendor/prism/templates/java/org/prism/Nodes.java.erb +403 -0
  105. data/vendor/prism/templates/javascript/src/deserialize.js.erb +448 -0
  106. data/vendor/prism/templates/javascript/src/nodes.js.erb +197 -0
  107. data/vendor/prism/templates/javascript/src/visitor.js.erb +78 -0
  108. data/vendor/prism/templates/lib/prism/compiler.rb.erb +43 -0
  109. data/vendor/prism/templates/lib/prism/dispatcher.rb.erb +103 -0
  110. data/vendor/prism/templates/lib/prism/dot_visitor.rb.erb +189 -0
  111. data/vendor/prism/templates/lib/prism/dsl.rb.erb +133 -0
  112. data/vendor/prism/templates/lib/prism/inspect_visitor.rb.erb +131 -0
  113. data/vendor/prism/templates/lib/prism/mutation_compiler.rb.erb +19 -0
  114. data/vendor/prism/templates/lib/prism/node.rb.erb +515 -0
  115. data/vendor/prism/templates/lib/prism/reflection.rb.erb +136 -0
  116. data/vendor/prism/templates/lib/prism/serialize.rb.erb +602 -0
  117. data/vendor/prism/templates/lib/prism/visitor.rb.erb +55 -0
  118. data/vendor/prism/templates/rbi/prism/dsl.rbi.erb +68 -0
  119. data/vendor/prism/templates/rbi/prism/node.rbi.erb +164 -0
  120. data/vendor/prism/templates/rbi/prism/visitor.rbi.erb +18 -0
  121. data/vendor/prism/templates/sig/prism/_private/dot_visitor.rbs.erb +45 -0
  122. data/vendor/prism/templates/sig/prism/dsl.rbs.erb +31 -0
  123. data/vendor/prism/templates/sig/prism/mutation_compiler.rbs.erb +7 -0
  124. data/vendor/prism/templates/sig/prism/node.rbs.erb +132 -0
  125. data/vendor/prism/templates/sig/prism/visitor.rbs.erb +17 -0
  126. data/vendor/prism/templates/sig/prism.rbs.erb +89 -0
  127. data/vendor/prism/templates/src/diagnostic.c.erb +523 -0
  128. data/vendor/prism/templates/src/node.c.erb +333 -0
  129. data/vendor/prism/templates/src/prettyprint.c.erb +166 -0
  130. data/vendor/prism/templates/src/serialize.c.erb +406 -0
  131. data/vendor/prism/templates/src/token_type.c.erb +369 -0
  132. data/vendor/prism/templates/template.rb +689 -0
  133. metadata +112 -2
@@ -0,0 +1,689 @@
1
+ #!/usr/bin/env ruby
2
+ # typed: ignore
3
+
4
+ require "erb"
5
+ require "fileutils"
6
+ require "yaml"
7
+
8
+ module Prism
9
+ module Template
10
+ SERIALIZE_ONLY_SEMANTICS_FIELDS = ENV.fetch("PRISM_SERIALIZE_ONLY_SEMANTICS_FIELDS", false)
11
+ REMOVE_ON_ERROR_TYPES = SERIALIZE_ONLY_SEMANTICS_FIELDS
12
+ CHECK_FIELD_KIND = ENV.fetch("CHECK_FIELD_KIND", false)
13
+
14
+ JAVA_BACKEND = ENV["PRISM_JAVA_BACKEND"] || "truffleruby"
15
+ JAVA_STRING_TYPE = JAVA_BACKEND == "jruby" ? "org.jruby.RubySymbol" : "String"
16
+ INCLUDE_NODE_ID = !SERIALIZE_ONLY_SEMANTICS_FIELDS || JAVA_BACKEND == "jruby"
17
+
18
+ COMMON_FLAGS_COUNT = 2
19
+
20
+ class Error
21
+ attr_reader :name
22
+
23
+ def initialize(name)
24
+ @name = name
25
+ end
26
+ end
27
+
28
+ class Warning
29
+ attr_reader :name
30
+
31
+ def initialize(name)
32
+ @name = name
33
+ end
34
+ end
35
+
36
+ # This module contains methods for escaping characters in JavaDoc comments.
37
+ module JavaDoc
38
+ ESCAPES = {
39
+ "'" => "'",
40
+ "\"" => """,
41
+ "@" => "@",
42
+ "&" => "&",
43
+ "<" => "&lt;",
44
+ ">" => "&gt;"
45
+ }.freeze
46
+
47
+ def self.escape(value)
48
+ value.gsub(/['&"<>@]/, ESCAPES)
49
+ end
50
+ end
51
+
52
+ # A comment attached to a field or node.
53
+ class ConfigComment
54
+ attr_reader :value
55
+
56
+ def initialize(value)
57
+ @value = value
58
+ end
59
+
60
+ def each_line(&block)
61
+ value.each_line { |line| yield line.prepend(" ").rstrip }
62
+ end
63
+
64
+ def each_java_line(&block)
65
+ ConfigComment.new(JavaDoc.escape(value)).each_line(&block)
66
+ end
67
+ end
68
+
69
+ # This represents a field on a node. It contains all of the necessary
70
+ # information to template out the code for that field.
71
+ class Field
72
+ attr_reader :name, :comment, :options
73
+
74
+ def initialize(name:, comment: nil, **options)
75
+ @name = name
76
+ @comment = comment
77
+ @options = options
78
+ end
79
+
80
+ def each_comment_line(&block)
81
+ ConfigComment.new(comment).each_line(&block) if comment
82
+ end
83
+
84
+ def each_comment_java_line(&block)
85
+ ConfigComment.new(comment).each_java_line(&block) if comment
86
+ end
87
+
88
+ def semantic_field?
89
+ true
90
+ end
91
+
92
+ def should_be_serialized?
93
+ SERIALIZE_ONLY_SEMANTICS_FIELDS ? semantic_field? : true
94
+ end
95
+ end
96
+
97
+ # Some node fields can be specialized if they point to a specific kind of
98
+ # node and not just a generic node.
99
+ class NodeKindField < Field
100
+ def initialize(kind:, **options)
101
+ @kind = kind
102
+ super(**options)
103
+ end
104
+
105
+ def c_type
106
+ if specific_kind
107
+ "pm_#{specific_kind.gsub(/(?<=.)[A-Z]/, "_\\0").downcase}"
108
+ else
109
+ "pm_node"
110
+ end
111
+ end
112
+
113
+ def ruby_type
114
+ specific_kind || "Node"
115
+ end
116
+
117
+ def java_type
118
+ specific_kind || "Node"
119
+ end
120
+
121
+ def java_cast
122
+ if specific_kind
123
+ "(Nodes.#{@kind}) "
124
+ else
125
+ ""
126
+ end
127
+ end
128
+
129
+ def specific_kind
130
+ @kind unless @kind.is_a?(Array)
131
+ end
132
+
133
+ def union_kind
134
+ @kind if @kind.is_a?(Array)
135
+ end
136
+ end
137
+
138
+ # This represents a field on a node that is itself a node. We pass them as
139
+ # references and store them as references.
140
+ class NodeField < NodeKindField
141
+ def rbs_class
142
+ if specific_kind
143
+ specific_kind
144
+ elsif union_kind
145
+ union_kind.join(" | ")
146
+ else
147
+ "Prism::node"
148
+ end
149
+ end
150
+
151
+ def rbi_class
152
+ if specific_kind
153
+ "Prism::#{specific_kind}"
154
+ elsif union_kind
155
+ "T.any(#{union_kind.map { |kind| "Prism::#{kind}" }.join(", ")})"
156
+ else
157
+ "Prism::Node"
158
+ end
159
+ end
160
+
161
+ def check_field_kind
162
+ if union_kind
163
+ "[#{union_kind.join(', ')}].include?(#{name}.class)"
164
+ else
165
+ "#{name}.is_a?(#{ruby_type})"
166
+ end
167
+ end
168
+ end
169
+
170
+ # This represents a field on a node that is itself a node and can be
171
+ # optionally null. We pass them as references and store them as references.
172
+ class OptionalNodeField < NodeKindField
173
+ def rbs_class
174
+ if specific_kind
175
+ "#{specific_kind}?"
176
+ elsif union_kind
177
+ [*union_kind, "nil"].join(" | ")
178
+ else
179
+ "Prism::node?"
180
+ end
181
+ end
182
+
183
+ def rbi_class
184
+ if specific_kind
185
+ "T.nilable(Prism::#{specific_kind})"
186
+ elsif union_kind
187
+ "T.nilable(T.any(#{union_kind.map { |kind| "Prism::#{kind}" }.join(", ")}))"
188
+ else
189
+ "T.nilable(Prism::Node)"
190
+ end
191
+ end
192
+
193
+ def check_field_kind
194
+ if union_kind
195
+ "[#{union_kind.join(', ')}, NilClass].include?(#{name}.class)"
196
+ else
197
+ "#{name}.nil? || #{name}.is_a?(#{ruby_type})"
198
+ end
199
+ end
200
+ end
201
+
202
+ # This represents a field on a node that is a list of nodes. We pass them as
203
+ # references and store them directly on the struct.
204
+ class NodeListField < NodeKindField
205
+ def rbs_class
206
+ if specific_kind
207
+ "Array[#{specific_kind}]"
208
+ elsif union_kind
209
+ "Array[#{union_kind.join(" | ")}]"
210
+ else
211
+ "Array[Prism::node]"
212
+ end
213
+ end
214
+
215
+ def rbi_class
216
+ if specific_kind
217
+ "T::Array[Prism::#{specific_kind}]"
218
+ elsif union_kind
219
+ "T::Array[T.any(#{union_kind.map { |kind| "Prism::#{kind}" }.join(", ")})]"
220
+ else
221
+ "T::Array[Prism::Node]"
222
+ end
223
+ end
224
+
225
+ def java_type
226
+ "#{super}[]"
227
+ end
228
+
229
+ def check_field_kind
230
+ if union_kind
231
+ "#{name}.all? { |n| [#{union_kind.join(', ')}].include?(n.class) }"
232
+ else
233
+ "#{name}.all? { |n| n.is_a?(#{ruby_type}) }"
234
+ end
235
+ end
236
+ end
237
+
238
+ # This represents a field on a node that is the ID of a string interned
239
+ # through the parser's constant pool.
240
+ class ConstantField < Field
241
+ def rbs_class
242
+ "Symbol"
243
+ end
244
+
245
+ def rbi_class
246
+ "Symbol"
247
+ end
248
+
249
+ def java_type
250
+ JAVA_STRING_TYPE
251
+ end
252
+ end
253
+
254
+ # This represents a field on a node that is the ID of a string interned
255
+ # through the parser's constant pool and can be optionally null.
256
+ class OptionalConstantField < Field
257
+ def rbs_class
258
+ "Symbol?"
259
+ end
260
+
261
+ def rbi_class
262
+ "T.nilable(Symbol)"
263
+ end
264
+
265
+ def java_type
266
+ JAVA_STRING_TYPE
267
+ end
268
+ end
269
+
270
+ # This represents a field on a node that is a list of IDs that are associated
271
+ # with strings interned through the parser's constant pool.
272
+ class ConstantListField < Field
273
+ def rbs_class
274
+ "Array[Symbol]"
275
+ end
276
+
277
+ def rbi_class
278
+ "T::Array[Symbol]"
279
+ end
280
+
281
+ def java_type
282
+ "#{JAVA_STRING_TYPE}[]"
283
+ end
284
+ end
285
+
286
+ # This represents a field on a node that is a string.
287
+ class StringField < Field
288
+ def rbs_class
289
+ "String"
290
+ end
291
+
292
+ def rbi_class
293
+ "String"
294
+ end
295
+
296
+ def java_type
297
+ "byte[]"
298
+ end
299
+ end
300
+
301
+ # This represents a field on a node that is a location.
302
+ class LocationField < Field
303
+ def semantic_field?
304
+ false
305
+ end
306
+
307
+ def rbs_class
308
+ "Location"
309
+ end
310
+
311
+ def rbi_class
312
+ "Prism::Location"
313
+ end
314
+
315
+ def java_type
316
+ "Location"
317
+ end
318
+ end
319
+
320
+ # This represents a field on a node that is a location that is optional.
321
+ class OptionalLocationField < Field
322
+ def semantic_field?
323
+ false
324
+ end
325
+
326
+ def rbs_class
327
+ "Location?"
328
+ end
329
+
330
+ def rbi_class
331
+ "T.nilable(Prism::Location)"
332
+ end
333
+
334
+ def java_type
335
+ "Location"
336
+ end
337
+ end
338
+
339
+ # This represents an integer field.
340
+ class UInt8Field < Field
341
+ def rbs_class
342
+ "Integer"
343
+ end
344
+
345
+ def rbi_class
346
+ "Integer"
347
+ end
348
+
349
+ def java_type
350
+ "int"
351
+ end
352
+ end
353
+
354
+ # This represents an integer field.
355
+ class UInt32Field < Field
356
+ def rbs_class
357
+ "Integer"
358
+ end
359
+
360
+ def rbi_class
361
+ "Integer"
362
+ end
363
+
364
+ def java_type
365
+ "int"
366
+ end
367
+ end
368
+
369
+ # This represents an arbitrarily-sized integer. When it gets to Ruby it will
370
+ # be an Integer.
371
+ class IntegerField < Field
372
+ def rbs_class
373
+ "Integer"
374
+ end
375
+
376
+ def rbi_class
377
+ "Integer"
378
+ end
379
+
380
+ def java_type
381
+ "Object"
382
+ end
383
+ end
384
+
385
+ # This represents a double-precision floating point number. When it gets to
386
+ # Ruby it will be a Float.
387
+ class DoubleField < Field
388
+ def rbs_class
389
+ "Float"
390
+ end
391
+
392
+ def rbi_class
393
+ "Float"
394
+ end
395
+
396
+ def java_type
397
+ "double"
398
+ end
399
+ end
400
+
401
+ # This class represents a node in the tree, configured by the config.yml file
402
+ # in YAML format. It contains information about the name of the node and the
403
+ # various child nodes it contains.
404
+ class NodeType
405
+ attr_reader :name, :type, :human, :flags, :fields, :newline, :comment
406
+
407
+ def initialize(config, flags)
408
+ @name = config.fetch("name")
409
+
410
+ type = @name.gsub(/(?<=.)[A-Z]/, "_\\0")
411
+ @type = "PM_#{type.upcase}"
412
+ @human = type.downcase
413
+
414
+ @fields =
415
+ config.fetch("fields", []).map do |field|
416
+ type = field_type_for(field.fetch("type"))
417
+
418
+ options = field.transform_keys(&:to_sym)
419
+ options.delete(:type)
420
+
421
+ # If/when we have documentation on every field, this should be
422
+ # changed to use fetch instead of delete.
423
+ comment = options.delete(:comment)
424
+
425
+ if kinds = options[:kind]
426
+ kinds = [kinds] unless kinds.is_a?(Array)
427
+ kinds = kinds.map do |kind|
428
+ case kind
429
+ when "non-void expression"
430
+ # the actual list of types would be way too long
431
+ "Node"
432
+ when "pattern expression"
433
+ # the list of all possible types is too long with 37+ different classes
434
+ "Node"
435
+ when Hash
436
+ kind = kind.fetch("on error")
437
+ REMOVE_ON_ERROR_TYPES ? nil : kind
438
+ else
439
+ kind
440
+ end
441
+ end.compact
442
+ if kinds.size == 1
443
+ kinds = kinds.first
444
+ kinds = nil if kinds == "Node"
445
+ end
446
+ options[:kind] = kinds
447
+ else
448
+ if type < NodeKindField
449
+ raise "Missing kind in config.yml for field #{@name}##{options.fetch(:name)}"
450
+ end
451
+ end
452
+
453
+ type.new(comment: comment, **options)
454
+ end
455
+
456
+ @flags = config.key?("flags") ? flags.fetch(config.fetch("flags")) : nil
457
+ @newline = config.fetch("newline", true)
458
+ @comment = config.fetch("comment")
459
+ end
460
+
461
+ def each_comment_line(&block)
462
+ ConfigComment.new(comment).each_line(&block)
463
+ end
464
+
465
+ def each_comment_java_line(&block)
466
+ ConfigComment.new(comment).each_java_line(&block)
467
+ end
468
+
469
+ def semantic_fields
470
+ @semantic_fields ||= @fields.select(&:semantic_field?)
471
+ end
472
+
473
+ # Should emit serialized length of node so implementations can skip
474
+ # the node to enable lazy parsing.
475
+ def needs_serialized_length?
476
+ name == "DefNode"
477
+ end
478
+
479
+ private
480
+
481
+ def field_type_for(name)
482
+ case name
483
+ when "node" then NodeField
484
+ when "node?" then OptionalNodeField
485
+ when "node[]" then NodeListField
486
+ when "string" then StringField
487
+ when "constant" then ConstantField
488
+ when "constant?" then OptionalConstantField
489
+ when "constant[]" then ConstantListField
490
+ when "location" then LocationField
491
+ when "location?" then OptionalLocationField
492
+ when "uint8" then UInt8Field
493
+ when "uint32" then UInt32Field
494
+ when "integer" then IntegerField
495
+ when "double" then DoubleField
496
+ else raise("Unknown field type: #{name.inspect}")
497
+ end
498
+ end
499
+ end
500
+
501
+ # This represents a token in the lexer.
502
+ class Token
503
+ attr_reader :name, :value, :comment
504
+
505
+ def initialize(config)
506
+ @name = config.fetch("name")
507
+ @value = config["value"]
508
+ @comment = config.fetch("comment")
509
+ end
510
+ end
511
+
512
+ # Represents a set of flags that should be internally represented with an enum.
513
+ class Flags
514
+ # Represents an individual flag within a set of flags.
515
+ class Flag
516
+ attr_reader :name, :camelcase, :comment
517
+
518
+ def initialize(config)
519
+ @name = config.fetch("name")
520
+ @camelcase = @name.split("_").map(&:capitalize).join
521
+ @comment = config.fetch("comment")
522
+ end
523
+ end
524
+
525
+ attr_reader :name, :human, :values, :comment
526
+
527
+ def initialize(config)
528
+ @name = config.fetch("name")
529
+ @human = @name.gsub(/(?<=.)[A-Z]/, "_\\0").downcase
530
+ @values = config.fetch("values").map { |flag| Flag.new(flag) }
531
+ @comment = config.fetch("comment")
532
+ end
533
+
534
+ def self.empty
535
+ new("name" => "", "values" => [], "comment" => "")
536
+ end
537
+ end
538
+
539
+ class << self
540
+ # This templates out a file using ERB with the given locals. The locals are
541
+ # derived from the config.yml file.
542
+ def render(name, write_to: nil)
543
+ filepath = "templates/#{name}.erb"
544
+ template = File.expand_path("../#{filepath}", __dir__)
545
+
546
+ erb = read_template(template)
547
+ extension = File.extname(filepath.gsub(".erb", ""))
548
+
549
+ heading =
550
+ case extension
551
+ when ".rb"
552
+ <<~HEADING
553
+ # frozen_string_literal: true
554
+ # :markup: markdown
555
+
556
+ =begin
557
+ --
558
+ This file is generated by the templates/template.rb script and should not be
559
+ modified manually. See #{filepath}
560
+ if you are looking to modify the template
561
+ ++
562
+ =end
563
+
564
+ HEADING
565
+ when ".rbs"
566
+ <<~HEADING
567
+ # This file is generated by the templates/template.rb script and should not be
568
+ # modified manually. See #{filepath}
569
+ # if you are looking to modify the template
570
+
571
+ HEADING
572
+ when ".rbi"
573
+ <<~HEADING
574
+ # typed: strict
575
+
576
+ =begin
577
+ This file is generated by the templates/template.rb script and should not be
578
+ modified manually. See #{filepath}
579
+ if you are looking to modify the template
580
+ =end
581
+
582
+ HEADING
583
+ else
584
+ <<~HEADING
585
+ /* :markup: markdown */
586
+
587
+ /*----------------------------------------------------------------------------*/
588
+ /* This file is generated by the templates/template.rb script and should not */
589
+ /* be modified manually. See */
590
+ /* #{filepath.ljust(74)} */
591
+ /* if you are looking to modify the */
592
+ /* template */
593
+ /*----------------------------------------------------------------------------*/
594
+
595
+ HEADING
596
+ end
597
+
598
+ write_to ||= File.expand_path("../#{name}", __dir__)
599
+ contents = heading + erb.result_with_hash(locals)
600
+
601
+ if (extension == ".c" || extension == ".h") && !contents.ascii_only?
602
+ # Enforce that we only have ASCII characters here. This is necessary
603
+ # for non-UTF-8 locales that only allow ASCII characters in C source
604
+ # files.
605
+ contents.each_line.with_index(1) do |line, line_number|
606
+ raise "Non-ASCII character on line #{line_number} of #{write_to}" unless line.ascii_only?
607
+ end
608
+ end
609
+
610
+ FileUtils.mkdir_p(File.dirname(write_to))
611
+ File.write(write_to, contents)
612
+ end
613
+
614
+ private
615
+
616
+ def read_template(filepath)
617
+ template = File.read(filepath, encoding: Encoding::UTF_8)
618
+ erb = erb(template)
619
+ erb.filename = filepath
620
+ erb
621
+ end
622
+
623
+ def erb(template)
624
+ ERB.new(template, trim_mode: "-")
625
+ end
626
+
627
+ def locals
628
+ @locals ||=
629
+ begin
630
+ config = YAML.load_file(File.expand_path("../config.yml", __dir__))
631
+ flags = config.fetch("flags").to_h { |flags| [flags["name"], Flags.new(flags)] }
632
+
633
+ {
634
+ errors: config.fetch("errors").map { |name| Error.new(name) },
635
+ warnings: config.fetch("warnings").map { |name| Warning.new(name) },
636
+ nodes: config.fetch("nodes").map { |node| NodeType.new(node, flags) }.sort_by(&:name),
637
+ tokens: config.fetch("tokens").map { |token| Token.new(token) },
638
+ flags: flags.values
639
+ }
640
+ end
641
+ end
642
+ end
643
+
644
+ TEMPLATES = [
645
+ "ext/prism/api_node.c",
646
+ "include/prism/ast.h",
647
+ "include/prism/diagnostic.h",
648
+ "javascript/src/deserialize.js",
649
+ "javascript/src/nodes.js",
650
+ "javascript/src/visitor.js",
651
+ "java/org/prism/Loader.java",
652
+ "java/org/prism/Nodes.java",
653
+ "java/org/prism/AbstractNodeVisitor.java",
654
+ "lib/prism/compiler.rb",
655
+ "lib/prism/dispatcher.rb",
656
+ "lib/prism/dot_visitor.rb",
657
+ "lib/prism/dsl.rb",
658
+ "lib/prism/inspect_visitor.rb",
659
+ "lib/prism/mutation_compiler.rb",
660
+ "lib/prism/node.rb",
661
+ "lib/prism/reflection.rb",
662
+ "lib/prism/serialize.rb",
663
+ "lib/prism/visitor.rb",
664
+ "src/diagnostic.c",
665
+ "src/node.c",
666
+ "src/prettyprint.c",
667
+ "src/serialize.c",
668
+ "src/token_type.c",
669
+ "rbi/prism/dsl.rbi",
670
+ "rbi/prism/node.rbi",
671
+ "rbi/prism/visitor.rbi",
672
+ "sig/prism.rbs",
673
+ "sig/prism/dsl.rbs",
674
+ "sig/prism/mutation_compiler.rbs",
675
+ "sig/prism/node.rbs",
676
+ "sig/prism/visitor.rbs",
677
+ "sig/prism/_private/dot_visitor.rbs"
678
+ ]
679
+ end
680
+ end
681
+
682
+ if __FILE__ == $0
683
+ if ARGV.empty?
684
+ Prism::Template::TEMPLATES.each { |filepath| Prism::Template.render(filepath) }
685
+ else # ruby/ruby
686
+ name, write_to = ARGV
687
+ Prism::Template.render(name, write_to: write_to)
688
+ end
689
+ end