prism 2.0.0.rc1-arm64-darwin
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 +7 -0
- data/BSDmakefile +58 -0
- data/CHANGELOG.md +870 -0
- data/CODE_OF_CONDUCT.md +76 -0
- data/CONTRIBUTING.md +58 -0
- data/LICENSE.md +7 -0
- data/Makefile +145 -0
- data/README.md +81 -0
- data/config.yml +4752 -0
- data/docs/build_system.md +119 -0
- data/docs/configuration.md +69 -0
- data/docs/cruby_compilation.md +27 -0
- data/docs/design.md +53 -0
- data/docs/encoding.md +121 -0
- data/docs/fuzzing.md +81 -0
- data/docs/heredocs.md +36 -0
- data/docs/javascript.md +133 -0
- data/docs/local_variable_depth.md +229 -0
- data/docs/mapping.md +117 -0
- data/docs/parser_translation.md +24 -0
- data/docs/parsing_rules.md +22 -0
- data/docs/releasing.md +147 -0
- data/docs/relocation.md +32 -0
- data/docs/ripper_translation.md +63 -0
- data/docs/ruby_api.md +45 -0
- data/docs/ruby_parser_translation.md +19 -0
- data/docs/serialization.md +230 -0
- data/docs/testing.md +72 -0
- data/ext/prism/api_node.c +7005 -0
- data/ext/prism/extconf.rb +126 -0
- data/ext/prism/extension.c +1783 -0
- data/ext/prism/extension.h +19 -0
- data/include/prism/arena.h +37 -0
- data/include/prism/ast.h +10371 -0
- data/include/prism/buffer.h +52 -0
- data/include/prism/comments.h +55 -0
- data/include/prism/compiler/accel.h +24 -0
- data/include/prism/compiler/align.h +36 -0
- data/include/prism/compiler/assume.h +25 -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 +93 -0
- 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 +91 -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/internal/encoding.h +242 -0
- 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/internal/list.h +62 -0
- 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 +984 -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/internal/static_literals.h +100 -0
- data/include/prism/internal/stringy.h +35 -0
- data/include/prism/internal/strncasecmp.h +18 -0
- data/include/prism/internal/strpbrk.h +33 -0
- data/include/prism/internal/tokens.h +11 -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 +94 -0
- data/include/prism/options.h +354 -0
- data/include/prism/parser.h +404 -0
- data/include/prism/prettyprint.h +31 -0
- 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 +63 -0
- data/include/prism/stringy.h +72 -0
- data/include/prism/version.h +38 -0
- data/include/prism.h +131 -0
- data/lib/prism/3.3/prism.bundle +0 -0
- data/lib/prism/3.4/prism.bundle +0 -0
- data/lib/prism/4.0/prism.bundle +0 -0
- data/lib/prism/compiler.rb +815 -0
- data/lib/prism/desugar_compiler.rb +463 -0
- data/lib/prism/dispatcher.rb +2383 -0
- data/lib/prism/dot_visitor.rb +4848 -0
- data/lib/prism/dsl.rb +1369 -0
- data/lib/prism/ffi.rb +740 -0
- data/lib/prism/inspect_visitor.rb +2425 -0
- data/lib/prism/lex_compat.rb +953 -0
- data/lib/prism/mutation_compiler.rb +780 -0
- data/lib/prism/node.rb +33204 -0
- data/lib/prism/node_ext.rb +504 -0
- data/lib/prism/node_find.rb +215 -0
- data/lib/prism/parse_result/errors.rb +72 -0
- data/lib/prism/parse_result/newlines.rb +423 -0
- data/lib/prism/parse_result.rb +1221 -0
- data/lib/prism/pattern.rb +314 -0
- data/lib/prism/polyfill/append_as_bytes.rb +15 -0
- data/lib/prism/polyfill/byteindex.rb +13 -0
- data/lib/prism/polyfill/scan_byte.rb +14 -0
- data/lib/prism/polyfill/unpack1.rb +14 -0
- data/lib/prism/polyfill/warn.rb +36 -0
- data/lib/prism/reflection.rb +427 -0
- data/lib/prism/relocation.rb +567 -0
- data/lib/prism/serialize.rb +5308 -0
- data/lib/prism/string_query.rb +46 -0
- data/lib/prism/translation/parser/builder.rb +70 -0
- data/lib/prism/translation/parser/compiler.rb +2226 -0
- data/lib/prism/translation/parser/lexer.rb +769 -0
- data/lib/prism/translation/parser.rb +376 -0
- data/lib/prism/translation/parser_current.rb +26 -0
- data/lib/prism/translation/parser_versions.rb +36 -0
- data/lib/prism/translation/ripper/filter.rb +53 -0
- data/lib/prism/translation/ripper/lexer.rb +133 -0
- data/lib/prism/translation/ripper/sexp.rb +118 -0
- data/lib/prism/translation/ripper/shim.rb +7 -0
- data/lib/prism/translation/ripper.rb +4278 -0
- data/lib/prism/translation/ruby_parser.rb +1721 -0
- data/lib/prism/translation.rb +20 -0
- data/lib/prism/visitor.rb +1440 -0
- data/lib/prism.rb +168 -0
- data/prism.gemspec +234 -0
- 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 +13681 -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 +764 -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 +181 -0
- data/rbi/generated/prism/string_query.rbi +36 -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.rbi +11 -0
- data/rbi/prism/translation/parser_versions.rbi +28 -0
- data/rbi/prism/translation/ripper.rbi +15 -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/generated/prism/mutation_compiler.rbs +464 -0
- data/sig/generated/prism/node.rbs +20160 -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 +859 -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 +195 -0
- data/sig/generated/prism/string_query.rbs +36 -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/buffer.c +380 -0
- data/src/char.c +289 -0
- data/src/comments.c +454 -0
- data/src/constant_pool.c +406 -0
- data/src/diagnostic.c +886 -0
- data/src/encoding.c +5345 -0
- data/src/errors_format.c +519 -0
- data/src/integer.c +681 -0
- data/src/json.c +5733 -0
- data/src/line_offset_list.c +100 -0
- data/src/list.c +24 -0
- data/src/memchr.c +37 -0
- data/src/node.c +4515 -0
- data/src/options.c +451 -0
- data/src/parser.c +349 -0
- data/src/prettyprint.c +9024 -0
- data/src/prism.c +23739 -0
- data/src/regexp.c +1706 -0
- data/src/serialize.c +2309 -0
- data/src/source.c +518 -0
- data/src/static_literals.c +685 -0
- data/src/string_query.c +166 -0
- data/src/stringy.c +91 -0
- data/src/strncasecmp.c +37 -0
- data/src/strpbrk.c +447 -0
- data/src/tokens.c +724 -0
- metadata +259 -0
data/docs/ruby_api.md
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# Ruby API
|
|
2
|
+
|
|
3
|
+
The `prism` gem provides a Ruby API for accessing the syntax tree.
|
|
4
|
+
|
|
5
|
+
For the most part, the API for accessing the tree mirrors that found in the [Syntax Tree](https://github.com/ruby-syntax-tree/syntax_tree) project. This means:
|
|
6
|
+
|
|
7
|
+
* Walking the tree involves creating a visitor and passing it to the `#accept` method on any node in the tree
|
|
8
|
+
* Nodes in the tree respond to named methods for accessing their children as well as `#child_nodes`
|
|
9
|
+
* Nodes respond to the pattern matching interfaces `#deconstruct` and `#deconstruct_keys`
|
|
10
|
+
|
|
11
|
+
Every entry in `config.yml` will generate a Ruby class as well as the code that builds the nodes themselves.
|
|
12
|
+
Creating a syntax tree involves calling one of the class methods on the `Prism` module.
|
|
13
|
+
The full API is documented below.
|
|
14
|
+
|
|
15
|
+
## API
|
|
16
|
+
|
|
17
|
+
* `Prism.dump(source)` - parse the syntax tree corresponding to the given source string, and serialize it to a string
|
|
18
|
+
* `Prism.dump_file(filepath)` - parse the syntax tree corresponding to the given source file and serialize it to a string
|
|
19
|
+
* `Prism.lex(source)` - parse the tokens corresponding to the given source string and return them as an array within a parse result
|
|
20
|
+
* `Prism.lex_file(filepath)` - parse the tokens corresponding to the given source file and return them as an array within a parse result
|
|
21
|
+
* `Prism.parse(source)` - parse the syntax tree corresponding to the given source string and return it within a parse result
|
|
22
|
+
* `Prism.parse_file(filepath)` - parse the syntax tree corresponding to the given source file and return it within a parse result
|
|
23
|
+
* `Prism.parse_stream(io)` - parse the syntax tree corresponding to the source that is read out of the given IO object using the `#gets` method and return it within a parse result
|
|
24
|
+
* `Prism.parse_lex(source)` - parse the syntax tree corresponding to the given source string and return it within a parse result, along with the tokens
|
|
25
|
+
* `Prism.parse_lex_file(filepath)` - parse the syntax tree corresponding to the given source file and return it within a parse result, along with the tokens
|
|
26
|
+
* `Prism.load(source, serialized, freeze = false)` - load the serialized syntax tree using the source as a reference into a syntax tree
|
|
27
|
+
* `Prism.parse_comments(source)` - parse the comments corresponding to the given source string and return them
|
|
28
|
+
* `Prism.parse_file_comments(source)` - parse the comments corresponding to the given source file and return them
|
|
29
|
+
* `Prism.parse_success?(source)` - parse the syntax tree corresponding to the given source string and return true if it was parsed without errors
|
|
30
|
+
* `Prism.parse_file_success?(filepath)` - parse the syntax tree corresponding to the given source file and return true if it was parsed without errors
|
|
31
|
+
|
|
32
|
+
## Nodes
|
|
33
|
+
|
|
34
|
+
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:
|
|
35
|
+
|
|
36
|
+
* `#accept(visitor)` - a method that will immediately call `visit_*` to specialize for the node type
|
|
37
|
+
* `#child_nodes` - a positional array of the child nodes of the node, with `nil` values for any missing children
|
|
38
|
+
* `#compact_child_nodes` - a positional array of the child nodes of the node with no `nil` values
|
|
39
|
+
* `#each_child_node` - with a block given yields all child nodes, without a block return an enumerator containing all child nodes
|
|
40
|
+
* `#copy(**keys)` - a method that allows creating a shallow copy of the node with the given keys overridden
|
|
41
|
+
* `#deconstruct`/`#deconstruct_keys(keys)` - the pattern matching interface for nodes
|
|
42
|
+
* `#inspect` - a string representation that looks like the syntax tree of the node
|
|
43
|
+
* `#location` - a `Location` object that describes the location of the node in the source file
|
|
44
|
+
* `#to_dot` - convert the node's syntax tree into graphviz dot notation
|
|
45
|
+
* `#type` - a symbol that represents the type of the node, useful for quick comparisons
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# ruby_parser translation
|
|
2
|
+
|
|
3
|
+
Prism ships with the ability to translate its syntax tree into the syntax tree used by the [seattlerb/ruby_parser](https://github.com/seattlerb/ruby_parser) gem. This allows you to use tools built on top of the `ruby_parser` gem with the `prism` parser.
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
You can call the `parse` and `parse_file` methods on the `Prism::Translation::RubyParser` module:
|
|
8
|
+
|
|
9
|
+
```ruby
|
|
10
|
+
filepath = "path/to/file.rb"
|
|
11
|
+
Prism::Translation::RubyParser.parse_file(filepath)
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
This will return to you `Sexp` objects that mirror the result of calling `RubyParser` methods, as in:
|
|
15
|
+
|
|
16
|
+
```ruby
|
|
17
|
+
filepath = "path/to/file.rb"
|
|
18
|
+
RubyParser.new.parse(File.read(filepath), filepath)
|
|
19
|
+
```
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
# Serialization
|
|
2
|
+
|
|
3
|
+
Prism ships with the ability to serialize a syntax tree to a single string.
|
|
4
|
+
The string can then be deserialized back into a syntax tree using a language other than C.
|
|
5
|
+
This is useful for using the parsing logic in other tools without having to write a parser in that language.
|
|
6
|
+
The syntax tree still requires a copy of the original source, as for the most part it just contains byte offsets into the source string.
|
|
7
|
+
|
|
8
|
+
## Types
|
|
9
|
+
|
|
10
|
+
Let us define some simple types for readability.
|
|
11
|
+
|
|
12
|
+
### varuint
|
|
13
|
+
|
|
14
|
+
A variable-length unsigned integer with the value fitting in `uint32_t` using between 1 and 5 bytes, using the [LEB128](https://en.wikipedia.org/wiki/LEB128) encoding.
|
|
15
|
+
This drastically cuts down on the size of the serialized string, especially when the source file is large.
|
|
16
|
+
|
|
17
|
+
### varsint
|
|
18
|
+
|
|
19
|
+
A variable-length signed integer with the value fitting in `int32_t` using between 1 and 5 bytes, using [ZigZag encoding](https://protobuf.dev/programming-guides/encoding/#signed-ints) into [LEB128].
|
|
20
|
+
|
|
21
|
+
### string
|
|
22
|
+
|
|
23
|
+
| # bytes | field |
|
|
24
|
+
| --- | --- |
|
|
25
|
+
| varuint | the length of the string in bytes |
|
|
26
|
+
| ... | the string bytes |
|
|
27
|
+
|
|
28
|
+
### location
|
|
29
|
+
|
|
30
|
+
| # bytes | field |
|
|
31
|
+
| --- | --- |
|
|
32
|
+
| varuint | byte offset into the source string where this location begins |
|
|
33
|
+
| varuint | length of the location in bytes in the source string |
|
|
34
|
+
|
|
35
|
+
### comment
|
|
36
|
+
|
|
37
|
+
The comment type is one of:
|
|
38
|
+
|
|
39
|
+
* 0=`INLINE` (`# comment`)
|
|
40
|
+
* 1=`EMBEDDED_DOCUMENT` (`=begin`/`=end`)
|
|
41
|
+
|
|
42
|
+
| # bytes | field |
|
|
43
|
+
| --- | --- |
|
|
44
|
+
| `1` | comment type |
|
|
45
|
+
| location | the location in the source of this comment |
|
|
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
|
+
|
|
58
|
+
### magic comment
|
|
59
|
+
|
|
60
|
+
| # bytes | field |
|
|
61
|
+
| --- | --- |
|
|
62
|
+
| location | the location of the key of the magic comment |
|
|
63
|
+
| location | the location of the value of the magic comment |
|
|
64
|
+
|
|
65
|
+
### error
|
|
66
|
+
|
|
67
|
+
| # bytes | field |
|
|
68
|
+
| --- | --- |
|
|
69
|
+
| varuint | type |
|
|
70
|
+
| string | error message (ASCII-only characters) |
|
|
71
|
+
| location | the location in the source this error applies to |
|
|
72
|
+
| `1` | the level of the error: `0` for `fatal`, `1` for `argument`, `2` for `load` |
|
|
73
|
+
|
|
74
|
+
### warning
|
|
75
|
+
|
|
76
|
+
| # bytes | field |
|
|
77
|
+
| --- | --- |
|
|
78
|
+
| varuint | type |
|
|
79
|
+
| string | warning message (ASCII-only characters) |
|
|
80
|
+
| location | the location in the source this warning applies to |
|
|
81
|
+
| `1` | the level of the warning: `0` for `default` and `1` for `verbose` |
|
|
82
|
+
|
|
83
|
+
### integer
|
|
84
|
+
|
|
85
|
+
| # bytes | field |
|
|
86
|
+
| --- | --- |
|
|
87
|
+
| `1` | `1` if the integer is negative, `0` if the integer is positive |
|
|
88
|
+
| varuint | the number of words in this integer |
|
|
89
|
+
| varuint+ | the words of the integer, least-significant to most-significant |
|
|
90
|
+
|
|
91
|
+
## Structure
|
|
92
|
+
|
|
93
|
+
The serialized string representing the syntax tree is composed of three parts: the header, the body, and the constant pool.
|
|
94
|
+
The header contains information like the version of prism that serialized the tree.
|
|
95
|
+
The body contains the actual nodes in the tree.
|
|
96
|
+
The constant pool contains constants that were interned while parsing.
|
|
97
|
+
|
|
98
|
+
The header is structured like the following table:
|
|
99
|
+
|
|
100
|
+
| # bytes | field |
|
|
101
|
+
| --- | --- |
|
|
102
|
+
| `5` | "PRISM" |
|
|
103
|
+
| `1` | major version number |
|
|
104
|
+
| `1` | minor version number |
|
|
105
|
+
| `1` | patch version number |
|
|
106
|
+
| `1` | 1 indicates only semantics fields were serialized, 0 indicates all fields were serialized (including location fields) |
|
|
107
|
+
| string | the encoding name |
|
|
108
|
+
| varsint | the start line |
|
|
109
|
+
| varuint | number of newline offsets |
|
|
110
|
+
| varuint* | newline offsets |
|
|
111
|
+
| varuint | number of comments |
|
|
112
|
+
| comment* | comments |
|
|
113
|
+
| varuint | number of nodes with attached comments |
|
|
114
|
+
| comment attachment* | comment attachments |
|
|
115
|
+
| varuint | number of magic comments |
|
|
116
|
+
| magic comment* | magic comments |
|
|
117
|
+
| location? | the optional location of the `__END__` keyword and its contents |
|
|
118
|
+
| varuint | number of errors |
|
|
119
|
+
| error* | errors |
|
|
120
|
+
| varuint | number of warnings |
|
|
121
|
+
| warning* | warnings |
|
|
122
|
+
| `1` | `1` if the source is continuable (incomplete but could become valid with more input), `0` otherwise |
|
|
123
|
+
| `4` | content pool offset |
|
|
124
|
+
| varuint | content pool size |
|
|
125
|
+
|
|
126
|
+
After the header comes the body of the serialized string.
|
|
127
|
+
The body consists of a sequence of nodes that is built using a prefix traversal order of the syntax tree.
|
|
128
|
+
Each node is structured like the following table:
|
|
129
|
+
|
|
130
|
+
| # bytes | field |
|
|
131
|
+
| --- | --- |
|
|
132
|
+
| `1` | node type |
|
|
133
|
+
| varuint | node identifier |
|
|
134
|
+
| location | node location |
|
|
135
|
+
| varuint | node flags |
|
|
136
|
+
|
|
137
|
+
Every field on the node is then appended to the serialized string. The fields can be determined by referencing `config.yml`. Depending on the type of field, it could take a couple of different forms, described below:
|
|
138
|
+
|
|
139
|
+
* `double` - A field that is a `double`. This is structured as a sequence of 8 bytes in native endian order.
|
|
140
|
+
* `node` - A field that is a node. This is structured just as like parent node.
|
|
141
|
+
* `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.
|
|
142
|
+
* `node[]` - A field that is an array of nodes. This is structured as a variable-length integer length, followed by the child nodes themselves.
|
|
143
|
+
* `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).
|
|
144
|
+
* `constant` - A variable-length integer that represents an index in the constant pool.
|
|
145
|
+
* `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.
|
|
146
|
+
* `integer` - A field that represents an arbitrary-sized integer. The structure is listed above.
|
|
147
|
+
* `location` - A field that is a location. This is structured as a variable-length integer start followed by a variable-length integer length.
|
|
148
|
+
* `location?` - A field that is a location that is optionally present. If the location is not present, then a single `0` byte will be written in its place. If it is present, then it will be structured just like the `location` child node.
|
|
149
|
+
* `uint8` - A field that is an 8-bit unsigned integer. This is structured as a single byte.
|
|
150
|
+
* `uint32` - A field that is a 32-bit unsigned integer. This is structured as a variable-length integer.
|
|
151
|
+
|
|
152
|
+
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:
|
|
153
|
+
|
|
154
|
+
| # bytes | field |
|
|
155
|
+
| --- | --- |
|
|
156
|
+
| `4` | the byte offset in the serialization for the contents of the constant |
|
|
157
|
+
| `4` | the byte length in the serialization |
|
|
158
|
+
|
|
159
|
+
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.
|
|
160
|
+
|
|
161
|
+
## APIs
|
|
162
|
+
|
|
163
|
+
The relevant APIs and struct definitions are listed below:
|
|
164
|
+
|
|
165
|
+
```c
|
|
166
|
+
// A pm_buffer_t is a simple memory buffer that stores data in a contiguous
|
|
167
|
+
// block of memory. It is used to store the serialized representation of a
|
|
168
|
+
// prism tree.
|
|
169
|
+
|
|
170
|
+
// Parse and serialize the AST represented by the given source to the given
|
|
171
|
+
// buffer.
|
|
172
|
+
void pm_serialize_parse(pm_buffer_t *buffer, const uint8_t *source, size_t length, const char *data);
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
Typically you would allocate a `pm_buffer_t` and call `pm_serialize_parse`, as in:
|
|
176
|
+
|
|
177
|
+
```c
|
|
178
|
+
void
|
|
179
|
+
serialize(const uint8_t *source, size_t length) {
|
|
180
|
+
pm_buffer_t *buffer = pm_buffer_new();
|
|
181
|
+
pm_serialize_parse(buffer, source, length, NULL);
|
|
182
|
+
|
|
183
|
+
// Do something with the serialized string.
|
|
184
|
+
|
|
185
|
+
pm_buffer_free(buffer);
|
|
186
|
+
}
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
The final argument to `pm_serialize_parse` is an optional string that controls the options to the parse function. This includes all of the normal options that could be passed to `pm_parser_init` through a `pm_options_t` struct, but serialized as a string to make it easier for callers through FFI. Note that no `varuint` are used here to make it easier to produce the data for the caller, and also serialized size is less important here. The format of the data is structured as follows:
|
|
190
|
+
|
|
191
|
+
| # bytes | field |
|
|
192
|
+
| ------- | -------------------------- |
|
|
193
|
+
| `4` | the length of the filepath |
|
|
194
|
+
| ... | the filepath bytes |
|
|
195
|
+
| `4` | the line number |
|
|
196
|
+
| `4` | the length the encoding |
|
|
197
|
+
| ... | the encoding bytes |
|
|
198
|
+
| `1` | frozen string literal |
|
|
199
|
+
| `1` | command line flags |
|
|
200
|
+
| `1` | syntax version, see [pm_options_version_t](https://github.com/ruby/prism/blob/main/include/prism/options.h) for valid values |
|
|
201
|
+
| `1` | whether or not the encoding is locked (should almost always be false) |
|
|
202
|
+
| `4` | the number of scopes |
|
|
203
|
+
| ... | the scopes |
|
|
204
|
+
|
|
205
|
+
Command line flags are a bitset. By default every flag is `0`. It includes the following values:
|
|
206
|
+
|
|
207
|
+
* `0x1` - the `-a` option
|
|
208
|
+
* `0x2` - the `-e` option
|
|
209
|
+
* `0x4` - the `-l` option
|
|
210
|
+
* `0x8` - the `-n` option
|
|
211
|
+
* `0x10` - the `-p` option
|
|
212
|
+
* `0x20` - the `-x` option
|
|
213
|
+
|
|
214
|
+
Scopes are ordered from the outermost scope to the innermost one.
|
|
215
|
+
|
|
216
|
+
Each scope is laid out as follows:
|
|
217
|
+
|
|
218
|
+
| # bytes | field |
|
|
219
|
+
| ------- | -------------------------- |
|
|
220
|
+
| `4` | the number of locals |
|
|
221
|
+
| ... | the locals |
|
|
222
|
+
|
|
223
|
+
Each local is laid out as follows:
|
|
224
|
+
|
|
225
|
+
| # bytes | field |
|
|
226
|
+
| ------- | -------------------------- |
|
|
227
|
+
| `4` | the length of the local |
|
|
228
|
+
| ... | the local bytes |
|
|
229
|
+
|
|
230
|
+
The data can be `NULL` (as seen in the example above).
|
data/docs/testing.md
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# Testing
|
|
2
|
+
|
|
3
|
+
This document explains how to test prism, both locally, and against existing test suites.
|
|
4
|
+
|
|
5
|
+
## Test suite
|
|
6
|
+
|
|
7
|
+
`rake test` will run all of the files in the `test/` directory. This can be conceived of as two parts: unit tests, and snapshot tests.
|
|
8
|
+
|
|
9
|
+
### Unit tests
|
|
10
|
+
|
|
11
|
+
These test specific prism implementation details like comments, errors, and regular expressions. There are corresponding files for each thing being tested (like `test/errors_test.rb`).
|
|
12
|
+
|
|
13
|
+
### Snapshot tests
|
|
14
|
+
|
|
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
|
+
|
|
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
|
+
|
|
19
|
+
When working on a specific example, you can limit tests to a single fixture via `FOCUS=strings.txt`.
|
|
20
|
+
|
|
21
|
+
#### Versioning
|
|
22
|
+
|
|
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.
|
|
27
|
+
|
|
28
|
+
Should you add logic specific to a new ruby version, validate the new behavior but also that old behavior remains the same.
|
|
29
|
+
|
|
30
|
+
### Error tests
|
|
31
|
+
|
|
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`.
|
|
35
|
+
|
|
36
|
+
## Local testing
|
|
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
|
|
66
|
+
```
|
|
67
|
+
|
|
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`.
|