yarp 0.7.0 → 0.9.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.
@@ -12,16 +12,24 @@
12
12
  // A yp_buffer_t is a simple memory buffer that stores data in a contiguous
13
13
  // block of memory. It is used to store the serialized representation of a
14
14
  // YARP tree.
15
- // NOTE: keep in sync with YARP::LibRubyParser::Buffer in lib/yarp.rb
16
15
  typedef struct {
17
16
  char *value;
18
17
  size_t length;
19
18
  size_t capacity;
20
19
  } yp_buffer_t;
21
20
 
21
+ // Return the size of the yp_buffer_t struct.
22
+ YP_EXPORTED_FUNCTION size_t yp_buffer_sizeof(void);
23
+
22
24
  // Initialize a yp_buffer_t with its default values.
23
25
  YP_EXPORTED_FUNCTION bool yp_buffer_init(yp_buffer_t *buffer);
24
26
 
27
+ // Return the value of the buffer.
28
+ YP_EXPORTED_FUNCTION char * yp_buffer_value(yp_buffer_t *buffer);
29
+
30
+ // Return the length of the buffer.
31
+ YP_EXPORTED_FUNCTION size_t yp_buffer_length(yp_buffer_t *buffer);
32
+
25
33
  // Append the given amount of space as zeroes to the buffer.
26
34
  void yp_buffer_append_zeroes(yp_buffer_t *buffer, size_t length);
27
35
 
@@ -51,6 +51,9 @@ typedef struct {
51
51
  size_t capacity;
52
52
  } yp_constant_pool_t;
53
53
 
54
+ // Define an empty constant pool.
55
+ #define YP_CONSTANT_POOL_EMPTY ((yp_constant_pool_t) { .constants = NULL, .size = 0, .capacity = 0 })
56
+
54
57
  // Initialize a new constant pool with a given capacity.
55
58
  bool yp_constant_pool_init(yp_constant_pool_t *pool, size_t capacity);
56
59
 
@@ -15,9 +15,7 @@
15
15
  // int value;
16
16
  // } yp_int_node_t;
17
17
  //
18
- // yp_list_t list;
19
- // yp_list_init(&list);
20
- //
18
+ // yp_list_t list = YP_LIST_EMPTY;
21
19
  // yp_int_node_t *node = malloc(sizeof(yp_int_node_t));
22
20
  // node->value = 5;
23
21
  //
@@ -45,18 +43,20 @@ typedef struct yp_list_node {
45
43
  // This represents the overall linked list. It keeps a pointer to the head and
46
44
  // tail so that iteration is easy and pushing new nodes is easy.
47
45
  typedef struct {
46
+ size_t size;
48
47
  yp_list_node_t *head;
49
48
  yp_list_node_t *tail;
50
49
  } yp_list_t;
51
50
 
52
- // Initializes a new list.
53
- YP_EXPORTED_FUNCTION void yp_list_init(yp_list_t *list);
51
+ // This represents an empty list. It's used to initialize a stack-allocated list
52
+ // as opposed to a method call.
53
+ #define YP_LIST_EMPTY ((yp_list_t) { .size = 0, .head = NULL, .tail = NULL })
54
54
 
55
55
  // Returns true if the given list is empty.
56
56
  YP_EXPORTED_FUNCTION bool yp_list_empty_p(yp_list_t *list);
57
57
 
58
- // Returns the size of the list in O(n) time.
59
- YP_EXPORTED_FUNCTION uint32_t yp_list_size(yp_list_t *list);
58
+ // Returns the size of the list.
59
+ YP_EXPORTED_FUNCTION size_t yp_list_size(yp_list_t *list);
60
60
 
61
61
  // Append a node to the given list.
62
62
  void yp_list_append(yp_list_t *list, yp_list_node_t *node);
@@ -35,6 +35,10 @@ typedef struct {
35
35
  size_t column;
36
36
  } yp_line_column_t;
37
37
 
38
+ #define YP_NEWLINE_LIST_EMPTY ((yp_newline_list_t) { \
39
+ .start = NULL, .offsets = NULL, .size = 0, .capacity = 0, .last_offset = 0, .last_index = 0 \
40
+ })
41
+
38
42
  // Initialize a new newline list with the given capacity. Returns true if the
39
43
  // allocation of the offsets succeeds, otherwise returns false.
