prism 0.19.0 → 0.24.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +102 -1
- data/Makefile +5 -0
- data/README.md +9 -6
- data/config.yml +236 -38
- data/docs/build_system.md +19 -2
- data/docs/cruby_compilation.md +27 -0
- data/docs/parser_translation.md +34 -0
- data/docs/parsing_rules.md +19 -0
- data/docs/releasing.md +84 -16
- data/docs/ruby_api.md +1 -1
- data/docs/ruby_parser_translation.md +19 -0
- data/docs/serialization.md +19 -5
- data/ext/prism/api_node.c +1989 -1525
- data/ext/prism/extension.c +130 -30
- data/ext/prism/extension.h +2 -2
- data/include/prism/ast.h +1700 -505
- data/include/prism/defines.h +8 -0
- data/include/prism/diagnostic.h +49 -7
- data/include/prism/encoding.h +17 -0
- data/include/prism/options.h +40 -14
- data/include/prism/parser.h +34 -18
- data/include/prism/util/pm_buffer.h +9 -0
- data/include/prism/util/pm_constant_pool.h +18 -0
- data/include/prism/util/pm_newline_list.h +4 -14
- data/include/prism/util/pm_strpbrk.h +4 -1
- data/include/prism/version.h +2 -2
- data/include/prism.h +19 -2
- data/lib/prism/debug.rb +11 -5
- data/lib/prism/desugar_compiler.rb +225 -80
- data/lib/prism/dot_visitor.rb +36 -14
- data/lib/prism/dsl.rb +302 -299
- data/lib/prism/ffi.rb +107 -76
- data/lib/prism/lex_compat.rb +17 -1
- data/lib/prism/node.rb +4580 -2607
- data/lib/prism/node_ext.rb +27 -4
- data/lib/prism/parse_result.rb +75 -29
- data/lib/prism/serialize.rb +633 -305
- data/lib/prism/translation/parser/compiler.rb +1838 -0
- data/lib/prism/translation/parser/lexer.rb +335 -0
- data/lib/prism/translation/parser/rubocop.rb +45 -0
- data/lib/prism/translation/parser.rb +190 -0
- data/lib/prism/translation/parser33.rb +12 -0
- data/lib/prism/translation/parser34.rb +12 -0
- data/lib/prism/translation/ripper.rb +696 -0
- data/lib/prism/translation/ruby_parser.rb +1521 -0
- data/lib/prism/translation.rb +11 -0
- data/lib/prism.rb +1 -1
- data/prism.gemspec +18 -7
- data/rbi/prism.rbi +150 -88
- data/rbi/prism_static.rbi +15 -3
- data/sig/prism.rbs +996 -961
- data/sig/prism_static.rbs +123 -46
- data/src/diagnostic.c +264 -219
- data/src/encoding.c +21 -26
- data/src/node.c +2 -6
- data/src/options.c +29 -5
- data/src/prettyprint.c +176 -44
- data/src/prism.c +1499 -564
- data/src/serialize.c +35 -21
- data/src/token_type.c +353 -4
- data/src/util/pm_buffer.c +11 -0
- data/src/util/pm_constant_pool.c +37 -11
- data/src/util/pm_newline_list.c +6 -15
- data/src/util/pm_string.c +0 -7
- data/src/util/pm_strpbrk.c +122 -14
- metadata +16 -5
- data/docs/building.md +0 -29
- data/lib/prism/ripper_compat.rb +0 -207
data/src/util/pm_newline_list.c
CHANGED
@@ -45,25 +45,13 @@ pm_newline_list_append(pm_newline_list_t *list, const uint8_t *cursor) {
|
|
45
45
|
return true;
|
46
46
|
}
|
47
47
|
|
48
|
-
/**
|
49
|
-
* Conditionally append a new offset to the newline list, if the value passed in
|
50
|
-
* is a newline.
|
51
|
-
*/
|
52
|
-
bool
|
53
|
-
pm_newline_list_check_append(pm_newline_list_t *list, const uint8_t *cursor) {
|
54
|
-
if (*cursor != '\n') {
|
55
|
-
return true;
|
56
|
-
}
|
57
|
-
return pm_newline_list_append(list, cursor);
|
58
|
-
}
|
59
|
-
|
60
48
|
/**
|
61
49
|
* Returns the line and column of the given offset. If the offset is not in the
|
62
50
|
* list, the line and column of the closest offset less than the given offset
|
63
51
|
* are returned.
|
64
52
|
*/
|
65
53
|
pm_line_column_t
|
66
|
-
pm_newline_list_line_column(const pm_newline_list_t *list, const uint8_t *cursor) {
|
54
|
+
pm_newline_list_line_column(const pm_newline_list_t *list, const uint8_t *cursor, int32_t start_line) {
|
67
55
|
assert(cursor >= list->start);
|
68
56
|
size_t offset = (size_t) (cursor - list->start);
|
69
57
|
|
@@ -74,7 +62,7 @@ pm_newline_list_line_column(const pm_newline_list_t *list, const uint8_t *cursor
|
|
74
62
|
size_t mid = left + (right - left) / 2;
|
75
63
|
|
76
64
|
if (list->offsets[mid] == offset) {
|
77
|
-
return ((pm_line_column_t) { mid, 0 });
|
65
|
+
return ((pm_line_column_t) { ((int32_t) mid) + start_line, 0 });
|
78
66
|
}
|
79
67
|
|
80
68
|
if (list->offsets[mid] < offset) {
|
@@ -84,7 +72,10 @@ pm_newline_list_line_column(const pm_newline_list_t *list, const uint8_t *cursor
|
|
84
72
|
}
|
85
73
|
}
|
86
74
|
|
87
|
-
return ((pm_line_column_t) {
|
75
|
+
return ((pm_line_column_t) {
|
76
|
+
.line = ((int32_t) left) + start_line - 1,
|
77
|
+
.column = (uint32_t) (offset - list->offsets[left - 1])
|
78
|
+
});
|
88
79
|
}
|
89
80
|
|
90
81
|
/**
|
data/src/util/pm_string.c
CHANGED
@@ -65,7 +65,6 @@ pm_string_mapped_init(pm_string_t *string, const char *filepath) {
|
|
65
65
|
HANDLE file = CreateFile(filepath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
66
66
|
|
67
67
|
if (file == INVALID_HANDLE_VALUE) {
|
68
|
-
perror("CreateFile failed");
|
69
68
|
return false;
|
70
69
|
}
|
71
70
|
|
@@ -73,7 +72,6 @@ pm_string_mapped_init(pm_string_t *string, const char *filepath) {
|
|
73
72
|
DWORD file_size = GetFileSize(file, NULL);
|
74
73
|
if (file_size == INVALID_FILE_SIZE) {
|
75
74
|
CloseHandle(file);
|
76
|
-
perror("GetFileSize failed");
|
77
75
|
return false;
|
78
76
|
}
|
79
77
|
|
@@ -90,7 +88,6 @@ pm_string_mapped_init(pm_string_t *string, const char *filepath) {
|
|
90
88
|
HANDLE mapping = CreateFileMapping(file, NULL, PAGE_READONLY, 0, 0, NULL);
|
91
89
|
if (mapping == NULL) {
|
92
90
|
CloseHandle(file);
|
93
|
-
perror("CreateFileMapping failed");
|
94
91
|
return false;
|
95
92
|
}
|
96
93
|
|
@@ -100,7 +97,6 @@ pm_string_mapped_init(pm_string_t *string, const char *filepath) {
|
|
100
97
|
CloseHandle(file);
|
101
98
|
|
102
99
|
if (source == NULL) {
|
103
|
-
perror("MapViewOfFile failed");
|
104
100
|
return false;
|
105
101
|
}
|
106
102
|
|
@@ -110,7 +106,6 @@ pm_string_mapped_init(pm_string_t *string, const char *filepath) {
|
|
110
106
|
// Open the file for reading
|
111
107
|
int fd = open(filepath, O_RDONLY);
|
112
108
|
if (fd == -1) {
|
113
|
-
perror("open");
|
114
109
|
return false;
|
115
110
|
}
|
116
111
|
|
@@ -118,7 +113,6 @@ pm_string_mapped_init(pm_string_t *string, const char *filepath) {
|
|
118
113
|
struct stat sb;
|
119
114
|
if (fstat(fd, &sb) == -1) {
|
120
115
|
close(fd);
|
121
|
-
perror("fstat");
|
122
116
|
return false;
|
123
117
|
}
|
124
118
|
|
@@ -135,7 +129,6 @@ pm_string_mapped_init(pm_string_t *string, const char *filepath) {
|
|
135
129
|
|
136
130
|
source = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
|
137
131
|
if (source == MAP_FAILED) {
|
138
|
-
perror("Map failed");
|
139
132
|
return false;
|
140
133
|
}
|
141
134
|
|
data/src/util/pm_strpbrk.c
CHANGED
@@ -1,10 +1,18 @@
|
|
1
1
|
#include "prism/util/pm_strpbrk.h"
|
2
2
|
|
3
3
|
/**
|
4
|
-
*
|
4
|
+
* Add an invalid multibyte character error to the parser.
|
5
|
+
*/
|
6
|
+
static inline void
|
7
|
+
pm_strpbrk_invalid_multibyte_character(pm_parser_t *parser, const uint8_t *start, const uint8_t *end) {
|
8
|
+
pm_diagnostic_list_append_format(&parser->error_list, start, end, PM_ERR_INVALID_MULTIBYTE_CHARACTER, *start);
|
9
|
+
}
|
10
|
+
|
11
|
+
/**
|
12
|
+
* This is the default path.
|
5
13
|
*/
|
6
14
|
static inline const uint8_t *
|
7
|
-
|
15
|
+
pm_strpbrk_utf8(pm_parser_t *parser, const uint8_t *source, const uint8_t *charset, size_t maximum, bool validate) {
|
8
16
|
size_t index = 0;
|
9
17
|
|
10
18
|
while (index < maximum) {
|
@@ -12,22 +20,39 @@ pm_strpbrk_multi_byte(const pm_parser_t *parser, const uint8_t *source, const ui
|
|
12
20
|
return source + index;
|
13
21
|
}
|
14
22
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
23
|
+
if (source[index] < 0x80) {
|
24
|
+
index++;
|
25
|
+
} else {
|
26
|
+
size_t width = pm_encoding_utf_8_char_width(source + index, (ptrdiff_t) (maximum - index));
|
19
27
|
|
20
|
-
|
28
|
+
if (width > 0) {
|
29
|
+
index += width;
|
30
|
+
} else if (!validate) {
|
31
|
+
index++;
|
32
|
+
} else {
|
33
|
+
// At this point we know we have an invalid multibyte character.
|
34
|
+
// We'll walk forward as far as we can until we find the next
|
35
|
+
// valid character so that we don't spam the user with a ton of
|
36
|
+
// the same kind of error.
|
37
|
+
const size_t start = index;
|
38
|
+
|
39
|
+
do {
|
40
|
+
index++;
|
41
|
+
} while (index < maximum && pm_encoding_utf_8_char_width(source + index, (ptrdiff_t) (maximum - index)) == 0);
|
42
|
+
|
43
|
+
pm_strpbrk_invalid_multibyte_character(parser, source + start, source + index);
|
44
|
+
}
|
45
|
+
}
|
21
46
|
}
|
22
47
|
|
23
48
|
return NULL;
|
24
49
|
}
|
25
50
|
|
26
51
|
/**
|
27
|
-
* This is the
|
52
|
+
* This is the path when the encoding is ASCII-8BIT.
|
28
53
|
*/
|
29
54
|
static inline const uint8_t *
|
30
|
-
|
55
|
+
pm_strpbrk_ascii_8bit(const uint8_t *source, const uint8_t *charset, size_t maximum) {
|
31
56
|
size_t index = 0;
|
32
57
|
|
33
58
|
while (index < maximum) {
|
@@ -41,6 +66,85 @@ pm_strpbrk_single_byte(const uint8_t *source, const uint8_t *charset, size_t max
|
|
41
66
|
return NULL;
|
42
67
|
}
|
43
68
|
|
69
|
+
/**
|
70
|
+
* This is the slow path that does care about the encoding.
|
71
|
+
*/
|
72
|
+
static inline const uint8_t *
|
73
|
+
pm_strpbrk_multi_byte(pm_parser_t *parser, const uint8_t *source, const uint8_t *charset, size_t maximum, bool validate) {
|
74
|
+
size_t index = 0;
|
75
|
+
|
76
|
+
while (index < maximum) {
|
77
|
+
if (strchr((const char *) charset, source[index]) != NULL) {
|
78
|
+
return source + index;
|
79
|
+
}
|
80
|
+
|
81
|
+
if (source[index] < 0x80) {
|
82
|
+
index++;
|
83
|
+
} else {
|
84
|
+
size_t width = parser->encoding->char_width(source + index, (ptrdiff_t) (maximum - index));
|
85
|
+
|
86
|
+
if (width > 0) {
|
87
|
+
index += width;
|
88
|
+
} else if (!validate) {
|
89
|
+
index++;
|
90
|
+
} else {
|
91
|
+
// At this point we know we have an invalid multibyte character.
|
92
|
+
// We'll walk forward as far as we can until we find the next
|
93
|
+
// valid character so that we don't spam the user with a ton of
|
94
|
+
// the same kind of error.
|
95
|
+
const size_t start = index;
|
96
|
+
|
97
|
+
do {
|
98
|
+
index++;
|
99
|
+
} while (index < maximum && parser->encoding->char_width(source + index, (ptrdiff_t) (maximum - index)) == 0);
|
100
|
+
|
101
|
+
pm_strpbrk_invalid_multibyte_character(parser, source + start, source + index);
|
102
|
+
}
|
103
|
+
}
|
104
|
+
}
|
105
|
+
|
106
|
+
return NULL;
|
107
|
+
}
|
108
|
+
|
109
|
+
/**
|
110
|
+
* This is the fast path that does not care about the encoding because we know
|
111
|
+
* the encoding only supports single-byte characters.
|
112
|
+
*/
|
113
|
+
static inline const uint8_t *
|
114
|
+
pm_strpbrk_single_byte(pm_parser_t *parser, const uint8_t *source, const uint8_t *charset, size_t maximum, bool validate) {
|
115
|
+
size_t index = 0;
|
116
|
+
|
117
|
+
while (index < maximum) {
|
118
|
+
if (strchr((const char *) charset, source[index]) != NULL) {
|
119
|
+
return source + index;
|
120
|
+
}
|
121
|
+
|
122
|
+
if (source[index] < 0x80 || !validate) {
|
123
|
+
index++;
|
124
|
+
} else {
|
125
|
+
size_t width = parser->encoding->char_width(source + index, (ptrdiff_t) (maximum - index));
|
126
|
+
|
127
|
+
if (width > 0) {
|
128
|
+
index += width;
|
129
|
+
} else {
|
130
|
+
// At this point we know we have an invalid multibyte character.
|
131
|
+
// We'll walk forward as far as we can until we find the next
|
132
|
+
// valid character so that we don't spam the user with a ton of
|
133
|
+
// the same kind of error.
|
134
|
+
const size_t start = index;
|
135
|
+
|
136
|
+
do {
|
137
|
+
index++;
|
138
|
+
} while (index < maximum && parser->encoding->char_width(source + index, (ptrdiff_t) (maximum - index)) == 0);
|
139
|
+
|
140
|
+
pm_strpbrk_invalid_multibyte_character(parser, source + start, source + index);
|
141
|
+
}
|
142
|
+
}
|
143
|
+
}
|
144
|
+
|
145
|
+
return NULL;
|
146
|
+
}
|
147
|
+
|
44
148
|
/**
|
45
149
|
* Here we have rolled our own version of strpbrk. The standard library strpbrk
|
46
150
|
* has undefined behavior when the source string is not null-terminated. We want
|
@@ -57,16 +161,20 @@ pm_strpbrk_single_byte(const uint8_t *source, const uint8_t *charset, size_t max
|
|
57
161
|
*
|
58
162
|
* Finally, we want to support encodings wherein the charset could contain
|
59
163
|
* characters that are trailing bytes of multi-byte characters. For example, in
|
60
|
-
*
|
164
|
+
* Shift_JIS, the backslash character can be a trailing byte. In that case we
|
61
165
|
* need to take a slower path and iterate one multi-byte character at a time.
|
62
166
|
*/
|
63
167
|
const uint8_t *
|
64
|
-
pm_strpbrk(
|
168
|
+
pm_strpbrk(pm_parser_t *parser, const uint8_t *source, const uint8_t *charset, ptrdiff_t length, bool validate) {
|
65
169
|
if (length <= 0) {
|
66
170
|
return NULL;
|
67
|
-
} else if (parser->encoding_changed
|
68
|
-
return
|
171
|
+
} else if (!parser->encoding_changed) {
|
172
|
+
return pm_strpbrk_utf8(parser, source, charset, (size_t) length, validate);
|
173
|
+
} else if (parser->encoding == PM_ENCODING_ASCII_8BIT_ENTRY) {
|
174
|
+
return pm_strpbrk_ascii_8bit(source, charset, (size_t) length);
|
175
|
+
} else if (parser->encoding->multibyte) {
|
176
|
+
return pm_strpbrk_multi_byte(parser, source, charset, (size_t) length, validate);
|
69
177
|
} else {
|
70
|
-
return pm_strpbrk_single_byte(source, charset, (size_t) length);
|
178
|
+
return pm_strpbrk_single_byte(parser, source, charset, (size_t) length, validate);
|
71
179
|
}
|
72
180
|
}
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: prism
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.24.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Shopify
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2024-02-15 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description:
|
14
14
|
email:
|
@@ -26,8 +26,8 @@ files:
|
|
26
26
|
- README.md
|
27
27
|
- config.yml
|
28
28
|
- docs/build_system.md
|
29
|
-
- docs/building.md
|
30
29
|
- docs/configuration.md
|
30
|
+
- docs/cruby_compilation.md
|
31
31
|
- docs/design.md
|
32
32
|
- docs/encoding.md
|
33
33
|
- docs/fuzzing.md
|
@@ -35,9 +35,12 @@ files:
|
|
35
35
|
- docs/javascript.md
|
36
36
|
- docs/local_variable_depth.md
|
37
37
|
- docs/mapping.md
|
38
|
+
- docs/parser_translation.md
|
39
|
+
- docs/parsing_rules.md
|
38
40
|
- docs/releasing.md
|
39
41
|
- docs/ripper.md
|
40
42
|
- docs/ruby_api.md
|
43
|
+
- docs/ruby_parser_translation.md
|
41
44
|
- docs/serialization.md
|
42
45
|
- docs/testing.md
|
43
46
|
- ext/prism/api_node.c
|
@@ -86,8 +89,16 @@ files:
|
|
86
89
|
- lib/prism/parse_result/comments.rb
|
87
90
|
- lib/prism/parse_result/newlines.rb
|
88
91
|
- lib/prism/pattern.rb
|
89
|
-
- lib/prism/ripper_compat.rb
|
90
92
|
- lib/prism/serialize.rb
|
93
|
+
- lib/prism/translation.rb
|
94
|
+
- lib/prism/translation/parser.rb
|
95
|
+
- lib/prism/translation/parser/compiler.rb
|
96
|
+
- lib/prism/translation/parser/lexer.rb
|
97
|
+
- lib/prism/translation/parser/rubocop.rb
|
98
|
+
- lib/prism/translation/parser33.rb
|
99
|
+
- lib/prism/translation/parser34.rb
|
100
|
+
- lib/prism/translation/ripper.rb
|
101
|
+
- lib/prism/translation/ruby_parser.rb
|
91
102
|
- lib/prism/visitor.rb
|
92
103
|
- prism.gemspec
|
93
104
|
- rbi/prism.rbi
|
@@ -130,7 +141,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
130
141
|
requirements:
|
131
142
|
- - ">="
|
132
143
|
- !ruby/object:Gem::Version
|
133
|
-
version:
|
144
|
+
version: 2.7.0
|
134
145
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
135
146
|
requirements:
|
136
147
|
- - ">="
|
data/docs/building.md
DELETED
@@ -1,29 +0,0 @@
|
|
1
|
-
# Building
|
2
|
-
|
3
|
-
The following describes how to build prism from source. This comes directly from the [Makefile](../Makefile).
|
4
|
-
|
5
|
-
## Common
|
6
|
-
|
7
|
-
All of the source files match `src/**/*.c` and all of the headers match `include/**/*.h`.
|
8
|
-
|
9
|
-
The following flags should be used to compile prism:
|
10
|
-
|
11
|
-
* `-std=c99` - Use the C99 standard
|
12
|
-
* `-Wall -Wconversion -Wextra -Wpedantic -Wundef -Wno-missing-braces` - Enable the warnings we care about
|
13
|
-
* `-Werror` - Treat warnings as errors
|
14
|
-
* `-fvisibility=hidden` - Hide all symbols by default
|
15
|
-
|
16
|
-
## Shared
|
17
|
-
|
18
|
-
If you want to build prism as a shared library and link against it, you should compile with:
|
19
|
-
|
20
|
-
* `-fPIC -shared` - Compile as a shared library
|
21
|
-
* `-DPRISM_EXPORT_SYMBOLS` - Export the symbols (by default nothing is exported)
|
22
|
-
|
23
|
-
## Flags
|
24
|
-
|
25
|
-
`make` respects the `MAKEFLAGS` environment variable. As such, to speed up the build you can run:
|
26
|
-
|
27
|
-
```
|
28
|
-
MAKEFLAGS="-j10" bundle exec rake compile
|
29
|
-
```
|
data/lib/prism/ripper_compat.rb
DELETED
@@ -1,207 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "ripper"
|
4
|
-
|
5
|
-
module Prism
|
6
|
-
# Note: This integration is not finished, and therefore still has many
|
7
|
-
# inconsistencies with Ripper. If you'd like to help out, pull requests would
|
8
|
-
# be greatly appreciated!
|
9
|
-
#
|
10
|
-
# This class is meant to provide a compatibility layer between prism and
|
11
|
-
# Ripper. It functions by parsing the entire tree first and then walking it
|
12
|
-
# and executing each of the Ripper callbacks as it goes.
|
13
|
-
#
|
14
|
-
# This class is going to necessarily be slower than the native Ripper API. It
|
15
|
-
# is meant as a stopgap until developers migrate to using prism. It is also
|
16
|
-
# meant as a test harness for the prism parser.
|
17
|
-
#
|
18
|
-
# To use this class, you treat `Prism::RipperCompat` effectively as you would
|
19
|
-
# treat the `Ripper` class.
|
20
|
-
class RipperCompat < Visitor
|
21
|
-
# This class mirrors the ::Ripper::SexpBuilder subclass of ::Ripper that
|
22
|
-
# returns the arrays of [type, *children].
|
23
|
-
class SexpBuilder < RipperCompat
|
24
|
-
private
|
25
|
-
|
26
|
-
Ripper::PARSER_EVENTS.each do |event|
|
27
|
-
define_method(:"on_#{event}") do |*args|
|
28
|
-
[event, *args]
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
Ripper::SCANNER_EVENTS.each do |event|
|
33
|
-
define_method(:"on_#{event}") do |value|
|
34
|
-
[:"@#{event}", value, [lineno, column]]
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
# This class mirrors the ::Ripper::SexpBuilderPP subclass of ::Ripper that
|
40
|
-
# returns the same values as ::Ripper::SexpBuilder except with a couple of
|
41
|
-
# niceties that flatten linked lists into arrays.
|
42
|
-
class SexpBuilderPP < SexpBuilder
|
43
|
-
private
|
44
|
-
|
45
|
-
def _dispatch_event_new # :nodoc:
|
46
|
-
[]
|
47
|
-
end
|
48
|
-
|
49
|
-
def _dispatch_event_push(list, item) # :nodoc:
|
50
|
-
list << item
|
51
|
-
list
|
52
|
-
end
|
53
|
-
|
54
|
-
Ripper::PARSER_EVENT_TABLE.each do |event, arity|
|
55
|
-
case event
|
56
|
-
when /_new\z/
|
57
|
-
alias_method :"on_#{event}", :_dispatch_event_new if arity == 0
|
58
|
-
when /_add\z/
|
59
|
-
alias_method :"on_#{event}", :_dispatch_event_push
|
60
|
-
end
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
# The source that is being parsed.
|
65
|
-
attr_reader :source
|
66
|
-
|
67
|
-
# The current line number of the parser.
|
68
|
-
attr_reader :lineno
|
69
|
-
|
70
|
-
# The current column number of the parser.
|
71
|
-
attr_reader :column
|
72
|
-
|
73
|
-
# Create a new RipperCompat object with the given source.
|
74
|
-
def initialize(source)
|
75
|
-
@source = source
|
76
|
-
@result = nil
|
77
|
-
@lineno = nil
|
78
|
-
@column = nil
|
79
|
-
end
|
80
|
-
|
81
|
-
############################################################################
|
82
|
-
# Public interface
|
83
|
-
############################################################################
|
84
|
-
|
85
|
-
# True if the parser encountered an error during parsing.
|
86
|
-
def error?
|
87
|
-
result.failure?
|
88
|
-
end
|
89
|
-
|
90
|
-
# Parse the source and return the result.
|
91
|
-
def parse
|
92
|
-
result.magic_comments.each do |magic_comment|
|
93
|
-
on_magic_comment(magic_comment.key, magic_comment.value)
|
94
|
-
end
|
95
|
-
|
96
|
-
if error?
|
97
|
-
result.errors.each do |error|
|
98
|
-
on_parse_error(error.message)
|
99
|
-
end
|
100
|
-
else
|
101
|
-
result.value.accept(self)
|
102
|
-
end
|
103
|
-
end
|
104
|
-
|
105
|
-
############################################################################
|
106
|
-
# Visitor methods
|
107
|
-
############################################################################
|
108
|
-
|
109
|
-
# Visit a CallNode node.
|
110
|
-
def visit_call_node(node)
|
111
|
-
if !node.message.match?(/^[[:alpha:]_]/) && node.opening_loc.nil? && node.arguments&.arguments&.length == 1
|
112
|
-
left = visit(node.receiver)
|
113
|
-
right = visit(node.arguments.arguments.first)
|
114
|
-
|
115
|
-
bounds(node.location)
|
116
|
-
on_binary(left, node.name, right)
|
117
|
-
else
|
118
|
-
raise NotImplementedError
|
119
|
-
end
|
120
|
-
end
|
121
|
-
|
122
|
-
# Visit a FloatNode node.
|
123
|
-
def visit_float_node(node)
|
124
|
-
bounds(node.location)
|
125
|
-
on_float(node.slice)
|
126
|
-
end
|
127
|
-
|
128
|
-
# Visit a ImaginaryNode node.
|
129
|
-
def visit_imaginary_node(node)
|
130
|
-
bounds(node.location)
|
131
|
-
on_imaginary(node.slice)
|
132
|
-
end
|
133
|
-
|
134
|
-
# Visit an IntegerNode node.
|
135
|
-
def visit_integer_node(node)
|
136
|
-
bounds(node.location)
|
137
|
-
on_int(node.slice)
|
138
|
-
end
|
139
|
-
|
140
|
-
# Visit a RationalNode node.
|
141
|
-
def visit_rational_node(node)
|
142
|
-
bounds(node.location)
|
143
|
-
on_rational(node.slice)
|
144
|
-
end
|
145
|
-
|
146
|
-
# Visit a StatementsNode node.
|
147
|
-
def visit_statements_node(node)
|
148
|
-
bounds(node.location)
|
149
|
-
node.body.inject(on_stmts_new) do |stmts, stmt|
|
150
|
-
on_stmts_add(stmts, visit(stmt))
|
151
|
-
end
|
152
|
-
end
|
153
|
-
|
154
|
-
# Visit a ProgramNode node.
|
155
|
-
def visit_program_node(node)
|
156
|
-
statements = visit(node.statements)
|
157
|
-
bounds(node.location)
|
158
|
-
on_program(statements)
|
159
|
-
end
|
160
|
-
|
161
|
-
############################################################################
|
162
|
-
# Entrypoints for subclasses
|
163
|
-
############################################################################
|
164
|
-
|
165
|
-
# This is a convenience method that runs the SexpBuilder subclass parser.
|
166
|
-
def self.sexp_raw(source)
|
167
|
-
SexpBuilder.new(source).parse
|
168
|
-
end
|
169
|
-
|
170
|
-
# This is a convenience method that runs the SexpBuilderPP subclass parser.
|
171
|
-
def self.sexp(source)
|
172
|
-
SexpBuilderPP.new(source).parse
|
173
|
-
end
|
174
|
-
|
175
|
-
private
|
176
|
-
|
177
|
-
# This method is responsible for updating lineno and column information
|
178
|
-
# to reflect the current node.
|
179
|
-
#
|
180
|
-
# This method could be drastically improved with some caching on the start
|
181
|
-
# of every line, but for now it's good enough.
|
182
|
-
def bounds(location)
|
183
|
-
@lineno = location.start_line
|
184
|
-
@column = location.start_column
|
185
|
-
end
|
186
|
-
|
187
|
-
# Lazily initialize the parse result.
|
188
|
-
def result
|
189
|
-
@result ||= Prism.parse(source)
|
190
|
-
end
|
191
|
-
|
192
|
-
def _dispatch0; end # :nodoc:
|
193
|
-
def _dispatch1(_); end # :nodoc:
|
194
|
-
def _dispatch2(_, _); end # :nodoc:
|
195
|
-
def _dispatch3(_, _, _); end # :nodoc:
|
196
|
-
def _dispatch4(_, _, _, _); end # :nodoc:
|
197
|
-
def _dispatch5(_, _, _, _, _); end # :nodoc:
|
198
|
-
def _dispatch7(_, _, _, _, _, _, _); end # :nodoc:
|
199
|
-
|
200
|
-
alias_method :on_parse_error, :_dispatch1
|
201
|
-
alias_method :on_magic_comment, :_dispatch2
|
202
|
-
|
203
|
-
(Ripper::SCANNER_EVENT_TABLE.merge(Ripper::PARSER_EVENT_TABLE)).each do |event, arity|
|
204
|
-
alias_method :"on_#{event}", :"_dispatch#{arity}"
|
205
|
-
end
|
206
|
-
end
|
207
|
-
end
|