prism 0.17.1 → 0.18.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +30 -1
  3. data/Makefile +5 -5
  4. data/README.md +2 -2
  5. data/config.yml +26 -13
  6. data/docs/build_system.md +6 -6
  7. data/docs/building.md +1 -1
  8. data/docs/configuration.md +1 -0
  9. data/docs/encoding.md +68 -32
  10. data/docs/heredocs.md +1 -1
  11. data/docs/javascript.md +29 -1
  12. data/docs/ruby_api.md +14 -0
  13. data/ext/prism/api_node.c +74 -45
  14. data/ext/prism/extconf.rb +91 -127
  15. data/ext/prism/extension.c +1 -1
  16. data/ext/prism/extension.h +1 -1
  17. data/include/prism/ast.h +148 -133
  18. data/include/prism/diagnostic.h +27 -1
  19. data/include/prism/enc/pm_encoding.h +42 -1
  20. data/include/prism/parser.h +6 -0
  21. data/include/prism/version.h +3 -3
  22. data/lib/prism/compiler.rb +3 -3
  23. data/lib/prism/debug.rb +4 -0
  24. data/lib/prism/desugar_compiler.rb +1 -0
  25. data/lib/prism/dispatcher.rb +14 -14
  26. data/lib/prism/dot_visitor.rb +4334 -0
  27. data/lib/prism/dsl.rb +11 -11
  28. data/lib/prism/ffi.rb +3 -3
  29. data/lib/prism/mutation_compiler.rb +6 -6
  30. data/lib/prism/node.rb +182 -113
  31. data/lib/prism/node_ext.rb +61 -3
  32. data/lib/prism/parse_result.rb +46 -12
  33. data/lib/prism/serialize.rb +125 -131
  34. data/lib/prism/visitor.rb +3 -3
  35. data/lib/prism.rb +1 -0
  36. data/prism.gemspec +5 -1
  37. data/rbi/prism.rbi +83 -54
  38. data/sig/prism.rbs +47 -32
  39. data/src/diagnostic.c +61 -3
  40. data/src/enc/pm_big5.c +63 -0
  41. data/src/enc/pm_cp51932.c +57 -0
  42. data/src/enc/pm_euc_jp.c +10 -0
  43. data/src/enc/pm_gbk.c +5 -2
  44. data/src/enc/pm_tables.c +1478 -148
  45. data/src/node.c +33 -21
  46. data/src/prettyprint.c +1027 -925
  47. data/src/prism.c +925 -374
  48. data/src/regexp.c +12 -12
  49. data/src/serialize.c +36 -9
  50. metadata +6 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1a4a6459265bca653fd8db970fcf2d3863bca8bf0b839629beff1a2a8bed68df
4
- data.tar.gz: 3b309ffb711f9f6a5b57e3b130a66ed8c3ffc172c903642e74b00d4729d41016
3
+ metadata.gz: ec5c0e5a9423ea40bad7c6ea608cb43dc0250c4d7119b4960271f547697540c1
4
+ data.tar.gz: d505901505b8c4eee195171721d68fe05dde76de171e5a80115a301ba030cc34
5
5
  SHA512:
6
- metadata.gz: 631a5028085ce1c853ab38b2e82462c869ba70860d8b8fc0532e1fdc5a53329f4aa582b0f2be58363d4c642f8d576751b14fa76e685aa90b68e854c6c51a38c3
7
- data.tar.gz: e2f7601d879b9f4d81e8e80d8ddf4776fca36395e38e460798635810ab229054753ad172dce8577174253b3353cc99d3176a7c1bdc31e1205a2f4e000a7ae3b3
6
+ metadata.gz: 3f3a4b92227885288d47e147f72b5cd2a27886222339ac08426a32ffe980b5b1397cf0356db93fcbdb2f6a3d16b1aa8db7185acbfe2a92473ef6adcf2aeda93e
7
+ data.tar.gz: 20528ed404ebdfb5e2093054cd7d1b28fed5f8d95c84df217eee9759ca25603cbd381943db985e17b8e4851756605aa1fb2fe9d41c0d050dfee9d770ffd915d7
data/CHANGELOG.md CHANGED
@@ -6,6 +6,34 @@ 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.18.0] - 2023-11-21
10
+
11
+ ### Added
12
+
13
+ - The `ParametersNode#signature` method is added, which returns the same thing as `Method#parameters`.
14
+ - Visitor functionality has been added to the JavaScript API.
15
+ - The `Node#to_dot` API has been added to convert syntax trees to Graphviz digraphs.
16
+ - `IfNode` and `UnlessNode` now have a `then_keyword_loc` field.
17
+ - Many more encodings are now supported.
18
+ - Some new `Location` APIs have been added for dealing with characters instead of bytes, which are: `start_character_offset`, `end_character_offset`, `start_character_column`, and `end_character_column`.
19
+ - A warning has been added for when `END {}` is used within a method.
20
+ - `ConstantPathNode#full_name{,_parts}` will now raise an error if the receiver of the constant path is not itself a constant.
21
+ - The `in` keyword and the `=>` operator now respect non-associativity.
22
+ - The `..` and `...` operators now properly respect non-associativity.
23
+
24
+ ### Changed
25
+
26
+ - Previously `...` in blocks was accepted, but it is now properly rejected.
27
+ - **BREAKING**: `librubyparser.*` has been renamed to `libprism.*` in the C API.
28
+ - We now properly reject floats with exponent and rational suffixes.
29
+ - We now properly reject void value expressions.
30
+ - **BREAKING**: The `--disable-static` option has been removed from the C extension.
31
+ - The rescue modifier keyword is now properly parsed in terms of precedence.
32
+ - We now properly reject defining a numbered parameter method.
33
+ - **BREAKING**: `MatchWriteNode` now has a list of `targets`, which are local variable target nodes. This is instead of `locals` which was a constant list. This is to support writing to local variables outside the current scope. It has the added benefit of providing location information for the local variable targets.
34
+ - **BREAKING**: `CaseNode` has been split into `CaseNode` and `CaseMatchNode`, the latter is used for `case ... in` expressions.
35
+ - **BREAKING**: `StringConcatNode` has been removed in favor of using `InterpolatedStringNode` as a list.
36
+
9
37
  ## [0.17.1] - 2023-11-03
