prism 0.18.0 → 0.19.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (68) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +31 -1
  3. data/README.md +2 -1
  4. data/config.yml +188 -55
  5. data/docs/building.md +9 -2
  6. data/docs/configuration.md +10 -9
  7. data/docs/encoding.md +24 -56
  8. data/docs/local_variable_depth.md +229 -0
  9. data/docs/ruby_api.md +2 -0
  10. data/docs/serialization.md +18 -13
  11. data/ext/prism/api_node.c +337 -195
  12. data/ext/prism/extconf.rb +13 -7
  13. data/ext/prism/extension.c +96 -32
  14. data/ext/prism/extension.h +1 -1
  15. data/include/prism/ast.h +340 -137
  16. data/include/prism/defines.h +17 -0
  17. data/include/prism/diagnostic.h +11 -5
  18. data/include/prism/encoding.h +248 -0
  19. data/include/prism/options.h +2 -2
  20. data/include/prism/parser.h +62 -42
  21. data/include/prism/regexp.h +2 -2
  22. data/include/prism/util/pm_buffer.h +9 -1
  23. data/include/prism/util/pm_memchr.h +2 -2
  24. data/include/prism/util/pm_strpbrk.h +3 -3
  25. data/include/prism/version.h +2 -2
  26. data/include/prism.h +13 -15
  27. data/lib/prism/compiler.rb +12 -0
  28. data/lib/prism/debug.rb +9 -4
  29. data/lib/prism/desugar_compiler.rb +3 -3
  30. data/lib/prism/dispatcher.rb +56 -0
  31. data/lib/prism/dot_visitor.rb +476 -198
  32. data/lib/prism/dsl.rb +66 -46
  33. data/lib/prism/ffi.rb +16 -3
  34. data/lib/prism/lex_compat.rb +19 -9
  35. data/lib/prism/mutation_compiler.rb +20 -0
  36. data/lib/prism/node.rb +1173 -450
  37. data/lib/prism/node_ext.rb +41 -16
  38. data/lib/prism/parse_result.rb +12 -15
  39. data/lib/prism/ripper_compat.rb +49 -34
  40. data/lib/prism/serialize.rb +242 -212
  41. data/lib/prism/visitor.rb +12 -0
  42. data/lib/prism.rb +20 -4
  43. data/prism.gemspec +4 -10
  44. data/rbi/prism.rbi +605 -230
  45. data/rbi/prism_static.rbi +3 -0
  46. data/sig/prism.rbs +379 -124
  47. data/sig/prism_static.rbs +1 -0
  48. data/src/diagnostic.c +228 -222
  49. data/src/encoding.c +5137 -0
  50. data/src/node.c +66 -0
  51. data/src/options.c +21 -2
  52. data/src/prettyprint.c +806 -406
  53. data/src/prism.c +1092 -700
  54. data/src/regexp.c +3 -3
  55. data/src/serialize.c +227 -157
  56. data/src/util/pm_buffer.c +10 -1
  57. data/src/util/pm_memchr.c +1 -1
  58. data/src/util/pm_strpbrk.c +4 -4
  59. metadata +5 -11
  60. data/include/prism/enc/pm_encoding.h +0 -227
  61. data/src/enc/pm_big5.c +0 -116
  62. data/src/enc/pm_cp51932.c +0 -57
  63. data/src/enc/pm_euc_jp.c +0 -69
  64. data/src/enc/pm_gbk.c +0 -65
  65. data/src/enc/pm_shift_jis.c +0 -57
  66. data/src/enc/pm_tables.c +0 -2073
  67. data/src/enc/pm_unicode.c +0 -2369
  68. data/src/enc/pm_windows_31j.c +0 -57
@@ -14,8 +14,49 @@ module Prism
14
14
  end
15
15
  end
16
16
 
17
+ class InterpolatedMatchLastLineNode < Node
18
+ include RegularExpressionOptions
19
+ end
20
+
21
+ class InterpolatedRegularExpressionNode < Node
22
+ include RegularExpressionOptions
23
+ end
24
+
25
+ class MatchLastLineNode < Node
26
+ include RegularExpressionOptions
27
+ end
28
+
29
+ class RegularExpressionNode < Node
30
+ include RegularExpressionOptions
31
+ end
32
+
17
33
  private_constant :RegularExpressionOptions
18
34
 
