prism 0.15.1 → 0.16.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +20 -1
  3. data/Makefile +6 -0
  4. data/README.md +2 -0
  5. data/config.yml +21 -20
  6. data/docs/configuration.md +2 -0
  7. data/docs/javascript.md +90 -0
  8. data/docs/releasing.md +27 -0
  9. data/docs/ruby_api.md +2 -0
  10. data/ext/prism/api_node.c +66 -68
  11. data/ext/prism/extension.c +73 -0
  12. data/ext/prism/extension.h +1 -1
  13. data/include/prism/ast.h +40 -40
  14. data/include/prism/defines.h +9 -0
  15. data/include/prism/enc/pm_encoding.h +1 -0
  16. data/include/prism/node.h +0 -17
  17. data/include/prism/parser.h +1 -0
  18. data/include/prism/prettyprint.h +15 -0
  19. data/include/prism/util/pm_buffer.h +10 -4
  20. data/include/prism/util/pm_constant_pool.h +1 -1
  21. data/include/prism/util/pm_newline_list.h +1 -1
  22. data/include/prism/version.h +3 -3
  23. data/include/prism.h +11 -11
  24. data/lib/prism/compiler.rb +0 -3
  25. data/lib/prism/debug.rb +20 -6
  26. data/lib/prism/desugar_compiler.rb +1 -1
  27. data/lib/prism/dispatcher.rb +0 -14
  28. data/lib/prism/dsl.rb +8 -13
  29. data/lib/prism/ffi.rb +25 -0
  30. data/lib/prism/lex_compat.rb +1 -1
  31. data/lib/prism/mutation_compiler.rb +3 -8
  32. data/lib/prism/node.rb +123 -159
  33. data/lib/prism/node_ext.rb +23 -16
  34. data/lib/prism/parse_result.rb +21 -5
  35. data/lib/prism/pattern.rb +3 -3
  36. data/lib/prism/serialize.rb +901 -305
  37. data/lib/prism/visitor.rb +0 -3
  38. data/prism.gemspec +8 -1
  39. data/rbi/prism.rbi +7261 -0
  40. data/rbi/prism_static.rbi +182 -0
  41. data/sig/prism.rbs +4439 -0
  42. data/sig/prism_static.rbs +110 -0
  43. data/src/enc/pm_unicode.c +1 -1
  44. data/src/node.c +28 -29
  45. data/src/prettyprint.c +7674 -1647
  46. data/src/prism.c +353 -300
  47. data/src/regexp.c +2 -0
  48. data/src/serialize.c +392 -381
  49. data/src/util/pm_buffer.c +47 -12
  50. data/src/util/pm_constant_pool.c +1 -1
  51. data/src/util/pm_newline_list.c +8 -54
  52. metadata +9 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 967c414d9d2354dd828368920733b1c81240d1f0c267fdc422d075965f1cec94
4
- data.tar.gz: a258399d77a8f4c6ca297fcea435c8b2c167bced045b32d2884f270a9483c33b
3
+ metadata.gz: 498f20248401af377faf45d30b7154d819b8bea560be2747c030d5ae536851c9
4
+ data.tar.gz: 8d3bb4fc0afb899869d8d6a55a526a40769fb46a582cf465035de750824d72d7
5
5
  SHA512:
6
- metadata.gz: 104746123657e06d12be6da5ebc6778afda2f94646d3544ff88acd42c5ef2d01f46855f96548360166ee0b4ce6d5164107f7a857c1b52984237421287da4e85c
7
- data.tar.gz: d4237da63459ab266d5ee824d680eadef6ee893da5b7fd47960b55f4496630e18b0804b455e51576dd52ab4f085a3eb013d1e08fc7c4577c1ed07441abf6b53e
6
+ metadata.gz: 2d71744c4342a671b578ed7ebf3dede7b0146404a61e40ed8b082c544f5d8e9d83dc4e1f0780555cab4c70bd052156e26f9d8b46a2f2012bcedf89bf5b21d54b
7
+ data.tar.gz: 2a2178b5615fe4c55d7c16156901423fe38dee075e4f60cc893c367630e7c136cc7d6bc19f3fcf037a6958d158b2663f61389656754fe679570271503a5b35a2
data/CHANGELOG.md CHANGED
@@ -6,6 +6,24 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) a
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [0.16.0] - 2023-10-30
10
+
11
+ ### Added
12
+
13
+ - `InterpolatedMatchLastLineNode#options` and `MatchLastLineNode#options` are added, which are the same methods as are exposed on `InterpolatedRegularExpressionNode` and `RegularExpressionNode`.
14
+ - The project can now be compiled with `wasi-sdk` to expose a WebAssembly interface.
15
+ - `ArgumentsNode#keyword_splat?` is added to indicate if the arguments node has a keyword splat.
16
+ - The C API `pm_prettyprint` has a much improved output which lines up closely with `Node#inspect`.
17
+ - Prism now ships with `RBS` and `RBI` type signatures (in the `/sig` and `/rbi` directories, respectively).
18
+ - `Prism::parse_comments` and `Prism::parse_file_comments` APIs are added to extract only the comments from the source code.
19
+
20
+ ### Changed
21
+
22
+ - **BREAKING**: `Multi{Target,Write}Node#targets` is split up now into `lefts`, `rest`, and `rights`. This is to avoid having to scan the list in the case that there are splat nodes.
23
+ - Some bugs are fixed on `Multi{Target,Write}Node` accidentally creating additional nesting when not necessary.
24
+ - **BREAKING**: `RequiredDestructuredParameterNode` has been removed in favor of using `MultiTargetNode` in those places.
25
+ - **BREAKING**: `HashPatternNode#assocs` has been renamed to `HashPatternNode#elements`. `HashPatternNode#kwrest` has been renamed to `HashPatternNode#rest`.
26
+
9
27
  ## [0.15.1] - 2023-10-18
