riml 0.1.5 → 0.1.6
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/bin/riml +9 -7
- data/lib/ast_rewriter.rb +21 -20
- data/lib/class_map.rb +4 -0
- data/lib/compiler.rb +26 -22
- data/lib/constants.rb +1 -2
- data/lib/grammar.y +9 -9
- data/lib/lexer.rb +45 -29
- data/lib/nodes.rb +20 -14
- data/lib/parser.rb +961 -976
- data/lib/riml.rb +8 -4
- data/lib/walker.rb +1 -1
- data/version.rb +2 -2
- metadata +4 -4
data/bin/riml
CHANGED
@@ -22,12 +22,14 @@ module Riml
|
|
22
22
|
opts.separator ""
|
23
23
|
opts.separator "Specific options:"
|
24
24
|
|
25
|
-
opts.on("-c", "--compile
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
25
|
+
opts.on("-c", "--compile FILES", Array, "Compile riml file(s) to VimL.") do |files|
|
26
|
+
files.each do |f|
|
27
|
+
if File.exists?(f)
|
28
|
+
options.compile_files << f
|
29
|
+
else
|
30
|
+
warn "Couldn't find file #{f.inspect}."
|
31
|
+
exit 1
|
32
|
+
end
|
31
33
|
end
|
32
34
|
end
|
33
35
|
|
@@ -65,7 +67,7 @@ module Riml
|
|
65
67
|
options = Options.parse(ARGV)
|
66
68
|
|
67
69
|
if options.stdio
|
68
|
-
puts Riml.compile($stdin.
|
70
|
+
puts Riml.compile($stdin.read)
|
69
71
|
elsif options.compile_files.any?
|
70
72
|
Riml.compile_files(*options.compile_files)
|
71
73
|
end
|
data/lib/ast_rewriter.rb
CHANGED
@@ -7,25 +7,26 @@ module Riml
|
|
7
7
|
include Riml::Constants
|
8
8
|
|
9
9
|
attr_accessor :ast
|
10
|
-
|
10
|
+
attr_reader :classes
|
11
|
+
|
12
|
+
def initialize(ast = nil, classes = nil)
|
11
13
|
@ast = ast
|
14
|
+
@classes = classes || ClassMap.new
|
12
15
|
end
|
13
16
|
|
14
|
-
# Map of {"ClassName" => ClassDefinitionNode}
|
15
|
-
# Can also query object for superclass of a named class, etc...
|
16
|
-
#
|
17
|
-
# Ex : classes["SomeClass"].superclass_name => "SomeClassBase"
|
18
|
-
def classes
|
19
|
-
@@classes ||= ClassMap.new
|
20
|
-
end
|
21
17
|
|
22
18
|
def rewrite
|
23
19
|
establish_parents(ast)
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
20
|
+
rewriters = [
|
21
|
+
StrictEqualsComparisonOperator.new(ast, classes),
|
22
|
+
VarEqualsComparisonOperator.new(ast, classes),
|
23
|
+
ClassDefinitionToFunctions.new(ast, classes),
|
24
|
+
ObjectInstantiationToCall.new(ast, classes),
|
25
|
+
CallToExplicitCall.new(ast, classes)
|
26
|
+
]
|
27
|
+
rewriters.each do |rewriter|
|
28
|
+
rewriter.rewrite_on_match
|
29
|
+
end
|
29
30
|
ast
|
30
31
|
end
|
31
32
|
|
@@ -100,7 +101,7 @@ module Riml
|
|
100
101
|
classes[node.name] = node
|
101
102
|
|
102
103
|
name, expressions = node.name, node.expressions
|
103
|
-
InsertInitializeMethod.new(node).rewrite_on_match
|
104
|
+
InsertInitializeMethod.new(node, classes).rewrite_on_match
|
104
105
|
constructor = node.constructor
|
105
106
|
constructor.scope_modifier = 'g:' unless constructor.scope_modifier
|
106
107
|
constructor.name = node.constructor_name
|
@@ -110,8 +111,8 @@ module Riml
|
|
110
111
|
AssignNode.new('=', GetVariableNode.new(nil, dict_name), DictionaryNode.new({}))
|
111
112
|
)
|
112
113
|
|
113
|
-
SuperToObjectExtension.new(constructor, node).rewrite_on_match
|
114
|
-
MethodToNestedFunction.new(node, constructor, dict_name).rewrite_on_match
|
114
|
+
SuperToObjectExtension.new(constructor, classes, node).rewrite_on_match
|
115
|
+
MethodToNestedFunction.new(node, classes, constructor, dict_name).rewrite_on_match
|
115
116
|
SelfToDictName.new(dict_name).rewrite_on_match(constructor)
|
116
117
|
|
117
118
|
constructor.expressions.push(
|
@@ -122,8 +123,8 @@ module Riml
|
|
122
123
|
|
123
124
|
class MethodToNestedFunction < AST_Rewriter
|
124
125
|
attr_reader :constructor, :dict_name
|
125
|
-
def initialize(class_node, constructor, dict_name)
|
126
|
-
super(class_node)
|
126
|
+
def initialize(class_node, classes, constructor, dict_name)
|
127
|
+
super(class_node, classes)
|
127
128
|
@dict_name, @constructor = dict_name, constructor
|
128
129
|
end
|
129
130
|
|
@@ -190,8 +191,8 @@ module Riml
|
|
190
191
|
|
191
192
|
class SuperToObjectExtension < AST_Rewriter
|
192
193
|
attr_reader :class_node
|
193
|
-
def initialize(constructor, class_node)
|
194
|
-
super(constructor)
|
194
|
+
def initialize(constructor, classes, class_node)
|
195
|
+
super(constructor, classes)
|
195
196
|
@class_node = class_node
|
196
197
|
end
|
197
198
|
|
data/lib/class_map.rb
CHANGED
@@ -1,6 +1,10 @@
|
|
1
1
|
module Riml
|
2
2
|
class ClassNotFound < NameError; end
|
3
3
|
|
4
|
+
# Map of {"ClassName" => ClassDefinitionNode}
|
5
|
+
# Can also query object for superclass of a named class, etc...
|
6
|
+
#
|
7
|
+
# Ex : classes["SomeClass"].superclass_name => "SomeClassBase"
|
4
8
|
class ClassMap
|
5
9
|
def initialize
|
6
10
|
@map = {}
|
data/lib/compiler.rb
CHANGED
@@ -6,7 +6,7 @@ module Riml
|
|
6
6
|
|
7
7
|
# Base abstract visitor
|
8
8
|
class Visitor
|
9
|
-
|
9
|
+
attr_writer :propagate_up_tree
|
10
10
|
attr_reader :value
|
11
11
|
|
12
12
|
def initialize(options={})
|
@@ -136,7 +136,7 @@ module Riml
|
|
136
136
|
when Numeric
|
137
137
|
node.value
|
138
138
|
when String
|
139
|
-
StringNode === node ? string_surround(node) : node.value
|
139
|
+
StringNode === node ? string_surround(node) : node.value
|
140
140
|
when Array
|
141
141
|
node.value.each {|n| n.parent_node = node}
|
142
142
|
'[' <<
|
@@ -145,7 +145,7 @@ module Riml
|
|
145
145
|
n.compiled_output
|
146
146
|
end.join(', ') << ']'
|
147
147
|
when Hash
|
148
|
-
node.value.each {|k_n, v_n| k_n.parent_node, v_n.parent_node =
|
148
|
+
node.value.each {|k_n, v_n| k_n.parent_node, v_n.parent_node = node, node}
|
149
149
|
'{' <<
|
150
150
|
node.value.map do |k,v|
|
151
151
|
k.accept(visitor_for_node(k))
|
@@ -185,11 +185,9 @@ module Riml
|
|
185
185
|
ContinueNodeVisitor = LiteralNodeVisitor
|
186
186
|
BreakNodeVisitor = LiteralNodeVisitor
|
187
187
|
|
188
|
-
class
|
188
|
+
class ListUnpackNodeVisitor < ListNodeVisitor
|
189
189
|
def compile(node)
|
190
|
-
node.
|
191
|
-
node.string_node.accept(StringNodeVisitor.new)
|
192
|
-
node.compiled_output
|
190
|
+
node.compiled_output = super.reverse.sub(',', ';').reverse
|
193
191
|
end
|
194
192
|
end
|
195
193
|
|
@@ -223,15 +221,16 @@ module Riml
|
|
223
221
|
# myVariable = "override riml default scoping"
|
224
222
|
node.scope_modifier = "" if node.scope_modifier == "n:"
|
225
223
|
return node.scope_modifier if node.scope_modifier
|
226
|
-
node.scope_modifier =
|
224
|
+
node.scope_modifier = scope_modifier_for_node(node)
|
227
225
|
end
|
228
226
|
|
229
227
|
private
|
230
|
-
def
|
228
|
+
def scope_modifier_for_node(node)
|
231
229
|
if node.scope
|
232
230
|
return "a:" if node.scope.argument_variable_names.include?(node.name)
|
233
|
-
return ""
|
231
|
+
return "" unless node.is_a?(CallNode)
|
234
232
|
end
|
233
|
+
return "" if (node.is_a?(CallNode) || node.is_a?(DefNode)) && node.autoload?
|
235
234
|
"s:"
|
236
235
|
end
|
237
236
|
end
|
@@ -265,7 +264,9 @@ module Riml
|
|
265
264
|
|
266
265
|
private
|
267
266
|
def check_for_splat_match!(node, splat)
|
268
|
-
if
|
267
|
+
# if `function doIt(*options)`, then:
|
268
|
+
# *options OR options in function body becomes `a:000`
|
269
|
+
if [ splat, splat[1..-1] ].include?(node.name)
|
269
270
|
node.scope_modifier = "a:"
|
270
271
|
node.name = '000'
|
271
272
|
end
|
@@ -333,17 +334,17 @@ module Riml
|
|
333
334
|
end
|
334
335
|
end
|
335
336
|
|
336
|
-
class DefNodeVisitor <
|
337
|
+
class DefNodeVisitor < ScopedVisitor
|
337
338
|
def visit(node)
|
338
339
|
setup_local_scope_for_descendants(node)
|
339
340
|
super
|
340
341
|
end
|
341
342
|
|
342
343
|
def compile(node)
|
343
|
-
|
344
|
+
set_modifier(node)
|
344
345
|
bang = node.bang
|
345
346
|
params = process_parameters!(node)
|
346
|
-
declaration = "function#{bang} #{
|
347
|
+
declaration = "function#{bang} #{node.scope_modifier}"
|
347
348
|
declaration <<
|
348
349
|
if node.name.respond_to?(:variable)
|
349
350
|
node.name.accept(visitor_for_node(node.name))
|
@@ -474,7 +475,9 @@ module Riml
|
|
474
475
|
end
|
475
476
|
node.compiled_output << 'source'
|
476
477
|
compile_arguments(node)
|
477
|
-
node.compiled_output.gsub(/['"]/, '')
|
478
|
+
node.compiled_output.gsub!(/['"]/, '')
|
479
|
+
node.compiled_output.sub!('.riml', '.vim')
|
480
|
+
node.compiled_output
|
478
481
|
end
|
479
482
|
end
|
480
483
|
|
@@ -514,9 +517,9 @@ module Riml
|
|
514
517
|
end
|
515
518
|
|
516
519
|
class TryNodeVisitor < Visitor
|
517
|
-
# try_block, catch_nodes,
|
520
|
+
# try_block, catch_nodes, finally_block
|
518
521
|
def compile(node)
|
519
|
-
try, catches,
|
522
|
+
try, catches, finally = node.try_block, node.catch_nodes, node.finally_block
|
520
523
|
node.compiled_output = "try\n"
|
521
524
|
try.accept(visitor_for_node(try, :propagate_up_tree => false))
|
522
525
|
try.compiled_output.each_line do |line|
|
@@ -530,10 +533,10 @@ module Riml
|
|
530
533
|
end
|
531
534
|
end if catches
|
532
535
|
|
533
|
-
if
|
536
|
+
if finally
|
534
537
|
node.compiled_output << "finally\n"
|
535
|
-
|
536
|
-
|
538
|
+
finally.accept(visitor_for_node(finally, :propagate_up_tree => false))
|
539
|
+
finally.compiled_output.each_line do |line|
|
537
540
|
node.compiled_output << node.indent + line
|
538
541
|
end
|
539
542
|
end
|
@@ -603,6 +606,9 @@ module Riml
|
|
603
606
|
end
|
604
607
|
end
|
605
608
|
|
609
|
+
# root node has access to compiler instance in order to append to
|
610
|
+
# the compiler's `compile_queue`. This happens when another file is
|
611
|
+
# sourced using `riml_source`.
|
606
612
|
module CompilerAccessible
|
607
613
|
attr_accessor :current_compiler
|
608
614
|
end
|
@@ -613,8 +619,6 @@ module Riml
|
|
613
619
|
|
614
620
|
# compiles nodes into output code
|
615
621
|
def compile(root_node)
|
616
|
-
# so we can compile concurrently, as some nodes have access to the
|
617
|
-
# compiler instance itself
|
618
622
|
root_node.extend CompilerAccessible
|
619
623
|
root_node.current_compiler = self
|
620
624
|
root_node.accept(NodesVisitor.new)
|
data/lib/constants.rb
CHANGED
@@ -7,8 +7,7 @@ module Riml
|
|
7
7
|
VIML_END_KEYWORDS =
|
8
8
|
%w(endfunction endif endwhile endfor endtry)
|
9
9
|
RIML_KEYWORDS =
|
10
|
-
%w(def defm super end then unless until true false nil
|
11
|
-
class new)
|
10
|
+
%w(def defm super end then unless until true false nil class new)
|
12
11
|
DEFINE_KEYWORDS = %w(def def! defm defm! function function!)
|
13
12
|
KEYWORDS = VIML_KEYWORDS + VIML_END_KEYWORDS + RIML_KEYWORDS
|
14
13
|
|
data/lib/grammar.y
CHANGED
@@ -2,7 +2,7 @@ class Riml::Parser
|
|
2
2
|
|
3
3
|
token IF ELSE ELSEIF THEN UNLESS END
|
4
4
|
token WHILE UNTIL BREAK CONTINUE
|
5
|
-
token TRY CATCH
|
5
|
+
token TRY CATCH FINALLY
|
6
6
|
token FOR IN
|
7
7
|
token DEF DEF_BANG SPLAT CALL BUILTIN_COMMAND # such as echo "hi"
|
8
8
|
token CLASS NEW DEFM DEFM_BANG SUPER RIML_COMMAND
|
@@ -10,7 +10,7 @@ token RETURN
|
|
10
10
|
token NEWLINE
|
11
11
|
token NUMBER
|
12
12
|
token STRING_D STRING_S # single- and double-quoted
|
13
|
-
token
|
13
|
+
token EX_LITERAL
|
14
14
|
token REGEXP
|
15
15
|
token TRUE FALSE NIL
|
16
16
|
token LET UNLET UNLET_BANG IDENTIFIER
|
@@ -79,7 +79,6 @@ rule
|
|
79
79
|
| VariableRetrieval { result = val[0] }
|
80
80
|
| Literal { result = val[0] }
|
81
81
|
| Call { result = val[0] }
|
82
|
-
| Heredoc { result = val[0] }
|
83
82
|
| Ternary { result = val[0] }
|
84
83
|
| ObjectInstantiation { result = val[0] }
|
85
84
|
| BinaryOperator { result = val[0] }
|
@@ -114,10 +113,6 @@ rule
|
|
114
113
|
| STRING_D { result = StringNode.new(val[0], :d) }
|
115
114
|
;
|
116
115
|
|
117
|
-
Heredoc:
|
118
|
-
HEREDOC String { result = HeredocNode.new(val[0], val[1]) }
|
119
|
-
;
|
120
|
-
|
121
116
|
Regexp:
|
122
117
|
REGEXP { result = RegexpNode.new(val[0]) }
|
123
118
|
;
|
@@ -130,6 +125,10 @@ rule
|
|
130
125
|
ListLiteral { result = ListNode.new(val[0]) }
|
131
126
|
;
|
132
127
|
|
128
|
+
ListUnpack:
|
129
|
+
'[' ListItems ';' ValueExpression ']' { result = ListUnpackNode.new(val[1] << val[3]) }
|
130
|
+
;
|
131
|
+
|
133
132
|
ListLiteral:
|
134
133
|
'[' ListItems ']' { result = val[1] }
|
135
134
|
| '[' ListItems ',' ']' { result = val[1] }
|
@@ -299,6 +298,7 @@ rule
|
|
299
298
|
VariableRetrieval { result = val[0] }
|
300
299
|
| DictGet { result = val[0] }
|
301
300
|
| List { result = val[0] }
|
301
|
+
| ListUnpack { result = val[0] }
|
302
302
|
| ListOrDictGet { result = val[0] }
|
303
303
|
;
|
304
304
|
|
@@ -401,7 +401,7 @@ rule
|
|
401
401
|
Try:
|
402
402
|
TRY Block END { result = TryNode.new(val[1], nil, nil) }
|
403
403
|
| TRY Block Catch END { result = TryNode.new(val[1], val[2], nil) }
|
404
|
-
| TRY Block Catch
|
404
|
+
| TRY Block Catch FINALLY Block END { result = TryNode.new(val[1], val[2], val[4]) }
|
405
405
|
;
|
406
406
|
|
407
407
|
Catch:
|
@@ -480,7 +480,7 @@ end
|
|
480
480
|
raise Riml::ParseError, "line #{@lexer.lineno}: #{e.message}"
|
481
481
|
end
|
482
482
|
|
483
|
-
@ast_rewriter
|
483
|
+
@ast_rewriter ||= ast_rewriter
|
484
484
|
return ast unless @ast_rewriter
|
485
485
|
@ast_rewriter.ast = ast
|
486
486
|
@ast_rewriter.rewrite
|
data/lib/lexer.rb
CHANGED
@@ -7,12 +7,18 @@ module Riml
|
|
7
7
|
|
8
8
|
SINGLE_LINE_COMMENT_REGEX = /\A\s*"(.*)$/
|
9
9
|
OPERATOR_REGEX = /\A#{Regexp.union(['||', '&&', '===', '+=', '-=', '.='] + COMPARISON_OPERATORS)}/
|
10
|
+
INTERPOLATION_REGEX = /\A"(.*?)(\#\{(.*?)\})(.*?)"/m
|
11
|
+
INTERPOLATION_SPLIT_REGEX = /(\#{.*?})/m
|
10
12
|
|
11
13
|
attr_reader :tokens, :prev_token, :lineno, :chunk
|
12
14
|
|
13
15
|
def initialize(code)
|
14
16
|
@code = code
|
15
17
|
@code.chomp!
|
18
|
+
set_start_state!
|
19
|
+
end
|
20
|
+
|
21
|
+
def set_start_state!
|
16
22
|
@i = 0 # number of characters consumed
|
17
23
|
@token_buf = []
|
18
24
|
@tokens = []
|
@@ -21,15 +27,15 @@ module Riml
|
|
21
27
|
@current_indent = 0
|
22
28
|
@indent_pending = false
|
23
29
|
@dedent_pending = false
|
24
|
-
@
|
30
|
+
@one_line_conditional_end_pending = false
|
25
31
|
@splat_allowed = false
|
32
|
+
@in_function_declaration = false
|
26
33
|
end
|
27
34
|
|
28
35
|
def tokenize
|
29
|
-
|
30
|
-
while
|
31
|
-
|
32
|
-
@tokens << new_token unless new_token.nil?
|
36
|
+
set_start_state!
|
37
|
+
while (token = next_token) != nil
|
38
|
+
@tokens << token
|
33
39
|
end
|
34
40
|
@tokens
|
35
41
|
end
|
@@ -38,7 +44,7 @@ module Riml
|
|
38
44
|
while @token_buf.empty? && more_code_to_tokenize?
|
39
45
|
tokenize_chunk(get_new_chunk)
|
40
46
|
end
|
41
|
-
if
|
47
|
+
if !@token_buf.empty?
|
42
48
|
return @prev_token = @token_buf.shift
|
43
49
|
end
|
44
50
|
check_indentation
|
@@ -65,7 +71,7 @@ module Riml
|
|
65
71
|
@i += splat_var.size
|
66
72
|
@token_buf << [:SCOPE_MODIFIER, 'a:'] << [:IDENTIFIER, splat_var[2..-1]]
|
67
73
|
# the 'n' scope modifier is added by riml
|
68
|
-
elsif scope_modifier = chunk[/\A([bwtglsavn]:)[\
|
74
|
+
elsif scope_modifier = chunk[/\A([bwtglsavn]:)[\w]/]
|
69
75
|
@i += 2
|
70
76
|
@token_buf << [:SCOPE_MODIFIER, $1]
|
71
77
|
elsif scope_modifier_literal = chunk[/\A([bwtglsavn]:)/]
|
@@ -85,9 +91,6 @@ module Riml
|
|
85
91
|
old_identifier = identifier.dup
|
86
92
|
identifier.sub!(/function/, "def")
|
87
93
|
@i += (old_identifier.size - identifier.size)
|
88
|
-
elsif identifier == 'finally'
|
89
|
-
identifier = 'ensure'
|
90
|
-
@i += 1 # diff b/t the two string lengths
|
91
94
|
elsif VIML_END_KEYWORDS.include? identifier
|
92
95
|
old_identifier = identifier.dup
|
93
96
|
identifier = 'end'
|
@@ -137,17 +140,10 @@ module Riml
|
|
137
140
|
elsif decimal = chunk[/\A[0-9]+(\.[0-9]+)?/]
|
138
141
|
@token_buf << [:NUMBER, decimal.to_s]
|
139
142
|
@i += decimal.size
|
140
|
-
elsif interpolation = chunk[
|
141
|
-
# "#{
|
142
|
-
|
143
|
-
|
144
|
-
@token_buf << ['.', '.']
|
145
|
-
end
|
146
|
-
@token_buf << [:IDENTIFIER, $3]
|
147
|
-
unless $4.empty?
|
148
|
-
@token_buf << ['.', '.']
|
149
|
-
@token_buf << [ :STRING_D, " #{$4[1..-1]}" ]
|
150
|
-
end
|
143
|
+
elsif interpolation = chunk[INTERPOLATION_REGEX]
|
144
|
+
# "hey there, #{name}" = "hey there, " . name
|
145
|
+
parts = interpolation[1...-1].split(INTERPOLATION_SPLIT_REGEX)
|
146
|
+
handle_interpolation(*parts)
|
151
147
|
@i += interpolation.size
|
152
148
|
elsif single_line_comment = chunk[SINGLE_LINE_COMMENT_REGEX] && (prev_token.nil? || prev_token[0] == :NEWLINE)
|
153
149
|
comment = chunk[SINGLE_LINE_COMMENT_REGEX]
|
@@ -166,8 +162,8 @@ module Riml
|
|
166
162
|
@token_buf << [:NEWLINE, "\n"] unless prev_token && prev_token[0] == :NEWLINE
|
167
163
|
|
168
164
|
# pending indents/dedents
|
169
|
-
if @
|
170
|
-
@
|
165
|
+
if @one_line_conditional_end_pending
|
166
|
+
@one_line_conditional_end_pending = false
|
171
167
|
elsif @indent_pending
|
172
168
|
@indent_pending = false
|
173
169
|
elsif @dedent_pending
|
@@ -179,11 +175,15 @@ module Riml
|
|
179
175
|
elsif heredoc_pattern = chunk[%r{\A<<(.+?)\r?\n}]
|
180
176
|
pattern = $1
|
181
177
|
@i += heredoc_pattern.size
|
182
|
-
@token_buf << [:HEREDOC, pattern]
|
183
178
|
new_chunk = get_new_chunk
|
184
|
-
heredoc_string = new_chunk[%r|(.+?\r?\n)(#{Regexp.escape(pattern)})
|
185
|
-
@i += heredoc_string.size +
|
186
|
-
|
179
|
+
heredoc_string = new_chunk[%r|(.+?\r?\n)(#{Regexp.escape(pattern)})|, 1]
|
180
|
+
@i += heredoc_string.size + pattern.size
|
181
|
+
if ('"' + heredoc_string + '"') =~ INTERPOLATION_REGEX
|
182
|
+
parts = heredoc_string.split(INTERPOLATION_SPLIT_REGEX)
|
183
|
+
handle_interpolation(*parts)
|
184
|
+
else
|
185
|
+
@token_buf << [:STRING_D, heredoc_string]
|
186
|
+
end
|
187
187
|
@lineno += (1 + heredoc_string.each_line.to_a.size)
|
188
188
|
# operators of more than 1 char
|
189
189
|
elsif operator = chunk[OPERATOR_REGEX]
|
@@ -220,13 +220,13 @@ module Riml
|
|
220
220
|
@indent_pending = true
|
221
221
|
when :if, :unless
|
222
222
|
if one_line_conditional?(chunk)
|
223
|
-
@
|
223
|
+
@one_line_conditional_end_pending = true
|
224
224
|
elsif !statement_modifier?(chunk)
|
225
225
|
@current_indent += 2
|
226
226
|
@indent_pending = true
|
227
227
|
end
|
228
228
|
when :end
|
229
|
-
unless @
|
229
|
+
unless @one_line_conditional_end_pending
|
230
230
|
@current_indent -= 2
|
231
231
|
@dedent_pending = true
|
232
232
|
end
|
@@ -258,6 +258,22 @@ module Riml
|
|
258
258
|
chunk[/^(if|unless).+?(else)?.+?end$/]
|
259
259
|
end
|
260
260
|
|
261
|
+
def handle_interpolation(*parts)
|
262
|
+
parts.delete_if {|p| p.empty?}.each_with_index do |part, i|
|
263
|
+
if part[0..1] == '#{' && part[-1] == '}'
|
264
|
+
@token_buf.concat tokenize_without_moving_pos(part[2...-1])
|
265
|
+
else
|
266
|
+
@token_buf << [:STRING_D, part]
|
267
|
+
end
|
268
|
+
# string-concatenate all the parts unless this is the last part
|
269
|
+
@token_buf << ['.', '.'] unless parts[i + 1].nil?
|
270
|
+
end
|
271
|
+
end
|
272
|
+
|
273
|
+
def tokenize_without_moving_pos(code)
|
274
|
+
Lexer.new(code).tokenize
|
275
|
+
end
|
276
|
+
|
261
277
|
def statement_modifier?(chunk)
|
262
278
|
old_i = @i
|
263
279
|
# backtrack until the beginning of the line
|