prism 0.30.0 → 1.0.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 (57) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +31 -1
  3. data/README.md +3 -1
  4. data/config.yml +185 -126
  5. data/docs/serialization.md +3 -0
  6. data/ext/prism/api_node.c +2843 -2085
  7. data/ext/prism/extconf.rb +1 -1
  8. data/ext/prism/extension.c +35 -25
  9. data/ext/prism/extension.h +2 -2
  10. data/include/prism/ast.h +1048 -69
  11. data/include/prism/defines.h +9 -0
  12. data/include/prism/diagnostic.h +11 -3
  13. data/include/prism/options.h +55 -1
  14. data/include/prism/parser.h +27 -3
  15. data/include/prism/regexp.h +2 -1
  16. data/include/prism/util/pm_integer.h +6 -6
  17. data/include/prism/util/pm_newline_list.h +11 -0
  18. data/include/prism/util/pm_string.h +1 -0
  19. data/include/prism/version.h +3 -3
  20. data/lib/prism/desugar_compiler.rb +111 -74
  21. data/lib/prism/dispatcher.rb +2 -1
  22. data/lib/prism/dot_visitor.rb +21 -31
  23. data/lib/prism/dsl.rb +656 -471
  24. data/lib/prism/ffi.rb +3 -0
  25. data/lib/prism/inspect_visitor.rb +285 -57
  26. data/lib/prism/mutation_compiler.rb +5 -5
  27. data/lib/prism/node.rb +2282 -4754
  28. data/lib/prism/node_ext.rb +72 -11
  29. data/lib/prism/parse_result/errors.rb +65 -0
  30. data/lib/prism/parse_result/newlines.rb +28 -28
  31. data/lib/prism/parse_result.rb +25 -2
  32. data/lib/prism/reflection.rb +7 -7
  33. data/lib/prism/serialize.rb +468 -610
  34. data/lib/prism/translation/parser/compiler.rb +18 -18
  35. data/lib/prism/translation/parser/lexer.rb +1 -1
  36. data/lib/prism/translation/parser.rb +3 -3
  37. data/lib/prism/translation/ripper.rb +14 -14
  38. data/lib/prism/translation/ruby_parser.rb +43 -7
  39. data/prism.gemspec +3 -1
  40. data/rbi/prism/dsl.rbi +521 -0
  41. data/rbi/prism/node.rbi +1456 -5616
  42. data/rbi/prism.rbi +16 -16
  43. data/sig/prism/dsl.rbs +189 -305
  44. data/sig/prism/node.rbs +702 -603
  45. data/sig/prism/parse_result.rbs +2 -0
  46. data/src/diagnostic.c +22 -6
  47. data/src/node.c +277 -284
  48. data/src/options.c +18 -0
  49. data/src/prettyprint.c +99 -108
  50. data/src/prism.c +1282 -760
  51. data/src/regexp.c +72 -4
  52. data/src/serialize.c +165 -50
  53. data/src/token_type.c +2 -2
  54. data/src/util/pm_integer.c +14 -14
  55. data/src/util/pm_newline_list.c +29 -0
  56. data/src/util/pm_string.c +9 -5
  57. metadata +4 -2
@@ -21,7 +21,10 @@ module Prism
21
21
  # Returns a numeric value that represents the flags that were used to create
22
22
  # the regular expression.
23
23
  def options
24
- o = flags & (RegularExpressionFlags::IGNORE_CASE | RegularExpressionFlags::EXTENDED | RegularExpressionFlags::MULTI_LINE)
24
+ o = 0
25
+ o |= Regexp::IGNORECASE if flags.anybits?(RegularExpressionFlags::IGNORE_CASE)
26
+ o |= Regexp::EXTENDED if flags.anybits?(RegularExpressionFlags::EXTENDED)
27
+ o |= Regexp::MULTILINE if flags.anybits?(RegularExpressionFlags::MULTI_LINE)
25
28
  o |= Regexp::FIXEDENCODING if flags.anybits?(RegularExpressionFlags::EUC_JP | RegularExpressionFlags::WINDOWS_31J | RegularExpressionFlags::UTF_8)