10
28
 
11
29
  ### Changed
@@ -201,7 +219,8 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) a
201
219
 
202
220
  - 🎉 Initial release! 🎉
203
221
 
204
- [unreleased]: https://github.com/ruby/prism/compare/v0.15.1...HEAD
222
+ [unreleased]: https://github.com/ruby/prism/compare/v0.16.0...HEAD
223
+ [0.16.0]: https://github.com/ruby/prism/compare/v0.15.1...v0.16.0
205
224
  [0.15.1]: https://github.com/ruby/prism/compare/v0.15.0...v0.15.1
206
225
  [0.15.0]: https://github.com/ruby/prism/compare/v0.14.0...v0.15.0
207
226
  [0.14.0]: https://github.com/ruby/prism/compare/v0.13.0...v0.14.0
data/Makefile CHANGED
@@ -13,6 +13,7 @@ SOEXT := $(shell ruby -e 'puts RbConfig::CONFIG["SOEXT"]')
13
13
  CPPFLAGS := -Iinclude
14
14
  CFLAGS := -g -O2 -std=c99 -Wall -Werror -Wextra -Wpedantic -Wundef -Wconversion -fPIC -fvisibility=hidden
15
15
  CC := cc
16
+ WASI_SDK_PATH := /opt/wasi-sdk
16
17
 
17
18
  HEADERS := $(shell find include -name '*.h')
18
19
  SOURCES := $(shell find src -name '*.c')
@@ -23,6 +24,7 @@ all: shared static
23
24
 
24
25
  shared: build/librubyparser.$(SOEXT)
25
26
  static: build/librubyparser.a
27
+ wasm: javascript/src/prism.wasm
26
28
 
27
29
  build/librubyparser.$(SOEXT): $(SHARED_OBJECTS)
28
30
  $(ECHO) "linking $@"
@@ -32,6 +34,10 @@ build/librubyparser.a: $(STATIC_OBJECTS)
32
34
  $(ECHO) "building $@"
33
35
  $(Q) $(AR) $(ARFLAGS) $@ $(STATIC_OBJECTS) $(Q1:0=>/dev/null)
34
36
 
37
+ javascript/src/prism.wasm: Makefile $(SOURCES) $(HEADERS)
38
+ $(ECHO) "building $@"
39
+ $(Q) $(WASI_SDK_PATH)/bin/clang --sysroot=$(WASI_SDK_PATH)/share/wasi-sysroot/ $(DEBUG_FLAGS) -DPRISM_EXPORT_SYMBOLS -D_WASI_EMULATED_MMAN -lwasi-emulated-mman $(CPPFLAGS) $(CFLAGS) -Wl,--export-all -Wl,--no-entry -mexec-model=reactor -o $@ $(SOURCES)
40
+
35
41
  build/shared/%.o: src/%.c Makefile $(HEADERS)
36
42
  $(ECHO) "compiling $@"
37
43
  $(Q) mkdir -p $(@D)
data/README.md CHANGED
@@ -85,7 +85,9 @@ See the [CONTRIBUTING.md](CONTRIBUTING.md) file for more information. We additio
85
85
  * [Encoding](docs/encoding.md)
86
86
  * [Fuzzing](docs/fuzzing.md)
87
87
  * [Heredocs](docs/heredocs.md)
88
+ * [JavaScript](docs/javascript.md)
88
89
  * [Mapping](docs/mapping.md)
90
+ * [Releasing](docs/releasing.md)
89
91
  * [Ripper](docs/ripper.md)
90
92
  * [Ruby API](docs/ruby_api.md)
91
93
  * [Serialization](docs/serialization.md)
data/config.yml CHANGED
@@ -329,6 +329,10 @@ tokens:
329
329
  - name: __END__
330
330
  comment: "marker for the point in the file at which the parser should stop"
331
331
  flags:
332
+ - name: ArgumentsNodeFlags
333
+ values:
334
+ - name: KEYWORD_SPLAT
335
+ comment: "if arguments contain keyword splat"
332
336
  - name: CallNodeFlags
333
337
  values:
334
338
  - name: SAFE_NAVIGATION
@@ -432,6 +436,9 @@ nodes:
432
436
  fields:
433
437
  - name: arguments
434
438
  type: node[]
439
+ - name: flags
440
+ type: flags
441
+ kind: ArgumentsNodeFlags
435
442
  comment: |