10
38
 
11
39
  ### Changed
@@ -239,7 +267,8 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) a
239
267
 
240
268
  - 🎉 Initial release! 🎉
241
269
 
242
- [unreleased]: https://github.com/ruby/prism/compare/v0.17.1...HEAD
270
+ [unreleased]: https://github.com/ruby/prism/compare/v0.18.0...HEAD
271
+ [0.18.0]: https://github.com/ruby/prism/compare/v0.17.1...v0.18.0
243
272
  [0.17.1]: https://github.com/ruby/prism/compare/v0.17.0...v0.17.1
244
273
  [0.17.0]: https://github.com/ruby/prism/compare/v0.16.0...v0.17.0
245
274
  [0.16.0]: https://github.com/ruby/prism/compare/v0.15.1...v0.16.0
data/Makefile CHANGED
@@ -11,7 +11,7 @@ FUZZ_OUTPUT_DIR = $(shell pwd)/fuzz/output
11
11
  SOEXT := $(shell ruby -e 'puts RbConfig::CONFIG["SOEXT"]')
12
12
 
13
13
  CPPFLAGS := -Iinclude
14
- CFLAGS := -g -O2 -std=c99 -Wall -Werror -Wextra -Wpedantic -Wundef -Wconversion -fPIC -fvisibility=hidden
14
+ CFLAGS := -g -O2 -std=c99 -Wall -Werror -Wextra -Wpedantic -Wundef -Wconversion -Wno-missing-braces -fPIC -fvisibility=hidden
15
15
  CC := cc
16
16
  WASI_SDK_PATH := /opt/wasi-sdk
17
17
 
@@ -22,15 +22,15 @@ STATIC_OBJECTS := $(subst src/,build/static/,$(SOURCES:.c=.o))
22
22
 
23
23
  all: shared static
24
24
 
25
- shared: build/librubyparser.$(SOEXT)
26
- static: build/librubyparser.a
25
+ shared: build/libprism.$(SOEXT)
26
+ static: build/libprism.a
27
27
  wasm: javascript/src/prism.wasm
28
28
 
29
- build/librubyparser.$(SOEXT): $(SHARED_OBJECTS)
29
+ build/libprism.$(SOEXT): $(SHARED_OBJECTS)
30
30
  $(ECHO) "linking $@"
31
31
  $(Q) $(CC) $(DEBUG_FLAGS) $(CFLAGS) -shared -o $@ $(SHARED_OBJECTS)
32
32
 
33
- build/librubyparser.a: $(STATIC_OBJECTS)
33
+ build/libprism.a: $(STATIC_OBJECTS)
34
34
  $(ECHO) "building $@"
35
35
  $(Q) $(AR) $(ARFLAGS) $@ $(STATIC_OBJECTS) $(Q1:0=>/dev/null)
36
36
 
data/README.md CHANGED
@@ -7,7 +7,7 @@ This is a parser for the Ruby programming language. It is designed to be portabl
7
7
 
8
8
  ## Overview
9
9
 
10
- The repository contains the infrastructure for both a shared library (librubyparser) and a native CRuby extension. The shared library has no bindings to CRuby itself, and so can be used by other projects. The native CRuby extension links against `ruby.h`, and so is suitable in the context of CRuby.
10
+ The repository contains the infrastructure for both a shared library (libprism) and a native CRuby extension. The shared library has no bindings to CRuby itself, and so can be used by other projects. The native CRuby extension links against `ruby.h`, and so is suitable in the context of CRuby.
11
11
 
