riml 0.3.6 → 0.3.7
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.
- data/Rakefile +4 -0
- data/bin/riml +12 -2
- data/lib/riml.rb +16 -10
- data/lib/riml/ast_rewriter.rb +46 -39
- data/lib/riml/backtrace_filter.rb +1 -1
- data/lib/riml/compiler.rb +21 -18
- data/lib/riml/constants.rb +5 -1
- data/lib/riml/errors.rb +49 -4
- data/lib/riml/file_rollback.rb +4 -2
- data/lib/riml/grammar.y +16 -11
- data/lib/riml/imported_class.rb +2 -2
- data/lib/riml/lexer.rb +116 -114
- data/lib/riml/nodes.rb +27 -12
- data/lib/riml/parser.rb +753 -723
- data/lib/riml/path_cache.rb +4 -0
- data/lib/riml/repl.rb +1 -1
- data/lib/riml/warning_buffer.rb +2 -0
- data/version.rb +2 -2
- metadata +10 -10
data/lib/riml/file_rollback.rb
CHANGED
data/lib/riml/grammar.y
CHANGED
@@ -39,8 +39,8 @@ rule
|
|
39
39
|
|
40
40
|
Root:
|
41
41
|
/* nothing */ { result = make_node(val) { |_| Riml::Nodes.new([]) } }
|
42
|
-
| Statements { result = val[0] }
|
43
42
|
| Terminator { result = make_node(val) { |_| Riml::Nodes.new([]) } }
|
43
|
+
| Statements { result = val[0] }
|
44
44
|
;
|
45
45
|
|
46
46
|
# any list of expressions
|
@@ -163,10 +163,12 @@ rule
|
|
163
163
|
DictionaryLiteral { result = make_node(val) { |v| Riml::DictionaryNode.new(v[0]) } }
|
164
164
|
;
|
165
165
|
|
166
|
-
# {'key': 'value', '
|
166
|
+
# {'key': 'value', 'key2': 'value2'}
|
167
|
+
# Save as [['key', 'value'], ['key2', 'value2']] because ruby-1.8.7 offers
|
168
|
+
# no guarantee for key-value pair ordering.
|
167
169
|
DictionaryLiteral:
|
168
|
-
'{' DictItems '}' { result =
|
169
|
-
| '{' DictItems ',' '}' { result =
|
170
|
+
'{' DictItems '}' { result = val[1] }
|
171
|
+
| '{' DictItems ',' '}' { result = val[1] }
|
170
172
|
;
|
171
173
|
|
172
174
|
# [[key, value], [key, value]]
|
@@ -558,8 +560,8 @@ end
|
|
558
560
|
---- header
|
559
561
|
require File.expand_path("../lexer", __FILE__)
|
560
562
|
require File.expand_path("../nodes", __FILE__)
|
561
|
-
require File.expand_path("../ast_rewriter", __FILE__)
|
562
563
|
require File.expand_path("../errors", __FILE__)
|
564
|
+
require File.expand_path("../ast_rewriter", __FILE__)
|
563
565
|
---- inner
|
564
566
|
# This code will be put as-is in the parser class
|
565
567
|
|
@@ -590,13 +592,14 @@ end
|
|
590
592
|
ast = do_parse
|
591
593
|
rescue Racc::ParseError => e
|
592
594
|
raise unless @lexer
|
593
|
-
if @lexer.prev_token_is_keyword?
|
594
|
-
warning = "#{
|
595
|
+
if (invalid_token = @lexer.prev_token_is_keyword?)
|
596
|
+
warning = "#{invalid_token.inspect} is a keyword, and cannot " \
|
595
597
|
"be used as a variable name"
|
596
598
|
end
|
597
|
-
error_msg =
|
598
|
-
error_msg << "\
|
599
|
-
|
599
|
+
error_msg = e.message
|
600
|
+
error_msg << "\nWARNING: #{warning}" if warning
|
601
|
+
error = Riml::ParseError.new(error_msg, @lexer.filename, @lexer.lineno)
|
602
|
+
raise error
|
600
603
|
end
|
601
604
|
self.class.ast_cache[filename] = ast if filename
|
602
605
|
end
|
@@ -613,7 +616,9 @@ end
|
|
613
616
|
def next_token
|
614
617
|
return @tokens.shift unless @lexer
|
615
618
|
token = @lexer.next_token
|
616
|
-
|
619
|
+
if token && @lexer.parser_info
|
620
|
+
@current_parser_info = token.pop
|
621
|
+
end
|
617
622
|
token
|
618
623
|
end
|
619
624
|
|
data/lib/riml/imported_class.rb
CHANGED
data/lib/riml/lexer.rb
CHANGED
@@ -1,3 +1,6 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'strscan'
|
1
4
|
require File.expand_path('../constants', __FILE__)
|
2
5
|
require File.expand_path('../errors', __FILE__)
|
3
6
|
|
@@ -11,23 +14,17 @@ module Riml
|
|
11
14
|
ANCHORED_INTERPOLATION_REGEX = /\A#{INTERPOLATION_REGEX}/m
|
12
15
|
INTERPOLATION_SPLIT_REGEX = /(\#\{.*?\})/m
|
13
16
|
|
14
|
-
attr_reader :tokens, :prev_token, :
|
15
|
-
:
|
17
|
+
attr_reader :tokens, :prev_token, :current_indent,
|
18
|
+
:filename, :parser_info
|
16
19
|
attr_accessor :lineno
|
17
20
|
# for REPL
|
18
21
|
attr_accessor :ignore_indentation_check
|
19
22
|
|
20
23
|
def initialize(code, filename = nil, parser_info = false)
|
21
|
-
|
22
|
-
@code
|
23
|
-
@filename = filename
|
24
|
+
code.chomp!
|
25
|
+
@s = StringScanner.new(code)
|
26
|
+
@filename = filename || COMPILED_STRING_LOCATION
|
24
27
|
@parser_info = parser_info
|
25
|
-
set_start_state!
|
26
|
-
end
|
27
|
-
|
28
|
-
def set_start_state!
|
29
|
-
# number of characters consumed
|
30
|
-
@i = 0
|
31
28
|
# array of doubles and triples: [tokenname, tokenval, lineno_to_add(optional)]
|
32
29
|
# ex: [[:NEWLINE, "\n"]] OR [[:NEWLINE, "\n", 1]]
|
33
30
|
@token_buf = []
|
@@ -43,18 +40,16 @@ module Riml
|
|
43
40
|
@indent_pending = false
|
44
41
|
@dedent_pending = false
|
45
42
|
@in_function_declaration = false
|
46
|
-
@invalid_keyword = nil
|
47
43
|
end
|
48
44
|
|
49
45
|
def tokenize
|
50
|
-
set_start_state!
|
51
46
|
while next_token != nil; end
|
52
47
|
@tokens
|
53
48
|
end
|
54
49
|
|
55
50
|
def next_token
|
56
|
-
while @token_buf.empty? &&
|
57
|
-
tokenize_chunk
|
51
|
+
while @token_buf.empty? && !@s.eos?
|
52
|
+
tokenize_chunk
|
58
53
|
end
|
59
54
|
if !@token_buf.empty?
|
60
55
|
token = @token_buf.shift
|
@@ -74,55 +69,48 @@ module Riml
|
|
74
69
|
nil
|
75
70
|
end
|
76
71
|
|
77
|
-
def tokenize_chunk
|
78
|
-
@chunk = chunk
|
72
|
+
def tokenize_chunk
|
79
73
|
# deal with line continuations
|
80
|
-
if cont =
|
81
|
-
@i += cont.size
|
74
|
+
if cont = @s.scan(/\A\r?\n*[ \t\f]*\\/m)
|
82
75
|
@lineno += cont.each_line.to_a.size - 1
|
83
76
|
return
|
84
77
|
end
|
85
78
|
|
86
79
|
# all lines that start with ':' pass right through unmodified
|
87
|
-
if (prev_token.nil? || prev_token[0] == :NEWLINE) && (
|
88
|
-
@
|
89
|
-
@token_buf << [:EX_LITERAL, $1]
|
80
|
+
if (prev_token.nil? || prev_token[0] == :NEWLINE) && @s.scan(/\A[ \t\f]*:(.*)?$/)
|
81
|
+
@token_buf << [:EX_LITERAL, @s[1]]
|
90
82
|
return
|
91
83
|
end
|
92
84
|
|
93
|
-
if splat_var =
|
94
|
-
@i += splat_var.size
|
85
|
+
if splat_var = @s.scan(/\Aa:\d+/)
|
95
86
|
@token_buf << [:SCOPE_MODIFIER, 'a:'] << [:IDENTIFIER, splat_var[2..-1]]
|
96
87
|
# the 'n' scope modifier is added by riml
|
97
|
-
elsif
|
98
|
-
@
|
99
|
-
@
|
100
|
-
elsif scope_modifier_literal =
|
101
|
-
@i += scope_modifier_literal.size
|
88
|
+
elsif @s.check(/\A([bwtglsavn]:)(\w|\{)/)
|
89
|
+
@token_buf << [:SCOPE_MODIFIER, @s[1]]
|
90
|
+
@s.pos += 2
|
91
|
+
elsif scope_modifier_literal = @s.scan(/\A([bwtglsavn]:)/)
|
102
92
|
@token_buf << [:SCOPE_MODIFIER_LITERAL, scope_modifier_literal]
|
103
|
-
elsif special_var_prefix =
|
93
|
+
elsif special_var_prefix = (!@s.check(/\A&(\w:)?&/) && @s.scan(/\A(&(\w:)?|\$|@)/))
|
104
94
|
@token_buf << [:SPECIAL_VAR_PREFIX, special_var_prefix.strip]
|
105
|
-
@i += special_var_prefix.size
|
106
95
|
if special_var_prefix == '@'
|
107
|
-
|
108
|
-
next_char = new_chunk[0]
|
96
|
+
next_char = @s.peek(1)
|
109
97
|
if REGISTERS.include?(next_char)
|
110
98
|
@token_buf << [:IDENTIFIER, next_char]
|
111
|
-
@
|
99
|
+
@s.getch
|
112
100
|
end
|
113
101
|
else
|
114
102
|
@expecting_identifier = true
|
115
103
|
end
|
116
|
-
elsif
|
117
|
-
@token_buf << [:IDENTIFIER,
|
118
|
-
@
|
119
|
-
elsif identifier =
|
104
|
+
elsif @s.scan(/\A(function)\(/)
|
105
|
+
@token_buf << [:IDENTIFIER, @s[1]]
|
106
|
+
@s.pos -= 1
|
107
|
+
elsif identifier = @s.check(/\A[a-zA-Z_][\w#]*(\?|!)?/)
|
120
108
|
# keyword identifiers
|
121
109
|
if KEYWORDS.include?(identifier)
|
122
110
|
if identifier.match(/\Afunction/)
|
123
111
|
old_identifier = identifier.dup
|
124
112
|
identifier.sub!(/function/, "def")
|
125
|
-
@
|
113
|
+
@s.pos += (old_identifier.size - identifier.size)
|
126
114
|
end
|
127
115
|
|
128
116
|
if DEFINE_KEYWORDS.include?(identifier)
|
@@ -131,72 +119,62 @@ module Riml
|
|
131
119
|
|
132
120
|
# strip '?' out of token names and replace '!' with '_bang'
|
133
121
|
token_name = identifier.sub(/\?\Z/, "").sub(/!\Z/, "_bang").upcase
|
134
|
-
track_indent_level(
|
122
|
+
track_indent_level(identifier)
|
135
123
|
|
136
124
|
if VIML_END_KEYWORDS.include?(identifier)
|
137
125
|
token_name = :END
|
138
126
|
end
|
139
127
|
|
140
|
-
@token_buf << [token_name.
|
128
|
+
@token_buf << [token_name.to_sym, identifier]
|
141
129
|
|
142
|
-
elsif BUILTIN_COMMANDS.include?(identifier) && peek(identifier.size) != '('
|
130
|
+
elsif BUILTIN_COMMANDS.include?(identifier) && @s.peek(identifier.size + 1)[-1, 1] != '('
|
143
131
|
@token_buf << [:BUILTIN_COMMAND, identifier]
|
144
132
|
elsif RIML_FILE_COMMANDS.include? identifier
|
145
133
|
@token_buf << [:RIML_FILE_COMMAND, identifier]
|
146
134
|
elsif RIML_CLASS_COMMANDS.include? identifier
|
147
135
|
@token_buf << [:RIML_CLASS_COMMAND, identifier]
|
148
136
|
elsif VIML_COMMANDS.include?(identifier) && (prev_token.nil? || prev_token[0] == :NEWLINE)
|
149
|
-
@
|
150
|
-
|
151
|
-
until_eol = new_chunk[/.*$/].to_s
|
137
|
+
@s.pos += identifier.size
|
138
|
+
until_eol = @s.scan(/.*$/).to_s
|
152
139
|
@token_buf << [:EX_LITERAL, identifier << until_eol]
|
153
|
-
@i += until_eol.size
|
154
140
|
return
|
155
141
|
# method names and variable names
|
156
142
|
else
|
157
143
|
@token_buf << [:IDENTIFIER, identifier]
|
158
144
|
end
|
159
145
|
|
160
|
-
@
|
146
|
+
@s.pos += identifier.size
|
161
147
|
|
162
148
|
parse_dict_vals!
|
163
149
|
|
164
|
-
elsif @in_function_declaration && (splat_param =
|
150
|
+
elsif @in_function_declaration && (splat_param = @s.scan(/\A(\.{3}|\*[a-zA-Z_]\w*)/))
|
165
151
|
@token_buf << [:SPLAT_PARAM, splat_param]
|
166
|
-
|
167
|
-
elsif !@in_function_declaration && (splat_arg = chunk[/\A\*([bwtglsavn]:)?([a-zA-Z_]\w*|\d+)/])
|
152
|
+
elsif !@in_function_declaration && (splat_arg = @s.scan(/\A\*([bwtglsavn]:)?([a-zA-Z_]\w*|\d+)/))
|
168
153
|
@token_buf << [:SPLAT_ARG, splat_arg]
|
169
|
-
@i += splat_arg.size
|
170
154
|
# integer (octal)
|
171
|
-
elsif octal =
|
155
|
+
elsif octal = @s.scan(/\A0[0-7]+/)
|
172
156
|
@token_buf << [:NUMBER, octal]
|
173
|
-
@i += octal.size
|
174
157
|
# integer (hex)
|
175
|
-
elsif hex =
|
158
|
+
elsif hex = @s.scan(/\A0[xX][0-9a-fA-F]+/)
|
176
159
|
@token_buf << [:NUMBER, hex]
|
177
|
-
@i += hex.size
|
178
160
|
# integer or float (decimal)
|
179
|
-
elsif decimal =
|
161
|
+
elsif decimal = @s.scan(/\A[0-9]+(\.[0-9]+([eE][+-]?[0-9]+)?)?/)
|
180
162
|
@token_buf << [:NUMBER, decimal]
|
181
|
-
|
182
|
-
elsif interpolation = chunk[ANCHORED_INTERPOLATION_REGEX]
|
163
|
+
elsif interpolation = @s.scan(ANCHORED_INTERPOLATION_REGEX)
|
183
164
|
# "hey there, #{name}" = "hey there, " . name
|
184
165
|
parts = interpolation[1...-1].split(INTERPOLATION_SPLIT_REGEX)
|
185
166
|
handle_interpolation(*parts)
|
186
|
-
|
187
|
-
|
188
|
-
@
|
167
|
+
elsif (single_line_comment = @s.check(SINGLE_LINE_COMMENT_REGEX)) && (prev_token.nil? || prev_token[0] == :NEWLINE)
|
168
|
+
@s.pos += single_line_comment.size
|
169
|
+
@s.pos += 1 unless @s.eos? # consume newline
|
189
170
|
@lineno += single_line_comment.each_line.to_a.size
|
190
|
-
elsif inline_comment =
|
191
|
-
@i += inline_comment.size # inline comment, don't consume newline character
|
171
|
+
elsif inline_comment = @s.scan(/\A[ \t\f]*"[^"]*?$/)
|
192
172
|
@lineno += inline_comment.each_line.to_a.size - 1
|
193
|
-
elsif
|
194
|
-
@token_buf << [:STRING_D,
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
@i += string_single.size + 2
|
199
|
-
elsif newlines = chunk[/\A([\r\n]+)/, 1]
|
173
|
+
elsif (str = lex_string_double)
|
174
|
+
@token_buf << [:STRING_D, str]
|
175
|
+
elsif @s.scan(/\A'(([^']|'')*)'/)
|
176
|
+
@token_buf << [:STRING_S, @s[1]]
|
177
|
+
elsif newlines = @s.scan(/\A([\r\n]+)/)
|
200
178
|
# push only 1 newline
|
201
179
|
@token_buf << [:NEWLINE, "\n"] unless prev_token && prev_token[0] == :NEWLINE
|
202
180
|
|
@@ -210,14 +188,13 @@ module Riml
|
|
210
188
|
@in_function_declaration = false
|
211
189
|
end
|
212
190
|
|
213
|
-
@i += newlines.size
|
214
191
|
@lineno += newlines.size
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
heredoc_string =
|
220
|
-
@
|
192
|
+
# heredoc
|
193
|
+
elsif @s.scan(%r{\A<<(.+?)\r?\n})
|
194
|
+
pattern = @s[1]
|
195
|
+
@s.check(%r|(.+?\r?\n)(#{Regexp.escape(pattern)})|m)
|
196
|
+
heredoc_string = @s[1]
|
197
|
+
@s.pos += (pattern.size + heredoc_string.size)
|
221
198
|
heredoc_string.chomp!
|
222
199
|
if heredoc_string =~ INTERPOLATION_REGEX || %Q("#{heredoc_string}") =~ INTERPOLATION_REGEX
|
223
200
|
parts = heredoc_string.split(INTERPOLATION_SPLIT_REGEX)
|
@@ -227,37 +204,39 @@ module Riml
|
|
227
204
|
end
|
228
205
|
@lineno += heredoc_string.each_line.to_a.size
|
229
206
|
# operators of more than 1 char
|
230
|
-
elsif operator =
|
207
|
+
elsif operator = @s.scan(OPERATOR_REGEX)
|
231
208
|
@token_buf << [operator, operator]
|
232
|
-
|
233
|
-
elsif regexp = chunk[%r{\A/.*?[^\\]/}]
|
209
|
+
elsif regexp = @s.scan(%r{\A/.*?[^\\]/})
|
234
210
|
@token_buf << [:REGEXP, regexp]
|
235
|
-
|
236
|
-
elsif
|
237
|
-
@i += whitespaces.size
|
211
|
+
# whitespaces
|
212
|
+
elsif @s.scan(/\A[ \t\f]+/)
|
238
213
|
# operators and tokens of single chars, one of: ( ) , . [ ] ! + - = < > /
|
239
214
|
else
|
240
|
-
value =
|
215
|
+
value = @s.getch
|
241
216
|
if value == '|'
|
242
217
|
@token_buf << [:NEWLINE, "\n"]
|
243
218
|
else
|
244
219
|
@token_buf << [value, value]
|
245
220
|
end
|
246
|
-
|
247
|
-
|
221
|
+
# if we encounter `funcCall().`, the next character must be treated as
|
222
|
+
# a dictionary retrieval operation, not a string concatenation
|
223
|
+
# operation.
|
224
|
+
# However, if we see `funcCall().l:localVar`, we know it must be a
|
225
|
+
# string concatenation operation.
|
226
|
+
if value == ']' || value == ')' && (@s.peek(1) == '.' && @s.peek(3) != ':')
|
248
227
|
parse_dict_vals!
|
249
228
|
end
|
250
229
|
end
|
251
230
|
end
|
252
231
|
|
253
232
|
# Checks if any of previous n tokens are keywords.
|
254
|
-
# If any found,
|
233
|
+
# If any found, return the keyword, otherwise returns `false`.
|
255
234
|
def prev_token_is_keyword?(n = 2)
|
256
235
|
return false if n <= 0
|
257
236
|
(1..n).each do |i|
|
258
237
|
t = tokens[-i]
|
259
238
|
if t && t[1] && KEYWORDS.include?(t[1])
|
260
|
-
return
|
239
|
+
return t[1]
|
261
240
|
end
|
262
241
|
end
|
263
242
|
false
|
@@ -265,6 +244,35 @@ module Riml
|
|
265
244
|
|
266
245
|
private
|
267
246
|
|
247
|
+
# we have negative lookbehind in regexp engine
|
248
|
+
if RUBY_VERSION >= '1.9'
|
249
|
+
# have to use string constructor, as parser would throw SyntaxError if
|
250
|
+
# RUBY_VERSION < '1.9'. Literal regexp is `/\A"(.*?)(?<!\\)"/`
|
251
|
+
STRING_DOUBLE_NEGATIVE_LOOKBEHIND_REGEX = Regexp.new('\A"(.*?)(?<!\\\\)"')
|
252
|
+
def lex_string_double
|
253
|
+
@s.scan(STRING_DOUBLE_NEGATIVE_LOOKBEHIND_REGEX) && @s[1]
|
254
|
+
end
|
255
|
+
# we don't have negative lookbehind in regexp engine
|
256
|
+
else
|
257
|
+
def lex_string_double
|
258
|
+
str = ''
|
259
|
+
regex = /\A"(.*?)"/
|
260
|
+
pos = @s.pos
|
261
|
+
while @s.scan(regex)
|
262
|
+
match = @s[1]
|
263
|
+
str << match
|
264
|
+
if match[-1, 1] == '\\'
|
265
|
+
str << '"'
|
266
|
+
regex = /\A(.*?)"/
|
267
|
+
else
|
268
|
+
return str
|
269
|
+
end
|
270
|
+
end
|
271
|
+
@s.pos = pos
|
272
|
+
nil
|
273
|
+
end
|
274
|
+
end
|
275
|
+
|
268
276
|
def decorate_token(token)
|
269
277
|
token << {
|
270
278
|
:lineno => @lineno,
|
@@ -273,7 +281,7 @@ module Riml
|
|
273
281
|
token
|
274
282
|
end
|
275
283
|
|
276
|
-
def track_indent_level(
|
284
|
+
def track_indent_level(identifier)
|
277
285
|
case identifier.to_sym
|
278
286
|
when :def, :def!, :defm, :defm!, :while, :until, :for, :try, :class
|
279
287
|
@current_indent += 2
|
@@ -289,12 +297,11 @@ module Riml
|
|
289
297
|
end
|
290
298
|
end
|
291
299
|
|
300
|
+
# `dict.key` or `dict.key.other_key`, etc.
|
292
301
|
def parse_dict_vals!
|
293
|
-
|
294
|
-
|
295
|
-
if vals = new_chunk[/\A\.([\w.]+)(?!:)/, 1]
|
302
|
+
if @s.scan(/\A\.([\w.]+)(?!:)/)
|
303
|
+
vals = @s[1]
|
296
304
|
parts = vals.split('.')
|
297
|
-
@i += vals.size + 1
|
298
305
|
if @in_function_declaration
|
299
306
|
@token_buf.last[1] << ".#{vals}"
|
300
307
|
else
|
@@ -306,13 +313,20 @@ module Riml
|
|
306
313
|
end
|
307
314
|
|
308
315
|
def check_indentation
|
309
|
-
|
310
|
-
|
316
|
+
if @current_indent > 0
|
317
|
+
error_msg = "Missing #{(@current_indent / 2)} END identifier(s)"
|
318
|
+
error = Riml::SyntaxError.new(error_msg, @filename, @lineno)
|
319
|
+
raise error
|
320
|
+
elsif @current_indent < 0
|
321
|
+
error_msg = "#{(@current_indent / 2).abs} too many END identifiers"
|
322
|
+
error = Riml::SyntaxError.new(error_msg, @filename, @lineno)
|
323
|
+
raise error
|
324
|
+
end
|
311
325
|
end
|
312
326
|
|
313
327
|
def handle_interpolation(*parts)
|
314
328
|
parts.delete_if {|p| p.empty?}.each_with_index do |part, i|
|
315
|
-
if part[0..1] == '#{' && part[-1] == '}'
|
329
|
+
if part[0..1] == '#{' && part[-1, 1] == '}'
|
316
330
|
interpolation_content = part[2...-1]
|
317
331
|
@token_buf.concat tokenize_without_moving_pos(interpolation_content)
|
318
332
|
else
|
@@ -331,30 +345,18 @@ module Riml
|
|
331
345
|
|
332
346
|
def tokenize_without_moving_pos(code)
|
333
347
|
Lexer.new(code, filename, false).tap do |l|
|
334
|
-
l.lineno = lineno
|
348
|
+
l.lineno = @lineno
|
335
349
|
end.tokenize
|
336
350
|
end
|
337
351
|
|
338
352
|
def statement_modifier?
|
339
|
-
|
353
|
+
old_pos = @s.pos
|
340
354
|
# backtrack until the beginning of the line
|
341
|
-
@
|
342
|
-
|
343
|
-
new_chunk.to_s[/\A(.+?)(if|unless).+?$/] && !$1.strip.empty?
|
355
|
+
@s.pos -= 1 until @s.bol?
|
356
|
+
@s.check(/\A(.+?)(if|unless).+?$/) && !@s[1].strip.empty?
|
344
357
|
ensure
|
345
|
-
@
|
346
|
-
end
|
347
|
-
|
348
|
-
def get_new_chunk
|
349
|
-
@code[@i..-1]
|
358
|
+
@s.pos = old_pos
|
350
359
|
end
|
351
360
|
|
352
|
-
|
353
|
-
@i < @code.size
|
354
|
-
end
|
355
|
-
|
356
|
-
def peek(n = 1)
|
357
|
-
@chunk[n]
|
358
|
-
end
|
359
|
-
end
|
361
|
+
end unless defined?(Riml::Lexer)
|
360
362
|
end
|