prism 0.15.1 → 0.16.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 (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();