12
12
  ```
13
13
  .
@@ -21,7 +21,7 @@ The repository contains the infrastructure for both a shared library (librubypar
21
21
  ├── ext
22
22
  │   └── prism
23
23
  │   ├── extconf.rb configuration to generate the Makefile for the native extension
24
- │   └── extension.c the native extension that interacts with librubyparser
24
+ │   └── extension.c the native extension that interacts with libprism
25
25
  ├── fuzz files related to fuzz testing
26
26
  ├── include
27
27
  │   ├── prism header files for the shared library
data/config.yml CHANGED
@@ -768,6 +768,26 @@ nodes:
768
768
 
769
769
  foo => [bar => baz]
770
770
  ^^^^^^^^^^^^
771
+ - name: CaseMatchNode
772
+ fields:
773
+ - name: predicate
774
+ type: node?
775
+ - name: conditions
776
+ type: node[]
777
+ - name: consequent
778
+ type: node?
779
+ kind: ElseNode
780
+ - name: case_keyword_loc
781
+ type: location
782
+ - name: end_keyword_loc
783
+ type: location
784
+ comment: |
785
+ Represents the use of a case statement for pattern matching.
786
+
787
+ case true
788
+ in false
789
+ end
790
+ ^^^^^^^^^
771
791
  - name: CaseNode
772
792
  fields:
773
793
  - name: predicate
@@ -1385,6 +1405,8 @@ nodes:
1385
1405
  type: location?
1386
1406
  - name: predicate
1387
1407
  type: node
1408
+ - name: then_keyword_loc
1409
+ type: location?
1388
1410
  - name: statements
1389
1411
  type: node?
1390
1412
  kind: StatementsNode
@@ -1878,8 +1900,8 @@ nodes:
1878
1900
  - name: call
1879
1901
  type: node
1880
1902
  kind: CallNode
1881
- - name: locals
1882
- type: constant[]
1903
+ - name: targets
1904
+ type: node[]
1883
1905
  comment: |
1884
1906
  Represents writing local variables using a regular expression match with
1885
1907
  named capture groups.
@@ -2348,17 +2370,6 @@ nodes:
2348
2370
 
2349
2371
  foo; bar; baz
2350
2372
  ^^^^^^^^^^^^^
2351
- - name: StringConcatNode
2352
- fields:
2353
- - name: left
2354
- type: node
2355
- - name: right
2356
- type: node
2357
- comment: |
2358
- Represents the use of compile-time string concatenation.
2359
-
2360
- "foo" "bar"
2361
- ^^^^^^^^^^^
2362
2373
  - name: StringNode
2363
2374
  fields:
2364
2375
  - name: flags
@@ -2446,6 +2457,8 @@ nodes:
2446
2457
  type: location
2447
2458
  - name: predicate
2448
2459
  type: node
2460
+ - name: then_keyword_loc
2461
+ type: location?
2449
2462
  - name: statements
2450
2463
  type: node?
2451
2464
  kind: StatementsNode
data/docs/build_system.md CHANGED
@@ -16,14 +16,14 @@ The main solution for the second point seems a Makefile, otherwise many of the u
16
16
  ## General Design
17
17
 
18
18
  1. Templates are generated by `templates/template.rb`
19
- 4. The `Makefile` compiles both `librubyparser.a` and `librubyparser.{so,dylib,dll}` from the `src/**/*.c` and `include/**/*.h` files
19
+ 4. The `Makefile` compiles both `libprism.a` and `libprism.{so,dylib,dll}` from the `src/**/*.c` and `include/**/*.h` files
20
20
  5. The `Rakefile` `:compile` task ensures the above prerequisites are done, then calls `make`,
21
- and uses `Rake::ExtensionTask` to compile the C extension (using its `extconf.rb`), which uses `librubyparser.a`
21
+ and uses `Rake::ExtensionTask` to compile the C extension (using its `extconf.rb`), which uses `libprism.a`
22
22
 
23
23
  This way there is minimal duplication, and each layer builds on the previous one and has its own responsibilities.
24
24
 
25
25
  The static library exports no symbols, to avoid any conflict.
26
- The shared library exports some symbols, and this is fine since there should only be one librubyparser shared library
26
+ The shared library exports some symbols, and this is fine since there should only be one libprism shared library
27
27
  loaded per process (i.e., at most one version of the prism *gem* loaded in a process, only the gem uses the shared library).
28
28
 
29
29
  ## The various ways to build prism
@@ -36,11 +36,11 @@ loaded per process (i.e., at most one version of the prism *gem* loaded in a pro
36
36
 
37
37
  The gem contains the pre-generated templates.
38
38
  When installing the gem, `extconf.rb` is used and that:
39
- * runs `make build/librubyparser.a`
39
+ * runs `make build/libprism.a`
40
40
  * compiles the C extension with mkmf
41
41
 
42
42
  When installing the gem on JRuby and TruffleRuby, no C extension is built, so instead of the last step,
43
- there is Ruby code using FFI which uses `librubyparser.{so,dylib,dll}`
43
+ there is Ruby code using FFI which uses `libprism.{so,dylib,dll}`
44
44
  to implement the same methods as the C extension, but using serialization instead of many native calls/accesses
45
45
  (JRuby does not support C extensions, serialization is faster on TruffleRuby than the C extension).
46
46
 
@@ -67,7 +67,7 @@ The script generates the templates when importing.
67
67
  Then when `mx build` builds TruffleRuby and the `prism` mx project inside, it runs `make`.
68
68
 
69
69
  Then the `prism bindings` mx project is built, which contains the [bindings](https://github.com/oracle/truffleruby/blob/master/src/main/c/prism_bindings/src/prism_bindings.c)
70
- and links to `librubyparser.a` (to avoid exporting symbols, so no conflict when installing the prism gem).
70
+ and links to `libprism.a` (to avoid exporting symbols, so no conflict when installing the prism gem).
71
71
 
72
72
  ### Building prism as part of JRuby
73
73
 
data/docs/building.md CHANGED
@@ -10,7 +10,7 @@ All of the source files match `src/**/*.c` and all of the headers match `include
10
10
  The following flags should be used to compile prism:
