prism 0.19.0 → 0.24.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (69) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +102 -1
  3. data/Makefile +5 -0
  4. data/README.md +9 -6
  5. data/config.yml +236 -38
  6. data/docs/build_system.md +19 -2
  7. data/docs/cruby_compilation.md +27 -0
  8. data/docs/parser_translation.md +34 -0
  9. data/docs/parsing_rules.md +19 -0
  10. data/docs/releasing.md +84 -16
  11. data/docs/ruby_api.md +1 -1
  12. data/docs/ruby_parser_translation.md +19 -0
  13. data/docs/serialization.md +19 -5
  14. data/ext/prism/api_node.c +1989 -1525
  15. data/ext/prism/extension.c +130 -30
  16. data/ext/prism/extension.h +2 -2
  17. data/include/prism/ast.h +1700 -505
  18. data/include/prism/defines.h +8 -0
  19. data/include/prism/diagnostic.h +49 -7
  20. data/include/prism/encoding.h +17 -0
  21. data/include/prism/options.h +40 -14
  22. data/include/prism/parser.h +34 -18
  23. data/include/prism/util/pm_buffer.h +9 -0
  24. data/include/prism/util/pm_constant_pool.h +18 -0
  25. data/include/prism/util/pm_newline_list.h +4 -14
  26. data/include/prism/util/pm_strpbrk.h +4 -1
  27. data/include/prism/version.h +2 -2
  28. data/include/prism.h +19 -2
  29. data/lib/prism/debug.rb +11 -5
  30. data/lib/prism/desugar_compiler.rb +225 -80
  31. data/lib/prism/dot_visitor.rb +36 -14
  32. data/lib/prism/dsl.rb +302 -299
  33. data/lib/prism/ffi.rb +107 -76
  34. data/lib/prism/lex_compat.rb +17 -1
  35. data/lib/prism/node.rb +4580 -2607
  36. data/lib/prism/node_ext.rb +27 -4
  37. data/lib/prism/parse_result.rb +75 -29
  38. data/lib/prism/serialize.rb +633 -305
  39. data/lib/prism/translation/parser/compiler.rb +1838 -0
  40. data/lib/prism/translation/parser/lexer.rb +335 -0
  41. data/lib/prism/translation/parser/rubocop.rb +45 -0
  42. data/lib/prism/translation/parser.rb +190 -0
  43. data/lib/prism/translation/parser33.rb +12 -0
  44. data/lib/prism/translation/parser34.rb +12 -0
  45. data/lib/prism/translation/ripper.rb +696 -0
  46. data/lib/prism/translation/ruby_parser.rb +1521 -0
  47. data/lib/prism/translation.rb +11 -0
  48. data/lib/prism.rb +1 -1
  49. data/prism.gemspec +18 -7
  50. data/rbi/prism.rbi +150 -88
  51. data/rbi/prism_static.rbi +15 -3
  52. data/sig/prism.rbs +996 -961
  53. data/sig/prism_static.rbs +123 -46
  54. data/src/diagnostic.c +264 -219
  55. data/src/encoding.c +21 -26
  56. data/src/node.c +2 -6
  57. data/src/options.c +29 -5
  58. data/src/prettyprint.c +176 -44
  59. data/src/prism.c +1499 -564
  60. data/src/serialize.c +35 -21
  61. data/src/token_type.c +353 -4
  62. data/src/util/pm_buffer.c +11 -0
  63. data/src/util/pm_constant_pool.c +37 -11
  64. data/src/util/pm_newline_list.c +6 -15
  65. data/src/util/pm_string.c +0 -7
  66. data/src/util/pm_strpbrk.c +122 -14
  67. metadata +16 -5
  68. data/docs/building.md +0 -29
  69. data/lib/prism/ripper_compat.rb +0 -207
@@ -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) { left - 1, offset - list->offsets[left - 1] });
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
 
@@ -1,10 +1,18 @@
1
1
  #include "prism/util/pm_strpbrk.h"
2
2
 
3
3
  /**
4
- * This is the slow path that does care about the encoding.
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
- pm_strpbrk_multi_byte(const pm_parser_t *parser, const uint8_t *source, const uint8_t *charset, size_t maximum) {
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
- size_t width = parser->encoding->char_width(source + index, (ptrdiff_t) (maximum - index));
16
- if (width == 0) {
17
- return NULL;
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
- index += width;
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 fast path that does not care about the encoding.
52
+ * This is the path when the encoding is ASCII-8BIT.
28
53
  */
29
54
  static inline const uint8_t *
30
- pm_strpbrk_single_byte(const uint8_t *source, const uint8_t *charset, size_t maximum) {
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
- * Shift-JIS, the backslash character can be a trailing byte. In that case we
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(const pm_parser_t *parser, const uint8_t *source, const uint8_t *charset, ptrdiff_t length) {
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 && parser->encoding->multibyte) {
68
- return pm_strpbrk_multi_byte(parser, source, charset, (size_t) length);
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.19.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: 2023-12-14 00:00:00.000000000 Z
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: 3.0.0
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
- ```
@@ -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