riml 0.1.5 → 0.1.6

Sign up to get free protection for your applications and to get access to all the features.
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 FILE", "Compile riml file to VimL.") do |file|
26
- if File.exists?(file)
27
- options.compile_files << file
28
- else
29
- warn "Couldn't find file #{file.inspect}."
30
- exit 1
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.gets)
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
- def initialize(ast = nil)
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
- StrictEqualsComparisonOperator.new(ast).rewrite_on_match
25
- VarEqualsComparisonOperator.new(ast).rewrite_on_match
26
- ClassDefinitionToFunctions.new(ast).rewrite_on_match
27
- ObjectInstantiationToCall.new(ast).rewrite_on_match
28
- CallToExplicitCall.new(ast).rewrite_on_match
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
- attr_accessor :propagate_up_tree
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.dup
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 = [node, 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 HeredocNodeVisitor < Visitor
188
+ class ListUnpackNodeVisitor < ListNodeVisitor
189
189
  def compile(node)
190
- node.string_node.parent_node = 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 = scope_modifier_for_variable_name(node)
224
+ node.scope_modifier = scope_modifier_for_node(node)
227
225
  end
228
226
 
229
227
  private
230
- def scope_modifier_for_variable_name(node)
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 node.name == splat[1..-1]
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 < Visitor
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
- modifier = node.scope ? nil : node.scope_modifier || 's:'
344
+ set_modifier(node)
344
345
  bang = node.bang
345
346
  params = process_parameters!(node)
346
- declaration = "function#{bang} #{modifier}"
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(/['"]/, '').gsub('.riml', '.vim')
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, ensure_block
520
+ # try_block, catch_nodes, finally_block
518
521
  def compile(node)
519
- try, catches, _ensure = node.try_block, node.catch_nodes, node.ensure_block
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 _ensure
536
+ if finally
534
537
  node.compiled_output << "finally\n"
535
- _ensure.accept(visitor_for_node(_ensure, :propagate_up_tree => false))
536
- _ensure.compiled_output.each_line do |line|
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 ensure
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 ENSURE
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 HEREDOC EX_LITERAL
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 ENSURE Block END { result = TryNode.new(val[1], val[2], val[4]) }
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 = 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
- @one_line_conditional_END_pending = false
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
- @i, @current_indent = 0, 0
30
- while more_code_to_tokenize?
31
- new_token = next_token
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 @token_buf.any?
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]:)[\w_]/]
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[/\A"(.*?)(\#\{(.*?)\})(.*?)"/]
141
- # "#{hey} guys" = "hey" . " guys"
142
- unless $1.empty?
143
- @token_buf << [:STRING_D, $1]
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 @one_line_conditional_END_pending
170
- @one_line_conditional_END_pending = false
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 + $2.size
186
- @token_buf << [:STRING_D, $1]
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
- @one_line_conditional_END_pending = true
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 @one_line_conditional_END_pending
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