11
11
 
12
12
  * `-std=c99` - Use the C99 standard
13
- * `-Wall -Wconversion -Wextra -Wpedantic -Wundef` - Enable the warnings we care about
13
+ * `-Wall -Wconversion -Wextra -Wpedantic -Wundef -Wno-missing-braces` - Enable the warnings we care about
14
14
  * `-Werror` - Treat warnings as errors
15
15
  * `-fvisibility=hidden` - Hide all symbols by default
16
16
 
@@ -11,6 +11,7 @@ A lot of code in prism's repository is templated from a single configuration fil
11
11
  * `java/org/prism/Nodes.java` - for defining the nodes in Java
12
12
  * `lib/prism/compiler.rb` - for defining the compiler for the nodes in Ruby
13
13
  * `lib/prism/dispatcher.rb` - for defining the dispatch visitors for the nodes in Ruby
14
+ * `lib/prism/dot_visitor.rb` - for defining the dot visitor for the nodes in Ruby
14
15
  * `lib/prism/dsl.rb` - for defining the DSL for the nodes in Ruby
15
16
  * `lib/prism/mutation_compiler.rb` - for defining the mutation compiler for the nodes in Ruby
16
17
  * `lib/prism/node.rb` - for defining the nodes in Ruby
data/docs/encoding.md CHANGED
@@ -12,43 +12,79 @@ If the file is not encoded in UTF-8, the user must specify the encoding in a "ma
12
12
 
13
13
  The key of the comment can be either "encoding" or "coding". The value of the comment must be a string that is a valid encoding name. The encodings that prism supports by default are:
14
14
 
15
- * `ascii`
16
- * `ascii-8bit`
17
- * `big5`
18
- * `binary`
19
- * `cp932`
20
- * `euc-jp`
21
- * `gbk`
22
- * `iso-8859-1`
23
- * `iso-8859-2`
24
- * `iso-8859-3`
25
- * `iso-8859-4`
26
- * `iso-8859-5`
27
- * `iso-8859-6`
28
- * `iso-8859-7`
29
- * `iso-8859-8`
30
- * `iso-8859-9`
31
- * `iso-8859-10`
32
- * `iso-8859-11`
33
- * `iso-8859-13`
34
- * `iso-8859-14`
35
- * `iso-8859-15`
36
- * `iso-8859-16`
37
- * `koi8-r`
38
- * `shift_jis`
39
- * `sjis`
40
- * `us-ascii`
41
- * `utf-8`
42
- * `utf8-mac`
43
- * `windows-31j`
44
- * `windows-1251`
45
- * `windows-1252`
15
+ * `ASCII-8BIT`
16
+ * `Big5`
17
+ * `Big5-HKSCS`
18
+ * `Big5-UAO`
19
+ * `CP51932`
20
+ * `CP850`
21
+ * `CP852`
22
+ * `CP855`
23
+ * `EUC-JP`
24
+ * `GB1988`
25
+ * `GBK`
26
+ * `IBM437`
27
+ * `IBM720`
28
+ * `IBM737`
29
+ * `IBM775`
30
+ * `IBM852`
31
+ * `IBM855`
32
+ * `IBM857`
33
+ * `IBM860`
34
+ * `IBM861`
35
+ * `IBM862`
36
+ * `IBM864`
37
+ * `IBM865`
38
+ * `IBM866`
39
+ * `IBM869`
40
+ * `ISO-8859-1`
41
+ * `ISO-8859-2`
42
+ * `ISO-8859-3`
43
+ * `ISO-8859-4`
44
+ * `ISO-8859-5`
45
+ * `ISO-8859-6`
46
+ * `ISO-8859-7`
47
+ * `ISO-8859-8`
48
+ * `ISO-8859-9`
49
+ * `ISO-8859-10`
50
+ * `ISO-8859-11`
51
+ * `ISO-8859-13`
52
+ * `ISO-8859-14`
53
+ * `ISO-8859-15`
54
+ * `ISO-8859-16`
55
+ * `KOI8-R`
56
+ * `macCentEuro`
57
+ * `macCroatian`
58
+ * `macCyrillic`
59
+ * `macGreek`
60
+ * `macIceland`
61
+ * `macRoman`
62
+ * `macRomania`
63
+ * `macThai`
64
+ * `macTurkish`
65
+ * `macUkraine`
66
+ * `Shift_JIS`
67
+ * `TIS-620`
68
+ * `US-ASCII`
69
+ * `UTF-8`
70
+ * `UTF8-MAC`
71
+ * `Windows-1250`
72
+ * `Windows-1251`
73
+ * `Windows-1252`
74
+ * `Windows-1253`
75
+ * `Windows-1254`
76
+ * `Windows-1255`
77
+ * `Windows-1256`
78
+ * `Windows-1257`
79
+ * `Windows-1258`
80
+ * `Windows-31J`
81
+ * `Windows-874`
46
82
 