26
29
  o |= Regexp::NOENCODING if flags.anybits?(RegularExpressionFlags::ASCII_8BIT)
27
30
  o
@@ -69,11 +72,12 @@ module Prism
69
72
  def to_interpolated
70
73
  InterpolatedStringNode.new(
71
74
  source,
75
+ -1,
76
+ location,
72
77
  frozen? ? InterpolatedStringNodeFlags::FROZEN : 0,
73
78
  opening_loc,
74
- [copy(opening_loc: nil, closing_loc: nil, location: content_loc)],
75
- closing_loc,
76
- location
79
+ [copy(location: content_loc, opening_loc: nil, closing_loc: nil)],
80
+ closing_loc
77
81
  )
78
82
  end
79
83
  end
@@ -86,10 +90,12 @@ module Prism
86
90
  def to_interpolated
87
91
  InterpolatedXStringNode.new(
88
92
  source,
93
+ -1,
94
+ location,
95
+ flags,
89
96
  opening_loc,
90
- [StringNode.new(source, 0, nil, content_loc, nil, unescaped, content_loc)],
91
- closing_loc,
92
- location
97
+ [StringNode.new(source, node_id, content_loc, 0, nil, content_loc, nil, unescaped)],
98
+ closing_loc
93
99
  )
94
100
  end
95
101
  end
@@ -115,9 +121,9 @@ module Prism
115
121
  deprecated("value", "numerator", "denominator")
116
122
 
117
123
  if denominator == 1
118
- IntegerNode.new(source, flags, numerator, location.chop)
124
+ IntegerNode.new(source, -1, location.chop, flags, numerator)
119
125
  else
120
- FloatNode.new(source, numerator.to_f / denominator, location.chop)
126
+ FloatNode.new(source, -1, location.chop, 0, numerator.to_f / denominator)
121
127
  end
122
128
  end
123
129
  end
@@ -195,7 +201,12 @@ module Prism
195
201
  # continue to supply that API.
196
202
  def child
197
203
  deprecated("name", "name_loc")
198
- name ? ConstantReadNode.new(source, name, name_loc) : MissingNode.new(source, location)
204
+
205
+ if name
206
+ ConstantReadNode.new(source, -1, name_loc, 0, name)
207
+ else
208
+ MissingNode.new(source, -1, location, 0)
209
+ end
199
210
  end
200
211
  end
201
212
 
@@ -231,7 +242,12 @@ module Prism
231
242
  # continue to supply that API.
232
243
  def child
233
244
  deprecated("name", "name_loc")
234
- name ? ConstantReadNode.new(source, name, name_loc) : MissingNode.new(source, location)
245
+
246
+ if name
247
+ ConstantReadNode.new(source, -1, name_loc, 0, name)
248
+ else
249
+ MissingNode.new(source, -1, location, 0)
250
+ end
235
251
  end
236
252
  end
237
253
 
@@ -444,4 +460,49 @@ module Prism
444
460
  binary_operator_loc
445
461
  end
446
462
  end
