Skip to content

Commit 9168287

Browse files
committed
Move node ext and parse result to their own files
1 parent e90f88f commit 9168287

File tree

5 files changed

+358
-350
lines changed

5 files changed

+358
-350
lines changed

lib/yarp.rb

Lines changed: 2 additions & 348 deletions
Original file line numberDiff line numberDiff line change
@@ -1,298 +1,6 @@
11
# frozen_string_literal: true
22

33
module YARP
4-
# This represents a source of Ruby code that has been parsed. It is used in
5-
# conjunction with locations to allow them to resolve line numbers and source
6-
# ranges.
7-
class Source
8-
attr_reader :source, :offsets
9-
10-
def initialize(source, offsets = compute_offsets(source))
11-
@source = source
12-
@offsets = offsets
13-
end
14-
15-
def slice(offset, length)
16-
source.byteslice(offset, length)
17-
end
18-
19-
def line(value)
20-
offsets.bsearch_index { |offset| offset > value } || offsets.length
21-
end
22-
23-
def line_offset(value)
24-
offsets[line(value) - 1]
25-
end
26-
27-
def column(value)
28-
value - offsets[line(value) - 1]
29-
end
30-
31-
private
32-
33-
def compute_offsets(code)
34-
offsets = [0]
35-
code.b.scan("\n") { offsets << $~.end(0) }
36-
offsets
37-
end
38-
end
39-
40-
# This represents a location in the source.
41-
class Location
42-
# A Source object that is used to determine more information from the given
43-
# offset and length.
44-
protected attr_reader :source
45-
46-
# The byte offset from the beginning of the source where this location
47-
# starts.
48-
attr_reader :start_offset
49-
50-
# The length of this location in bytes.
51-
attr_reader :length
52-
53-
# The list of comments attached to this location
54-
attr_reader :comments
55-
56-
def initialize(source, start_offset, length)
57-
@source = source
58-
@start_offset = start_offset
59-
@length = length
60-
@comments = []
61-
end
62-
63-
# Create a new location object with the given options.
64-
def copy(**options)
65-
Location.new(
66-
options.fetch(:source) { source },
67-
options.fetch(:start_offset) { start_offset },
68-
options.fetch(:length) { length }
69-
)
70-
end
71-
72-
# Returns a string representation of this location.
73-
def inspect
74-
"#<YARP::Location @start_offset=#{@start_offset} @length=#{@length} start_line=#{start_line}>"
75-
end
76-
77-
# The source code that this location represents.
78-
def slice
79-
source.slice(start_offset, length)
80-
end
81-
82-
# The byte offset from the beginning of the source where this location ends.
83-
def end_offset
84-
start_offset + length
85-
end
86-
87-
# The line number where this location starts.
88-
def start_line
89-
source.line(start_offset)
90-
end
91-
92-
# The content of the line where this location starts before this location.
93-
def start_line_slice
94-
offset = source.line_offset(start_offset)
95-
source.slice(offset, start_offset - offset)
96-
end
97-
98-
# The line number where this location ends.
99-
def end_line
100-
source.line(end_offset - 1)
101-
end
102-
103-
# The column number in bytes where this location starts from the start of
104-
# the line.
105-
def start_column
106-
source.column(start_offset)
107-
end
108-
109-
# The column number in bytes where this location ends from the start of the
110-
# line.
111-
def end_column
112-
source.column(end_offset)
113-
end
114-
115-
def deconstruct_keys(keys)
116-
{ start_offset: start_offset, end_offset: end_offset }
117-
end
118-
119-
def pretty_print(q)
120-
q.text("(#{start_line},#{start_column})-(#{end_line},#{end_column}))")
121-
end
122-
123-
def ==(other)
124-
other.is_a?(Location) &&
125-
other.start_offset == start_offset &&
126-
other.end_offset == end_offset
127-
end
128-
129-
# Returns a new location that stretches from this location to the given
130-
# other location. Raises an error if this location is not before the other
131-
# location or if they don't share the same source.
132-
def join(other)
133-
raise "Incompatible sources" if source != other.source
134-
raise "Incompatible locations" if start_offset > other.start_offset
135-
136-
Location.new(source, start_offset, other.end_offset - start_offset)
137-
end
138-
139-
def self.null
140-
new(0, 0)
141-
end
142-
end
143-
144-
# This represents a comment that was encountered during parsing.
145-
class Comment
146-
TYPES = [:inline, :embdoc, :__END__]
147-
148-
attr_reader :type, :location
149-
150-
def initialize(type, location)
151-
@type = type
152-
@location = location
153-
end
154-
155-
def deconstruct_keys(keys)
156-
{ type: type, location: location }
157-
end
158-
159-
# Returns true if the comment happens on the same line as other code and false if the comment is by itself
160-
def trailing?
161-
type == :inline && !location.start_line_slice.strip.empty?
162-
end
163-
164-
def inspect
165-
"#<YARP::Comment @type=#{@type.inspect} @location=#{@location.inspect}>"
166-
end
167-
end
168-
169-
# This represents an error that was encountered during parsing.
170-
class ParseError
171-
attr_reader :message, :location
172-
173-
def initialize(message, location)
174-
@message = message
175-
@location = location
176-
end
177-
178-
def deconstruct_keys(keys)
179-
{ message: message, location: location }
180-
end
181-
182-
def inspect
183-
"#<YARP::ParseError @message=#{@message.inspect} @location=#{@location.inspect}>"
184-
end
185-
end
186-
187-
# This represents a warning that was encountered during parsing.
188-
class ParseWarning
189-
attr_reader :message, :location
190-
191-
def initialize(message, location)
192-
@message = message
193-
@location = location
194-
end
195-
196-
def deconstruct_keys(keys)
197-
{ message: message, location: location }
198-
end
199-
200-
def inspect
201-
"#<YARP::ParseWarning @message=#{@message.inspect} @location=#{@location.inspect}>"
202-
end
203-
end
204-
205-
# This represents the result of a call to ::parse or ::parse_file. It contains
206-
# the AST, any comments that were encounters, and any errors that were
207-
# encountered.
208-
class ParseResult
209-
attr_reader :value, :comments, :errors, :warnings, :source
210-
211-
def initialize(value, comments, errors, warnings, source)
212-
@value = value
213-
@comments = comments
214-
@errors = errors
215-
@warnings = warnings
216-
@source = source
217-
end
218-
219-
def deconstruct_keys(keys)
220-
{ value: value, comments: comments, errors: errors, warnings: warnings }
221-
end
222-
223-
def success?
224-
errors.empty?
225-
end
226-
227-
def failure?
228-
!success?
229-
end
230-
end
231-
232-
# This represents a token from the Ruby source.
233-
class Token
234-
attr_reader :type, :value, :location
235-
236-
def initialize(type, value, location)
237-
@type = type
238-
@value = value
239-
@location = location
240-
end
241-
242-
def deconstruct_keys(keys)
243-
{ type: type, value: value, location: location }
244-
end
245-
246-
def pretty_print(q)
247-
q.group do
248-
q.text(type.to_s)
249-
self.location.pretty_print(q)
250-
q.text("(")
251-
q.nest(2) do
252-
q.breakable("")
253-
q.pp(value)
254-
end
255-
q.breakable("")
256-
q.text(")")
257-
end
258-
end
259-
260-
def ==(other)
261-
other.is_a?(Token) &&
262-
other.type == type &&
263-
other.value == value
264-
end
265-
end
266-
267-
# This represents a node in the tree.
268-
class Node
269-
attr_reader :location
270-
271-
def newline?
272-
@newline ? true : false
273-
end
274-
275-
def set_newline_flag(newline_marked)
276-
line = location.start_line
277-
unless newline_marked[line]
278-
newline_marked[line] = true
279-
@newline = true
280-
end
281-
end
282-
283-
# Slice the location of the node from the source.
284-
def slice
285-
location.slice
286-
end
287-
288-
def pretty_print(q)
289-
q.seplist(inspect.chomp.each_line, -> { q.breakable }) do |line|
290-
q.text(line.chomp)
291-
end
292-
q.current_group.break
293-
end
294-
end
295-
2964
# There are many files in YARP that are templated to handle every node type,
2975
# which means the files can end up being quite large. We autoload them to make
2986
# our require speed faster since consuming libraries are unlikely to use all
@@ -340,6 +48,8 @@ def self.load(source, serialized)
34048
end
34149