436
443
  Represents a set of arguments to a method or a keyword.
437
444
 
@@ -1349,9 +1356,9 @@ nodes:
1349
1356
  fields:
1350
1357
  - name: constant
1351
1358
  type: node?
1352
- - name: assocs
1359
+ - name: elements
1353
1360
  type: node[]
1354
- - name: kwrest
1361
+ - name: rest
1355
1362
  type: node?
1356
1363
  - name: opening_loc
1357
1364
  type: location?
@@ -1915,7 +1922,11 @@ nodes:
1915
1922
  ^^^^^^^^^^^^^^
1916
1923
  - name: MultiTargetNode
1917
1924
  fields:
1918
- - name: targets
1925
+ - name: lefts
1926
+ type: node[]
1927
+ - name: rest
1928
+ type: node?
1929
+ - name: rights
1919
1930
  type: node[]
1920
1931
  - name: lparen_loc
1921
1932
  type: location?
@@ -1924,11 +1935,15 @@ nodes:
1924
1935
  comment: |
1925
1936
  Represents a multi-target expression.
1926
1937
 
1927
- a, b, c = 1, 2, 3
1928
- ^^^^^^^
1938
+ a, (b, c) = 1, 2, 3
1939
+ ^^^^^^
1929
1940
  - name: MultiWriteNode
1930
1941
  fields:
1931
- - name: targets
1942
+ - name: lefts
1943
+ type: node[]
1944
+ - name: rest
1945
+ type: node?
1946
+ - name: rights
1932
1947
  type: node[]
1933
1948
  - name: lparen_loc
1934
1949
  type: location?
@@ -2169,20 +2184,6 @@ nodes:
2169
2184
 
2170
2185
  /foo/i
2171
2186
  ^^^^^^
2172
- - name: RequiredDestructuredParameterNode
2173
- fields:
2174
- - name: parameters
2175
- type: node[]
2176
- - name: opening_loc
2177
- type: location
2178
- - name: closing_loc
2179
- type: location
2180
- comment: |
2181
- Represents a destructured required parameter node.
2182
-
2183
- def foo((bar, baz))
2184
- ^^^^^^^^^^
2185
- end
2186
2187
  - name: RequiredParameterNode
2187
2188
  fields:
2188
2189
  - name: name
@@ -4,6 +4,8 @@ A lot of code in prism's repository is templated from a single configuration fil
4
4
 
5
5
  * `ext/prism/api_node.c` - for defining how to build Ruby objects for the nodes out of C structs
6
6
  * `include/prism/ast.h` - for defining the C structs that represent the nodes
7
+ * `javascript/src/deserialize.js` - for defining how to deserialize the nodes in JavaScript
8
+ * `javascript/src/nodes.js` - for defining the nodes in JavaScript
7
9
  * `java/org/prism/AbstractNodeVisitor.java` - for defining the visitor interface for the nodes in Java
8
10
  * `java/org/prism/Loader.java` - for defining how to deserialize the nodes in Java
9
11
  * `java/org/prism/Nodes.java` - for defining the nodes in Java
