prism 0.17.1 → 0.18.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +30 -1
- data/Makefile +5 -5
- data/README.md +2 -2
- data/config.yml +26 -13
- data/docs/build_system.md +6 -6
- data/docs/building.md +1 -1
- data/docs/configuration.md +1 -0
- data/docs/encoding.md +68 -32
- data/docs/heredocs.md +1 -1
- data/docs/javascript.md +29 -1
- data/docs/ruby_api.md +14 -0
- data/ext/prism/api_node.c +74 -45
- data/ext/prism/extconf.rb +91 -127
- data/ext/prism/extension.c +1 -1
- data/ext/prism/extension.h +1 -1
- data/include/prism/ast.h +148 -133
- data/include/prism/diagnostic.h +27 -1
- data/include/prism/enc/pm_encoding.h +42 -1
- data/include/prism/parser.h +6 -0
- data/include/prism/version.h +3 -3
- data/lib/prism/compiler.rb +3 -3
- data/lib/prism/debug.rb +4 -0
- data/lib/prism/desugar_compiler.rb +1 -0
- data/lib/prism/dispatcher.rb +14 -14
- data/lib/prism/dot_visitor.rb +4334 -0
- data/lib/prism/dsl.rb +11 -11
- data/lib/prism/ffi.rb +3 -3
- data/lib/prism/mutation_compiler.rb +6 -6
- data/lib/prism/node.rb +182 -113
- data/lib/prism/node_ext.rb +61 -3
- data/lib/prism/parse_result.rb +46 -12
- data/lib/prism/serialize.rb +125 -131
- data/lib/prism/visitor.rb +3 -3
- data/lib/prism.rb +1 -0
- data/prism.gemspec +5 -1
- data/rbi/prism.rbi +83 -54
- data/sig/prism.rbs +47 -32
- data/src/diagnostic.c +61 -3
- data/src/enc/pm_big5.c +63 -0
- data/src/enc/pm_cp51932.c +57 -0
- data/src/enc/pm_euc_jp.c +10 -0
- data/src/enc/pm_gbk.c +5 -2
- data/src/enc/pm_tables.c +1478 -148
- data/src/node.c +33 -21
- data/src/prettyprint.c +1027 -925
- data/src/prism.c +925 -374
- data/src/regexp.c +12 -12
- data/src/serialize.c +36 -9
- metadata +6 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ec5c0e5a9423ea40bad7c6ea608cb43dc0250c4d7119b4960271f547697540c1
|
4
|
+
data.tar.gz: d505901505b8c4eee195171721d68fe05dde76de171e5a80115a301ba030cc34
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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.
|
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/
|
26
|
-
static: build/
|
25
|
+
shared: build/libprism.$(SOEXT)
|
26
|
+
static: build/libprism.a
|
27
27
|
wasm: javascript/src/prism.wasm
|
28
28
|
|
29
|
-
build/
|
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/
|
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 (
|
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
|
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:
|
1882
|
-
type:
|
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 `
|
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 `
|
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
|
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/
|
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 `
|
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 `
|
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
|
|
data/docs/configuration.md
CHANGED
@@ -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
|
-
* `
|
16
|
-
* `
|
17
|
-
* `
|
18
|
-
* `
|
19
|
-
* `
|
20
|
-
* `
|
21
|
-
* `
|
22
|
-
* `
|
23
|
-
* `
|
24
|
-
* `
|
25
|
-
* `
|
26
|
-
* `
|
27
|
-
* `
|
28
|
-
* `
|
29
|
-
* `
|
30
|
-
* `
|
31
|
-
* `
|
32
|
-
* `
|
33
|
-
* `
|
34
|
-
* `
|
35
|
-
* `
|
36
|
-
* `
|
37
|
-
* `
|
38
|
-
* `
|
39
|
-
* `
|
40
|
-
* `
|
41
|
-
* `
|
42
|
-
* `
|
43
|
-
* `
|
44
|
-
* `
|
45
|
-
* `
|
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
|
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
|
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[
|
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[
|
2795
|
+
argv[3] = rb_ary_pop(value_stack);
|
2752
2796
|
|
2753
2797
|
// consequent
|
2754
2798
|
#line 155 "api_node.c.erb"
|
2755
|
-
argv[
|
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[
|
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[
|
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(
|
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
|
-
//
|
3608
|
-
#line
|
3609
|
-
argv[1] = rb_ary_new_capa(cast->
|
3610
|
-
for (size_t index = 0; index < cast->
|
3611
|
-
|
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[
|
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[
|
4622
|
+
argv[3] = rb_ary_pop(value_stack);
|
4594
4623
|
|
4595
4624
|
// consequent
|
4596
4625
|
#line 155 "api_node.c.erb"
|
4597
|
-
argv[
|
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[
|
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[
|
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(
|
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);
|