riml 0.1.3

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/lib/grammar.y ADDED
@@ -0,0 +1,485 @@
1
+ class Riml::Parser
2
+
3
+ token IF ELSE ELSEIF THEN UNLESS END
4
+ token WHILE UNTIL BREAK CONTINUE
5
+ token TRY CATCH ENSURE
6
+ token FOR IN
7
+ token DEF DEF_BANG SPLAT CALL BUILTIN_COMMAND # such as echo "hi"
8
+ token CLASS NEW DEFM DEFM_BANG SUPER
9
+ token RETURN
10
+ token NEWLINE
11
+ token NUMBER
12
+ token STRING_D STRING_S # single- and double-quoted
13
+ token HEREDOC EX_LITERAL
14
+ token REGEXP
15
+ token TRUE FALSE NIL
16
+ token LET UNLET UNLET_BANG IDENTIFIER
17
+ token DICT_VAL # like dict.key, 'key' is a DICT_VAL
18
+ token SCOPE_MODIFIER SCOPE_MODIFIER_LITERAL SPECIAL_VAR_PREFIX
19
+ token FINISH
20
+
21
+ prechigh
22
+ right '!'
23
+ left '*' '/' '%'
24
+ left '+' '+=' '-' '-=' '.'
25
+ left '>' '>#' '>?' '<' '<#' '<?' '>=' '>=#' '>=?' '<=' '<=#' '<=?'
26
+ left '==' '==?' '==#' '=~' '=~?' '=~#' '!~' '!~?' '!~#' '!=' '!=?' '!=#'
27
+ left IS ISNOT
28
+ left '&&'
29
+ left '||'
30
+ right '?'
31
+ right '='
32
+ left ','
33
+ left IF UNLESS
34
+ preclow
35
+
36
+ # All rules
37
+ rule
38
+
39
+ Root:
40
+ /* nothing */ { result = Nodes.new([]) }
41
+ | Expressions { result = val[0] }
42
+ ;
43
+
44
+ # any list of expressions
45
+ Expressions:
46
+ Expression { result = Nodes.new([ val[0] ]) }
47
+ | Expressions Terminator Expression { result = val[0] << val[2] }
48
+ | Expressions Terminator { result = val[0] }
49
+ | Terminator { result = Nodes.new([]) }
50
+ ;
51
+
52
+ # All types of expressions in Riml
53
+ Expression:
54
+ BinaryOperator { result = val[0] }
55
+ | UnaryOperator { result = val[0] }
56
+ | Call { result = val[0] }
57
+ | Assign { result = val[0] }
58
+ | DictGet { result = val[0] }
59
+ | ListOrDictGet { result = val[0] }
60
+ | Def { result = val[0] }
61
+ | Return { result = val[0] }
62
+ | VariableRetrieval { result = val[0] }
63
+ | UnletVariable { result = val[0] }
64
+ | Literal { result = val[0] }
65
+ | ExLiteral { result = val[0] }
66
+ | Heredoc { result = val[0] }
67
+ | If { result = val[0] }
68
+ | Unless { result = val[0] }
69
+ | Ternary { result = val[0] }
70
+ | While { result = val[0] }
71
+ | Until { result = val[0] }
72
+ | For { result = val[0] }
73
+ | Try { result = val[0] }
74
+ | ClassDefinition { result = val[0] }
75
+ | ObjectInstantiation { result = val[0] }
76
+ | Super { result = val[0] }
77
+ | LoopKeyword { result = val[0] }
78
+ | EndScript { result = val[0] }
79
+ | '(' Expression ')' { result = val[1] }
80
+ ;
81
+
82
+ Terminator:
83
+ NEWLINE { result = nil }
84
+ | ';' { result = nil }
85
+ | '|' { result = nil }
86
+ ;
87
+
88
+ # All hard-coded values
89
+ Literal:
90
+ Number { result = val[0] }
91
+ | String { result = val[0] }
92
+ | Regexp { result = val[0] }
93
+ | List { result = val[0] }
94
+ | Dictionary { result = val[0] }
95
+ | ScopeModifierLiteral { result = val[0] }
96
+ | TRUE { result = TrueNode.new }
97
+ | FALSE { result = FalseNode.new }
98
+ | NIL { result = NilNode.new }
99
+ ;
100
+
101
+ Number:
102
+ NUMBER { result = NumberNode.new(val[0]) }
103
+ | '-' NUMBER { result = NumberNode.new(val[1].insert(0, val[0])) }
104
+ | '+' NUMBER { result = NumberNode.new(val[1].insert(0, val[0])) }
105
+ ;
106
+
107
+ String:
108
+ STRING_S { result = StringNode.new(val[0], :s) }
109
+ | STRING_D { result = StringNode.new(val[0], :d) }
110
+ ;
111
+
112
+ Heredoc:
113
+ HEREDOC String { result = HeredocNode.new(val[0], val[1]) }
114
+ ;
115
+
116
+ Regexp:
117
+ REGEXP { result = RegexpNode.new(val[0]) }
118
+ ;
119
+
120
+ ScopeModifierLiteral:
121
+ SCOPE_MODIFIER_LITERAL { result = ScopeModifierLiteralNode.new(val[0]) }
122
+ ;
123
+
124
+ List:
125
+ ListLiteral { result = ListNode.new(val[0]) }
126
+ ;
127
+
128
+ ListLiteral:
129
+ '[' ListItems ']' { result = val[1] }
130
+ | '[' ListItems ',' ']' { result = val[1] }
131
+ ;
132
+
133
+ ListItems:
134
+ /* nothing */ { result = [] }
135
+ | Expression { result = [val[0]] }
136
+ | ListItems ',' Expression { result = val[0] << val[2] }
137
+ ;
138
+
139
+ Dictionary:
140
+ DictionaryLiteral { result = DictionaryNode.new(val[0]) }
141
+ ;
142
+
143
+ # {'key': 'value', 'key': 'value'}
144
+ DictionaryLiteral:
145
+ '{' DictItems '}' { result = Hash[val[1]] }
146
+ | '{' DictItems ',' '}' { result = Hash[val[1]] }
147
+ ;
148
+
149
+ # [[key, value], [key, value]]
150
+ DictItems:
151
+ /* nothing */ { result = [] }
152
+ | DictItem { result = val }
153
+ | DictItems ',' DictItem { result = val[0] << val[2] }
154
+ ;
155
+
156
+ # [key, value]
157
+ DictItem:
158
+ Literal ':' Literal { result = [val[0], val[2]] }
159
+ ;
160
+
161
+ DictGet:
162
+ Dictionary ListOrDictGetWithBrackets { result = DictGetBracketNode.new(val[0], val[1]) }
163
+ | Dictionary DictGetWithDotLiteral { result = DictGetDotNode.new(val[0], val[1]) }
164
+ | VariableRetrieval DictGetWithDot { result = DictGetDotNode.new(val[0], val[1]) }
165
+ | ListOrDictGet DictGetWithDot { result = DictGetDotNode.new(val[0], val[1]) }
166
+ ;
167
+
168
+ ListOrDictGet:
169
+ VariableRetrieval ListOrDictGetWithBrackets { result = ListOrDictGetNode.new(val[0], val[1]) }
170
+ | DictGet ListOrDictGetWithBrackets { result = ListOrDictGetNode.new(val[0], val[1]) }
171
+ | Call ListOrDictGetWithBrackets { result = ListOrDictGetNode.new(val[0], val[1]) }
172
+ ;
173
+
174
+ ListOrDictGetWithBrackets:
175
+ '[' Expression ']' { result = [val[1]] }
176
+ | '[' SubList ']' { result = [val[1]] }
177
+ | ListOrDictGetWithBrackets '[' Expression ']' { result = val[0] << val[2] }
178
+ | ListOrDictGetWithBrackets '[' SubList ']' { result = val[0] << val[2] }
179
+ ;
180
+
181
+ SubList:
182
+ Expression ':' Expression { result = Nodes.new([val[0], LiteralNode.new(' : '), val[2]]) }
183
+ | Expression ':' { result = Nodes.new([val[0], LiteralNode.new(' :')]) }
184
+ | ':' Expression { result = Nodes.new([LiteralNode.new(': '), val[1]]) }
185
+ | ':' { result = Nodes.new([LiteralNode.new(':')]) }
186
+ ;
187
+
188
+ DictGetWithDot:
189
+ DICT_VAL { result = [val[0]]}
190
+ | DictGetWithDot DICT_VAL { result = val[0] << val[1] }
191
+ ;
192
+
193
+ DictGetWithDotLiteral:
194
+ '.' IDENTIFIER { result = [val[1]] }
195
+ | DictGetWithDotLiteral DICT_VAL { result = val[0] << val[1] }
196
+ ;
197
+
198
+ Call:
199
+ Scope DefCallIdentifier '(' ArgList ')' { result = CallNode.new(val[0], val[1], val[3]) }
200
+ | DictGet '(' ArgList ')' { result = CallNode.new(nil, val[0], val[2]) }
201
+ | CALL Scope DefCallIdentifier '(' ArgList ')' { result = ExplicitCallNode.new(val[1], val[2], val[4]) }
202
+ | CALL DictGet '(' ArgList ')' { result = ExplicitCallNode.new(nil, val[1], val[3]) }
203
+ | BUILTIN_COMMAND '(' ArgList ')' { result = CallNode.new(nil, val[0], val[2]) }
204
+ | BUILTIN_COMMAND ArgList { result = CallNode.new(nil, val[0], val[1]) }
205
+ ;
206
+
207
+ Scope:
208
+ SCOPE_MODIFIER { result = val[0] }
209
+ | /* nothing */ { result = nil }
210
+ ;
211
+
212
+ ArgList:
213
+ /* nothing */ { result = [] }
214
+ | Expression { result = val }
215
+ | ArgList "," Expression { result = val[0] << val[2] }
216
+ ;
217
+
218
+ BinaryOperator:
219
+ Expression '||' Expression { result = BinaryOperatorNode.new(val[1], [val[0], val[2]]) }
220
+ | Expression '&&' Expression { result = BinaryOperatorNode.new(val[1], [val[0], val[2]]) }
221
+
222
+ | Expression '==' Expression { result = BinaryOperatorNode.new(val[1], [val[0], val[2]]) }
223
+ | Expression '==#' Expression { result = BinaryOperatorNode.new(val[1], [val[0], val[2]]) }
224
+ | Expression '==?' Expression { result = BinaryOperatorNode.new(val[1], [val[0], val[2]]) }
225
+
226
+ # added by riml
227
+ | Expression '===' Expression { result = BinaryOperatorNode.new(val[1], [val[0], val[2]]) }
228
+
229
+ | Expression '!=' Expression { result = BinaryOperatorNode.new(val[1], [val[0], val[2]]) }
230
+ | Expression '!=#' Expression { result = BinaryOperatorNode.new(val[1], [val[0], val[2]]) }
231
+ | Expression '!=?' Expression { result = BinaryOperatorNode.new(val[1], [val[0], val[2]]) }
232
+
233
+ | Expression '=~' Expression { result = BinaryOperatorNode.new(val[1], [val[0], val[2]]) }
234
+ | Expression '=~#' Expression { result = BinaryOperatorNode.new(val[1], [val[0], val[2]]) }
235
+ | Expression '=~?' Expression { result = BinaryOperatorNode.new(val[1], [val[0], val[2]]) }
236
+
237
+ | Expression '!~' Expression { result = BinaryOperatorNode.new(val[1], [val[0], val[2]]) }
238
+ | Expression '!~#' Expression { result = BinaryOperatorNode.new(val[1], [val[0], val[2]]) }
239
+ | Expression '!~?' Expression { result = BinaryOperatorNode.new(val[1], [val[0], val[2]]) }
240
+
241
+ | Expression '>' Expression { result = BinaryOperatorNode.new(val[1], [val[0], val[2]]) }
242
+ | Expression '>#' Expression { result = BinaryOperatorNode.new(val[1], [val[0], val[2]]) }
243
+ | Expression '>?' Expression { result = BinaryOperatorNode.new(val[1], [val[0], val[2]]) }
244
+
245
+ | Expression '>=' Expression { result = BinaryOperatorNode.new(val[1], [val[0], val[2]]) }
246
+ | Expression '>=#' Expression { result = BinaryOperatorNode.new(val[1], [val[0], val[2]]) }
247
+ | Expression '>=?' Expression { result = BinaryOperatorNode.new(val[1], [val[0], val[2]]) }
248
+
249
+ | Expression '<' Expression { result = BinaryOperatorNode.new(val[1], [val[0], val[2]]) }
250
+ | Expression '<#' Expression { result = BinaryOperatorNode.new(val[1], [val[0], val[2]]) }
251
+ | Expression '<?' Expression { result = BinaryOperatorNode.new(val[1], [val[0], val[2]]) }
252
+
253
+ | Expression '<=' Expression { result = BinaryOperatorNode.new(val[1], [val[0], val[2]]) }
254
+ | Expression '<=#' Expression { result = BinaryOperatorNode.new(val[1], [val[0], val[2]]) }
255
+ | Expression '<=?' Expression { result = BinaryOperatorNode.new(val[1], [val[0], val[2]]) }
256
+
257
+ | Expression '+' Expression { result = BinaryOperatorNode.new(val[1], [val[0], val[2]]) }
258
+ | Expression '-' Expression { result = BinaryOperatorNode.new(val[1], [val[0], val[2]]) }
259
+ | Expression '*' Expression { result = BinaryOperatorNode.new(val[1], [val[0], val[2]]) }
260
+ | Expression '/' Expression { result = BinaryOperatorNode.new(val[1], [val[0], val[2]]) }
261
+ | Expression '.' Expression { result = BinaryOperatorNode.new(val[1], [val[0], val[2]]) }
262
+
263
+ | Expression IS Expression { result = BinaryOperatorNode.new(val[1], [val[0], val[2]]) }
264
+ | Expression ISNOT Expression { result = BinaryOperatorNode.new(val[1], [val[0], val[2]]) }
265
+ ;
266
+
267
+ UnaryOperator:
268
+ '!' Expression { result = UnaryOperatorNode.new(val[0], val[1]) }
269
+ ;
270
+
271
+ # ['=', LHS, RHS]
272
+ Assign:
273
+ LET AssignExpression { result = AssignNode.new(val[1][0], val[1][1], val[1][2]) }
274
+ | AssignExpression { result = AssignNode.new(val[0][0], val[0][1], val[0][2]) }
275
+ ;
276
+
277
+ # ['=', AssignLHS, Expression]
278
+ AssignExpression:
279
+ AssignLHS '=' Expression { result = [val[1], val[0], val[2]] }
280
+ | AssignLHS '+=' Expression { result = [val[1], val[0], val[2]] }
281
+ | AssignLHS '-=' Expression { result = [val[1], val[0], val[2]] }
282
+ | AssignLHS '.=' Expression { result = [val[1], val[0], val[2]] }
283
+ ;
284
+
285
+ AssignLHS:
286
+ VariableRetrieval { result = val[0] }
287
+ | DictGet { result = val[0] }
288
+ | List { result = val[0] }
289
+ | ListOrDictGet { result = val[0] }
290
+ ;
291
+
292
+ # retrieving the value of a variable
293
+ VariableRetrieval:
294
+ Scope IDENTIFIER { result = GetVariableNode.new(val[0], val[1]) }
295
+ | SPECIAL_VAR_PREFIX IDENTIFIER { result = GetSpecialVariableNode.new(val[0], val[1]) }
296
+ | Scope CurlyBraceName { result = GetCurlyBraceNameNode.new(val[0], val[1])}
297
+ ;
298
+
299
+ UnletVariable:
300
+ UNLET VariableRetrieval { result = UnletVariableNode.new('!', [ val[1] ]) }
301
+ | UNLET_BANG VariableRetrieval { result = UnletVariableNode.new('!', [ val[1] ]) }
302
+ | UnletVariable VariableRetrieval { result = val[0] << val[1] }
303
+ ;
304
+
305
+ CurlyBraceName:
306
+ IDENTIFIER '{' VariableRetrieval '}' { result = CurlyBraceVariable.new([ CurlyBracePart.new(val[0]), CurlyBracePart.new(val[2]) ]) }
307
+ | '{' VariableRetrieval '}' IDENTIFIER { result = CurlyBraceVariable.new([ CurlyBracePart.new(val[1]), CurlyBracePart.new(val[3]) ]) }
308
+ | CurlyBraceName IDENTIFIER { result = val[0] << CurlyBracePart.new(val[1]) }
309
+ ;
310
+
311
+ # Method definition
312
+ # [scope_modifier, name, parameters, keyword, expressions]
313
+ Def:
314
+ FunctionType Scope DefCallIdentifier Keyword Block END { result = Object.const_get(val[0]).new('!', val[1], val[2], [], val[3], val[4]) }
315
+ | FunctionType Scope DefCallIdentifier '(' ParamList ')' Keyword Block END { result = Object.const_get(val[0]).new('!', val[1], val[2], val[4], val[6], val[7]) }
316
+ | FunctionType Scope DefCallIdentifier '(' SPLAT ')' Keyword Block END { result = Object.const_get(val[0]).new('!', val[1], val[2], [val[4]], val[6], val[7]) }
317
+ | FunctionType Scope DefCallIdentifier '(' ParamList ',' SPLAT ')' Keyword Block END { result = Object.const_get(val[0]).new('!', val[1], val[2], val[4] << val[6], val[8], val[9]) }
318
+ ;
319
+
320
+ FunctionType:
321
+ DEF { result = "DefNode" }
322
+ | DEF_BANG { result = "DefNode" }
323
+ | DEFM { result = "DefMethodNode" }
324
+ ;
325
+
326
+ DefCallIdentifier:
327
+ # use '' for first argument instead of nil in order to avoid a double scope-modifier
328
+ CurlyBraceName { result = GetCurlyBraceNameNode.new('', val[0])}
329
+ | IDENTIFIER { result = val[0] }
330
+ ;
331
+
332
+ # Example: 'range' or 'dict' after function definition
333
+ Keyword:
334
+ IDENTIFIER { result = val[0] }
335
+ | /* nothing */ { result = nil }
336
+ ;
337
+
338
+ ParamList:
339
+ /* nothing */ { result = [] }
340
+ | IDENTIFIER { result = val }
341
+ | ParamList ',' IDENTIFIER { result = val[0] << val[2] }
342
+ ;
343
+
344
+ Return:
345
+ RETURN Expression { result = ReturnNode.new(val[1]) }
346
+ | RETURN Terminator { result = ReturnNode.new(nil) }
347
+ ;
348
+
349
+ EndScript:
350
+ FINISH { result = FinishNode.new }
351
+ ;
352
+
353
+ # [expression, expressions]
354
+ If:
355
+ IF Expression IfBlock END { result = IfNode.new(val[1], val[2]) }
356
+ | IF Expression THEN Expression END { result = IfNode.new(val[1], Nodes.new([val[3]])) }
357
+ | Expression IF Expression { result = IfNode.new(val[2], Nodes.new([val[0]])) }
358
+ ;
359
+
360
+ Unless:
361
+ UNLESS Expression IfBlock END { result = UnlessNode.new(val[1], val[2]) }
362
+ | UNLESS Expression THEN Expression END { result = UnlessNode.new(val[1], Nodes.new([val[3]])) }
363
+ | Expression UNLESS Expression { result = UnlessNode.new(val[2], Nodes.new([val[0]])) }
364
+ ;
365
+
366
+ Ternary:
367
+ Expression '?' Expression ':' Expression { result = TernaryOperatorNode.new([val[0], val[2], val[4]]) }
368
+ ;
369
+
370
+ While:
371
+ WHILE Expression Block END { result = WhileNode.new(val[1], val[2]) }
372
+ ;
373
+
374
+ LoopKeyword:
375
+ BREAK { result = BreakNode.new }
376
+ | CONTINUE { result = ContinueNode.new }
377
+ ;
378
+
379
+ Until:
380
+ UNTIL Expression Block END { result = UntilNode.new(val[1], val[2]) }
381
+ ;
382
+
383
+ For:
384
+ FOR IDENTIFIER IN Expression Block END { result = ForNode.new(val[1], val[3], val[4]) }
385
+ | FOR List IN Expression Block END { result = ForNode.new(val[1], val[3], val[4]) }
386
+ ;
387
+
388
+ Try:
389
+ TRY Block END { result = TryNode.new(val[1], nil, nil) }
390
+ | TRY Block Catch END { result = TryNode.new(val[1], val[2], nil) }
391
+ | TRY Block Catch ENSURE Block END { result = TryNode.new(val[1], val[2], val[4]) }
392
+ ;
393
+
394
+ Catch:
395
+ /* nothing */ { result = nil }
396
+ | CATCH Block { result = [ CatchNode.new(nil, val[1]) ] }
397
+ | CATCH Regexp Block { result = [ CatchNode.new(val[1], val[2]) ] }
398
+ | Catch CATCH Block { result = val[0] << CatchNode.new(nil, val[2]) }
399
+ | Catch CATCH Regexp Block { result = val[0] << CatchNode.new(val[2], val[3]) }
400
+ ;
401
+
402
+ # [expressions]
403
+ # expressions list could contain an ElseNode, which contains expressions
404
+ # itself
405
+ Block:
406
+ NEWLINE Expressions { result = val[1] }
407
+ | NEWLINE { result = Nodes.new([]) }
408
+ ;
409
+
410
+ IfBlock:
411
+ Block { result = val[0] }
412
+ | NEWLINE Expressions ElseBlock { result = val[1].concat(val[2]) }
413
+ ;
414
+
415
+ ElseBlock:
416
+ ElseifBlock ELSE NEWLINE Expressions { result = [val[0], ElseNode.new(val[3])].compact }
417
+ ;
418
+
419
+ ElseifBlock:
420
+ ELSEIF Expression NEWLINE Expressions { result = Nodes.new([ElseifNode.new(val[1], val[3])]) }
421
+ | ElseifBlock ELSEIF Expression NEWLINE Expressions { result = val[0] << ElseifNode.new(val[2], val[4]) }
422
+ | /* nothing */ { result = nil }
423
+ ;
424
+
425
+ ClassDefinition:
426
+ CLASS IDENTIFIER Block END { result = ClassDefinitionNode.new(val[1], nil, val[2]) }
427
+ | CLASS IDENTIFIER '<' IDENTIFIER Block END { result = ClassDefinitionNode.new(val[1], val[3], val[4]) }
428
+ ;
429
+
430
+ ObjectInstantiation:
431
+ NEW Call { result = ObjectInstantiationNode.new(val[1]) }
432
+ ;
433
+
434
+ Super:
435
+ SUPER '(' ArgList ')' { result = SuperNode.new(val[2], true) }
436
+ | SUPER { result = SuperNode.new([], false) }
437
+ ;
438
+
439
+ ExLiteral:
440
+ EX_LITERAL { result = ExLiteralNode.new(val[0])}
441
+ ;
442
+ end
443
+
444
+ ---- header
445
+ require File.expand_path("../lexer", __FILE__)
446
+ require File.expand_path("../nodes", __FILE__)
447
+ require File.expand_path("../ast_rewriter", __FILE__)
448
+ require File.expand_path("../errors", __FILE__)
449
+ ---- inner
450
+ # This code will be put as-is in the parser class
451
+
452
+ # parses tokens or code into output nodes
453
+ def parse(object, rewrite_ast = true)
454
+ if tokens?(object)
455
+ @tokens = object
456
+ elsif code?(object)
457
+ @lexer = Riml::Lexer.new(object)
458
+ end
459
+ ast = do_parse
460
+ return ast if rewrite_ast == false
461
+ AST_Rewriter.new(ast).rewrite
462
+ end
463
+
464
+ alias do_parse_without_error_handling do_parse
465
+ def do_parse_with_error_handling
466
+ do_parse_without_error_handling
467
+ rescue Racc::ParseError => e
468
+ raise unless @lexer
469
+ raise Riml::ParseError, "line #{@lexer.lineno}: #{e.message}"
470
+ end
471
+ alias do_parse do_parse_with_error_handling
472
+
473
+ def next_token
474
+ return @tokens.shift unless @lexer
475
+ @lexer.next_token
476
+ end
477
+
478
+ private
479
+ def tokens?(object)
480
+ Array === object
481
+ end
482
+
483
+ def code?(object)
484
+ String === object
485
+ end
data/lib/helper.rb ADDED
@@ -0,0 +1,45 @@
1
+ require File.expand_path('../../config/environment', __FILE__)
2
+ require 'nodes'
3
+ require 'lexer'
4
+ require 'parser'
5
+ require 'compiler'
6
+
7
+ module Riml
8
+ # lex code into tokens
9
+ def self.lex(code)
10
+ Lexer.new(code).tokenize
11
+ end
12
+
13
+ # parse code (or tokens) into nodes
14
+ def self.parse(input, rewrite_ast = true)
15
+ unless input.is_a?(Array) || input.is_a?(String)
16
+ raise ArgumentError, "input must be tokens or code, is #{input.class}"
17
+ end
18
+ Parser.new.parse(input, rewrite_ast)
19
+ end
20
+
21
+ # compile nodes (or tokens or code) into output code
22
+ def self.compile(input)
23
+ if input.is_a?(Nodes)
24
+ nodes = input
25
+ elsif input.is_a?(String) || input.is_a?(Array)
26
+ nodes = parse(input)
27
+ else
28
+ raise ArgumentError, "input must be nodes, tokens or code, is #{input.class}"
29
+ end
30
+ Compiler.new.compile(nodes)
31
+ end
32
+
33
+ # expects `file_name` to be readable file
34
+ def self.compile_file(file_name)
35
+ input = File.read(file_name)
36
+ output = compile(input)
37
+ file_basename = File.basename(file_name)
38
+ unless File.extname(file_basename).empty?
39
+ file_basename = file_basename.split(".").tap {|parts| parts.pop}.join(".")
40
+ end
41
+ File.open("#{file_basename}.vim", 'w') do |f|
42
+ f.write output
43
+ end
44
+ end
45
+ end