2 // Parser.cs: Port of Mozilla's Rhino parser.
3 // This class implements the JScript parser.
6 // Cesar Lopez Nataren (cesar@ciencias.unam.mx)
8 // (C) 2004, Cesar Lopez Nataren
9 // Copyright (C) 2005, Novell Inc (http://novell.com)
13 // Permission is hereby granted, free of charge, to any person obtaining
14 // a copy of this software and associated documentation files (the
15 // "Software"), to deal in the Software without restriction, including
16 // without limitation the rights to use, copy, modify, merge, publish,
17 // distribute, sublicense, and/or sell copies of the Software, and to
18 // permit persons to whom the Software is furnished to do so, subject to
19 // the following conditions:
21 // The above copyright notice and this permission notice shall be
22 // included in all copies or substantial portions of the Software.
24 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
27 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
28 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
29 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
30 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
33 using System.Collections;
37 namespace Microsoft.JScript {
40 * There are three types of functions that can be defined. The first
41 * is a function statement. This is a function appearing as a top-level
42 * statement (i.e., not nested inside some other statement) in either a
43 * script or a function.
45 * The second is a function expression, which is a function appearing in
46 * an expression except for the third type, which is...
48 * The third type is a function expression where the expression is the
49 * top-level expression in an expression statement.
51 * The three types of functions have different treatment and must be
66 internal class Location {
67 private string source_name;
68 private int line_number;
70 internal string SourceName {
71 get { return source_name; }
74 internal int LineNumber {
75 get { return line_number; }
78 internal Location (string source_name, int line_number)
80 this.source_name = source_name;
81 this.line_number = line_number;
85 internal class Parser {
88 bool ok; // did the parse encounter an error?
89 ScriptBlock current_script_or_fn;
90 int nesting_of_function;
92 bool allow_member_expr_as_function_name;
93 Decompiler decompiler;
99 internal Decompiler CreateDecompiler ()
101 return new Decompiler ();
105 /// Build a parse tree from a given source_string
109 /// return an AST representing the parsed program.
110 /// If the parse fails, null will be returned.
112 internal AST Parse (string source_string, string source_location, int line_number)
114 ts = new TokenStream (null, source_string, source_location, line_number);
117 } catch (IOException ex) {
118 throw new Exception ("Illegal state exception");
123 /// Build a parse tree from a given source_reader
127 /// return an AST representing the parsed program.
128 /// If the parse fails, null will be returned.
130 internal AST Parse (StreamReader source_reader, string source_location, int line_number)
132 ts = new TokenStream (source_reader, null, source_location, line_number);
136 void MustMatchToken (int to_match, string message_id)
139 if ((tt = ts.GetToken ()) != to_match) {
140 ReportError (message_id);
141 ts.UnGetToken (tt); // in case the parser decides to continue
145 void ReportError (string message_id)
148 ts.ReportCurrentLineError (message_id);
149 throw new ParserException ();
154 decompiler = CreateDecompiler ();
155 current_script_or_fn = new ScriptBlock (new Location (ts.SourceName, ts.LineNumber));
156 decompiler.GetCurrentOffset ();
157 decompiler.AddToken (Token.SCRIPT);
162 ts.allow_reg_exp = true;
163 int tt = ts.GetToken ();
164 ts.allow_reg_exp = false;
170 if (tt == Token.FUNCTION) {
172 n = Function (current_script_or_fn, FunctionType.Statement);
173 } catch (ParserException e) {
179 n = Statement (current_script_or_fn);
181 current_script_or_fn.Add (n);
183 } catch (StackOverflowException ex) {
184 throw new Exception ("Error: too deep parser recursion.");
190 this.decompiler = null; // It helps GC
191 return current_script_or_fn;
195 get { return ts.EOF; }
198 bool InsideFunction {
199 get { return nesting_of_function != 0; }
202 Block ParseFunctionBody (AST parent)
204 ++nesting_of_function;
205 Block pn = new Block (parent, new Location (ts.SourceName, ts.LineNumber));
208 while ((tt = ts.PeekToken ()) > Token.EOF && tt != Token.RC) {
210 if (tt == Token.FUNCTION) {
212 n = Function (parent, FunctionType.Statement);
214 n = Statement (parent);
217 } catch (ParserException e) {
220 --nesting_of_function;
225 AST Function (AST parent, FunctionType ft)
227 FunctionType synthetic_type = ft;
229 AST member_expr = null;
231 if (ts.MatchToken (Token.NAME)) {
233 if (!ts.MatchToken (Token.LP)) {
234 if (allow_member_expr_as_function_name) {
235 // Extension to ECMA: if 'function <name>' does not follow
236 // by '(', assume <name> starts memberExpr
237 // FIXME: is StringLiteral the correct AST to build?
238 decompiler.AddName (name);
239 AST member_expr_head = new StringLiteral (null, name,
240 new Location (ts.SourceName, ts.LineNumber));
242 member_expr = MemberExprTail (parent, false, member_expr_head);
244 MustMatchToken (Token.LP, "msg.no.paren.parms");
246 } else if (ts.MatchToken (Token.LP)) {
247 // Anonymous function
251 if (allow_member_expr_as_function_name) {
252 // Note that memberExpr can not start with '(' like
253 // in function (1+2).toString(), because 'function (' already
254 // processed as anonymous function
255 member_expr = MemberExpr (parent, false);
257 MustMatchToken (Token.LP, "msg.no.paren.parms");
260 if (member_expr != null) {
261 synthetic_type = FunctionType.Expression;
262 decompiler.AddToken (Token.ASSIGN);
265 bool nested = InsideFunction;
266 Function fn = CreateFunction (parent, synthetic_type, name);
271 if (nested || nesting_of_with > 0) {
272 // 1. Nested functions are not affected by the dynamic scope flag
273 // as dynamic scope is already a parent of their scope.
274 // 2. Functions defined under the with statement also immune to
275 // this setup, in which case dynamic scope is ignored in favor
277 fn.IgnoreDynamicScope = true;
280 // FIXME: which is old version of Decompiler.MarkFunctionStart
281 int functionSourceStart = decompiler.MarkFunctionStart ((int) synthetic_type);
284 decompiler.AddName (name);
286 ScriptBlock saved_script_or_fn = current_script_or_fn;
287 int saved_nesting_of_with = nesting_of_with;
290 FormalParameterList _params = new FormalParameterList (new Location (ts.SourceName, ts.LineNumber));
294 decompiler.AddToken (Token.LP);
295 if (!ts.MatchToken (Token.RP)) {
299 decompiler.AddToken (Token.COMMA);
301 MustMatchToken (Token.NAME, "msg.no.parm");
302 string s = ts.GetString;
303 _params.Add (s, String.Empty, new Location (ts.SourceName, ts.LineNumber));
304 decompiler.AddName (s);
305 } while (ts.MatchToken (Token.COMMA));
306 MustMatchToken (Token.RP, "msg.no.paren.after.parms");
308 decompiler.AddToken (Token.RP);
310 MustMatchToken (Token.LC, "msg.no.brace.body");
311 decompiler.AddEOL (Token.LC);
312 body = ParseFunctionBody (fn);
313 MustMatchToken (Token.RC, "msg.no.brace.after.body");
315 decompiler.AddToken (Token.RC);
316 decompiler.MarkFunctionEnd (functionSourceStart);
318 fn.func_obj.source = decompiler.SourceToString (functionSourceStart);
320 if (ft != FunctionType.Expression) {
321 CheckWellTerminatedFunction ();
322 if (member_expr == null)
323 decompiler.AddToken (Token.EOL);
325 decompiler.AddEOL (Token.SEMI);
328 current_script_or_fn = saved_script_or_fn;
329 nesting_of_with = saved_nesting_of_with;
332 fn.Init (body, _params);
335 if (member_expr == null) {
339 // FIXME, research about createExprStatementNoReturn
340 if (ft == FunctionType.ExpressionStatement)
345 pn = new Assign (null, member_expr, pn, JSToken.Assign, false, new Location (ts.SourceName, ts.LineNumber));
346 // FIXME, research about createExprStatement
347 if (ft != FunctionType.Expression)
353 Function CreateFunction (AST parent, FunctionType func_type, string name)
356 Location location = new Location (ts.SourceName, ts.LineNumber);
358 if (func_type == FunctionType.Statement)
359 func = new FunctionDeclaration (parent, name, location);
360 else if (func_type == FunctionType.Expression)
361 func = new FunctionExpression (parent, name, location);
362 else if (func_type == FunctionType.ExpressionStatement)
363 throw new NotImplementedException ();
365 throw new Exception ("Unknown FunctionType");
369 AST Statements (AST parent)
372 Block pn = new Block (parent, new Location (ts.SourceName, ts.LineNumber));
373 while ((tt = ts.PeekToken ()) > Token.EOF && tt != Token.RC)
374 pn.Add (Statement (pn));
378 AST Condition (AST parent)
381 MustMatchToken (Token.LP, "msg.no.paren.cond");
382 decompiler.AddToken (Token.LP);
383 pn = Expr (parent, false);
384 MustMatchToken (Token.RP, "msg.no.paren.after.cond");
385 decompiler.AddToken (Token.RP);
389 void CheckWellTerminated ()
391 int tt = ts.PeekTokenSameLine ();
392 if (tt == Token.ERROR || tt == Token.EOF || tt == Token.EOL ||
393 tt == Token.SEMI || tt == Token.RC || tt == Token.FUNCTION)
395 ReportError ("msg.no.semi.stmt");
398 void CheckWellTerminatedFunction ()
400 CheckWellTerminated ();
405 int line_number = ts.LineNumber;
408 tt = ts.PeekTokenSameLine ();
409 if (tt == Token.NAME) {
411 label = ts.GetString;
414 if (line_number == ts.LineNumber)
415 CheckWellTerminated ();
420 AST Statement (AST parent)
423 return StatementHelper (parent);
424 } catch (ParserException e) {
425 // skip to end of statement
429 } while (t != Token.SEMI && t != Token.EOL &&
430 t != Token.EOF && t != Token.ERROR);
432 throw new Exception ("must create expr stm with ");
437 * Whether the "catch (e: e instanceof Exception) { ... }" syntax
441 AST StatementHelper (AST parent)
445 // If skipsemi == true, don't add SEMI + EOL to source at the
446 // end of this statment. For compound statements, IF/FOR etc.
447 bool skip_semi = false;
452 if (tt == Token.IF) {
455 decompiler.AddToken (Token.IF);
457 AST cond = Condition (parent);
459 decompiler.AddEOL (Token.LC);
461 AST if_true = Statement (parent);
464 if (ts.MatchToken (Token.ELSE)) {
465 decompiler.AddToken (Token.RC);
466 decompiler.AddToken (Token.ELSE);
467 decompiler.AddEOL (Token.LC);
468 if_false = Statement (parent);
470 decompiler.AddEOL (Token.RC);
471 pn = new If (parent, cond, if_true, if_false, new Location (ts.SourceName, ts.LineNumber));
472 } else if (tt == Token.SWITCH) {
475 decompiler.AddToken (Token.SWITCH);
477 pn = new Switch (parent, new Location (ts.SourceName, ts.LineNumber));
479 MustMatchToken (Token.LP, "msg.no.paren.switch");
481 decompiler.AddToken (Token.LP);
483 ((Switch) pn).exp = Expr (parent, false);
484 MustMatchToken (Token.RP, "msg.no.paren.after.switch");
486 decompiler.AddToken (Token.RP);
488 MustMatchToken (Token.LC, "msg.no.brace.switch");
490 decompiler.AddEOL (Token.LC);
492 ClauseType clause_type = ClauseType.Case;
494 while ((tt = ts.GetToken ()) != Token.RC && tt != Token.EOF) {
495 if (tt == Token.CASE) {
496 decompiler.AddToken (Token.CASE);
497 cur_case = new Clause (pn, new Location (ts.SourceName, ts.LineNumber));
498 cur_case.exp = Expr (pn, false);
499 decompiler.AddEOL (Token.COLON);
500 if (clause_type == ClauseType.Default)
501 clause_type = ClauseType.CaseAfterDefault;
502 } else if (tt == Token.DEFAULT) {
504 clause_type = ClauseType.Default;
505 decompiler.AddToken (Token.DEFAULT);
506 decompiler.AddEOL (Token.COLON);
509 ReportError ("msg.bad.switch");
511 MustMatchToken (Token.COLON, "msg.no.colon.case");
513 while ((tt = ts.PeekToken ()) != Token.RC && tt != Token.CASE && tt != Token.DEFAULT && tt != Token.EOF) {
514 if (clause_type == ClauseType.Case || clause_type == ClauseType.CaseAfterDefault)
515 cur_case.AddStm (Statement (pn));
516 else if (clause_type == ClauseType.Default)
517 ((Switch) pn).default_clauses.Add (Statement (pn));
519 ((Switch) pn).AddClause (cur_case, clause_type);
521 decompiler.AddEOL (Token.RC);
522 } else if (tt == Token.WHILE) {
524 decompiler.AddToken (Token.WHILE);
525 While w = new While (new Location (ts.SourceName, ts.LineNumber));
526 AST cond = Condition (w);
527 decompiler.AddEOL (Token.LC);
528 AST body = Statement (w);
529 decompiler.AddEOL (Token.RC);
530 w.Init (parent, cond, body);
532 } else if (tt == Token.DO) {
533 decompiler.AddToken (Token.DO);
534 decompiler.AddEOL (Token.LC);
535 int line_number = ts.LineNumber;
536 DoWhile do_while = new DoWhile (new Location (ts.SourceName, line_number));
537 AST body = Statement (do_while);
538 decompiler.AddToken (Token.RC);
539 MustMatchToken (Token.WHILE, "msg.no.while.do");
540 decompiler.AddToken (Token.WHILE);
541 AST cond = Condition (do_while);
542 do_while.Init (parent, body, cond);
544 } else if (tt == Token.FOR) {
546 decompiler.AddToken (Token.FOR);
547 AST init, cond, incr = null, body;
549 MustMatchToken (Token.LP, "msg.no.paren.for");
550 decompiler.AddToken (Token.LP);
551 tt = ts.PeekToken ();
553 if (tt == Token.SEMI)
556 if (tt == Token.VAR) {
557 // set init to a var list or initial
558 ts.GetToken (); // throw away the 'var' token
559 init = Variables (parent, true);
561 init = Expr (parent, true);
564 if (ts.MatchToken (Token.IN)) {
565 decompiler.AddToken (Token.IN);
566 cond = Expr (parent, false); // 'cond' is the object over which we're iterating
569 MustMatchToken (Token.SEMI, "msg.no.semi.for");
570 decompiler.AddToken (Token.SEMI);
572 if (ts.PeekToken () == Token.SEMI)
573 cond = null; // no loop condition
575 cond = Expr (parent, false);
577 MustMatchToken (Token.SEMI, "msg.no.semi.for.cond");
578 decompiler.AddToken (Token.SEMI);
580 if (ts.PeekToken () == Token.RP)
583 incr = Expr (parent, false);
586 MustMatchToken (Token.RP, "msg.no.paren.for.ctrl");
587 decompiler.AddToken (Token.RP);
588 decompiler.AddEOL (Token.LC);
589 body = Statement (pn);
590 decompiler.AddEOL (Token.RC);
592 if (incr == null) // cond could be null if 'in obj' got eaten by the init node.
593 pn = new ForIn (parent, init, cond, body, new Location (ts.SourceName, ts.LineNumber));
595 pn = new For (parent, init, cond, incr, body, new Location (ts.SourceName, ts.LineNumber));
597 } else if (tt == Token.TRY) {
598 int line_number = ts.LineNumber;
600 ArrayList catch_blocks = null;
601 AST finally_block = null;
604 decompiler.AddToken (Token.TRY);
605 decompiler.AddEOL (Token.LC);
607 try_block = Statement (parent);
608 decompiler.AddEOL (Token.RC);
609 catch_blocks = new ArrayList ();
611 bool saw_default_catch = false;
612 int peek = ts.PeekToken ();
614 if (peek == Token.CATCH) {
615 while (ts.MatchToken (Token.CATCH)) {
616 if (saw_default_catch)
617 ReportError ("msg.catch.unreachable");
618 decompiler.AddToken (Token.CATCH);
619 MustMatchToken (Token.LP, "msg.no.paren.catch");
620 decompiler.AddToken (Token.LP);
621 MustMatchToken (Token.NAME, "msg.bad.catchcond");
622 string var_name = ts.GetString;
623 decompiler.AddName (var_name);
624 AST catch_cond = null;
626 if (ts.MatchToken (Token.IF)) {
627 decompiler.AddToken (Token.IF);
628 catch_cond = Expr (parent, false);
630 saw_default_catch = true;
632 MustMatchToken (Token.RP, "msg.bad.catchcond");
633 decompiler.AddToken (Token.RP);
634 MustMatchToken (Token.LC, "msg.no.brace.catchblock");
635 decompiler.AddEOL (Token.LC);
637 catch_blocks.Add (new Catch (var_name, catch_cond,
638 Statements (null), parent, new Location (ts.SourceName, line_number)));
639 MustMatchToken (Token.RC, "msg.no.brace.after.body");
640 decompiler.AddEOL (Token.RC);
642 } else if (peek != Token.FINALLY)
643 MustMatchToken (Token.FINALLY, "msg.try.no.catchfinally");
645 if (ts.MatchToken (Token.FINALLY)) {
646 decompiler.AddToken (Token.FINALLY);
647 decompiler.AddEOL (Token.LC);
648 finally_block = Statement (parent);
649 decompiler.AddEOL (Token.RC);
651 pn = new Try (try_block, catch_blocks, finally_block, parent, new Location (ts.SourceName, ts.LineNumber));
652 } else if (tt == Token.THROW) {
653 int line_number = ts.LineNumber;
654 decompiler.AddToken (Token.THROW);
655 pn = new Throw (Expr (parent, false), new Location (ts.SourceName, ts.LineNumber));
657 if (line_number == ts.LineNumber)
658 CheckWellTerminated ();
659 } else if (tt == Token.BREAK) {
660 decompiler.AddToken (Token.BREAK);
662 // MatchLabel only matches if there is one
663 string label = MatchLabel ();
666 decompiler.AddName (label);
668 pn = new Break (parent, label, new Location (ts.SourceName, ts.LineNumber));
669 } else if (tt == Token.CONTINUE) {
670 decompiler.AddToken (Token.CONTINUE);
672 // MatchLabel only matches if there is one
673 string label = MatchLabel ();
676 decompiler.AddName (label);
678 pn = new Continue (parent, label, new Location (ts.SourceName, ts.LineNumber));
679 } else if (tt == Token.WITH) {
681 decompiler.AddToken (Token.WITH);
682 MustMatchToken (Token.LP, "msg.no.paren.with");
683 decompiler.AddToken (Token.LP);
684 AST obj = Expr (parent, false);
685 MustMatchToken (Token.RP, "msg.no.paren.after.with");
686 decompiler.AddToken (Token.RP);
687 decompiler.AddToken (Token.LC);
691 body = Statement (parent);
695 decompiler.AddEOL (Token.RC);
696 pn = new With (parent, obj, body, new Location (ts.SourceName, ts.LineNumber));
697 } else if (tt == Token.VAR) {
698 int line_number = ts.LineNumber;
699 pn = Variables (parent, false);
700 if (ts.LineNumber == line_number)
701 CheckWellTerminated ();
702 } else if (tt == Token.RETURN) {
704 decompiler.AddToken (Token.RETURN);
705 pn = new Return (new Location (ts.SourceName, ts.LineNumber));
708 ReportError ("msg.bad.return");
710 /* This is ugly, but we don't want to require a semicolon. */
711 ts.allow_reg_exp = true;
712 tt = ts.PeekTokenSameLine ();
713 ts.allow_reg_exp = false;
715 int line_number = ts.LineNumber;
716 if (tt != Token.EOF && tt != Token.EOL && tt != Token.SEMI && tt != Token.RC) {
717 ret_expr = Expr (pn, false);
718 if (ts.LineNumber == line_number)
719 CheckWellTerminated ();
721 ((Return) pn).Init (parent, ret_expr);
722 } else if (tt == Token.LC) {
724 pn = Statements (parent);
725 MustMatchToken (Token.RC, "msg.no.brace.block");
726 } else if (tt == Token.ERROR || tt == Token.EOL || tt == Token.SEMI) {
730 } else if (tt == Token.FUNCTION) {
731 pn = Function (parent, FunctionType.ExpressionStatement);
733 int last_expr_type = tt;
734 int token_number = ts.TokenNumber;
736 int line_number = ts.LineNumber;
738 pn = Expr (parent, false);
740 if (ts.PeekToken () == Token.COLON) {
741 /* check that the last thing the tokenizer returned was a
742 * NAME and that only one token was consumed.
744 if (last_expr_type != Token.NAME || (ts.TokenNumber != token_number))
745 ReportError ("msg.bad.label");
747 ts.GetToken (); // eat the colon
749 string name = ts.GetString;
751 // bind 'Statement (pn)' to the label
752 Labelled labelled = new Labelled (parent, new Location (ts.SourceName, ts.LineNumber));
753 labelled.Init (parent, name, Statement (labelled), new Location (ts.SourceName, ts.LineNumber));
755 // depend on decompiling lookahead to guess that that
756 // last name was a label.
757 decompiler.AddEOL (Token.COLON);
761 // pn = nf.createExprStatement(pn, lineno);
762 if (ts.LineNumber == line_number)
763 CheckWellTerminated ();
765 ts.MatchToken (Token.SEMI);
768 decompiler.AddEOL (Token.SEMI);
773 AST Variables (AST parent, bool in_for_init)
775 VariableStatement pn = new VariableStatement (parent, new Location (ts.SourceName, ts.LineNumber));
777 decompiler.AddToken (Token.VAR);
780 VariableDeclaration name;
782 MustMatchToken (Token.NAME, "msg.bad.var");
783 string s = ts.GetString;
786 decompiler.AddToken (Token.COMMA);
789 decompiler.AddName (s);
790 name = new VariableDeclaration (parent, s, null, null, new Location (ts.SourceName, ts.LineNumber));
792 // ommited check for argument hiding
793 if (ts.MatchToken (Token.ASSIGN)) {
794 decompiler.AddToken (Token.ASSIGN);
795 init = AssignExpr (parent, in_for_init);
800 if (!ts.MatchToken (Token.COMMA))
806 AST Expr (AST parent, bool in_for_init)
808 Expression pn = new Expression (parent, new Location (ts.SourceName, ts.LineNumber));
809 AST init = AssignExpr (parent, in_for_init);
813 throw new Exception ("Expr, L680, AST is null");
815 while (ts.MatchToken (Token.COMMA)) {
816 decompiler.AddToken (Token.COMMA);
817 pn.Add (AssignExpr (parent, in_for_init));
822 AST AssignExpr (AST parent, bool in_for_init)
824 AST pn = CondExpr (parent, in_for_init);
825 int tt = ts.PeekToken ();
827 // omitted: "invalid assignment left-hand side" check.
828 if (tt == Token.ASSIGN) {
830 decompiler.AddToken (Token.ASSIGN);
831 pn = new Assign (parent, pn, AssignExpr (parent, in_for_init), JSToken.Assign, false, new Location (ts.SourceName, ts.LineNumber));
833 } else if (tt == Token.ASSIGNOP) {
835 int op = ts.GetOp ();
836 decompiler.AddAssignOp (op);
837 pn = new Assign (parent, pn, AssignExpr (parent, in_for_init), ToJSToken (op, tt), false,
838 new Location (ts.SourceName, ts.LineNumber));
843 AST CondExpr (AST parent, bool in_for_init)
847 AST pn = OrExpr (parent, in_for_init);
849 if (ts.MatchToken (Token.HOOK)) {
850 decompiler.AddToken (Token.HOOK);
851 if_true = AssignExpr (parent, false);
852 MustMatchToken (Token.COLON, "msg.no.colon.cond");
853 decompiler.AddToken (Token.COLON);
854 if_false = AssignExpr (parent, in_for_init);
855 return new Conditional (parent, pn, if_true, if_false, new Location (ts.SourceName, ts.LineNumber));
860 AST OrExpr (AST parent, bool in_for_init)
862 AST pn = AndExpr (parent, in_for_init);
863 if (ts.MatchToken (Token.OR)) {
864 decompiler.AddToken (Token.OR);
865 return new Binary (parent, pn, OrExpr (parent, in_for_init), JSToken.LogicalOr,
866 new Location (ts.SourceName, ts.LineNumber));
871 AST AndExpr (AST parent, bool in_for_init)
873 AST pn = BitOrExpr (parent, in_for_init);
874 if (ts.MatchToken (Token.AND)) {
875 decompiler.AddToken (Token.AND);
876 return new Binary (parent, pn, AndExpr (parent, in_for_init), JSToken.LogicalAnd,
877 new Location (ts.SourceName, ts.LineNumber));
882 AST BitOrExpr (AST parent, bool in_for_init)
884 AST pn = BitXorExpr (parent, in_for_init);
885 while (ts.MatchToken (Token.BITOR)) {
886 decompiler.AddToken (Token.BITOR);
887 pn = new Binary (parent, pn, BitXorExpr (parent, in_for_init), JSToken.BitwiseOr,
888 new Location (ts.SourceName, ts.LineNumber));
893 AST BitXorExpr (AST parent, bool in_for_init)
895 AST pn = BitAndExpr (parent, in_for_init);
896 while (ts.MatchToken (Token.BITXOR)) {
897 decompiler.AddToken (Token.BITXOR);
898 pn = new Binary (parent, pn, BitAndExpr (parent, in_for_init), JSToken.BitwiseXor,
899 new Location (ts.SourceName, ts.LineNumber));
904 AST BitAndExpr (AST parent, bool in_for_init)
906 AST pn = EqExpr (parent, in_for_init);
907 while (ts.MatchToken (Token.BITAND)) {
908 decompiler.AddToken (Token.BITAND);
909 pn = new Binary (parent, pn, EqExpr (parent, in_for_init), JSToken.BitwiseAnd,
910 new Location (ts.SourceName, ts.LineNumber));
915 AST EqExpr (AST parent, bool in_for_init)
917 AST pn = RelExpr (parent, in_for_init);
918 ArrayList tokens = new ArrayList ();
920 int tt = ts.PeekToken ();
922 if (tt == Token.EQ || tt == Token.NE) {
923 foreach (int token in tokens)
924 decompiler.AddToken (token);
928 pn = new Equality (parent, pn, RelExpr (parent, in_for_init), ToJSToken (tt),
929 new Location (ts.SourceName, ts.LineNumber));
931 } else if (tt == Token.SHEQ || tt == Token.SHNE) {
932 foreach (int token in tokens)
933 decompiler.AddToken (token);
937 pn = new StrictEquality (parent, pn, RelExpr (parent, in_for_init), ToJSToken (tt), new Location (ts.SourceName, ts.LineNumber));
945 AST RelExpr (AST parent, bool in_for_init)
947 AST pn = ShiftExpr (parent);
949 int tt = ts.PeekToken ();
950 if (tt == Token.IN) {
955 decompiler.AddToken (tt);
956 pn = new Relational (parent, pn, ShiftExpr (parent), ToJSToken (tt), new Location (ts.SourceName, ts.LineNumber));
959 } else if (tt == Token.INSTANCEOF || tt == Token.LE || tt == Token.LT || tt == Token.GE || tt == Token.GT) {
961 decompiler.AddToken (tt);
962 pn = new Relational (parent, pn, ShiftExpr (parent), ToJSToken (tt), new Location (ts.SourceName, ts.LineNumber));
970 AST ShiftExpr (AST parent)
972 AST pn = AddExpr (parent);
974 int tt = ts.PeekToken ();
975 if (tt == Token.LSH || tt == Token.URSH || tt == Token.RSH) {
977 decompiler.AddToken (tt);
979 JSToken op = JSToken.LeftShift;
981 op = JSToken.RightShift;
982 else if (tt == Token.URSH)
983 op = JSToken.UnsignedRightShift;
985 pn = new Binary (parent, pn, AddExpr (parent), op,
986 new Location (ts.SourceName, ts.LineNumber));
994 AST AddExpr (AST parent)
996 AST pn = MulExpr (parent);
998 int tt = ts.PeekToken ();
999 if (tt == Token.ADD || tt == Token.SUB) {
1001 decompiler.AddToken (tt);
1002 pn = new Binary (parent, pn, MulExpr (parent), ToJSToken (tt),
1003 new Location (ts.SourceName, ts.LineNumber));
1011 AST MulExpr (AST parent)
1013 AST pn = UnaryExpr (parent);
1015 int tt = ts.PeekToken ();
1016 if (tt == Token.MUL || tt == Token.DIV || tt == Token.MOD) {
1018 decompiler.AddToken (tt);
1019 pn = new Binary (parent, pn, UnaryExpr (parent), ToJSToken (tt),
1020 new Location (ts.SourceName, ts.LineNumber));
1028 AST UnaryExpr (AST parent)
1032 ts.allow_reg_exp = true;
1033 tt = ts.GetToken ();
1034 ts.allow_reg_exp = false;
1036 if (tt == Token.VOID || tt == Token.NOT || tt == Token.BITNOT || tt == Token.TYPEOF ||
1037 tt == Token.ADD || tt == Token.SUB || tt == Token.DELPROP) {
1038 if (tt == Token.VOID || tt == Token.NOT || tt == Token.BITNOT || tt == Token.TYPEOF)
1039 decompiler.AddToken (tt);
1040 else if (tt == Token.ADD)
1041 decompiler.AddToken (Token.POS);
1042 else if (tt == Token.SUB)
1043 decompiler.AddToken (Token.NEG);
1045 decompiler.AddToken (tt);
1047 Unary u = new Unary (parent, ToJSToken (tt), new Location (ts.SourceName, ts.LineNumber));
1048 u.operand = UnaryExpr (u);
1050 } else if (tt == Token.INC || tt == Token.DEC) {
1051 decompiler.AddToken (tt);
1052 return new PostOrPrefixOperator (parent, MemberExpr (parent, true), ToJSToken (tt), true,
1053 new Location (ts.SourceName, ts.LineNumber));
1054 } else if (tt == Token.ERROR) {
1058 int line_number = ts.LineNumber;
1060 AST pn = MemberExpr (parent, true);
1062 /* don't look across a newline boundary for a postfix incop.
1064 * the rhino scanner seems to work differently than the js
1065 * scanner here; in js, it works to have the line number check
1066 * precede the peekToken calls. It'd be better if they had
1067 * similar behavior...
1070 if (((peeked = ts.PeekToken ()) == Token.INC || peeked == Token.DEC) && ts.LineNumber == line_number) {
1071 int pf = ts.GetToken ();
1072 decompiler.AddToken (pf);
1073 return new PostOrPrefixOperator (parent, pn, ToJSToken (peeked), false,
1074 new Location (ts.SourceName, ts.LineNumber));
1078 return new StringLiteral (null, "Error", new Location (ts.SourceName, ts.LineNumber)); // Only reached on error. Try to continue.
1081 JSToken ToJSToken (int tt)
1083 if (tt == Token.DELPROP)
1084 return JSToken.Delete;
1085 else if (tt == Token.VOID)
1086 return JSToken.Void;
1087 else if (tt == Token.TYPEOF)
1088 return JSToken.Typeof;
1089 else if (tt == Token.INC)
1090 return JSToken.Increment;
1091 else if (tt == Token.DEC)
1092 return JSToken.Decrement;
1093 else if (tt == Token.ADD)
1094 return JSToken.Plus;
1095 else if (tt == Token.SUB)
1096 return JSToken.Minus;
1097 else if (tt == Token.BITNOT)
1098 return JSToken.BitwiseNot;
1099 else if (tt == Token.NOT)
1100 return JSToken.LogicalNot;
1101 else if (tt == Token.EQ)
1102 return JSToken.Equal;
1103 else if (tt == Token.NE)
1104 return JSToken.NotEqual;
1105 else if (tt == Token.SHEQ)
1106 return JSToken.StrictEqual;
1107 else if (tt == Token.SHNE)
1108 return JSToken.StrictNotEqual;
1109 else if (tt == Token.MUL)
1110 return JSToken.Multiply;
1111 else if (tt == Token.DIV)
1112 return JSToken.Divide;
1113 else if (tt == Token.MOD)
1114 return JSToken.Modulo;
1115 else if (tt == Token.IN)
1117 else if (tt == Token.INSTANCEOF)
1118 return JSToken.Instanceof;
1119 else if (tt == Token.LE)
1120 return JSToken.LessThanEqual;
1121 else if (tt == Token.LT)
1122 return JSToken.LessThan;
1123 else if (tt == Token.GE)
1124 return JSToken.GreaterThanEqual;
1125 else if (tt == Token.GT)
1126 return JSToken.GreaterThan;
1128 throw new NotImplementedException ();
1132 // Takes care of +=, -=
1134 JSToken ToJSToken (int left, int right)
1136 if (right == Token.ASSIGNOP) {
1137 if (left == Token.ADD)
1138 return JSToken.PlusAssign;
1139 else if (left == Token.SUB)
1140 return JSToken.MinusAssign;
1141 else if (left == Token.MUL)
1142 return JSToken.MultiplyAssign;
1143 else if (left == Token.DIV)
1144 return JSToken.DivideAssign;
1145 else if (left == Token.BITAND)
1146 return JSToken.BitwiseAndAssign;
1147 else if (left == Token.BITOR)
1148 return JSToken.BitwiseOrAssign;
1149 else if (left == Token.BITXOR)
1150 return JSToken.BitwiseXorAssign;
1151 else if (left == Token.MOD)
1152 return JSToken.ModuloAssign;
1153 else if (left == Token.LSH)
1154 return JSToken.LeftShiftAssign;
1155 else if (left == Token.RSH)
1156 return JSToken.RightShiftAssign;
1157 else if (left == Token.URSH)
1158 return JSToken.UnsignedRightShiftAssign;
1160 throw new NotImplementedException ();
1163 void ArgumentList (AST parent, ICallable list)
1166 ts.allow_reg_exp = true;
1167 matched = ts.MatchToken (Token.RP);
1168 ts.allow_reg_exp = false;
1174 decompiler.AddToken (Token.COMMA);
1176 list.AddArg (AssignExpr (parent, false));
1177 } while (ts.MatchToken (Token.COMMA));
1178 MustMatchToken (Token.RP, "msg.no.paren.arg");
1180 decompiler.AddToken (Token.RP);
1183 AST MemberExpr (AST parent, bool allow_call_syntax)
1188 /* Check for new expressions. */
1189 ts.allow_reg_exp = true;
1190 tt = ts.PeekToken ();
1191 ts.allow_reg_exp = false;
1193 if (tt == Token.NEW) {
1194 /* Eat the NEW token. */
1196 decompiler.AddToken (Token.NEW);
1198 /* Make a NEW node to append to. */
1199 pn = new New (parent, MemberExpr (parent, false), new Location (ts.SourceName, ts.LineNumber));
1201 if (ts.MatchToken (Token.LP)) {
1202 decompiler.AddToken (Token.LP);
1203 ArgumentList (parent, (ICallable) pn);
1206 /* XXX there's a check in the C source against
1207 * "too many constructor arguments" - how many
1208 * do we claim to support?
1211 /* Experimental syntax: allow an object literal to follow a new expression,
1212 * which will mean a kind of anonymous class built with the JavaAdapter.
1213 * the object literal will be passed as an additional argument to the constructor.
1216 tt = ts.PeekToken ();
1218 pn = PrimaryExpr (parent);
1220 pn = PrimaryExpr (parent);
1221 return MemberExprTail (pn, allow_call_syntax, pn);
1224 AST MemberExprTail (AST parent, bool allow_call_syntax, AST pn)
1228 while ((tt = ts.GetToken ()) > Token.EOF) {
1229 if (tt == Token.DOT) {
1230 decompiler.AddToken (Token.DOT);
1231 MustMatchToken (Token.NAME, "msg.no.name.after.dot");
1232 string s = ts.GetString;
1233 decompiler.AddName (s);
1234 // FIXME: is 'new Identifier' appropriate here?
1235 pn = new Binary (parent, pn,
1236 new Identifier (parent, ts.GetString, new Location (ts.SourceName, ts.LineNumber)),
1237 JSToken.AccessField, new Location (ts.SourceName, ts.LineNumber));
1238 } else if (tt == Token.LB) {
1239 decompiler.AddToken (Token.LB);
1240 Binary b = new Binary (parent, pn, JSToken.LeftBracket,
1241 new Location (ts.SourceName, ts.LineNumber));
1242 b.right = Expr (b, false);
1244 MustMatchToken (Token.RB, "msg.no.bracket.index");
1245 decompiler.AddToken (Token.RB);
1246 } else if (allow_call_syntax && tt == Token.LP) {
1247 /* make a call node */
1248 decompiler.AddToken (Token.LP);
1249 pn = new Call (parent, pn, new Location (ts.SourceName, ts.LineNumber));
1251 /* Add the arguments to pn, if any are supplied. */
1252 ArgumentList (parent, (ICallable) pn);
1261 AST PrimaryExpr (AST parent)
1266 ts.allow_reg_exp = true;
1267 tt = ts.GetToken ();
1268 ts.allow_reg_exp = false;
1270 if (tt == Token.FUNCTION) {
1271 return Function (parent, FunctionType.Expression);
1272 } else if (tt == Token.LB) {
1273 ASTList elems = new ASTList (parent, new Location (ts.SourceName, ts.LineNumber));
1275 decompiler.AddToken (Token.LB);
1276 bool after_lb_or_comma = true;
1278 ts.allow_reg_exp = true;
1279 tt = ts.PeekToken ();
1280 ts.allow_reg_exp = false;
1282 if (tt == Token.COMMA) {
1284 decompiler.AddToken (Token.COMMA);
1285 if (!after_lb_or_comma)
1286 after_lb_or_comma = true;
1291 } else if (tt == Token.RB) {
1293 decompiler.AddToken (Token.RB);
1296 if (!after_lb_or_comma)
1297 ReportError ("msg.no.bracket.arg");
1298 elems.Add (AssignExpr (parent, false));
1299 after_lb_or_comma = false;
1302 // FIXME: pass a real Context
1303 return new ArrayLiteral (null, elems, skip_count, new Location (ts.SourceName, ts.LineNumber));
1304 } else if (tt == Token.LC) {
1305 Location location = new Location (ts.SourceName, ts.LineNumber);
1306 ArrayList elems = new ArrayList ();
1307 decompiler.AddToken (Token.LC);
1309 if (!ts.MatchToken (Token.RC)) {
1314 ObjectLiteralItem property;
1317 decompiler.AddToken (Token.COMMA);
1321 tt = ts.GetToken ();
1323 if (tt == Token.NAME || tt == Token.STRING) {
1324 string s = ts.GetString;
1325 if (tt == Token.NAME)
1326 decompiler.AddName (s);
1328 decompiler.AddString (s);
1329 property = new ObjectLiteralItem (s);
1330 } else if (tt == Token.NUMBER) {
1331 double n = ts.GetNumber;
1332 decompiler.AddNumber (n);
1333 property = new ObjectLiteralItem (n);
1334 } else if (tt == Token.RC) {
1335 // trailing comma is OK
1337 goto leave_commaloop;
1339 ReportError ("msg.bad.prop");
1340 goto leave_commaloop;
1342 MustMatchToken (Token.COLON, "msg.no.colon.prop");
1343 // OBJLIT is used as ':' in object literal for
1344 // decompilation to solve spacing ambiguity.
1345 decompiler.AddToken (Token.OBJECTLIT);
1346 property.exp = AssignExpr (parent, false);
1347 elems.Add (property);
1348 } while (ts.MatchToken (Token.COMMA));
1349 MustMatchToken (Token.RC, "msg.no.brace.prop");
1354 return new ObjectLiteral (elems, location);
1355 } else if (tt == Token.LP) {
1356 decompiler.AddToken (Token.LP);
1357 pn = Expr (parent, false);
1358 decompiler.AddToken (Token.RP);
1359 MustMatchToken (Token.RP, "msg.no.paren");
1360 decompiler.AddToken (Token.RP);
1362 } else if (tt == Token.NAME) {
1363 string name = ts.GetString;
1364 decompiler.AddName (name);
1365 return new Identifier (parent, name, new Location (ts.SourceName, ts.LineNumber));
1366 } else if (tt == Token.NUMBER) {
1367 double n = ts.GetNumber;
1368 decompiler.AddNumber (n);
1369 return new NumericLiteral (parent, n, new Location (ts.SourceName, ts.LineNumber));
1370 } else if (tt == Token.STRING) {
1371 string s = ts.GetString;
1372 decompiler.AddString (s);
1373 return new StringLiteral (null, s, new Location (ts.SourceName, ts.LineNumber));
1374 } else if (tt == Token.REGEXP) {
1375 string flags = ts.reg_exp_flags;
1376 ts.reg_exp_flags = null;
1377 string re = ts.GetString;
1378 decompiler.AddRegexp (re, flags);
1379 return new RegExpLiteral (parent, re, flags, new Location (ts.SourceName, ts.LineNumber));
1380 } else if (tt == Token.NULL) {
1381 decompiler.AddToken (tt);
1382 // FIXME, build the null object;
1384 } else if (tt == Token.THIS) {
1385 decompiler.AddToken (tt);
1386 return new This (parent, new Location (ts.SourceName, ts.LineNumber));
1387 } else if (tt == Token.FALSE || tt == Token.TRUE) {
1388 decompiler.AddToken (tt);
1390 if (tt == Token.FALSE)
1394 return new BooleanLiteral (null, v, new Location (ts.SourceName, ts.LineNumber));
1395 } else if (tt == Token.RESERVED) {
1396 ReportError ("msg.reserved.id");
1397 } else if (tt == Token.ERROR) {
1398 /* the scanner or one of its subroutines reported the error. */
1400 ReportError ("msg.syntax");
1401 return null; // should never reach here