prism 2.0.0.rc1-x86_64-linux-gnu

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (216) hide show
  1. checksums.yaml +7 -0
  2. data/BSDmakefile +58 -0
  3. data/CHANGELOG.md +870 -0
  4. data/CODE_OF_CONDUCT.md +76 -0
  5. data/CONTRIBUTING.md +58 -0
  6. data/LICENSE.md +7 -0
  7. data/Makefile +145 -0
  8. data/README.md +81 -0
  9. data/config.yml +4752 -0
  10. data/docs/build_system.md +119 -0
  11. data/docs/configuration.md +69 -0
  12. data/docs/cruby_compilation.md +27 -0
  13. data/docs/design.md +53 -0
  14. data/docs/encoding.md +121 -0
  15. data/docs/fuzzing.md +81 -0
  16. data/docs/heredocs.md +36 -0
  17. data/docs/javascript.md +133 -0
  18. data/docs/local_variable_depth.md +229 -0
  19. data/docs/mapping.md +117 -0
  20. data/docs/parser_translation.md +24 -0
  21. data/docs/parsing_rules.md +22 -0
  22. data/docs/releasing.md +147 -0
  23. data/docs/relocation.md +32 -0
  24. data/docs/ripper_translation.md +63 -0
  25. data/docs/ruby_api.md +45 -0
  26. data/docs/ruby_parser_translation.md +19 -0
  27. data/docs/serialization.md +230 -0
  28. data/docs/testing.md +72 -0
  29. data/ext/prism/api_node.c +7005 -0
  30. data/ext/prism/extconf.rb +126 -0
  31. data/ext/prism/extension.c +1783 -0
  32. data/ext/prism/extension.h +19 -0
  33. data/include/prism/arena.h +37 -0
  34. data/include/prism/ast.h +10371 -0
  35. data/include/prism/buffer.h +52 -0
  36. data/include/prism/comments.h +55 -0
  37. data/include/prism/compiler/accel.h +24 -0
  38. data/include/prism/compiler/align.h +36 -0
  39. data/include/prism/compiler/assume.h +25 -0
  40. data/include/prism/compiler/exported.h +24 -0
  41. data/include/prism/compiler/fallthrough.h +22 -0
  42. data/include/prism/compiler/filesystem.h +32 -0
  43. data/include/prism/compiler/flex_array.h +19 -0
  44. data/include/prism/compiler/force_inline.h +21 -0
  45. data/include/prism/compiler/format.h +25 -0
  46. data/include/prism/compiler/inline.h +17 -0
  47. data/include/prism/compiler/nodiscard.h +22 -0
  48. data/include/prism/compiler/nonnull.h +18 -0
  49. data/include/prism/compiler/unused.h +18 -0
  50. data/include/prism/constant_pool.h +81 -0
  51. data/include/prism/diagnostic.h +93 -0
  52. data/include/prism/errors_format.h +53 -0
  53. data/include/prism/excludes.h +29 -0
  54. data/include/prism/integer.h +41 -0
  55. data/include/prism/internal/allocator.h +68 -0
  56. data/include/prism/internal/allocator_debug.h +88 -0
  57. data/include/prism/internal/arena.h +108 -0
  58. data/include/prism/internal/bit.h +42 -0
  59. data/include/prism/internal/buffer.h +91 -0
  60. data/include/prism/internal/char.h +145 -0
  61. data/include/prism/internal/comments.h +53 -0
  62. data/include/prism/internal/constant_pool.h +152 -0
  63. data/include/prism/internal/diagnostic.h +389 -0
  64. data/include/prism/internal/encoding.h +242 -0
  65. data/include/prism/internal/integer.h +68 -0
  66. data/include/prism/internal/isinf.h +16 -0
  67. data/include/prism/internal/line_offset_list.h +34 -0
  68. data/include/prism/internal/list.h +62 -0
  69. data/include/prism/internal/magic_comments.h +23 -0
  70. data/include/prism/internal/memchr.h +15 -0
  71. data/include/prism/internal/node.h +38 -0
  72. data/include/prism/internal/options.h +232 -0
  73. data/include/prism/internal/parser.h +984 -0
  74. data/include/prism/internal/regexp.h +26 -0
  75. data/include/prism/internal/serialize.h +39 -0
  76. data/include/prism/internal/source.h +72 -0
  77. data/include/prism/internal/static_literals.h +100 -0
  78. data/include/prism/internal/stringy.h +35 -0
  79. data/include/prism/internal/strncasecmp.h +18 -0
  80. data/include/prism/internal/strpbrk.h +33 -0
  81. data/include/prism/internal/tokens.h +11 -0
  82. data/include/prism/json.h +32 -0
  83. data/include/prism/line_offset_list.h +61 -0
  84. data/include/prism/magic_comments.h +35 -0
  85. data/include/prism/node.h +94 -0
  86. data/include/prism/options.h +354 -0
  87. data/include/prism/parser.h +404 -0
  88. data/include/prism/prettyprint.h +31 -0
  89. data/include/prism/serialize.h +115 -0
  90. data/include/prism/source.h +155 -0
  91. data/include/prism/stream.h +28 -0
  92. data/include/prism/string_query.h +63 -0
  93. data/include/prism/stringy.h +72 -0
  94. data/include/prism/version.h +38 -0
  95. data/include/prism.h +131 -0
  96. data/lib/prism/3.3/prism.so +0 -0
  97. data/lib/prism/3.4/prism.so +0 -0
  98. data/lib/prism/4.0/prism.so +0 -0
  99. data/lib/prism/compiler.rb +815 -0
  100. data/lib/prism/desugar_compiler.rb +463 -0
  101. data/lib/prism/dispatcher.rb +2383 -0
  102. data/lib/prism/dot_visitor.rb +4848 -0
  103. data/lib/prism/dsl.rb +1369 -0
  104. data/lib/prism/ffi.rb +740 -0
  105. data/lib/prism/inspect_visitor.rb +2425 -0
  106. data/lib/prism/lex_compat.rb +953 -0
  107. data/lib/prism/mutation_compiler.rb +780 -0
  108. data/lib/prism/node.rb +33204 -0
  109. data/lib/prism/node_ext.rb +504 -0
  110. data/lib/prism/node_find.rb +215 -0
  111. data/lib/prism/parse_result/errors.rb +72 -0
  112. data/lib/prism/parse_result/newlines.rb +423 -0
  113. data/lib/prism/parse_result.rb +1221 -0
  114. data/lib/prism/pattern.rb +314 -0
  115. data/lib/prism/polyfill/append_as_bytes.rb +15 -0
  116. data/lib/prism/polyfill/byteindex.rb +13 -0
  117. data/lib/prism/polyfill/scan_byte.rb +14 -0
  118. data/lib/prism/polyfill/unpack1.rb +14 -0
  119. data/lib/prism/polyfill/warn.rb +36 -0
  120. data/lib/prism/reflection.rb +427 -0
  121. data/lib/prism/relocation.rb +567 -0
  122. data/lib/prism/serialize.rb +5308 -0
  123. data/lib/prism/string_query.rb +46 -0
  124. data/lib/prism/translation/parser/builder.rb +70 -0
  125. data/lib/prism/translation/parser/compiler.rb +2226 -0
  126. data/lib/prism/translation/parser/lexer.rb +769 -0
  127. data/lib/prism/translation/parser.rb +376 -0
  128. data/lib/prism/translation/parser_current.rb +26 -0
  129. data/lib/prism/translation/parser_versions.rb +36 -0
  130. data/lib/prism/translation/ripper/filter.rb +53 -0
  131. data/lib/prism/translation/ripper/lexer.rb +133 -0
  132. data/lib/prism/translation/ripper/sexp.rb +118 -0
  133. data/lib/prism/translation/ripper/shim.rb +7 -0
  134. data/lib/prism/translation/ripper.rb +4278 -0
  135. data/lib/prism/translation/ruby_parser.rb +1721 -0
  136. data/lib/prism/translation.rb +20 -0
  137. data/lib/prism/visitor.rb +1440 -0
  138. data/lib/prism.rb +168 -0
  139. data/prism.gemspec +234 -0
  140. data/rbi/generated/prism/compiler.rbi +489 -0
  141. data/rbi/generated/prism/desugar_compiler.rbi +306 -0
  142. data/rbi/generated/prism/dispatcher.rbi +1138 -0
  143. data/rbi/generated/prism/dot_visitor.rbi +618 -0
  144. data/rbi/generated/prism/dsl.rbi +751 -0
  145. data/rbi/generated/prism/inspect_visitor.rbi +503 -0
  146. data/rbi/generated/prism/lex_compat.rbi +162 -0
  147. data/rbi/generated/prism/mutation_compiler.rbi +464 -0
  148. data/rbi/generated/prism/node.rbi +13681 -0
  149. data/rbi/generated/prism/node_ext.rbi +259 -0
  150. data/rbi/generated/prism/node_find.rbi +79 -0
  151. data/rbi/generated/prism/parse_result/errors.rbi +21 -0
  152. data/rbi/generated/prism/parse_result/newlines.rbi +208 -0
  153. data/rbi/generated/prism/parse_result.rbi +764 -0
  154. data/rbi/generated/prism/pattern.rbi +134 -0
  155. data/rbi/generated/prism/reflection.rbi +89 -0
  156. data/rbi/generated/prism/relocation.rbi +329 -0
  157. data/rbi/generated/prism/serialize.rbi +181 -0
  158. data/rbi/generated/prism/string_query.rbi +36 -0
  159. data/rbi/generated/prism/translation.rbi +7 -0
  160. data/rbi/generated/prism/visitor.rbi +650 -0
  161. data/rbi/generated/prism.rbi +86 -0
  162. data/rbi/prism/translation/parser.rbi +11 -0
  163. data/rbi/prism/translation/parser_versions.rbi +28 -0
  164. data/rbi/prism/translation/ripper.rbi +15 -0
  165. data/rbi/rubyvm/node_find.rbi +14 -0
  166. data/sig/generated/prism/compiler.rbs +492 -0
  167. data/sig/generated/prism/desugar_compiler.rbs +305 -0
  168. data/sig/generated/prism/dispatcher.rbs +1292 -0
  169. data/sig/generated/prism/dot_visitor.rbs +635 -0
  170. data/sig/generated/prism/dsl.rbs +928 -0
  171. data/sig/generated/prism/inspect_visitor.rbs +509 -0
  172. data/sig/generated/prism/lex_compat.rbs +166 -0
  173. data/sig/generated/prism/mutation_compiler.rbs +464 -0
  174. data/sig/generated/prism/node.rbs +20160 -0
  175. data/sig/generated/prism/node_ext.rbs +290 -0
  176. data/sig/generated/prism/node_find.rbs +90 -0
  177. data/sig/generated/prism/parse_result/errors.rbs +22 -0
  178. data/sig/generated/prism/parse_result/newlines.rbs +223 -0
  179. data/sig/generated/prism/parse_result.rbs +859 -0
  180. data/sig/generated/prism/pattern.rbs +156 -0
  181. data/sig/generated/prism/reflection.rbs +101 -0
  182. data/sig/generated/prism/relocation.rbs +428 -0
  183. data/sig/generated/prism/serialize.rbs +195 -0
  184. data/sig/generated/prism/string_query.rbs +36 -0
  185. data/sig/generated/prism/translation.rbs +8 -0
  186. data/sig/generated/prism/visitor.rbs +1111 -0
  187. data/sig/generated/prism.rbs +84 -0
  188. data/sig/manifest.yaml +2 -0
  189. data/src/arena.c +117 -0
  190. data/src/buffer.c +380 -0
  191. data/src/char.c +289 -0
  192. data/src/comments.c +454 -0
  193. data/src/constant_pool.c +406 -0
  194. data/src/diagnostic.c +886 -0
  195. data/src/encoding.c +5345 -0
  196. data/src/errors_format.c +519 -0
  197. data/src/integer.c +681 -0
  198. data/src/json.c +5733 -0
  199. data/src/line_offset_list.c +100 -0
  200. data/src/list.c +24 -0
  201. data/src/memchr.c +37 -0
  202. data/src/node.c +4515 -0
  203. data/src/options.c +451 -0
  204. data/src/parser.c +349 -0
  205. data/src/prettyprint.c +9024 -0
  206. data/src/prism.c +23739 -0
  207. data/src/regexp.c +1706 -0
  208. data/src/serialize.c +2309 -0
  209. data/src/source.c +518 -0
  210. data/src/static_literals.c +685 -0
  211. data/src/string_query.c +166 -0
  212. data/src/stringy.c +91 -0
  213. data/src/strncasecmp.c +37 -0
  214. data/src/strpbrk.c +447 -0
  215. data/src/tokens.c +724 -0
  216. metadata +259 -0
