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.
- 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.so +0 -0
- data/lib/prism/3.4/prism.so +0 -0
- data/lib/prism/4.0/prism.so +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
|
@@ -0,0 +1,953 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# :markup: markdown
|
|
3
|
+
#--
|
|
4
|
+
# rbs_inline: enabled
|
|
5
|
+
|
|
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
|
+
|
|
35
|
+
# This class is responsible for lexing the source using prism and then
|
|
36
|
+
# converting those tokens to be compatible with Ripper. In the vast majority
|
|
37
|
+
# of cases, this is a one-to-one mapping of the token type. Everything else
|
|
38
|
+
# generally lines up. However, there are a few cases that require special
|
|
39
|
+
# handling.
|
|
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
|
+
|
|
45
|
+
# A result class specialized for holding tokens produced by the lexer.
|
|
46
|
+
class Result < Prism::Result
|
|
47
|
+
# The list of tokens that were produced by the lexer.
|
|
48
|
+
attr_reader :value #: Array[lex_compat_token]
|
|
49
|
+
|
|
50
|
+
# Create a new lex compat result object with the given values.
|
|
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)
|
|
54
|
+
@value = value
|
|
55
|
+
super(magic_comments, data_loc, errors, warnings, continuable, source)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Implement the hash pattern matching interface for Result.
|
|
59
|
+
#
|
|
60
|
+
#: (Array[Symbol]? keys) -> Hash[Symbol, untyped]
|
|
61
|
+
def deconstruct_keys(keys) # :nodoc:
|
|
62
|
+
super.merge!(value: value)
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# This is a mapping of prism token types to Ripper token types. This is a
|
|
67
|
+
# many-to-one mapping because we split up our token types, whereas Ripper
|
|
68
|
+
# tends to group them.
|
|
69
|
+
RIPPER = {
|
|
70
|
+
AMPERSAND: :on_op,
|
|
71
|
+
AMPERSAND_AMPERSAND: :on_op,
|
|
72
|
+
AMPERSAND_AMPERSAND_EQUAL: :on_op,
|
|
73
|
+
AMPERSAND_DOT: :on_op,
|
|
74
|
+
AMPERSAND_EQUAL: :on_op,
|
|
75
|
+
BACK_REFERENCE: :on_backref,
|
|
76
|
+
BACKTICK: :on_backtick,
|
|
77
|
+
BANG: :on_op,
|
|
78
|
+
BANG_EQUAL: :on_op,
|
|
79
|
+
BANG_TILDE: :on_op,
|
|
80
|
+
BRACE_LEFT: :on_lbrace,
|
|
81
|
+
BRACE_LEFT_ARGUMENT: :on_lbrace,
|
|
82
|
+
BRACE_LEFT_HASH: :on_lbrace,
|
|
83
|
+
BRACE_RIGHT: :on_rbrace,
|
|
84
|
+
BRACKET_LEFT: :on_lbracket,
|
|
85
|
+
BRACKET_LEFT_ARRAY: :on_lbracket,
|
|
86
|
+
BRACKET_LEFT_RIGHT: :on_op,
|
|
87
|
+
BRACKET_LEFT_RIGHT_EQUAL: :on_op,
|
|
88
|
+
BRACKET_RIGHT: :on_rbracket,
|
|
89
|
+
CARET: :on_op,
|
|
90
|
+
CARET_EQUAL: :on_op,
|
|
91
|
+
CHARACTER_LITERAL: :on_CHAR,
|
|
92
|
+
CLASS_VARIABLE: :on_cvar,
|
|
93
|
+
COLON: :on_op,
|
|
94
|
+
COLON_COLON: :on_op,
|
|
95
|
+
COMMA: :on_comma,
|
|
96
|
+
COMMENT: :on_comment,
|
|
97
|
+
CONSTANT: :on_const,
|
|
98
|
+
DOT: :on_period,
|
|
99
|
+
DOT_DOT: :on_op,
|
|
100
|
+
DOT_DOT_DOT: :on_op,
|
|
101
|
+
EMBDOC_BEGIN: :on_embdoc_beg,
|
|
102
|
+
EMBDOC_END: :on_embdoc_end,
|
|
103
|
+
EMBDOC_LINE: :on_embdoc,
|
|
104
|
+
EMBEXPR_BEGIN: :on_embexpr_beg,
|
|
105
|
+
EMBEXPR_END: :on_embexpr_end,
|
|
106
|
+
EMBVAR: :on_embvar,
|
|
107
|
+
EOF: :on_eof,
|
|
108
|
+
EQUAL: :on_op,
|
|
109
|
+
EQUAL_EQUAL: :on_op,
|
|
110
|
+
EQUAL_EQUAL_EQUAL: :on_op,
|
|
111
|
+
EQUAL_GREATER: :on_op,
|
|
112
|
+
EQUAL_TILDE: :on_op,
|
|
113
|
+
FLOAT: :on_float,
|
|
114
|
+
FLOAT_IMAGINARY: :on_imaginary,
|
|
115
|
+
FLOAT_RATIONAL: :on_rational,
|
|
116
|
+
FLOAT_RATIONAL_IMAGINARY: :on_imaginary,
|
|
117
|
+
GREATER: :on_op,
|
|
118
|
+
GREATER_EQUAL: :on_op,
|
|
119
|
+
GREATER_GREATER: :on_op,
|
|
120
|
+
GREATER_GREATER_EQUAL: :on_op,
|
|
121
|
+
GLOBAL_VARIABLE: :on_gvar,
|
|
122
|
+
HEREDOC_END: :on_heredoc_end,
|
|
123
|
+
HEREDOC_START: :on_heredoc_beg,
|
|
124
|
+
IDENTIFIER: :on_ident,
|
|
125
|
+
IGNORED_NEWLINE: :on_ignored_nl,
|
|
126
|
+
INTEGER: :on_int,
|
|
127
|
+
INTEGER_IMAGINARY: :on_imaginary,
|
|
128
|
+
INTEGER_RATIONAL: :on_rational,
|
|
129
|
+
INTEGER_RATIONAL_IMAGINARY: :on_imaginary,
|
|
130
|
+
INSTANCE_VARIABLE: :on_ivar,
|
|
131
|
+
INVALID: :INVALID,
|
|
132
|
+
KEYWORD___ENCODING__: :on_kw,
|
|
133
|
+
KEYWORD___LINE__: :on_kw,
|
|
134
|
+
KEYWORD___FILE__: :on_kw,
|
|
135
|
+
KEYWORD_ALIAS: :on_kw,
|
|
136
|
+
KEYWORD_AND: :on_kw,
|
|
137
|
+
KEYWORD_BEGIN: :on_kw,
|
|
138
|
+
KEYWORD_BEGIN_UPCASE: :on_kw,
|
|
139
|
+
KEYWORD_BREAK: :on_kw,
|
|
140
|
+
KEYWORD_CASE: :on_kw,
|
|
141
|
+
KEYWORD_CLASS: :on_kw,
|
|
142
|
+
KEYWORD_DEF: :on_kw,
|
|
143
|
+
KEYWORD_DEFINED: :on_kw,
|
|
144
|
+
KEYWORD_DO: :on_kw,
|
|
145
|
+
KEYWORD_DO_BLOCK: :on_kw,
|
|
146
|
+
KEYWORD_DO_LAMBDA: :on_kw,
|
|
147
|
+
KEYWORD_DO_LOOP: :on_kw,
|
|
148
|
+
KEYWORD_ELSE: :on_kw,
|
|
149
|
+
KEYWORD_ELSIF: :on_kw,
|
|
150
|
+
KEYWORD_END: :on_kw,
|
|
151
|
+
KEYWORD_END_UPCASE: :on_kw,
|
|
152
|
+
KEYWORD_ENSURE: :on_kw,
|
|
153
|
+
KEYWORD_FALSE: :on_kw,
|
|
154
|
+
KEYWORD_FOR: :on_kw,
|
|
155
|
+
KEYWORD_IF: :on_kw,
|
|
156
|
+
KEYWORD_IF_MODIFIER: :on_kw,
|
|
157
|
+
KEYWORD_IN: :on_kw,
|
|
158
|
+
KEYWORD_MODULE: :on_kw,
|
|
159
|
+
KEYWORD_NEXT: :on_kw,
|
|
160
|
+
KEYWORD_NIL: :on_kw,
|
|
161
|
+
KEYWORD_NOT: :on_kw,
|
|
162
|
+
KEYWORD_OR: :on_kw,
|
|
163
|
+
KEYWORD_REDO: :on_kw,
|
|
164
|
+
KEYWORD_RESCUE: :on_kw,
|
|
165
|
+
KEYWORD_RESCUE_MODIFIER: :on_kw,
|
|
166
|
+
KEYWORD_RETRY: :on_kw,
|
|
167
|
+
KEYWORD_RETURN: :on_kw,
|
|
168
|
+
KEYWORD_SELF: :on_kw,
|
|
169
|
+
KEYWORD_SUPER: :on_kw,
|
|
170
|
+
KEYWORD_THEN: :on_kw,
|
|
171
|
+
KEYWORD_TRUE: :on_kw,
|
|
172
|
+
KEYWORD_UNDEF: :on_kw,
|
|
173
|
+
KEYWORD_UNLESS: :on_kw,
|
|
174
|
+
KEYWORD_UNLESS_MODIFIER: :on_kw,
|
|
175
|
+
KEYWORD_UNTIL: :on_kw,
|
|
176
|
+
KEYWORD_UNTIL_MODIFIER: :on_kw,
|
|
177
|
+
KEYWORD_WHEN: :on_kw,
|
|
178
|
+
KEYWORD_WHILE: :on_kw,
|
|
179
|
+
KEYWORD_WHILE_MODIFIER: :on_kw,
|
|
180
|
+
KEYWORD_YIELD: :on_kw,
|
|
181
|
+
LABEL: :on_label,
|
|
182
|
+
LABEL_END: :on_label_end,
|
|
183
|
+
LAMBDA_BEGIN: :on_tlambeg,
|
|
184
|
+
LESS: :on_op,
|
|
185
|
+
LESS_EQUAL: :on_op,
|
|
186
|
+
LESS_EQUAL_GREATER: :on_op,
|
|
187
|
+
LESS_LESS: :on_op,
|
|
188
|
+
LESS_LESS_EQUAL: :on_op,
|
|
189
|
+
METHOD_NAME: :on_ident,
|
|
190
|
+
MINUS: :on_op,
|
|
191
|
+
MINUS_EQUAL: :on_op,
|
|
192
|
+
MINUS_GREATER: :on_tlambda,
|
|
193
|
+
NEWLINE: :on_nl,
|
|
194
|
+
NEWLINE_TERMINATOR: :on_ignored_nl,
|
|
195
|
+
NUMBERED_REFERENCE: :on_backref,
|
|
196
|
+
PARENTHESIS_LEFT: :on_lparen,
|
|
197
|
+
PARENTHESIS_LEFT_GROUPING: :on_lparen,
|
|
198
|
+
PARENTHESIS_LEFT_PARENTHESES: :on_lparen,
|
|
199
|
+
PARENTHESIS_RIGHT: :on_rparen,
|
|
200
|
+
PERCENT: :on_op,
|
|
201
|
+
PERCENT_EQUAL: :on_op,
|
|
202
|
+
PERCENT_LOWER_I: :on_qsymbols_beg,
|
|
203
|
+
PERCENT_LOWER_W: :on_qwords_beg,
|
|
204
|
+
PERCENT_LOWER_X: :on_backtick,
|
|
205
|
+
PERCENT_UPPER_I: :on_symbols_beg,
|
|
206
|
+
PERCENT_UPPER_W: :on_words_beg,
|
|
207
|
+
PIPE: :on_op,
|
|
208
|
+
PIPE_EQUAL: :on_op,
|
|
209
|
+
PIPE_PIPE: :on_op,
|
|
210
|
+
PIPE_PIPE_EQUAL: :on_op,
|
|
211
|
+
PLUS: :on_op,
|
|
212
|
+
PLUS_EQUAL: :on_op,
|
|
213
|
+
QUESTION_MARK: :on_op,
|
|
214
|
+
RATIONAL_FLOAT: :on_rational,
|
|
215
|
+
RATIONAL_INTEGER: :on_rational,
|
|
216
|
+
REGEXP_BEGIN: :on_regexp_beg,
|
|
217
|
+
REGEXP_END: :on_regexp_end,
|
|
218
|
+
SEMICOLON: :on_semicolon,
|
|
219
|
+
SLASH: :on_op,
|
|
220
|
+
SLASH_EQUAL: :on_op,
|
|
221
|
+
STAR: :on_op,
|
|
222
|
+
STAR_EQUAL: :on_op,
|
|
223
|
+
STAR_STAR: :on_op,
|
|
224
|
+
STAR_STAR_EQUAL: :on_op,
|
|
225
|
+
STRING_BEGIN: :on_tstring_beg,
|
|
226
|
+
STRING_CONTENT: :on_tstring_content,
|
|
227
|
+
STRING_END: :on_tstring_end,
|
|
228
|
+
SYMBOL_BEGIN: :on_symbeg,
|
|
229
|
+
TILDE: :on_op,
|
|
230
|
+
UAMPERSAND: :on_op,
|
|
231
|
+
UCOLON_COLON: :on_op,
|
|
232
|
+
UDOT_DOT: :on_op,
|
|
233
|
+
UDOT_DOT_DOT: :on_op,
|
|
234
|
+
UMINUS: :on_op,
|
|
235
|
+
UMINUS_NUM: :on_op,
|
|
236
|
+
UPLUS: :on_op,
|
|
237
|
+
USTAR: :on_op,
|
|
238
|
+
USTAR_STAR: :on_op,
|
|
239
|
+
WORDS_SEP: :on_words_sep,
|
|
240
|
+
XSTRING_BEGIN: :on_backtick,
|
|
241
|
+
"__END__": :on___end__
|
|
242
|
+
}.freeze
|
|
243
|
+
|
|
244
|
+
# A heredoc in this case is a list of tokens that belong to the body of the
|
|
245
|
+
# heredoc that should be appended onto the list of tokens when the heredoc
|
|
246
|
+
# closes.
|
|
247
|
+
module Heredoc # :nodoc:
|
|
248
|
+
# Heredocs that are no dash or tilde heredocs are just a list of tokens.
|
|
249
|
+
# We need to keep them around so that we can insert them in the correct
|
|
250
|
+
# order back into the token stream and set the state of the last token to
|
|
251
|
+
# the state that the heredoc was opened in.
|
|
252
|
+
class PlainHeredoc # :nodoc:
|
|
253
|
+
attr_reader :tokens #: Array[lex_compat_token]
|
|
254
|
+
|
|
255
|
+
#: () -> void
|
|
256
|
+
def initialize
|
|
257
|
+
@tokens = []
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
#: (lex_compat_token token) -> void
|
|
261
|
+
def <<(token)
|
|
262
|
+
tokens << token
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
#: () -> Array[lex_compat_token]
|
|
266
|
+
def to_a
|
|
267
|
+
tokens
|
|
268
|
+
end
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
# Dash heredocs are a little more complicated. They are a list of tokens
|
|
272
|
+
# that need to be split on "\\\n" to mimic Ripper's behavior. We also need
|
|
273
|
+
# to keep track of the state that the heredoc was opened in.
|
|
274
|
+
class DashHeredoc # :nodoc:
|
|
275
|
+
attr_reader :split #: bool
|
|
276
|
+
attr_reader :tokens #: Array[lex_compat_token]
|
|
277
|
+
|
|
278
|
+
#: (bool split) -> void
|
|
279
|
+
def initialize(split)
|
|
280
|
+
@split = split
|
|
281
|
+
@tokens = []
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
#: (lex_compat_token token) -> void
|
|
285
|
+
def <<(token)
|
|
286
|
+
tokens << token
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
#: () -> Array[lex_compat_token]
|
|
290
|
+
def to_a
|
|
291
|
+
embexpr_balance = 0
|
|
292
|
+
|
|
293
|
+
tokens.each_with_object([]) do |token, results| #$ Array[lex_compat_token]
|
|
294
|
+
case token[1]
|
|
295
|
+
when :on_embexpr_beg
|
|
296
|
+
embexpr_balance += 1
|
|
297
|
+
results << token
|
|
298
|
+
when :on_embexpr_end
|
|
299
|
+
embexpr_balance -= 1
|
|
300
|
+
results << token
|
|
301
|
+
when :on_tstring_content
|
|
302
|
+
if embexpr_balance == 0
|
|
303
|
+
lineno = token[0][0]
|
|
304
|
+
column = token[0][1]
|
|
305
|
+
|
|
306
|
+
if split
|
|
307
|
+
# Split on "\\\n" to mimic Ripper's behavior. Use a lookbehind
|
|
308
|
+
# to keep the delimiter in the result.
|
|
309
|
+
token[2].split(/(?<=[^\\]\\\n)|(?<=[^\\]\\\r\n)/).each_with_index do |value, index|
|
|
310
|
+
column = 0 if index > 0
|
|
311
|
+
results << [[lineno, column], :on_tstring_content, value, token[3]]
|
|
312
|
+
lineno += value.count("\n")
|
|
313
|
+
end
|
|
314
|
+
else
|
|
315
|
+
results << token
|
|
316
|
+
end
|
|
317
|
+
else
|
|
318
|
+
results << token
|
|
319
|
+
end
|
|
320
|
+
else
|
|
321
|
+
results << token
|
|
322
|
+
end
|
|
323
|
+
end
|
|
324
|
+
end
|
|
325
|
+
end
|
|
326
|
+
|
|
327
|
+
# Heredocs that are dedenting heredocs are a little more complicated.
|
|
328
|
+
# Ripper outputs on_ignored_sp tokens for the whitespace that is being
|
|
329
|
+
# removed from the output. prism only modifies the node itself and keeps
|
|
330
|
+
# the token the same. This simplifies prism, but makes comparing against
|
|
331
|
+
# Ripper much harder because there is a length mismatch.
|
|
332
|
+
#
|
|
333
|
+
# Fortunately, we already have to pull out the heredoc tokens in order to
|
|
334
|
+
# insert them into the stream in the correct order. As such, we can do
|
|
335
|
+
# some extra manipulation on the tokens to make them match Ripper's
|
|
336
|
+
# output by mirroring the dedent logic that Ripper uses.
|
|
337
|
+
class DedentingHeredoc # :nodoc:
|
|
338
|
+
TAB_WIDTH = 8
|
|
339
|
+
|
|
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
|
|
345
|
+
|
|
346
|
+
#: () -> void
|
|
347
|
+
def initialize
|
|
348
|
+
@tokens = []
|
|
349
|
+
@dedent_next = true
|
|
350
|
+
@dedent = nil
|
|
351
|
+
@embexpr_balance = 0
|
|
352
|
+
@ended_on_newline = false
|
|
353
|
+
end
|
|
354
|
+
|
|
355
|
+
# As tokens are coming in, we track the minimum amount of common leading
|
|
356
|
+
# whitespace on plain string content tokens. This allows us to later
|
|
357
|
+
# remove that amount of whitespace from the beginning of each line.
|
|
358
|
+
#
|
|
359
|
+
#: (lex_compat_token token) -> void
|
|
360
|
+
def <<(token)
|
|
361
|
+
case token[1]
|
|
362
|
+
when :on_embexpr_beg, :on_heredoc_beg
|
|
363
|
+
@embexpr_balance += 1
|
|
364
|
+
@dedent = 0 if @dedent_next && @ended_on_newline
|
|
365
|
+
when :on_embexpr_end, :on_heredoc_end
|
|
366
|
+
@embexpr_balance -= 1
|
|
367
|
+
when :on_tstring_content
|
|
368
|
+
if embexpr_balance == 0
|
|
369
|
+
line = token[2]
|
|
370
|
+
|
|
371
|
+
if dedent_next && !(line.strip.empty? && line.end_with?("\n"))
|
|
372
|
+
leading = line[/\A(\s*)\n?/, 1] #: String
|
|
373
|
+
next_dedent = 0
|
|
374
|
+
|
|
375
|
+
leading.each_char do |char|
|
|
376
|
+
if char == "\t"
|
|
377
|
+
next_dedent = next_dedent - (next_dedent % TAB_WIDTH) + TAB_WIDTH
|
|
378
|
+
else
|
|
379
|
+
next_dedent += 1
|
|
380
|
+
end
|
|
381
|
+
end
|
|
382
|
+
|
|
383
|
+
@dedent = [dedent, next_dedent].compact.min
|
|
384
|
+
@dedent_next = true
|
|
385
|
+
@ended_on_newline = line.end_with?("\n")
|
|
386
|
+
tokens << token
|
|
387
|
+
return
|
|
388
|
+
end
|
|
389
|
+
end
|
|
390
|
+
end
|
|
391
|
+
|
|
392
|
+
@dedent_next = token[1] == :on_tstring_content && embexpr_balance == 0
|
|
393
|
+
@ended_on_newline = false
|
|
394
|
+
tokens << token
|
|
395
|
+
end
|
|
396
|
+
|
|
397
|
+
#: () -> Array[lex_compat_token]
|
|
398
|
+
def to_a
|
|
399
|
+
# If every line in the heredoc is blank, we still need to split up the
|
|
400
|
+
# string content token into multiple tokens.
|
|
401
|
+
if dedent.nil?
|
|
402
|
+
results = [] #: Array[lex_compat_token]
|
|
403
|
+
embexpr_balance = 0
|
|
404
|
+
|
|
405
|
+
tokens.each do |token|
|
|
406
|
+
case token[1]
|
|
407
|
+
when :on_embexpr_beg, :on_heredoc_beg
|
|
408
|
+
embexpr_balance += 1
|
|
409
|
+
results << token
|
|
410
|
+
when :on_embexpr_end, :on_heredoc_end
|
|
411
|
+
embexpr_balance -= 1
|
|
412
|
+
results << token
|
|
413
|
+
when :on_tstring_content
|
|
414
|
+
if embexpr_balance == 0
|
|
415
|
+
lineno = token[0][0]
|
|
416
|
+
column = token[0][1]
|
|
417
|
+
|
|
418
|
+
token[2].split(/(?<=\n)/).each_with_index do |value, index|
|
|
419
|
+
column = 0 if index > 0
|
|
420
|
+
results << [[lineno, column], :on_tstring_content, value, token[3]]
|
|
421
|
+
lineno += 1
|
|
422
|
+
end
|
|
423
|
+
else
|
|
424
|
+
results << token
|
|
425
|
+
end
|
|
426
|
+
else
|
|
427
|
+
results << token
|
|
428
|
+
end
|
|
429
|
+
end
|
|
430
|
+
|
|
431
|
+
return results
|
|
432
|
+
end
|
|
433
|
+
|
|
434
|
+
# If the minimum common whitespace is 0, then we need to concatenate
|
|
435
|
+
# string nodes together that are immediately adjacent.
|
|
436
|
+
if dedent == 0
|
|
437
|
+
results = [] #: Array[lex_compat_token]
|
|
438
|
+
embexpr_balance = 0
|
|
439
|
+
|
|
440
|
+
index = 0
|
|
441
|
+
max_index = tokens.length
|
|
442
|
+
|
|
443
|
+
while index < max_index
|
|
444
|
+
token = tokens[index]
|
|
445
|
+
results << token
|
|
446
|
+
index += 1
|
|
447
|
+
|
|
448
|
+
case token[1]
|
|
449
|
+
when :on_embexpr_beg, :on_heredoc_beg
|
|
450
|
+
embexpr_balance += 1
|
|
451
|
+
when :on_embexpr_end, :on_heredoc_end
|
|
452
|
+
embexpr_balance -= 1
|
|
453
|
+
when :on_tstring_content
|
|
454
|
+
if embexpr_balance == 0
|
|
455
|
+
while index < max_index && tokens[index][1] == :on_tstring_content && !token[2].match?(/\\\r?\n\z/)
|
|
456
|
+
token[2] << tokens[index][2]
|
|
457
|
+
index += 1
|
|
458
|
+
end
|
|
459
|
+
end
|
|
460
|
+
end
|
|
461
|
+
end
|
|
462
|
+
|
|
463
|
+
return results
|
|
464
|
+
end
|
|
465
|
+
|
|
466
|
+
# Otherwise, we're going to run through each token in the list and
|
|
467
|
+
# insert on_ignored_sp tokens for the amount of dedent that we need to
|
|
468
|
+
# perform. We also need to remove the dedent from the beginning of
|
|
469
|
+
# each line of plain string content tokens.
|
|
470
|
+
results = [] #: Array[lex_compat_token]
|
|
471
|
+
dedent_next = true
|
|
472
|
+
embexpr_balance = 0
|
|
473
|
+
|
|
474
|
+
tokens.each do |token|
|
|
475
|
+
# Notice that the structure of this conditional largely matches the
|
|
476
|
+
# whitespace calculation we performed above. This is because
|
|
477
|
+
# checking if the subsequent token needs to be dedented is common to
|
|
478
|
+
# both the dedent calculation and the ignored_sp insertion.
|
|
479
|
+
case token[1]
|
|
480
|
+
when :on_embexpr_beg
|
|
481
|
+
embexpr_balance += 1
|
|
482
|
+
results << token
|
|
483
|
+
when :on_embexpr_end
|
|
484
|
+
embexpr_balance -= 1
|
|
485
|
+
results << token
|
|
486
|
+
when :on_tstring_content
|
|
487
|
+
if embexpr_balance == 0
|
|
488
|
+
# Here we're going to split the string on newlines, but maintain
|
|
489
|
+
# the newlines in the resulting array. We'll do that with a look
|
|
490
|
+
# behind assertion.
|
|
491
|
+
splits = token[2].split(/(?<=\n)/)
|
|
492
|
+
index = 0
|
|
493
|
+
|
|
494
|
+
while index < splits.length
|
|
495
|
+
line = splits[index]
|
|
496
|
+
lineno = token[0][0] + index
|
|
497
|
+
column = token[0][1]
|
|
498
|
+
|
|
499
|
+
# Blank lines do not count toward common leading whitespace
|
|
500
|
+
# calculation and do not need to be dedented.
|
|
501
|
+
if dedent_next || index > 0
|
|
502
|
+
column = 0
|
|
503
|
+
end
|
|
504
|
+
|
|
505
|
+
# If the dedent is 0 and we're not supposed to dedent the next
|
|
506
|
+
# line or this line doesn't start with whitespace, then we
|
|
507
|
+
# should concatenate the rest of the string to match ripper.
|
|
508
|
+
if dedent == 0 && (!dedent_next || !line.start_with?(/\s/))
|
|
509
|
+
unjoined = splits[index..] #: Array[String]
|
|
510
|
+
line = unjoined.join
|
|
511
|
+
index = splits.length
|
|
512
|
+
end
|
|
513
|
+
|
|
514
|
+
# If we are supposed to dedent this line or if this is not the
|
|
515
|
+
# first line of the string and this line isn't entirely blank,
|
|
516
|
+
# then we need to insert an on_ignored_sp token and remove the
|
|
517
|
+
# dedent from the beginning of the line.
|
|
518
|
+
if (dedent > 0) && (dedent_next || index > 0)
|
|
519
|
+
deleting = 0
|
|
520
|
+
deleted_chars = [] #: Array[String]
|
|
521
|
+
|
|
522
|
+
# Gather up all of the characters that we're going to
|
|
523
|
+
# delete, stopping when you hit a character that would put
|
|
524
|
+
# you over the dedent amount.
|
|
525
|
+
line.each_char.with_index do |char, i|
|
|
526
|
+
case char
|
|
527
|
+
when "\r"
|
|
528
|
+
if line[i + 1] == "\n"
|
|
529
|
+
break
|
|
530
|
+
end
|
|
531
|
+
when "\n"
|
|
532
|
+
break
|
|
533
|
+
when "\t"
|
|
534
|
+
deleting = deleting - (deleting % TAB_WIDTH) + TAB_WIDTH
|
|
535
|
+
else
|
|
536
|
+
deleting += 1
|
|
537
|
+
end
|
|
538
|
+
|
|
539
|
+
break if deleting > dedent
|
|
540
|
+
deleted_chars << char
|
|
541
|
+
end
|
|
542
|
+
|
|
543
|
+
# If we have something to delete, then delete it from the
|
|
544
|
+
# string and insert an on_ignored_sp token.
|
|
545
|
+
if deleted_chars.any?
|
|
546
|
+
ignored = deleted_chars.join
|
|
547
|
+
line.delete_prefix!(ignored)
|
|
548
|
+
|
|
549
|
+
results << [[lineno, 0], :on_ignored_sp, ignored, token[3]]
|
|
550
|
+
column = ignored.length
|
|
551
|
+
end
|
|
552
|
+
end
|
|
553
|
+
|
|
554
|
+
results << [[lineno, column], token[1], line, token[3]] unless line.empty?
|
|
555
|
+
index += 1
|
|
556
|
+
end
|
|
557
|
+
else
|
|
558
|
+
results << token
|
|
559
|
+
end
|
|
560
|
+
else
|
|
561
|
+
results << token
|
|
562
|
+
end
|
|
563
|
+
|
|
564
|
+
dedent_next =
|
|
565
|
+
((token[1] == :on_tstring_content) || (token[1] == :on_heredoc_end)) &&
|
|
566
|
+
embexpr_balance == 0
|
|
567
|
+
end
|
|
568
|
+
|
|
569
|
+
results
|
|
570
|
+
end
|
|
571
|
+
end
|
|
572
|
+
|
|
573
|
+
# Here we will split between the two types of heredocs and return the
|
|
574
|
+
# object that will store their tokens.
|
|
575
|
+
#
|
|
576
|
+
#: (lex_compat_token opening) -> (PlainHeredoc | DashHeredoc | DedentingHeredoc)
|
|
577
|
+
def self.build(opening)
|
|
578
|
+
case opening[2][2]
|
|
579
|
+
when "~"
|
|
580
|
+
DedentingHeredoc.new
|
|
581
|
+
when "-"
|
|
582
|
+
DashHeredoc.new(opening[2][3] != "'")
|
|
583
|
+
else
|
|
584
|
+
PlainHeredoc.new
|
|
585
|
+
end
|
|
586
|
+
end
|
|
587
|
+
end
|
|
588
|
+
|
|
589
|
+
private_constant :Heredoc
|
|
590
|
+
|
|
591
|
+
# In previous versions of Ruby, Ripper wouldn't flush the bom before the
|
|
592
|
+
# first token, so we had to have a hack in place to account for that.
|
|
593
|
+
BOM_FLUSHED = RUBY_VERSION >= "3.3.0"
|
|
594
|
+
private_constant :BOM_FLUSHED
|
|
595
|
+
|
|
596
|
+
attr_reader :options #: Hash[Symbol, untyped]
|
|
597
|
+
# @rbs @source: String
|
|
598
|
+
|
|
599
|
+
#: (String source, **untyped options) -> void
|
|
600
|
+
def initialize(source, **options)
|
|
601
|
+
@source = source
|
|
602
|
+
@options = options
|
|
603
|
+
end
|
|
604
|
+
|
|
605
|
+
#: () -> Result
|
|
606
|
+
def result
|
|
607
|
+
tokens = [] #: Array[lex_compat_token]
|
|
608
|
+
|
|
609
|
+
state = :default
|
|
610
|
+
heredoc_stack = [[]] #: Array[Array[Heredoc::PlainHeredoc | Heredoc::DashHeredoc | Heredoc::DedentingHeredoc]]
|
|
611
|
+
|
|
612
|
+
result = Prism.lex(@source, **options)
|
|
613
|
+
source = result.source
|
|
614
|
+
result_value = result.value
|
|
615
|
+
previous_state = nil #: Translation::Ripper::Lexer::State?
|
|
616
|
+
last_heredoc_end = nil #: Integer?
|
|
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
|
|
664
|
+
|
|
665
|
+
# If there's a UTF-8 byte-order mark as the start of the file, then for
|
|
666
|
+
# certain tokens ripper sets the first token back by 3 bytes. It also
|
|
667
|
+
# keeps the byte order mark in the first token's value. This is weird,
|
|
668
|
+
# and I don't want to mirror that in our parser. So instead, we'll match
|
|
669
|
+
# up the columns and values here.
|
|
670
|
+
if bom && lineno == 1
|
|
671
|
+
column -= 3
|
|
672
|
+
|
|
673
|
+
if index == 0 && column == 0 && !BOM_FLUSHED
|
|
674
|
+
flushed =
|
|
675
|
+
case prism_token.type
|
|
676
|
+
when :BACK_REFERENCE, :INSTANCE_VARIABLE, :CLASS_VARIABLE,
|
|
677
|
+
:GLOBAL_VARIABLE, :NUMBERED_REFERENCE, :PERCENT_LOWER_I,
|
|
678
|
+
:PERCENT_LOWER_X, :PERCENT_LOWER_W, :PERCENT_UPPER_I,
|
|
679
|
+
:PERCENT_UPPER_W, :STRING_BEGIN
|
|
680
|
+
true
|
|
681
|
+
when :REGEXP_BEGIN, :SYMBOL_BEGIN
|
|
682
|
+
value.start_with?("%")
|
|
683
|
+
else
|
|
684
|
+
false
|
|
685
|
+
end
|
|
686
|
+
|
|
687
|
+
unless flushed
|
|
688
|
+
column -= 3
|
|
689
|
+
value.prepend(String.new("\xEF\xBB\xBF", encoding: value.encoding))
|
|
690
|
+
end
|
|
691
|
+
end
|
|
692
|
+
end
|
|
693
|
+
|
|
694
|
+
lex_compat_token =
|
|
695
|
+
case event
|
|
696
|
+
when :on___end__
|
|
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]
|
|
701
|
+
when :on_comment
|
|
702
|
+
[[lineno, column], event, value, lex_state]
|
|
703
|
+
when :on_heredoc_end
|
|
704
|
+
# Heredoc end tokens can be emitted in an odd order, so we don't
|
|
705
|
+
# want to bother comparing the state on them.
|
|
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
|
+
when :on_embexpr_end
|
|
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
|
|
724
|
+
when :on_regexp_end
|
|
725
|
+
# On regex end, Ripper scans and then sets end state, so the ripper
|
|
726
|
+
# lexed output is begin, when it should be end. prism sets lex state
|
|
727
|
+
# correctly to end state, but we want to be able to compare against
|
|
728
|
+
# Ripper's lexed state. So here, if it's a regexp end token, we
|
|
729
|
+
# output the state as the previous state, solely for the sake of
|
|
730
|
+
# comparison.
|
|
731
|
+
lex_state =
|
|
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]
|
|
736
|
+
else
|
|
737
|
+
previous_state
|
|
738
|
+
end
|
|
739
|
+
|
|
740
|
+
[[lineno, column], event, value, lex_state]
|
|
741
|
+
when :on_eof
|
|
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
|
+
|
|
771
|
+
tokens << [[lineno, 0], :on_nl, source.slice(start_offset, end_offset - start_offset), lex_state]
|
|
772
|
+
end
|
|
773
|
+
end
|
|
774
|
+
end
|
|
775
|
+
|
|
776
|
+
[[lineno, column], event, value, lex_state]
|
|
777
|
+
else
|
|
778
|
+
[[lineno, column], event, value, lex_state]
|
|
779
|
+
end #: lex_compat_token
|
|
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
|
|
789
|
+
|
|
790
|
+
# The order in which tokens appear in our lexer is different from the
|
|
791
|
+
# order that they appear in Ripper. When we hit the declaration of a
|
|
792
|
+
# heredoc in prism, we skip forward and lex the rest of the content of
|
|
793
|
+
# the heredoc before going back and lexing at the end of the heredoc
|
|
794
|
+
# identifier.
|
|
795
|
+
#
|
|
796
|
+
# To match up to ripper, we keep a small state variable around here to
|
|
797
|
+
# track whether we're in the middle of a heredoc or not. In this way we
|
|
798
|
+
# can shuffle around the token to match Ripper's output.
|
|
799
|
+
case state
|
|
800
|
+
when :default
|
|
801
|
+
# The default state is when there are no heredocs at all. In this
|
|
802
|
+
# state we can append the token to the list of tokens and move on.
|
|
803
|
+
tokens << lex_compat_token
|
|
804
|
+
|
|
805
|
+
# If we get the declaration of a heredoc, then we open a new heredoc
|
|
806
|
+
# and move into the heredoc_opened state.
|
|
807
|
+
if event == :on_heredoc_beg
|
|
808
|
+
state = :heredoc_opened
|
|
809
|
+
heredoc_stack.last << Heredoc.build(lex_compat_token)
|
|
810
|
+
end
|
|
811
|
+
when :heredoc_opened
|
|
812
|
+
# The heredoc_opened state is when we've seen the declaration of a
|
|
813
|
+
# heredoc and are now lexing the body of the heredoc. In this state we
|
|
814
|
+
# push tokens onto the most recently created heredoc.
|
|
815
|
+
heredoc_stack.last.last << lex_compat_token
|
|
816
|
+
|
|
817
|
+
case event
|
|
818
|
+
when :on_heredoc_beg
|
|
819
|
+
# If we receive a heredoc declaration while lexing the body of a
|
|
820
|
+
# heredoc, this means we have nested heredocs. In this case we'll
|
|
821
|
+
# push a new heredoc onto the stack and stay in the heredoc_opened
|
|
822
|
+
# state since we're now lexing the body of the new heredoc.
|
|
823
|
+
heredoc_stack << [Heredoc.build(lex_compat_token)]
|
|
824
|
+
when :on_heredoc_end
|
|
825
|
+
# If we receive the end of a heredoc, then we're done lexing the
|
|
826
|
+
# body of the heredoc. In this case we now have a completed heredoc
|
|
827
|
+
# but need to wait for the next newline to push it into the token
|
|
828
|
+
# stream.
|
|
829
|
+
state = :heredoc_closed
|
|
830
|
+
end
|
|
831
|
+
when :heredoc_closed
|
|
832
|
+
if %i[on_nl on_ignored_nl on_comment].include?(event) || ((event == :on_tstring_content) && value.end_with?("\n"))
|
|
833
|
+
if heredoc_stack.size > 1
|
|
834
|
+
flushing = heredoc_stack.pop #: Array[Heredoc::PlainHeredoc | Heredoc::DashHeredoc | Heredoc::DedentingHeredoc]
|
|
835
|
+
heredoc_stack.last.last << lex_compat_token
|
|
836
|
+
|
|
837
|
+
flushing.each do |heredoc|
|
|
838
|
+
heredoc.to_a.each do |flushed_token|
|
|
839
|
+
heredoc_stack.last.last << flushed_token
|
|
840
|
+
end
|
|
841
|
+
end
|
|
842
|
+
|
|
843
|
+
state = :heredoc_opened
|
|
844
|
+
next
|
|
845
|
+
end
|
|
846
|
+
elsif event == :on_heredoc_beg
|
|
847
|
+
tokens << lex_compat_token
|
|
848
|
+
state = :heredoc_opened
|
|
849
|
+
heredoc_stack.last << Heredoc.build(lex_compat_token)
|
|
850
|
+
next
|
|
851
|
+
elsif heredoc_stack.size > 1
|
|
852
|
+
heredoc_stack[-2].last << lex_compat_token
|
|
853
|
+
next
|
|
854
|
+
end
|
|
855
|
+
|
|
856
|
+
heredoc_stack.last.each do |heredoc|
|
|
857
|
+
tokens.concat(heredoc.to_a)
|
|
858
|
+
end
|
|
859
|
+
|
|
860
|
+
heredoc_stack.last.clear
|
|
861
|
+
state = :default
|
|
862
|
+
|
|
863
|
+
tokens << lex_compat_token
|
|
864
|
+
end
|
|
865
|
+
end
|
|
866
|
+
|
|
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)
|
|
880
|
+
|
|
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
|
|
947
|
+
|
|
948
|
+
new_tokens
|
|
949
|
+
end
|
|
950
|
+
end
|
|
951
|
+
|
|
952
|
+
private_constant :LexCompat
|
|
953
|
+
end
|