463
+
464
+ class CaseMatchNode < Node
465
+ # Returns the else clause of the case match node. This method is deprecated
466
+ # in favor of #else_clause.
467
+ def consequent
468
+ deprecated("else_clause")
469
+ else_clause
470
+ end
471
+ end
472
+
473
+ class CaseNode < Node
474
+ # Returns the else clause of the case node. This method is deprecated in
475
+ # favor of #else_clause.
476
+ def consequent
477
+ deprecated("else_clause")
478
+ else_clause
479
+ end
480
+ end
481
+
482
+ class IfNode < Node
483
+ # Returns the subsequent if/elsif/else clause of the if node. This method is
484
+ # deprecated in favor of #subsequent.
485
+ def consequent
486
+ deprecated("subsequent")
487
+ subsequent
488
+ end
489
+ end
490
+
491
+ class RescueNode < Node
492
+ # Returns the subsequent rescue clause of the rescue node. This method is
493
+ # deprecated in favor of #subsequent.
494
+ def consequent
495
+ deprecated("subsequent")
496
+ subsequent
497
+ end
498
+ end
499
+
500
+ class UnlessNode < Node
501
+ # Returns the else clause of the unless node. This method is deprecated in
502
+ # favor of #else_clause.
503
+ def consequent
504
+ deprecated("else_clause")
505
+ else_clause
506
+ end
507
+ end
447
508
  end
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "stringio"
4
+
5
+ module Prism
6
+ class ParseResult < Result
7
+ # An object to represent the set of errors on a parse result. This object
8
+ # can be used to format the errors in a human-readable way.
9
+ class Errors
10
+ # The parse result that contains the errors.
11
+ attr_reader :parse_result
12
+
13
+ # Initialize a new set of errors from the given parse result.
14
+ def initialize(parse_result)
15
+ @parse_result = parse_result
16
+ end
17
+
18
+ # Formats the errors in a human-readable way and return them as a string.
19
+ def format
20
+ error_lines = {}
21
+ parse_result.errors.each do |error|
22
+ location = error.location
23
+ (location.start_line..location.end_line).each do |line|
24
+ error_lines[line] ||= []
25
+ error_lines[line] << error
26
+ end
27
+ end
28
+
29
+ source_lines = parse_result.source.source.lines
30
+ source_lines << "" if error_lines.key?(source_lines.size + 1)
31
+
32
+ io = StringIO.new
33
+ source_lines.each.with_index(1) do |line, line_number|
34
+ io.puts(line)
35
+
36
+ (error_lines.delete(line_number) || []).each do |error|
37
+ location = error.location
38
+
39
+ case line_number
40
+ when location.start_line
41
+ io.print(" " * location.start_column + "^")
42
+
43
+ if location.start_line == location.end_line
44
+ if location.start_column != location.end_column
45
+ io.print("~" * (location.end_column - location.start_column - 1))
46
+ end
47
+
48
+ io.puts(" " + error.message)
49
+ else
50
+ io.puts("~" * (line.bytesize - location.start_column))
51
+ end
52
+ when location.end_line
53
+ io.puts("~" * location.end_column + " " + error.message)
54
+ else
55
+ io.puts("~" * line.bytesize)
56
+ end
57
+ end
58
+ end
59
+
60
+ io.puts
61
+ io.string
62
+ end
63
+ end
64
+ end
65
+ end
@@ -45,7 +45,7 @@ module Prism
45
45
 
46
46
  # Mark if/unless nodes as newlines.
47
47
  def visit_if_node(node)
48
- node.newline!(@lines)
48
+ node.newline_flag!(@lines)
49
49
  super(node)
50
50
  end
51
51
 
@@ -54,7 +54,7 @@ module Prism
54
54
  # Permit statements lists to mark newlines within themselves.
55
55
  def visit_statements_node(node)
56
56
  node.body.each do |child|
57
- child.newline!(@lines)
57
+ child.newline_flag!(@lines)
58
58
  end
59
59
  super(node)
60
60
  end
@@ -62,93 +62,93 @@ module Prism
62
62
  end
63
63
 
64
64
  class Node
65
- def newline? # :nodoc:
66
- @newline ? true : false
65
+ def newline_flag? # :nodoc:
66
+ @newline_flag ? true : false
67
67
  end
68
68
 
69
- def newline!(lines) # :nodoc:
69
+ def newline_flag!(lines) # :nodoc:
70
70
  line = location.start_line
71
71
  unless lines[line]
72
72
  lines[line] = true
73
- @newline = true
73
+ @newline_flag = true
74
74
  end
75
75
  end
76
76
  end
77
77
 
78
78
  class BeginNode < Node