@@ -0,0 +1,90 @@
1
+ # JavaScript
2
+
3
+ Prism provides bindings to JavaScript out of the box.
4
+
5
+ ## Node
6
+
7
+ To use the package from node, install the `@ruby/prism` dependency:
8
+
9
+ ```sh
10
+ npm install @ruby/prism
11
+ ```
12
+
13
+ Then import the package:
14
+
15
+ ```js
16
+ import { loadPrism } from "@ruby/prism";
17
+ ```
18
+
19
+ Then call the load function to get a parse function:
20
+
21
+ ```js
22
+ const parse = await loadPrism();
23
+ ```
24
+
25
+ ## Browser
26
+
27
+ To use the package from the browser, you will need to do some additional work. The [javascript/example.html](javascript/example.html) file shows an example of running Prism in the browser. You will need to instantiate the WebAssembly module yourself and then pass it to the `parsePrism` function.
28
+
29
+ First, get a shim for WASI since not all browsers support it yet.
30
+
31
+ ```js
32
+ import { WASI } from "https://unpkg.com/@bjorn3/browser_wasi_shim@latest/dist/index.js";
33
+ ```
34
+
35
+ Next, import the `parsePrism` function from `@ruby/prism`, either through a CDN or by bundling it with your application.
36
+
37
+ ```js
38
+ import { parsePrism } from "https://unpkg.com/@ruby/prism@latest/src/parsePrism.js";
39
+ ```
40
+
41
+ Next, fetch and instantiate the WebAssembly module. You can access it through a CDN or by bundling it with your application.
42
+
43
+ ```js
44
+ const wasm = await WebAssembly.compileStreaming(fetch("https://unpkg.com/@ruby/prism@latest/src/prism.wasm"));
45
+ ```
46
+
47
+ Next, instantiate the module and initialize WASI.
48
+
49
+ ```js
50
+ const wasi = new WASI([], [], []);
51
+ const instance = await WebAssembly.instantiate(wasm, { wasi_snapshot_preview1: wasi.wasiImport });
52
+ wasi.initialize(instance);
53
+ ```
54
+
55
+ Finally, you can create a function that will parse a string of Ruby code.
56
+
57
+ ```js
58
+ function parse(source) {
59
+ return parsePrism(instance.exports, source);
60
+ }
61
+ ```
62
+
63
+ ## API
64
+
65
+ Now that we have access to a `parse` function, we can use it to parse Ruby code:
66
+
67
+ ```js
68
+ const parseResult = parse("1 + 2");
69
+ ```
70
+
71
+ A ParseResult object is very similar to the Prism::ParseResult object from Ruby. It has the same properties: `value`, `comments`, `magicComments`, `errors`, and `warnings`. Here we can serialize the AST to JSON.
72
+
73
+ ```js
74
+ console.log(JSON.stringify(parseResult.value, null, 2));
75
+ ```
76
+
77
+ ## Building
78
+
79
+ To build the WASM package yourself, first obtain a copy of `wasi-sdk`. You can retrieve this here: <https://github.com/WebAssembly/wasi-sdk>. Next, run:
80
+
81
+ ```sh
82
+ make wasm WASI_SDK_PATH=path/to/wasi-sdk
83
+ ```
84
+
85
+ This will generate `javascript/src/prism.wasm`. From there, you can run the tests to verify everything was generated correctly.
86
+
87
+ ```sh
88
+ cd javascript
89
+ node test
90
+ ```
data/docs/releasing.md ADDED
@@ -0,0 +1,27 @@
1
+ # Releasing
2
+
3
+ To release a new version of Prism, perform the following steps:
4
+
5
+ ## Preparation
6
+
7
+ * Update the CHANGELOG.md file.
8
+ * Add a new section for the new version at the top of the file.
9
+ * Fill in the relevant changes — it may be easiest to click the link for the `Unreleased` heading to find the commits.
10
+ * Update the links at the bottom of the file.
11
+ * Update the version in the following files:
12
+ * `prism.gemspec` in the `Gem::Specification#version=` method call
13
+ * `ext/prism/extension.h` in the `EXPECTED_PRISM_VERSION` macro
14
+ * `include/prism/version.h` in the version macros
15
+ * `javascript/package.json` in the `version` field
16
+ * `rust/prism-sys/tests/utils_tests.rs` in the `version_test` function
17
+ * `templates/java/org/prism/Loader.java.erb` in the `load` function
18
+ * `templates/javascript/src/deserialize.js.erb` in the version constants
19
+ * `templates/lib/prism/serialize.rb.erb` in the version constants
20
+ * Run `bundle install` to update the `Gemfile.lock` file.
21
+ * Update `rust/prism-sys/Cargo.toml` to match the new version and run `cargo build`
22
+ * Update `rust/prism/Cargo.toml` to match the new version and run `cargo build`
23
+ * Commit all of the updated files.
24
+
25
+ ## Publishing
26
+
27
+ * Run `bundle exec rake release` to publish the gem to [rubygems.org](rubygems.org). Note that you must have access to the `prism` gem to do this.
data/docs/ruby_api.md CHANGED
@@ -23,3 +23,5 @@ The full API is documented below.
23
23
  * `Prism.parse_lex(source)` - parse the syntax tree corresponding to the given source string and return it within a parse result, along with the tokens
24
24
  * `Prism.parse_lex_file(filepath)` - parse the syntax tree corresponding to the given source file and return it within a parse result, along with the tokens
25
25
  * `Prism.load(source, serialized)` - load the serialized syntax tree using the source as a reference into a syntax tree
26
+ * `Prism.parse_comments(source)` - parse the comments corresponding to the given source string and return them
27
+ * `Prism.parse_file_comments(source)` - parse the comments corresponding to the given source file and return them
data/ext/prism/api_node.c CHANGED
@@ -132,7 +132,6 @@ static VALUE rb_cPrismRangeNode;
132
132
  static VALUE rb_cPrismRationalNode;
133
133
  static VALUE rb_cPrismRedoNode;
134
134
  static VALUE rb_cPrismRegularExpressionNode;
135
- static VALUE rb_cPrismRequiredDestructuredParameterNode;
136
135
  static VALUE rb_cPrismRequiredParameterNode;
137
136
  static VALUE rb_cPrismRescueModifierNode;
138
137
  static VALUE rb_cPrismRescueNode;