40
44
  bool yp_newline_list_init(yp_newline_list_t *list, const char *start, size_t capacity);
@@ -43,6 +47,9 @@ bool yp_newline_list_init(yp_newline_list_t *list, const char *start, size_t cap
43
47
  // the offsets succeeds (if one was necessary), otherwise returns false.
44
48
  bool yp_newline_list_append(yp_newline_list_t *list, const char *cursor);
45
49
 
50
+ // Conditionally append a new offset to the newline list, if the value passed in is a newline.
51
+ bool yp_newline_list_check_append(yp_newline_list_t *list, const char *cursor);
52
+
46
53
  // Returns the line and column of the given offset. If the offset is not in the
47
54
  // list, the line and column of the closest offset less than the given offset
48
55
  // are returned.
@@ -10,7 +10,7 @@
10
10
  typedef uint32_t yp_state_stack_t;
11
11
 
12
12
  // Initializes the state stack to an empty stack.
13
- void yp_state_stack_init(yp_state_stack_t *stack);
13
+ #define YP_STATE_STACK_EMPTY ((yp_state_stack_t) 0)
14
14
 
15
15
  // Pushes a value onto the stack.
16
16
  void yp_state_stack_push(yp_state_stack_t *stack, bool value);
@@ -1,4 +1,4 @@
1
1
  #define YP_VERSION_MAJOR 0
2
- #define YP_VERSION_MINOR 7
2
+ #define YP_VERSION_MINOR 9
3
3
  #define YP_VERSION_PATCH 0
4
- #define YP_VERSION "0.7.0"
4
+ #define YP_VERSION "0.9.0"
data/include/yarp.h CHANGED
@@ -13,6 +13,7 @@
13
13
  #include "yarp/util/yp_char.h"
14
14
  #include "yarp/util/yp_memchr.h"
15
15
  #include "yarp/util/yp_strpbrk.h"
16
+ #include "yarp/version.h"
16
17
 
17
18
  #include <assert.h>
18
19
  #include <stdarg.h>
@@ -30,6 +31,11 @@ void yp_serialize_content(yp_parser_t *parser, yp_node_t *node, yp_buffer_t *buf
30
31
 
31
32
  void yp_print_node(yp_parser_t *parser, yp_node_t *node);
32
33
 
34
+ void yp_parser_metadata(yp_parser_t *parser, const char *metadata);
35
+
36
+ // Generate a scope node from the given node.
37
+ void yp_scope_node_init(yp_node_t *node, yp_scope_node_t *dest);
38
+
33
39
  // The YARP version and the serialization format.
34
40
  YP_EXPORTED_FUNCTION const char * yp_version(void);
35
41
 
@@ -65,6 +71,10 @@ YP_EXPORTED_FUNCTION void yp_parse_serialize(const char *source, size_t size, yp
65
71
  // Lex the given source and serialize to the given buffer.
66
72
  YP_EXPORTED_FUNCTION void yp_lex_serialize(const char *source, size_t size, const char *filepath, yp_buffer_t *buffer);
67
73
 
74
+ // Parse and serialize both the AST and the tokens represented by the given
75
+ // source to the given buffer.
76
+ YP_EXPORTED_FUNCTION void yp_parse_lex_serialize(const char *source, size_t size, yp_buffer_t *buffer, const char *metadata);
77
+
68
78
  // Returns a string representation of the given token type.
69
79
  YP_EXPORTED_FUNCTION const char * yp_token_type_to_str(yp_token_type_t token_type);
70
80
 
@@ -0,0 +1,267 @@
1
+ # frozen_string_literal: true
2
+
3
+ module YARP
4
+ class DesugarVisitor < MutationVisitor
5
+ # @@foo &&= bar
6
+ #
7
+ # becomes
8
+ #
9
+ # @@foo && @@foo = bar
10
+ def visit_class_variable_and_write_node(node)
11
+ AndNode.new(
12
+ ClassVariableReadNode.new(node.name_loc),
13
+ ClassVariableWriteNode.new(node.name_loc, node.value, node.operator_loc, node.location),
14
+ node.operator_loc,
15
+ node.location
16
+ )
17
+ end
18
+
19
+ # @@foo ||= bar
20
+ #
21
+ # becomes
22
+ #
23
+ # @@foo || @@foo = bar
24
+ def visit_class_variable_or_write_node(node)
25
+ OrNode.new(
26
+ ClassVariableReadNode.new(node.name_loc),
27
+ ClassVariableWriteNode.new(node.name_loc, node.value, node.operator_loc, node.location),
28
+ node.operator_loc,
29
+ node.location
30
+ )
31
+ end
32
+
33
+ # @@foo += bar
34
+ #
35
+ # becomes
36
+ #
37
+ # @@foo = @@foo + bar
38
+ def visit_class_variable_operator_write_node(node)
39
+ desugar_operator_write_node(node, ClassVariableWriteNode, ClassVariableReadNode)
40
+ end
41
+
42
+ # Foo &&= bar
43
+ #
44
+ # becomes
45
+ #
46
+ # Foo && Foo = bar
47
+ def visit_constant_and_write_node(node)
48
+ AndNode.new(
49
+ ConstantReadNode.new(node.name_loc),
50
+ ConstantWriteNode.new(node.name_loc, node.value, node.operator_loc, node.location),
51
+ node.operator_loc,
52
+ node.location
53
+ )
54
+ end
55
+
56
+ # Foo ||= bar
57
+ #
58
+ # becomes
59
+ #
60
+ # Foo || Foo = bar
61
+ def visit_constant_or_write_node(node)
62
+ OrNode.new(
63
+ ConstantReadNode.new(node.name_loc),
64
+ ConstantWriteNode.new(node.name_loc, node.value, node.operator_loc, node.location),
65
+ node.operator_loc,
66
+ node.location
67
+ )
68
+ end
69
+
70
+ # Foo += bar
71
+ #
72
+ # becomes
73
+ #
74
+ # Foo = Foo + bar
75
+ def visit_constant_operator_write_node(node)
76
+ desugar_operator_write_node(node, ConstantWriteNode, ConstantReadNode)
77
+ end
78
+
79
+ # Foo::Bar &&= baz
80
+ #
81
+ # becomes
82
+ #
83
+ # Foo::Bar && Foo::Bar = baz
84
+ def visit_constant_path_and_write_node(node)
85
+ AndNode.new(
86
+ node.target,
87
+ ConstantPathWriteNode.new(node.target, node.value, node.operator_loc, node.location),
88
+ node.operator_loc,
89
+ node.location
90
+ )
91
+ end
92
+
93
+ # Foo::Bar ||= baz
94
+ #
95
+ # becomes
96
+ #
97
+ # Foo::Bar || Foo::Bar = baz
98
+ def visit_constant_path_or_write_node(node)
99
+ OrNode.new(
100
+ node.target,
101
+ ConstantPathWriteNode.new(node.target, node.value, node.operator_loc, node.location),
102
+ node.operator_loc,
103
+ node.location
104
+ )
105
+ end
106
+
107
+ # Foo::Bar += baz
108
+ #
109
+ # becomes
110
+ #
111
+ # Foo::Bar = Foo::Bar + baz
112
+ def visit_constant_path_operator_write_node(node)
113
+ ConstantPathWriteNode.new(
114
+ node.target,
115
+ CallNode.new(
116
+ node.target,
117
+ nil,
118
+ node.operator_loc.copy(length: node.operator_loc.length - 1),
119
+ nil,
120
+ ArgumentsNode.new([node.value], node.value.location),
121
+ nil,
122
+ nil,
123
+ 0,
124
+ node.operator_loc.slice.chomp("="),
125
+ node.location
126
+ ),
127
+ node.operator_loc.copy(start_offset: node.operator_loc.end_offset - 1, length: 1),
128
+ node.location
129
+ )
130
+ end
131
+
132
+ # $foo &&= bar
133
+ #
134
+ # becomes
135
+ #
136
+ # $foo && $foo = bar
137
+ def visit_global_variable_and_write_node(node)
138
+ AndNode.new(
139
+ GlobalVariableReadNode.new(node.name_loc),
140
+ GlobalVariableWriteNode.new(node.name_loc, node.value, node.operator_loc, node.location),
141
+ node.operator_loc,
142
+ node.location
143
+ )
144
+ end
145
+
146
+ # $foo ||= bar
147
+ #
148
+ # becomes
149
+ #
150
+ # $foo || $foo = bar
151
+ def visit_global_variable_or_write_node(node)
152
+ OrNode.new(
153
+ GlobalVariableReadNode.new(node.name_loc),
154
+ GlobalVariableWriteNode.new(node.name_loc, node.value, node.operator_loc, node.location),
155
+ node.operator_loc,
156
+ node.location
157
+ )
158
+ end
159
+
160
+ # $foo += bar
161
+ #
162
+ # becomes
163
+ #
164
+ # $foo = $foo + bar
165
+ def visit_global_variable_operator_write_node(node)
166
+ desugar_operator_write_node(node, GlobalVariableWriteNode, GlobalVariableReadNode)
167
+ end
168
+
169
+ # @foo &&= bar
170
+ #
171
+ # becomes
172
+ #
173
+ # @foo && @foo = bar
174
+ def visit_instance_variable_and_write_node(node)
175
+ AndNode.new(
176
+ InstanceVariableReadNode.new(node.name_loc),
177
+ InstanceVariableWriteNode.new(node.name_loc, node.value, node.operator_loc, node.location),
178
+ node.operator_loc,
179
+ node.location
180
+ )
181
+ end
182
+
183
+ # @foo ||= bar
184
+ #
185
+ # becomes
186
+ #
187
+ # @foo || @foo = bar
188
+ def visit_instance_variable_or_write_node(node)
189
+ OrNode.new(
190
+ InstanceVariableReadNode.new(node.name_loc),
191
+ InstanceVariableWriteNode.new(node.name_loc, node.value, node.operator_loc, node.location),
192
+ node.operator_loc,
193
+ node.location
194
+ )
195
+ end
196
+
197
+ # @foo += bar
198
+ #
199
+ # becomes
200
+ #
201
+ # @foo = @foo + bar
202
+ def visit_instance_variable_operator_write_node(node)
203
+ desugar_operator_write_node(node, InstanceVariableWriteNode, InstanceVariableReadNode)
204
+ end
205
+
206
+ # foo &&= bar
207
+ #
208
+ # becomes
209
+ #
210
+ # foo && foo = bar
211
+ def visit_local_variable_and_write_node(node)
212
+ AndNode.new(
213
+ LocalVariableReadNode.new(node.constant_id, node.depth, node.name_loc),
214
+ LocalVariableWriteNode.new(node.constant_id, node.depth, node.name_loc, node.value, node.operator_loc, node.location),
215
+ node.operator_loc,
216
+ node.location
217
+ )
218
+ end
219
+
220
+ # foo ||= bar
221
+ #
222
+ # becomes
223
+ #
224
+ # foo || foo = bar
225
+ def visit_local_variable_or_write_node(node)
226
+ OrNode.new(
227
+ LocalVariableReadNode.new(node.constant_id, node.depth, node.name_loc),
228
+ LocalVariableWriteNode.new(node.constant_id, node.depth, node.name_loc, node.value, node.operator_loc, node.location),
229
+ node.operator_loc,
230
+ node.location
231
+ )
232
+ end
233
+
234
+ # foo += bar
235
+ #
236
+ # becomes
237
+ #
238
+ # foo = foo + bar
239
+ def visit_local_variable_operator_write_node(node)
240
+ desugar_operator_write_node(node, LocalVariableWriteNode, LocalVariableReadNode, arguments: [node.constant_id, node.depth])
241
+ end
242
+
243
+ private
244
+
245
+ # Desugar `x += y` to `x = x + y`
246
+ def desugar_operator_write_node(node, write_class, read_class, arguments: [])
247
+ write_class.new(
248
+ *arguments,
249
+ node.name_loc,
250
+ CallNode.new(
251
+ read_class.new(*arguments, node.name_loc),
252
+ nil,
253
+ node.operator_loc.copy(length: node.operator_loc.length - 1),
254
+ nil,
255
+ ArgumentsNode.new([node.value], node.value.location),
256
+ nil,
257
+ nil,
258
+ 0,
259
+ node.operator_loc.slice.chomp("="),
260
+ node.location
261
+ ),
262
+ node.operator_loc.copy(start_offset: node.operator_loc.end_offset - 1, length: 1),
263
+ node.location
264
+ )
265
+ end
266
+ end
267
+ end
data/lib/yarp/ffi.rb CHANGED
@@ -70,12 +70,16 @@ module YARP
70
70
  "yarp.h",
71
71
  "yp_version",
72
72
  "yp_parse_serialize",
73
- "yp_lex_serialize"
73
+ "yp_lex_serialize",
74
+ "yp_parse_lex_serialize"
74
75
  )
75
76
 
76
77
  load_exported_functions_from(
77
78
  "yarp/util/yp_buffer.h",
79
+ "yp_buffer_sizeof",
78
80
  "yp_buffer_init",
81
+ "yp_buffer_value",
82
+ "yp_buffer_length",
79
83
  "yp_buffer_free"
80
84
  )
81
85
 
@@ -88,34 +92,49 @@ module YARP
88
92
  "yp_string_sizeof"
89
93
  )
90
94
 
91
- # This object represents a yp_buffer_t. Its structure must be kept in sync
92
- # with the C version.
93
- class YPBuffer < FFI::Struct
94
- layout value: :pointer, length: :size_t, capacity: :size_t
95
+ # This object represents a yp_buffer_t. We only use it as an opaque pointer,
96
+ # so it doesn't need to know the fields of yp_buffer_t.
97
+ class YPBuffer
98
+ SIZEOF = LibRubyParser.yp_buffer_sizeof
95
99
 
96
- # Read the contents of the buffer into a String object and return it.
97
- def to_ruby_string
98
- self[:value].read_string(self[:length])
100
+ attr_reader :pointer
101
+
102
+ def initialize(pointer)
103
+ @pointer = pointer
99
104
  end
100
- end
101
105
 
102
- # Initialize a new buffer and yield it to the block. The buffer will be
103
- # automatically freed when the block returns.
104
- def self.with_buffer(&block)
105
- buffer = YPBuffer.new
106
-
107
- begin
108
- raise unless yp_buffer_init(buffer)
109
- yield buffer
110
- ensure
111
- yp_buffer_free(buffer)
112
- buffer.pointer.free
106
+ def value
107
+ LibRubyParser.yp_buffer_value(pointer)
108
+ end
109
+
110
+ def length
111
+ LibRubyParser.yp_buffer_length(pointer)
112
+ end
113
+
114
+ def read
115
+ value.read_string(length)
116
+ end
117
+
118
+ # Initialize a new buffer and yield it to the block. The buffer will be
119
+ # automatically freed when the block returns.
120
+ def self.with(&block)
121
+ pointer = FFI::MemoryPointer.new(SIZEOF)
122
+
123
+ begin
124
+ raise unless LibRubyParser.yp_buffer_init(pointer)
125
+ yield new(pointer)
126
+ ensure
127
+ LibRubyParser.yp_buffer_free(pointer)
128
+ pointer.free
129
+ end
113
130
  end
114
131
  end
115
132
 
116
133
  # This object represents a yp_string_t. We only use it as an opaque pointer,
117
134
  # so it doesn't have to be an FFI::Struct.
118
135
  class YPString
136
+ SIZEOF = LibRubyParser.yp_string_sizeof
137
+
119
138
  attr_reader :pointer
120
139
 
121
140
  def initialize(pointer)
@@ -133,23 +152,18 @@ module YARP
133
152
  def read
134
153
  source.read_string(length)
135
154
  end
136
- end
137
155
 
138
- # This is the size of a yp_string_t. It is returned by the yp_string_sizeof
139
- # function which we call once to ensure we have sufficient space for the
140
- # yp_string_t FFI pointer.
141
- SIZEOF_YP_STRING = yp_string_sizeof
142
-
143
- # Yields a yp_string_t pointer to the given block.
144
- def self.with_string(filepath, &block)
145
- string = FFI::MemoryPointer.new(SIZEOF_YP_STRING)
146
-
147
- begin
148
- raise unless yp_string_mapped_init(string, filepath)
149
- yield YPString.new(string)
150
- ensure
151
- yp_string_free(string)
152
- string.free
156
+ # Yields a yp_string_t pointer to the given block.
157
+ def self.with(filepath, &block)
158
+ pointer = FFI::MemoryPointer.new(SIZEOF)
159
+
160
+ begin
161
+ raise unless LibRubyParser.yp_string_mapped_init(pointer, filepath)
162
+ yield new(pointer)
163
+ ensure
164
+ LibRubyParser.yp_string_free(pointer)
165
+ pointer.free
166
+ end
153
167
  end
154
168
  end
155
169
  end
@@ -162,10 +176,10 @@ module YARP
162
176
  VERSION = LibRubyParser.yp_version.read_string
163
177
 
164
178
  def self.dump_internal(source, source_size, filepath)
165
- LibRubyParser.with_buffer do |buffer|
179
+ LibRubyParser::YPBuffer.with do |buffer|
166
180
  metadata = [filepath.bytesize, filepath.b, 0].pack("LA*L") if filepath
167
- LibRubyParser.yp_parse_serialize(source, source_size, buffer, metadata)
168
- buffer.to_ruby_string
181
+ LibRubyParser.yp_parse_serialize(source, source_size, buffer.pointer, metadata)
182
+ buffer.read
169
183
  end
170
184
  end
171
185
  private_class_method :dump_internal
@@ -177,35 +191,62 @@ module YARP
177
191
 
178
192
  # Mirror the YARP.dump_file API by using the serialization API.
179
193
  def self.dump_file(filepath)
180
- LibRubyParser.with_string(filepath) do |string|
194
+ LibRubyParser::YPString.with(filepath) do |string|
181
195
  dump_internal(string.source, string.length, filepath)
182
196
  end
183
197
  end
184
198
 
185
199
  # Mirror the YARP.lex API by using the serialization API.
186
200
  def self.lex(code, filepath = nil)
187
- LibRubyParser.with_buffer do |buffer|
188
- LibRubyParser.yp_lex_serialize(code, code.bytesize, filepath, buffer)
189
-
190
- source = Source.new(code)
191
- Serialize.load_tokens(source, buffer.to_ruby_string).with_source(source)
201
+ LibRubyParser::YPBuffer.with do |buffer|
202
+ LibRubyParser.yp_lex_serialize(code, code.bytesize, filepath, buffer.pointer)
203
+ Serialize.load_tokens(Source.new(code), buffer.read)
192
204
  end
193
205
  end
194
206
 
195
207
  # Mirror the YARP.lex_file API by using the serialization API.
196
208
  def self.lex_file(filepath)
197
- LibRubyParser.with_string(filepath) { |string| lex(string.read, filepath) }
209
+ LibRubyParser::YPString.with(filepath) do |string|
210
+ lex(string.read, filepath)
211
+ end
198
212
  end
199
213
 
200
214
  # Mirror the YARP.parse API by using the serialization API.
201
215
  def self.parse(code, filepath = nil)
202
- YARP.load(code, dump(code, filepath)).with_source(Source.new(code))
216
+ YARP.load(code, dump(code, filepath))
203
217
  end
204
218
 
205
219
  # Mirror the YARP.parse_file API by using the serialization API. This uses
206
220
  # native strings instead of Ruby strings because it allows us to use mmap when
207
221
  # it is available.
208
222
  def self.parse_file(filepath)
209
- LibRubyParser.with_string(filepath) { |string| parse(string.read, filepath) }
223
+ LibRubyParser::YPString.with(filepath) do |string|
224
+ parse(string.read, filepath)
225
+ end
226
+ end
227
+
228
+ # Mirror the YARP.parse_lex API by using the serialization API.
229
+ def self.parse_lex(code, filepath = nil)
230
+ LibRubyParser::YPBuffer.with do |buffer|
231
+ metadata = [filepath.bytesize, filepath.b, 0].pack("LA*L") if filepath
232
+ LibRubyParser.yp_parse_lex_serialize(code, code.bytesize, buffer.pointer, metadata)
233
+
234
+ source = Source.new(code)
235
+ loader = Serialize::Loader.new(source, buffer.read)
236
+
237
+ tokens = loader.load_tokens
238
+ node, comments, errors, warnings = loader.load_nodes
239
+
240
+ tokens.each { |token,| token.value.force_encoding(loader.encoding) }
241
+
242
+ ParseResult.new([node, tokens], comments, errors, warnings, source)
243
+ end
244
+ end
245
+
246
+ # Mirror the YARP.parse_lex_file API by using the serialization API.
247
+ def self.parse_lex_file(filepath)
248
+ LibRubyParser::YPString.with(filepath) do |string|
249
+ parse_lex(string.read, filepath)
250
+ end
210
251
  end
211
252
  end