79
- def newline!(lines) # :nodoc:
79
+ def newline_flag!(lines) # :nodoc:
80
80
  # Never mark BeginNode with a newline flag, mark children instead.
81
81
  end
82
82
  end
83
83
 
84
84
  class ParenthesesNode < Node
85
- def newline!(lines) # :nodoc:
85
+ def newline_flag!(lines) # :nodoc:
86
86
  # Never mark ParenthesesNode with a newline flag, mark children instead.
87
87
  end
88
88
  end
89
89
 
90
90
  class IfNode < Node
91
- def newline!(lines) # :nodoc:
92
- predicate.newline!(lines)
91
+ def newline_flag!(lines) # :nodoc:
92
+ predicate.newline_flag!(lines)
93
93
  end
94
94
  end
95
95
 
96
96
  class UnlessNode < Node
97
- def newline!(lines) # :nodoc:
98
- predicate.newline!(lines)
97
+ def newline_flag!(lines) # :nodoc:
98
+ predicate.newline_flag!(lines)
99
99
  end
100
100
  end
101
101
 
102
102
  class UntilNode < Node
103
- def newline!(lines) # :nodoc:
104
- predicate.newline!(lines)
103
+ def newline_flag!(lines) # :nodoc:
104
+ predicate.newline_flag!(lines)
105
105
  end
106
106
  end
107
107
 
108
108
  class WhileNode < Node
109
- def newline!(lines) # :nodoc:
110
- predicate.newline!(lines)
109
+ def newline_flag!(lines) # :nodoc:
110
+ predicate.newline_flag!(lines)
111
111
  end
112
112
  end
113
113
 
114
114
  class RescueModifierNode < Node
115
- def newline!(lines) # :nodoc:
116
- expression.newline!(lines)
115
+ def newline_flag!(lines) # :nodoc:
116
+ expression.newline_flag!(lines)
117
117
  end
118
118
  end
119
119
 
120
120
  class InterpolatedMatchLastLineNode < Node
121
- def newline!(lines) # :nodoc:
121
+ def newline_flag!(lines) # :nodoc:
122
122
  first = parts.first
123
- first.newline!(lines) if first
123
+ first.newline_flag!(lines) if first
124
124
  end
125
125
  end
126
126
 
127
127
  class InterpolatedRegularExpressionNode < Node
128
- def newline!(lines) # :nodoc:
128
+ def newline_flag!(lines) # :nodoc:
129
129
  first = parts.first
130
- first.newline!(lines) if first
130
+ first.newline_flag!(lines) if first
131
131
  end
132
132
  end
133
133
 
134
134
  class InterpolatedStringNode < Node
135
- def newline!(lines) # :nodoc:
135
+ def newline_flag!(lines) # :nodoc:
136
136
  first = parts.first
137
- first.newline!(lines) if first
137
+ first.newline_flag!(lines) if first
138
138
  end
139
139
  end
140
140
 
141
141
  class InterpolatedSymbolNode < Node
142
- def newline!(lines) # :nodoc:
142
+ def newline_flag!(lines) # :nodoc:
143
143
  first = parts.first
144
- first.newline!(lines) if first
144
+ first.newline_flag!(lines) if first
145
145
  end
146
146
  end
147
147
 
148
148
  class InterpolatedXStringNode < Node
149
- def newline!(lines) # :nodoc:
149
+ def newline_flag!(lines) # :nodoc:
150
150
  first = parts.first
151
- first.newline!(lines) if first
151
+ first.newline_flag!(lines) if first
152
152
  end
153
153
  end
154
154
  end
@@ -10,7 +10,11 @@ module Prism
10
10
  # specialized and more performant `ASCIISource` if no multibyte characters
11
11
  # are present in the source code.
12
12
  def self.for(source, start_line = 1, offsets = [])
13
- source.ascii_only? ? ASCIISource.new(source, start_line, offsets): new(source, start_line, offsets)
13
+ if source.ascii_only?
14
+ ASCIISource.new(source, start_line, offsets)
15
+ else
16
+ new(source, start_line, offsets)
17
+ end
14
18
  end
