mochiscript 0.4.0.pre10 → 0.4.0.pre12

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,1091 @@
1
+ require 'v8'
2
+ require 'json'
3
+
4
+ module Mochiscript
5
+ VERSION = "0.4.0-pre12".sub("-", '.')
6
+ class Context
7
+ def initialize
8
+ @ctx = V8::Context.new
9
+ @ctx['_$m_adapter'] = Adapter.new
10
+ @ctx.eval(Parser::JAVASCRIPT)
11
+ end
12
+
13
+ def parse(str)
14
+ @ctx.eval_js("$m.parse(#{str.to_json})")
15
+ end
16
+
17
+ def to_json(str)
18
+ @ctx.eval_js("$m.toJSON(#{str.to_json})")
19
+ end
20
+
21
+ def eval_ms(str)
22
+ @ctx.eval_js(parse(str))
23
+ end
24
+
25
+ protected
26
+
27
+ def method_missing(name, *args, &block)
28
+ @ctx.send(name, *args, &block)
29
+ end
30
+ end
31
+
32
+ class Adapter
33
+ def out(arg)
34
+ print arg
35
+ end
36
+
37
+ def outs(arg)
38
+ puts arg
39
+ end
40
+ end
41
+
42
+ class Parser
43
+ JAVASCRIPT = <<'FINISH'
44
+ var $m = { ROOT: this, ADAPTER: _$m_adapter, PLATFORM: 'ruby' };
45
+ var JS2 = $m;
46
+ (function () {
47
+ // CLASS HELPERS
48
+ (function (undefined, $m) {
49
+
50
+ var OO = function (klass, par) {
51
+ this.klass = klass;
52
+ this.par = par;
53
+
54
+ this.members = {};
55
+ this.staticMembers = {};
56
+ this.children = [];
57
+ this.included = [];
58
+
59
+ if (this.par) this.par.OO.children.push(klass);
60
+ };
61
+
62
+ OO.prototype = {
63
+ forbiddenMembers: {
64
+ 'prototype': undefined,
65
+ 'OO': undefined
66
+ },
67
+
68
+ include: function(module) {
69
+ this.included.push(module);
70
+ var members = module.OO.members;
71
+ for (var name in members) {
72
+ if (members.hasOwnProperty(name)) {
73
+ this.addMember(name, members[name]);
74
+ }
75
+ }
76
+
77
+ var staticMembers = module.OO.staticMembers;
78
+ for (var name in staticMembers) {
79
+ if (staticMembers.hasOwnProperty(name)) {
80
+ this.addStaticMember(name, staticMembers[name]);
81
+ }
82
+ }
83
+
84
+ if (typeof staticMembers['included'] == 'function') {
85
+ staticMembers['included'](this.klass);
86
+ }
87
+ },
88
+
89
+ createNamespace: function(name) {
90
+ var splitted = name.split('.');
91
+ var klassName = splitted.pop();
92
+ var root = $m.ROOT;
93
+
94
+ while (splitted.length > 0) {
95
+ var name = splitted.shift();
96
+ if (!root[name]) root[name] = $m.Class.extend({});
97
+ root = root[name];
98
+ }
99
+
100
+ return [ root, klassName ];
101
+ },
102
+
103
+ makeSuper: function(newMethod, oldMethod) {
104
+ if (!oldMethod) return newMethod;
105
+
106
+ return function() {
107
+ this.$super = oldMethod;
108
+ return newMethod.apply(this, arguments);
109
+ };
110
+ },
111
+
112
+ addMember: function(name, member) {
113
+ if (this.forbiddenMembers.hasOwnProperty(name)) return;
114
+
115
+ var proto = this.klass.prototype;
116
+ if (typeof proto[name] == 'function' && !(proto[name] instanceof RegExp)) {
117
+ member = this.makeSuper(member, proto[name]);
118
+ }
119
+
120
+ proto[name] = member;
121
+ this.members[name] = member;
122
+ },
123
+
124
+ addStaticMember: function(name, member) {
125
+ if (this.forbiddenMembers.hasOwnProperty(name)) return;
126
+
127
+ if (typeof this.klass[name] == 'function') {
128
+ if (!this.klass.hasOwnProperty(name)) {
129
+ member = this.makeSuper(member, this.klass[name]);
130
+ }
131
+ }
132
+
133
+ this.klass[name] = member;
134
+ this.staticMembers[name] = member;
135
+ }
136
+ };
137
+
138
+ $m.Class = function() { this.initialize.apply(this, arguments); };
139
+ $m.Class.OO = new OO($m.Class);
140
+ $m.Class.prototype = {
141
+ initialize: function () {},
142
+ oo: $m.Class.OO
143
+ };
144
+
145
+ var namedClasses = {};
146
+ $m.getClass = function(name) {
147
+ return namedClasses[name];
148
+ };
149
+
150
+ var noInit = false;
151
+ $m.Class.extend = function(name, klassDef) {
152
+ var klass = function() { if (!noInit) this.initialize.apply(this, arguments); };
153
+ klass.OO = new OO(klass, this);
154
+
155
+ if (typeof name != 'string') {
156
+ klassDef = name;
157
+ } else {
158
+ namedClasses[name] = klass;
159
+ var namespace = this.OO.createNamespace(name);
160
+ namespace[0][namespace[1]] = klass;
161
+ }
162
+
163
+ // create instance of this as prototype for new this
164
+ noInit = true;
165
+ var proto = new this();
166
+ noInit = false;
167
+
168
+ klass.prototype = proto;
169
+ var oo = klass.OO;
170
+ proto.OO = oo;
171
+
172
+ for (var name in this) {
173
+ oo.addStaticMember(name, this[name]);
174
+ }
175
+
176
+ if (typeof klassDef == 'function') {
177
+ klassDef(klass, oo);
178
+ } else {
179
+ for (var name in klassDef) {
180
+ oo.addMember(name, klassDef[name]);
181
+ }
182
+ }
183
+
184
+ return klass;
185
+ };
186
+
187
+ $m.Module = $m.Class;
188
+
189
+ var assert = {
190
+ 'eq': function(expected, actual) { if (expected != actual) $m.outs("Expected "+expected+", but got "+actual+".") },
191
+ 'isFalse': function(val) { if (val) $m.outs("Expected false, but got "+JSON.stringify(val)+".") },
192
+ 'isTrue': function(val) { if (!val) $m.outs("Expected true, but got " +val+".") }
193
+ };
194
+
195
+ $m.test = function(message, callback) {
196
+ if (!callback) callback = message;
197
+ callback(assert);
198
+ };
199
+
200
+ function addListener(type, listener) {
201
+ var events = this.__$events || (this.__$events = {});
202
+ this.emit('newListener', type, listener);
203
+ if (!events[type]) events[type] = [];
204
+ events[type].push(listener);
205
+ }
206
+
207
+ $m.EventEmitter = $m.Module.extend({
208
+ emit: function () {
209
+ // TODO optimize
210
+ var type = arguments[0];
211
+ var events = this.__$events || (this.__$events = {});
212
+ var handlers = events[type];
213
+
214
+ if (!handlers) return false;
215
+
216
+ var args = [];
217
+ for (var i=1,len=arguments.length; i<len; i++) args[i-1] = arguments[i];
218
+ for (var i=0,len=handlers.length; i<len; i++) handlers[i].apply(this, args);
219
+ },
220
+
221
+ addListener: addListener,
222
+ on: addListener
223
+ });
224
+
225
+ $m.out = function () {
226
+ for(var i=0,_c1=arguments,_l1=_c1.length,arg;(arg=_c1[i])||(i<_l1);i++){
227
+ $m.ADAPTER.out(arg);
228
+ if (i < arguments.length-1) {
229
+ $m.ADAPTER.out(',');
230
+ }
231
+ }
232
+ };
233
+
234
+ $m.outs = function () {
235
+ for(var _i1=0,_c1=arguments,_l1=_c1.length,arg;(arg=_c1[_i1])||(_i1<_l1);_i1++){
236
+ $m.ADAPTER.outs(arg);
237
+ }
238
+ };
239
+
240
+
241
+ return $m;
242
+ })(undefined, $m);
243
+
244
+
245
+ var IDENT = "[\\$\\w]+";
246
+ var TOKENS = [
247
+ [ "SPACE", "\\s+" ],
248
+
249
+ [ "STATIC", "static\\b" ],
250
+ [ "MODULE", "module\\b", 'ModuleParser' ],
251
+ [ "CLASS", "class\\b", 'ClassParser' ],
252
+ [ "FUNCTION", "function\\b" ],
253
+ [ "INCLUDE", "include\\b" ],
254
+ [ "VAR", "var\\b" ],
255
+ [ "PRIVATE", "private\\b" ],
256
+ [ "EXTENDS", "extends\\b" ],
257
+ [ "FOREACH", "foreach\\b", 'ForeachParser' ],
258
+
259
+ [ "SHORTHAND_FUNCTION", "#(?:{|\\()", 'ShorthandFunctionParser' ],
260
+ [ "ISTRING_START", "%{", 'IStringParser' ],
261
+ [ "HEREDOC", "<<[A-Z][0-9A-Z]*", 'HereDocParser' ],
262
+
263
+ [ "DSTRING", "\"(?:\\\\.|[^\"])*\"" ],
264
+ [ "SSTRING", "\'(?:\\\\.|[^\'])*\'" ],
265
+
266
+ [ "SEMICOLON", ";" ],
267
+ [ "OPERATOR", "\\+|\\-|\\++" ],
268
+ [ "EQUALS", "=" ],
269
+
270
+ [ "COMMENT", "\\/\\/|\\/\\*", "CommentParser" ],
271
+ [ "REGEX", "/", 'RegexParser' ],
272
+
273
+ [ "LBRACE", "\\(" ],
274
+ [ "RBRACE", "\\)" ],
275
+ [ "LCURLY", "\\{" ],
276
+ [ "RCURLY", "\\}" ],
277
+
278
+ [ "IDENT", IDENT ],
279
+ [ "WHATEVER", "." ]
280
+ ];
281
+
282
+ var $c = $m.ROOT;
283
+ var TYPES = {};
284
+ var REGEXES = [];
285
+ var MAIN_REGEX = null;
286
+ var RTYPES = {};
287
+
288
+ for(var i=0,_c1=TOKENS,_l1=_c1.length,t;(t=_c1[i])||(i<_l1);i++){
289
+ TYPES[t[0]] = i;
290
+ RTYPES[i] = t[0];
291
+ REGEXES.push("(" + t[1] + ")");
292
+ }
293
+
294
+ var EXTRA_REGEX_STRINGS = {
295
+ ARGS: "\\(\s*(?:" + IDENT + ")?(?:\\s*,\\s*" + IDENT + ")*\s*\\)",
296
+ CLASSNAME: "[\\$\\w\\.]+"
297
+ };
298
+
299
+ var MAIN_REGEX = new RegExp("^" + REGEXES.join('|'));
300
+
301
+ JS2.Class.extend('Tokens', function(KLASS, OO){
302
+ OO.addMember("initialize",function (str) {
303
+ this.orig = str;
304
+ this.str = str;
305
+ this.iterator = 0;
306
+ this.consumed = 0;
307
+ });
308
+
309
+ OO.addMember("peek",function () {
310
+ if (this._peek) return this._peek;
311
+
312
+ var m = this.str.match(MAIN_REGEX);
313
+ if (!m) return null;
314
+
315
+ for(var i=0,_c1=TOKENS,_l1=_c1.length,ele;(ele=_c1[i])||(i<_l1);i++){
316
+ if (m[i+1]) return this._peek = [ i, m[i+1], ele[2] ];
317
+ }
318
+ });
319
+
320
+ OO.addStaticMember("regex",function (str) {
321
+ var regexStr = str.replace(/\*\*/g, "\\s*").replace(/\s+/g, "\\s+").replace(/\>\</g, ">\\s*<").replace(/\<(\w+)\>/g, function($1,$2,$3){
322
+ return "(" + (EXTRA_REGEX_STRINGS[$2] || TOKENS[TYPES[$2]][1]) + ")";
323
+ });
324
+
325
+ return new RegExp("^" + regexStr);
326
+ });
327
+
328
+ OO.addMember("consume",function (n) {
329
+ this.str = this.str.substr(n, this.str.length-n);
330
+ this._peek = null;
331
+ this.consumed += n;
332
+ });
333
+
334
+ OO.addMember("length",function () {
335
+ return this.str.length;
336
+ });
337
+
338
+ OO.addMember("lookback",function (n) {
339
+ var starting = this.consumed;
340
+ while (this.orig.charAt(starting).match(/\s/)) starting--;
341
+ return this.orig.substr(starting-n, n);
342
+ });
343
+
344
+ OO.addMember("lookahead",function (n) {
345
+ var starting = this.consumed;
346
+ while (this.orig.charAt(starting).match(/\s/)) starting++;
347
+ return this.orig.substr(starting, n);
348
+ });
349
+
350
+
351
+ OO.addMember("any",function () {
352
+ return this.str.length > 0;
353
+ });
354
+
355
+ OO.addMember("match",function (regex) {
356
+ return this.str.match(regex);
357
+ });
358
+ });
359
+ var Tokens = $c.Tokens;
360
+
361
+
362
+ $m.parse = function (str) {
363
+ var parser = new $c.RootParser();
364
+ parser.parse(new $c.Tokens(str));
365
+ return parser.toString();
366
+ };
367
+
368
+ $m.toJSON = function (str) {
369
+ var parser = new $c.RootParser();
370
+ parser.parse(new $c.Tokens(str));
371
+ return parser.toJSON();
372
+ };
373
+
374
+
375
+ JS2.Class.extend('RootParser', function(KLASS, OO){
376
+ OO.addMember("handlers",{});
377
+
378
+ OO.addMember("initialize",function () {
379
+ this.out = [];
380
+ this.finished = false;
381
+ });
382
+
383
+ OO.addMember("parse",function (tokens) {
384
+ var len = tokens.length();
385
+ if (this.startParse(tokens) === false || this.parseTokens(tokens) === false || this.endParse(tokens) === false) return false
386
+ return len != tokens.length();
387
+ });
388
+
389
+ // TODO: messy clean this process up
390
+ OO.addMember("parseTokens",function (tokens) {
391
+ var sanity = 100;
392
+ var origLen = tokens.length();
393
+
394
+ while (tokens.any()) {
395
+ var token = tokens.peek();
396
+ if (!token) break;
397
+
398
+ // has a parser class associated with this token
399
+ var handlerClass = this.getHandler(token) || token[2];
400
+ if (handlerClass) {
401
+ var handler = new $c[handlerClass];
402
+ if (handler.parse(tokens) !== false) {
403
+ this.out.push(handler);
404
+ tokens.lastHandler = handler;
405
+ } else {
406
+ this.handleToken(token, tokens);
407
+ }
408
+ }
409
+
410
+ // no parser class, use "this" to just consume it
411
+ else {
412
+ this.handleToken(token, tokens);
413
+ }
414
+
415
+ if (this.finished) break;
416
+
417
+ if (origLen == tokens.length() && sanity-- == 0) {
418
+ throw "parse error";
419
+ } else {
420
+ sanity = 100;
421
+ }
422
+ }
423
+ });
424
+
425
+ OO.addMember("startParse",function () { });
426
+ OO.addMember("endParse",function () { });
427
+
428
+ OO.addMember("handleToken",function (token, tokens) {
429
+ this.out.push(token[1]);
430
+ tokens.consume(token[1].length);
431
+ });
432
+
433
+ OO.addMember("toString",function () {
434
+ var ret = [];
435
+ for(var _i1=0,_c1=this.out,_l1=_c1.length,ele;(ele=_c1[_i1])||(_i1<_l1);_i1++){
436
+ ret.push(ele.toString());
437
+ }
438
+ return ret.join("");
439
+ });
440
+
441
+ OO.addMember("toJSON",function () {
442
+ return JSON.stringify(this.toStruct());
443
+ });
444
+
445
+ OO.addMember("toStruct",function () {
446
+ var ret = [];
447
+ for(var _i1=0,_c1=this.out,_l1=_c1.length,ele;(ele=_c1[_i1])||(_i1<_l1);_i1++){
448
+ ret.push(ele.toStruct ? ele.toStruct() : ele);
449
+ }
450
+ return ret;
451
+ });
452
+
453
+ // intercepts parser class for special cases
454
+ OO.addMember("getHandler",function (token) {
455
+ return null;
456
+ });
457
+
458
+ OO.addMember("chop",function () {
459
+ this.out.pop();
460
+ });
461
+ });
462
+
463
+ var RootParser = $c.RootParser;
464
+
465
+ RootParser.extend('ClassParser', function(KLASS, OO){
466
+ // private closure
467
+
468
+ var REGEX = Tokens.regex("<CLASS> <CLASSNAME><LCURLY>");
469
+ var EXTENDS = Tokens.regex("<CLASS> <CLASSNAME><EXTENDS><CLASSNAME><LCURLY>");
470
+
471
+
472
+ OO.addMember("parse",function (tokens) {
473
+ var m = tokens.match(REGEX) || tokens.match(EXTENDS);
474
+ var name = m[2];
475
+ var extending = m[4] || "$m.Class";
476
+
477
+ tokens.consume(m[0].length-1);
478
+
479
+ var content = new $c.ClassContentParser();
480
+ content.parse(tokens);
481
+
482
+ var behind = tokens.lookback(7);
483
+ var isPublic = ($m.PLATFORM == 'node' && behind.match(/public$/)) ? "\nexports." + name + '=' + name + ';' : '';
484
+ var isExports = ($m.PLATFORM == 'node' && behind == 'export') ? "\nmodule.exports." + name + '=' + name + ';' : '';
485
+
486
+
487
+ this.out = [ "var ", name, " = " + extending + ".extend(function(KLASS, OO)", content, ");", isPublic, isExports ];
488
+ });
489
+ });
490
+
491
+ RootParser.extend('ModuleParser', function(KLASS, OO){
492
+ // private closure
493
+
494
+ var REGEX = Tokens.regex("<MODULE> <CLASSNAME><LCURLY>");
495
+
496
+
497
+ OO.addMember("parse",function (tokens) {
498
+ var m = tokens.match(REGEX);
499
+ if (!m) return false;
500
+ var name = m[2];
501
+ tokens.consume(m[0].length-1);
502
+
503
+ var content = new $c.ClassContentParser();
504
+ content.parse(tokens);
505
+
506
+ this.out = [ "var ", name, " = $m.Module.extend(function(KLASS, OO)", content, ");" ];
507
+ });
508
+ });
509
+
510
+ RootParser.extend('CurlyParser', function(KLASS, OO){
511
+ OO.addMember("initialize",function (chop) {
512
+ this.chop = chop;
513
+ this.$super();
514
+ });
515
+
516
+ OO.addMember("handleToken",function (token, tokens) {
517
+ if (this.curly === undefined) this.curly = 0;
518
+ if (this.foo) console.log(RTYPES[token[0]], token[1]);
519
+ if (token[0] == TYPES.RCURLY) {
520
+ this.curly--;
521
+ } else if (token[0] == TYPES.LCURLY) {
522
+ this.curly++;
523
+ }
524
+
525
+ this.$super(token, tokens);
526
+
527
+ if (this.curly == 0) {
528
+ this.finished = true;
529
+ }
530
+ });
531
+
532
+ OO.addMember("endParse",function (tokens) {
533
+ if (this.chop) {
534
+ this.out.pop();
535
+ this.out.shift();
536
+ }
537
+ });
538
+ });
539
+
540
+ var CurlyParser = $c.CurlyParser;
541
+
542
+ CurlyParser.extend('ClassContentParser', function(KLASS, OO){
543
+ OO.addMember("getHandler",function (token) {
544
+ switch(token[0]) {
545
+ case TYPES.STATIC: return "StaticParser";
546
+ case TYPES.VAR: return "MemberParser";
547
+ case TYPES.FUNCTION: return "MethodParser";
548
+ case TYPES.PRIVATE: return "PrivateParser";
549
+ case TYPES.INCLUDE: return "IncludeParser";
550
+ }
551
+ });
552
+
553
+ });
554
+
555
+ RootParser.extend('LineParser', function(KLASS, OO){
556
+ OO.addMember("handleToken",function (token, tokens) {
557
+ this.$super(token, tokens);
558
+ if (token[0] == TYPES.SEMICOLON) {
559
+ this.finished = true;
560
+ }
561
+ });
562
+ });
563
+
564
+ CurlyParser.extend('PrivateParser', function(KLASS, OO){
565
+ // private closure
566
+
567
+ var REGEX = Tokens.regex("<PRIVATE>\\s*");
568
+
569
+
570
+ OO.addMember("startParse",function (tokens) {
571
+ var m = tokens.match(REGEX);
572
+ tokens.consume(m[0].length);
573
+ });
574
+
575
+ OO.addMember("endParse",function (tokens) {
576
+ this.out.pop();
577
+ this.out.shift();
578
+ });
579
+ });
580
+
581
+
582
+ RootParser.extend('IStringParser', function(KLASS, OO){
583
+ // private closure
584
+
585
+ var BEGIN = Tokens.regex("<ISTRING_START>");
586
+
587
+
588
+ OO.addMember("parse",function (tokens) {
589
+ var m = tokens.match(BEGIN);
590
+ tokens.consume(m[0].length);
591
+ this.out.push('"');
592
+
593
+ while (1) {
594
+ var m = tokens.match(/^((?:\\.|.)*?)(#\{|})/);
595
+ var str = m[1];
596
+ var len = m[0].length;
597
+ str.replace(/"/, '\\"');
598
+
599
+ if (m[2] == '#{') {
600
+ this.out.push(str+'"+(');
601
+ tokens.consume(len-1);
602
+ this.parseMiddle(tokens);
603
+ this.out.push(')+"');
604
+ }
605
+
606
+ else if (m[2] == '}') {
607
+ this.out.push(str);
608
+ this.out.push('"');
609
+ tokens.consume(len);
610
+ return;
611
+ }
612
+ }
613
+ });
614
+
615
+ OO.addMember("parseMiddle",function (tokens) {
616
+ var parser = new CurlyParser(true);
617
+ parser.parse(tokens);
618
+ this.out.push(parser);
619
+ });
620
+ });
621
+
622
+ RootParser.extend('StaticParser', function(KLASS, OO){
623
+ // private closure
624
+
625
+ var VAR_REGEX = Tokens.regex("(<STATIC>(\\s+))<VAR>");
626
+ var FUNCT_REGEX = Tokens.regex("(<STATIC>(\\s+))<FUNCTION>");
627
+
628
+
629
+ OO.addMember("parseTokens",function (tokens) {
630
+ var varMatch = tokens.match(VAR_REGEX);
631
+ if (varMatch) {
632
+ tokens.consume(varMatch[1].length);
633
+ var parser = new MemberParser();
634
+ parser.isStatic = true;
635
+ parser.parse(tokens);
636
+ this.out.push(parser);
637
+ }
638
+
639
+ else {
640
+ var functMatch = tokens.match(FUNCT_REGEX);
641
+ tokens.consume(functMatch[1].length);
642
+
643
+ var parser = new MethodParser();
644
+ parser.isStatic = true;
645
+ parser.parse(tokens);
646
+ this.out.push(parser);
647
+ }
648
+ });
649
+ });
650
+
651
+ RootParser.extend('MemberParser', function(KLASS, OO){
652
+ // private closure
653
+
654
+ var REGEX = Tokens.regex("var <IDENT>\\s*=\\s*?");
655
+
656
+
657
+ OO.addMember("parse",function (tokens) {
658
+ var m = tokens.str.match(REGEX);
659
+ this.name = m[1];
660
+ tokens.consume(m[0].length);
661
+
662
+ var parser = new $c.LineParser();
663
+ parser.parse(tokens);
664
+ parser.chop();
665
+ var addMethod = this.isStatic ? 'addStaticMember' : 'addMember';
666
+
667
+ this.out = [ "OO." + addMethod + "(", JSON.stringify(this.name), ",", parser, ");" ];
668
+ });
669
+ });
670
+
671
+
672
+
673
+ RootParser.extend('IncludeParser', function(KLASS, OO){
674
+ // private closure
675
+
676
+ var REGEX = Tokens.regex("<INCLUDE> <CLASSNAME><SEMICOLON>");
677
+
678
+
679
+ OO.addMember("parse",function (tokens) {
680
+ var m = tokens.match(REGEX);
681
+ tokens.consume(m[0].length);
682
+ this.out = [ 'OO.include(', m[2], ');' ];
683
+ });
684
+ });
685
+
686
+ RootParser.extend('HereDocParser', function(KLASS, OO){
687
+ // private closure
688
+
689
+ var REGEX = Tokens.regex("<HEREDOC>");
690
+
691
+
692
+ OO.addMember("parse",function (tokens) {
693
+ var beginning = tokens.match(/^<<(\w+)\s*([;\)])*\n/);
694
+ tokens.consume(beginning[0].length);
695
+
696
+ var spacing = tokens.match(/^(\s*)/);
697
+ var regexSub = new RegExp("^" + (spacing[0] || ''), "mg");
698
+
699
+
700
+ var strMatch = tokens.match(new RegExp("^([\\s\\S]*?)\\n\\s*" + beginning[1] + "\\s*\\n"));
701
+ var toParse = strMatch[1] || '';
702
+
703
+ toParse = toParse.replace(regexSub, '');
704
+ toParse = toParse.replace("\n", "\\n");
705
+
706
+ var string = $m.parse('%{' + toParse + '}');
707
+ tokens.consume(strMatch[0] ? strMatch[0].length : 0);
708
+
709
+ this.out = [ string, beginning[2] || ';' ];
710
+ });
711
+ });
712
+
713
+ RootParser.extend('MethodParser', function(KLASS, OO){
714
+ // private closure
715
+
716
+ var REGEX = Tokens.regex("<FUNCTION> <IDENT><ARGS><SPACE>");
717
+
718
+
719
+ OO.addMember("parse",function (tokens) {
720
+ var m = tokens.str.match(REGEX);
721
+ tokens.consume(m[0].length);
722
+ var name = m[2];
723
+ var args = m[3];
724
+
725
+ var body = new CurlyParser();
726
+ body.parse(tokens);
727
+
728
+ var addMethod = this.isStatic ? 'addStaticMember' : 'addMember';
729
+
730
+
731
+ this.out = [ 'OO.' + addMethod + '(', JSON.stringify(name), ', function', args, body, ');' ];
732
+ });
733
+ });
734
+
735
+ RootParser.extend('ShorthandFunctionParser', function(KLASS, OO){
736
+ // private closure
737
+
738
+ var ARGS_REGEX = Tokens.regex("<ARGS>\\s*");
739
+
740
+
741
+ OO.addMember("parse",function (tokens) {
742
+ tokens.consume(1);
743
+ var argsMatch = tokens.match(ARGS_REGEX);
744
+ var args = null;
745
+
746
+ if (argsMatch) {
747
+ args = argsMatch[0];
748
+ tokens.consume(argsMatch[0].length);
749
+ } else {
750
+ args = "($1,$2,$3)";
751
+ }
752
+
753
+ var body = new CurlyParser();
754
+ body.parse(tokens);
755
+ var semi = tokens.match(/^\s*[,;\)]/) ? '' : ';';
756
+
757
+ this.out = [ 'function', args, body, semi ];
758
+ });
759
+ });
760
+
761
+ RootParser.extend('CommentParser', function(KLASS, OO){
762
+ OO.addMember("parse",function (tokens) {
763
+ var m = tokens.match(/^\/\/.*?\n/);
764
+ if (m) {
765
+ tokens.consume(m[0].length);
766
+ this.out = [ m[0] ];
767
+ return;
768
+ }
769
+
770
+ var m2 = tokens.match(/^\/\*[\s\S]*?\*\//);
771
+ if (m2) {
772
+ tokens.consume(m2[0].length);
773
+ this.out = [ m2[0] ];
774
+ return;
775
+ }
776
+
777
+ return false;
778
+ });
779
+ });
780
+
781
+ RootParser.extend('RegexParser', function(KLASS, OO){
782
+ // private closure
783
+
784
+ var REGEX = /^\/(\\.|[^\/])+\/[imgy]{0,4}/;
785
+ var DIVIDE = /(\}|\)|\+\+|\-\-|[\w\$])$/;
786
+
787
+
788
+ OO.addMember("parseTokens",function (tokens) {
789
+ var back = tokens.lookback(2);
790
+
791
+ if (back.match(DIVIDE)) {
792
+ tokens.consume(1);
793
+ this.out.push("/");
794
+ }
795
+
796
+ else {
797
+ var m = tokens.match(REGEX);
798
+ if (m) {
799
+ this.out.push(m[0]);
800
+ tokens.consume(m[0].length);
801
+ } else {
802
+ return false;
803
+ }
804
+ }
805
+
806
+ });
807
+
808
+ });
809
+
810
+ CurlyParser.extend('ForeachParser', function(KLASS, OO){
811
+ // private closure
812
+
813
+ var REGEX = Tokens.regex("<FOREACH><LBRACE><VAR> <IDENT>(?:**:**<IDENT>)? in (.*?)**<RBRACE>**{");
814
+
815
+
816
+ OO.addMember("startParse",function (tokens) {
817
+ var m = tokens.match(REGEX);
818
+ namespace = tokens.iterator++;
819
+
820
+ this.item = m[4];
821
+ this.iterator = m[5] || "_i_" + namespace;
822
+ this.list = m[6];
823
+
824
+ // TODO ugly, revisit this later
825
+ tokens.consume(m[0].length-1);
826
+ var declare = [ this.iterator + "=0", this.item + "=null", "_list_" + namespace + "=" + this.list, "_len_" + namespace + "=_list_" + namespace + ".length" ].join(',');
827
+
828
+ var bool = "(" + this.item + "=" + "_list_" + namespace + "[" + this.iterator + "])||" + this.iterator + "<_len_" + namespace;
829
+
830
+ this.out = [ "for (", declare, ";", bool, ';', this.iterator + "++)" ];
831
+ });
832
+
833
+ OO.addMember("endParse",function (tokens) {
834
+ tokens.iterator--;
835
+ });
836
+
837
+ });
838
+
839
+
840
+
841
+ JS2.Class.extend('JSML', function(KLASS, OO){
842
+ OO.addStaticMember("process",function (txt) {
843
+ return new KLASS(txt);
844
+ });
845
+
846
+ OO.addMember("initialize",function (txt) {
847
+ var lines = txt.split(/\n/);
848
+ this.root = new JS2.JSMLElement();
849
+ this.stack = [ this.root ];
850
+
851
+ for(var _i1=0,_c1=lines,_l1=_c1.length,l;(l=_c1[_i1])||(_i1<_l1);_i1++){
852
+ if (l.match(/^\s*$/)) continue;
853
+ this.processLine(l);
854
+ }
855
+
856
+ var toEval = 'function process() { var out = [];\n' + this.flatten().join('') + '\n return out.join("");\n}';
857
+ eval(toEval);
858
+
859
+ this.result = function(bound) {
860
+ bound = bound || {};
861
+ return process.call(bound);
862
+ };
863
+ });
864
+
865
+ OO.addMember("flatten",function () {
866
+ return this.root.flatten();
867
+ });
868
+
869
+ OO.addMember("processLine",function (line) {
870
+ if (line.match(/^\s*$/)) return;
871
+
872
+ var ele = new JS2.JSMLElement(line);
873
+ var scope = this.getScope();
874
+
875
+ if (ele.scope == scope) {
876
+ this.stack.pop();
877
+ this.getLast().push(ele);
878
+ this.stack.push(ele);
879
+ } else if (ele.scope > scope) {
880
+ this.getLast().push(ele);
881
+ this.stack.push(ele);
882
+ } else if (ele.scope < scope) {
883
+ var diff = scope - ele.scope + 1;
884
+ while(diff-- > 0) {
885
+ this.stack.pop();
886
+ }
887
+ this.getLast().push(ele);
888
+ this.stack.push(ele);
889
+ }
890
+ });
891
+
892
+
893
+ OO.addMember("getScope",function () {
894
+ return this.stack.length - 1;
895
+ });
896
+
897
+ OO.addMember("getLast",function () {
898
+ return this.stack[this.stack.length-1];
899
+ });
900
+
901
+ });
902
+
903
+ JS2.Class.extend('JSMLElement', function(KLASS, OO){
904
+ OO.addMember("SCOPE_REGEX",/^(\s*)(.*)$/);
905
+ OO.addMember("SPLIT_REGEX",/^((?:\.|\#|\%)[^=\s\{]*)?(\{.*\})?(=|-)?(?:\s*)(.*)$/);
906
+ OO.addMember("TOKEN_REGEX",/(\%|\#|\.)([\w][\w\-]*)/g);
907
+ OO.addMember("JS_REGEX",/^(-|=)(.*)$/g);
908
+ OO.addMember("SCOPE_OFFSET",1);
909
+ OO.addMember("SELF_CLOSING",{ area: null, basefont: null, br: null, hr: null, input: null, img: null, link: null, meta: null });
910
+
911
+ OO.addMember("initialize",function (line) {
912
+ this.children = [];
913
+
914
+ if (line == null) {
915
+ this.scope = this.SCOPE_OFFSET;
916
+ return;
917
+ }
918
+
919
+ var spaceMatch = line.match(this.SCOPE_REGEX);
920
+ this.scope = spaceMatch[1].length / 2 + this.SCOPE_OFFSET;
921
+
922
+ this.classes = [];
923
+ this.nodeID = null;
924
+
925
+ this.parse(spaceMatch[2]);
926
+ });
927
+
928
+ OO.addMember("push",function (child) {
929
+ this.children.push(child);
930
+ });
931
+
932
+ OO.addMember("parse",function (line) {
933
+ this.attributes;
934
+ this.line = line;
935
+ var self = this;
936
+
937
+ var splitted = line.match(this.SPLIT_REGEX);
938
+ var tokens = splitted[1];
939
+ var attrs = splitted[2];
940
+ var jsType = splitted[3];
941
+ var content = splitted[4];
942
+
943
+ if (tokens) {
944
+ tokens.replace(this.TOKEN_REGEX, function(match, type, name){
945
+ switch(type) {
946
+ case '%': self.nodeType = name; break;
947
+ case '.': self.classes.push(name); break;
948
+ case '#': self.nodeID = name; break;
949
+ }
950
+ return '';
951
+ });
952
+ }
953
+
954
+ if (jsType == '=') {
955
+ this.jsEQ = content;
956
+ } else if (jsType == '-') {
957
+ this.jsExec = content;
958
+ } else {
959
+ this.content = content;
960
+ }
961
+
962
+ if (attrs) {
963
+ this.attributes = attrs;
964
+ }
965
+
966
+ if (!this.nodeType && (this.classes.length || this.nodeID)) {
967
+ this.nodeType = 'div';
968
+ }
969
+
970
+ if (this.SELF_CLOSING.hasOwnProperty(this.nodeType) && this.children.length == 0) {
971
+ this.selfClose = '/';
972
+ } else {
973
+ this.selfClose = '';
974
+ }
975
+ });
976
+
977
+ OO.addMember("flatten",function () {
978
+ var out = [];
979
+
980
+ for(var _i1=0,_c1=this.children,_l1=_c1.length,c;(c=_c1[_i1])||(_i1<_l1);_i1++){
981
+ var arr = c.flatten();
982
+ for(var _i2=0,_c2=arr,_l2=_c2.length,item;(item=_c2[_i2])||(_i2<_l2);_i2++){
983
+ out.push(item);
984
+ }
985
+ }
986
+
987
+ if (this.nodeType) {
988
+ this.handleJsEQ(out);
989
+ this.handleContent(out);
990
+ out.unshift('out.push("<' + this.nodeType + '"+JS2.JSMLElement.parseAttributes(' + (this.attributes || "{}") + ', ' + JSON.stringify(this.classes || []) + ', ' + JSON.stringify(this.id || null) + ')+"' + this.selfClose + '>");\n');
991
+ if (this.selfClose == '') {
992
+ out.push('out.push(' + JSON.stringify("</"+(this.nodeType)+">") + ');\n');
993
+ }
994
+ } else {
995
+ this.handleJsExec(out);
996
+ this.handleJsEQ(out);
997
+ this.handleContent(out);
998
+ }
999
+
1000
+ return out;
1001
+ });
1002
+
1003
+ OO.addMember("handleJsEQ",function (out) {
1004
+ if (this.jsEQ) {
1005
+ this.jsEQ = this.jsEQ.replace(/;\s*$/, '');
1006
+ out.unshift('out.push(' + this.jsEQ + ');\n');
1007
+ }
1008
+ });
1009
+
1010
+ OO.addMember("handleContent",function (out) {
1011
+ if (this.content != null && this.content.length > 0) {
1012
+ out.unshift('out.push(' + JSON.stringify(this.content) + ');\n');
1013
+ }
1014
+ });
1015
+
1016
+
1017
+ OO.addMember("handleJsExec",function (out) {
1018
+ if (this.jsExec) {
1019
+ out.unshift(this.jsExec);
1020
+ if (this.jsExec.match(/\{\s*$/)) {
1021
+ out.push("}\n");
1022
+ }
1023
+ }
1024
+ });
1025
+
1026
+ OO.addStaticMember("parseAttributes",function (hash, classes, id) {
1027
+ var out = [];
1028
+ classes = classes || [];
1029
+ if (hash['class']) classes.push(hash['class']);
1030
+ if (classes.length) hash['class'] = classes.join(" ");
1031
+
1032
+ for (var k in hash) {
1033
+ if (hash.hasOwnProperty(k)) {
1034
+ out.push(k + '=' + JSON.stringify(hash[k]));
1035
+ }
1036
+ }
1037
+ return (out.length ? ' ' : '') + out.join(' ');
1038
+ });
1039
+ });
1040
+
1041
+
1042
+ JS2.Class.extend('CLI', function(KLASS, OO){
1043
+ // private closure
1044
+
1045
+ var COMMANDS = {
1046
+ help: 'help',
1047
+ render: 'render',
1048
+ compile: 'compile',
1049
+ watch: 'watch'
1050
+ };
1051
+
1052
+
1053
+ OO.addMember("run",function (args) {
1054
+ var opts = this.parseOpts(args);
1055
+ var options = opts[0];
1056
+ var command = opts[1];
1057
+ var files = opts[2];
1058
+ });
1059
+
1060
+ OO.addMember("parseOpts",function (args) {
1061
+ var files = [];
1062
+ var options = {};
1063
+ var command = null;
1064
+
1065
+ var endedArgs = false;
1066
+
1067
+ for(var i=0,_c1=args,_l1=_c1.length,arg;(arg=_c1[i])||(i<_l1);i++){
1068
+ if (endedArgs) {
1069
+ files.push(arg);
1070
+ }
1071
+
1072
+ else if (COMMANDS[arg]) {
1073
+ command = arg;
1074
+ endedArgs = true;
1075
+ }
1076
+
1077
+ else {
1078
+ var setting = arg.match(/^(\w+)(?:=(.*))?$/);
1079
+ if (setting) options[setting[0]] = setting[1] || 'true';
1080
+ }
1081
+ }
1082
+
1083
+ return [ options, command, files ];
1084
+ });
1085
+ });
1086
+
1087
+
1088
+ })();
1089
+ FINISH
1090
+ end
1091
+ end