prism 0.19.0 → 0.24.0

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.
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