prism 1.1.0 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
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}│ "]