prism 1.9.0 → 2.0.0.rc2
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +88 -0
- data/CODE_OF_CONDUCT.md +1 -1
- data/LICENSE.md +1 -1
- data/Makefile +41 -12
- data/README.md +35 -97
- data/config.yml +174 -161
- data/docs/build_system.md +2 -2
- data/docs/configuration.md +5 -4
- data/docs/encoding.md +2 -2
- data/docs/fuzzing.md +4 -11
- data/docs/javascript.md +19 -4
- data/docs/mapping.md +1 -1
- data/docs/releasing.md +77 -5
- data/docs/relocation.md +2 -4
- data/docs/serialization.md +22 -24
- data/docs/testing.md +46 -29
- data/ext/prism/api_node.c +1461 -1401
- data/ext/prism/extconf.rb +3 -4
- data/ext/prism/extension.c +796 -422
- data/ext/prism/extension.h +3 -3
- data/include/prism/arena.h +37 -0
- data/include/prism/ast.h +2727 -610
- data/include/prism/buffer.h +52 -0
- data/include/prism/comments.h +55 -0
- data/include/prism/compiler/accel.h +26 -0
- data/include/prism/compiler/align.h +36 -0
- data/include/prism/compiler/assume.h +25 -0
- data/include/prism/compiler/bswap.h +46 -0
- data/include/prism/compiler/endianness.h +34 -0
- data/include/prism/compiler/exported.h +24 -0
- data/include/prism/compiler/fallthrough.h +22 -0
- data/include/prism/compiler/filesystem.h +32 -0
- data/include/prism/compiler/flex_array.h +19 -0
- data/include/prism/compiler/force_inline.h +21 -0
- data/include/prism/compiler/format.h +25 -0
- data/include/prism/compiler/inline.h +17 -0
- data/include/prism/compiler/nodiscard.h +22 -0
- data/include/prism/compiler/nonnull.h +18 -0
- data/include/prism/compiler/unused.h +18 -0
- data/include/prism/constant_pool.h +81 -0
- data/include/prism/diagnostic.h +38 -403
- data/include/prism/errors_format.h +53 -0
- data/include/prism/excludes.h +29 -0
- data/include/prism/integer.h +41 -0
- data/include/prism/internal/allocator.h +68 -0
- data/include/prism/internal/allocator_debug.h +88 -0
- data/include/prism/internal/arena.h +108 -0
- data/include/prism/internal/bit.h +42 -0
- data/include/prism/internal/buffer.h +94 -0
- data/include/prism/internal/char.h +145 -0
- data/include/prism/internal/comments.h +53 -0
- data/include/prism/internal/constant_pool.h +152 -0
- data/include/prism/internal/diagnostic.h +389 -0
- data/include/prism/{encoding.h → internal/encoding.h} +27 -68
- data/include/prism/internal/integer.h +68 -0
- data/include/prism/internal/isinf.h +16 -0
- data/include/prism/internal/line_offset_list.h +34 -0
- data/include/prism/{util/pm_list.h → internal/list.h} +11 -52
- data/include/prism/internal/magic_comments.h +23 -0
- data/include/prism/internal/memchr.h +15 -0
- data/include/prism/internal/node.h +38 -0
- data/include/prism/internal/options.h +232 -0
- data/include/prism/internal/parser.h +1004 -0
- data/include/prism/internal/regexp.h +26 -0
- data/include/prism/internal/serialize.h +39 -0
- data/include/prism/internal/source.h +72 -0
- data/include/prism/{static_literals.h → internal/static_literals.h} +26 -47
- data/include/prism/internal/stringy.h +35 -0
- data/include/prism/{util/pm_strncasecmp.h → internal/strncasecmp.h} +3 -17
- data/include/prism/{util/pm_strpbrk.h → internal/strpbrk.h} +7 -20
- data/include/prism/internal/tokens.h +11 -0
- data/include/prism/internal/xxh3.h +16 -0
- data/include/prism/json.h +32 -0
- data/include/prism/line_offset_list.h +61 -0
- data/include/prism/magic_comments.h +35 -0
- data/include/prism/node.h +18 -53
- data/include/prism/options.h +146 -280
- data/include/prism/parser.h +356 -867
- data/include/prism/prettyprint.h +6 -9
- data/include/prism/serialize.h +115 -0
- data/include/prism/source.h +155 -0
- data/include/prism/stream.h +28 -0
- data/include/prism/string_query.h +74 -0
- data/include/prism/stringy.h +72 -0
- data/include/prism/version.h +12 -3
- data/include/prism.h +43 -320
- data/lib/prism/compiler.rb +319 -305
- data/lib/prism/desugar_compiler.rb +105 -34
- data/lib/prism/dispatcher.rb +646 -473
- data/lib/prism/dot_visitor.rb +592 -511
- data/lib/prism/dsl.rb +391 -25
- data/lib/prism/ffi.rb +258 -90
- data/lib/prism/inspect_visitor.rb +357 -325
- data/lib/prism/lex_compat.rb +254 -212
- data/lib/prism/mutation_compiler.rb +313 -305
- data/lib/prism/node.rb +20966 -6992
- data/lib/prism/node_ext.rb +180 -187
- data/lib/prism/node_find.rb +215 -0
- data/lib/prism/parse_result/errors.rb +7 -1
- data/lib/prism/parse_result/newlines.rb +276 -8
- data/lib/prism/parse_result.rb +468 -139
- data/lib/prism/pattern.rb +65 -20
- data/lib/prism/reflection.rb +23 -12
- data/lib/prism/relocation.rb +149 -87
- data/lib/prism/serialize.rb +3460 -539
- data/lib/prism/string_query.rb +24 -1
- data/lib/prism/translation/parser/builder.rb +10 -2
- data/lib/prism/translation/parser/compiler.rb +44 -52
- data/lib/prism/translation/parser/lexer.rb +41 -92
- data/lib/prism/translation/parser.rb +1 -1
- data/lib/prism/translation/parser_current.rb +1 -1
- data/lib/prism/translation/ripper/lexer.rb +8 -10
- data/lib/prism/translation/ripper/sexp.rb +2 -10
- data/lib/prism/translation/ripper/shim.rb +2 -0
- data/lib/prism/translation/ripper.rb +967 -209
- data/lib/prism/translation/ruby_parser.rb +73 -316
- data/lib/prism/translation.rb +2 -0
- data/lib/prism/visitor.rb +632 -5
- data/lib/prism.rb +79 -9
- data/prism.gemspec +128 -62
- data/rbi/generated/prism/compiler.rbi +489 -0
- data/rbi/generated/prism/desugar_compiler.rbi +306 -0
- data/rbi/generated/prism/dispatcher.rbi +1138 -0
- data/rbi/generated/prism/dot_visitor.rbi +618 -0
- data/rbi/generated/prism/dsl.rbi +751 -0
- data/rbi/generated/prism/inspect_visitor.rbi +503 -0
- data/rbi/generated/prism/lex_compat.rbi +162 -0
- data/rbi/generated/prism/mutation_compiler.rbi +464 -0
- data/rbi/generated/prism/node.rbi +14407 -0
- data/rbi/generated/prism/node_ext.rbi +259 -0
- data/rbi/generated/prism/node_find.rbi +79 -0
- data/rbi/generated/prism/parse_result/errors.rbi +21 -0
- data/rbi/generated/prism/parse_result/newlines.rbi +208 -0
- data/rbi/generated/prism/parse_result.rbi +776 -0
- data/rbi/generated/prism/pattern.rbi +134 -0
- data/rbi/generated/prism/reflection.rbi +89 -0
- data/rbi/generated/prism/relocation.rbi +329 -0
- data/rbi/generated/prism/serialize.rbi +184 -0
- data/rbi/generated/prism/string_query.rbi +43 -0
- data/rbi/generated/prism/translation.rbi +7 -0
- data/rbi/generated/prism/visitor.rbi +650 -0
- data/rbi/generated/prism.rbi +86 -0
- data/rbi/prism/translation/parser_versions.rbi +5 -0
- data/rbi/rubyvm/node_find.rbi +14 -0
- data/sig/generated/prism/compiler.rbs +492 -0
- data/sig/generated/prism/desugar_compiler.rbs +305 -0
- data/sig/generated/prism/dispatcher.rbs +1292 -0
- data/sig/generated/prism/dot_visitor.rbs +635 -0
- data/sig/generated/prism/dsl.rbs +928 -0
- data/sig/generated/prism/inspect_visitor.rbs +509 -0
- data/sig/generated/prism/lex_compat.rbs +166 -0
- data/sig/{prism → generated/prism}/mutation_compiler.rbs +309 -4
- data/sig/generated/prism/node.rbs +20896 -0
- data/sig/generated/prism/node_ext.rbs +290 -0
- data/sig/generated/prism/node_find.rbs +90 -0
- data/sig/generated/prism/parse_result/errors.rbs +22 -0
- data/sig/generated/prism/parse_result/newlines.rbs +223 -0
- data/sig/generated/prism/parse_result.rbs +871 -0
- data/sig/generated/prism/pattern.rbs +156 -0
- data/sig/generated/prism/reflection.rbs +101 -0
- data/sig/generated/prism/relocation.rbs +428 -0
- data/sig/generated/prism/serialize.rbs +198 -0
- data/sig/generated/prism/string_query.rbs +43 -0
- data/sig/generated/prism/translation.rbs +8 -0
- data/sig/generated/prism/visitor.rbs +1111 -0
- data/sig/generated/prism.rbs +84 -0
- data/sig/manifest.yaml +2 -0
- data/src/arena.c +117 -0
- data/src/{util/pm_buffer.c → buffer.c} +53 -21
- data/src/{util/pm_char.c → char.c} +28 -57
- data/src/comments.c +454 -0
- data/src/constant_pool.c +406 -0
- data/src/diagnostic.c +97 -65
- data/src/encoding.c +8 -3
- data/src/errors_format.c +519 -0
- data/src/{util/pm_integer.c → integer.c} +33 -22
- data/src/json.c +5733 -0
- data/src/line_offset_list.c +100 -0
- data/src/list.c +24 -0
- data/src/{util/pm_memchr.c → memchr.c} +8 -6
- data/src/node.c +3504 -7674
- data/src/options.c +162 -49
- data/src/parser.c +367 -0
- data/src/prettyprint.c +421 -354
- data/src/prism.c +8402 -7161
- data/src/regexp.c +961 -45
- data/src/serialize.c +460 -424
- data/src/source.c +518 -0
- data/src/static_literals.c +111 -43
- data/src/string_query.c +180 -0
- data/src/stringy.c +91 -0
- data/src/{util/pm_strncasecmp.c → strncasecmp.c} +5 -4
- data/src/strpbrk.c +447 -0
- data/src/{token_type.c → tokens.c} +39 -18
- data/src/xxh3.c +292 -0
- metadata +128 -63
- data/ext/prism/api_pack.c +0 -276
- data/include/prism/defines.h +0 -260
- data/include/prism/pack.h +0 -163
- data/include/prism/regexp.h +0 -43
- data/include/prism/util/pm_buffer.h +0 -236
- data/include/prism/util/pm_char.h +0 -204
- data/include/prism/util/pm_constant_pool.h +0 -218
- data/include/prism/util/pm_integer.h +0 -130
- data/include/prism/util/pm_memchr.h +0 -29
- data/include/prism/util/pm_newline_list.h +0 -113
- data/include/prism/util/pm_string.h +0 -200
- data/lib/prism/pack.rb +0 -230
- data/lib/prism/parse_result/comments.rb +0 -188
- data/rbi/prism/compiler.rbi +0 -12
- data/rbi/prism/dsl.rbi +0 -524
- data/rbi/prism/inspect_visitor.rbi +0 -12
- data/rbi/prism/node.rbi +0 -8750
- data/rbi/prism/node_ext.rbi +0 -107
- data/rbi/prism/parse_result.rbi +0 -404
- data/rbi/prism/reflection.rbi +0 -58
- data/rbi/prism/string_query.rbi +0 -12
- data/rbi/prism/visitor.rbi +0 -473
- data/rbi/prism.rbi +0 -63
- data/sig/prism/compiler.rbs +0 -9
- data/sig/prism/dispatcher.rbs +0 -19
- data/sig/prism/dot_visitor.rbs +0 -6
- data/sig/prism/dsl.rbs +0 -351
- data/sig/prism/inspect_visitor.rbs +0 -22
- data/sig/prism/lex_compat.rbs +0 -10
- data/sig/prism/node.rbs +0 -4042
- data/sig/prism/node_ext.rbs +0 -149
- data/sig/prism/pack.rbs +0 -43
- data/sig/prism/parse_result/comments.rbs +0 -38
- data/sig/prism/parse_result.rbs +0 -197
- data/sig/prism/pattern.rbs +0 -13
- data/sig/prism/reflection.rbs +0 -50
- data/sig/prism/relocation.rbs +0 -185
- data/sig/prism/serialize.rbs +0 -8
- data/sig/prism/string_query.rbs +0 -11
- data/sig/prism/visitor.rbs +0 -169
- data/sig/prism.rbs +0 -272
- data/src/pack.c +0 -509
- data/src/util/pm_constant_pool.c +0 -342
- data/src/util/pm_list.c +0 -49
- data/src/util/pm_newline_list.c +0 -125
- data/src/util/pm_string.c +0 -381
- data/src/util/pm_strpbrk.c +0 -206
data/docs/build_system.md
CHANGED
|
@@ -87,7 +87,7 @@ If you need to use memory allocation functions implemented outside of the standa
|
|
|
87
87
|
* Additionally, include `-I [path/to/custom_allocator]` where your `prism_xallocator.h` is located
|
|
88
88
|
* Link the implementation of `prism_xallocator.c` that contains functions declared in `prism_xallocator.h`
|
|
89
89
|
|
|
90
|
-
For further clarity, refer to `include/prism/
|
|
90
|
+
For further clarity, refer to `include/prism/internal/allocator.h`.
|
|
91
91
|
|
|
92
92
|
### Building prism from source as a C library
|
|
93
93
|
|
|
@@ -97,6 +97,7 @@ If you want to build prism as a shared library and link against it, you should c
|
|
|
97
97
|
|
|
98
98
|
* `-fPIC -shared` - Compile as a shared library
|
|
99
99
|
* `-DPRISM_EXPORT_SYMBOLS` - Export the symbols (by default nothing is exported)
|
|
100
|
+
* `-Wl,-Bsymbolic` (ELF platforms) - Bind the library's internal calls to its own exported functions at link time, so that they cannot be redirected to another copy of libprism loaded in the same process
|
|
100
101
|
|
|
101
102
|
#### Flags
|
|
102
103
|
|
|
@@ -113,7 +114,6 @@ MAKEFLAGS="-j10" bundle exec rake compile
|
|
|
113
114
|
* `PRISM_ENCODING_EXCLUDE_FULL` - Will cause the library to exclude the full encoding API, and only include the minimal number of encodings to support parsing Ruby code without encoding comments. By default this is off.
|
|
114
115
|
* `PRISM_EXPORT_SYMBOLS` - Will cause the shared library to export symbols. By default this is off.
|
|
115
116
|
* `PRISM_EXCLUDE_JSON` - Will cause the library to exclude the JSON API. By default this is off.
|
|
116
|
-
* `PRISM_EXCLUDE_PACK` - Will cause the library to exclude the pack API. By default this is off.
|
|
117
117
|
* `PRISM_EXCLUDE_PRETTYPRINT` - Will cause the library to exclude the prettyprint API. By default this is off.
|
|
118
118
|
* `PRISM_EXCLUDE_SERIALIZATION` - Will cause the library to exclude the serialization API. By default this is off.
|
|
119
119
|
* `PRISM_XALLOCATOR` - Will cause the library to use the custom memory allocator. By default this is off.
|
data/docs/configuration.md
CHANGED
|
@@ -5,11 +5,12 @@ A lot of code in prism's repository is templated from a single configuration fil
|
|
|
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
7
|
* `include/prism/diagnostic.h` - for defining the diagnostics
|
|
8
|
+
* `include/prism/node_new.h` - for defining the functions that create the nodes in C
|
|
8
9
|
* `javascript/src/deserialize.js` - for defining how to deserialize the nodes in JavaScript
|
|
9
10
|
* `javascript/src/nodes.js` - for defining the nodes in JavaScript
|
|
10
|
-
* `java/org/prism/AbstractNodeVisitor.java` - for defining the visitor interface for the nodes in Java
|
|
11
|
-
* `java/org/prism/Loader.java` - for defining how to deserialize the nodes in Java
|
|
12
|
-
* `java/org/prism/Nodes.java` - for defining the nodes in Java
|
|
11
|
+
* `java/api/src/main/java-templates/org/ruby_lang/prism/AbstractNodeVisitor.java` - for defining the visitor interface for the nodes in Java
|
|
12
|
+
* `java/api/src/main/java-templates/org/ruby_lang/prism/Loader.java` - for defining how to deserialize the nodes in Java
|
|
13
|
+
* `java/api/src/main/java-templates/org/ruby_lang/prism/Nodes.java` - for defining the nodes in Java
|
|
13
14
|
* `lib/prism/compiler.rb` - for defining the compiler for the nodes in Ruby
|
|
14
15
|
* `lib/prism/dispatcher.rb` - for defining the dispatch visitors for the nodes in Ruby
|
|
15
16
|
* `lib/prism/dot_visitor.rb` - for defining the dot visitor for the nodes in Ruby
|
|
@@ -61,7 +62,7 @@ The available values for `type` are:
|
|
|
61
62
|
* `constant` - A field that is an integer that represents an index in the constant pool. This is a `pm_constant_id_t` in C.
|
|
62
63
|
* `constant[]` - A field that is an array of constants. This is a `pm_constant_id_list_t` in C.
|
|
63
64
|
* `location` - A field that is a location. This is a `pm_location_t` in C.
|
|
64
|
-
* `location?` - A field that is a location that is optionally present. This is a `pm_location_t` in C, but if the value is not present then the `
|
|
65
|
+
* `location?` - A field that is a location that is optionally present. This is a `pm_location_t` in C, but if the value is not present then the `length` field will be `0`.
|
|
65
66
|
* `uint8` - A field that is an 8-bit unsigned integer. This is a `uint8_t` in C.
|
|
66
67
|
* `uint32` - A field that is a 32-bit unsigned integer. This is a `uint32_t` in C.
|
|
67
68
|
|
data/docs/encoding.md
CHANGED
|
@@ -107,7 +107,7 @@ For each of these encodings, prism provides functions for checking if the subseq
|
|
|
107
107
|
|
|
108
108
|
## Getting notified when the encoding changes
|
|
109
109
|
|
|
110
|
-
You may want to get notified when the encoding changes based on the result of parsing an encoding comment. We use this internally for our `lex` function in order to provide the correct encodings for the tokens that are returned. For that you can register a callback with `
|
|
110
|
+
You may want to get notified when the encoding changes based on the result of parsing an encoding comment. We use this internally for our `lex` function in order to provide the correct encodings for the tokens that are returned. For that you can register a callback with `pm_parser_encoding_changed_callback_set`. The callback will be called with a pointer to the parser. The encoding can be accessed through `parser->encoding`.
|
|
111
111
|
|
|
112
112
|
```c
|
|
113
113
|
// When the encoding that is being used to parse the source is changed by prism,
|
|
@@ -117,5 +117,5 @@ typedef void (*pm_encoding_changed_callback_t)(pm_parser_t *parser);
|
|
|
117
117
|
// Register a callback that will be called whenever prism changes the encoding
|
|
118
118
|
// it is using to parse based on the magic comment.
|
|
119
119
|
PRISM_EXPORTED_FUNCTION void
|
|
120
|
-
|
|
120
|
+
pm_parser_encoding_changed_callback_set(pm_parser_t *parser, pm_encoding_changed_callback_t callback);
|
|
121
121
|
```
|
data/docs/fuzzing.md
CHANGED
|
@@ -5,8 +5,7 @@ We use fuzzing to test the various entrypoints to the library. The fuzzer we use
|
|
|
5
5
|
```
|
|
6
6
|
fuzz
|
|
7
7
|
├── corpus
|
|
8
|
-
│
|
|
9
|
-
│ └── regexp fuzzing corpus for regexp
|
|
8
|
+
│ └── parse fuzzing corpus for parsing (a symlink to our fixtures)
|
|
10
9
|
├── dict a AFL++ dictionary containing various tokens
|
|
11
10
|
├── docker
|
|
12
11
|
│ └── Dockerfile for building a container with the fuzzer toolchain
|
|
@@ -14,8 +13,6 @@ fuzz
|
|
|
14
13
|
├── heisenbug.c entrypoint for reproducing a crash or hang
|
|
15
14
|
├── parse.c fuzz handler for parsing
|
|
16
15
|
├── parse.sh script to run parsing fuzzer
|
|
17
|
-
├── regexp.c fuzz handler for regular expression parsing
|
|
18
|
-
├── regexp.sh script to run regexp fuzzer
|
|
19
16
|
└── tools
|
|
20
17
|
├── backtrace.sh generates backtrace files for a crash directory
|
|
21
18
|
└── minimize.sh generates minimized crash or hang files
|
|
@@ -23,16 +20,14 @@ fuzz
|
|
|
23
20
|
|
|
24
21
|
## Usage
|
|
25
22
|
|
|
26
|
-
There
|
|
23
|
+
There is currently one fuzz target:
|
|
27
24
|
|
|
28
25
|
- `pm_serialize_parse` (parse)
|
|
29
|
-
- `pm_regexp_parse` (regexp)
|
|
30
26
|
|
|
31
|
-
|
|
27
|
+
Fuzzing can be performed with
|
|
32
28
|
|
|
33
29
|
```
|
|
34
30
|
make fuzz-run-parse
|
|
35
|
-
make fuzz-run-regexp
|
|
36
31
|
```
|
|
37
32
|
|
|
38
33
|
To end a fuzzing job, interrupt with CTRL+C. To enter a container with the fuzzing toolchain and debug utilities, run
|
|
@@ -43,8 +38,6 @@ make fuzz-debug
|
|
|
43
38
|
|
|
44
39
|
# Out-of-bounds reads
|
|
45
40
|
|
|
46
|
-
Currently, encoding functionality implementing the `pm_encoding_t` interface can read outside of inputs. For the time being, ASAN instrumentation is disabled for functions from src/enc. See `fuzz/asan.ignore`.
|
|
47
|
-
|
|
48
41
|
To disable ASAN read instrumentation globally, use the `FUZZ_FLAGS` environment variable e.g.
|
|
49
42
|
|
|
50
43
|
```
|
|
@@ -55,7 +48,7 @@ Note, that this may make reproducing bugs difficult as they may depend on memory
|
|
|
55
48
|
|
|
56
49
|
```
|
|
57
50
|
make fuzz-debug # enter the docker container with build tools
|
|
58
|
-
make build/fuzz.heisenbug.parse
|
|
51
|
+
make build/fuzz.heisenbug.parse
|
|
59
52
|
./build/fuzz.heisenbug.parse path-to-problem-input
|
|
60
53
|
```
|
|
61
54
|
|
data/docs/javascript.md
CHANGED
|
@@ -16,10 +16,25 @@ Then import the package:
|
|
|
16
16
|
import { loadPrism } from "@ruby/prism";
|
|
17
17
|
```
|
|
18
18
|
|
|
19
|
-
Then call the load function to get a parse function
|
|
19
|
+
Then call the load function to get a parse function. It takes the source as
|
|
20
|
+
bytes, in the encoding the source is written in, because that is what the parser
|
|
21
|
+
reads and what the locations in the result are offsets into. If you are starting
|
|
22
|
+
from a string, encode it first:
|
|
20
23
|
|
|
21
24
|
```js
|
|
22
|
-
const
|
|
25
|
+
const parseArray = await loadPrism();
|
|
26
|
+
|
|
27
|
+
function parse(source) {
|
|
28
|
+
return parseArray(new TextEncoder().encode(source));
|
|
29
|
+
}
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Reading a file gives you those bytes already, so there is nothing to encode:
|
|
33
|
+
|
|
34
|
+
```js
|
|
35
|
+
import { readFile } from "node:fs/promises";
|
|
36
|
+
|
|
37
|
+
const parseResult = parseArray(await readFile("example.rb"));
|
|
23
38
|
```
|
|
24
39
|
|
|
25
40
|
## Browser
|
|
@@ -56,7 +71,7 @@ Finally, you can create a function that will parse a string of Ruby code.
|
|
|
56
71
|
|
|
57
72
|
```js
|
|
58
73
|
function parse(source) {
|
|
59
|
-
return parsePrism(instance.exports, source);
|
|
74
|
+
return parsePrism(instance.exports, new TextEncoder().encode(source));
|
|
60
75
|
}
|
|
61
76
|
```
|
|
62
77
|
|
|
@@ -84,7 +99,7 @@ Here's an example of a custom `FooCalls` visitor:
|
|
|
84
99
|
import { loadPrism, Visitor } from "@ruby/prism"
|
|
85
100
|
|
|
86
101
|
const parse = await loadPrism();
|
|
87
|
-
const parseResult = parse("foo()");
|
|
102
|
+
const parseResult = parse(new TextEncoder().encode("foo()"));
|
|
88
103
|
|
|
89
104
|
class FooCalls extends Visitor {
|
|
90
105
|
visitCallNode(node) {
|
data/docs/mapping.md
CHANGED
|
@@ -112,6 +112,6 @@ The following table shows how the various CRuby nodes are mapped to prism nodes.
|
|
|
112
112
|
| `NODE_ARYPTN` | `PM_ARRAY_PATTERN_NODE` |
|
|
113
113
|
| `NODE_HSHPTN` | `PM_HASH_PATTERN_NODE` |
|
|
114
114
|
| `NODE_FNDPTN` | `PM_FIND_PATTERN_NODE` |
|
|
115
|
-
| `NODE_ERROR` | `
|
|
115
|
+
| `NODE_ERROR` | `PM_ERROR_RECOVERY_NODE` |
|
|
116
116
|
| `NODE_LAST` | |
|
|
117
117
|
```
|
data/docs/releasing.md
CHANGED
|
@@ -23,9 +23,10 @@ ruby -pi -e 'gsub(/PRISM_VERSION_PATCH \d+/, %Q{PRISM_VERSION_PATCH #{ENV["PRISM
|
|
|
23
23
|
ruby -pi -e 'gsub(/PRISM_VERSION ".+?"/, %Q{PRISM_VERSION "#{ENV["PRISM_VERSION"]}"})' include/prism/version.h
|
|
24
24
|
ruby -pi -e 'gsub(/"version": ".+?"/, %Q{"version": "#{ENV["PRISM_VERSION"]}"})' javascript/package.json
|
|
25
25
|
ruby -pi -e 'gsub(/lossy\(\), ".+?"/, %Q{lossy(), "#{ENV["PRISM_VERSION"]}"})' rust/ruby-prism-sys/tests/utils_tests.rs
|
|
26
|
-
ruby -pi -e 'gsub(
|
|
27
|
-
ruby -pi -e 'gsub(/\d+, "prism
|
|
28
|
-
ruby -pi -e 'gsub(/\d+, "prism
|
|
26
|
+
ruby -pi -e 'gsub(/assertEquals\(".+?", prism.version\(\)\)/, %Q{assertEquals("#{ENV["PRISM_VERSION"]}", prism.version())})' java/wasm/src/test/java/org/jruby/parser/prism/WASMTest.java
|
|
27
|
+
ruby -pi -e 'gsub(/\d+, "prism major/, %Q{#{ENV["PRISM_MAJOR"]}, "prism major})' templates/java/api/src/main/java-templates/org/ruby_lang/prism/Loader.java.erb
|
|
28
|
+
ruby -pi -e 'gsub(/\d+, "prism minor/, %Q{#{ENV["PRISM_MINOR"]}, "prism minor})' templates/java/api/src/main/java-templates/org/ruby_lang/prism/Loader.java.erb
|
|
29
|
+
ruby -pi -e 'gsub(/\d+, "prism patch/, %Q{#{ENV["PRISM_PATCH"]}, "prism patch})' templates/java/api/src/main/java-templates/org/ruby_lang/prism/Loader.java.erb
|
|
29
30
|
ruby -pi -e 'gsub(/MAJOR_VERSION = \d+/, %Q{MAJOR_VERSION = #{ENV["PRISM_MAJOR"]}})' templates/javascript/src/deserialize.js.erb
|
|
30
31
|
ruby -pi -e 'gsub(/MINOR_VERSION = \d+/, %Q{MINOR_VERSION = #{ENV["PRISM_MINOR"]}})' templates/javascript/src/deserialize.js.erb
|
|
31
32
|
ruby -pi -e 'gsub(/PATCH_VERSION = \d+/, %Q{PATCH_VERSION = #{ENV["PRISM_PATCH"]}})' templates/javascript/src/deserialize.js.erb
|
|
@@ -57,6 +58,8 @@ chruby ruby-4.1.0-dev && BUNDLE_GEMFILE=gemfiles/4.1/Gemfile bundle install
|
|
|
57
58
|
bundle exec rake cargo:build
|
|
58
59
|
```
|
|
59
60
|
|
|
61
|
+
* Regenerate `rust/ruby-prism-sys/src/bindings.rs` with `bundle exec rake cargo:bindings`. CI fails if the committed file is stale.
|
|
62
|
+
|
|
60
63
|
* Commit all of the updated files:
|
|
61
64
|
|
|
62
65
|
```sh
|
|
@@ -71,5 +74,74 @@ git push
|
|
|
71
74
|
|
|
72
75
|
## Publishing
|
|
73
76
|
|
|
74
|
-
|
|
75
|
-
|
|
77
|
+
### Automatic (Github Actions)
|
|
78
|
+
|
|
79
|
+
The [`publish-gem.yml`](../.github/workflows/publish-gem.yml) workflow handles publishing. It triggers either on a `v*` tag push or via manual `workflow_dispatch`. In both cases it:
|
|
80
|
+
|
|
81
|
+
- Builds the source gem and all native gems (calling the reusable [`build-gems.yml`](../.github/workflows/build-gems.yml) workflow with `--release`).
|
|
82
|
+
- Pushes every gem to RubyGems.org using OIDC trusted publishing (no API key required).
|
|
83
|
+
- Creates (or updates) a GitHub release for the tag, attaches the `.gem` files and a `CHECKSUMS.txt`, and auto-generates release notes.
|
|
84
|
+
|
|
85
|
+
The `push` job is gated on `github.repository == 'ruby/prism'` and runs in the `rubygems.org` GitHub environment, which is the trust anchor for OIDC publishing to RubyGems.org. Pushes that already exist on RubyGems.org are reported as warnings rather than failing the run, so re-runs after a partial failure are safe.
|
|
86
|
+
|
|
87
|
+
#### 1. Trigger the release pipeline
|
|
88
|
+
|
|
89
|
+
After the preparation commit is on `main`, tag and push:
|
|
90
|
+
|
|
91
|
+
```sh
|
|
92
|
+
git tag "v${PRISM_VERSION}"
|
|
93
|
+
git push origin "v${PRISM_VERSION}"
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
The `v*` tag push triggers `publish-gem.yml` automatically.
|
|
97
|
+
|
|
98
|
+
If you need to re-trigger the workflow against an existing tag — for example, if the tag-push run failed partway through and you've fixed the cause — go to the Actions tab and use "Run workflow" on `Publish gem to rubygems.org`, passing the tag (e.g. `v1.10.0`) as the `version_tag` input. The workflow will check out that tag, rebuild, and push.
|
|
99
|
+
|
|
100
|
+
#### 2. Edit the release notes
|
|
101
|
+
|
|
102
|
+
The workflow creates the GitHub release with auto-generated notes. Edit it to paste in the relevant section of `CHANGELOG.md`.
|
|
103
|
+
|
|
104
|
+
### Manually
|
|
105
|
+
|
|
106
|
+
If for whatever reason (like an emergency), you need to release a version and the Github Actions pipelines aren't available, here's how you can build the full set of native gems.
|
|
107
|
+
|
|
108
|
+
#### 1. Build gems
|
|
109
|
+
|
|
110
|
+
```sh
|
|
111
|
+
bin/build-gems
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
This script will:
|
|
115
|
+
- Run `bundle update` and `bundle package` to vendor dependencies
|
|
116
|
+
- Run a safety check (`compile` and `test`)
|
|
117
|
+
- Build all gems via `rake gem:all` (source gem + native gems for all platforms using rake-compiler-dock)
|
|
118
|
+
- Place all gems in `./gems/`
|
|
119
|
+
- Verify all built gems with `test/prism/packaging/test-gem-file-contents`
|
|
120
|
+
- Print SHA256 checksums for inclusion in release notes
|
|
121
|
+
|
|
122
|
+
#### 2. Push gems
|
|
123
|
+
|
|
124
|
+
Push the native gems first, then the source gem last (so that when users see the new version, native gems are already available):
|
|
125
|
+
|
|
126
|
+
```sh
|
|
127
|
+
# push native gems first
|
|
128
|
+
for gem in gems/prism-*-*.gem ; do
|
|
129
|
+
gem push "$gem"
|
|
130
|
+
done
|
|
131
|
+
|
|
132
|
+
# push source gem last
|
|
133
|
+
gem push gems/prism-${PRISM_VERSION}.gem
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
#### 3. Push tag
|
|
137
|
+
|
|
138
|
+
Push a new tag to the GitHub repository, following the `vX.Y.Z` format:
|
|
139
|
+
|
|
140
|
+
```sh
|
|
141
|
+
git tag "v${PRISM_VERSION}"
|
|
142
|
+
git push origin "v${PRISM_VERSION}"
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
#### 4. Create a release
|
|
146
|
+
|
|
147
|
+
Update the GitHub release page with a copy of the latest entry in the `CHANGELOG.md` file, including the SHA256 checksums from the build output.
|
data/docs/relocation.md
CHANGED
|
@@ -6,10 +6,10 @@ The `Relocation` module provides an API around this property. It allows you to "
|
|
|
6
6
|
|
|
7
7
|
## Getting started
|
|
8
8
|
|
|
9
|
-
To get started with the `Relocation` module, you would first instantiate a `Repository` object. You do this through a DSL that chains method calls for configuration. For example, if for every entry in the repository you want to store the start and end lines, the start and end code unit columns for in UTF-16,
|
|
9
|
+
To get started with the `Relocation` module, you would first instantiate a `Repository` object. You do this through a DSL that chains method calls for configuration. For example, if for every entry in the repository you want to store the start and end lines, and the start and end code unit columns for in UTF-16, you would:
|
|
10
10
|
|
|
11
11
|
```ruby
|
|
12
|
-
repository = Prism::Relocation.filepath("path/to/file").lines.code_unit_columns(Encoding::UTF_16)
|
|
12
|
+
repository = Prism::Relocation.filepath("path/to/file").lines.code_unit_columns(Encoding::UTF_16)
|
|
13
13
|
```
|
|
14
14
|
|
|
15
15
|
Now that you have the repository, you can pass it into any of the `save*` APIs on nodes or locations to create entries in the repository that will be lazily reified.
|
|
@@ -27,8 +27,6 @@ entry.end_line
|
|
|
27
27
|
|
|
28
28
|
entry.start_code_units_column
|
|
29
29
|
entry.end_code_units_column
|
|
30
|
-
|
|
31
|
-
entry.leading_comments
|
|
32
30
|
```
|
|
33
31
|
|
|
34
32
|
Note that if you had configured other fields to be saved, you would be able to access them as well. The first time one of these fields is accessed, the repository will reify every entry it knows about and then clean itself up. In this way, you can effectively treat them as if you had kept around lightweight versions of `Prism::Node` or `Prism::Location` objects.
|
data/docs/serialization.md
CHANGED
|
@@ -44,6 +44,17 @@ The comment type is one of:
|
|
|
44
44
|
| `1` | comment type |
|
|
45
45
|
| location | the location in the source of this comment |
|
|
46
46
|
|
|
47
|
+
### comment attachment
|
|
48
|
+
|
|
49
|
+
A comment attachment lists the comments owned by a single node.
|
|
50
|
+
Only nodes that own comments have an attachment, and there are none unless the `attach_comments` option was set.
|
|
51
|
+
|
|
52
|
+
| # bytes | field |
|
|
53
|
+
| --- | --- |
|
|
54
|
+
| varuint | the node identifier of the node that owns the comments |
|
|
55
|
+
| varuint | number of comments owned by the node |
|
|
56
|
+
| varuint* | the index of each owned comment in the list of comments, ordered by start offset |
|
|
57
|
+
|
|
47
58
|
### magic comment
|
|
48
59
|
|
|
49
60
|
| # bytes | field |
|
|
@@ -95,10 +106,13 @@ The header is structured like the following table:
|
|
|
95
106
|
| `1` | 1 indicates only semantics fields were serialized, 0 indicates all fields were serialized (including location fields) |
|
|
96
107
|
| string | the encoding name |
|
|
97
108
|
| varsint | the start line |
|
|
109
|
+
| `8` | the checksum of the source up to the end of the `__END__` line in native endian order, see `pm_parser_checksum` |
|
|
98
110
|
| varuint | number of newline offsets |
|
|
99
111
|
| varuint* | newline offsets |
|
|
100
112
|
| varuint | number of comments |
|
|
101
113
|
| comment* | comments |
|
|
114
|
+
| varuint | number of nodes with attached comments |
|
|
115
|
+
| comment attachment* | comment attachments |
|
|
102
116
|
| varuint | number of magic comments |
|
|
103
117
|
| magic comment* | magic comments |
|
|
104
118
|
| location? | the optional location of the `__END__` keyword and its contents |
|
|
@@ -106,6 +120,7 @@ The header is structured like the following table:
|
|
|
106
120
|
| error* | errors |
|
|
107
121
|
| varuint | number of warnings |
|
|
108
122
|
| warning* | warnings |
|
|
123
|
+
| `1` | `1` if the source is continuable (incomplete but could become valid with more input), `0` otherwise |
|
|
109
124
|
| `4` | content pool offset |
|
|
110
125
|
| varuint | content pool size |
|
|
111
126
|
|
|
@@ -126,7 +141,7 @@ Every field on the node is then appended to the serialized string. The fields ca
|
|
|
126
141
|
* `node` - A field that is a node. This is structured just as like parent node.
|
|
127
142
|
* `node?` - A field that is a node that is optionally present. If the node is not present, then a single `0` byte will be written in its place. If it is present, then it will be structured just as like parent node.
|
|
128
143
|
* `node[]` - A field that is an array of nodes. This is structured as a variable-length integer length, followed by the child nodes themselves.
|
|
129
|
-
* `string` - A field that is a string. For example, this is used as the name of the method in a call node, since it cannot directly reference the source string (as in `@-` or `foo=`). This is structured as a variable-length integer byte length, followed by the string
|
|
144
|
+
* `string` - A field that is a string. For example, this is used as the name of the method in a call node, since it cannot directly reference the source string (as in `@-` or `foo=`). This is structured as a variable-length integer byte length, followed by the string bytes (_without_ a trailing null byte).
|
|
130
145
|
* `constant` - A variable-length integer that represents an index in the constant pool.
|
|
131
146
|
* `constant?` - An optional variable-length integer that represents an index in the constant pool. If it's not present, then a single `0` byte will be written in its place.
|
|
132
147
|
* `integer` - A field that represents an arbitrary-sized integer. The structure is listed above.
|
|
@@ -135,23 +150,14 @@ Every field on the node is then appended to the serialized string. The fields ca
|
|
|
135
150
|
* `uint8` - A field that is an 8-bit unsigned integer. This is structured as a single byte.
|
|
136
151
|
* `uint32` - A field that is a 32-bit unsigned integer. This is structured as a variable-length integer.
|
|
137
152
|
|
|
138
|
-
After the syntax tree, the content pool is serialized. This is a list of constants that were referenced from within the tree. The content pool begins at the offset specified in the header.
|
|
139
|
-
|
|
140
|
-
In the case that it is owned, the constant is structured as follows:
|
|
153
|
+
After the syntax tree, the content pool is serialized. This is a list of constants that were referenced from within the tree. The content pool begins at the offset specified in the header. Every constant is embedded in the serialization. Each constant is structured as follows:
|
|
141
154
|
|
|
142
155
|
| # bytes | field |
|
|
143
156
|
| --- | --- |
|
|
144
157
|
| `4` | the byte offset in the serialization for the contents of the constant |
|
|
145
158
|
| `4` | the byte length in the serialization |
|
|
146
159
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
| # bytes | field |
|
|
150
|
-
| --- | --- |
|
|
151
|
-
| `4` | the byte offset in the source string for the contents of the constant |
|
|
152
|
-
| `4` | the byte length in the source string |
|
|
153
|
-
|
|
154
|
-
After the constant pool, the contents of the owned constants are serialized. This is just a sequence of bytes that represent the contents of the constants. At the end of the serialization, the buffer is null terminated.
|
|
160
|
+
After the constant pool, the contents of the constants are serialized. This is just a sequence of bytes that represent the contents of the constants. At the end of the serialization, the buffer is null terminated.
|
|
155
161
|
|
|
156
162
|
## APIs
|
|
157
163
|
|
|
@@ -161,31 +167,23 @@ The relevant APIs and struct definitions are listed below:
|
|
|
161
167
|
// A pm_buffer_t is a simple memory buffer that stores data in a contiguous
|
|
162
168
|
// block of memory. It is used to store the serialized representation of a
|
|
163
169
|
// prism tree.
|
|
164
|
-
typedef struct {
|
|
165
|
-
char *value;
|
|
166
|
-
size_t length;
|
|
167
|
-
size_t capacity;
|
|
168
|
-
} pm_buffer_t;
|
|
169
|
-
|
|
170
|
-
// Free the memory associated with the buffer.
|
|
171
|
-
void pm_buffer_free(pm_buffer_t *);
|
|
172
170
|
|
|
173
171
|
// Parse and serialize the AST represented by the given source to the given
|
|
174
172
|
// buffer.
|
|
175
173
|
void pm_serialize_parse(pm_buffer_t *buffer, const uint8_t *source, size_t length, const char *data);
|
|
176
174
|
```
|
|
177
175
|
|
|
178
|
-
Typically you would
|
|
176
|
+
Typically you would allocate a `pm_buffer_t` and call `pm_serialize_parse`, as in:
|
|
179
177
|
|
|
180
178
|
```c
|
|
181
179
|
void
|
|
182
180
|
serialize(const uint8_t *source, size_t length) {
|
|
183
|
-
pm_buffer_t buffer =
|
|
184
|
-
pm_serialize_parse(
|
|
181
|
+
pm_buffer_t *buffer = pm_buffer_new();
|
|
182
|
+
pm_serialize_parse(buffer, source, length, NULL);
|
|
185
183
|
|
|
186
184
|
// Do something with the serialized string.
|
|
187
185
|
|
|
188
|
-
pm_buffer_free(
|
|
186
|
+
pm_buffer_free(buffer);
|
|
189
187
|
}
|
|
190
188
|
```
|
|
191
189
|
|
data/docs/testing.md
CHANGED
|
@@ -14,42 +14,59 @@ These test specific prism implementation details like comments, errors, and regu
|
|
|
14
14
|
|
|
15
15
|
Snapshot tests ensure that parsed output is equivalent to previous parsed output. There are many categorized examples of valid syntax within the `test/prism/fixtures/` directory. When the test suite runs, it will parse all of this syntax, and compare it against corresponding files in the `test/prism/snapshots/` directory. For example, `test/prism/fixtures/strings.txt` has a corresponding `test/prism/snapshots/strings.txt`.
|
|
16
16
|
|
|
17
|
-
If the parsed files do not match, it will raise an error. If there is not a corresponding file in the `test/prism/snapshots/` directory, one will be created so that it exists for the next test run.
|
|
17
|
+
If the parsed files do not match, it will raise an error. If there is not a corresponding file in the `test/prism/snapshots/` directory, one will be created so that it exists for the next test run. If the snapshot already exists but differs you can run with `UPDATE_SNAPSHOTS=1` to regenerate them.
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
When working on a specific example, you can limit tests to a single fixture via `FOCUS=strings.txt`.
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
#### Versioning
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
1. **bin/lex** takes a filepath and compares prism's lexed output to Ripper's lexed output. It prints any lexed output that doesn't match. It does some minor transformations to the lexed output in order to compare them, like split prism's heredoc tokens to mirror Ripper's.
|
|
28
|
-
|
|
29
|
-
```
|
|
30
|
-
$ bin/lex test.rb
|
|
31
|
-
```
|
|
23
|
+
Prism is capable of parsing code with different version specifiers. The test suite reflects this:
|
|
24
|
+
* Test files under `test/prism/snapshots/*.txt` are validated against all supported versions.
|
|
25
|
+
* Test files under `test/prism/snapshots/[MAJOR].[MINOR]/*.txt` are validated against all versions starting from the given version.
|
|
26
|
+
* Test files under `test/prism/snapshots/[MAJOR].[MINOR]-[MAJOR].[MINOR]/*.txt` are validated against the given version range.
|
|
32
27
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
```
|
|
36
|
-
$ VERBOSE=1 bin/lex test.rb
|
|
37
|
-
```
|
|
28
|
+
Should you add logic specific to a new ruby version, validate the new behavior but also that old behavior remains the same.
|
|
38
29
|
|
|
39
|
-
|
|
30
|
+
### Error tests
|
|
40
31
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
32
|
+
Error tests are similar to snapshot tests and expectations are located under `test/prism/errors/`.
|
|
33
|
+
They contain invalid syntax and rendered errors, similar to how `ruby -c` would show them.
|
|
34
|
+
Error tests are versioned in the same way that snapshot tests are and also support `FOCUS`/`UPDATE_SNAPSHOTS`.
|
|
44
35
|
|
|
45
|
-
|
|
36
|
+
## Local testing
|
|
46
37
|
|
|
38
|
+
As you are working, you will likely want to test your code locally. `test.rb` is ignored by git, so it can be used for local testing. **bin/prism** contains a multitude of options to aid you, depending on which area
|
|
39
|
+
of prism you are working on. `bin/prism --help` prints a list of all available commands.
|
|
40
|
+
|
|
41
|
+
Commands that take a `[source]` can be called in three different ways:
|
|
42
|
+
1. `bin/prism parse` will use `test.rb` as input.
|
|
43
|
+
1. `bin/prism parse foo.rb` will read from the given path.
|
|
44
|
+
1. `bin/prism parse -e "1 + 2"` will use the given source code.
|
|
45
|
+
|
|
46
|
+
`bin/prism parse` is aliased as `bin/parse` for for convenience.
|
|
47
|
+
|
|
48
|
+
### Validating your changes against real code
|
|
49
|
+
|
|
50
|
+
If you create a change which is not expected to have a large impact on real code, you can use
|
|
51
|
+
`bin/compare` to validate that assumption. It compiles two versions of prism (for example
|
|
52
|
+
your feature branch and main), comparing ast, errors and warnings. Once done, a report is
|
|
53
|
+
printed showcasing in what way files changed, if any (syntax valid -> syntax invalid, changed AST, etc.)
|
|
54
|
+
|
|
55
|
+
```sh
|
|
56
|
+
$ bin/compare main feature-branch ~/all-of-rubygems`
|
|
57
|
+
...
|
|
58
|
+
0/3157385
|
|
59
|
+
1000/3157385
|
|
60
|
+
...
|
|
61
|
+
3157000/3157385
|
|
62
|
+
Oops:
|
|
63
|
+
intermine-1.05.00/lib/intermine/lists.rb changed from valid(true) to valid(false)
|
|
64
|
+
whistle-0.1.1/lib/resource.rb changed from valid(true) to valid(false)
|
|
65
|
+
Took 2661.5390081949999512 seconds
|
|
47
66
|
```
|
|
48
|
-
$ bin/parse test.rb
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
`bin/parse` can also be used with `-e` and then source code, like this:
|
|
52
67
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
68
|
+
It is up to you to aquire code samples. Here are some resources you can use for this purpose:
|
|
69
|
+
* https://github.com/jeromedalbert/real-world-ruby-apps
|
|
70
|
+
* https://github.com/eliotsykes/real-world-rails
|
|
71
|
+
* https://github.com/rubygems/rubygems-mirror
|
|
72
|
+
Requires some work to unpack the downloaded gems. You should also run with `RUBYGEMS_MIRROR_ONLY_LATEST=TRUE`.
|