35
+ module HeredocQuery # :nodoc:
36
+ # Returns true if this node was represented as a heredoc in the source code.
37
+ def heredoc?
38
+ opening&.start_with?("<<")
39
+ end
40
+ end
41
+
42
+ class InterpolatedStringNode < Node
43
+ include HeredocQuery
44
+ end
45
+
46
+ class InterpolatedXStringNode < Node
47
+ include HeredocQuery
48
+ end
49
+
50
+ class StringNode < Node
51
+ include HeredocQuery
52
+ end
53
+
54
+ class XStringNode < Node
55
+ include HeredocQuery
56
+ end
57
+
58
+ private_constant :HeredocQuery
59
+
19
60
  class FloatNode < Node
20
61
  # Returns the value of the node as a Ruby Float.
21
62
  def value
@@ -37,18 +78,6 @@ module Prism
37
78
  end
38
79
  end
39
80
 
40
- class InterpolatedMatchLastLineNode < Node
41
- include RegularExpressionOptions
42
- end
43
-
44
- class InterpolatedRegularExpressionNode < Node
45
- include RegularExpressionOptions
46
- end
47
-
48
- class MatchLastLineNode < Node
49
- include RegularExpressionOptions
50
- end
51
-
52
81
  class RationalNode < Node
53
82
  # Returns the value of the node as a Ruby Rational.
54
83
  def value
@@ -56,10 +85,6 @@ module Prism
56
85
  end
57
86
  end
58
87
 
59
- class RegularExpressionNode < Node
60
- include RegularExpressionOptions
61
- end
62
-
63
88
  class ConstantReadNode < Node
64
89
  # Returns the list of parts for the full name of this constant.
65
90
  # For example: [:Foo]
@@ -238,11 +238,6 @@ module Prism
238
238
  def deconstruct_keys(keys)
239
239
  { location: location }
240
240
  end
241
-
242
- # This can only be true for inline comments.
243
- def trailing?
244
- false
245
- end
246
241
  end
247
242
 
248
243
  # InlineComment objects are the most common. They correspond to comments in
@@ -263,18 +258,14 @@ module Prism
263
258
  # EmbDocComment objects correspond to comments that are surrounded by =begin
264
259
  # and =end.
265
260
  class EmbDocComment < Comment
266
- # Returns a string representation of this comment.
267
- def inspect
268
- "#<Prism::EmbDocComment @location=#{location.inspect}>"
261
+ # This can only be true for inline comments.
262
+ def trailing?
263
+ false
269
264
  end
270
- end
271
265
 
272
- # DATAComment objects correspond to comments that are after the __END__
273
- # keyword in a source file.
274
- class DATAComment < Comment
275
266
  # Returns a string representation of this comment.
276
267
  def inspect
277
- "#<Prism::DATAComment @location=#{location.inspect}>"
268
+ "#<Prism::EmbDocComment @location=#{location.inspect}>"
278
269
  end
279
270
  end
280
271
 
@@ -378,6 +369,11 @@ module Prism
378
369
  # The list of magic comments that were encountered during parsing.
379
370
  attr_reader :magic_comments
380
371
 
372
+ # An optional location that represents the location of the content after the
373
+ # __END__ marker. This content is loaded into the DATA constant when the
374
+ # file being parsed is the main file being executed.
375
+ attr_reader :data_loc
376
+
381
377
  # The list of errors that were generated during parsing.
382
378
  attr_reader :errors
383
379
 
@@ -388,10 +384,11 @@ module Prism
388
384
  attr_reader :source
389
385
 
390
386
  # Create a new parse result object with the given values.
391
- def initialize(value, comments, magic_comments, errors, warnings, source)
387
+ def initialize(value, comments, magic_comments, data_loc, errors, warnings, source)
392
388
  @value = value
393
389
  @comments = comments
394
390
  @magic_comments = magic_comments
391
+ @data_loc = data_loc
395
392
  @errors = errors
396
393
  @warnings = warnings
397
394
  @source = source
@@ -399,7 +396,7 @@ module Prism
399
396
 
400
397
  # Implement the hash pattern matching interface for ParseResult.
401
398
  def deconstruct_keys(keys)
402
- { value: value, comments: comments, magic_comments: magic_comments, errors: errors, warnings: warnings }
399
+ { value: value, comments: comments, magic_comments: magic_comments, data_loc: data_loc, errors: errors, warnings: warnings }
403
400
  end
404
401
 
405
402
  # Returns true if there were no errors during parsing and false if there
@@ -3,6 +3,10 @@
3
3
  require "ripper"
4
4
 
5
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
+ #
6
10
  # This class is meant to provide a compatibility layer between prism and