47
83
  For each of these encodings, prism provides a function for checking if the subsequent bytes form an alphabetic or alphanumeric character.
48
84
 
49
85
  ## Support for other encodings
50
86
 
51
- If an encoding is encountered that is not supported by prism, prism will call a user-provided callback function with the name of the encoding if one is provided. That function can be registered with `pm_parser_register_encoding_decode_callback`. The user-provided callback function can then provide a pointer to an encoding struct that contains the requisite functions that prism will use those to parse identifiers going forward.
87
+ If an encoding is encountered that is not supported by prism, prism will call a user-provided callback function with the name of the encoding if one is provided. That function can be registered with `pm_parser_register_encoding_decode_callback`. The user-provided callback function can then provide a pointer to an encoding struct that contains the requisite functions that prism will use to parse identifiers going forward.
52
88
 
53
89
  If the user-provided callback function returns `NULL` (the value also provided by the default implementation in case a callback was not registered), an error will be added to the parser's error list and parsing will continue on using the default UTF-8 encoding.
54
90
 
data/docs/heredocs.md CHANGED
@@ -12,7 +12,7 @@ When a heredoc identifier is encountered in the regular process of lexing, we pu
12
12
 
13
13
  We also set the special `parser.next_start` field which is a pointer to the place in the source where we should start lexing the next token. This is set to the pointer of the character immediately following the next newline.
14
14
 
15
- Note that if the `parser.heredoc_end` field is already set, then it means we have already encountered a heredoc on this line. In that case the `parser.next_start` field will be set to the `parser.heredoc_end` field. This is because we want to skip past the heredoc previous heredocs on this line and instead lex the body of this heredoc.
15
+ Note that if the `parser.heredoc_end` field is already set, then it means we have already encountered a heredoc on this line. In that case the `parser.next_start` field will be set to the `parser.heredoc_end` field. This is because we want to skip past the previous heredocs on this line and instead lex the body of this heredoc.
16
16
 
17
17
  ## 2. Lexing the body
18
18
 
data/docs/javascript.md CHANGED
@@ -24,7 +24,7 @@ const parse = await loadPrism();
24
24
 
25
25
  ## Browser
26
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.
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
28
 
29
29
  First, get a shim for WASI since not all browsers support it yet.
30
30
 
@@ -74,6 +74,34 @@ A ParseResult object is very similar to the Prism::ParseResult object from Ruby.
74
74
  console.log(JSON.stringify(parseResult.value, null, 2));
75
75
  ```
76
76
 
77
+ ## Visitors
78
+
79
+ Prism allows you to traverse the AST of parsed Ruby code using visitors.
80
+
81
+ Here's an example of a custom `FooCalls` visitor:
82
+
83
+ ```js
84
+ import { loadPrism, Visitor } from "@ruby/prism"
85
+
86
+ const parse = await loadPrism();
87
+ const parseResult = parse("foo()");
88
+
89
+ class FooCalls extends Visitor {
90
+ visitCallNode(node) {
91
+ if (node.name === "foo") {
92
+ // Do something with the node
93
+ }
94
+
95
+ // Call super so that the visitor continues walking the tree
96
+ super.visitCallNode(node);
97
+ }
98
+ }
99
+
100
+ const fooVisitor = new FooCalls();
101
+
102
+ parseResult.value.accept(fooVisitor);
103
+ ```
104
+
77
105
  ## Building
78
106
 
79
107
  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:
data/docs/ruby_api.md CHANGED
@@ -25,3 +25,17 @@ The full API is documented below.
25
25
  * `Prism.load(source, serialized)` - load the serialized syntax tree using the source as a reference into a syntax tree
26
26
  * `Prism.parse_comments(source)` - parse the comments corresponding to the given source string and return them
27
27
  * `Prism.parse_file_comments(source)` - parse the comments corresponding to the given source file and return them
28
+
29
+ ## Nodes
30
+
31
+ Once you have nodes in hand coming out of a parse result, there are a number of common APIs that are available on each instance. They are:
32
+
33
+ * `#accept(visitor)` - a method that will immediately call `visit_*` to specialize for the node type
34
+ * `#child_nodes` - a positional array of the child nodes of the node, with `nil` values for any missing children
35
+ * `#compact_child_nodes` - a positional array of the child nodes of the node with no `nil` values
36
+ * `#copy(**keys)` - a method that allows creating a shallow copy of the node with the given keys overridden
37
+ * `#deconstruct`/`#deconstruct_keys(keys)` - the pattern matching interface for nodes
38
+ * `#inspect` - a string representation that looks like the syntax tree of the node
39
+ * `#location` - a `Location` object that describes the location of the node in the source file
40
+ * `#to_dot` - convert the node's syntax tree into graphviz dot notation
41
+ * `#type` - a symbol that represents the type of the node, useful for quick comparisons
data/ext/prism/api_node.c CHANGED
@@ -36,6 +36,7 @@ static VALUE rb_cPrismCallNode;
36
36
  static VALUE rb_cPrismCallOperatorWriteNode;