15
19
 
16
20
  # The source code that this source object represents.
@@ -87,7 +91,12 @@ module Prism
87
91
  # encodings, it is not captured here.
88
92
  def code_units_offset(byte_offset, encoding)
89
93
  byteslice = (source.byteslice(0, byte_offset) or raise).encode(encoding)
90
- (encoding == Encoding::UTF_16LE || encoding == Encoding::UTF_16BE) ? (byteslice.bytesize / 2) : byteslice.length
94
+
95
+ if encoding == Encoding::UTF_16LE || encoding == Encoding::UTF_16BE
96
+ byteslice.bytesize / 2
97
+ else
98
+ byteslice.length
99
+ end
91
100
  end
92
101
 
93
102
  # Returns the column number in code units for the given encoding for the
@@ -575,9 +584,11 @@ module Prism
575
584
  # This is a result specific to the `parse` and `parse_file` methods.
576
585
  class ParseResult < Result
577
586
  autoload :Comments, "prism/parse_result/comments"
587
+ autoload :Errors, "prism/parse_result/errors"
578
588
  autoload :Newlines, "prism/parse_result/newlines"
579
589
 
580
590
  private_constant :Comments
591
+ private_constant :Errors
581
592
  private_constant :Newlines
582
593
 
583
594
  # The syntax tree that was parsed from the source code.
@@ -604,6 +615,12 @@ module Prism
604
615
  def mark_newlines!
605
616
  value.accept(Newlines.new(source.offsets.size)) # steep:ignore
606
617
  end
618
+
619
+ # Returns a string representation of the syntax tree with the errors
620
+ # displayed inline.
621
+ def errors_format
622
+ Errors.new(self).format
623
+ end
607
624
  end
608
625
 
609
626
  # This is a result specific to the `lex` and `lex_file` methods.
@@ -694,5 +711,11 @@ module Prism
694
711
  other.type == type &&
695
712
  other.value == value
696
713
  end
714
+
715
+ # Returns a string representation of this token.
716
+ def inspect
717
+ location
718
+ super
719
+ end
697
720
  end
698
721
  end
@@ -112,7 +112,7 @@ module Prism
112
112
  when :and_node
113
113
  [NodeField.new(:left), NodeField.new(:right), LocationField.new(:operator_loc)]
114
114
  when :arguments_node
115
- [FlagsField.new(:flags, [:contains_keywords?, :contains_keyword_splat?]), NodeListField.new(:arguments)]
115
+ [FlagsField.new(:flags, [:contains_keywords?, :contains_keyword_splat?, :contains_splat?]), NodeListField.new(:arguments)]
116
116
  when :array_node
117
117
  [FlagsField.new(:flags, [:contains_splat?]), NodeListField.new(:elements), OptionalLocationField.new(:opening_loc), OptionalLocationField.new(:closing_loc)]
118
118
  when :array_pattern_node
@@ -150,9 +150,9 @@ module Prism
150
150
  when :capture_pattern_node
151
151
  [NodeField.new(:value), NodeField.new(:target), LocationField.new(:operator_loc)]
152
152
  when :case_match_node
153
- [OptionalNodeField.new(:predicate), NodeListField.new(:conditions), OptionalNodeField.new(:consequent), LocationField.new(:case_keyword_loc), LocationField.new(:end_keyword_loc)]
153
+ [OptionalNodeField.new(:predicate), NodeListField.new(:conditions), OptionalNodeField.new(:else_clause), LocationField.new(:case_keyword_loc), LocationField.new(:end_keyword_loc)]
154
154
  when :case_node
155
- [OptionalNodeField.new(:predicate), NodeListField.new(:conditions), OptionalNodeField.new(:consequent), LocationField.new(:case_keyword_loc), LocationField.new(:end_keyword_loc)]
155
+ [OptionalNodeField.new(:predicate), NodeListField.new(:conditions), OptionalNodeField.new(:else_clause), LocationField.new(:case_keyword_loc), LocationField.new(:end_keyword_loc)]
156
156
  when :class_node
