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 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