34250
require_relative "yarp/node"
51+
require_relative "yarp/node_ext"
52+
require_relative "yarp/parse_result"
34353
require_relative "yarp/parse_result/comments"
34454
require_relative "yarp/parse_result/newlines"
34555

@@ -352,59 +62,3 @@ def self.load(source, serialized)
35262
else
35363
require_relative "yarp/ffi"
35464
end
355-
356-
# Reopening the YARP module after yarp/node is required so that constant
357-
# reflection APIs will find the constants defined in the node file before these.
358-
# This block is meant to contain extra APIs we define on YARP nodes that aren't
359-
# templated and are meant as convenience methods.
360-
module YARP
361-
class FloatNode < Node
362-
# Returns the value of the node as a Ruby Float.
363-
def value
364-
Float(slice)
365-
end
366-
end
367-
368-
class ImaginaryNode < Node
369-
# Returns the value of the node as a Ruby Complex.
370-
def value
371-
Complex(0, numeric.value)
372-
end
373-
end
374-
375-
class IntegerNode < Node
376-
# Returns the value of the node as a Ruby Integer.
377-
def value
378-
Integer(slice)
379-
end
380-
end
381-
382-
class InterpolatedRegularExpressionNode < Node
383-
# Returns a numeric value that represents the flags that were used to create
384-
# the regular expression.
385-
def options
386-
o = flags & (RegularExpressionFlags::IGNORE_CASE | RegularExpressionFlags::EXTENDED | RegularExpressionFlags::MULTI_LINE)
387-
o |= Regexp::FIXEDENCODING if flags.anybits?(RegularExpressionFlags::EUC_JP | RegularExpressionFlags::WINDOWS_31J | RegularExpressionFlags::UTF_8)
388-
o |= Regexp::NOENCODING if flags.anybits?(RegularExpressionFlags::ASCII_8BIT)
389-
o
390-
end
391-
end
392-
393-
class RationalNode < Node
394-
# Returns the value of the node as a Ruby Rational.
395-
def value
396-
Rational(slice.chomp("r"))
397-
end
398-
end
399-
400-
class RegularExpressionNode < Node
401-
# Returns a numeric value that represents the flags that were used to create
402-
# the regular expression.
403-
def options
404-
o = flags & (RegularExpressionFlags::IGNORE_CASE | RegularExpressionFlags::EXTENDED | RegularExpressionFlags::MULTI_LINE)
405-
o |= Regexp::FIXEDENCODING if flags.anybits?(RegularExpressionFlags::EUC_JP | RegularExpressionFlags::WINDOWS_31J | RegularExpressionFlags::UTF_8)
406-
o |= Regexp::NOENCODING if flags.anybits?(RegularExpressionFlags::ASCII_8BIT)
407-
o
408-
end
409-
end
410-
end

0 commit comments

Comments
 (0)