@@ -0,0 +1,229 @@
1
+ # Local variable depth
2
+
3
+ One feature of Prism is that it resolves local variables as it parses. It's necessary to do this because of ambiguities in the grammar. For example, consider the following code:
4
+
5
+ ```ruby
6
+ foo / bar#/
7
+ ```
8
+
9
+ If `foo` is a local variable, this is a call to `/` with `bar` as an argument, followed by a comment. If it's not a local variable, this is a method call to `foo` with a regular expression argument.
10
+
11
+ "Depth" refers to the number of visible scopes that Prism has to go up to find the declaration of a local variable.
12
+ Note that this follows the same scoping rules as Ruby, so a local variable is only visible in the scope it is declared in and in blocks nested in that scope.
13
+ The rules for calculating the depth are very important to understand because they may differ from individual Ruby implementations since they are not specified by the language.
14
+
15
+ Prism uses the minimum number of scopes, i.e., it only creates scopes when necessary semantically, in other words when there must be distinct scopes (which can be observed through `binding.local_variables`).
16
+ That are no "transparent/invisible" scopes in Prism.
17
+ Some Ruby implementations use those for some language constructs and need to adjust by maintaining a depth offset.
18
+
19
+ Below are the places where a local variable can be written/targeted, along with how the depth is calculated at that point.
20
+
21
+ ## General
22
+
23
+ In the course of general Ruby code when reading a local variable, the depth is equal to the number of scopes to go up to find the declaration of that variable. For example:
24
+
25
+ ```ruby
26
+ foo = 1
27
+ bar = 2
28
+ baz = 3
29
+
30
+ foo # depth 0
31
+ tap { bar } # depth 1
32
+ tap { tap { baz } } # depth 2
33
+ ```
34
+
35
+ This also includes writing to a local variable, which could be writing to a local variable that is already declared. For example:
36
+
37
+ ```ruby
38
+ foo = 1
39
+ bar = 2
40
+
41
+ foo = 3 # depth 0
42
+ tap { bar = 4 } # depth 1
43
+ ```
44
+
45
+ This includes multiple assignment, where the same principle applies. For example:
46
+
47
+ ```ruby
48
+ foo = 1
49
+ bar = 2
50
+
51
+ foo, bar = 3, 4 # depth 0
52
+ tap { foo, bar = 5, 6 } # depth 1
53
+ ```
54
+
55
+ ## `for` loops
56
+
57
+ `for` loops in Ruby break down to calls to `.each` with a block.
58
+ However in that case local variable reads and writes within the block will be in the same scope as the scope surrounding the `for` and not in a deeper/separate scope (surprising, but this is Ruby semantics).
59
+ For example:
60
+
61
+ ```ruby
62
+ foo = 1
63
+
64
+ for e in baz
65
+ foo # depth 0
66
+ bar = 2 # depth 0
67
+ end
68
+
69
+ p bar # depth 0, prints 2
70
+ ```
71
+
72
+ The local variable(s) used for the index of the `for` are also at the same depth (as variables inside and outside the `for`):
73
+
74
+ ```ruby
75
+ for e in [1, 2] # depth 0
76
+ e # depth 0
77
+ end
78
+
79
+ p e # depth 0, prints 2
80
+ ```
81
+
82
+ ## Pattern matching captures
83
+
84
+ You can target a local variable in a pattern matching expression using capture syntax. Using this syntax, you can target local variables in the current scope or in visible parent scopes. For example:
85
+
86
+ ```ruby
87
+ 42 => bar # depth 0
88
+ ```
89
+
90
+ The example above writes to a local variable in the current scope. If the variable is already declared in a higher visible scope, it will be written to that scope instead. For example:
91
+
92
+ ```ruby
93
+ foo = 1
94
+ tap { 42 => foo } # depth 1
95
+ ```
96
+
97
+ ## Named capture groups
98
+
99
+ You can target local variables through named capture groups in regular expressions if they are used on the left-hand side of a `=~` operator. For example:
100
+
101
+ ```ruby
102
+ /(?<foo>\d+)/ =~ "42" # depth 0
103
+ ```
104
+
105
+ This will write to a `foo` local variable. If the variable is already declared in a higher visible scope, it will be written to that scope instead. For example:
106
+
107
+ ```ruby
108
+ foo = 1
109
+ tap { /(?<foo>\d+)/ =~ "42" } # depth 1
110
+ ```
111
+
112
+ ## "interpolated once" regular expressions
113
+
114
+ Regular expressions that interpolate local variables (unrelated to capture group local variables) and have the `o` flag will only interpolate the local variables once for the runtime of the program.
115
+ In CRuby, this is implemented by compiling the regular expression within a nested instruction sequence, which means CRuby thinks the depth is one more than prism does. For example:
116
+
117
+ ```
118
+ $ ruby --dump=insns -e 'foo = 1; /#{foo}/o'
119
+ == disasm: #<ISeq:<main>@-e:1 (1,0)-(1,18)> (catch: false)
120
+ local table (size: 1, argc: 0 [opts: 0, rest: -1, post: 0, block: -1, kw: -1@-1, kwrest: -1])
121
+ [ 1] foo@0
122
+ 0000 putobject_INT2FIX_1_ ( 1)[Li]
123
+ 0001 setlocal_WC_0 foo@0
124
+ 0003 once block in <main>, <is:0>
125
+ 0006 leave
126
+
127
+ == disasm: #<ISeq:block in <main>@-e:1 (1,9)-(1,18)> (catch: false)
128
+ 0000 putobject "" ( 1)
129
+ 0002 getlocal_WC_1 foo@0
130
+ 0004 dup
131
+ 0005 objtostring <calldata!mid:to_s, argc:0, FCALL|ARGS_SIMPLE>
132
+ 0007 anytostring
133
+ 0008 toregexp 0, 2
134
+ 0011 leave
135
+ ```
136
+
137
+ In this case CRuby fetches the local variable with `getlocal_WC_1` as the second instruction to the "once" instruction sequence. When compiling CRuby, prism therefore will adjust the depth to account for this difference.
138
+
139
+ ## `rescue` clauses
140
+
141
+ In CRuby, `rescue` clauses are implemented as their own instruction sequence, and therefore CRuby thinks the depth is one more than prism does. For example:
142
+
143
+ ```
144
+ $ ruby --dump=insns -e 'begin; foo = 1; rescue; foo; end'
145
+ == disasm: #<ISeq:<main>@-e:1 (1,0)-(1,32)> (catch: true)
146
+ == catch table
147
+ | catch type: rescue st: 0000 ed: 0004 sp: 0000 cont: 0005
148
+ | == disasm: #<ISeq:rescue in <main>@-e:1 (1,16)-(1,28)> (catch: true)
149
+ | local table (size: 1, argc: 0 [opts: 0, rest: -1, post: 0, block: -1, kw: -1@-1, kwrest: -1])
150
+ | [ 1] $!@0
151
+ | 0000 getlocal_WC_0 $!@0 ( 1)
152
+ | 0002 putobject StandardError
153
+ | 0004 checkmatch 3
154
+ | 0006 branchunless 11
155
+ | 0008 getlocal_WC_1 foo@0[Li]
156
+ | 0010 leave
157
+ | 0011 getlocal_WC_0 $!@0
158
+ | 0013 throw 0
159
+ | catch type: retry st: 0004 ed: 0005 sp: 0000 cont: 0000
160
+ |------------------------------------------------------------------------
161
+ local table (size: 1, argc: 0 [opts: 0, rest: -1, post: 0, block: -1, kw: -1@-1, kwrest: -1])
162
+ [ 1] foo@0
163
+ 0000 putobject_INT2FIX_1_ ( 1)[Li]
164
+ 0001 dup
165
+ 0002 setlocal_WC_0 foo@0
166
+ 0004 nop
167
+ 0005 leave
168
+ ```
169
+
170
+ In the catch table, CRuby is reading the `foo` local variable using `getlocal_WC_1` as the fifth instruction to the "rescue" instruction sequence. When compiling CRuby, prism therefore will adjust the depth to account for this difference.
171
+
172
+ Note that this includes the error reference, which can target local variables, as in:
173
+
174
+ ```
175
+ $ ruby --dump=insns -e 'foo = 1; begin; rescue => foo; end'
176
+ == disasm: #<ISeq:<main>@-e:1 (1,0)-(1,34)> (catch: true)
177
+ == catch table
178
+ | catch type: rescue st: 0003 ed: 0004 sp: 0000 cont: 0005
179
+ | == disasm: #<ISeq:rescue in <main>@-e:1 (1,16)-(1,30)> (catch: true)
180
+ | local table (size: 1, argc: 0 [opts: 0, rest: -1, post: 0, block: -1, kw: -1@-1, kwrest: -1])
181
+ | [ 1] $!@0
182
+ | 0000 getlocal_WC_0 $!@0 ( 1)
183
+ | 0002 putobject StandardError
184
+ | 0004 checkmatch 3
185
+ | 0006 branchunless 14
186
+ | 0008 getlocal_WC_0 $!@0
187
+ | 0010 setlocal_WC_1 foo@0
188
+ | 0012 putnil
189
+ | 0013 leave
190
+ | 0014 getlocal_WC_0 $!@0
191
+ | 0016 throw 0
192
+ | catch type: retry st: 0004 ed: 0005 sp: 0000 cont: 0003
193
+ |------------------------------------------------------------------------
194
+ local table (size: 1, argc: 0 [opts: 0, rest: -1, post: 0, block: -1, kw: -1@-1, kwrest: -1])
195
+ [ 1] foo@0
196
+ 0000 putobject_INT2FIX_1_ ( 1)[Li]
197
+ 0001 setlocal_WC_0 foo@0
198
+ 0003 putnil
199
+ 0004 nop
200
+ 0005 leave
201
+ ```
202
+
203
+ Note that CRuby is writing to the `foo` local variable using the `setlocal_WC_1` instruction as the sixth instruction to the "rescue" instruction sequence. When compiling CRuby, prism therefore will adjust the depth to account for this difference.
204
+
205
+ ## Post execution blocks
206
+
207
+ The `END {}` syntax allows executing code when the program exits. In CRuby, this is implemented as two nested instruction sequences. CRuby therefore thinks the depth is two more than prism does. For example:
208
+
209
+ ```
210
+ $ ruby --dump=insns -e 'foo = 1; END { foo }'
211
+ == disasm: #<ISeq:<main>@-e:1 (1,0)-(1,20)> (catch: false)
212
+ local table (size: 1, argc: 0 [opts: 0, rest: -1, post: 0, block: -1, kw: -1@-1, kwrest: -1])
213
+ [ 1] foo@0
214
+ 0000 putobject_INT2FIX_1_ ( 1)[Li]
215
+ 0001 setlocal_WC_0 foo@0
216
+ 0003 once block in <main>, <is:0>
217
+ 0006 leave
218
+
219
+ == disasm: #<ISeq:block in <main>@-e:0 (0,0)-(-1,-1)> (catch: false)
220
+ 0000 putspecialobject 1 ( 1)
221
+ 0002 send <calldata!mid:core#set_postexe, argc:0, FCALL>, block in <main>
222
+ 0005 leave
223
+
224
+ == disasm: #<ISeq:block in <main>@-e:1 (1,9)-(1,20)> (catch: false)
225
+ 0000 getlocal foo@0, 2 ( 1)[LiBc]
226
+ 0003 leave [Br]
227
+ ```
228
+
229
+ In the instruction sequence corresponding to the code that gets executed inside the `END` block, CRuby is reading the `foo` local variable using `getlocal` as the second instruction to the `"block in <main>"` instruction sequence. When compiling CRuby, prism therefore will adjust the depth to account for this difference.
data/docs/mapping.md ADDED
@@ -0,0 +1,117 @@
1
+ # Mapping
2
+
3
+ When considering the previous CRuby parser versus prism, this document should be helpful to understand how various concepts are mapped.
4
+
5
+ ## Nodes
6
+
7
+ The following table shows how the various CRuby nodes are mapped to prism nodes.
8
+
9
+ | CRuby | prism |
10
+ | --- | --- |
11
+ | `NODE_SCOPE` | |
12
+ | `NODE_BLOCK` | |
13
+ | `NODE_IF` | `PM_IF_NODE` |
14
+ | `NODE_UNLESS` | `PM_UNLESS_NODE` |
15
+ | `NODE_CASE` | `PM_CASE_NODE` |
16
+ | `NODE_CASE2` | `PM_CASE_NODE` (with a null predicate) |
17
+ | `NODE_CASE3` | |
18
+ | `NODE_WHEN` | `PM_WHEN_NODE` |
19
+ | `NODE_IN` | `PM_IN_NODE` |
20
+ | `NODE_WHILE` | `PM_WHILE_NODE` |
21
+ | `NODE_UNTIL` | `PM_UNTIL_NODE` |
22
+ | `NODE_ITER` | `PM_CALL_NODE` (with a non-null block) |
23
+ | `NODE_FOR` | `PM_FOR_NODE` |
24
+ | `NODE_FOR_MASGN` | `PM_FOR_NODE` (with a multi-write node as the index) |
25
+ | `NODE_BREAK` | `PM_BREAK_NODE` |
26
+ | `NODE_NEXT` | `PM_NEXT_NODE` |
27
+ | `NODE_REDO` | `PM_REDO_NODE` |
28
+ | `NODE_RETRY` | `PM_RETRY_NODE` |
29
+ | `NODE_BEGIN` | `PM_BEGIN_NODE` |
30
+ | `NODE_RESCUE` | `PM_RESCUE_NODE` |
31
+ | `NODE_RESBODY` | |
32
+ | `NODE_ENSURE` | `PM_ENSURE_NODE` |
33
+ | `NODE_AND` | `PM_AND_NODE` |
34
+ | `NODE_OR` | `PM_OR_NODE` |
35
+ | `NODE_MASGN` | `PM_MULTI_WRITE_NODE` |
36
+ | `NODE_LASGN` | `PM_LOCAL_VARIABLE_WRITE_NODE` |
37
+ | `NODE_DASGN` | `PM_LOCAL_VARIABLE_WRITE_NODE` |
38
+ | `NODE_GASGN` | `PM_GLOBAL_VARIABLE_WRITE_NODE` |
39
+ | `NODE_IASGN` | `PM_INSTANCE_VARIABLE_WRITE_NODE` |
40
+ | `NODE_CDECL` | `PM_CONSTANT_PATH_WRITE_NODE` |
41
+ | `NODE_CVASGN` | `PM_CLASS_VARIABLE_WRITE_NODE` |
42
+ | `NODE_OP_ASGN1` | |
43
+ | `NODE_OP_ASGN2` | |
44
+ | `NODE_OP_ASGN_AND` | `PM_OPERATOR_AND_ASSIGNMENT_NODE` |
45
+ | `NODE_OP_ASGN_OR` | `PM_OPERATOR_OR_ASSIGNMENT_NODE` |
46
+ | `NODE_OP_CDECL` | |
47
+ | `NODE_CALL` | `PM_CALL_NODE` |
48
+ | `NODE_OPCALL` | `PM_CALL_NODE` (with an operator as the method) |
49
+ | `NODE_FCALL` | `PM_CALL_NODE` (with a null receiver and parentheses) |
50
+ | `NODE_VCALL` | `PM_CALL_NODE` (with a null receiver and parentheses or arguments) |
51
+ | `NODE_QCALL` | `PM_CALL_NODE` (with a &. operator) |
52
+ | `NODE_SUPER` | `PM_SUPER_NODE` |
53
+ | `NODE_ZSUPER` | `PM_FORWARDING_SUPER_NODE` |
54
+ | `NODE_LIST` | `PM_ARRAY_NODE` |
55
+ | `NODE_ZLIST` | `PM_ARRAY_NODE` (with no child elements) |
56
+ | `NODE_VALUES` | `PM_ARGUMENTS_NODE` |
57
+ | `NODE_HASH` | `PM_HASH_NODE` |
58
+ | `NODE_RETURN` | `PM_RETURN_NODE` |
59
+ | `NODE_YIELD` | `PM_YIELD_NODE` |
60
+ | `NODE_LVAR` | `PM_LOCAL_VARIABLE_READ_NODE` |
61
+ | `NODE_DVAR` | `PM_LOCAL_VARIABLE_READ_NODE` |
62
+ | `NODE_GVAR` | `PM_GLOBAL_VARIABLE_READ_NODE` |
63
+ | `NODE_IVAR` | `PM_INSTANCE_VARIABLE_READ_NODE` |
64
+ | `NODE_CONST` | `PM_CONSTANT_PATH_READ_NODE` |
65
+ | `NODE_CVAR` | `PM_CLASS_VARIABLE_READ_NODE` |
66
+ | `NODE_NTH_REF` | `PM_NUMBERED_REFERENCE_READ_NODE` |
67
+ | `NODE_BACK_REF` | `PM_BACK_REFERENCE_READ_NODE` |
68
+ | `NODE_MATCH` | |
69
+ | `NODE_MATCH2` | `PM_CALL_NODE` (with regular expression as receiver) |
70
+ | `NODE_MATCH3` | `PM_CALL_NODE` (with regular expression as only argument) |
71
+ | `NODE_LIT` | |
72
+ | `NODE_STR` | `PM_STRING_NODE` |
73
+ | `NODE_DSTR` | `PM_INTERPOLATED_STRING_NODE` |
74
+ | `NODE_XSTR` | `PM_X_STRING_NODE` |
75
+ | `NODE_DXSTR` | `PM_INTERPOLATED_X_STRING_NODE` |
76
+ | `NODE_EVSTR` | `PM_STRING_INTERPOLATED_NODE` |
77
+ | `NODE_DREGX` | `PM_INTERPOLATED_REGULAR_EXPRESSION_NODE` |
78
+ | `NODE_ONCE` | |
79
+ | `NODE_ARGS` | `PM_PARAMETERS_NODE` |
80
+ | `NODE_ARGS_AUX` | |
81
+ | `NODE_OPT_ARG` | `PM_OPTIONAL_PARAMETER_NODE` |
82
+ | `NODE_KW_ARG` | `PM_KEYWORD_PARAMETER_NODE` |
83
+ | `NODE_POSTARG` | `PM_REQUIRED_PARAMETER_NODE` |
84
+ | `NODE_ARGSCAT` | |
85
+ | `NODE_ARGSPUSH` | |
86
+ | `NODE_SPLAT` | `PM_SPLAT_NODE` |
87
+ | `NODE_BLOCK_PASS` | `PM_BLOCK_ARGUMENT_NODE` |
88
+ | `NODE_DEFN` | `PM_DEF_NODE` (with a null receiver) |
89
+ | `NODE_DEFS` | `PM_DEF_NODE` (with a non-null receiver) |
90
+ | `NODE_ALIAS` | `PM_ALIAS_NODE` |
91
+ | `NODE_VALIAS` | `PM_ALIAS_NODE` (with a global variable first argument) |
92
+ | `NODE_UNDEF` | `PM_UNDEF_NODE` |
93
+ | `NODE_CLASS` | `PM_CLASS_NODE` |
94
+ | `NODE_MODULE` | `PM_MODULE_NODE` |
95
+ | `NODE_SCLASS` | `PM_S_CLASS_NODE` |
96
+ | `NODE_COLON2` | `PM_CONSTANT_PATH_NODE` |
97
+ | `NODE_COLON3` | `PM_CONSTANT_PATH_NODE` (with a null receiver) |
98
+ | `NODE_DOT2` | `PM_RANGE_NODE` (with a .. operator) |
99
+ | `NODE_DOT3` | `PM_RANGE_NODE` (with a ... operator) |
100
+ | `NODE_FLIP2` | `PM_RANGE_NODE` (with a .. operator) |
101
+ | `NODE_FLIP3` | `PM_RANGE_NODE` (with a ... operator) |
102
+ | `NODE_SELF` | `PM_SELF_NODE` |
103
+ | `NODE_NIL` | `PM_NIL_NODE` |
104
+ | `NODE_TRUE` | `PM_TRUE_NODE` |
105
+ | `NODE_FALSE` | `PM_FALSE_NODE` |
106
+ | `NODE_ERRINFO` | |
107
+ | `NODE_DEFINED` | `PM_DEFINED_NODE` |
108
+ | `NODE_POSTEXE` | `PM_POST_EXECUTION_NODE` |
109
+ | `NODE_DSYM` | `PM_INTERPOLATED_SYMBOL_NODE` |
110
+ | `NODE_ATTRASGN` | `PM_CALL_NODE` (with a message that ends with =) |
111
+ | `NODE_LAMBDA` | `PM_LAMBDA_NODE` |
112
+ | `NODE_ARYPTN` | `PM_ARRAY_PATTERN_NODE` |
113
+ | `NODE_HSHPTN` | `PM_HASH_PATTERN_NODE` |
114
+ | `NODE_FNDPTN` | `PM_FIND_PATTERN_NODE` |
115
+ | `NODE_ERROR` | `PM_ERROR_RECOVERY_NODE` |
116
+ | `NODE_LAST` | |
117
+ ```
@@ -0,0 +1,24 @@
1
+ # parser translation
2
+
3
+ Prism ships with the ability to translate its syntax tree into the syntax tree used by the [whitequark/parser](https://github.com/whitequark/parser) gem. This allows you to use tools built on top of the `parser` gem with the `prism` parser.
4
+
5
+ ## Usage
6
+
7
+ The `parser` gem provides multiple parsers to support different versions of the Ruby grammar. This includes all of the Ruby versions going back to 1.8, as well as third-party parsers like MacRuby and RubyMotion. The `prism` gem provides another parser that uses the `prism` parser to build the syntax tree.
8
+
9
+ You can use the `prism` parser like you would any other. After requiring `prism`, you should be able to call any of the regular `Parser::Base` APIs that you would normally use.
10
+
11
+ ```ruby
12
+ require "prism"
13
+
14
+ # Same as `Parser::Ruby34`
15
+ Prism::Translation::Parser34.parse_file("path/to/file.rb")
16
+
17
+ # Same as `Parser::CurrentRuby`
18
+ Prism::Translation::ParserCurrent.parse("puts 'Hello World!'")
19
+ ```
20
+
21
+ All the parsers are autoloaded, so you don't have to worry about requiring them yourself.
22
+
23
+ If you also need to parse Ruby versions below 3.3 (for which the `prism` translation layer does not have explicit support), check out
24
+ [this guide](https://github.com/whitequark/parser/blob/master/doc/PRISM_TRANSLATION.md) from the `parser` gem on how to use both in conjunction.
@@ -0,0 +1,22 @@
1
+ # Rules
2
+
3
+ This document contains information related to the rules of the parser for Ruby source code.
4
+
5
+ As an example, in the documentation of many of the fields of nodes, it's mentioned that a field follows the lexing rules for `identifier` or `constant`. This document describes what those rules are.
6
+
7
+ ## Constants
8
+
9
+ Constants in Ruby begin with an upper-case letter. This is followed by any number of underscores, alphanumeric, or non-ASCII characters. The definition of "alphanumeric" and "upper-case letter" are encoding-dependent.
10
+
11
+ ## Non-void expression
12
+
13
+ Most expressions in CRuby are non-void. This means the expression they represent resolves to a value. For example, `1 + 2` is a non-void expression, because it resolves to a method call. Even things like `class Foo; end` is a non-void expression, because it returns the last evaluated expression in the body of the class (or `nil`).
14
+
15
+ Certain nodes, however, are void expressions, and cannot be combined to form larger expressions.
16
+ * `BEGIN {}`, `END {}`, `alias foo bar`, and `undef foo` can only be at a statement position.
17
+ * The "jumps": `return`, `break`, `next`, `redo`, `retry` are void expressions.
18
+ * `value => pattern` is also considered a void expression.
19
+
20
+ ## Identifiers
21
+
22
+ Identifiers in Ruby begin with an underscore or lower-case letter. This is followed by any number of underscores, alphanumeric, or non-ASCII characters. The definition of "alphanumeric" and "lower-case letter" are encoding-dependent.
data/docs/releasing.md ADDED
@@ -0,0 +1,147 @@
1
+ # Releasing
2
+
3
+ To release a new version of Prism, perform the following steps:
4
+
5
+ ## Preparation
6
+
7
+ * Update the `CHANGELOG.md` file.
8
+ * Add a new section for the new version at the top of the file.
9
+ * Fill in the relevant changes — it may be easiest to click the link for the `Unreleased` heading to find the commits.
10
+ * Update the links at the bottom of the file.
11
+ * Update the version numbers in the various files that reference them:
12
+
13
+ ```sh
14
+ export PRISM_MAJOR="x"
15
+ export PRISM_MINOR="y"
16
+ export PRISM_PATCH="z"
17
+ export PRISM_VERSION="$PRISM_MAJOR.$PRISM_MINOR.$PRISM_PATCH"
18
+ ruby -pi -e 'gsub(/spec\.version = ".+?"/, %Q{spec.version = "#{ENV["PRISM_VERSION"]}"})' prism.gemspec
19
+ ruby -pi -e 'gsub(/EXPECTED_PRISM_VERSION ".+?"/, %Q{EXPECTED_PRISM_VERSION "#{ENV["PRISM_VERSION"]}"})' ext/prism/extension.h
20
+ ruby -pi -e 'gsub(/PRISM_VERSION_MAJOR \d+/, %Q{PRISM_VERSION_MAJOR #{ENV["PRISM_MAJOR"]}})' include/prism/version.h
21
+ ruby -pi -e 'gsub(/PRISM_VERSION_MINOR \d+/, %Q{PRISM_VERSION_MINOR #{ENV["PRISM_MINOR"]}})' include/prism/version.h
22
+ ruby -pi -e 'gsub(/PRISM_VERSION_PATCH \d+/, %Q{PRISM_VERSION_PATCH #{ENV["PRISM_PATCH"]}})' include/prism/version.h
23
+ ruby -pi -e 'gsub(/PRISM_VERSION ".+?"/, %Q{PRISM_VERSION "#{ENV["PRISM_VERSION"]}"})' include/prism/version.h
24
+ ruby -pi -e 'gsub(/"version": ".+?"/, %Q{"version": "#{ENV["PRISM_VERSION"]}"})' javascript/package.json
25
+ ruby -pi -e 'gsub(/lossy\(\), ".+?"/, %Q{lossy(), "#{ENV["PRISM_VERSION"]}"})' rust/ruby-prism-sys/tests/utils_tests.rs
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
30
+ ruby -pi -e 'gsub(/MAJOR_VERSION = \d+/, %Q{MAJOR_VERSION = #{ENV["PRISM_MAJOR"]}})' templates/javascript/src/deserialize.js.erb
31
+ ruby -pi -e 'gsub(/MINOR_VERSION = \d+/, %Q{MINOR_VERSION = #{ENV["PRISM_MINOR"]}})' templates/javascript/src/deserialize.js.erb
32
+ ruby -pi -e 'gsub(/PATCH_VERSION = \d+/, %Q{PATCH_VERSION = #{ENV["PRISM_PATCH"]}})' templates/javascript/src/deserialize.js.erb
33
+ ruby -pi -e 'gsub(/MAJOR_VERSION = \d+/, %Q{MAJOR_VERSION = #{ENV["PRISM_MAJOR"]}})' templates/lib/prism/serialize.rb.erb
34
+ ruby -pi -e 'gsub(/MINOR_VERSION = \d+/, %Q{MINOR_VERSION = #{ENV["PRISM_MINOR"]}})' templates/lib/prism/serialize.rb.erb
35
+ ruby -pi -e 'gsub(/PATCH_VERSION = \d+/, %Q{PATCH_VERSION = #{ENV["PRISM_PATCH"]}})' templates/lib/prism/serialize.rb.erb
36
+ ruby -pi -e 'gsub(/^version = ".+?"/, %Q{version = "#{ENV["PRISM_VERSION"]}"})' rust/ruby-prism-sys/Cargo.toml
37
+ ruby -pi -e 'gsub(/^version = ".+?"/, %Q{version = "#{ENV["PRISM_VERSION"]}"})' rust/ruby-prism/Cargo.toml
38
+ ruby -pi -e 'gsub(/^ruby-prism-sys = \{ version = ".+?"/, %Q{ruby-prism-sys = \{ version = "#{ENV["PRISM_VERSION"]}"})' rust/ruby-prism/Cargo.toml
39
+ ```
40
+
41
+ * Update the `Gemfile.lock` file:
42
+
43
+ ```sh
44
+ chruby ruby-4.0.0-dev
45
+ bundle install
46
+ ```
47
+
48
+ * Update the version-specific lockfiles:
49
+
50
+ ```sh
51
+ for VERSION in "2.7" "3.0" "3.1" "3.2" "3.3" "3.4" "4.0"; do docker run -it --rm -v "$PWD":/usr/src/app -w /usr/src/app -e BUNDLE_GEMFILE="gemfiles/$VERSION/Gemfile" "ruby:$VERSION" bundle update; done
52
+ chruby ruby-4.1.0-dev && BUNDLE_GEMFILE=gemfiles/4.1/Gemfile bundle install
53
+ ```
54
+
55
+ * Update the cargo lockfiles:
56
+
57
+ ```sh
58
+ bundle exec rake cargo:build
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
+
63
+ * Commit all of the updated files:
64
+
65
+ ```sh
66
+ git commit -am "Bump to v$PRISM_VERSION"
67
+ ```
68
+
69
+ * Push up the changes:
70
+
71
+ ```sh
72
+ git push
73
+ ```
74
+
75
+ ## Publishing
76
+
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.
@@ -0,0 +1,32 @@
1
+ # Relocation
2
+
3
+ Prism parses deterministically for the same input. This provides a nice property that is exposed through the `#node_id` API on nodes. Effectively this means that for the same input, these values will remain consistent every time the source is parsed. This means we can reparse the source same with a `#node_id` value and find the exact same node again.
4
+
5
+ The `Relocation` module provides an API around this property. It allows you to "save" nodes and locations using a minimal amount of memory (just the node_id and a field identifier) and then reify them later. This minimizes the amount of memory you need to allocate to store this information because it does not keep around a pointer to the source string.
6
+
7
+ ## Getting started
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, and the start and end code unit columns for in UTF-16, you would:
10
+
11
+ ```ruby
12
+ repository = Prism::Relocation.filepath("path/to/file").lines.code_unit_columns(Encoding::UTF_16)
13
+ ```
14
+
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.
16
+
17
+ ```ruby
18
+ # assume that node is a Prism::ClassNode object
19
+ entry = node.constant_path.save(repository)
20
+ ```
21
+
22
+ Now that you have the entry object, you do not need to keep around a reference to the repository, it will be cleaned up on its own when the last entry is reified. Now, whenever you need to, you may call the associated field methods on the entry object, as in:
23
+
24
+ ```ruby
25
+ entry.start_line
26
+ entry.end_line
27
+
28
+ entry.start_code_units_column
29
+ entry.end_code_units_column
30
+ ```
31
+
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.
@@ -0,0 +1,63 @@
1
+ # Ripper translation
2
+
3
+ Prism provides the ability to mirror the `Ripper` standard library. It is available under `Prism::Translation::Ripper`. You can use the entire public API, and also some undocumented features that are commonly used.
4
+
5
+ Briefly, `Ripper` is a streaming parser that allows you to construct your own syntax tree. As an example:
6
+
7
+ ```ruby
8
+ class ArithmeticRipper < Prism::Translation::Ripper
9
+ def on_binary(left, operator, right)
10
+ left.public_send(operator, right)
11
+ end
12
+
13
+ def on_int(value)
14
+ value.to_i
15
+ end
16
+
17
+ def on_program(stmts)
18
+ stmts
19
+ end
20
+
21
+ def on_stmts_new
22
+ []
23
+ end
24
+
25
+ def on_stmts_add(stmts, stmt)
26
+ stmts << stmt
27
+ stmts
28
+ end
29
+ end
30
+
31
+ ArithmeticRipper.new("1 + 2 - 3").parse # => [0]
32
+ ```
33
+
34
+ The exact names of the `on_*` methods are listed in the `Ripper` source.
35
+
36
+ You can can also automatically use the ripper translation in places that don't explicitly use the translation layer by doing the following:
37
+
38
+ ```ruby
39
+ # Will redirect access of the `Ripper` constant to `Prism::Translation::Ripper`.
40
+ require "prism/translation/ripper/shim"
41
+ ```
42
+
43
+ ## Background
44
+
45
+ It is helpful to understand the differences between the `Ripper` library and the `Prism` library. Both libraries perform parsing and provide you with APIs to manipulate and understand the resulting syntax tree. However, there are a few key differences.
46
+
47
+ ### Design
48
+
49
+ `Ripper` is a streaming parser. This means as it is parsing Ruby code, it dispatches events back to the consumer. This allows quite a bit of flexibility. You can use it to build your own syntax tree or to find specific patterns in the code. `Prism` on the other hand returns to you the completed syntax tree _before_ it allows you to manipulate it. This means the tree that you get back is the only representation that can be generated by the parser _at parse time_ (but of course can be manipulated later).
50
+
51
+ ### Fields
52
+
53
+ We use the term "field" to mean a piece of information on a syntax tree node. `Ripper` provides the minimal number of fields to accurately represent the syntax tree for the purposes of compilation/interpretation. For example, in the callbacks for nodes that are based on keywords (`class`, `module`, `for`, `while`, etc.) you are not given the keyword itself, you need to attach it on your own. In other cases, tokens are not necessarily dispatched at all, meaning you need to find them yourself. `Prism` provides the opposite: the maximum number of fields on nodes is provided. As a tradeoff, this requires more memory, but this is chosen to make it easier on consumers.
54
+
55
+ ### Maintainability
56
+
57
+ The `Ripper` interface is not guaranteed in any way, and tends to change between patch versions of CRuby. This is largely due to the fact that `Ripper` is a by-product of the generated parser, as opposed to its own parser. As an example, in the expression `foo::bar = baz`, there are three different represents possible for the call operator, including:
58
+
59
+ * `:"::"` - Ruby 1.9 to Ruby 3.1.4
60
+ * `73` - Ruby 3.1.5 to Ruby 3.1.6
61
+ * `[:@op, "::", [lineno, column]]` - Ruby 3.2.0 and later
62
+
63
+ The `Prism` interface is guaranteed going forward to be the consistent, and the official Ruby syntax tree interface. This means you can rely on this interface without having to worry about individual changes between Ruby versions. It also is a gem, which means it is versioned based on the gem version, as opposed to being versioned based on the Ruby version. Finally, you can use `Prism` to parse multiple versions of Ruby, whereas `Ripper` is tied to the Ruby version it is running on.