prism 1.8.1 → 2.0.0.rc1
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 +90 -2
- data/Makefile +41 -12
- data/README.md +35 -97
- data/config.yml +174 -163
- 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/ripper_translation.md +8 -17
- data/docs/serialization.md +21 -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 +778 -422
- data/ext/prism/extension.h +3 -3
- data/include/prism/arena.h +37 -0
- data/include/prism/ast.h +2729 -612
- 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 +38 -405
- 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/{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 +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/{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/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 +336 -868
- 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 +63 -0
- data/include/prism/stringy.h +72 -0
- data/include/prism/version.h +13 -4
- 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 +252 -90
- data/lib/prism/inspect_visitor.rb +357 -325
- data/lib/prism/lex_compat.rb +311 -228
- data/lib/prism/mutation_compiler.rb +313 -305
- data/lib/prism/node.rb +20284 -7038
- 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 +462 -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 +3442 -536
- data/lib/prism/string_query.rb +16 -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/filter.rb +53 -0
- data/lib/prism/translation/ripper/lexer.rb +92 -5
- data/lib/prism/translation/ripper/sexp.rb +2 -10
- data/lib/prism/translation/ripper/shim.rb +2 -0
- data/lib/prism/translation/ripper.rb +1005 -224
- 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 -22
- data/prism.gemspec +123 -61
- 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_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 +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/{util/pm_buffer.c → buffer.c} +44 -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 +93 -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 +349 -0
- data/src/prettyprint.c +421 -354
- data/src/prism.c +8035 -7019
- data/src/regexp.c +961 -45
- data/src/serialize.c +459 -424
- data/src/source.c +518 -0
- data/src/static_literals.c +111 -43
- data/src/string_query.c +166 -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
- metadata +123 -62
- 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/lex_ripper.rb +0 -64
- 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 -8747
- 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 -66
- 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 -4039
- 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 -196
- 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 -258
- 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/lib/prism/lex_compat.rb
CHANGED
|
@@ -1,28 +1,64 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
# :markup: markdown
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
#--
|
|
4
|
+
# rbs_inline: enabled
|
|
5
5
|
|
|
6
6
|
module Prism
|
|
7
|
+
# @rbs!
|
|
8
|
+
# module Translation
|
|
9
|
+
# class Ripper
|
|
10
|
+
# EXPR_NONE: Integer
|
|
11
|
+
# EXPR_BEG: Integer
|
|
12
|
+
# EXPR_MID: Integer
|
|
13
|
+
# EXPR_END: Integer
|
|
14
|
+
# EXPR_CLASS: Integer
|
|
15
|
+
# EXPR_VALUE: Integer
|
|
16
|
+
# EXPR_ARG: Integer
|
|
17
|
+
# EXPR_CMDARG: Integer
|
|
18
|
+
# EXPR_ENDARG: Integer
|
|
19
|
+
# EXPR_ENDFN: Integer
|
|
20
|
+
#
|
|
21
|
+
# class Lexer < Ripper
|
|
22
|
+
# class State
|
|
23
|
+
# def self.[]: (Integer value) -> State
|
|
24
|
+
# end
|
|
25
|
+
# end
|
|
26
|
+
#
|
|
27
|
+
# class LineAndColumnCache
|
|
28
|
+
# def initialize: (Source source) -> void
|
|
29
|
+
#
|
|
30
|
+
# def line_and_column: (Integer byte_offset) -> [Integer, Integer]
|
|
31
|
+
# end
|
|
32
|
+
# end
|
|
33
|
+
# end
|
|
34
|
+
|
|
7
35
|
# This class is responsible for lexing the source using prism and then
|
|
8
36
|
# converting those tokens to be compatible with Ripper. In the vast majority
|
|
9
37
|
# of cases, this is a one-to-one mapping of the token type. Everything else
|
|
10
38
|
# generally lines up. However, there are a few cases that require special
|
|
11
39
|
# handling.
|
|
12
40
|
class LexCompat # :nodoc:
|
|
41
|
+
# @rbs!
|
|
42
|
+
# # A token produced by the Ripper lexer that Prism is replicating.
|
|
43
|
+
# type lex_compat_token = [[Integer, Integer], Symbol, String, untyped]
|
|
44
|
+
|
|
13
45
|
# A result class specialized for holding tokens produced by the lexer.
|
|
14
46
|
class Result < Prism::Result
|
|
15
47
|
# The list of tokens that were produced by the lexer.
|
|
16
|
-
attr_reader :value
|
|
48
|
+
attr_reader :value #: Array[lex_compat_token]
|
|
17
49
|
|
|
18
50
|
# Create a new lex compat result object with the given values.
|
|
19
|
-
|
|
51
|
+
#
|
|
52
|
+
#: (Array[lex_compat_token] value, Array[MagicComment] magic_comments, Location? data_loc, Array[ParseError] errors, Array[ParseWarning] warnings, bool continuable, Source source) -> void
|
|
53
|
+
def initialize(value, magic_comments, data_loc, errors, warnings, continuable, source)
|
|
20
54
|
@value = value
|
|
21
|
-
super(
|
|
55
|
+
super(magic_comments, data_loc, errors, warnings, continuable, source)
|
|
22
56
|
end
|
|
23
57
|
|
|
24
58
|
# Implement the hash pattern matching interface for Result.
|
|
25
|
-
|
|
59
|
+
#
|
|
60
|
+
#: (Array[Symbol]? keys) -> Hash[Symbol, untyped]
|
|
61
|
+
def deconstruct_keys(keys) # :nodoc:
|
|
26
62
|
super.merge!(value: value)
|
|
27
63
|
end
|
|
28
64
|
end
|
|
@@ -42,6 +78,8 @@ module Prism
|
|
|
42
78
|
BANG_EQUAL: :on_op,
|
|
43
79
|
BANG_TILDE: :on_op,
|
|
44
80
|
BRACE_LEFT: :on_lbrace,
|
|
81
|
+
BRACE_LEFT_ARGUMENT: :on_lbrace,
|
|
82
|
+
BRACE_LEFT_HASH: :on_lbrace,
|
|
45
83
|
BRACE_RIGHT: :on_rbrace,
|
|
46
84
|
BRACKET_LEFT: :on_lbracket,
|
|
47
85
|
BRACKET_LEFT_ARRAY: :on_lbracket,
|
|
@@ -104,6 +142,8 @@ module Prism
|
|
|
104
142
|
KEYWORD_DEF: :on_kw,
|
|
105
143
|
KEYWORD_DEFINED: :on_kw,
|
|
106
144
|
KEYWORD_DO: :on_kw,
|
|
145
|
+
KEYWORD_DO_BLOCK: :on_kw,
|
|
146
|
+
KEYWORD_DO_LAMBDA: :on_kw,
|
|
107
147
|
KEYWORD_DO_LOOP: :on_kw,
|
|
108
148
|
KEYWORD_ELSE: :on_kw,
|
|
109
149
|
KEYWORD_ELSIF: :on_kw,
|
|
@@ -151,8 +191,10 @@ module Prism
|
|
|
151
191
|
MINUS_EQUAL: :on_op,
|
|
152
192
|
MINUS_GREATER: :on_tlambda,
|
|
153
193
|
NEWLINE: :on_nl,
|
|
194
|
+
NEWLINE_TERMINATOR: :on_ignored_nl,
|
|
154
195
|
NUMBERED_REFERENCE: :on_backref,
|
|
155
196
|
PARENTHESIS_LEFT: :on_lparen,
|
|
197
|
+
PARENTHESIS_LEFT_GROUPING: :on_lparen,
|
|
156
198
|
PARENTHESIS_LEFT_PARENTHESES: :on_lparen,
|
|
157
199
|
PARENTHESIS_RIGHT: :on_rparen,
|
|
158
200
|
PERCENT: :on_op,
|
|
@@ -195,96 +237,10 @@ module Prism
|
|
|
195
237
|
USTAR: :on_op,
|
|
196
238
|
USTAR_STAR: :on_op,
|
|
197
239
|
WORDS_SEP: :on_words_sep,
|
|
240
|
+
XSTRING_BEGIN: :on_backtick,
|
|
198
241
|
"__END__": :on___end__
|
|
199
242
|
}.freeze
|
|
200
243
|
|
|
201
|
-
# When we produce tokens, we produce the same arrays that Ripper does.
|
|
202
|
-
# However, we add a couple of convenience methods onto them to make them a
|
|
203
|
-
# little easier to work with. We delegate all other methods to the array.
|
|
204
|
-
class Token < SimpleDelegator
|
|
205
|
-
# @dynamic initialize, each, []
|
|
206
|
-
|
|
207
|
-
# The location of the token in the source.
|
|
208
|
-
def location
|
|
209
|
-
self[0]
|
|
210
|
-
end
|
|
211
|
-
|
|
212
|
-
# The type of the token.
|
|
213
|
-
def event
|
|
214
|
-
self[1]
|
|
215
|
-
end
|
|
216
|
-
|
|
217
|
-
# The slice of the source that this token represents.
|
|
218
|
-
def value
|
|
219
|
-
self[2]
|
|
220
|
-
end
|
|
221
|
-
|
|
222
|
-
# The state of the lexer when this token was produced.
|
|
223
|
-
def state
|
|
224
|
-
self[3]
|
|
225
|
-
end
|
|
226
|
-
end
|
|
227
|
-
|
|
228
|
-
# Ripper doesn't include the rest of the token in the event, so we need to
|
|
229
|
-
# trim it down to just the content on the first line when comparing.
|
|
230
|
-
class EndContentToken < Token
|
|
231
|
-
def ==(other) # :nodoc:
|
|
232
|
-
[self[0], self[1], self[2][0..self[2].index("\n")], self[3]] == other
|
|
233
|
-
end
|
|
234
|
-
end
|
|
235
|
-
|
|
236
|
-
# Tokens where state should be ignored
|
|
237
|
-
# used for :on_comment, :on_heredoc_end, :on_embexpr_end
|
|
238
|
-
class IgnoreStateToken < Token
|
|
239
|
-
def ==(other) # :nodoc:
|
|
240
|
-
self[0...-1] == other[0...-1]
|
|
241
|
-
end
|
|
242
|
-
end
|
|
243
|
-
|
|
244
|
-
# Ident tokens for the most part are exactly the same, except sometimes we
|
|
245
|
-
# know an ident is a local when ripper doesn't (when they are introduced
|
|
246
|
-
# through named captures in regular expressions). In that case we don't
|
|
247
|
-
# compare the state.
|
|
248
|
-
class IdentToken < Token
|
|
249
|
-
def ==(other) # :nodoc:
|
|
250
|
-
(self[0...-1] == other[0...-1]) && (
|
|
251
|
-
(other[3] == Translation::Ripper::EXPR_LABEL | Translation::Ripper::EXPR_END) ||
|
|
252
|
-
(other[3] & (Translation::Ripper::EXPR_ARG | Translation::Ripper::EXPR_CMDARG) != 0)
|
|
253
|
-
)
|
|
254
|
-
end
|
|
255
|
-
end
|
|
256
|
-
|
|
257
|
-
# Ignored newlines can occasionally have a LABEL state attached to them, so
|
|
258
|
-
# we compare the state differently here.
|
|
259
|
-
class IgnoredNewlineToken < Token
|
|
260
|
-
def ==(other) # :nodoc:
|
|
261
|
-
return false unless self[0...-1] == other[0...-1]
|
|
262
|
-
|
|
263
|
-
if self[3] == Translation::Ripper::EXPR_ARG | Translation::Ripper::EXPR_LABELED
|
|
264
|
-
other[3] & Translation::Ripper::EXPR_ARG | Translation::Ripper::EXPR_LABELED != 0
|
|
265
|
-
else
|
|
266
|
-
self[3] == other[3]
|
|
267
|
-
end
|
|
268
|
-
end
|
|
269
|
-
end
|
|
270
|
-
|
|
271
|
-
# If we have an identifier that follows a method name like:
|
|
272
|
-
#
|
|
273
|
-
# def foo bar
|
|
274
|
-
#
|
|
275
|
-
# then Ripper will mark bar as END|LABEL if there is a local in a parent
|
|
276
|
-
# scope named bar because it hasn't pushed the local table yet. We do this
|
|
277
|
-
# more accurately, so we need to allow comparing against both END and
|
|
278
|
-
# END|LABEL.
|
|
279
|
-
class ParamToken < Token
|
|
280
|
-
def ==(other) # :nodoc:
|
|
281
|
-
(self[0...-1] == other[0...-1]) && (
|
|
282
|
-
(other[3] == Translation::Ripper::EXPR_END) ||
|
|
283
|
-
(other[3] == Translation::Ripper::EXPR_END | Translation::Ripper::EXPR_LABEL)
|
|
284
|
-
)
|
|
285
|
-
end
|
|
286
|
-
end
|
|
287
|
-
|
|
288
244
|
# A heredoc in this case is a list of tokens that belong to the body of the
|
|
289
245
|
# heredoc that should be appended onto the list of tokens when the heredoc
|
|
290
246
|
# closes.
|
|
@@ -294,16 +250,19 @@ module Prism
|
|
|
294
250
|
# order back into the token stream and set the state of the last token to
|
|
295
251
|
# the state that the heredoc was opened in.
|
|
296
252
|
class PlainHeredoc # :nodoc:
|
|
297
|
-
attr_reader :tokens
|
|
253
|
+
attr_reader :tokens #: Array[lex_compat_token]
|
|
298
254
|
|
|
255
|
+
#: () -> void
|
|
299
256
|
def initialize
|
|
300
257
|
@tokens = []
|
|
301
258
|
end
|
|
302
259
|
|
|
260
|
+
#: (lex_compat_token token) -> void
|
|
303
261
|
def <<(token)
|
|
304
262
|
tokens << token
|
|
305
263
|
end
|
|
306
264
|
|
|
265
|
+
#: () -> Array[lex_compat_token]
|
|
307
266
|
def to_a
|
|
308
267
|
tokens
|
|
309
268
|
end
|
|
@@ -313,22 +272,26 @@ module Prism
|
|
|
313
272
|
# that need to be split on "\\\n" to mimic Ripper's behavior. We also need
|
|
314
273
|
# to keep track of the state that the heredoc was opened in.
|
|
315
274
|
class DashHeredoc # :nodoc:
|
|
316
|
-
attr_reader :split
|
|
275
|
+
attr_reader :split #: bool
|
|
276
|
+
attr_reader :tokens #: Array[lex_compat_token]
|
|
317
277
|
|
|
278
|
+
#: (bool split) -> void
|
|
318
279
|
def initialize(split)
|
|
319
280
|
@split = split
|
|
320
281
|
@tokens = []
|
|
321
282
|
end
|
|
322
283
|
|
|
284
|
+
#: (lex_compat_token token) -> void
|
|
323
285
|
def <<(token)
|
|
324
286
|
tokens << token
|
|
325
287
|
end
|
|
326
288
|
|
|
289
|
+
#: () -> Array[lex_compat_token]
|
|
327
290
|
def to_a
|
|
328
291
|
embexpr_balance = 0
|
|
329
292
|
|
|
330
|
-
tokens.each_with_object([]) do |token, results| #$ Array[
|
|
331
|
-
case token
|
|
293
|
+
tokens.each_with_object([]) do |token, results| #$ Array[lex_compat_token]
|
|
294
|
+
case token[1]
|
|
332
295
|
when :on_embexpr_beg
|
|
333
296
|
embexpr_balance += 1
|
|
334
297
|
results << token
|
|
@@ -343,9 +306,9 @@ module Prism
|
|
|
343
306
|
if split
|
|
344
307
|
# Split on "\\\n" to mimic Ripper's behavior. Use a lookbehind
|
|
345
308
|
# to keep the delimiter in the result.
|
|
346
|
-
token.
|
|
309
|
+
token[2].split(/(?<=[^\\]\\\n)|(?<=[^\\]\\\r\n)/).each_with_index do |value, index|
|
|
347
310
|
column = 0 if index > 0
|
|
348
|
-
results <<
|
|
311
|
+
results << [[lineno, column], :on_tstring_content, value, token[3]]
|
|
349
312
|
lineno += value.count("\n")
|
|
350
313
|
end
|
|
351
314
|
else
|
|
@@ -374,8 +337,13 @@ module Prism
|
|
|
374
337
|
class DedentingHeredoc # :nodoc:
|
|
375
338
|
TAB_WIDTH = 8
|
|
376
339
|
|
|
377
|
-
attr_reader :tokens
|
|
340
|
+
attr_reader :tokens #: Array[lex_compat_token]
|
|
341
|
+
attr_reader :dedent_next #: bool
|
|
342
|
+
attr_reader :dedent #: Integer?
|
|
343
|
+
attr_reader :embexpr_balance #: Integer
|
|
344
|
+
# @rbs @ended_on_newline: bool
|
|
378
345
|
|
|
346
|
+
#: () -> void
|
|
379
347
|
def initialize
|
|
380
348
|
@tokens = []
|
|
381
349
|
@dedent_next = true
|
|
@@ -387,8 +355,10 @@ module Prism
|
|
|
387
355
|
# As tokens are coming in, we track the minimum amount of common leading
|
|
388
356
|
# whitespace on plain string content tokens. This allows us to later
|
|
389
357
|
# remove that amount of whitespace from the beginning of each line.
|
|
358
|
+
#
|
|
359
|
+
#: (lex_compat_token token) -> void
|
|
390
360
|
def <<(token)
|
|
391
|
-
case token
|
|
361
|
+
case token[1]
|
|
392
362
|
when :on_embexpr_beg, :on_heredoc_beg
|
|
393
363
|
@embexpr_balance += 1
|
|
394
364
|
@dedent = 0 if @dedent_next && @ended_on_newline
|
|
@@ -396,10 +366,10 @@ module Prism
|
|
|
396
366
|
@embexpr_balance -= 1
|
|
397
367
|
when :on_tstring_content
|
|
398
368
|
if embexpr_balance == 0
|
|
399
|
-
line = token
|
|
369
|
+
line = token[2]
|
|
400
370
|
|
|
401
371
|
if dedent_next && !(line.strip.empty? && line.end_with?("\n"))
|
|
402
|
-
leading = line[/\A(\s*)\n?/, 1]
|
|
372
|
+
leading = line[/\A(\s*)\n?/, 1] #: String
|
|
403
373
|
next_dedent = 0
|
|
404
374
|
|
|
405
375
|
leading.each_char do |char|
|
|
@@ -419,20 +389,21 @@ module Prism
|
|
|
419
389
|
end
|
|
420
390
|
end
|
|
421
391
|
|
|
422
|
-
@dedent_next = token
|
|
392
|
+
@dedent_next = token[1] == :on_tstring_content && embexpr_balance == 0
|
|
423
393
|
@ended_on_newline = false
|
|
424
394
|
tokens << token
|
|
425
395
|
end
|
|
426
396
|
|
|
397
|
+
#: () -> Array[lex_compat_token]
|
|
427
398
|
def to_a
|
|
428
399
|
# If every line in the heredoc is blank, we still need to split up the
|
|
429
400
|
# string content token into multiple tokens.
|
|
430
401
|
if dedent.nil?
|
|
431
|
-
results = [] #: Array[
|
|
402
|
+
results = [] #: Array[lex_compat_token]
|
|
432
403
|
embexpr_balance = 0
|
|
433
404
|
|
|
434
405
|
tokens.each do |token|
|
|
435
|
-
case token
|
|
406
|
+
case token[1]
|
|
436
407
|
when :on_embexpr_beg, :on_heredoc_beg
|
|
437
408
|
embexpr_balance += 1
|
|
438
409
|
results << token
|
|
@@ -444,9 +415,9 @@ module Prism
|
|
|
444
415
|
lineno = token[0][0]
|
|
445
416
|
column = token[0][1]
|
|
446
417
|
|
|
447
|
-
token.
|
|
418
|
+
token[2].split(/(?<=\n)/).each_with_index do |value, index|
|
|
448
419
|
column = 0 if index > 0
|
|
449
|
-
results <<
|
|
420
|
+
results << [[lineno, column], :on_tstring_content, value, token[3]]
|
|
450
421
|
lineno += 1
|
|
451
422
|
end
|
|
452
423
|
else
|
|
@@ -463,7 +434,7 @@ module Prism
|
|
|
463
434
|
# If the minimum common whitespace is 0, then we need to concatenate
|
|
464
435
|
# string nodes together that are immediately adjacent.
|
|
465
436
|
if dedent == 0
|
|
466
|
-
results = [] #: Array[
|
|
437
|
+
results = [] #: Array[lex_compat_token]
|
|
467
438
|
embexpr_balance = 0
|
|
468
439
|
|
|
469
440
|
index = 0
|
|
@@ -474,15 +445,15 @@ module Prism
|
|
|
474
445
|
results << token
|
|
475
446
|
index += 1
|
|
476
447
|
|
|
477
|
-
case token
|
|
448
|
+
case token[1]
|
|
478
449
|
when :on_embexpr_beg, :on_heredoc_beg
|
|
479
450
|
embexpr_balance += 1
|
|
480
451
|
when :on_embexpr_end, :on_heredoc_end
|
|
481
452
|
embexpr_balance -= 1
|
|
482
453
|
when :on_tstring_content
|
|
483
454
|
if embexpr_balance == 0
|
|
484
|
-
while index < max_index && tokens[index]
|
|
485
|
-
token
|
|
455
|
+
while index < max_index && tokens[index][1] == :on_tstring_content && !token[2].match?(/\\\r?\n\z/)
|
|
456
|
+
token[2] << tokens[index][2]
|
|
486
457
|
index += 1
|
|
487
458
|
end
|
|
488
459
|
end
|
|
@@ -496,7 +467,7 @@ module Prism
|
|
|
496
467
|
# insert on_ignored_sp tokens for the amount of dedent that we need to
|
|
497
468
|
# perform. We also need to remove the dedent from the beginning of
|
|
498
469
|
# each line of plain string content tokens.
|
|
499
|
-
results = [] #: Array[
|
|
470
|
+
results = [] #: Array[lex_compat_token]
|
|
500
471
|
dedent_next = true
|
|
501
472
|
embexpr_balance = 0
|
|
502
473
|
|
|
@@ -505,7 +476,7 @@ module Prism
|
|
|
505
476
|
# whitespace calculation we performed above. This is because
|
|
506
477
|
# checking if the subsequent token needs to be dedented is common to
|
|
507
478
|
# both the dedent calculation and the ignored_sp insertion.
|
|
508
|
-
case token
|
|
479
|
+
case token[1]
|
|
509
480
|
when :on_embexpr_beg
|
|
510
481
|
embexpr_balance += 1
|
|
511
482
|
results << token
|
|
@@ -517,7 +488,7 @@ module Prism
|
|
|
517
488
|
# Here we're going to split the string on newlines, but maintain
|
|
518
489
|
# the newlines in the resulting array. We'll do that with a look
|
|
519
490
|
# behind assertion.
|
|
520
|
-
splits = token.
|
|
491
|
+
splits = token[2].split(/(?<=\n)/)
|
|
521
492
|
index = 0
|
|
522
493
|
|
|
523
494
|
while index < splits.length
|
|
@@ -535,7 +506,8 @@ module Prism
|
|
|
535
506
|
# line or this line doesn't start with whitespace, then we
|
|
536
507
|
# should concatenate the rest of the string to match ripper.
|
|
537
508
|
if dedent == 0 && (!dedent_next || !line.start_with?(/\s/))
|
|
538
|
-
|
|
509
|
+
unjoined = splits[index..] #: Array[String]
|
|
510
|
+
line = unjoined.join
|
|
539
511
|
index = splits.length
|
|
540
512
|
end
|
|
541
513
|
|
|
@@ -574,12 +546,12 @@ module Prism
|
|
|
574
546
|
ignored = deleted_chars.join
|
|
575
547
|
line.delete_prefix!(ignored)
|
|
576
548
|
|
|
577
|
-
results <<
|
|
549
|
+
results << [[lineno, 0], :on_ignored_sp, ignored, token[3]]
|
|
578
550
|
column = ignored.length
|
|
579
551
|
end
|
|
580
552
|
end
|
|
581
553
|
|
|
582
|
-
results <<
|
|
554
|
+
results << [[lineno, column], token[1], line, token[3]] unless line.empty?
|
|
583
555
|
index += 1
|
|
584
556
|
end
|
|
585
557
|
else
|
|
@@ -590,7 +562,7 @@ module Prism
|
|
|
590
562
|
end
|
|
591
563
|
|
|
592
564
|
dedent_next =
|
|
593
|
-
((token
|
|
565
|
+
((token[1] == :on_tstring_content) || (token[1] == :on_heredoc_end)) &&
|
|
594
566
|
embexpr_balance == 0
|
|
595
567
|
end
|
|
596
568
|
|
|
@@ -600,12 +572,14 @@ module Prism
|
|
|
600
572
|
|
|
601
573
|
# Here we will split between the two types of heredocs and return the
|
|
602
574
|
# object that will store their tokens.
|
|
575
|
+
#
|
|
576
|
+
#: (lex_compat_token opening) -> (PlainHeredoc | DashHeredoc | DedentingHeredoc)
|
|
603
577
|
def self.build(opening)
|
|
604
|
-
case opening
|
|
578
|
+
case opening[2][2]
|
|
605
579
|
when "~"
|
|
606
580
|
DedentingHeredoc.new
|
|
607
581
|
when "-"
|
|
608
|
-
DashHeredoc.new(opening
|
|
582
|
+
DashHeredoc.new(opening[2][3] != "'")
|
|
609
583
|
else
|
|
610
584
|
PlainHeredoc.new
|
|
611
585
|
end
|
|
@@ -619,29 +593,74 @@ module Prism
|
|
|
619
593
|
BOM_FLUSHED = RUBY_VERSION >= "3.3.0"
|
|
620
594
|
private_constant :BOM_FLUSHED
|
|
621
595
|
|
|
622
|
-
attr_reader :
|
|
596
|
+
attr_reader :options #: Hash[Symbol, untyped]
|
|
597
|
+
# @rbs @source: String
|
|
623
598
|
|
|
599
|
+
#: (String source, **untyped options) -> void
|
|
624
600
|
def initialize(source, **options)
|
|
625
601
|
@source = source
|
|
626
602
|
@options = options
|
|
627
603
|
end
|
|
628
604
|
|
|
605
|
+
#: () -> Result
|
|
629
606
|
def result
|
|
630
|
-
tokens = [] #: Array[
|
|
607
|
+
tokens = [] #: Array[lex_compat_token]
|
|
631
608
|
|
|
632
609
|
state = :default
|
|
633
610
|
heredoc_stack = [[]] #: Array[Array[Heredoc::PlainHeredoc | Heredoc::DashHeredoc | Heredoc::DedentingHeredoc]]
|
|
634
611
|
|
|
635
|
-
result = Prism.lex(source, **options)
|
|
612
|
+
result = Prism.lex(@source, **options)
|
|
613
|
+
source = result.source
|
|
636
614
|
result_value = result.value
|
|
637
|
-
previous_state = nil #: State?
|
|
615
|
+
previous_state = nil #: Translation::Ripper::Lexer::State?
|
|
638
616
|
last_heredoc_end = nil #: Integer?
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
617
|
+
eof_token = nil #: Token?
|
|
618
|
+
|
|
619
|
+
# The two tokens Ripper scanned most recently, oldest last. Tokens that
|
|
620
|
+
# this loop does not emit are not part of Ripper's view, so they never
|
|
621
|
+
# become either one.
|
|
622
|
+
previous_token = nil #: Token?
|
|
623
|
+
previous_previous_token = nil #: Token?
|
|
624
|
+
|
|
625
|
+
# The lex state in effect at each embedded expression that is still open,
|
|
626
|
+
# innermost last, and the state at the opener of the one that closed most
|
|
627
|
+
# recently. Ending an embedded expression restores the state its opener
|
|
628
|
+
# had, which on_regexp_end below has to reproduce.
|
|
629
|
+
embexpr_states = [] #: Array[Integer]
|
|
630
|
+
last_embexpr_state = nil #: Integer?
|
|
631
|
+
|
|
632
|
+
bom = source.slice(0, 3) == "\xEF\xBB\xBF"
|
|
633
|
+
|
|
634
|
+
last_comment_token = nil #: lex_compat_token?
|
|
635
|
+
last_comment_end = nil #: Integer?
|
|
636
|
+
|
|
637
|
+
result_value.each_with_index do |prism_token, index|
|
|
638
|
+
lineno = prism_token.location.start_line
|
|
639
|
+
column = prism_token.location.start_column
|
|
640
|
+
|
|
641
|
+
# Ripper is a scanner, so every event it emits corresponds to source
|
|
642
|
+
# characters. Implicit word separators have none.
|
|
643
|
+
next if prism_token.type == :WORDS_SEP_IMPLICIT
|
|
644
|
+
|
|
645
|
+
event = RIPPER.fetch(prism_token.type)
|
|
646
|
+
value = prism_token.value
|
|
647
|
+
lex_state = Translation::Ripper::Lexer::State[prism_token._ripper_state]
|
|
648
|
+
|
|
649
|
+
# A comment token does not include its terminating newline, but
|
|
650
|
+
# ripper's comment value does, so the newline token that directly
|
|
651
|
+
# follows a comment is folded back into it.
|
|
652
|
+
if last_comment_token && last_comment_end == prism_token.location.start_offset && (event == :on_nl || event == :on_ignored_nl)
|
|
653
|
+
last_comment_token[2] += value
|
|
654
|
+
last_comment_token = nil
|
|
655
|
+
last_comment_end = nil
|
|
656
|
+
|
|
657
|
+
# The newline's characters are part of the comment Ripper emits, so
|
|
658
|
+
# it is still the token Ripper scanned most recently, and the trailing
|
|
659
|
+
# whitespace check at on_eof measures from its end.
|
|
660
|
+
previous_previous_token = previous_token
|
|
661
|
+
previous_token = prism_token
|
|
662
|
+
next
|
|
663
|
+
end
|
|
645
664
|
|
|
646
665
|
# If there's a UTF-8 byte-order mark as the start of the file, then for
|
|
647
666
|
# certain tokens ripper sets the first token back by 3 bytes. It also
|
|
@@ -653,68 +672,55 @@ module Prism
|
|
|
653
672
|
|
|
654
673
|
if index == 0 && column == 0 && !BOM_FLUSHED
|
|
655
674
|
flushed =
|
|
656
|
-
case
|
|
675
|
+
case prism_token.type
|
|
657
676
|
when :BACK_REFERENCE, :INSTANCE_VARIABLE, :CLASS_VARIABLE,
|
|
658
677
|
:GLOBAL_VARIABLE, :NUMBERED_REFERENCE, :PERCENT_LOWER_I,
|
|
659
678
|
:PERCENT_LOWER_X, :PERCENT_LOWER_W, :PERCENT_UPPER_I,
|
|
660
679
|
:PERCENT_UPPER_W, :STRING_BEGIN
|
|
661
680
|
true
|
|
662
681
|
when :REGEXP_BEGIN, :SYMBOL_BEGIN
|
|
663
|
-
|
|
682
|
+
value.start_with?("%")
|
|
664
683
|
else
|
|
665
684
|
false
|
|
666
685
|
end
|
|
667
686
|
|
|
668
687
|
unless flushed
|
|
669
688
|
column -= 3
|
|
670
|
-
value = token.value
|
|
671
689
|
value.prepend(String.new("\xEF\xBB\xBF", encoding: value.encoding))
|
|
672
690
|
end
|
|
673
691
|
end
|
|
674
692
|
end
|
|
675
693
|
|
|
676
|
-
|
|
677
|
-
value = token.value
|
|
678
|
-
lex_state = Translation::Ripper::Lexer::State.new(lex_state)
|
|
679
|
-
|
|
680
|
-
token =
|
|
694
|
+
lex_compat_token =
|
|
681
695
|
case event
|
|
682
696
|
when :on___end__
|
|
683
|
-
|
|
697
|
+
# Ripper doesn't include the rest of the token in the event, so we need to
|
|
698
|
+
# trim it down to just the content on the first line.
|
|
699
|
+
value = value[0..value.index("\n")] #: String
|
|
700
|
+
[[lineno, column], event, value, lex_state]
|
|
684
701
|
when :on_comment
|
|
685
|
-
|
|
702
|
+
[[lineno, column], event, value, lex_state]
|
|
686
703
|
when :on_heredoc_end
|
|
687
704
|
# Heredoc end tokens can be emitted in an odd order, so we don't
|
|
688
705
|
# want to bother comparing the state on them.
|
|
689
|
-
last_heredoc_end =
|
|
690
|
-
|
|
691
|
-
when :
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
#
|
|
695
|
-
# def foo bar
|
|
696
|
-
#
|
|
697
|
-
# then Ripper will mark bar as END|LABEL if there is a local in a
|
|
698
|
-
# parent scope named bar because it hasn't pushed the local table
|
|
699
|
-
# yet. We do this more accurately, so we need to allow comparing
|
|
700
|
-
# against both END and END|LABEL.
|
|
701
|
-
ParamToken.new([[lineno, column], event, value, lex_state])
|
|
702
|
-
elsif lex_state == Translation::Ripper::EXPR_END | Translation::Ripper::EXPR_LABEL
|
|
703
|
-
# In the event that we're comparing identifiers, we're going to
|
|
704
|
-
# allow a little divergence. Ripper doesn't account for local
|
|
705
|
-
# variables introduced through named captures in regexes, and we
|
|
706
|
-
# do, which accounts for this difference.
|
|
707
|
-
IdentToken.new([[lineno, column], event, value, lex_state])
|
|
708
|
-
else
|
|
709
|
-
Token.new([[lineno, column], event, value, lex_state])
|
|
710
|
-
end
|
|
706
|
+
last_heredoc_end = prism_token.location.end_offset
|
|
707
|
+
[[lineno, column], event, value, lex_state]
|
|
708
|
+
when :on_embexpr_beg
|
|
709
|
+
embexpr_states.push(prism_token._ripper_state)
|
|
710
|
+
[[lineno, column], event, value, lex_state]
|
|
711
711
|
when :on_embexpr_end
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
#
|
|
716
|
-
|
|
717
|
-
|
|
712
|
+
last_embexpr_state = embexpr_states.pop
|
|
713
|
+
[[lineno, column], event, value, lex_state]
|
|
714
|
+
when :on_words_sep
|
|
715
|
+
# Ripper emits one token each per line.
|
|
716
|
+
value.each_line.with_index do |line, index|
|
|
717
|
+
if index > 0
|
|
718
|
+
lineno += 1
|
|
719
|
+
column = 0
|
|
720
|
+
end
|
|
721
|
+
tokens << [[lineno, column], event, line, lex_state]
|
|
722
|
+
end
|
|
723
|
+
tokens.pop #: lex_compat_token
|
|
718
724
|
when :on_regexp_end
|
|
719
725
|
# On regex end, Ripper scans and then sets end state, so the ripper
|
|
720
726
|
# lexed output is begin, when it should be end. prism sets lex state
|
|
@@ -722,62 +728,64 @@ module Prism
|
|
|
722
728
|
# Ripper's lexed state. So here, if it's a regexp end token, we
|
|
723
729
|
# output the state as the previous state, solely for the sake of
|
|
724
730
|
# comparison.
|
|
725
|
-
previous_token = result_value[index - 1][0]
|
|
726
731
|
lex_state =
|
|
727
|
-
if
|
|
728
|
-
#
|
|
729
|
-
#
|
|
730
|
-
|
|
731
|
-
# embedded expression. So we have to go and find that state and
|
|
732
|
-
# set it here.
|
|
733
|
-
counter = 1
|
|
734
|
-
current_index = index - 1
|
|
735
|
-
|
|
736
|
-
until counter == 0
|
|
737
|
-
current_index -= 1
|
|
738
|
-
current_event = RIPPER.fetch(result_value[current_index][0].type)
|
|
739
|
-
counter += { on_embexpr_beg: -1, on_embexpr_end: 1 }[current_event] || 0
|
|
740
|
-
end
|
|
741
|
-
|
|
742
|
-
Translation::Ripper::Lexer::State.new(result_value[current_index][1])
|
|
732
|
+
if previous_token&.type == :EMBEXPR_END && last_embexpr_state
|
|
733
|
+
# An embedded expression that just closed restored the state its
|
|
734
|
+
# opener had, so that is the state Ripper reports here.
|
|
735
|
+
Translation::Ripper::Lexer::State[last_embexpr_state]
|
|
743
736
|
else
|
|
744
737
|
previous_state
|
|
745
738
|
end
|
|
746
739
|
|
|
747
|
-
|
|
740
|
+
[[lineno, column], event, value, lex_state]
|
|
748
741
|
when :on_eof
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
#
|
|
759
|
-
# there is
|
|
760
|
-
#
|
|
761
|
-
#
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
742
|
+
eof_token = prism_token
|
|
743
|
+
|
|
744
|
+
if (previous = previous_token)
|
|
745
|
+
# A newline that was folded back into a comment still marks the
|
|
746
|
+
# comment boundary for the check below.
|
|
747
|
+
before = previous_previous_token
|
|
748
|
+
comment_boundary = previous.type == :COMMENT ||
|
|
749
|
+
(before && %i[NEWLINE NEWLINE_TERMINATOR IGNORED_NEWLINE].include?(previous.type) && before.type == :COMMENT && before.location.end_offset == previous.location.start_offset)
|
|
750
|
+
|
|
751
|
+
# If we're at the end of the file and the previous token was a
|
|
752
|
+
# comment and there is still whitespace after the comment, then
|
|
753
|
+
# Ripper will append a on_nl token (even though there isn't
|
|
754
|
+
# necessarily a newline). We mirror that here.
|
|
755
|
+
if comment_boundary
|
|
756
|
+
# If the comment is at the start of a heredoc: <<HEREDOC # comment
|
|
757
|
+
# then the comment's end_offset is up near the heredoc_beg.
|
|
758
|
+
# This is not the correct offset to use for figuring out if
|
|
759
|
+
# there is trailing whitespace after the last token.
|
|
760
|
+
# Use the greater offset of the two to determine the start of
|
|
761
|
+
# the trailing whitespace.
|
|
762
|
+
start_offset = [previous.location.end_offset, last_heredoc_end].compact.max
|
|
763
|
+
end_offset = prism_token.location.start_offset
|
|
764
|
+
|
|
765
|
+
if start_offset < end_offset
|
|
766
|
+
if bom
|
|
767
|
+
start_offset += 3
|
|
768
|
+
end_offset += 3
|
|
769
|
+
end
|
|
770
770
|
|
|
771
|
-
|
|
771
|
+
tokens << [[lineno, 0], :on_nl, source.slice(start_offset, end_offset - start_offset), lex_state]
|
|
772
|
+
end
|
|
772
773
|
end
|
|
773
774
|
end
|
|
774
775
|
|
|
775
|
-
|
|
776
|
+
[[lineno, column], event, value, lex_state]
|
|
776
777
|
else
|
|
777
|
-
|
|
778
|
-
end
|
|
778
|
+
[[lineno, column], event, value, lex_state]
|
|
779
|
+
end #: lex_compat_token
|
|
779
780
|
|
|
780
781
|
previous_state = lex_state
|
|
782
|
+
previous_previous_token = previous_token
|
|
783
|
+
previous_token = prism_token
|
|
784
|
+
|
|
785
|
+
if event == :on_comment
|
|
786
|
+
last_comment_token = lex_compat_token
|
|
787
|
+
last_comment_end = prism_token.location.end_offset
|
|
788
|
+
end
|
|
781
789
|
|
|
782
790
|
# The order in which tokens appear in our lexer is different from the
|
|
783
791
|
# order that they appear in Ripper. When we hit the declaration of a
|
|
@@ -792,19 +800,19 @@ module Prism
|
|
|
792
800
|
when :default
|
|
793
801
|
# The default state is when there are no heredocs at all. In this
|
|
794
802
|
# state we can append the token to the list of tokens and move on.
|
|
795
|
-
tokens <<
|
|
803
|
+
tokens << lex_compat_token
|
|
796
804
|
|
|
797
805
|
# If we get the declaration of a heredoc, then we open a new heredoc
|
|
798
806
|
# and move into the heredoc_opened state.
|
|
799
807
|
if event == :on_heredoc_beg
|
|
800
808
|
state = :heredoc_opened
|
|
801
|
-
heredoc_stack.last << Heredoc.build(
|
|
809
|
+
heredoc_stack.last << Heredoc.build(lex_compat_token)
|
|
802
810
|
end
|
|
803
811
|
when :heredoc_opened
|
|
804
812
|
# The heredoc_opened state is when we've seen the declaration of a
|
|
805
813
|
# heredoc and are now lexing the body of the heredoc. In this state we
|
|
806
814
|
# push tokens onto the most recently created heredoc.
|
|
807
|
-
heredoc_stack.last.last <<
|
|
815
|
+
heredoc_stack.last.last << lex_compat_token
|
|
808
816
|
|
|
809
817
|
case event
|
|
810
818
|
when :on_heredoc_beg
|
|
@@ -812,7 +820,7 @@ module Prism
|
|
|
812
820
|
# heredoc, this means we have nested heredocs. In this case we'll
|
|
813
821
|
# push a new heredoc onto the stack and stay in the heredoc_opened
|
|
814
822
|
# state since we're now lexing the body of the new heredoc.
|
|
815
|
-
heredoc_stack << [Heredoc.build(
|
|
823
|
+
heredoc_stack << [Heredoc.build(lex_compat_token)]
|
|
816
824
|
when :on_heredoc_end
|
|
817
825
|
# If we receive the end of a heredoc, then we're done lexing the
|
|
818
826
|
# body of the heredoc. In this case we now have a completed heredoc
|
|
@@ -821,10 +829,10 @@ module Prism
|
|
|
821
829
|
state = :heredoc_closed
|
|
822
830
|
end
|
|
823
831
|
when :heredoc_closed
|
|
824
|
-
if %i[on_nl on_ignored_nl on_comment].include?(event) || (event == :on_tstring_content && value.end_with?("\n"))
|
|
832
|
+
if %i[on_nl on_ignored_nl on_comment].include?(event) || ((event == :on_tstring_content) && value.end_with?("\n"))
|
|
825
833
|
if heredoc_stack.size > 1
|
|
826
|
-
flushing = heredoc_stack.pop
|
|
827
|
-
heredoc_stack.last.last <<
|
|
834
|
+
flushing = heredoc_stack.pop #: Array[Heredoc::PlainHeredoc | Heredoc::DashHeredoc | Heredoc::DedentingHeredoc]
|
|
835
|
+
heredoc_stack.last.last << lex_compat_token
|
|
828
836
|
|
|
829
837
|
flushing.each do |heredoc|
|
|
830
838
|
heredoc.to_a.each do |flushed_token|
|
|
@@ -836,12 +844,12 @@ module Prism
|
|
|
836
844
|
next
|
|
837
845
|
end
|
|
838
846
|
elsif event == :on_heredoc_beg
|
|
839
|
-
tokens <<
|
|
847
|
+
tokens << lex_compat_token
|
|
840
848
|
state = :heredoc_opened
|
|
841
|
-
heredoc_stack.last << Heredoc.build(
|
|
849
|
+
heredoc_stack.last << Heredoc.build(lex_compat_token)
|
|
842
850
|
next
|
|
843
851
|
elsif heredoc_stack.size > 1
|
|
844
|
-
heredoc_stack[-2].last <<
|
|
852
|
+
heredoc_stack[-2].last << lex_compat_token
|
|
845
853
|
next
|
|
846
854
|
end
|
|
847
855
|
|
|
@@ -852,17 +860,92 @@ module Prism
|
|
|
852
860
|
heredoc_stack.last.clear
|
|
853
861
|
state = :default
|
|
854
862
|
|
|
855
|
-
tokens <<
|
|
863
|
+
tokens << lex_compat_token
|
|
856
864
|
end
|
|
857
865
|
end
|
|
858
866
|
|
|
859
|
-
# Drop the EOF token from the list
|
|
860
|
-
|
|
867
|
+
# Drop the EOF token from the list. The EOF token may not be
|
|
868
|
+
# present if the source was syntax invalid
|
|
869
|
+
if tokens.dig(-1, 1) == :on_eof
|
|
870
|
+
tokens = tokens[0...-1] #: Array[lex_compat_token]
|
|
871
|
+
end
|
|
872
|
+
|
|
873
|
+
# We sort by location because Ripper.lex sorts.
|
|
874
|
+
tokens.sort_by! do |token|
|
|
875
|
+
line, column = token[0]
|
|
876
|
+
source.byte_offset(line, column)
|
|
877
|
+
end
|
|
878
|
+
|
|
879
|
+
tokens = post_process_tokens(tokens, source, result.data_loc, bom, eof_token)
|
|
861
880
|
|
|
862
|
-
|
|
863
|
-
|
|
881
|
+
Result.new(tokens, result.magic_comments, result.data_loc, result.errors, result.warnings, result.continuable?, source)
|
|
882
|
+
end
|
|
883
|
+
|
|
884
|
+
private
|
|
885
|
+
|
|
886
|
+
#: (Array[lex_compat_token] tokens, Source source, Location? data_loc, bool bom, Token? eof_token) -> Array[lex_compat_token]
|
|
887
|
+
def post_process_tokens(tokens, source, data_loc, bom, eof_token)
|
|
888
|
+
new_tokens = [] #: Array[lex_compat_token]
|
|
889
|
+
|
|
890
|
+
prev_token_state = Translation::Ripper::Lexer::State[Translation::Ripper::EXPR_BEG]
|
|
891
|
+
prev_token_end = bom ? 3 : 0
|
|
892
|
+
|
|
893
|
+
cache = Translation::Ripper::LineAndColumnCache.new(source)
|
|
894
|
+
|
|
895
|
+
tokens.each do |token|
|
|
896
|
+
# Skip missing heredoc ends.
|
|
897
|
+
next if token[1] == :on_heredoc_end && token[2] == ""
|
|
898
|
+
|
|
899
|
+
# Add :on_sp tokens.
|
|
900
|
+
line, column = token[0]
|
|
901
|
+
start_offset = source.byte_offset(line, column)
|
|
902
|
+
|
|
903
|
+
# Ripper reports columns on line 1 without counting the BOM, so we
|
|
904
|
+
# adjust to get the real offset
|
|
905
|
+
start_offset += 3 if line == 1 && bom
|
|
906
|
+
|
|
907
|
+
if start_offset > prev_token_end
|
|
908
|
+
sp_value = source.slice(prev_token_end, start_offset - prev_token_end)
|
|
909
|
+
sp_line, sp_column = cache.line_and_column(prev_token_end)
|
|
910
|
+
# Ripper reports columns on line 1 without counting the BOM
|
|
911
|
+
sp_column -= 3 if sp_line == 1 && bom
|
|
912
|
+
continuation_index = sp_value.byteindex("\\")
|
|
913
|
+
|
|
914
|
+
# ripper emits up to three :on_sp tokens when line continuations are used
|
|
915
|
+
if continuation_index
|
|
916
|
+
next_whitespace_index = continuation_index + 1
|
|
917
|
+
next_whitespace_index += 1 if sp_value.byteslice(next_whitespace_index) == "\r"
|
|
918
|
+
next_whitespace_index += 1
|
|
919
|
+
first_whitespace = sp_value[0...continuation_index] #: String
|
|
920
|
+
continuation = sp_value[continuation_index...next_whitespace_index] #: String
|
|
921
|
+
second_whitespace = sp_value[next_whitespace_index..] || ""
|
|
922
|
+
|
|
923
|
+
new_tokens << [[sp_line, sp_column], :on_sp, first_whitespace, prev_token_state] unless first_whitespace.empty?
|
|
924
|
+
new_tokens << [[sp_line, sp_column + continuation_index], :on_sp, continuation, prev_token_state]
|
|
925
|
+
new_tokens << [[sp_line + 1, 0], :on_sp, second_whitespace, prev_token_state] unless second_whitespace.empty?
|
|
926
|
+
else
|
|
927
|
+
new_tokens << [[sp_line, sp_column], :on_sp, sp_value, prev_token_state]
|
|
928
|
+
end
|
|
929
|
+
end
|
|
930
|
+
|
|
931
|
+
new_tokens << token
|
|
932
|
+
prev_token_state = token[3]
|
|
933
|
+
prev_token_end = start_offset + token[2].bytesize
|
|
934
|
+
end
|
|
935
|
+
|
|
936
|
+
if !data_loc && eof_token # no trailing :on_sp with __END__ as it is always preceded by :on_nl
|
|
937
|
+
end_offset = eof_token.location.end_offset
|
|
938
|
+
if prev_token_end < end_offset
|
|
939
|
+
new_tokens << [
|
|
940
|
+
[source.line(prev_token_end), source.column(prev_token_end)],
|
|
941
|
+
:on_sp,
|
|
942
|
+
source.slice(prev_token_end, end_offset - prev_token_end),
|
|
943
|
+
prev_token_state
|
|
944
|
+
]
|
|
945
|
+
end
|
|
946
|
+
end
|
|
864
947
|
|
|
865
|
-
|
|
948
|
+
new_tokens
|
|
866
949
|
end
|
|
867
950
|
end
|
|
868
951
|
|