@@ -607,10 +606,10 @@ pm_ast_new(pm_parser_t *parser, pm_node_t *node, rb_encoding *encoding) {
607
606
  case PM_HASH_PATTERN_NODE: {
608
607
  pm_hash_pattern_node_t *cast = (pm_hash_pattern_node_t *) node;
609
608
  pm_node_stack_push(&node_stack, (pm_node_t *) cast->constant);
610
- for (size_t index = 0; index < cast->assocs.size; index++) {
611
- pm_node_stack_push(&node_stack, (pm_node_t *) cast->assocs.nodes[index]);
609
+ for (size_t index = 0; index < cast->elements.size; index++) {
610
+ pm_node_stack_push(&node_stack, (pm_node_t *) cast->elements.nodes[index]);
612
611
  }
613
- pm_node_stack_push(&node_stack, (pm_node_t *) cast->kwrest);
612
+ pm_node_stack_push(&node_stack, (pm_node_t *) cast->rest);
614
613
  break;
615
614
  }
616
615
  #line 108 "api_node.c.erb"
@@ -806,16 +805,24 @@ pm_ast_new(pm_parser_t *parser, pm_node_t *node, rb_encoding *encoding) {
806
805
  #line 108 "api_node.c.erb"
807
806
  case PM_MULTI_TARGET_NODE: {
808
807
  pm_multi_target_node_t *cast = (pm_multi_target_node_t *) node;
809
- for (size_t index = 0; index < cast->targets.size; index++) {
810
- pm_node_stack_push(&node_stack, (pm_node_t *) cast->targets.nodes[index]);
808
+ for (size_t index = 0; index < cast->lefts.size; index++) {
809
+ pm_node_stack_push(&node_stack, (pm_node_t *) cast->lefts.nodes[index]);
810
+ }
811
+ pm_node_stack_push(&node_stack, (pm_node_t *) cast->rest);
812
+ for (size_t index = 0; index < cast->rights.size; index++) {
813
+ pm_node_stack_push(&node_stack, (pm_node_t *) cast->rights.nodes[index]);
811
814
  }
812
815
  break;
813
816
  }
814
817
  #line 108 "api_node.c.erb"
815
818
  case PM_MULTI_WRITE_NODE: {
816
819
  pm_multi_write_node_t *cast = (pm_multi_write_node_t *) node;
817
- for (size_t index = 0; index < cast->targets.size; index++) {
818
- pm_node_stack_push(&node_stack, (pm_node_t *) cast->targets.nodes[index]);
820
+ for (size_t index = 0; index < cast->lefts.size; index++) {
821
+ pm_node_stack_push(&node_stack, (pm_node_t *) cast->lefts.nodes[index]);
822
+ }
823
+ pm_node_stack_push(&node_stack, (pm_node_t *) cast->rest);
824
+ for (size_t index = 0; index < cast->rights.size; index++) {
825
+ pm_node_stack_push(&node_stack, (pm_node_t *) cast->rights.nodes[index]);
819
826
  }
820
827
  pm_node_stack_push(&node_stack, (pm_node_t *) cast->value);
821
828
  break;
@@ -908,14 +915,6 @@ pm_ast_new(pm_parser_t *parser, pm_node_t *node, rb_encoding *encoding) {
908
915
  pm_node_stack_push(&node_stack, (pm_node_t *) cast->numeric);
909
916
  break;
910
917
  }
911
- #line 108 "api_node.c.erb"
912
- case PM_REQUIRED_DESTRUCTURED_PARAMETER_NODE: {
913
- pm_required_destructured_parameter_node_t *cast = (pm_required_destructured_parameter_node_t *) node;
914
- for (size_t index = 0; index < cast->parameters.size; index++) {
915
- pm_node_stack_push(&node_stack, (pm_node_t *) cast->parameters.nodes[index]);
916
- }
917
- break;
918
- }
919
918
  #line 108 "api_node.c.erb"
920
919
  case PM_RESCUE_MODIFIER_NODE: {
921
920
  pm_rescue_modifier_node_t *cast = (pm_rescue_modifier_node_t *) node;
@@ -1123,7 +1122,7 @@ pm_ast_new(pm_parser_t *parser, pm_node_t *node, rb_encoding *encoding) {
1123
1122
  #line 134 "api_node.c.erb"
1124
1123
  case PM_ARGUMENTS_NODE: {
1125
1124
  pm_arguments_node_t *cast = (pm_arguments_node_t *) node;
1126
- VALUE argv[2];
1125
+ VALUE argv[3];
1127
1126
 
1128
1127
  // arguments
1129
1128
  #line 148 "api_node.c.erb"
@@ -1132,10 +1131,14 @@ pm_ast_new(pm_parser_t *parser, pm_node_t *node, rb_encoding *encoding) {
1132
1131
  rb_ary_push(argv[0], rb_ary_pop(value_stack));
1133
1132
  }
1134
1133
 
1134
+ // flags
1135
+ #line 179 "api_node.c.erb"
1136
+ argv[1] = ULONG2NUM(node->flags & ~PM_NODE_FLAG_COMMON_MASK);
1137
+
1135
1138
  // location
1136
- argv[1] = pm_location_new(parser, node->location.start, node->location.end, source);
1139
+ argv[2] = pm_location_new(parser, node->location.start, node->location.end, source);
1137
1140
 
1138
- rb_ary_push(value_stack, rb_class_new_instance(2, argv, rb_cPrismArgumentsNode));
1141
+ rb_ary_push(value_stack, rb_class_new_instance(3, argv, rb_cPrismArgumentsNode));
1139
1142
  break;
1140
1143
  }
1141
1144
  #line 134 "api_node.c.erb"
@@ -2694,14 +2697,14 @@ pm_ast_new(pm_parser_t *parser, pm_node_t *node, rb_encoding *encoding) {
2694
2697
  #line 145 "api_node.c.erb"
2695
2698
  argv[0] = rb_ary_pop(value_stack);
2696
2699
 
2697
- // assocs
2700
+ // elements
2698
2701
  #line 148 "api_node.c.erb"
2699
- argv[1] = rb_ary_new_capa(cast->assocs.size);
2700
- for (size_t index = 0; index < cast->assocs.size; index++) {
2702
+ argv[1] = rb_ary_new_capa(cast->elements.size);
2703
+ for (size_t index = 0; index < cast->elements.size; index++) {
2701
2704
  rb_ary_push(argv[1], rb_ary_pop(value_stack));
2702
2705
  }
2703
2706
 
2704
- // kwrest
2707
+ // rest
2705
2708
  #line 145 "api_node.c.erb"
2706
2709
  argv[2] = rb_ary_pop(value_stack);
2707
2710
 
@@ -3681,61 +3684,83 @@ pm_ast_new(pm_parser_t *parser, pm_node_t *node, rb_encoding *encoding) {
3681
3684
  #line 134 "api_node.c.erb"
3682
3685
  case PM_MULTI_TARGET_NODE: {
3683
3686
  pm_multi_target_node_t *cast = (pm_multi_target_node_t *) node;
3684
- VALUE argv[4];
3687
+ VALUE argv[6];
3685
3688
 
3686
- // targets
3689
+ // lefts
3687
3690
  #line 148 "api_node.c.erb"
3688
- argv[0] = rb_ary_new_capa(cast->targets.size);
3689
- for (size_t index = 0; index < cast->targets.size; index++) {
3691
+ argv[0] = rb_ary_new_capa(cast->lefts.size);
3692
+ for (size_t index = 0; index < cast->lefts.size; index++) {
3690
3693
  rb_ary_push(argv[0], rb_ary_pop(value_stack));
3691
3694
  }
3692
3695
 
3696
+ // rest
3697
+ #line 145 "api_node.c.erb"
3698
+ argv[1] = rb_ary_pop(value_stack);
3699
+
3700
+ // rights
3701
+ #line 148 "api_node.c.erb"
3702
+ argv[2] = rb_ary_new_capa(cast->rights.size);
3703
+ for (size_t index = 0; index < cast->rights.size; index++) {
3704
+ rb_ary_push(argv[2], rb_ary_pop(value_stack));
3705
+ }
3706
+
3693
3707
  // lparen_loc
3694
3708
  #line 173 "api_node.c.erb"
3695
- argv[1] = cast->lparen_loc.start == NULL ? Qnil : pm_location_new(parser, cast->lparen_loc.start, cast->lparen_loc.end, source);
3709
+ argv[3] = cast->lparen_loc.start == NULL ? Qnil : pm_location_new(parser, cast->lparen_loc.start, cast->lparen_loc.end, source);
3696
3710
 
3697
3711
  // rparen_loc
3698
3712
  #line 173 "api_node.c.erb"
3699
- argv[2] = cast->rparen_loc.start == NULL ? Qnil : pm_location_new(parser, cast->rparen_loc.start, cast->rparen_loc.end, source);
3713
+ argv[4] = cast->rparen_loc.start == NULL ? Qnil : pm_location_new(parser, cast->rparen_loc.start, cast->rparen_loc.end, source);
3700
3714
 
3701
3715
  // location
3702
- argv[3] = pm_location_new(parser, node->location.start, node->location.end, source);
3716
+ argv[5] = pm_location_new(parser, node->location.start, node->location.end, source);
3703
3717
 
3704
- rb_ary_push(value_stack, rb_class_new_instance(4, argv, rb_cPrismMultiTargetNode));
3718
+ rb_ary_push(value_stack, rb_class_new_instance(6, argv, rb_cPrismMultiTargetNode));
3705
3719
  break;
3706
3720
  }
3707
3721
  #line 134 "api_node.c.erb"
3708
3722
  case PM_MULTI_WRITE_NODE: {
3709
3723
  pm_multi_write_node_t *cast = (pm_multi_write_node_t *) node;
3710
- VALUE argv[6];
3724
+ VALUE argv[8];
3711
3725
 
3712
- // targets
3726
+ // lefts
3713
3727
  #line 148 "api_node.c.erb"
3714
- argv[0] = rb_ary_new_capa(cast->targets.size);
3715
- for (size_t index = 0; index < cast->targets.size; index++) {
3728
+ argv[0] = rb_ary_new_capa(cast->lefts.size);
3729
+ for (size_t index = 0; index < cast->lefts.size; index++) {
3716
3730
  rb_ary_push(argv[0], rb_ary_pop(value_stack));
3717
3731
  }
3718
3732
 
3733
+ // rest
3734
+ #line 145 "api_node.c.erb"
3735
+ argv[1] = rb_ary_pop(value_stack);
3736
+
3737
+ // rights
3738
+ #line 148 "api_node.c.erb"
3739
+ argv[2] = rb_ary_new_capa(cast->rights.size);
3740
+ for (size_t index = 0; index < cast->rights.size; index++) {
3741
+ rb_ary_push(argv[2], rb_ary_pop(value_stack));
3742
+ }
3743
+
3719
3744
  // lparen_loc
3720
3745
  #line 173 "api_node.c.erb"
3721
- argv[1] = cast->lparen_loc.start == NULL ? Qnil : pm_location_new(parser, cast->lparen_loc.start, cast->lparen_loc.end, source);
3746
+ argv[3] = cast->lparen_loc.start == NULL ? Qnil : pm_location_new(parser, cast->lparen_loc.start, cast->lparen_loc.end, source);
3722
3747
 
3723
3748
  // rparen_loc
3724
3749
  #line 173 "api_node.c.erb"
3725
- argv[2] = cast->rparen_loc.start == NULL ? Qnil : pm_location_new(parser, cast->rparen_loc.start, cast->rparen_loc.end, source);
3750
+ argv[4] = cast->rparen_loc.start == NULL ? Qnil : pm_location_new(parser, cast->rparen_loc.start, cast->rparen_loc.end, source);
3726
3751
 
3727
3752
  // operator_loc
3728
3753
  #line 170 "api_node.c.erb"
3729
- argv[3] = pm_location_new(parser, cast->operator_loc.start, cast->operator_loc.end, source);
3754
+ argv[5] = pm_location_new(parser, cast->operator_loc.start, cast->operator_loc.end, source);
3730
3755
 
3731
3756
  // value
3732
3757
  #line 145 "api_node.c.erb"
3733
- argv[4] = rb_ary_pop(value_stack);
3758
+ argv[6] = rb_ary_pop(value_stack);
3734
3759
 
3735
3760
  // location
3736
- argv[5] = pm_location_new(parser, node->location.start, node->location.end, source);
3761
+ argv[7] = pm_location_new(parser, node->location.start, node->location.end, source);
3737
3762
 
3738
- rb_ary_push(value_stack, rb_class_new_instance(6, argv, rb_cPrismMultiWriteNode));
3763
+ rb_ary_push(value_stack, rb_class_new_instance(8, argv, rb_cPrismMultiWriteNode));
3739
3764
  break;
3740
3765
  }
3741
3766
  #line 134 "api_node.c.erb"
@@ -4131,32 +4156,6 @@ pm_ast_new(pm_parser_t *parser, pm_node_t *node, rb_encoding *encoding) {
4131
4156
  rb_ary_push(value_stack, rb_class_new_instance(6, argv, rb_cPrismRegularExpressionNode));
4132
4157
  break;
4133
4158
  }
4134
- #line 134 "api_node.c.erb"
4135
- case PM_REQUIRED_DESTRUCTURED_PARAMETER_NODE: {
4136
- pm_required_destructured_parameter_node_t *cast = (pm_required_destructured_parameter_node_t *) node;
4137
- VALUE argv[4];
4138
-
4139
- // parameters
4140
- #line 148 "api_node.c.erb"
4141
- argv[0] = rb_ary_new_capa(cast->parameters.size);
4142
- for (size_t index = 0; index < cast->parameters.size; index++) {
4143
- rb_ary_push(argv[0], rb_ary_pop(value_stack));
4144
- }
4145
-
4146
- // opening_loc
4147
- #line 170 "api_node.c.erb"
4148
- argv[1] = pm_location_new(parser, cast->opening_loc.start, cast->opening_loc.end, source);
4149
-
4150
- // closing_loc
4151
- #line 170 "api_node.c.erb"
4152
- argv[2] = pm_location_new(parser, cast->closing_loc.start, cast->closing_loc.end, source);
4153
-
4154
- // location
4155
- argv[3] = pm_location_new(parser, node->location.start, node->location.end, source);
4156
-
4157
- rb_ary_push(value_stack, rb_class_new_instance(4, argv, rb_cPrismRequiredDestructuredParameterNode));
4158
- break;
4159
- }
4160
4159
  #line 134 "api_node.c.erb"
4161
4160
  case PM_REQUIRED_PARAMETER_NODE: {
4162
4161
  pm_required_parameter_node_t *cast = (pm_required_parameter_node_t *) node;
@@ -4849,7 +4848,6 @@ Init_prism_api_node(void) {
4849
4848
  rb_cPrismRationalNode = rb_define_class_under(rb_cPrism, "RationalNode", rb_cPrismNode);
4850
4849
  rb_cPrismRedoNode = rb_define_class_under(rb_cPrism, "RedoNode", rb_cPrismNode);
4851
4850
  rb_cPrismRegularExpressionNode = rb_define_class_under(rb_cPrism, "RegularExpressionNode", rb_cPrismNode);
4852
- rb_cPrismRequiredDestructuredParameterNode = rb_define_class_under(rb_cPrism, "RequiredDestructuredParameterNode", rb_cPrismNode);
4853
4851
  rb_cPrismRequiredParameterNode = rb_define_class_under(rb_cPrism, "RequiredParameterNode", rb_cPrismNode);
4854
4852
  rb_cPrismRescueModifierNode = rb_define_class_under(rb_cPrism, "RescueModifierNode", rb_cPrismNode);
4855
4853
  rb_cPrismRescueNode = rb_define_class_under(rb_cPrism, "RescueNode", rb_cPrismNode);
@@ -396,6 +396,24 @@ parse_input(pm_string_t *input, const char *filepath) {
396
396
  return result;
397
397
  }
398
398
 
399
+ // Parse the given input and return an array of Comment objects.
400
+ static VALUE
401
+ parse_input_comments(pm_string_t *input, const char *filepath) {
402
+ pm_parser_t parser;
403
+ pm_parser_init(&parser, pm_string_source(input), pm_string_length(input), filepath);
404
+
405
+ pm_node_t *node = pm_parse(&parser);
406
+ rb_encoding *encoding = rb_enc_find(parser.encoding.name);
407
+
408
+ VALUE source = pm_source_new(&parser, encoding);
409
+ VALUE comments = parser_comments(&parser, source);
410
+
411
+ pm_node_destroy(&parser, node);
412
+ pm_parser_free(&parser);
413
+
414
+ return comments;
415
+ }
416
+
399
417
  // Parse the given string and return a ParseResult instance.
400
418
  static VALUE
401
419
  parse(int argc, VALUE *argv, VALUE self) {
@@ -436,6 +454,33 @@ parse_file(VALUE self, VALUE filepath) {
436
454
  return value;
437
455
  }
438
456
 
457
+ // Parse the given string and return an array of Comment objects.
458
+ static VALUE
459
+ parse_comments(int argc, VALUE *argv, VALUE self) {
460
+ VALUE string;
461
+ VALUE filepath;
462
+ rb_scan_args(argc, argv, "11", &string, &filepath);
463
+
464
+ pm_string_t input;
465
+ input_load_string(&input, string);
466
+
467
+ return parse_input_comments(&input, check_string(filepath));
468
+ }
469
+
470
+ // Parse the given file and return an array of Comment objects.
471
+ static VALUE
472
+ parse_file_comments(VALUE self, VALUE filepath) {
473
+ pm_string_t input;
474
+
475
+ const char *checked = check_string(filepath);
476
+ if (!pm_string_mapped_init(&input, checked)) return Qnil;
477
+
478
+ VALUE value = parse_input_comments(&input, checked);
479
+ pm_string_free(&input);
480
+
481
+ return value;
482
+ }
483
+
439
484
  // Parse the given string and return a ParseResult instance.
440
485
  static VALUE
441
486
  parse_lex(int argc, VALUE *argv, VALUE self) {
@@ -554,6 +599,31 @@ parse_serialize_file_metadata(VALUE self, VALUE filepath, VALUE metadata) {
554
599
  return result;
555
600
  }
556
601
 
602
+ // Inspect the AST that represents the given source using the prism pretty print
603
+ // as opposed to the Ruby implementation.
604
+ static VALUE
605
+ inspect_node(VALUE self, VALUE source) {
606
+ pm_string_t input;
607
+ input_load_string(&input, source);
608
+
609
+ pm_parser_t parser;
610
+ pm_parser_init(&parser, pm_string_source(&input), pm_string_length(&input), NULL);
611
+
612
+ pm_node_t *node = pm_parse(&parser);
613
+ pm_buffer_t buffer = { 0 };
614
+
615
+ pm_prettyprint(&buffer, &parser, node);
616
+
617
+ rb_encoding *encoding = rb_enc_find(parser.encoding.name);
618
+ VALUE string = rb_enc_str_new(pm_buffer_value(&buffer), pm_buffer_length(&buffer), encoding);
619
+
620
+ pm_buffer_free(&buffer);
621
+ pm_node_destroy(&parser, node);
622
+ pm_parser_free(&parser);
623
+
624
+ return string;
625
+ }
626
+
557
627
  /******************************************************************************/
558
628
  /* Initialization of the extension */
559
629
  /******************************************************************************/
@@ -596,6 +666,8 @@ Init_prism(void) {
596
666
  rb_define_singleton_method(rb_cPrism, "lex_file", lex_file, 1);
597
667
  rb_define_singleton_method(rb_cPrism, "parse", parse, -1);
598
668
  rb_define_singleton_method(rb_cPrism, "parse_file", parse_file, 1);
669
+ rb_define_singleton_method(rb_cPrism, "parse_comments", parse_comments, -1);
670
+ rb_define_singleton_method(rb_cPrism, "parse_file_comments", parse_file_comments, 1);
599
671
  rb_define_singleton_method(rb_cPrism, "parse_lex", parse_lex, -1);
600
672
  rb_define_singleton_method(rb_cPrism, "parse_lex_file", parse_lex_file, 1);
601
673
 
@@ -606,6 +678,7 @@ Init_prism(void) {
606
678
  rb_define_singleton_method(rb_cPrismDebug, "memsize", memsize, 1);
607
679
  rb_define_singleton_method(rb_cPrismDebug, "profile_file", profile_file, 1);
608
680
  rb_define_singleton_method(rb_cPrismDebug, "parse_serialize_file_metadata", parse_serialize_file_metadata, 2);
681
+ rb_define_singleton_method(rb_cPrismDebug, "inspect_node", inspect_node, 1);
609
682
 
610
683
  // Next, initialize the other APIs.
611
684
  Init_prism_api_node();