mochiscript 0.4.0.pre10 → 0.4.0.pre12

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