37
37
  static VALUE rb_cPrismCallOrWriteNode;
38
38
  static VALUE rb_cPrismCapturePatternNode;
39
+ static VALUE rb_cPrismCaseMatchNode;
39
40
  static VALUE rb_cPrismCaseNode;
40
41
  static VALUE rb_cPrismClassNode;
41
42
  static VALUE rb_cPrismClassVariableAndWriteNode;
@@ -146,7 +147,6 @@ static VALUE rb_cPrismSourceFileNode;
146
147
  static VALUE rb_cPrismSourceLineNode;
147
148
  static VALUE rb_cPrismSplatNode;
148
149
  static VALUE rb_cPrismStatementsNode;
149
- static VALUE rb_cPrismStringConcatNode;
150
150
  static VALUE rb_cPrismStringNode;
151
151
  static VALUE rb_cPrismSuperNode;
152
152
  static VALUE rb_cPrismSymbolNode;
@@ -403,6 +403,16 @@ pm_ast_new(pm_parser_t *parser, pm_node_t *node, rb_encoding *encoding) {
403
403
  pm_node_stack_push(&node_stack, (pm_node_t *) cast->target);
404
404
  break;
405
405
  }
406
+ #line 118 "api_node.c.erb"
407
+ case PM_CASE_MATCH_NODE: {
408
+ pm_case_match_node_t *cast = (pm_case_match_node_t *) node;
409
+ pm_node_stack_push(&node_stack, (pm_node_t *) cast->predicate);
410
+ for (size_t index = 0; index < cast->conditions.size; index++) {
411
+ pm_node_stack_push(&node_stack, (pm_node_t *) cast->conditions.nodes[index]);
412
+ }
413
+ pm_node_stack_push(&node_stack, (pm_node_t *) cast->consequent);
414
+ break;
415
+ }
406
416
  #line 118 "api_node.c.erb"