157
157
  [ConstantListField.new(:locals), LocationField.new(:class_keyword_loc), NodeField.new(:constant_path), OptionalLocationField.new(:inheritance_operator_loc), OptionalNodeField.new(:superclass), OptionalNodeField.new(:body), LocationField.new(:end_keyword_loc), ConstantField.new(:name)]
158
158
  when :class_variable_and_write_node
@@ -236,7 +236,7 @@ module Prism
236
236
  when :hash_pattern_node
237
237
  [OptionalNodeField.new(:constant), NodeListField.new(:elements), OptionalNodeField.new(:rest), OptionalLocationField.new(:opening_loc), OptionalLocationField.new(:closing_loc)]
238
238
  when :if_node
239
- [OptionalLocationField.new(:if_keyword_loc), NodeField.new(:predicate), OptionalLocationField.new(:then_keyword_loc), OptionalNodeField.new(:statements), OptionalNodeField.new(:consequent), OptionalLocationField.new(:end_keyword_loc)]
239
+ [OptionalLocationField.new(:if_keyword_loc), NodeField.new(:predicate), OptionalLocationField.new(:then_keyword_loc), OptionalNodeField.new(:statements), OptionalNodeField.new(:subsequent), OptionalLocationField.new(:end_keyword_loc)]
240
240
  when :imaginary_node
241
241
  [NodeField.new(:numeric)]
242
242
  when :implicit_node
@@ -360,13 +360,13 @@ module Prism
360
360
  when :rescue_modifier_node
361
361
  [NodeField.new(:expression), LocationField.new(:keyword_loc), NodeField.new(:rescue_expression)]
362
362
  when :rescue_node
363
- [LocationField.new(:keyword_loc), NodeListField.new(:exceptions), OptionalLocationField.new(:operator_loc), OptionalNodeField.new(:reference), OptionalNodeField.new(:statements), OptionalNodeField.new(:consequent)]
363
+ [LocationField.new(:keyword_loc), NodeListField.new(:exceptions), OptionalLocationField.new(:operator_loc), OptionalNodeField.new(:reference), OptionalNodeField.new(:statements), OptionalNodeField.new(:subsequent)]
364
364
  when :rest_parameter_node
365
365
  [FlagsField.new(:flags, [:repeated_parameter?]), OptionalConstantField.new(:name), OptionalLocationField.new(:name_loc), LocationField.new(:operator_loc)]
366
366
  when :retry_node
367
367
  []
368
368
  when :return_node
369
- [FlagsField.new(:flags, [:redundant?]), LocationField.new(:keyword_loc), OptionalNodeField.new(:arguments)]
369
+ [LocationField.new(:keyword_loc), OptionalNodeField.new(:arguments)]
370
370
  when :self_node
371
371
  []
372
372
  when :shareable_constant_node
@@ -394,7 +394,7 @@ module Prism
394
394
  when :undef_node
395
395
  [NodeListField.new(:names), LocationField.new(:keyword_loc)]
396
396
  when :unless_node
397
- [LocationField.new(:keyword_loc), NodeField.new(:predicate), OptionalLocationField.new(:then_keyword_loc), OptionalNodeField.new(:statements), OptionalNodeField.new(:consequent), OptionalLocationField.new(:end_keyword_loc)]
397
+ [LocationField.new(:keyword_loc), NodeField.new(:predicate), OptionalLocationField.new(:then_keyword_loc), OptionalNodeField.new(:statements), OptionalNodeField.new(:else_clause), OptionalLocationField.new(:end_keyword_loc)]
398
398
  when :until_node
399
399
  [FlagsField.new(:flags, [:begin_modifier?]), LocationField.new(:keyword_loc), OptionalLocationField.new(:closing_loc), NodeField.new(:predicate), OptionalNodeField.new(:statements)]
400
400
  when :when_node