prism 1.1.0 → 1.3.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.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +39 -1
  3. data/Makefile +1 -1
  4. data/config.yml +422 -3
  5. data/docs/build_system.md +8 -11
  6. data/docs/relocation.md +34 -0
  7. data/ext/prism/api_node.c +18 -10
  8. data/ext/prism/extconf.rb +13 -36
  9. data/ext/prism/extension.c +68 -0
  10. data/ext/prism/extension.h +1 -1
  11. data/include/prism/ast.h +427 -3
  12. data/include/prism/defines.h +22 -7
  13. data/include/prism/diagnostic.h +1 -0
  14. data/include/prism/parser.h +25 -12
  15. data/include/prism/version.h +2 -2
  16. data/include/prism.h +47 -0
  17. data/lib/prism/dot_visitor.rb +10 -0
  18. data/lib/prism/dsl.rb +4 -4
  19. data/lib/prism/ffi.rb +49 -2
  20. data/lib/prism/inspect_visitor.rb +2 -0
  21. data/lib/prism/node.rb +1839 -96
  22. data/lib/prism/parse_result/errors.rb +1 -1
  23. data/lib/prism/parse_result.rb +140 -3
  24. data/lib/prism/reflection.rb +2 -2
  25. data/lib/prism/relocation.rb +504 -0
  26. data/lib/prism/serialize.rb +17 -5
  27. data/lib/prism/string_query.rb +30 -0
  28. data/lib/prism/translation/parser/compiler.rb +36 -26
  29. data/lib/prism/translation/parser.rb +3 -3
  30. data/lib/prism/translation/ripper.rb +1 -5
  31. data/lib/prism/translation/ruby_parser.rb +14 -5
  32. data/lib/prism.rb +6 -4
  33. data/prism.gemspec +7 -1
  34. data/rbi/prism/dsl.rbi +4 -4
  35. data/rbi/prism/node.rbi +5118 -1030
  36. data/rbi/prism/parse_result.rbi +29 -0
  37. data/rbi/prism/string_query.rbi +12 -0
  38. data/rbi/prism.rbi +34 -34
  39. data/sig/prism/dsl.rbs +2 -2
  40. data/sig/prism/node.rbs +13 -98
  41. data/sig/prism/parse_result.rbs +20 -0
  42. data/sig/prism/relocation.rbs +185 -0
  43. data/sig/prism/string_query.rbs +11 -0
  44. data/src/diagnostic.c +3 -1
  45. data/src/node.c +18 -0
  46. data/src/prettyprint.c +32 -0
  47. data/src/prism.c +586 -195
  48. data/src/regexp.c +7 -3
  49. data/src/serialize.c +12 -0
  50. data/src/static_literals.c +1 -1
  51. data/src/util/pm_char.c +1 -1
  52. data/src/util/pm_string.c +1 -0
  53. metadata +9 -3
@@ -137,14 +137,15 @@
137
137
  #endif
138
138
 
139
139
  /**
140
- * isinf on Windows is defined as accepting a float, but on POSIX systems it
141
- * accepts a float, a double, or a long double. We want to mirror this behavior
142
- * on windows.
140
+ * isinf on POSIX systems it accepts a float, a double, or a long double.
141
+ * But mingw didn't provide an isinf macro, only an isinf function that only
142
+ * accepts floats, so we need to use _finite instead.
143
143
  */
144
- #ifdef _WIN32
145
- # include <float.h>
146
- # undef isinf
147
- # define isinf(x) (sizeof(x) == sizeof(float) ? !_finitef(x) : !_finite(x))
144
+ #ifdef __MINGW64__
145
+ #include <float.h>
146
+ #define PRISM_ISINF(x) (!_finite(x))
147
+ #else
148
+ #define PRISM_ISINF(x) isinf(x)
148
149
  #endif
149
150
 
