PageRenderTime 37ms CodeModel.GetById 1ms app.highlight 30ms RepoModel.GetById 1ms app.codeStats 1ms

/jsmeter/parse.js

http://jsmeter.googlecode.com/
JavaScript | 782 lines | 682 code | 60 blank | 40 comment | 114 complexity | f6beebda37eb0020a7b12cc969525cc6 MD5 | raw file
  1// parse.js
  2// Parser for Simplified JavaScript written in Simplified JavaScript
  3// From Top Down Operator Precedence
  4// http://javascript.crockford.com/tdop/index.html
  5// Douglas Crockford
  6// 2008-07-07
  7
  8exports.make_parse = function () {
  9    var scope;
 10    var symbol_table = {};
 11    var token;
 12    var tokens;
 13    var token_nr;
 14    var nextComments = [ ];
 15
 16    var itself = function () {
 17        return this;
 18    };
 19
 20    var original_scope = {
 21        define: function (n) {
 22            var t = this.def[n.value];
 23            /*if (typeof t === "object") {
 24                n.error(t.reserved ? "Already reserved." : "Already defined.");
 25            }*/
 26            this.def[n.value] = n;
 27            n.reserved = false;
 28            n.nud      = itself;
 29            n.led      = null;
 30            n.std      = null;
 31            n.lbp      = 0;
 32            n.scope    = scope;
 33            return n;
 34        },
 35        find: function (n) {
 36            var e = this, o;
 37            while (true) {
 38                o = e.def[n];
 39                if (o && o.nud) {
 40                    return o;
 41                }
 42                e = e.parent;
 43                if (!e) {
 44                    if (!symbol_table.hasOwnProperty(n)) {
 45                        var s = symbol(n);
 46                        s.nud = function() {
 47                            return this;
 48                        }
 49                    }
 50                    return symbol_table[n];
 51                }
 52            }
 53        },
 54        pop: function () {
 55            scope = this.parent;
 56        },
 57        reserve: function (n) {
 58            if (n.arity !== "name" || n.reserved) {
 59                return;
 60            }
 61            var t = this.def[n.value];
 62            if (t) {
 63                if (t.reserved) {
 64                    return;
 65                }
 66                if (t.arity === "name") {
 67                    //n.error("Already defined.");
 68                }
 69            }
 70            this.def[n.value] = n;
 71            n.reserved = true;
 72        }
 73    };
 74
 75    var new_scope = function () {
 76        var s = scope;
 77        scope = Object.create(original_scope);
 78        scope.def = {};
 79        scope.parent = s;
 80        return scope;
 81    };
 82
 83    var advance = function (id) {
 84        var a, o, t, v, cl, cli;
 85        if (id && token.id !== id) {
 86            token.error("Expected '" + id + "'.");
 87        }
 88        if (token_nr >= tokens.length) {
 89            token = symbol_table["(end)"];
 90            return;
 91        }
 92        t = tokens[token_nr];
 93        token_nr += 1;
 94        v = t.value;
 95        a = t.type;
 96        if (a === "name") {
 97            o = scope.find(v);
 98        } else if (a === "operator") {
 99            o = symbol_table[v];
100            if (!o) {
101                t.error("Unknown operator.");
102            }
103        } else if (a === "string" || a ===  "number" || a === "regexp" || a === "regexpops") {
104            o = symbol_table["(literal)"];
105            a = "literal";
106        } else if (a === "comment") {
107            o = symbol_table["(comment)"];
108        } else {
109            t.error("Unexpected token.");
110        }
111        token = Object.create(o);
112        token.from  = t.from;
113        token.to    = t.to;
114        token.line  = t.line;
115        token.value = v;
116        token.arity = a;
117        //window.status = JSON.stringify(token);
118        
119        if (token.arity === "comment") {
120            cl = v.split(/\n/g);
121            for (cli=0; cli<cl.length; cli++) {
122                nextComments.push(cl[cli]);
123            }
124            advance();
125        }
126        
127        return token;
128    };
129
130    var expression = function (rbp) {
131        var left;
132        var t = token;
133        advance();
134        left = t.nud();
135        while (rbp < token.lbp) {
136            t = token;
137            advance();
138            left = t.led(left);
139        }  
140        if (left) {
141            left.comments = nextComments;
142            nextComments = [ ];
143        }
144        return left;
145    };
146
147    var statement = function () {
148        var n = token, v;
149
150        if (n.std) {
151            advance();
152            scope.reserve(n);
153            return n.std();
154        }
155        v = expression(0);
156        /*if (!v.assignment && 
157            v.id !== "(" && 
158            v.id!== "++" && 
159            v.id!== "--" && 
160            v.value!=="use strict" &&
161            v.id!=="typeof") {
162                v.error("Bad expression statement.");
163        }*/
164        /*if (v.assignment && v.arity==="function") {
165            advance();
166        } else {
167            advance(";");
168        }*/
169        if (token.id===";") {
170            advance(";");
171        }  
172        if (v) {
173            v.comments = nextComments;
174            nextComments = [ ];
175        }
176        return v;
177    };
178
179    var statements = function () {
180        var a = [], s;
181        while (true) {
182            if (token.id === "}" || token.id === "(end)") {
183                break;
184            }
185            s = statement();
186            if (s) {
187                a.push(s);
188            }
189        }
190        return a.length === 0 ? null : a.length === 1 ? a[0] : a;
191    };
192
193    var block = function () {
194        var t = token;
195        advance("{");
196        return t.std();
197    };
198
199    var original_symbol = {
200        nud: function () {
201            //this.error("Undefined.");
202        },
203        led: function (left) {
204            this.error("Missing operator.");
205        }
206    };
207
208    var symbol = function (id, bp) {
209        var s = symbol_table[id];
210        bp = bp || 0;
211        if (s) {
212            if (bp >= s.lbp) {
213                s.lbp = bp;
214            }
215        } else {
216            s = Object.create(original_symbol);
217            s.id = s.value = id;
218            s.lbp = bp;
219            symbol_table[id] = s;
220        }
221        return s;
222    };
223
224    var constant = function (s, v) {
225        var x = symbol(s);
226        x.nud = function () {
227            scope.reserve(this);
228            this.value = symbol_table[this.id].value;
229            this.arity = "literal";
230            return this;
231        };
232        x.value = v;
233        return x;
234    };
235
236    var infix = function (id, bp, led) {
237        var s = symbol(id, bp);
238        s.led = led || function (left) {
239            this.first = left;
240            this.second = expression(bp);
241            this.arity = "binary";
242            return this;
243        };
244        return s;
245    };
246
247    var infixr = function (id, bp, led) {
248        var s = symbol(id, bp);
249        s.led = led || function (left) {
250            this.first = left;
251            this.second = expression(bp - 1);
252            this.arity = "binary";
253            return this;
254        };
255        return s;
256    };
257
258    var assignment = function (id) {
259        return infixr(id, 10, function (left) {
260            if (left.id !== "." && left.id !== "[" && left.arity !== "name") {
261                left.error("Bad lvalue.");
262            }
263            this.first = left;
264            this.second = expression(9);
265            this.assignment = true;
266            this.arity = "binary";
267            if (token.id===",") {
268                advance(",");
269            }
270            return this;
271        });
272    };
273
274    var prefix = function (id, nud) {
275        var s = symbol(id);
276        s.nud = nud || function () {
277            scope.reserve(this);
278            this.first = expression(70);
279            this.arity = "unary";
280            return this;
281        };
282        return s;
283    };
284
285    var stmt = function (s, f) {
286        var x = symbol(s);
287        x.std = f;
288        return x;
289    };
290
291    symbol("(end)");
292    symbol("(name)");
293    symbol(":");
294    symbol(";");
295    symbol(")");
296    symbol("]");
297    symbol("}");
298    symbol(",");
299    symbol("else");
300
301    constant("true", true);
302    constant("false", false);
303    constant("null", null);
304    constant("pi", 3.141592653589793);
305    constant("Object", {});
306    constant("Array", []);
307    constant("Date", "Date");
308    constant("Math", "Math");
309
310    symbol("(literal)").nud = itself;
311    symbol("(comment)");
312
313    symbol("this").nud = function () {
314        scope.reserve(this);
315        this.arity = "this";
316        return this;
317    };
318
319    assignment("=");
320    assignment("+=");
321    assignment("-=");
322    assignment("*=");
323    assignment("/=");
324    assignment("%=");
325    assignment("&=");
326    assignment("|=");
327    assignment("^=");
328    assignment(">>=");
329    assignment(">>>=");
330    assignment("<<=");
331
332    infix("?", 20, function (left) {
333        this.first = left;
334        this.second = expression(0);
335        advance(":");
336        this.third = expression(0);
337        this.arity = "ternary";
338        return this;
339    });
340
341    infixr("&", 20);
342    infixr("|", 20);
343    
344    infixr("&&", 30);
345    infixr("||", 30);
346    
347    infixr("in", 40);
348    infixr("==", 40);
349    infixr("!=", 40);
350    infixr("===", 40);
351    infixr("!==", 40);
352    infixr("<", 40);
353    infixr("<=", 40);
354    infixr(">", 40);
355    infixr(">=", 40);
356    infixr(">>", 40);
357    infixr(">>>", 40);
358    infixr("<<", 40);
359
360    infixr("instanceof", 45);
361    infix("+", 50);
362    infix("-", 50);
363
364    infix("^", 60);
365    infix("*", 60);
366    infix("/", 60);
367    infix("%", 60);
368
369    infix("++", 65, function (left) {
370            this.first = left;
371            this.arity = "unary";
372            return this;
373        });
374    infix("--", 65, function (left) {
375            this.first = left;
376            this.arity = "unary";
377            return this;
378        });
379
380    infix(".", 80, function (left) {
381        this.first = left;
382        //if (token.arity !== "name") {
383        //    token.error("Expected a property name.");
384        //}
385        token.arity = "literal";
386        this.second = token;
387        this.arity = "binary";
388        advance();
389        return this;
390    });
391
392    infix("[", 80, function (left) {
393        this.first = left;
394        this.second = expression(0);
395        this.arity = "binary";
396        advance("]");
397        return this;
398    });
399
400    infix("(", 80, function (left) {
401        var a = [];
402        if (left && (left.id === "." || left.id === "[")) {
403            this.arity = "ternary";
404            this.first = left.first;
405            this.second = left.second;
406            this.third = a;
407        } else {
408            this.arity = "binary";
409            this.first = left;
410            this.second = a;
411            /*if ((left.arity !== "unary" || left.id !== "function") &&
412                    left.arity !== "name" && left.id !== "(" &&
413                    left.id !== "&&" && left.id !== "||" && left.id !== "?" &&
414                    left.id !== "function") {
415                left.error("Expected a variable name.");
416            }*/
417        }
418        if (token.id !== ")") {
419            while (true)  {
420                a.push(expression(0));
421                if (token.id !== ",") {
422                    break;
423                }
424                advance(",");
425            }
426        }
427        advance(")");
428        return this;
429    });
430
431    prefix("new");
432
433    prefix("!");
434    prefix("~");
435    prefix("-");
436    prefix("+");
437    prefix("--");
438    prefix("++");
439    prefix("typeof", function() {
440        var e = expression(0);
441        this.first = e;
442        return this;
443    });
444
445    prefix("(", function () {
446        var e = expression(0);
447        advance(")");
448        return e;
449    });
450
451    prefix("function", function () {
452        var a = [];
453        new_scope();
454        if (token.arity === "name") {
455            scope.define(token);
456            this.name = token.value;
457            advance();
458        }
459        if (token.id !== "(") {
460            scope.define(token);
461            this.name = token.value;
462            advance();
463        }
464        advance("(");
465        if (token.id !== ")") {
466            while (true) {
467                if (token.arity !== "name") {
468                    token.error("Expected a parameter name.");
469                }
470                scope.define(token);
471                a.push(token);
472                advance();
473                if (token.id !== ",") {
474                    break;
475                }
476                advance(",");
477            }
478        }
479        this.first = a;
480        advance(")");
481        this.second = block();
482        /*advance("{");
483        this.second = statements();
484        advance("}");*/
485        this.arity = "function";
486        this.assignment = true;
487        scope.pop();
488        return this;
489    });
490
491    prefix("[", function () {
492        var a = [];
493        if (token.id !== "]") {
494            while (true) {
495                a.push(expression(0));
496                if (token.id !== ",") {
497                    break;
498                }
499                advance(",");
500            }
501        }
502        advance("]");
503        this.first = a;
504        this.arity = "unary";
505        return this;
506    });
507
508    prefix("{", function () {
509        var a = [], n, v;
510        if (token.id !== "}") {
511            while (true) {
512                n = token;
513                if (n.arity !== "name" && n.arity !== "literal") {
514                    token.error("Bad property name.");
515                }
516                advance();
517                advance(":");
518                v = expression(0);
519                v.key = n.value;
520                a.push(v);
521                if (token.id !== ",") {
522                    break;
523                }
524                advance(",");
525            }
526        }
527        advance("}");
528        this.first = a;
529        this.arity = "unary";
530        return this;
531    });
532
533    stmt("<script", function() {
534        while (token.value!==">") {
535            advance();
536        }
537        advance(">");
538    });
539
540    stmt("</script", function() {
541        while (token.value!==">") {
542            advance();
543        }
544        advance(">");
545    });
546
547    stmt("{", function () {
548        new_scope();
549        var a = statements();
550        advance("}");
551        scope.pop();
552        return a;
553    });
554
555    stmt("var", function () {
556        var a = [], n, t;
557        while (true) {
558            n = token;
559            if (n.arity !== "name") {
560                n.error("Expected a new variable name.");
561            }
562            scope.define(n);
563            advance();
564            if (token.id === "=") {
565                t = token;
566                advance("=");
567                t.first = n;
568                t.second = expression(0);
569                t.arity = "binary";
570                a.push(t);
571            }
572            if (token.id === "in") {
573                t = token;
574                advance("in");
575                t.first = n;
576                t.second = expression(0);
577                t.arity = "binary";
578                a.push(t);
579            }
580            if (token.id !== ",") {
581                break;
582            }
583            advance(",");
584        }
585        if (token.id === ";") {
586            advance(";");
587        }
588        return a.length === 0 ? null : a.length === 1 ? a[0] : a;
589    });
590    
591    stmt("try", function() {
592        this.first = block();
593        if (token.value === "catch") {
594            this.second = statement();
595        }
596        if (token.value === "finally") {
597            //this.third = statement();
598        }
599        this.arity = "statement";
600        return this;
601    });
602    
603    stmt("catch", function() {
604        advance("(");
605        if (token.id!==")") {
606            this.first = expression(0);
607        }
608        advance(")");
609        this.second = block();
610        this.arity = "statement";
611        return this;
612    });
613    
614    stmt("finally", function() {
615        this.first = block();
616        this.arity = "statement";
617        return this;
618    });
619
620    stmt("if", function () {
621        advance("(");
622        this.first = expression(0);
623        advance(")");
624        if (token.value==="{") {
625            this.second = block();
626        } else {
627            this.second = statement();
628        }
629        if (token.id === "else") {
630            scope.reserve(token);
631            advance("else");
632            if (token.id==="if") {
633                this.third = statement();
634            } else if (token.value==="{") {
635                this.third = block();
636            } else {
637                this.third = statement();
638            }
639            //this.third = token.id === "if" ? statement() : block();
640        } else {
641            this.third = null;
642        }
643        this.arity = "statement";
644        return this;
645    });
646    
647    stmt("debugger", function() {
648        if (token.id === ";") {
649            advance(";");
650        }
651        this.arity = "statement";
652        return this;
653    });
654
655    stmt("return", function () {
656        this.first = null;
657        this.second = null;
658        if (token.id !== ";") {
659            this.first = expression(0);
660        }
661        if (token.id === ";") {
662            advance(";");
663        }
664        this.arity = "statement";
665        return this;
666    });
667    
668    stmt("throw", function() {
669        this.first = expression(0);
670        if (token.id === ";") {
671            advance(";");
672        }
673        this.arity = "statement";
674        return this;
675    });
676    
677    stmt("delete", function() {
678        this.first = expression(0);
679        if (token.id === ";") {
680            advance(";");
681        }
682        this.arity = "statement";
683        return this;
684    });
685
686    stmt("break", function () {
687        if (token.id === ";") {
688            advance(";");
689        }
690        if (token.id !== "}" && token.id !== "case" && token.id !== "default" && token.id !== "return") {
691            //token.error("Unreachable statement.");
692        }
693        this.arity = "statement";
694        return this;
695    });
696
697    stmt("while", function () {
698        advance("(");
699        this.first = expression(0);
700        advance(")");
701        if (token.value==="{") {
702            this.second = block();
703        } else {
704            this.second = statement();
705        }
706        this.arity = "statement";
707        return this;
708    });
709    
710    stmt("switch", function() {
711        advance("(");
712        this.first = expression(0);
713        advance(")");
714        this.second = block();
715        this.arity = "statement";
716        return this;
717    });
718    
719    stmt("case", function() {
720        this.first = expression(0);
721        advance(":");
722        this.arity = "statement";
723        return this;
724    });
725    
726    stmt("default", function() {
727        advance(":");
728        this.arity = "statement";
729        return this;
730    });
731    
732    stmt("for", function() {
733        this.first = [ ];
734        advance("(");
735        if (token.value==="var") {
736            this.first.push(statement());
737        } else if (token.value!==";") {
738            while (token.id!==";" && token.id!==")") {
739                this.first.push(expression(0));
740            }
741            if (token.id===";") {
742                advance(";");
743            }
744        } else {
745            advance(";");
746        }
747        while (token.id!==")") {
748            if (token.value!==";") {
749                this.first.push(expression(0));
750                if (token.id===";") {
751                    advance(";");
752                }
753            } else {
754                advance(";");
755            }
756        }
757        advance(")");
758        if (token.value==="{") {
759            this.second = block();
760        } else {
761            this.second = statement();
762        }
763        this.arity = "statement";
764        return this;
765    });
766
767    return function (source) {
768        tokens = source.tokens('=<>!+-*&|/%^', '=<>&|+-/');
769        token_nr = 0;
770        new_scope();
771        advance();
772        var s = statements();
773        advance("(end)");
774        scope.pop();
775        if (s.length) {
776            s[s.length-1].comments = nextComments;
777        } else {
778            s.comments = nextComments;
779        }
780        return s;
781    };
782};