407
417
  case PM_CASE_NODE: {
408
418
  pm_case_node_t *cast = (pm_case_node_t *) node;
@@ -798,6 +808,9 @@ pm_ast_new(pm_parser_t *parser, pm_node_t *node, rb_encoding *encoding) {
798
808
  case PM_MATCH_WRITE_NODE: {
799
809
  pm_match_write_node_t *cast = (pm_match_write_node_t *) node;
800
810
  pm_node_stack_push(&node_stack, (pm_node_t *) cast->call);
811
+ for (size_t index = 0; index < cast->targets.size; index++) {
812
+ pm_node_stack_push(&node_stack, (pm_node_t *) cast->targets.nodes[index]);
813
+ }
801
814
  break;
802
815
  }
803
816
  #line 118 "api_node.c.erb"
@@ -971,13 +984,6 @@ pm_ast_new(pm_parser_t *parser, pm_node_t *node, rb_encoding *encoding) {
971
984
  }
972
985
  break;
973
986
  }
974
- #line 118 "api_node.c.erb"
975
- case PM_STRING_CONCAT_NODE: {
976
- pm_string_concat_node_t *cast = (pm_string_concat_node_t *) node;
977
- pm_node_stack_push(&node_stack, (pm_node_t *) cast->left);
978
- pm_node_stack_push(&node_stack, (pm_node_t *) cast->right);
979
- break;
980
- }
981
987
  #line 118 "api_node.c.erb"
982
988
  case PM_SUPER_NODE: {
983
989
  pm_super_node_t *cast = (pm_super_node_t *) node;
@@ -1664,6 +1670,40 @@ pm_ast_new(pm_parser_t *parser, pm_node_t *node, rb_encoding *encoding) {
1664
1670
  rb_ary_push(value_stack, rb_class_new_instance(4, argv, rb_cPrismCapturePatternNode));
1665
1671
  break;
1666
1672
  }
1673
+ #line 144 "api_node.c.erb"
1674
+ case PM_CASE_MATCH_NODE: {
1675
+ pm_case_match_node_t *cast = (pm_case_match_node_t *) node;
1676
+ VALUE argv[6];
1677
+
1678
+ // predicate
1679
+ #line 155 "api_node.c.erb"
1680
+ argv[0] = rb_ary_pop(value_stack);
1681
+
1682
+ // conditions
1683
+ #line 158 "api_node.c.erb"
1684
+ argv[1] = rb_ary_new_capa(cast->conditions.size);
1685
+ for (size_t index = 0; index < cast->conditions.size; index++) {
1686
+ rb_ary_push(argv[1], rb_ary_pop(value_stack));
1687
+ }
1688
+
1689
+ // consequent
1690
+ #line 155 "api_node.c.erb"
1691
+ argv[2] = rb_ary_pop(value_stack);
1692
+
1693
+ // case_keyword_loc
1694
+ #line 180 "api_node.c.erb"
1695
+ argv[3] = pm_location_new(parser, cast->case_keyword_loc.start, cast->case_keyword_loc.end, source);
1696
+
1697
+ // end_keyword_loc
1698
+ #line 180 "api_node.c.erb"
1699
+ argv[4] = pm_location_new(parser, cast->end_keyword_loc.start, cast->end_keyword_loc.end, source);
1700
+
1701
+ // location
1702
+ argv[5] = pm_location_new(parser, node->location.start, node->location.end, source);
1703
+
1704
+ rb_ary_push(value_stack, rb_class_new_instance(6, argv, rb_cPrismCaseMatchNode));
1705
+ break;
1706
+ }
1667
1707
  #line 144 "api_node.c.erb"
1668
1708
  case PM_CASE_NODE: {
1669
1709
  pm_case_node_t *cast = (pm_case_node_t *) node;
@@ -2736,7 +2776,7 @@ pm_ast_new(pm_parser_t *parser, pm_node_t *node, rb_encoding *encoding) {
2736
2776
  #line 144 "api_node.c.erb"
2737
2777
  case PM_IF_NODE: {
2738
2778
  pm_if_node_t *cast = (pm_if_node_t *) node;
2739
- VALUE argv[6];
2779
+ VALUE argv[7];
2740
2780
 
2741
2781
  // if_keyword_loc
2742
2782
  #line 183 "api_node.c.erb"
@@ -2746,22 +2786,26 @@ pm_ast_new(pm_parser_t *parser, pm_node_t *node, rb_encoding *encoding) {
2746
2786
  #line 155 "api_node.c.erb"
2747
2787
  argv[1] = rb_ary_pop(value_stack);
2748
2788
 
2789
+ // then_keyword_loc
2790
+ #line 183 "api_node.c.erb"
2791
+ argv[2] = cast->then_keyword_loc.start == NULL ? Qnil : pm_location_new(parser, cast->then_keyword_loc.start, cast->then_keyword_loc.end, source);
2792
+
2749
2793
  // statements
2750
2794
  #line 155 "api_node.c.erb"
2751
- argv[2] = rb_ary_pop(value_stack);
2795
+ argv[3] = rb_ary_pop(value_stack);
2752
2796
 
2753
2797
  // consequent
2754
2798
  #line 155 "api_node.c.erb"
2755
- argv[3] = rb_ary_pop(value_stack);
2799
+ argv[4] = rb_ary_pop(value_stack);
2756
2800
 
2757
2801
  // end_keyword_loc
2758
2802
  #line 183 "api_node.c.erb"
2759
- argv[4] = cast->end_keyword_loc.start == NULL ? Qnil : pm_location_new(parser, cast->end_keyword_loc.start, cast->end_keyword_loc.end, source);
2803
+ argv[5] = cast->end_keyword_loc.start == NULL ? Qnil : pm_location_new(parser, cast->end_keyword_loc.start, cast->end_keyword_loc.end, source);
2760
2804
 
2761
2805
  // location
2762
- argv[5] = pm_location_new(parser, node->location.start, node->location.end, source);
2806
+ argv[6] = pm_location_new(parser, node->location.start, node->location.end, source);
2763
2807
 
2764
- rb_ary_push(value_stack, rb_class_new_instance(6, argv, rb_cPrismIfNode));
2808
+ rb_ary_push(value_stack, rb_class_new_instance(7, argv, rb_cPrismIfNode));
2765
2809
  break;
2766
2810
  }
2767
2811
  #line 144 "api_node.c.erb"
@@ -3604,12 +3648,11 @@ pm_ast_new(pm_parser_t *parser, pm_node_t *node, rb_encoding *encoding) {
3604
3648
  #line 155 "api_node.c.erb"
3605
3649
  argv[0] = rb_ary_pop(value_stack);
3606
3650
 
3607
- // locals
3608
- #line 173 "api_node.c.erb"
3609
- argv[1] = rb_ary_new_capa(cast->locals.size);
3610
- for (size_t index = 0; index < cast->locals.size; index++) {
3611
- assert(cast->locals.ids[index] != 0);
3612
- rb_ary_push(argv[1], rb_id2sym(constants[cast->locals.ids[index] - 1]));
3651
+ // targets
3652
+ #line 158 "api_node.c.erb"
3653
+ argv[1] = rb_ary_new_capa(cast->targets.size);
3654
+ for (size_t index = 0; index < cast->targets.size; index++) {
3655
+ rb_ary_push(argv[1], rb_ary_pop(value_stack));
3613
3656
  }
3614
3657
 
3615
3658
  // location
@@ -4436,24 +4479,6 @@ pm_ast_new(pm_parser_t *parser, pm_node_t *node, rb_encoding *encoding) {
4436
4479
  rb_ary_push(value_stack, rb_class_new_instance(2, argv, rb_cPrismStatementsNode));
4437
4480
  break;
4438
4481
  }
4439
- #line 144 "api_node.c.erb"
4440
- case PM_STRING_CONCAT_NODE: {
4441
- VALUE argv[3];
4442
-
4443
- // left
4444
- #line 155 "api_node.c.erb"
4445
- argv[0] = rb_ary_pop(value_stack);
4446
-
4447
- // right
4448
- #line 155 "api_node.c.erb"
4449
- argv[1] = rb_ary_pop(value_stack);
4450
-
4451
- // location
4452
- argv[2] = pm_location_new(parser, node->location.start, node->location.end, source);
4453
-
4454
- rb_ary_push(value_stack, rb_class_new_instance(3, argv, rb_cPrismStringConcatNode));
4455
- break;
4456
- }
4457
4482
  #line 144 "api_node.c.erb"
4458
4483
  case PM_STRING_NODE: {
4459
4484
  pm_string_node_t *cast = (pm_string_node_t *) node;
@@ -4578,7 +4603,7 @@ pm_ast_new(pm_parser_t *parser, pm_node_t *node, rb_encoding *encoding) {
4578
4603
  #line 144 "api_node.c.erb"
4579
4604
  case PM_UNLESS_NODE: {
4580
4605
  pm_unless_node_t *cast = (pm_unless_node_t *) node;
4581
- VALUE argv[6];
4606
+ VALUE argv[7];
4582
4607
 
4583
4608
  // keyword_loc
4584
4609
  #line 180 "api_node.c.erb"
@@ -4588,22 +4613,26 @@ pm_ast_new(pm_parser_t *parser, pm_node_t *node, rb_encoding *encoding) {
4588
4613
  #line 155 "api_node.c.erb"
4589
4614
  argv[1] = rb_ary_pop(value_stack);
4590
4615
 
4616
+ // then_keyword_loc
4617
+ #line 183 "api_node.c.erb"
4618
+ argv[2] = cast->then_keyword_loc.start == NULL ? Qnil : pm_location_new(parser, cast->then_keyword_loc.start, cast->then_keyword_loc.end, source);
4619
+
4591
4620
  // statements
4592
4621
  #line 155 "api_node.c.erb"
4593
- argv[2] = rb_ary_pop(value_stack);
4622
+ argv[3] = rb_ary_pop(value_stack);
4594
4623
 
4595
4624
  // consequent
4596
4625
  #line 155 "api_node.c.erb"
4597
- argv[3] = rb_ary_pop(value_stack);
4626
+ argv[4] = rb_ary_pop(value_stack);
4598
4627
 
4599
4628
  // end_keyword_loc
4600
4629
  #line 183 "api_node.c.erb"
4601
- argv[4] = cast->end_keyword_loc.start == NULL ? Qnil : pm_location_new(parser, cast->end_keyword_loc.start, cast->end_keyword_loc.end, source);
4630
+ argv[5] = cast->end_keyword_loc.start == NULL ? Qnil : pm_location_new(parser, cast->end_keyword_loc.start, cast->end_keyword_loc.end, source);
4602
4631
 
4603
4632
  // location
4604
- argv[5] = pm_location_new(parser, node->location.start, node->location.end, source);
4633
+ argv[6] = pm_location_new(parser, node->location.start, node->location.end, source);
4605
4634
 
4606
- rb_ary_push(value_stack, rb_class_new_instance(6, argv, rb_cPrismUnlessNode));
4635
+ rb_ary_push(value_stack, rb_class_new_instance(7, argv, rb_cPrismUnlessNode));
4607
4636
  break;
4608
4637
  }
4609
4638
  #line 144 "api_node.c.erb"
@@ -4783,6 +4812,7 @@ Init_prism_api_node(void) {
4783
4812
  rb_cPrismCallOperatorWriteNode = rb_define_class_under(rb_cPrism, "CallOperatorWriteNode", rb_cPrismNode);
4784
4813
  rb_cPrismCallOrWriteNode = rb_define_class_under(rb_cPrism, "CallOrWriteNode", rb_cPrismNode);
4785
4814
  rb_cPrismCapturePatternNode = rb_define_class_under(rb_cPrism, "CapturePatternNode", rb_cPrismNode);
4815
+ rb_cPrismCaseMatchNode = rb_define_class_under(rb_cPrism, "CaseMatchNode", rb_cPrismNode);
4786
4816
  rb_cPrismCaseNode = rb_define_class_under(rb_cPrism, "CaseNode", rb_cPrismNode);
4787
4817
  rb_cPrismClassNode = rb_define_class_under(rb_cPrism, "ClassNode", rb_cPrismNode);
4788
4818
  rb_cPrismClassVariableAndWriteNode = rb_define_class_under(rb_cPrism, "ClassVariableAndWriteNode", rb_cPrismNode);
@@ -4893,7 +4923,6 @@ Init_prism_api_node(void) {
4893
4923
  rb_cPrismSourceLineNode = rb_define_class_under(rb_cPrism, "SourceLineNode", rb_cPrismNode);
4894
4924
  rb_cPrismSplatNode = rb_define_class_under(rb_cPrism, "SplatNode", rb_cPrismNode);
4895
4925
  rb_cPrismStatementsNode = rb_define_class_under(rb_cPrism, "StatementsNode", rb_cPrismNode);
4896
- rb_cPrismStringConcatNode = rb_define_class_under(rb_cPrism, "StringConcatNode", rb_cPrismNode);
4897
4926
  rb_cPrismStringNode = rb_define_class_under(rb_cPrism, "StringNode", rb_cPrismNode);
4898
4927
  rb_cPrismSuperNode = rb_define_class_under(rb_cPrism, "SuperNode", rb_cPrismNode);
4899
4928
  rb_cPrismSymbolNode = rb_define_class_under(rb_cPrism, "SymbolNode", rb_cPrismNode);