7
11
  # Ripper. It functions by parsing the entire tree first and then walking it
8
12
  # and executing each of the Ripper callbacks as it goes.
@@ -10,7 +14,10 @@ module Prism
10
14
  # This class is going to necessarily be slower than the native Ripper API. It
11
15
  # is meant as a stopgap until developers migrate to using prism. It is also
12
16
  # meant as a test harness for the prism parser.
13
- class RipperCompat
17
+ #
18
+ # To use this class, you treat `Prism::RipperCompat` effectively as you would
19
+ # treat the `Ripper` class.
20
+ class RipperCompat < Visitor
14
21
  # This class mirrors the ::Ripper::SexpBuilder subclass of ::Ripper that
15
22
  # returns the arrays of [type, *children].
16
23
  class SexpBuilder < RipperCompat
@@ -77,43 +84,63 @@ module Prism
77
84
 
78
85
  # True if the parser encountered an error during parsing.
79
86
  def error?
80
- result.errors.any?
87
+ result.failure?
81
88
  end
82
89
 
83
90
  # Parse the source and return the result.
84
91
  def parse
85
- result.value.accept(self) unless error?
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
86
103
  end
87
104
 
88
105
  ############################################################################
89
106
  # Visitor methods
90
107
  ############################################################################
91
108
 
92
- # This method is responsible for dispatching to the correct visitor method
93
- # based on the type of the node.
94
- def visit(node)
95
- node&.accept(self)
96
- end
97
-
98
109
  # Visit a CallNode node.
99
110
  def visit_call_node(node)
100
- if !node.opening_loc && node.arguments.arguments.length == 1
101
- bounds(node.receiver.location)
111
+ if !node.message.match?(/^[[:alpha:]_]/) && node.opening_loc.nil? && node.arguments&.arguments&.length == 1
102
112
  left = visit(node.receiver)
103
-
104
- bounds(node.arguments.arguments.first.location)
105
113
  right = visit(node.arguments.arguments.first)
106
114
 
107
- on_binary(left, source[node.message_loc.start_offset...node.message_loc.end_offset].to_sym, right)
115
+ bounds(node.location)
116
+ on_binary(left, node.name, right)
108
117
  else
109
118
  raise NotImplementedError
110
119
  end
111
120
  end
112
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
+
113
134
  # Visit an IntegerNode node.
114
135
  def visit_integer_node(node)
115
136
  bounds(node.location)
116
- on_int(source[node.location.start_offset...node.location.end_offset])
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)
117
144
  end
118
145
 
119
146
  # Visit a StatementsNode node.
@@ -124,24 +151,11 @@ module Prism
124
151
  end
125
152
  end
126
153
 
127
- # Visit a token found during parsing.
128
- def visit_token(node)
129
- bounds(node.location)
130
-
131
- case node.type
132
- when :MINUS
133
- on_op(node.value)
134
- when :PLUS
135
- on_op(node.value)
136
- else
137
- raise NotImplementedError, "Unknown token: #{node.type}"
138
- end
139
- end
140
-
141
154
  # Visit a ProgramNode node.
142
155
  def visit_program_node(node)
156
+ statements = visit(node.statements)
143
157
  bounds(node.location)
144
- on_program(visit(node.statements))
158
+ on_program(statements)
145
159
  end
146
160
 
147
161
  ############################################################################
@@ -166,10 +180,8 @@ module Prism
166
180
  # This method could be drastically improved with some caching on the start
167
181
  # of every line, but for now it's good enough.
168
182
  def bounds(location)
169
- start_offset = location.start_offset
170
-
171
- @lineno = source[0..start_offset].count("\n") + 1
172
- @column = start_offset - (source.rindex("\n", start_offset) || 0)
183
+ @lineno = location.start_line
184
+ @column = location.start_column
173
185
  end
174
186
 
175
187
  # Lazily initialize the parse result.
@@ -185,6 +197,9 @@ module Prism
185
197
  def _dispatch5(_, _, _, _, _); end # :nodoc:
186
198
  def _dispatch7(_, _, _, _, _, _, _); end # :nodoc:
187
199
 
200
+ alias_method :on_parse_error, :_dispatch1
201
+ alias_method :on_magic_comment, :_dispatch2
202
+
188
203
  (Ripper::SCANNER_EVENT_TABLE.merge(Ripper::PARSER_EVENT_TABLE)).each do |event, arity|
189
204
  alias_method :"on_#{event}", :"_dispatch#{arity}"
190
205
  end