riml 0.3.6 → 0.3.7
Sign up to get free protection for your applications and to get access to all the features.
- 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
|