150
151
  /**
@@ -239,4 +240,18 @@
239
240
  #define PRISM_UNLIKELY(x) (x)
240
241
  #endif
241
242
 
243
+ /**
244
+ * We use -Wimplicit-fallthrough to guard potentially unintended fall-through between cases of a switch.
245
+ * Use PRISM_FALLTHROUGH to explicitly annotate cases where the fallthrough is intentional.
246
+ */
247
+ #if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 202311L // C23 or later
248
+ #define PRISM_FALLTHROUGH [[fallthrough]];
249
+ #elif defined(__GNUC__) || defined(__clang__)
250
+ #define PRISM_FALLTHROUGH __attribute__((fallthrough));
251
+ #elif defined(_MSC_VER)
252
+ #define PRISM_FALLTHROUGH __fallthrough;
253
+ #else
254
+ #define PRISM_FALLTHROUGH
255
+ #endif
256
+
242
257
  #endif
@@ -170,6 +170,7 @@ typedef enum {
170
170
  PM_ERR_INSTANCE_VARIABLE_BARE,
171
171
  PM_ERR_INVALID_BLOCK_EXIT,
172
172
  PM_ERR_INVALID_CHARACTER,
173
+ PM_ERR_INVALID_COMMA,
173
174
  PM_ERR_INVALID_ENCODING_MAGIC_COMMENT,
174
175
  PM_ERR_INVALID_ESCAPE_CHARACTER,
175
176
  PM_ERR_INVALID_FLOAT_EXPONENT,
@@ -82,6 +82,23 @@ typedef enum {
82
82
  PM_HEREDOC_INDENT_TILDE,
83
83
  } pm_heredoc_indent_t;
84
84
 
85
+ /**
86
+ * All of the information necessary to store to lexing a heredoc.
87
+ */
88
+ typedef struct {
89
+ /** A pointer to the start of the heredoc identifier. */
90
+ const uint8_t *ident_start;
91
+
92
+ /** The length of the heredoc identifier. */
93
+ size_t ident_length;
94
+
95
+ /** The type of quote that the heredoc uses. */
96
+ pm_heredoc_quote_t quote;
97
+
98
+ /** The type of indentation that the heredoc uses. */
99
+ pm_heredoc_indent_t indent;
100
+ } pm_heredoc_lex_mode_t;
101
+
85
102
  /**
86
103
  * When lexing Ruby source, the lexer has a small amount of state to tell which
87
104
  * kind of token it is currently lexing. For example, when we find the start of
@@ -210,17 +227,10 @@ typedef struct pm_lex_mode {
210
227
  } string;
211
228
 
212
229
  struct {
213
- /** A pointer to the start of the heredoc identifier. */
214
- const uint8_t *ident_start;
215
-
216
- /** The length of the heredoc identifier. */
217
- size_t ident_length;
218
-
219
- /** The type of quote that the heredoc uses. */
220
- pm_heredoc_quote_t quote;
221
-
222
- /** The type of indentation that the heredoc uses. */
223
- pm_heredoc_indent_t indent;
230
+ /**
231
+ * All of the data necessary to lex a heredoc.
232
+ */
233
+ pm_heredoc_lex_mode_t base;
224
234
 
225
235
  /**
226
236
  * This is the pointer to the character where lexing should resume
@@ -233,7 +243,7 @@ typedef struct pm_lex_mode {
233
243
  * line so that we know how much to dedent each line in the case of
234
244
  * a tilde heredoc.
235
245
  */
236
- size_t common_whitespace;
246
+ size_t *common_whitespace;
237
247
 
238
248
  /** True if the previous token ended with a line continuation. */
239
249
  bool line_continuation;
@@ -382,6 +392,9 @@ typedef enum {
382
392
  /** a rescue statement within a module statement */
383
393
  PM_CONTEXT_MODULE_RESCUE,
384
394
 
395
+ /** a multiple target expression */
396
+ PM_CONTEXT_MULTI_TARGET,
397
+
385
398
  /** a parenthesized expression */
386
399
  PM_CONTEXT_PARENS,
387
400
 
@@ -14,7 +14,7 @@
14
14
  /**
15
15
  * The minor version of the Prism library as an int.
16
16
  */
17
- #define PRISM_VERSION_MINOR 1
17
+ #define PRISM_VERSION_MINOR 3
18
18
 
19
19
  /**
20
20
  * The patch version of the Prism library as an int.
@@ -24,6 +24,6 @@
24
24
  /**
25
25
  * The version of the Prism library as a constant string.
26
26
  */
27
- #define PRISM_VERSION "1.1.0"
27
+ #define PRISM_VERSION "1.3.0"
28
28
 
29
29
  #endif
data/include/prism.h CHANGED
@@ -234,6 +234,53 @@ PRISM_EXPORTED_FUNCTION void pm_dump_json(pm_buffer_t *buffer, const pm_parser_t
234
234
 
235
235
  #endif
236
236
 
237
+ /**
238
+ * Represents the results of a slice query.
239
+ */
240
+ typedef enum {
241
+ /** Returned if the encoding given to a slice query was invalid. */
242
+ PM_STRING_QUERY_ERROR = -1,
243
+
244
+ /** Returned if the result of the slice query is false. */
245
+ PM_STRING_QUERY_FALSE,
246
+
247
+ /** Returned if the result of the slice query is true. */
248
+ PM_STRING_QUERY_TRUE
249
+ } pm_string_query_t;
250
+
251
+ /**
252
+ * Check that the slice is a valid local variable name.
253
+ *
254
+ * @param source The source to check.
255
+ * @param length The length of the source.
256
+ * @param encoding_name The name of the encoding of the source.
257
+ * @return PM_STRING_QUERY_TRUE if the query is true, PM_STRING_QUERY_FALSE if
258
+ * the query is false, and PM_STRING_QUERY_ERROR if the encoding was invalid.
259
+ */
260
+ PRISM_EXPORTED_FUNCTION pm_string_query_t pm_string_query_local(const uint8_t *source, size_t length, const char *encoding_name);
261
+
262
+ /**
263
+ * Check that the slice is a valid constant name.
264
+ *
265
+ * @param source The source to check.
266
+ * @param length The length of the source.
267
+ * @param encoding_name The name of the encoding of the source.
268
+ * @return PM_STRING_QUERY_TRUE if the query is true, PM_STRING_QUERY_FALSE if
269
+ * the query is false, and PM_STRING_QUERY_ERROR if the encoding was invalid.
270
+ */
271
+ PRISM_EXPORTED_FUNCTION pm_string_query_t pm_string_query_constant(const uint8_t *source, size_t length, const char *encoding_name);
272
+
273
+ /**
274
+ * Check that the slice is a valid method name.
275
+ *
276
+ * @param source The source to check.
277
+ * @param length The length of the source.
278
+ * @param encoding_name The name of the encoding of the source.
279
+ * @return PM_STRING_QUERY_TRUE if the query is true, PM_STRING_QUERY_FALSE if
280
+ * the query is false, and PM_STRING_QUERY_ERROR if the encoding was invalid.
281
+ */
282
+ PRISM_EXPORTED_FUNCTION pm_string_query_t pm_string_query_method_name(const uint8_t *source, size_t length, const char *encoding_name);
283
+
237
284
  /**
238
285
  * @mainpage
239
286
  *
@@ -4414,6 +4414,11 @@ module Prism
4414
4414
  # keyword_loc
4415
4415
  table.field("keyword_loc", location_inspect(node.keyword_loc))
4416
4416
 
4417
+ # do_keyword_loc
4418
+ unless (do_keyword_loc = node.do_keyword_loc).nil?
4419
+ table.field("do_keyword_loc", location_inspect(do_keyword_loc))
4420
+ end
4421
+
4417
4422
  # closing_loc
4418
4423
  unless (closing_loc = node.closing_loc).nil?
4419
4424
  table.field("closing_loc", location_inspect(closing_loc))
@@ -4490,6 +4495,11 @@ module Prism
4490
4495
  # keyword_loc
4491
4496
  table.field("keyword_loc", location_inspect(node.keyword_loc))
4492
4497
 
4498
+ # do_keyword_loc
4499
+ unless (do_keyword_loc = node.do_keyword_loc).nil?
4500
+ table.field("do_keyword_loc", location_inspect(do_keyword_loc))
4501
+ end
4502
+
4493
4503
  # closing_loc
4494
4504
  unless (closing_loc = node.closing_loc).nil?
4495
4505
  table.field("closing_loc", location_inspect(closing_loc))
data/lib/prism/dsl.rb CHANGED
@@ -804,8 +804,8 @@ module Prism
804
804
  end
805
805
 
806
806
  # Create a new UntilNode node.
807
- def until_node(source: default_source, node_id: 0, location: default_location, flags: 0, keyword_loc: location, closing_loc: nil, predicate: default_node(source, location), statements: nil)
808
- UntilNode.new(source, node_id, location, flags, keyword_loc, closing_loc, predicate, statements)
807
+ def until_node(source: default_source, node_id: 0, location: default_location, flags: 0, keyword_loc: location, do_keyword_loc: nil, closing_loc: nil, predicate: default_node(source, location), statements: nil)
808
+ UntilNode.new(source, node_id, location, flags, keyword_loc, do_keyword_loc, closing_loc, predicate, statements)
809
809
  end
810
810
 
811
811
  # Create a new WhenNode node.
@@ -814,8 +814,8 @@ module Prism
814
814
  end
815
815
 
816
816
  # Create a new WhileNode node.
817
- def while_node(source: default_source, node_id: 0, location: default_location, flags: 0, keyword_loc: location, closing_loc: nil, predicate: default_node(source, location), statements: nil)
818
- WhileNode.new(source, node_id, location, flags, keyword_loc, closing_loc, predicate, statements)
817
+ def while_node(source: default_source, node_id: 0, location: default_location, flags: 0, keyword_loc: location, do_keyword_loc: nil, closing_loc: nil, predicate: default_node(source, location), statements: nil)
818
+ WhileNode.new(source, node_id, location, flags, keyword_loc, do_keyword_loc, closing_loc, predicate, statements)
819
819
  end
820
820
 
821
821
  # Create a new XStringNode node.
data/lib/prism/ffi.rb CHANGED
@@ -13,7 +13,15 @@ module Prism
13
13
 
14
14
  # Define the library that we will be pulling functions from. Note that this
15
15
  # must align with the build shared library from make/rake.
16
- ffi_lib File.expand_path("../../build/libprism.#{RbConfig::CONFIG["SOEXT"]}", __dir__)
16
+ libprism_in_build = File.expand_path("../../build/libprism.#{RbConfig::CONFIG["SOEXT"]}", __dir__)
17
+ libprism_in_libdir = "#{RbConfig::CONFIG["libdir"]}/prism/libprism.#{RbConfig::CONFIG["SOEXT"]}"
18
+ if File.exist? libprism_in_build
19
+ INCLUDE_DIR = File.expand_path("../../include", __dir__)
20
+ ffi_lib libprism_in_build
21
+ else
22
+ INCLUDE_DIR = "#{RbConfig::CONFIG["libdir"]}/prism/include"
23
+ ffi_lib libprism_in_libdir
24
+ end
17
25
 
18
26
  # Convert a native C type declaration into a symbol that FFI understands.
19
27
  # For example:
@@ -38,7 +46,7 @@ module Prism
38
46
  # given functions. For each one, define a function with the same name and
39
47
  # signature as the C function.
40
48
  def self.load_exported_functions_from(header, *functions, callbacks)
41
- File.foreach(File.expand_path("../../include/#{header}", __dir__)) do |line|
49
+ File.foreach("#{INCLUDE_DIR}/#{header}") do |line|
42
50
  # We only want to attempt to load exported functions.
43
51
  next unless line.start_with?("PRISM_EXPORTED_FUNCTION ")
44
52
 
@@ -73,6 +81,7 @@ module Prism
73
81
 
74
82
  callback :pm_parse_stream_fgets_t, [:pointer, :int, :pointer], :pointer
75
83
  enum :pm_string_init_result_t, %i[PM_STRING_INIT_SUCCESS PM_STRING_INIT_ERROR_GENERIC PM_STRING_INIT_ERROR_DIRECTORY]
84
+ enum :pm_string_query_t, [:PM_STRING_QUERY_ERROR, -1, :PM_STRING_QUERY_FALSE, :PM_STRING_QUERY_TRUE]
76
85
 
77
86
  load_exported_functions_from(
78
87
  "prism.h",
@@ -83,6 +92,9 @@ module Prism
83
92
  "pm_serialize_lex",
84
93
  "pm_serialize_parse_lex",
85
94
  "pm_parse_success_p",
95
+ "pm_string_query_local",
96
+ "pm_string_query_constant",
97
+ "pm_string_query_method_name",
86
98
  [:pm_parse_stream_fgets_t]
87
99
  )
88
100
 
@@ -492,4 +504,39 @@ module Prism
492
504
  values.pack(template)
493
505
  end
494
506
  end
507
+
508
+ # Here we are going to patch StringQuery to put in the class-level methods so
509
+ # that it can maintain a consistent interface
510
+ class StringQuery
511
+ class << self
512
+ # Mirrors the C extension's StringQuery::local? method.
513
+ def local?(string)
514
+ query(LibRubyParser.pm_string_query_local(string, string.bytesize, string.encoding.name))
515
+ end
516
+
517
+ # Mirrors the C extension's StringQuery::constant? method.
518
+ def constant?(string)
519
+ query(LibRubyParser.pm_string_query_constant(string, string.bytesize, string.encoding.name))
520
+ end
521
+
522
+ # Mirrors the C extension's StringQuery::method_name? method.
523
+ def method_name?(string)
524
+ query(LibRubyParser.pm_string_query_method_name(string, string.bytesize, string.encoding.name))
525
+ end
526
+
527
+ private
528
+
529
+ # Parse the enum result and return an appropriate boolean.
530
+ def query(result)
531
+ case result
532
+ when :PM_STRING_QUERY_ERROR
533
+ raise ArgumentError, "Invalid or non ascii-compatible encoding"
534
+ when :PM_STRING_QUERY_FALSE
535
+ false
536
+ when :PM_STRING_QUERY_TRUE
537
+ true
538
+ end
539
+ end
540
+ end
541
+ end
495
542
  end
@@ -2287,6 +2287,7 @@ module Prism
2287
2287
  flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ("begin_modifier" if node.begin_modifier?)].compact
2288
2288
  commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent]
2289
2289
  commands << ["├── keyword_loc: #{inspect_location(node.keyword_loc)}\n", indent]
2290
+ commands << ["├── do_keyword_loc: #{inspect_location(node.do_keyword_loc)}\n", indent]
2290
2291
  commands << ["├── closing_loc: #{inspect_location(node.closing_loc)}\n", indent]
2291
2292
  commands << ["├── predicate:\n", indent]
2292
2293
  commands << [node.predicate, "#{indent}│ "]
@@ -2328,6 +2329,7 @@ module Prism
2328
2329
  flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ("begin_modifier" if node.begin_modifier?)].compact
2329
2330
  commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent]
2330
2331
  commands << ["├── keyword_loc: #{inspect_location(node.keyword_loc)}\n", indent]
2332
+ commands << ["├── do_keyword_loc: #{inspect_location(node.do_keyword_loc)}\n", indent]
2331
2333
  commands << ["├── closing_loc: #{inspect_location(node.closing_loc)}\n", indent]
2332
2334
  commands << ["├── predicate:\n", indent]
2333
2335
  commands << [node.predicate, "#{indent}│ "]