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,1783 @@
|
|
|
1
|
+
#include "prism/extension.h"
|
|
2
|
+
|
|
3
|
+
#ifdef _WIN32
|
|
4
|
+
#include <ruby/win32.h>
|
|
5
|
+
#endif
|
|
6
|
+
|
|
7
|
+
#include <errno.h>
|
|
8
|
+
|
|
9
|
+
// NOTE: this file should contain only bindings. All non-trivial logic should be
|
|
10
|
+
// in libprism so it can be shared its the various callers.
|
|
11
|
+
|
|
12
|
+
VALUE rb_cPrism;
|
|
13
|
+
VALUE rb_cPrismNode;
|
|
14
|
+
VALUE rb_cPrismSource;
|
|
15
|
+
VALUE rb_cPrismToken;
|
|
16
|
+
VALUE rb_cPrismLocation;
|
|
17
|
+
|
|
18
|
+
VALUE rb_cPrismComment;
|
|
19
|
+
VALUE rb_cPrismInlineComment;
|
|
20
|
+
VALUE rb_cPrismEmbDocComment;
|
|
21
|
+
VALUE rb_cPrismMagicComment;
|
|
22
|
+
VALUE rb_cPrismParseError;
|
|
23
|
+
VALUE rb_cPrismParseWarning;
|
|
24
|
+
VALUE rb_cPrismResult;
|
|
25
|
+
VALUE rb_cPrismParseResult;
|
|
26
|
+
VALUE rb_cPrismLexResult;
|
|
27
|
+
VALUE rb_cPrismParseLexResult;
|
|
28
|
+
VALUE rb_cPrismStringQuery;
|
|
29
|
+
VALUE rb_cPrismScope;
|
|
30
|
+
VALUE rb_cPrismCurrentVersionError;
|
|
31
|
+
|
|
32
|
+
VALUE rb_cPrismDebugEncoding;
|
|
33
|
+
|
|
34
|
+
ID rb_id_option_attach_comments;
|
|
35
|
+
ID rb_id_option_command_line;
|
|
36
|
+
ID rb_id_option_encoding;
|
|
37
|
+
ID rb_id_option_filepath;
|
|
38
|
+
ID rb_id_option_freeze;
|
|
39
|
+
ID rb_id_option_frozen_string_literal;
|
|
40
|
+
ID rb_id_option_line;
|
|
41
|
+
ID rb_id_option_main_script;
|
|
42
|
+
ID rb_id_option_partial_script;
|
|
43
|
+
ID rb_id_option_raise_error;
|
|
44
|
+
ID rb_id_option_scopes;
|
|
45
|
+
ID rb_id_option_version;
|
|
46
|
+
|
|
47
|
+
ID rb_id_source_for;
|
|
48
|
+
|
|
49
|
+
ID rb_id_forwarding_positionals;
|
|
50
|
+
ID rb_id_forwarding_keywords;
|
|
51
|
+
ID rb_id_forwarding_block;
|
|
52
|
+
ID rb_id_forwarding_all;
|
|
53
|
+
|
|
54
|
+
ID rb_id_raise_error_plain;
|
|
55
|
+
ID rb_id_raise_error_style;
|
|
56
|
+
ID rb_id_raise_error_color;
|
|
57
|
+
|
|
58
|
+
/******************************************************************************/
|
|
59
|
+
/* Result struct for working with functions that may need to raise errors. */
|
|
60
|
+
/******************************************************************************/
|
|
61
|
+
|
|
62
|
+
typedef struct {
|
|
63
|
+
enum {
|
|
64
|
+
RESULT_OK,
|
|
65
|
+
RESULT_ERR
|
|
66
|
+
} type;
|
|
67
|
+
VALUE value;
|
|
68
|
+
} result_t;
|
|
69
|
+
|
|
70
|
+
static result_t
|
|
71
|
+
result_ok(VALUE value) {
|
|
72
|
+
return (result_t) { .type = RESULT_OK, .value = value };
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
static result_t
|
|
76
|
+
result_err(VALUE value) {
|
|
77
|
+
return (result_t) { .type = RESULT_ERR, .value = value };
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
static VALUE
|
|
81
|
+
result_get(result_t result) {
|
|
82
|
+
if (result.type == RESULT_OK) {
|
|
83
|
+
return result.value;
|
|
84
|
+
} else {
|
|
85
|
+
rb_exc_raise(result.value);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/******************************************************************************/
|
|
90
|
+
/* IO of Ruby code */
|
|
91
|
+
/******************************************************************************/
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Check if the given VALUE is a string. If it's not a string, then raise a
|
|
95
|
+
* TypeError. Otherwise return the VALUE as a C string.
|
|
96
|
+
*/
|
|
97
|
+
static const char *
|
|
98
|
+
check_string(VALUE value) {
|
|
99
|
+
// Check if the value is a string. If it's not, then raise a type error.
|
|
100
|
+
if (!RB_TYPE_P(value, T_STRING)) {
|
|
101
|
+
rb_raise(rb_eTypeError, "wrong argument type %" PRIsVALUE " (expected String)", rb_obj_class(value));
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Otherwise, return the value as a C string.
|
|
105
|
+
return RSTRING_PTR(value);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/******************************************************************************/
|
|
109
|
+
/* Building C options from Ruby options */
|
|
110
|
+
/******************************************************************************/
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Build the scopes associated with the provided Ruby keyword value.
|
|
114
|
+
*/
|
|
115
|
+
static void
|
|
116
|
+
build_options_scopes(pm_options_t *options, VALUE scopes) {
|
|
117
|
+
// Check if the value is an array. If it's not, then raise a type error.
|
|
118
|
+
if (!RB_TYPE_P(scopes, T_ARRAY)) {
|
|
119
|
+
rb_raise(rb_eTypeError, "wrong argument type %"PRIsVALUE" (expected Array)", rb_obj_class(scopes));
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Initialize the scopes array.
|
|
123
|
+
size_t scopes_count = RARRAY_LEN(scopes);
|
|
124
|
+
if (!pm_options_scopes_init(options, scopes_count)) {
|
|
125
|
+
rb_raise(rb_eNoMemError, "failed to allocate memory");
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Iterate over the scopes and add them to the options.
|
|
129
|
+
for (size_t scope_index = 0; scope_index < scopes_count; scope_index++) {
|
|
130
|
+
VALUE scope = rb_ary_entry(scopes, scope_index);
|
|
131
|
+
|
|
132
|
+
// The scope can be either an array or it can be a Prism::Scope object.
|
|
133
|
+
// Parse out the correct values here from either.
|
|
134
|
+
VALUE locals;
|
|
135
|
+
uint8_t forwarding = PM_OPTIONS_SCOPE_FORWARDING_NONE;
|
|
136
|
+
|
|
137
|
+
if (RB_TYPE_P(scope, T_ARRAY)) {
|
|
138
|
+
locals = scope;
|
|
139
|
+
} else if (rb_obj_is_kind_of(scope, rb_cPrismScope)) {
|
|
140
|
+
locals = rb_ivar_get(scope, rb_intern("@locals"));
|
|
141
|
+
if (!RB_TYPE_P(locals, T_ARRAY)) {
|
|
142
|
+
rb_raise(rb_eTypeError, "wrong argument type %"PRIsVALUE" (expected Array)", rb_obj_class(locals));
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
VALUE names = rb_ivar_get(scope, rb_intern("@forwarding"));
|
|
146
|
+
if (!RB_TYPE_P(names, T_ARRAY)) {
|
|
147
|
+
rb_raise(rb_eTypeError, "wrong argument type %"PRIsVALUE" (expected Array)", rb_obj_class(names));
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
size_t names_count = RARRAY_LEN(names);
|
|
151
|
+
for (size_t name_index = 0; name_index < names_count; name_index++) {
|
|
152
|
+
VALUE name = rb_ary_entry(names, name_index);
|
|
153
|
+
|
|
154
|
+
// Check that the name is a symbol. If it's not, then raise
|
|
155
|
+
// a type error.
|
|
156
|
+
if (!RB_TYPE_P(name, T_SYMBOL)) {
|
|
157
|
+
rb_raise(rb_eTypeError, "wrong argument type %"PRIsVALUE" (expected Symbol)", rb_obj_class(name));
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
ID id = SYM2ID(name);
|
|
161
|
+
if (id == rb_id_forwarding_positionals) {
|
|
162
|
+
forwarding |= PM_OPTIONS_SCOPE_FORWARDING_POSITIONALS;
|
|
163
|
+
} else if (id == rb_id_forwarding_keywords) {
|
|
164
|
+
forwarding |= PM_OPTIONS_SCOPE_FORWARDING_KEYWORDS;
|
|
165
|
+
} else if (id == rb_id_forwarding_block) {
|
|
166
|
+
forwarding |= PM_OPTIONS_SCOPE_FORWARDING_BLOCK;
|
|
167
|
+
} else if (id == rb_id_forwarding_all) {
|
|
168
|
+
forwarding |= PM_OPTIONS_SCOPE_FORWARDING_ALL;
|
|
169
|
+
} else {
|
|
170
|
+
rb_raise(rb_eArgError, "invalid forwarding value: %" PRIsVALUE, name);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
} else {
|
|
174
|
+
rb_raise(rb_eTypeError, "wrong argument type %"PRIsVALUE" (expected Array or Prism::Scope)", rb_obj_class(scope));
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Initialize the scope array.
|
|
178
|
+
size_t locals_count = RARRAY_LEN(locals);
|
|
179
|
+
pm_options_scope_t *options_scope = pm_options_scope_mut(options, scope_index);
|
|
180
|
+
pm_options_scope_init(options_scope, locals_count);
|
|
181
|
+
|
|
182
|
+
// Iterate over the locals and add them to the scope.
|
|
183
|
+
for (size_t local_index = 0; local_index < locals_count; local_index++) {
|
|
184
|
+
VALUE local = rb_ary_entry(locals, local_index);
|
|
185
|
+
|
|
186
|
+
// Check that the local is a symbol. If it's not, then raise a
|
|
187
|
+
// type error.
|
|
188
|
+
if (!RB_TYPE_P(local, T_SYMBOL)) {
|
|
189
|
+
rb_raise(rb_eTypeError, "wrong argument type %"PRIsVALUE" (expected Symbol)", rb_obj_class(local));
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// Add the local to the scope.
|
|
193
|
+
pm_string_t *scope_local = pm_options_scope_local_mut(options_scope, local_index);
|
|
194
|
+
const char *name = rb_id2name(SYM2ID(local));
|
|
195
|
+
pm_string_constant_init(scope_local, name, strlen(name));
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// Now set the forwarding options.
|
|
199
|
+
pm_options_scope_forwarding_set(options_scope, forwarding);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Set the version option to the syntax version of the currently running Ruby,
|
|
205
|
+
* or to the closest version prism supports if it does not know that one.
|
|
206
|
+
*/
|
|
207
|
+
static void
|
|
208
|
+
options_version_set_nearest(pm_options_t *options) {
|
|
209
|
+
if (!pm_options_version_set(options, ruby_version, 3)) {
|
|
210
|
+
// Prism doesn't know this specific version. Is it lower?
|
|
211
|
+
if (ruby_version[0] < '3' || (ruby_version[0] == '3' && ruby_version[2] < '3')) {
|
|
212
|
+
pm_options_version_set_lowest(options);
|
|
213
|
+
} else {
|
|
214
|
+
// Must be higher.
|
|
215
|
+
pm_options_version_set_highest(options);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* An iterator function that is called for each key-value in the keywords hash.
|
|
222
|
+
*/
|
|
223
|
+
static int
|
|
224
|
+
build_options_i(VALUE key, VALUE value, VALUE argument) {
|
|
225
|
+
pm_options_t *options = (pm_options_t *) argument;
|
|
226
|
+
ID key_id = SYM2ID(key);
|
|
227
|
+
|
|
228
|
+
if (key_id == rb_id_option_filepath) {
|
|
229
|
+
if (!NIL_P(value)) pm_options_filepath_set(options, check_string(value));
|
|
230
|
+
} else if (key_id == rb_id_option_encoding) {
|
|
231
|
+
if (!NIL_P(value)) {
|
|
232
|
+
if (value == Qfalse) {
|
|
233
|
+
pm_options_encoding_locked_set(options, true);
|
|
234
|
+
} else {
|
|
235
|
+
pm_options_encoding_set(options, rb_enc_name(rb_to_encoding(value)));
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
} else if (key_id == rb_id_option_line) {
|
|
239
|
+
if (!NIL_P(value)) pm_options_line_set(options, NUM2INT(value));
|
|
240
|
+
} else if (key_id == rb_id_option_frozen_string_literal) {
|
|
241
|
+
if (!NIL_P(value)) pm_options_frozen_string_literal_set(options, RTEST(value));
|
|
242
|
+
} else if (key_id == rb_id_option_version) {
|
|
243
|
+
if (!NIL_P(value)) {
|
|
244
|
+
const char *version = check_string(value);
|
|
245
|
+
|
|
246
|
+
if (RSTRING_LEN(value) == 7 && strncmp(version, "current", 7) == 0) {
|
|
247
|
+
if (!pm_options_version_set(options, ruby_version, 3)) {
|
|
248
|
+
rb_exc_raise(rb_exc_new_cstr(rb_cPrismCurrentVersionError, ruby_version));
|
|
249
|
+
}
|
|
250
|
+
} else if (RSTRING_LEN(value) == 7 && strncmp(version, "nearest", 7) == 0) {
|
|
251
|
+
options_version_set_nearest(options);
|
|
252
|
+
} else if (!pm_options_version_set(options, version, RSTRING_LEN(value))) {
|
|
253
|
+
rb_raise(rb_eArgError, "invalid version: %" PRIsVALUE, value);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
} else if (key_id == rb_id_option_scopes) {
|
|
257
|
+
if (!NIL_P(value)) build_options_scopes(options, value);
|
|
258
|
+
} else if (key_id == rb_id_option_command_line) {
|
|
259
|
+
if (!NIL_P(value)) {
|
|
260
|
+
const char *string = check_string(value);
|
|
261
|
+
uint8_t command_line = 0;
|
|
262
|
+
|
|
263
|
+
for (size_t index = 0; index < strlen(string); index++) {
|
|
264
|
+
switch (string[index]) {
|
|
265
|
+
case 'a': command_line |= PM_OPTIONS_COMMAND_LINE_A; break;
|
|
266
|
+
case 'e': command_line |= PM_OPTIONS_COMMAND_LINE_E; break;
|
|
267
|
+
case 'l': command_line |= PM_OPTIONS_COMMAND_LINE_L; break;
|
|
268
|
+
case 'n': command_line |= PM_OPTIONS_COMMAND_LINE_N; break;
|
|
269
|
+
case 'p': command_line |= PM_OPTIONS_COMMAND_LINE_P; break;
|
|
270
|
+
case 'x': command_line |= PM_OPTIONS_COMMAND_LINE_X; break;
|
|
271
|
+
default: rb_raise(rb_eArgError, "invalid command line flag: '%c'", string[index]); break;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
pm_options_command_line_set(options, command_line);
|
|
276
|
+
}
|
|
277
|
+
} else if (key_id == rb_id_option_main_script) {
|
|
278
|
+
if (!NIL_P(value)) pm_options_main_script_set(options, RTEST(value));
|
|
279
|
+
} else if (key_id == rb_id_option_partial_script) {
|
|
280
|
+
if (!NIL_P(value)) pm_options_partial_script_set(options, RTEST(value));
|
|
281
|
+
} else if (key_id == rb_id_option_freeze) {
|
|
282
|
+
if (!NIL_P(value)) pm_options_freeze_set(options, RTEST(value));
|
|
283
|
+
} else if (key_id == rb_id_option_attach_comments) {
|
|
284
|
+
if (!NIL_P(value)) pm_options_attach_comments_set(options, RTEST(value));
|
|
285
|
+
} else if (key_id == rb_id_option_raise_error) {
|
|
286
|
+
if (!NIL_P(value)) {
|
|
287
|
+
if (value == Qtrue) {
|
|
288
|
+
/* When given true, color when $stderr is a terminal (unless
|
|
289
|
+
* NO_COLOR is set), bold styling when NO_COLOR is set, plain
|
|
290
|
+
* otherwise.
|
|
291
|
+
*
|
|
292
|
+
* rb_stderr_tty_p is not available to extensions, so instead
|
|
293
|
+
* we call the tty? method on $stderr, guarding against it
|
|
294
|
+
* having been replaced by an object that does not respond to
|
|
295
|
+
* tty?. */
|
|
296
|
+
if (rb_respond_to(rb_stderr, rb_intern_const("tty?")) && RTEST(rb_funcall(rb_stderr, rb_intern_const("tty?"), 0))) {
|
|
297
|
+
const char *no_color = getenv("NO_COLOR");
|
|
298
|
+
if (no_color == NULL || no_color[0] == '\0') {
|
|
299
|
+
pm_options_raise_error_set(options, (uint8_t) PM_ERRORS_FORMAT_COLOR);
|
|
300
|
+
} else {
|
|
301
|
+
pm_options_raise_error_set(options, (uint8_t) PM_ERRORS_FORMAT_STYLE);
|
|
302
|
+
}
|
|
303
|
+
} else {
|
|
304
|
+
pm_options_raise_error_set(options, (uint8_t) PM_ERRORS_FORMAT_PLAIN);
|
|
305
|
+
}
|
|
306
|
+
} else {
|
|
307
|
+
if (!SYMBOL_P(value)) {
|
|
308
|
+
rb_raise(rb_eTypeError, "wrong argument type %"PRIsVALUE" (expected Symbol)", rb_obj_class(value));
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
ID value_id = SYM2ID(value);
|
|
312
|
+
if (value_id == rb_id_raise_error_plain) {
|
|
313
|
+
pm_options_raise_error_set(options, (uint8_t) PM_ERRORS_FORMAT_PLAIN);
|
|
314
|
+
} else if (value_id == rb_id_raise_error_style) {
|
|
315
|
+
pm_options_raise_error_set(options, (uint8_t) PM_ERRORS_FORMAT_STYLE);
|
|
316
|
+
} else if (value_id == rb_id_raise_error_color) {
|
|
317
|
+
pm_options_raise_error_set(options, (uint8_t) PM_ERRORS_FORMAT_COLOR);
|
|
318
|
+
} else {
|
|
319
|
+
rb_raise(rb_eArgError, "invalid raise_error value: %" PRIsVALUE, value);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
} else {
|
|
324
|
+
rb_raise(rb_eArgError, "unknown keyword: %" PRIsVALUE, key);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
return ST_CONTINUE;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* We need a struct here to pass through rb_protect and it has to be a single
|
|
332
|
+
* value. Because the sizeof(VALUE) == sizeof(void *), we're going to pass this
|
|
333
|
+
* through as an opaque pointer and cast it on both sides.
|
|
334
|
+
*/
|
|
335
|
+
struct build_options_data {
|
|
336
|
+
pm_options_t *options;
|
|
337
|
+
VALUE keywords;
|
|
338
|
+
};
|
|
339
|
+
|
|
340
|
+
/**
|
|
341
|
+
* Build the set of options from the given keywords. Note that this can raise a
|
|
342
|
+
* Ruby error if the options are not valid.
|
|
343
|
+
*/
|
|
344
|
+
static VALUE
|
|
345
|
+
build_options(VALUE argument) {
|
|
346
|
+
struct build_options_data *data = (struct build_options_data *) argument;
|
|
347
|
+
rb_hash_foreach(data->keywords, build_options_i, (VALUE) data->options);
|
|
348
|
+
return Qnil;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
/**
|
|
352
|
+
* Extract the options from the given keyword arguments.
|
|
353
|
+
*/
|
|
354
|
+
static void
|
|
355
|
+
extract_options(pm_options_t *options, VALUE filepath, VALUE keywords) {
|
|
356
|
+
pm_options_line_set(options, 1); /* default */
|
|
357
|
+
options_version_set_nearest(options); /* default */
|
|
358
|
+
|
|
359
|
+
if (!NIL_P(keywords)) {
|
|
360
|
+
struct build_options_data data = { .options = options, .keywords = keywords };
|
|
361
|
+
struct build_options_data *argument = &data;
|
|
362
|
+
|
|
363
|
+
int state = 0;
|
|
364
|
+
rb_protect(build_options, (VALUE) argument, &state);
|
|
365
|
+
|
|
366
|
+
if (state != 0) {
|
|
367
|
+
pm_options_free(options);
|
|
368
|
+
rb_jump_tag(state);
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
if (!NIL_P(filepath)) {
|
|
373
|
+
if (!RB_TYPE_P(filepath, T_STRING)) {
|
|
374
|
+
pm_options_free(options);
|
|
375
|
+
rb_raise(rb_eTypeError, "wrong argument type %"PRIsVALUE" (expected String)", rb_obj_class(filepath));
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
pm_options_filepath_set(options, RSTRING_PTR(filepath));
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
* Read options for methods that look like (source, **options).
|
|
384
|
+
*/
|
|
385
|
+
static VALUE
|
|
386
|
+
string_options(int argc, VALUE *argv, pm_options_t *options) {
|
|
387
|
+
VALUE string;
|
|
388
|
+
VALUE keywords;
|
|
389
|
+
rb_scan_args(argc, argv, "1:", &string, &keywords);
|
|
390
|
+
|
|
391
|
+
if (!RB_TYPE_P(string, T_STRING)) {
|
|
392
|
+
pm_options_free(options);
|
|
393
|
+
rb_raise(rb_eTypeError, "wrong argument type %"PRIsVALUE" (expected String)", rb_obj_class(string));
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
extract_options(options, Qnil, keywords);
|
|
397
|
+
return string;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
/**
|
|
401
|
+
* Read options for methods that look like (filepath, **options).
|
|
402
|
+
*/
|
|
403
|
+
static pm_source_t *
|
|
404
|
+
file_options(int argc, VALUE *argv, pm_options_t *options, VALUE *encoded_filepath) {
|
|
405
|
+
VALUE filepath;
|
|
406
|
+
VALUE keywords;
|
|
407
|
+
rb_scan_args(argc, argv, "1:", &filepath, &keywords);
|
|
408
|
+
|
|
409
|
+
int state = 0;
|
|
410
|
+
filepath = rb_protect(rb_get_path, filepath, &state);
|
|
411
|
+
if (state != 0) {
|
|
412
|
+
pm_options_free(options);
|
|
413
|
+
rb_jump_tag(state);
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
*encoded_filepath = rb_str_encode_ospath(filepath);
|
|
417
|
+
extract_options(options, *encoded_filepath, keywords);
|
|
418
|
+
|
|
419
|
+
const char *source = (const char *) pm_string_source(pm_options_filepath(options));
|
|
420
|
+
pm_source_init_result_t result;
|
|
421
|
+
pm_source_t *pm_src = pm_source_file_new(source, &result);
|
|
422
|
+
|
|
423
|
+
switch (result) {
|
|
424
|
+
case PM_SOURCE_INIT_SUCCESS:
|
|
425
|
+
break;
|
|
426
|
+
case PM_SOURCE_INIT_ERROR_GENERIC: {
|
|
427
|
+
pm_options_free(options);
|
|
428
|
+
|
|
429
|
+
#ifdef _WIN32
|
|
430
|
+
int e = rb_w32_map_errno(GetLastError());
|
|
431
|
+
#else
|
|
432
|
+
int e = errno;
|
|
433
|
+
#endif
|
|
434
|
+
|
|
435
|
+
rb_syserr_fail(e, source);
|
|
436
|
+
break;
|
|
437
|
+
}
|
|
438
|
+
case PM_SOURCE_INIT_ERROR_DIRECTORY:
|
|
439
|
+
pm_options_free(options);
|
|
440
|
+
rb_syserr_fail(EISDIR, source);
|
|
441
|
+
break;
|
|
442
|
+
default:
|
|
443
|
+
pm_options_free(options);
|
|
444
|
+
rb_raise(rb_eRuntimeError, "Unknown error (%d) initializing file: %s", result, source);
|
|
445
|
+
break;
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
return pm_src;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
/**
|
|
452
|
+
* Check if the given parser has any errors and the raise option was set. If
|
|
453
|
+
* both are true, format them and return the formatted error wrapped in a
|
|
454
|
+
* result.
|
|
455
|
+
*/
|
|
456
|
+
static result_t
|
|
457
|
+
check_raise_error_option(pm_parser_t *parser, const pm_options_t *options, rb_encoding *path_encoding) {
|
|
458
|
+
if (pm_parser_errors_size(parser) == 0) return result_ok(Qnil);
|
|
459
|
+
|
|
460
|
+
uint8_t raise_error = pm_options_raise_error(options);
|
|
461
|
+
if (raise_error == 0) return result_ok(Qnil);
|
|
462
|
+
|
|
463
|
+
pm_buffer_t *buffer = pm_buffer_new();
|
|
464
|
+
if (buffer == NULL) {
|
|
465
|
+
return result_err(rb_exc_new_cstr(rb_eNoMemError, "failed to allocate memory"));
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
pm_error_level_t error_level = pm_errors_format(parser, buffer, (pm_errors_format_type_t) raise_error);
|
|
469
|
+
|
|
470
|
+
/* Copy the message out of the buffer and free it before building any
|
|
471
|
+
* other Ruby objects so that the buffer cannot be leaked if building the
|
|
472
|
+
* objects raises. */
|
|
473
|
+
rb_encoding *message_encoding = (error_level == PM_ERROR_LEVEL_LOAD) ? rb_locale_encoding() : rb_enc_find(pm_parser_encoding_name(parser));
|
|
474
|
+
VALUE message = rb_enc_str_new(pm_buffer_value(buffer), (long) pm_buffer_length(buffer), message_encoding);
|
|
475
|
+
pm_buffer_free(buffer);
|
|
476
|
+
|
|
477
|
+
VALUE error = Qnil;
|
|
478
|
+
switch (error_level) {
|
|
479
|
+
case PM_ERROR_LEVEL_SYNTAX: {
|
|
480
|
+
error = rb_exc_new_str(rb_eSyntaxError, message);
|
|
481
|
+
|
|
482
|
+
rb_encoding *path_encoding_normal = path_encoding == NULL ? rb_utf8_encoding() : path_encoding;
|
|
483
|
+
VALUE path = rb_enc_str_new((const char *) pm_string_source(pm_options_filepath(options)), pm_string_length(pm_options_filepath(options)), path_encoding_normal);
|
|
484
|
+
rb_ivar_set(error, rb_intern_const("@path"), path);
|
|
485
|
+
break;
|
|
486
|
+
}
|
|
487
|
+
case PM_ERROR_LEVEL_ARGUMENT: {
|
|
488
|
+
error = rb_exc_new_str(rb_eArgError, message);
|
|
489
|
+
break;
|
|
490
|
+
}
|
|
491
|
+
case PM_ERROR_LEVEL_LOAD: {
|
|
492
|
+
error = rb_exc_new_str(rb_eLoadError, message);
|
|
493
|
+
rb_ivar_set(error, rb_intern_const("@path"), Qnil);
|
|
494
|
+
break;
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
return result_err(error);
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
#ifndef PRISM_EXCLUDE_SERIALIZATION
|
|
502
|
+
|
|
503
|
+
/******************************************************************************/
|
|
504
|
+
/* Serializing the AST */
|
|
505
|
+
/******************************************************************************/
|
|
506
|
+
|
|
507
|
+
/**
|
|
508
|
+
* Dump the AST corresponding to the given input to a string.
|
|
509
|
+
*/
|
|
510
|
+
static result_t
|
|
511
|
+
dump_input(const uint8_t *input, size_t input_length, const pm_options_t *options, rb_encoding *path_encoding) {
|
|
512
|
+
pm_arena_t *arena = pm_arena_new();
|
|
513
|
+
pm_parser_t *parser = pm_parser_new(arena, input, input_length, options);
|
|
514
|
+
pm_node_t *node = pm_parse(parser);
|
|
515
|
+
|
|
516
|
+
result_t result = check_raise_error_option(parser, options, path_encoding);
|
|
517
|
+
if (result.type == RESULT_OK) {
|
|
518
|
+
pm_buffer_t *buffer = pm_buffer_new();
|
|
519
|
+
if (buffer) {
|
|
520
|
+
pm_serialize(parser, node, buffer);
|
|
521
|
+
result = result_ok(rb_str_new(pm_buffer_value(buffer), pm_buffer_length(buffer)));
|
|
522
|
+
pm_buffer_free(buffer);
|
|
523
|
+
|
|
524
|
+
if (pm_options_freeze(options)) rb_obj_freeze(result.value);
|
|
525
|
+
} else {
|
|
526
|
+
result = result_err(rb_exc_new_cstr(rb_eNoMemError, "failed to allocate memory"));
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
pm_parser_free(parser);
|
|
531
|
+
pm_arena_free(arena);
|
|
532
|
+
|
|
533
|
+
return result;
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
/**
|
|
537
|
+
* :markup: markdown
|
|
538
|
+
* call-seq:
|
|
539
|
+
* dump(source, **options) -> String
|
|
540
|
+
*
|
|
541
|
+
* Dump the AST corresponding to the given string to a string. For supported
|
|
542
|
+
* options, see Prism.parse.
|
|
543
|
+
*/
|
|
544
|
+
static VALUE
|
|
545
|
+
dump(int argc, VALUE *argv, VALUE self) {
|
|
546
|
+
pm_options_t *options = pm_options_new();
|
|
547
|
+
VALUE string = string_options(argc, argv, options);
|
|
548
|
+
|
|
549
|
+
const uint8_t *source = (const uint8_t *) RSTRING_PTR(string);
|
|
550
|
+
size_t length = RSTRING_LEN(string);
|
|
551
|
+
|
|
552
|
+
#ifdef PRISM_BUILD_DEBUG
|
|
553
|
+
char* dup = xmalloc(length);
|
|
554
|
+
memcpy(dup, source, length);
|
|
555
|
+
source = (const uint8_t *) dup;
|
|
556
|
+
#endif
|
|
557
|
+
|
|
558
|
+
result_t result = dump_input(source, length, options, NULL);
|
|
559
|
+
|
|
560
|
+
#ifdef PRISM_BUILD_DEBUG
|
|
561
|
+
#ifdef xfree_sized
|
|
562
|
+
xfree_sized(dup, length);
|
|
563
|
+
#else
|
|
564
|
+
xfree(dup);
|
|
565
|
+
#endif
|
|
566
|
+
#endif
|
|
567
|
+
|
|
568
|
+
pm_options_free(options);
|
|
569
|
+
return result_get(result);
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
/**
|
|
573
|
+
* :markup: markdown
|
|
574
|
+
* call-seq:
|
|
575
|
+
* dump_file(filepath, **options) -> String
|
|
576
|
+
*
|
|
577
|
+
* Dump the AST corresponding to the given file to a string. For supported
|
|
578
|
+
* options, see Prism.parse.
|
|
579
|
+
*/
|
|
580
|
+
static VALUE
|
|
581
|
+
dump_file(int argc, VALUE *argv, VALUE self) {
|
|
582
|
+
pm_options_t *options = pm_options_new();
|
|
583
|
+
|
|
584
|
+
VALUE encoded_filepath;
|
|
585
|
+
pm_source_t *src = file_options(argc, argv, options, &encoded_filepath);
|
|
586
|
+
|
|
587
|
+
result_t result = dump_input(pm_source_source(src), pm_source_length(src), options, rb_enc_get(encoded_filepath));
|
|
588
|
+
pm_source_free(src);
|
|
589
|
+
pm_options_free(options);
|
|
590
|
+
|
|
591
|
+
return result_get(result);
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
#endif
|
|
595
|
+
|
|
596
|
+
/******************************************************************************/
|
|
597
|
+
/* Extracting values for the parse result */
|
|
598
|
+
/******************************************************************************/
|
|
599
|
+
|
|
600
|
+
/**
|
|
601
|
+
* The same as rb_class_new_instance, but accepts an additional boolean to
|
|
602
|
+
* indicate whether or not the resulting class instance should be frozen.
|
|
603
|
+
*/
|
|
604
|
+
static inline VALUE
|
|
605
|
+
rb_class_new_instance_freeze(int argc, const VALUE *argv, VALUE klass, bool freeze) {
|
|
606
|
+
VALUE value = rb_class_new_instance(argc, argv, klass);
|
|
607
|
+
if (freeze) rb_obj_freeze(value);
|
|
608
|
+
return value;
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
/**
|
|
612
|
+
* Create a new Location instance from the given parser and bounds.
|
|
613
|
+
*/
|
|
614
|
+
static inline VALUE
|
|
615
|
+
parser_location(VALUE source, bool freeze, uint32_t start, uint32_t length) {
|
|
616
|
+
VALUE argv[] = { source, LONG2FIX(start), LONG2FIX(length) };
|
|
617
|
+
return rb_class_new_instance_freeze(3, argv, rb_cPrismLocation, freeze);
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
/**
|
|
621
|
+
* Create a new Location instance from the given parser and location.
|
|
622
|
+
*/
|
|
623
|
+
#define PARSER_LOCATION(source, freeze, location) \
|
|
624
|
+
parser_location(source, freeze, location.start, location.length)
|
|
625
|
+
|
|
626
|
+
/**
|
|
627
|
+
* Build a new Comment instance from the given parser and comment.
|
|
628
|
+
*/
|
|
629
|
+
static inline VALUE
|
|
630
|
+
parser_comment(VALUE source, bool freeze, const pm_comment_t *comment) {
|
|
631
|
+
VALUE argv[] = { PARSER_LOCATION(source, freeze, pm_comment_location(comment)) };
|
|
632
|
+
VALUE type = (pm_comment_type(comment) == PM_COMMENT_EMBDOC) ? rb_cPrismEmbDocComment : rb_cPrismInlineComment;
|
|
633
|
+
return rb_class_new_instance_freeze(1, argv, type, freeze);
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
typedef struct {
|
|
637
|
+
VALUE comments;
|
|
638
|
+
VALUE source;
|
|
639
|
+
bool freeze;
|
|
640
|
+
long index;
|
|
641
|
+
} parser_comments_each_data_t;
|
|
642
|
+
|
|
643
|
+
static void
|
|
644
|
+
parser_comments_each(const pm_comment_t *comment, void *data) {
|
|
645
|
+
parser_comments_each_data_t *each_data = (parser_comments_each_data_t *) data;
|
|
646
|
+
VALUE value = parser_comment(each_data->source, each_data->freeze, comment);
|
|
647
|
+
rb_ary_store(each_data->comments, each_data->index++, value);
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
/**
|
|
651
|
+
* Extract the comments out of the parser into the given array, which was
|
|
652
|
+
* created before the source so that the source could hold it.
|
|
653
|
+
*/
|
|
654
|
+
static void
|
|
655
|
+
parser_comments(const pm_parser_t *parser, VALUE comments, VALUE source, bool freeze) {
|
|
656
|
+
parser_comments_each_data_t each_data = { .comments = comments, .source = source, .freeze = freeze };
|
|
657
|
+
pm_parser_comments_each(parser, parser_comments_each, &each_data);
|
|
658
|
+
if (freeze) rb_obj_freeze(comments);
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
/**
|
|
662
|
+
* Build a new MagicComment instance from the given parser and magic comment.
|
|
663
|
+
*/
|
|
664
|
+
static inline VALUE
|
|
665
|
+
parser_magic_comment(VALUE source, bool freeze, const pm_magic_comment_t *magic_comment) {
|
|
666
|
+
pm_location_t key = pm_magic_comment_key(magic_comment);
|
|
667
|
+
pm_location_t value = pm_magic_comment_value(magic_comment);
|
|
668
|
+
|
|
669
|
+
VALUE key_loc = parser_location(source, freeze, key.start, key.length);
|
|
670
|
+
VALUE value_loc = parser_location(source, freeze, value.start, value.length);
|
|
671
|
+
|
|
672
|
+
VALUE argv[] = { key_loc, value_loc };
|
|
673
|
+
return rb_class_new_instance_freeze(2, argv, rb_cPrismMagicComment, freeze);
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
typedef struct {
|
|
677
|
+
VALUE magic_comments;
|
|
678
|
+
VALUE source;
|
|
679
|
+
bool freeze;
|
|
680
|
+
} parser_magic_comments_each_data_t;
|
|
681
|
+
|
|
682
|
+
static void
|
|
683
|
+
parser_magic_comments_each(const pm_magic_comment_t *magic_comment, void *data) {
|
|
684
|
+
parser_magic_comments_each_data_t *each_data = (parser_magic_comments_each_data_t *) data;
|
|
685
|
+
VALUE value = parser_magic_comment(each_data->source, each_data->freeze, magic_comment);
|
|
686
|
+
rb_ary_push(each_data->magic_comments, value);
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
/**
|
|
690
|
+
* Extract the magic comments out of the parser into an array.
|
|
691
|
+
*/
|
|
692
|
+
static VALUE
|
|
693
|
+
parser_magic_comments(const pm_parser_t *parser, VALUE source, bool freeze) {
|
|
694
|
+
VALUE magic_comments = rb_ary_new_capa(pm_parser_magic_comments_size(parser));
|
|
695
|
+
|
|
696
|
+
parser_magic_comments_each_data_t each_data = { magic_comments, source, freeze };
|
|
697
|
+
pm_parser_magic_comments_each(parser, parser_magic_comments_each, &each_data);
|
|
698
|
+
|
|
699
|
+
if (freeze) rb_obj_freeze(magic_comments);
|
|
700
|
+
return magic_comments;
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
/**
|
|
704
|
+
* Extract out the data location from the parser into a Location instance if one
|
|
705
|
+
* exists.
|
|
706
|
+
*/
|
|
707
|
+
static VALUE
|
|
708
|
+
parser_data_loc(const pm_parser_t *parser, VALUE source, bool freeze) {
|
|
709
|
+
const pm_location_t *data_loc = pm_parser_data_loc(parser);
|
|
710
|
+
|
|
711
|
+
if (data_loc->length == 0) {
|
|
712
|
+
return Qnil;
|
|
713
|
+
} else {
|
|
714
|
+
return parser_location(source, freeze, data_loc->start, data_loc->length);
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
typedef struct {
|
|
719
|
+
VALUE errors;
|
|
720
|
+
rb_encoding *encoding;
|
|
721
|
+
VALUE source;
|
|
722
|
+
bool freeze;
|
|
723
|
+
} parser_errors_each_data_t;
|
|
724
|
+
|
|
725
|
+
static void
|
|
726
|
+
parser_errors_each(const pm_diagnostic_t *diagnostic, void *data) {
|
|
727
|
+
parser_errors_each_data_t *each_data = (parser_errors_each_data_t *) data;
|
|
728
|
+
|
|
729
|
+
VALUE type = ID2SYM(rb_intern(pm_diagnostic_type(diagnostic)));
|
|
730
|
+
VALUE message = rb_obj_freeze(rb_enc_str_new_cstr(pm_diagnostic_message(diagnostic), each_data->encoding));
|
|
731
|
+
VALUE location = PARSER_LOCATION(each_data->source, each_data->freeze, pm_diagnostic_location(diagnostic));
|
|
732
|
+
|
|
733
|
+
pm_error_level_t error_level = pm_diagnostic_error_level(diagnostic);
|
|
734
|
+
VALUE level = Qnil;
|
|
735
|
+
|
|
736
|
+
switch (error_level) {
|
|
737
|
+
case PM_ERROR_LEVEL_SYNTAX:
|
|
738
|
+
level = ID2SYM(rb_intern("syntax"));
|
|
739
|
+
break;
|
|
740
|
+
case PM_ERROR_LEVEL_ARGUMENT:
|
|
741
|
+
level = ID2SYM(rb_intern("argument"));
|
|
742
|
+
break;
|
|
743
|
+
case PM_ERROR_LEVEL_LOAD:
|
|
744
|
+
level = ID2SYM(rb_intern("load"));
|
|
745
|
+
break;
|
|
746
|
+
default:
|
|
747
|
+
rb_raise(rb_eRuntimeError, "Unknown level: %" PRIu8, error_level);
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
VALUE argv[] = { type, message, location, level };
|
|
751
|
+
VALUE value = rb_class_new_instance_freeze(4, argv, rb_cPrismParseError, each_data->freeze);
|
|
752
|
+
rb_ary_push(each_data->errors, value);
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
/**
|
|
756
|
+
* Extract the errors out of the parser into an array.
|
|
757
|
+
*/
|
|
758
|
+
static VALUE
|
|
759
|
+
parser_errors(const pm_parser_t *parser, rb_encoding *encoding, VALUE source, bool freeze) {
|
|
760
|
+
VALUE errors = rb_ary_new_capa(pm_parser_errors_size(parser));
|
|
761
|
+
|
|
762
|
+
parser_errors_each_data_t each_data = { errors, encoding, source, freeze };
|
|
763
|
+
pm_parser_errors_each(parser, parser_errors_each, &each_data);
|
|
764
|
+
|
|
765
|
+
if (freeze) rb_obj_freeze(errors);
|
|
766
|
+
return errors;
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
typedef struct {
|
|
770
|
+
VALUE warnings;
|
|
771
|
+
rb_encoding *encoding;
|
|
772
|
+
VALUE source;
|
|
773
|
+
bool freeze;
|
|
774
|
+
} parser_warnings_each_data_t;
|
|
775
|
+
|
|
776
|
+
static void
|
|
777
|
+
parser_warnings_each(const pm_diagnostic_t *diagnostic, void *data) {
|
|
778
|
+
parser_warnings_each_data_t *each_data = (parser_warnings_each_data_t *) data;
|
|
779
|
+
|
|
780
|
+
VALUE type = ID2SYM(rb_intern(pm_diagnostic_type(diagnostic)));
|
|
781
|
+
VALUE message = rb_obj_freeze(rb_enc_str_new_cstr(pm_diagnostic_message(diagnostic), each_data->encoding));
|
|
782
|
+
VALUE location = PARSER_LOCATION(each_data->source, each_data->freeze, pm_diagnostic_location(diagnostic));
|
|
783
|
+
|
|
784
|
+
pm_warning_level_t warning_level = pm_diagnostic_warning_level(diagnostic);
|
|
785
|
+
VALUE level = Qnil;
|
|
786
|
+
|
|
787
|
+
switch (warning_level) {
|
|
788
|
+
case PM_WARNING_LEVEL_DEFAULT:
|
|
789
|
+
level = ID2SYM(rb_intern("default"));
|
|
790
|
+
break;
|
|
791
|
+
case PM_WARNING_LEVEL_VERBOSE:
|
|
792
|
+
level = ID2SYM(rb_intern("verbose"));
|
|
793
|
+
break;
|
|
794
|
+
default:
|
|
795
|
+
rb_raise(rb_eRuntimeError, "Unknown level: %" PRIu8, warning_level);
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
VALUE argv[] = { type, message, location, level };
|
|
799
|
+
VALUE value = rb_class_new_instance_freeze(4, argv, rb_cPrismParseWarning, each_data->freeze);
|
|
800
|
+
rb_ary_push(each_data->warnings, value);
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
/**
|
|
804
|
+
* Extract the warnings out of the parser into an array.
|
|
805
|
+
*/
|
|
806
|
+
static VALUE
|
|
807
|
+
parser_warnings(const pm_parser_t *parser, rb_encoding *encoding, VALUE source, bool freeze) {
|
|
808
|
+
VALUE warnings = rb_ary_new_capa(pm_parser_warnings_size(parser));
|
|
809
|
+
|
|
810
|
+
parser_warnings_each_data_t each_data = { warnings, encoding, source, freeze };
|
|
811
|
+
pm_parser_warnings_each(parser, parser_warnings_each, &each_data);
|
|
812
|
+
|
|
813
|
+
if (freeze) rb_obj_freeze(warnings);
|
|
814
|
+
return warnings;
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
/**
|
|
818
|
+
* Create the arrays that hold the comments attached to each node, sized for the
|
|
819
|
+
* parser's comment attachments. They stay empty unless the attach_comments
|
|
820
|
+
* option was set.
|
|
821
|
+
*/
|
|
822
|
+
static void
|
|
823
|
+
parser_attachments_new(const pm_parser_t *parser, VALUE *attachments, VALUE *ranges) {
|
|
824
|
+
uint32_t ranges_size = pm_parser_comment_ranges_size(parser);
|
|
825
|
+
*attachments = rb_ary_new_capa(ranges_size == 0 ? 0 : (long) pm_parser_comments_size(parser));
|
|
826
|
+
*ranges = rb_ary_new_capa(ranges_size == 0 ? 0 : (long) ranges_size + 1);
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
/**
|
|
830
|
+
* Fill in the comments attached to each node. The attachments are the Comment
|
|
831
|
+
* objects in the order of the parser's comment attachments, which groups them
|
|
832
|
+
* by node in order of node id. The ranges are indexed by node id: for each node
|
|
833
|
+
* that owns comments, the index at which its comments begin is stored at its
|
|
834
|
+
* node id and the index at which they end is stored at the following node id.
|
|
835
|
+
*/
|
|
836
|
+
static void
|
|
837
|
+
parser_attachments(const pm_parser_t *parser, VALUE comments, VALUE attachments, VALUE ranges, bool freeze) {
|
|
838
|
+
uint32_t ranges_size = pm_parser_comment_ranges_size(parser);
|
|
839
|
+
|
|
840
|
+
if (ranges_size > 0) {
|
|
841
|
+
long size = RARRAY_LEN(comments);
|
|
842
|
+
|
|
843
|
+
for (long index = 0; index < size; index++) {
|
|
844
|
+
rb_ary_store(attachments, index, RARRAY_AREF(comments, pm_parser_comment_attachment(parser, (uint32_t) index)));
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
for (uint32_t node_id = 0; node_id < ranges_size; node_id++) {
|
|
848
|
+
pm_comment_range_t range = pm_parser_comment_range(parser, node_id);
|
|
849
|
+
|
|
850
|
+
if (range.length > 0) {
|
|
851
|
+
rb_ary_store(ranges, (long) node_id, LONG2FIX(range.index));
|
|
852
|
+
rb_ary_store(ranges, (long) node_id + 1, LONG2FIX(range.index + range.length));
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
if (freeze) {
|
|
858
|
+
rb_obj_freeze(attachments);
|
|
859
|
+
rb_obj_freeze(ranges);
|
|
860
|
+
}
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
/**
|
|
864
|
+
* Create a new parse result from the given parser, value, encoding, and source.
|
|
865
|
+
*/
|
|
866
|
+
static VALUE
|
|
867
|
+
parse_result_create(VALUE class, const pm_parser_t *parser, VALUE value, rb_encoding *encoding, VALUE source, bool freeze) {
|
|
868
|
+
VALUE result_argv[] = {
|
|
869
|
+
value,
|
|
870
|
+
parser_magic_comments(parser, source, freeze),
|
|
871
|
+
parser_data_loc(parser, source, freeze),
|
|
872
|
+
parser_errors(parser, encoding, source, freeze),
|
|
873
|
+
parser_warnings(parser, encoding, source, freeze),
|
|
874
|
+
pm_parser_continuable(parser) ? Qtrue : Qfalse,
|
|
875
|
+
source
|
|
876
|
+
};
|
|
877
|
+
|
|
878
|
+
return rb_class_new_instance_freeze(7, result_argv, class, freeze);
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
/******************************************************************************/
|
|
882
|
+
/* Lexing Ruby code */
|
|
883
|
+
/******************************************************************************/
|
|
884
|
+
|
|
885
|
+
/**
|
|
886
|
+
* This struct gets stored in the parser and passed in to the lex callback any
|
|
887
|
+
* time a new token is found. We use it to store the necessary information to
|
|
888
|
+
* initialize a Token instance.
|
|
889
|
+
*/
|
|
890
|
+
typedef struct {
|
|
891
|
+
VALUE source;
|
|
892
|
+
VALUE tokens;
|
|
893
|
+
rb_encoding *encoding;
|
|
894
|
+
bool freeze;
|
|
895
|
+
} parse_lex_data_t;
|
|
896
|
+
|
|
897
|
+
/**
|
|
898
|
+
* This is passed as a callback to the parser. It gets called every time a new
|
|
899
|
+
* token is found. Once found, we initialize a new instance of Token and push it
|
|
900
|
+
* onto the tokens array.
|
|
901
|
+
*/
|
|
902
|
+
static void
|
|
903
|
+
parse_lex_token(pm_parser_t *parser, pm_token_t *token, void *data) {
|
|
904
|
+
parse_lex_data_t *parse_lex_data = (parse_lex_data_t *) data;
|
|
905
|
+
|
|
906
|
+
VALUE value = pm_token_new(parser, token, parse_lex_data->encoding, parse_lex_data->source, parse_lex_data->freeze);
|
|
907
|
+
rb_ary_push(parse_lex_data->tokens, value);
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
/**
|
|
911
|
+
* This is called whenever the encoding changes based on the magic comment at
|
|
912
|
+
* the top of the file. We use it to update the encoding that we are using to
|
|
913
|
+
* create tokens.
|
|
914
|
+
*/
|
|
915
|
+
static void
|
|
916
|
+
parse_lex_encoding_changed_callback(pm_parser_t *parser) {
|
|
917
|
+
parse_lex_data_t *parse_lex_data = (parse_lex_data_t *) pm_parser_lex_callback_data(parser);
|
|
918
|
+
parse_lex_data->encoding = rb_enc_find(pm_parser_encoding_name(parser));
|
|
919
|
+
|
|
920
|
+
// Since the encoding changed, we need to go back and change the encoding of
|
|
921
|
+
// the tokens that were already lexed. This is only going to end up being
|
|
922
|
+
// one or two tokens, since the encoding can only change at the top of the
|
|
923
|
+
// file.
|
|
924
|
+
VALUE tokens = parse_lex_data->tokens;
|
|
925
|
+
VALUE next_tokens = rb_ary_new();
|
|
926
|
+
|
|
927
|
+
for (long index = 0; index < RARRAY_LEN(tokens); index++) {
|
|
928
|
+
VALUE token = rb_ary_entry(tokens, index);
|
|
929
|
+
VALUE value = rb_ivar_get(token, rb_intern("@value"));
|
|
930
|
+
VALUE next_value = rb_str_dup(value);
|
|
931
|
+
|
|
932
|
+
rb_enc_associate(next_value, parse_lex_data->encoding);
|
|
933
|
+
if (parse_lex_data->freeze) rb_obj_freeze(next_value);
|
|
934
|
+
|
|
935
|
+
VALUE next_token_argv[] = {
|
|
936
|
+
parse_lex_data->source,
|
|
937
|
+
rb_ivar_get(token, rb_intern("@type")),
|
|
938
|
+
next_value,
|
|
939
|
+
rb_ivar_get(token, rb_intern("@location")),
|
|
940
|
+
rb_ivar_get(token, rb_intern("@state")),
|
|
941
|
+
};
|
|
942
|
+
|
|
943
|
+
VALUE next_token = rb_class_new_instance(5, next_token_argv, rb_cPrismToken);
|
|
944
|
+
|
|
945
|
+
if (parse_lex_data->freeze) {
|
|
946
|
+
rb_obj_freeze(next_token);
|
|
947
|
+
}
|
|
948
|
+
|
|
949
|
+
rb_ary_push(next_tokens, next_token);
|
|
950
|
+
}
|
|
951
|
+
|
|
952
|
+
rb_ary_replace(parse_lex_data->tokens, next_tokens);
|
|
953
|
+
}
|
|
954
|
+
|
|
955
|
+
/**
|
|
956
|
+
* Parse the given input and return a ParseResult containing just the tokens or
|
|
957
|
+
* the nodes and tokens.
|
|
958
|
+
*/
|
|
959
|
+
static result_t
|
|
960
|
+
parse_lex_input(const uint8_t *input, size_t input_length, const pm_options_t *options, rb_encoding *path_encoding, bool return_nodes) {
|
|
961
|
+
pm_arena_t *arena = pm_arena_new();
|
|
962
|
+
pm_parser_t *parser = pm_parser_new(arena, input, input_length, options);
|
|
963
|
+
pm_parser_encoding_changed_callback_set(parser, parse_lex_encoding_changed_callback);
|
|
964
|
+
|
|
965
|
+
VALUE source_string = rb_str_new((const char *) input, input_length);
|
|
966
|
+
VALUE offsets = rb_ary_new_capa(pm_parser_line_offsets(parser)->size);
|
|
967
|
+
VALUE comments = rb_ary_new();
|
|
968
|
+
VALUE attachments = rb_ary_new();
|
|
969
|
+
VALUE ranges = rb_ary_new();
|
|
970
|
+
VALUE source = rb_funcall(rb_cPrismSource, rb_id_source_for, 6, source_string, LONG2NUM(pm_parser_start_line(parser)), offsets, comments, attachments, ranges);
|
|
971
|
+
|
|
972
|
+
parse_lex_data_t parse_lex_data = {
|
|
973
|
+
.source = source,
|
|
974
|
+
.tokens = rb_ary_new(),
|
|
975
|
+
.encoding = rb_enc_find(pm_parser_encoding_name(parser)),
|
|
976
|
+
.freeze = pm_options_freeze(options),
|
|
977
|
+
};
|
|
978
|
+
|
|
979
|
+
parse_lex_data_t *data = &parse_lex_data;
|
|
980
|
+
pm_parser_lex_callback_set(parser, parse_lex_token, data);
|
|
981
|
+
|
|
982
|
+
pm_node_t *node = pm_parse(parser);
|
|
983
|
+
|
|
984
|
+
result_t result = check_raise_error_option(parser, options, path_encoding);
|
|
985
|
+
if (result.type == RESULT_OK) {
|
|
986
|
+
/* Update the Source object with the correct encoding and line offsets,
|
|
987
|
+
* which are only available after pm_parse() completes. */
|
|
988
|
+
rb_encoding *encoding = rb_enc_find(pm_parser_encoding_name(parser));
|
|
989
|
+
rb_enc_associate(source_string, encoding);
|
|
990
|
+
|
|
991
|
+
const pm_line_offset_list_t *line_offsets = pm_parser_line_offsets(parser);
|
|
992
|
+
for (size_t index = 0; index < line_offsets->size; index++) {
|
|
993
|
+
rb_ary_store(offsets, (long) index, ULONG2NUM(line_offsets->offsets[index]));
|
|
994
|
+
}
|
|
995
|
+
|
|
996
|
+
if (pm_options_freeze(options)) {
|
|
997
|
+
rb_obj_freeze(source_string);
|
|
998
|
+
rb_obj_freeze(offsets);
|
|
999
|
+
rb_obj_freeze(source);
|
|
1000
|
+
rb_obj_freeze(parse_lex_data.tokens);
|
|
1001
|
+
}
|
|
1002
|
+
|
|
1003
|
+
/* The comment lists were created along with the source before parsing,
|
|
1004
|
+
* so they are grown to their final sizes now that those are known. */
|
|
1005
|
+
uint32_t ranges_size = pm_parser_comment_ranges_size(parser);
|
|
1006
|
+
rb_ary_resize(comments, (long) pm_parser_comments_size(parser));
|
|
1007
|
+
|
|
1008
|
+
if (ranges_size > 0) {
|
|
1009
|
+
rb_ary_resize(attachments, (long) pm_parser_comments_size(parser));
|
|
1010
|
+
rb_ary_resize(ranges, (long) ranges_size + 1);
|
|
1011
|
+
}
|
|
1012
|
+
|
|
1013
|
+
parser_comments(parser, comments, source, pm_options_freeze(options));
|
|
1014
|
+
parser_attachments(parser, comments, attachments, ranges, pm_options_freeze(options));
|
|
1015
|
+
|
|
1016
|
+
if (return_nodes) {
|
|
1017
|
+
VALUE value = rb_ary_new_capa(2);
|
|
1018
|
+
rb_ary_push(value, pm_ast_new(parser, node, parse_lex_data.encoding, source, pm_options_freeze(options)));
|
|
1019
|
+
rb_ary_push(value, parse_lex_data.tokens);
|
|
1020
|
+
if (pm_options_freeze(options)) rb_obj_freeze(value);
|
|
1021
|
+
result = result_ok(parse_result_create(rb_cPrismParseLexResult, parser, value, parse_lex_data.encoding, source, pm_options_freeze(options)));
|
|
1022
|
+
} else {
|
|
1023
|
+
result = result_ok(parse_result_create(rb_cPrismLexResult, parser, parse_lex_data.tokens, parse_lex_data.encoding, source, pm_options_freeze(options)));
|
|
1024
|
+
}
|
|
1025
|
+
}
|
|
1026
|
+
|
|
1027
|
+
pm_parser_free(parser);
|
|
1028
|
+
pm_arena_free(arena);
|
|
1029
|
+
|
|
1030
|
+
return result;
|
|
1031
|
+
}
|
|
1032
|
+
|
|
1033
|
+
/**
|
|
1034
|
+
* :markup: markdown
|
|
1035
|
+
* call-seq:
|
|
1036
|
+
* lex(source, **options) -> LexResult
|
|
1037
|
+
*
|
|
1038
|
+
* Return a LexResult instance that contains an array of Token instances
|
|
1039
|
+
* corresponding to the given string. For supported options, see Prism.parse.
|
|
1040
|
+
*/
|
|
1041
|
+
static VALUE
|
|
1042
|
+
lex(int argc, VALUE *argv, VALUE self) {
|
|
1043
|
+
pm_options_t *options = pm_options_new();
|
|
1044
|
+
VALUE string = string_options(argc, argv, options);
|
|
1045
|
+
|
|
1046
|
+
result_t result = parse_lex_input((const uint8_t *) RSTRING_PTR(string), RSTRING_LEN(string), options, NULL, false);
|
|
1047
|
+
pm_options_free(options);
|
|
1048
|
+
|
|
1049
|
+
return result_get(result);
|
|
1050
|
+
}
|
|
1051
|
+
|
|
1052
|
+
/**
|
|
1053
|
+
* :markup: markdown
|
|
1054
|
+
* call-seq:
|
|
1055
|
+
* lex_file(filepath, **options) -> LexResult
|
|
1056
|
+
*
|
|
1057
|
+
* Return a LexResult instance that contains an array of Token instances
|
|
1058
|
+
* corresponding to the given file. For supported options, see Prism.parse.
|
|
1059
|
+
*/
|
|
1060
|
+
static VALUE
|
|
1061
|
+
lex_file(int argc, VALUE *argv, VALUE self) {
|
|
1062
|
+
pm_options_t *options = pm_options_new();
|
|
1063
|
+
|
|
1064
|
+
VALUE encoded_filepath;
|
|
1065
|
+
pm_source_t *src = file_options(argc, argv, options, &encoded_filepath);
|
|
1066
|
+
|
|
1067
|
+
result_t result = parse_lex_input(pm_source_source(src), pm_source_length(src), options, rb_enc_get(encoded_filepath), false);
|
|
1068
|
+
pm_source_free(src);
|
|
1069
|
+
pm_options_free(options);
|
|
1070
|
+
|
|
1071
|
+
return result_get(result);
|
|
1072
|
+
}
|
|
1073
|
+
|
|
1074
|
+
/******************************************************************************/
|
|
1075
|
+
/* Parsing Ruby code */
|
|
1076
|
+
/******************************************************************************/
|
|
1077
|
+
|
|
1078
|
+
/**
|
|
1079
|
+
* Parse the given input and return a ParseResult instance.
|
|
1080
|
+
*/
|
|
1081
|
+
static result_t
|
|
1082
|
+
parse_input(const uint8_t *input, size_t input_length, const pm_options_t *options, rb_encoding *path_encoding) {
|
|
1083
|
+
pm_arena_t *arena = pm_arena_new();
|
|
1084
|
+
pm_parser_t *parser = pm_parser_new(arena, input, input_length, options);
|
|
1085
|
+
|
|
1086
|
+
pm_node_t *node = pm_parse(parser);
|
|
1087
|
+
|
|
1088
|
+
result_t result = check_raise_error_option(parser, options, path_encoding);
|
|
1089
|
+
if (result.type == RESULT_OK) {
|
|
1090
|
+
rb_encoding *encoding = rb_enc_find(pm_parser_encoding_name(parser));
|
|
1091
|
+
|
|
1092
|
+
bool freeze = pm_options_freeze(options);
|
|
1093
|
+
VALUE comments = rb_ary_new_capa(pm_parser_comments_size(parser));
|
|
1094
|
+
VALUE attachments, ranges;
|
|
1095
|
+
parser_attachments_new(parser, &attachments, &ranges);
|
|
1096
|
+
|
|
1097
|
+
VALUE source = pm_source_new(parser, encoding, comments, attachments, ranges, freeze);
|
|
1098
|
+
parser_comments(parser, comments, source, freeze);
|
|
1099
|
+
parser_attachments(parser, comments, attachments, ranges, freeze);
|
|
1100
|
+
|
|
1101
|
+
VALUE value = pm_ast_new(parser, node, encoding, source, freeze);
|
|
1102
|
+
result = result_ok(parse_result_create(rb_cPrismParseResult, parser, value, encoding, source, freeze));
|
|
1103
|
+
}
|
|
1104
|
+
|
|
1105
|
+
pm_parser_free(parser);
|
|
1106
|
+
pm_arena_free(arena);
|
|
1107
|
+
|
|
1108
|
+
return result;
|
|
1109
|
+
}
|
|
1110
|
+
|
|
1111
|
+
/**
|
|
1112
|
+
* :markup: markdown
|
|
1113
|
+
* call-seq:
|
|
1114
|
+
* parse(source, **options) -> ParseResult
|
|
1115
|
+
*
|
|
1116
|
+
* Parse the given string and return a ParseResult instance. The options that
|
|
1117
|
+
* are supported are:
|
|
1118
|
+
*
|
|
1119
|
+
* * `attach_comments` - whether or not to attach each comment to the node that
|
|
1120
|
+
* owns it. When set, the comments on the result are sorted by start
|
|
1121
|
+
* offset. This should be a boolean or nil.
|
|
1122
|
+
* * `command_line` - either nil or a string of the various options that were
|
|
1123
|
+
* set on the command line. Valid values are combinations of "a", "l",
|
|
1124
|
+
* "n", "p", and "x".
|
|
1125
|
+
* * `encoding` - the encoding of the source being parsed. This should be an
|
|
1126
|
+
* encoding or nil.
|
|
1127
|
+
* * `filepath` - the filepath of the source being parsed. This should be a
|
|
1128
|
+
* string or nil.
|
|
1129
|
+
* * `freeze` - whether or not to deeply freeze the AST. This should be a
|
|
1130
|
+
* boolean or nil.
|
|
1131
|
+
* * `frozen_string_literal` - whether or not the frozen string literal pragma
|
|
1132
|
+
* has been set. This should be a boolean or nil.
|
|
1133
|
+
* * `line` - the line number that the parse starts on. This should be an
|
|
1134
|
+
* integer or nil. Note that this is 1-indexed.
|
|
1135
|
+
* * `main_script` - a boolean indicating whether or not the source being parsed
|
|
1136
|
+
* is the main script being run by the interpreter. This controls whether
|
|
1137
|
+
* or not shebangs are parsed for additional flags and whether or not the
|
|
1138
|
+
* parser will attempt to find a matching shebang if the first one does
|
|
1139
|
+
* not contain the word "ruby".
|
|
1140
|
+
* * `partial_script` - when the file being parsed is considered a "partial"
|
|
1141
|
+
* script, jumps will not be marked as errors if they are not contained
|
|
1142
|
+
* within loops/blocks. This is used in the case that you're parsing a
|
|
1143
|
+
* script that you know will be embedded inside another script later, but
|
|
1144
|
+
* you do not have that context yet. For example, when parsing an ERB
|
|
1145
|
+
* template that will be evaluated inside another script.
|
|
1146
|
+
* * `raise_error` - either nil, true, or a symbol indicating that an error
|
|
1147
|
+
* should be raised if the source contains any errors. The message of
|
|
1148
|
+
* the error will include the source of the lines that contain errors
|
|
1149
|
+
* when possible, and the value of this option controls how those lines
|
|
1150
|
+
* are formatted. Valid values are `:plain` (no formatting), `:style`
|
|
1151
|
+
* (bold formatting), and `:color` (bold and color formatting). When
|
|
1152
|
+
* `true` is given, the formatting is determined by whether or not
|
|
1153
|
+
* `$stderr` is a terminal and whether or not the `NO_COLOR` environment
|
|
1154
|
+
* variable is set, mirroring the behavior of CRuby itself. Syntax-level
|
|
1155
|
+
* errors raise SyntaxError, argument-level errors raise ArgumentError,
|
|
1156
|
+
* and load-level errors raise LoadError. Note that this option is
|
|
1157
|
+
* honored by every API that accepts these options, including predicates
|
|
1158
|
+
* like Prism.parse_success?, which will raise instead of returning a
|
|
1159
|
+
* boolean when the source contains errors.
|
|
1160
|
+
* * `scopes` - the locals that are in scope surrounding the code that is being
|
|
1161
|
+
* parsed. This should be an array of arrays of symbols or nil. Scopes are
|
|
1162
|
+
* ordered from the outermost scope to the innermost one.
|
|
1163
|
+
* * `version` - the version of Ruby syntax that prism should used to parse Ruby
|
|
1164
|
+
* code. By default prism parses with the syntax version nearest to the
|
|
1165
|
+
* current Ruby that is running (which you can trigger with `nil` or
|
|
1166
|
+
* `"nearest"`): the running version if prism supports it, otherwise the
|
|
1167
|
+
* closest version that it does support. You may also restrict the syntax
|
|
1168
|
+
* to a specific version of Ruby, e.g., with `"3.3.0"`. To parse with
|
|
1169
|
+
* exactly the same syntax version that the current Ruby is running use
|
|
1170
|
+
* `version: "current"`, which raises Prism::CurrentVersionError if prism
|
|
1171
|
+
* does not support it. To parse with the latest version of Ruby syntax
|
|
1172
|
+
* that prism supports, use `version: "latest"`. Raises ArgumentError if
|
|
1173
|
+
* the version is not currently supported by Prism.
|
|
1174
|
+
*/
|
|
1175
|
+
static VALUE
|
|
1176
|
+
parse(int argc, VALUE *argv, VALUE self) {
|
|
1177
|
+
pm_options_t *options = pm_options_new();
|
|
1178
|
+
VALUE string = string_options(argc, argv, options);
|
|
1179
|
+
|
|
1180
|
+
const uint8_t *source = (const uint8_t *) RSTRING_PTR(string);
|
|
1181
|
+
size_t length = RSTRING_LEN(string);
|
|
1182
|
+
|
|
1183
|
+
#ifdef PRISM_BUILD_DEBUG
|
|
1184
|
+
char* dup = xmalloc(length);
|
|
1185
|
+
memcpy(dup, source, length);
|
|
1186
|
+
source = (const uint8_t *) dup;
|
|
1187
|
+
#endif
|
|
1188
|
+
|
|
1189
|
+
result_t result = parse_input(source, length, options, NULL);
|
|
1190
|
+
|
|
1191
|
+
#ifdef PRISM_BUILD_DEBUG
|
|
1192
|
+
#ifdef xfree_sized
|
|
1193
|
+
xfree_sized(dup, length);
|
|
1194
|
+
#else
|
|
1195
|
+
xfree(dup);
|
|
1196
|
+
#endif
|
|
1197
|
+
#endif
|
|
1198
|
+
|
|
1199
|
+
pm_options_free(options);
|
|
1200
|
+
return result_get(result);
|
|
1201
|
+
}
|
|
1202
|
+
|
|
1203
|
+
/**
|
|
1204
|
+
* :markup: markdown
|
|
1205
|
+
* call-seq:
|
|
1206
|
+
* parse_file(filepath, **options) -> ParseResult
|
|
1207
|
+
*
|
|
1208
|
+
* Parse the given file and return a ParseResult instance. For supported
|
|
1209
|
+
* options, see Prism.parse.
|
|
1210
|
+
*/
|
|
1211
|
+
static VALUE
|
|
1212
|
+
parse_file(int argc, VALUE *argv, VALUE self) {
|
|
1213
|
+
pm_options_t *options = pm_options_new();
|
|
1214
|
+
|
|
1215
|
+
VALUE encoded_filepath;
|
|
1216
|
+
pm_source_t *src = file_options(argc, argv, options, &encoded_filepath);
|
|
1217
|
+
|
|
1218
|
+
result_t result = parse_input(pm_source_source(src), pm_source_length(src), options, rb_enc_get(encoded_filepath));
|
|
1219
|
+
pm_source_free(src);
|
|
1220
|
+
pm_options_free(options);
|
|
1221
|
+
|
|
1222
|
+
return result_get(result);
|
|
1223
|
+
}
|
|
1224
|
+
|
|
1225
|
+
/**
|
|
1226
|
+
* Parse the given input and return nothing.
|
|
1227
|
+
*/
|
|
1228
|
+
static result_t
|
|
1229
|
+
profile_input(const uint8_t *input, size_t input_length, const pm_options_t *options, rb_encoding *path_encoding) {
|
|
1230
|
+
pm_arena_t *arena = pm_arena_new();
|
|
1231
|
+
pm_parser_t *parser = pm_parser_new(arena, input, input_length, options);
|
|
1232
|
+
|
|
1233
|
+
pm_parse(parser);
|
|
1234
|
+
|
|
1235
|
+
result_t result = check_raise_error_option(parser, options, path_encoding);
|
|
1236
|
+
pm_parser_free(parser);
|
|
1237
|
+
pm_arena_free(arena);
|
|
1238
|
+
|
|
1239
|
+
return result;
|
|
1240
|
+
}
|
|
1241
|
+
|
|
1242
|
+
/**
|
|
1243
|
+
* :markup: markdown
|
|
1244
|
+
* call-seq:
|
|
1245
|
+
* profile(source, **options) -> nil
|
|
1246
|
+
*
|
|
1247
|
+
* Parse the given string and return nothing. This method is meant to allow
|
|
1248
|
+
* profilers to avoid the overhead of reifying the AST to Ruby. For supported
|
|
1249
|
+
* options, see Prism.parse.
|
|
1250
|
+
*/
|
|
1251
|
+
static VALUE
|
|
1252
|
+
profile(int argc, VALUE *argv, VALUE self) {
|
|
1253
|
+
pm_options_t *options = pm_options_new();
|
|
1254
|
+
VALUE string = string_options(argc, argv, options);
|
|
1255
|
+
|
|
1256
|
+
result_t result = profile_input((const uint8_t *) RSTRING_PTR(string), RSTRING_LEN(string), options, NULL);
|
|
1257
|
+
pm_options_free(options);
|
|
1258
|
+
result_get(result);
|
|
1259
|
+
|
|
1260
|
+
return Qnil;
|
|
1261
|
+
}
|
|
1262
|
+
|
|
1263
|
+
/**
|
|
1264
|
+
* :markup: markdown
|
|
1265
|
+
* call-seq:
|
|
1266
|
+
* profile_file(filepath, **options) -> nil
|
|
1267
|
+
*
|
|
1268
|
+
* Parse the given file and return nothing. This method is meant to allow
|
|
1269
|
+
* profilers to avoid the overhead of reifying the AST to Ruby. For supported
|
|
1270
|
+
* options, see Prism.parse.
|
|
1271
|
+
*/
|
|
1272
|
+
static VALUE
|
|
1273
|
+
profile_file(int argc, VALUE *argv, VALUE self) {
|
|
1274
|
+
pm_options_t *options = pm_options_new();
|
|
1275
|
+
|
|
1276
|
+
VALUE encoded_filepath;
|
|
1277
|
+
pm_source_t *src = file_options(argc, argv, options, &encoded_filepath);
|
|
1278
|
+
|
|
1279
|
+
result_t result = profile_input(pm_source_source(src), pm_source_length(src), options, rb_enc_get(encoded_filepath));
|
|
1280
|
+
pm_source_free(src);
|
|
1281
|
+
pm_options_free(options);
|
|
1282
|
+
result_get(result);
|
|
1283
|
+
|
|
1284
|
+
return Qnil;
|
|
1285
|
+
}
|
|
1286
|
+
|
|
1287
|
+
static int
|
|
1288
|
+
parse_stream_eof(void *stream) {
|
|
1289
|
+
if (rb_funcall((VALUE) stream, rb_intern("eof?"), 0)) {
|
|
1290
|
+
return 1;
|
|
1291
|
+
}
|
|
1292
|
+
return 0;
|
|
1293
|
+
}
|
|
1294
|
+
|
|
1295
|
+
/**
|
|
1296
|
+
* The largest number of bytes a single character can occupy in any encoding
|
|
1297
|
+
* that Ruby supports (CESU-8). `IO#gets(limit)` treats its argument as a soft
|
|
1298
|
+
* limit: to avoid splitting a multi-byte character it may return up to
|
|
1299
|
+
* `MAX_ENC_LEN - 1` more bytes than requested. Reserving `MAX_ENC_LEN` bytes of
|
|
1300
|
+
* headroom therefore leaves room for both that overshoot and the terminating
|
|
1301
|
+
* NUL byte.
|
|
1302
|
+
*
|
|
1303
|
+
* The value can be re-derived from the Ruby source tree with:
|
|
1304
|
+
*
|
|
1305
|
+
* grep -Erh "max (enc|byte) length" enc | grep -Eo '[0-9]+' | sort | tail -n1
|
|
1306
|
+
*/
|
|
1307
|
+
#define MAX_ENC_LEN 6
|
|
1308
|
+
|
|
1309
|
+
/**
|
|
1310
|
+
* An implementation of fgets that is suitable for use with Ruby IO objects. As
|
|
1311
|
+
* required by pm_source_stream_fgets_t, this never writes more than `size`
|
|
1312
|
+
* bytes into `string` (including the terminating NUL byte).
|
|
1313
|
+
*/
|
|
1314
|
+
static char *
|
|
1315
|
+
parse_stream_fgets(char *string, int size, void *stream) {
|
|
1316
|
+
RUBY_ASSERT(size > MAX_ENC_LEN);
|
|
1317
|
+
|
|
1318
|
+
/*
|
|
1319
|
+
* Request fewer bytes than the buffer can hold. `gets` may return more
|
|
1320
|
+
* bytes than requested when the limit falls in the middle of a multi-byte
|
|
1321
|
+
* character, and the reserved headroom guarantees the result still fits.
|
|
1322
|
+
*/
|
|
1323
|
+
VALUE line = rb_funcall((VALUE) stream, rb_intern("gets"), 1, INT2FIX(size - MAX_ENC_LEN));
|
|
1324
|
+
|
|
1325
|
+
/*
|
|
1326
|
+
* A well-behaved stream returns a String, or nil at EOF. Coerce anything
|
|
1327
|
+
* else to nil so that we never treat a non-String as a byte buffer.
|
|
1328
|
+
*/
|
|
1329
|
+
line = rb_check_string_type(line);
|
|
1330
|
+
if (NIL_P(line)) {
|
|
1331
|
+
return NULL;
|
|
1332
|
+
}
|
|
1333
|
+
|
|
1334
|
+
const char *cstr = RSTRING_PTR(line);
|
|
1335
|
+
long length = RSTRING_LEN(line);
|
|
1336
|
+
|
|
1337
|
+
/*
|
|
1338
|
+
* Defensively clamp the copy. A misbehaving `gets` may ignore the limit
|
|
1339
|
+
* entirely and return an arbitrarily long string; we must never write past
|
|
1340
|
+
* the caller's buffer. One byte is reserved for the NUL terminator.
|
|
1341
|
+
*/
|
|
1342
|
+
if (length > (long) (size - 1)) {
|
|
1343
|
+
length = (long) (size - 1);
|
|
1344
|
+
}
|
|
1345
|
+
|
|
1346
|
+
memcpy(string, cstr, (size_t) length);
|
|
1347
|
+
string[length] = '\0';
|
|
1348
|
+
|
|
1349
|
+
return string;
|
|
1350
|
+
}
|
|
1351
|
+
|
|
1352
|
+
#undef MAX_ENC_LEN
|
|
1353
|
+
|
|
1354
|
+
/**
|
|
1355
|
+
* :markup: markdown
|
|
1356
|
+
* call-seq:
|
|
1357
|
+
* parse_stream(stream, **options) -> ParseResult
|
|
1358
|
+
*
|
|
1359
|
+
* Parse the given object that responds to `gets` and return a ParseResult
|
|
1360
|
+
* instance. The options that are supported are the same as Prism.parse.
|
|
1361
|
+
*/
|
|
1362
|
+
static VALUE
|
|
1363
|
+
parse_stream(int argc, VALUE *argv, VALUE self) {
|
|
1364
|
+
VALUE stream;
|
|
1365
|
+
VALUE keywords;
|
|
1366
|
+
rb_scan_args(argc, argv, "1:", &stream, &keywords);
|
|
1367
|
+
|
|
1368
|
+
pm_options_t *options = pm_options_new();
|
|
1369
|
+
extract_options(options, Qnil, keywords);
|
|
1370
|
+
|
|
1371
|
+
pm_source_t *src = pm_source_stream_new((void *) stream, parse_stream_fgets, parse_stream_eof);
|
|
1372
|
+
pm_arena_t *arena = pm_arena_new();
|
|
1373
|
+
pm_parser_t *parser;
|
|
1374
|
+
|
|
1375
|
+
pm_node_t *node = pm_parse_stream(&parser, arena, src, options);
|
|
1376
|
+
|
|
1377
|
+
result_t result = check_raise_error_option(parser, options, NULL);
|
|
1378
|
+
if (result.type == RESULT_OK) {
|
|
1379
|
+
rb_encoding *encoding = rb_enc_find(pm_parser_encoding_name(parser));
|
|
1380
|
+
|
|
1381
|
+
VALUE comments = rb_ary_new_capa(pm_parser_comments_size(parser));
|
|
1382
|
+
VALUE attachments, ranges;
|
|
1383
|
+
parser_attachments_new(parser, &attachments, &ranges);
|
|
1384
|
+
|
|
1385
|
+
VALUE source = pm_source_new(parser, encoding, comments, attachments, ranges, pm_options_freeze(options));
|
|
1386
|
+
parser_comments(parser, comments, source, pm_options_freeze(options));
|
|
1387
|
+
parser_attachments(parser, comments, attachments, ranges, pm_options_freeze(options));
|
|
1388
|
+
|
|
1389
|
+
VALUE value = pm_ast_new(parser, node, encoding, source, pm_options_freeze(options));
|
|
1390
|
+
|
|
1391
|
+
result = result_ok(parse_result_create(rb_cPrismParseResult, parser, value, encoding, source, pm_options_freeze(options)));
|
|
1392
|
+
}
|
|
1393
|
+
|
|
1394
|
+
pm_source_free(src);
|
|
1395
|
+
pm_parser_free(parser);
|
|
1396
|
+
pm_arena_free(arena);
|
|
1397
|
+
pm_options_free(options);
|
|
1398
|
+
|
|
1399
|
+
return result_get(result);
|
|
1400
|
+
}
|
|
1401
|
+
|
|
1402
|
+
/**
|
|
1403
|
+
* Parse the given input and return an array of Comment objects.
|
|
1404
|
+
*/
|
|
1405
|
+
static result_t
|
|
1406
|
+
parse_input_comments(const uint8_t *input, size_t input_length, const pm_options_t *options, rb_encoding *path_encoding) {
|
|
1407
|
+
pm_arena_t *arena = pm_arena_new();
|
|
1408
|
+
pm_parser_t *parser = pm_parser_new(arena, input, input_length, options);
|
|
1409
|
+
|
|
1410
|
+
pm_parse(parser);
|
|
1411
|
+
|
|
1412
|
+
result_t result = check_raise_error_option(parser, options, path_encoding);
|
|
1413
|
+
if (result.type == RESULT_OK) {
|
|
1414
|
+
rb_encoding *encoding = rb_enc_find(pm_parser_encoding_name(parser));
|
|
1415
|
+
|
|
1416
|
+
VALUE comments = rb_ary_new_capa(pm_parser_comments_size(parser));
|
|
1417
|
+
VALUE attachments, ranges;
|
|
1418
|
+
parser_attachments_new(parser, &attachments, &ranges);
|
|
1419
|
+
|
|
1420
|
+
VALUE source = pm_source_new(parser, encoding, comments, attachments, ranges, pm_options_freeze(options));
|
|
1421
|
+
parser_comments(parser, comments, source, pm_options_freeze(options));
|
|
1422
|
+
parser_attachments(parser, comments, attachments, ranges, pm_options_freeze(options));
|
|
1423
|
+
|
|
1424
|
+
result = result_ok(comments);
|
|
1425
|
+
}
|
|
1426
|
+
|
|
1427
|
+
pm_parser_free(parser);
|
|
1428
|
+
pm_arena_free(arena);
|
|
1429
|
+
|
|
1430
|
+
return result;
|
|
1431
|
+
}
|
|
1432
|
+
|
|
1433
|
+
/**
|
|
1434
|
+
* :markup: markdown
|
|
1435
|
+
* call-seq:
|
|
1436
|
+
* parse_comments(source, **options) -> Array
|
|
1437
|
+
*
|
|
1438
|
+
* Parse the given string and return an array of Comment objects. For supported
|
|
1439
|
+
* options, see Prism.parse.
|
|
1440
|
+
*/
|
|
1441
|
+
static VALUE
|
|
1442
|
+
parse_comments(int argc, VALUE *argv, VALUE self) {
|
|
1443
|
+
pm_options_t *options = pm_options_new();
|
|
1444
|
+
VALUE string = string_options(argc, argv, options);
|
|
1445
|
+
|
|
1446
|
+
result_t result = parse_input_comments((const uint8_t *) RSTRING_PTR(string), RSTRING_LEN(string), options, NULL);
|
|
1447
|
+
pm_options_free(options);
|
|
1448
|
+
|
|
1449
|
+
return result_get(result);
|
|
1450
|
+
}
|
|
1451
|
+
|
|
1452
|
+
/**
|
|
1453
|
+
* :markup: markdown
|
|
1454
|
+
* call-seq:
|
|
1455
|
+
* parse_file_comments(filepath, **options) -> Array
|
|
1456
|
+
*
|
|
1457
|
+
* Parse the given file and return an array of Comment objects. For supported
|
|
1458
|
+
* options, see Prism.parse.
|
|
1459
|
+
*/
|
|
1460
|
+
static VALUE
|
|
1461
|
+
parse_file_comments(int argc, VALUE *argv, VALUE self) {
|
|
1462
|
+
pm_options_t *options = pm_options_new();
|
|
1463
|
+
|
|
1464
|
+
VALUE encoded_filepath;
|
|
1465
|
+
pm_source_t *src = file_options(argc, argv, options, &encoded_filepath);
|
|
1466
|
+
|
|
1467
|
+
result_t result = parse_input_comments(pm_source_source(src), pm_source_length(src), options, rb_enc_get(encoded_filepath));
|
|
1468
|
+
pm_source_free(src);
|
|
1469
|
+
pm_options_free(options);
|
|
1470
|
+
|
|
1471
|
+
return result_get(result);
|
|
1472
|
+
}
|
|
1473
|
+
|
|
1474
|
+
/**
|
|
1475
|
+
* :markup: markdown
|
|
1476
|
+
* call-seq:
|
|
1477
|
+
* parse_lex(source, **options) -> ParseLexResult
|
|
1478
|
+
*
|
|
1479
|
+
* Parse the given string and return a ParseLexResult instance that contains a
|
|
1480
|
+
* 2-element array, where the first element is the AST and the second element is
|
|
1481
|
+
* an array of Token instances.
|
|
1482
|
+
*
|
|
1483
|
+
* This API is only meant to be used in the case where you need both the AST and
|
|
1484
|
+
* the tokens. If you only need one or the other, use either Prism.parse or
|
|
1485
|
+
* Prism.lex.
|
|
1486
|
+
*
|
|
1487
|
+
* For supported options, see Prism.parse.
|
|
1488
|
+
*/
|
|
1489
|
+
static VALUE
|
|
1490
|
+
parse_lex(int argc, VALUE *argv, VALUE self) {
|
|
1491
|
+
pm_options_t *options = pm_options_new();
|
|
1492
|
+
VALUE string = string_options(argc, argv, options);
|
|
1493
|
+
|
|
1494
|
+
result_t result = parse_lex_input((const uint8_t *) RSTRING_PTR(string), RSTRING_LEN(string), options, NULL, true);
|
|
1495
|
+
pm_options_free(options);
|
|
1496
|
+
|
|
1497
|
+
return result_get(result);
|
|
1498
|
+
}
|
|
1499
|
+
|
|
1500
|
+
/**
|
|
1501
|
+
* :markup: markdown
|
|
1502
|
+
* call-seq:
|
|
1503
|
+
* parse_lex_file(filepath, **options) -> ParseLexResult
|
|
1504
|
+
*
|
|
1505
|
+
* Parse the given file and return a ParseLexResult instance that contains a
|
|
1506
|
+
* 2-element array, where the first element is the AST and the second element is
|
|
1507
|
+
* an array of Token instances.
|
|
1508
|
+
*
|
|
1509
|
+
* This API is only meant to be used in the case where you need both the AST and
|
|
1510
|
+
* the tokens. If you only need one or the other, use either Prism.parse_file
|
|
1511
|
+
* or Prism.lex_file.
|
|
1512
|
+
*
|
|
1513
|
+
* For supported options, see Prism.parse.
|
|
1514
|
+
*/
|
|
1515
|
+
static VALUE
|
|
1516
|
+
parse_lex_file(int argc, VALUE *argv, VALUE self) {
|
|
1517
|
+
pm_options_t *options = pm_options_new();
|
|
1518
|
+
|
|
1519
|
+
VALUE encoded_filepath;
|
|
1520
|
+
pm_source_t *src = file_options(argc, argv, options, &encoded_filepath);
|
|
1521
|
+
|
|
1522
|
+
result_t result = parse_lex_input(pm_source_source(src), pm_source_length(src), options, rb_enc_get(encoded_filepath), true);
|
|
1523
|
+
pm_source_free(src);
|
|
1524
|
+
pm_options_free(options);
|
|
1525
|
+
|
|
1526
|
+
return result_get(result);
|
|
1527
|
+
}
|
|
1528
|
+
|
|
1529
|
+
/**
|
|
1530
|
+
* Parse the given input and return true if it parses without errors.
|
|
1531
|
+
*/
|
|
1532
|
+
static result_t
|
|
1533
|
+
parse_input_success_p(const uint8_t *input, size_t input_length, const pm_options_t *options, rb_encoding *path_encoding) {
|
|
1534
|
+
pm_arena_t *arena = pm_arena_new();
|
|
1535
|
+
pm_parser_t *parser = pm_parser_new(arena, input, input_length, options);
|
|
1536
|
+
pm_parse(parser);
|
|
1537
|
+
|
|
1538
|
+
result_t result = check_raise_error_option(parser, options, path_encoding);
|
|
1539
|
+
if (result.type == RESULT_OK) {
|
|
1540
|
+
result = result_ok(pm_parser_errors_size(parser) == 0 ? Qtrue : Qfalse);
|
|
1541
|
+
}
|
|
1542
|
+
|
|
1543
|
+
pm_parser_free(parser);
|
|
1544
|
+
pm_arena_free(arena);
|
|
1545
|
+
|
|
1546
|
+
return result;
|
|
1547
|
+
}
|
|
1548
|
+
|
|
1549
|
+
/**
|
|
1550
|
+
* :markup: markdown
|
|
1551
|
+
* call-seq:
|
|
1552
|
+
* parse_success?(source, **options) -> bool
|
|
1553
|
+
*
|
|
1554
|
+
* Parse the given string and return true if it parses without errors. For
|
|
1555
|
+
* supported options, see Prism.parse.
|
|
1556
|
+
*/
|
|
1557
|
+
static VALUE
|
|
1558
|
+
parse_success_p(int argc, VALUE *argv, VALUE self) {
|
|
1559
|
+
pm_options_t *options = pm_options_new();
|
|
1560
|
+
VALUE string = string_options(argc, argv, options);
|
|
1561
|
+
|
|
1562
|
+
result_t result = parse_input_success_p((const uint8_t *) RSTRING_PTR(string), RSTRING_LEN(string), options, NULL);
|
|
1563
|
+
pm_options_free(options);
|
|
1564
|
+
|
|
1565
|
+
return result_get(result);
|
|
1566
|
+
}
|
|
1567
|
+
|
|
1568
|
+
/**
|
|
1569
|
+
* :markup: markdown
|
|
1570
|
+
* call-seq:
|
|
1571
|
+
* parse_failure?(source, **options) -> bool
|
|
1572
|
+
*
|
|
1573
|
+
* Parse the given string and return true if it parses with errors. For
|
|
1574
|
+
* supported options, see Prism.parse.
|
|
1575
|
+
*/
|
|
1576
|
+
static VALUE
|
|
1577
|
+
parse_failure_p(int argc, VALUE *argv, VALUE self) {
|
|
1578
|
+
return RTEST(parse_success_p(argc, argv, self)) ? Qfalse : Qtrue;
|
|
1579
|
+
}
|
|
1580
|
+
|
|
1581
|
+
/**
|
|
1582
|
+
* :markup: markdown
|
|
1583
|
+
* call-seq:
|
|
1584
|
+
* parse_file_success?(filepath, **options) -> bool
|
|
1585
|
+
*
|
|
1586
|
+
* Parse the given file and return true if it parses without errors. For
|
|
1587
|
+
* supported options, see Prism.parse.
|
|
1588
|
+
*/
|
|
1589
|
+
static VALUE
|
|
1590
|
+
parse_file_success_p(int argc, VALUE *argv, VALUE self) {
|
|
1591
|
+
pm_options_t *options = pm_options_new();
|
|
1592
|
+
|
|
1593
|
+
VALUE encoded_filepath;
|
|
1594
|
+
pm_source_t *src = file_options(argc, argv, options, &encoded_filepath);
|
|
1595
|
+
|
|
1596
|
+
result_t result = parse_input_success_p(pm_source_source(src), pm_source_length(src), options, rb_enc_get(encoded_filepath));
|
|
1597
|
+
pm_source_free(src);
|
|
1598
|
+
pm_options_free(options);
|
|
1599
|
+
|
|
1600
|
+
return result_get(result);
|
|
1601
|
+
}
|
|
1602
|
+
|
|
1603
|
+
/**
|
|
1604
|
+
* :markup: markdown
|
|
1605
|
+
* call-seq:
|
|
1606
|
+
* parse_file_failure?(filepath, **options) -> bool
|
|
1607
|
+
*
|
|
1608
|
+
* Parse the given file and return true if it parses with errors. For
|
|
1609
|
+
* supported options, see Prism.parse.
|
|
1610
|
+
*/
|
|
1611
|
+
static VALUE
|
|
1612
|
+
parse_file_failure_p(int argc, VALUE *argv, VALUE self) {
|
|
1613
|
+
return RTEST(parse_file_success_p(argc, argv, self)) ? Qfalse : Qtrue;
|
|
1614
|
+
}
|
|
1615
|
+
|
|
1616
|
+
/******************************************************************************/
|
|
1617
|
+
/* String query methods */
|
|
1618
|
+
/******************************************************************************/
|
|
1619
|
+
|
|
1620
|
+
/**
|
|
1621
|
+
* Process the result of a call to a string query method and return an
|
|
1622
|
+
* appropriate value.
|
|
1623
|
+
*/
|
|
1624
|
+
static VALUE
|
|
1625
|
+
string_query(pm_string_query_t result) {
|
|
1626
|
+
switch (result) {
|
|
1627
|
+
case PM_STRING_QUERY_ERROR:
|
|
1628
|
+
rb_raise(rb_eArgError, "Invalid or non ascii-compatible encoding");
|
|
1629
|
+
return Qfalse;
|
|
1630
|
+
case PM_STRING_QUERY_FALSE:
|
|
1631
|
+
return Qfalse;
|
|
1632
|
+
case PM_STRING_QUERY_TRUE:
|
|
1633
|
+
return Qtrue;
|
|
1634
|
+
}
|
|
1635
|
+
return Qfalse;
|
|
1636
|
+
}
|
|
1637
|
+
|
|
1638
|
+
/**
|
|
1639
|
+
* :markup: markdown
|
|
1640
|
+
* call-seq:
|
|
1641
|
+
* local?(string) -> bool
|
|
1642
|
+
*
|
|
1643
|
+
* Returns true if the string constitutes a valid local variable name. Note that
|
|
1644
|
+
* this means the names that can be set through Binding#local_variable_set, not
|
|
1645
|
+
* necessarily the ones that can be set through a local variable assignment.
|
|
1646
|
+
*/
|
|
1647
|
+
static VALUE
|
|
1648
|
+
string_query_local_p(VALUE self, VALUE string) {
|
|
1649
|
+
const uint8_t *source = (const uint8_t *) check_string(string);
|
|
1650
|
+
return string_query(pm_string_query_local(source, RSTRING_LEN(string), rb_enc_get(string)->name));
|
|
1651
|
+
}
|
|
1652
|
+
|
|
1653
|
+
/**
|
|
1654
|
+
* :markup: markdown
|
|
1655
|
+
* call-seq:
|
|
1656
|
+
* constant?(string) -> bool
|
|
1657
|
+
*
|
|
1658
|
+
* Returns true if the string constitutes a valid constant name. Note that this
|
|
1659
|
+
* means the names that can be set through Module#const_set, not necessarily the
|
|
1660
|
+
* ones that can be set through a constant assignment.
|
|
1661
|
+
*/
|
|
1662
|
+
static VALUE
|
|
1663
|
+
string_query_constant_p(VALUE self, VALUE string) {
|
|
1664
|
+
const uint8_t *source = (const uint8_t *) check_string(string);
|
|
1665
|
+
return string_query(pm_string_query_constant(source, RSTRING_LEN(string), rb_enc_get(string)->name));
|
|
1666
|
+
}
|
|
1667
|
+
|
|
1668
|
+
/**
|
|
1669
|
+
* :markup: markdown
|
|
1670
|
+
* call-seq:
|
|
1671
|
+
* method_name?(string) -> bool
|
|
1672
|
+
*
|
|
1673
|
+
* Returns true if the string constitutes a valid method name.
|
|
1674
|
+
*/
|
|
1675
|
+
static VALUE
|
|
1676
|
+
string_query_method_name_p(VALUE self, VALUE string) {
|
|
1677
|
+
const uint8_t *source = (const uint8_t *) check_string(string);
|
|
1678
|
+
return string_query(pm_string_query_method_name(source, RSTRING_LEN(string), rb_enc_get(string)->name));
|
|
1679
|
+
}
|
|
1680
|
+
|
|
1681
|
+
/******************************************************************************/
|
|
1682
|
+
/* Initialization of the extension */
|
|
1683
|
+
/******************************************************************************/
|
|
1684
|
+
|
|
1685
|
+
/**
|
|
1686
|
+
* The init function that Ruby calls when loading this extension.
|
|
1687
|
+
*/
|
|
1688
|
+
RUBY_FUNC_EXPORTED void
|
|
1689
|
+
Init_prism(void) {
|
|
1690
|
+
/* Make sure that the prism library version matches the expected version.
|
|
1691
|
+
* Otherwise something was compiled incorrectly. */
|
|
1692
|
+
if (strcmp(pm_version(), EXPECTED_PRISM_VERSION) != 0) {
|
|
1693
|
+
rb_raise(
|
|
1694
|
+
rb_eRuntimeError,
|
|
1695
|
+
"The prism library version (%s) does not match the expected version (%s)",
|
|
1696
|
+
pm_version(),
|
|
1697
|
+
EXPECTED_PRISM_VERSION
|
|
1698
|
+
);
|
|
1699
|
+
}
|
|
1700
|
+
|
|
1701
|
+
#ifdef HAVE_RB_EXT_RACTOR_SAFE
|
|
1702
|
+
/* Mark this extension as Ractor-safe. */
|
|
1703
|
+
rb_ext_ractor_safe(true);
|
|
1704
|
+
#endif
|
|
1705
|
+
|
|
1706
|
+
rb_cPrism = rb_define_module("Prism");
|
|
1707
|
+
rb_cPrismNode = rb_define_class_under(rb_cPrism, "Node", rb_cObject);
|
|
1708
|
+
rb_cPrismSource = rb_define_class_under(rb_cPrism, "Source", rb_cObject);
|
|
1709
|
+
rb_cPrismToken = rb_define_class_under(rb_cPrism, "Token", rb_cObject);
|
|
1710
|
+
rb_cPrismLocation = rb_define_class_under(rb_cPrism, "Location", rb_cObject);
|
|
1711
|
+
rb_cPrismComment = rb_define_class_under(rb_cPrism, "Comment", rb_cObject);
|
|
1712
|
+
rb_cPrismInlineComment = rb_define_class_under(rb_cPrism, "InlineComment", rb_cPrismComment);
|
|
1713
|
+
rb_cPrismEmbDocComment = rb_define_class_under(rb_cPrism, "EmbDocComment", rb_cPrismComment);
|
|
1714
|
+
rb_cPrismMagicComment = rb_define_class_under(rb_cPrism, "MagicComment", rb_cObject);
|
|
1715
|
+
rb_cPrismParseError = rb_define_class_under(rb_cPrism, "ParseError", rb_cObject);
|
|
1716
|
+
rb_cPrismParseWarning = rb_define_class_under(rb_cPrism, "ParseWarning", rb_cObject);
|
|
1717
|
+
rb_cPrismResult = rb_define_class_under(rb_cPrism, "Result", rb_cObject);
|
|
1718
|
+
rb_cPrismParseResult = rb_define_class_under(rb_cPrism, "ParseResult", rb_cPrismResult);
|
|
1719
|
+
rb_cPrismLexResult = rb_define_class_under(rb_cPrism, "LexResult", rb_cPrismResult);
|
|
1720
|
+
rb_cPrismParseLexResult = rb_define_class_under(rb_cPrism, "ParseLexResult", rb_cPrismResult);
|
|
1721
|
+
rb_cPrismStringQuery = rb_define_class_under(rb_cPrism, "StringQuery", rb_cObject);
|
|
1722
|
+
rb_cPrismScope = rb_define_class_under(rb_cPrism, "Scope", rb_cObject);
|
|
1723
|
+
|
|
1724
|
+
rb_cPrismCurrentVersionError = rb_const_get(rb_cPrism, rb_intern("CurrentVersionError"));
|
|
1725
|
+
|
|
1726
|
+
/* Intern all of the IDs eagerly that we support so that we do not have to
|
|
1727
|
+
* do it every time we parse. */
|
|
1728
|
+
rb_id_option_attach_comments = rb_intern_const("attach_comments");
|
|
1729
|
+
rb_id_option_command_line = rb_intern_const("command_line");
|
|
1730
|
+
rb_id_option_encoding = rb_intern_const("encoding");
|
|
1731
|
+
rb_id_option_filepath = rb_intern_const("filepath");
|
|
1732
|
+
rb_id_option_freeze = rb_intern_const("freeze");
|
|
1733
|
+
rb_id_option_frozen_string_literal = rb_intern_const("frozen_string_literal");
|
|
1734
|
+
rb_id_option_line = rb_intern_const("line");
|
|
1735
|
+
rb_id_option_main_script = rb_intern_const("main_script");
|
|
1736
|
+
rb_id_option_partial_script = rb_intern_const("partial_script");
|
|
1737
|
+
rb_id_option_raise_error = rb_intern_const("raise_error");
|
|
1738
|
+
rb_id_option_scopes = rb_intern_const("scopes");
|
|
1739
|
+
rb_id_option_version = rb_intern_const("version");
|
|
1740
|
+
|
|
1741
|
+
rb_id_source_for = rb_intern("for");
|
|
1742
|
+
|
|
1743
|
+
rb_id_forwarding_positionals = rb_intern("*");
|
|
1744
|
+
rb_id_forwarding_keywords = rb_intern("**");
|
|
1745
|
+
rb_id_forwarding_block = rb_intern("&");
|
|
1746
|
+
rb_id_forwarding_all = rb_intern("...");
|
|
1747
|
+
|
|
1748
|
+
rb_id_raise_error_plain = rb_intern_const("plain");
|
|
1749
|
+
rb_id_raise_error_style = rb_intern_const("style");
|
|
1750
|
+
rb_id_raise_error_color = rb_intern_const("color");
|
|
1751
|
+
|
|
1752
|
+
/**
|
|
1753
|
+
* The version of the prism library.
|
|
1754
|
+
*/
|
|
1755
|
+
rb_define_const(rb_cPrism, "VERSION", rb_str_freeze(rb_str_new_cstr(EXPECTED_PRISM_VERSION)));
|
|
1756
|
+
|
|
1757
|
+
rb_define_singleton_method(rb_cPrism, "lex", lex, -1);
|
|
1758
|
+
rb_define_singleton_method(rb_cPrism, "lex_file", lex_file, -1);
|
|
1759
|
+
rb_define_singleton_method(rb_cPrism, "parse", parse, -1);
|
|
1760
|
+
rb_define_singleton_method(rb_cPrism, "parse_file", parse_file, -1);
|
|
1761
|
+
rb_define_singleton_method(rb_cPrism, "profile", profile, -1);
|
|
1762
|
+
rb_define_singleton_method(rb_cPrism, "profile_file", profile_file, -1);
|
|
1763
|
+
rb_define_singleton_method(rb_cPrism, "parse_stream", parse_stream, -1);
|
|
1764
|
+
rb_define_singleton_method(rb_cPrism, "parse_comments", parse_comments, -1);
|
|
1765
|
+
rb_define_singleton_method(rb_cPrism, "parse_file_comments", parse_file_comments, -1);
|
|
1766
|
+
rb_define_singleton_method(rb_cPrism, "parse_lex", parse_lex, -1);
|
|
1767
|
+
rb_define_singleton_method(rb_cPrism, "parse_lex_file", parse_lex_file, -1);
|
|
1768
|
+
rb_define_singleton_method(rb_cPrism, "parse_success?", parse_success_p, -1);
|
|
1769
|
+
rb_define_singleton_method(rb_cPrism, "parse_failure?", parse_failure_p, -1);
|
|
1770
|
+
rb_define_singleton_method(rb_cPrism, "parse_file_success?", parse_file_success_p, -1);
|
|
1771
|
+
rb_define_singleton_method(rb_cPrism, "parse_file_failure?", parse_file_failure_p, -1);
|
|
1772
|
+
|
|
1773
|
+
#ifndef PRISM_EXCLUDE_SERIALIZATION
|
|
1774
|
+
rb_define_singleton_method(rb_cPrism, "dump", dump, -1);
|
|
1775
|
+
rb_define_singleton_method(rb_cPrism, "dump_file", dump_file, -1);
|
|
1776
|
+
#endif
|
|
1777
|
+
|
|
1778
|
+
rb_define_singleton_method(rb_cPrismStringQuery, "local?", string_query_local_p, 1);
|
|
1779
|
+
rb_define_singleton_method(rb_cPrismStringQuery, "constant?", string_query_constant_p, 1);
|
|
1780
|
+
rb_define_singleton_method(rb_cPrismStringQuery, "method_name?", string_query_method_name_p, 1);
|
|
1781
|
+
|
|
1782
|
+
Init_prism_api_node();
|
|
1783
|
+
}
|