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;
38 namespace Microsoft.JScript {
41 * There are three types of functions that can be defined. The first
42 * is a function statement. This is a function appearing as a top-level
43 * statement (i.e., not nested inside some other statement) in either a
44 * script or a function.
46 * The second is a function expression, which is a function appearing in
47 * an expression except for the third type, which is...
49 * The third type is a function expression where the expression is the
50 * top-level expression in an expression statement.
52 * The three types of functions have different treatment and must be
67 internal class Location {
68 private string source_name;
69 private int line_number;
71 internal string SourceName {
72 get { return source_name; }
75 internal int LineNumber {
76 get { return line_number; }
79 internal Location (string source_name, int line_number)
81 this.source_name = source_name;
82 this.line_number = line_number;
86 internal class Parser {
89 bool ok; // did the parse encounter an error?
90 int nesting_of_function;
92 bool allow_member_expr_as_function_name;
93 Decompiler decompiler;
100 internal Parser (ArrayList code_items)
102 this.code_items = code_items;
105 internal ScriptBlock [] ParseAll ()
107 int i = 0, n = code_items.Count;
108 ScriptBlock [] blocks = new ScriptBlock [n];
110 foreach (VsaCodeItem item in code_items)
111 blocks [i++] = Parse (item.SourceText, item.Name, 0);
116 internal Decompiler CreateDecompiler ()
118 return new Decompiler ();
122 /// Test if n is between the range stablished by min and max
124 private bool InRangeOf (double n, double min, double max)
126 return min <= n && n <= max;
129 private bool HasNoDecimals (double v)
131 return Math.Round (v) == v;
135 /// Build a parse tree from a given source_string
139 /// return an ScriptBlock representing the parsed program
140 /// that corresponds to a source file.
141 /// If the parse fails, null will be returned.
143 internal ScriptBlock Parse (string source_string, string source_location, int line_number)
145 ts = new TokenStream (null, source_string, source_location, line_number);
148 } catch (IOException ex) {
149 throw new Exception ("Illegal state exception");
154 /// Build a parse tree from a given source_reader
158 /// return an AST representing the parsed program.
159 /// If the parse fails, null will be returned.
161 internal AST Parse (StreamReader source_reader, string source_location, int line_number)
163 ts = new TokenStream (source_reader, null, source_location, line_number);
167 void MustMatchToken (int to_match, string message_id)
170 if ((tt = ts.GetToken ()) != to_match) {
171 ReportError (message_id);
172 ts.UnGetToken (tt); // in case the parser decides to continue
176 void ReportError (string message_id)
179 ts.ReportCurrentLineError (message_id);
180 throw new ParserException ();
185 decompiler = CreateDecompiler ();
186 ScriptBlock current_script_or_fn = new ScriptBlock (new Location (ts.SourceName, ts.LineNumber));
187 decompiler.GetCurrentOffset ();
188 decompiler.AddToken (Token.SCRIPT);
193 ts.allow_reg_exp = true;
194 int tt = ts.GetToken ();
195 ts.allow_reg_exp = false;
201 if (tt == Token.FUNCTION) {
203 n = Function (current_script_or_fn, FunctionType.Statement);
204 } catch (ParserException e) {
210 n = Statement (current_script_or_fn);
212 current_script_or_fn.Add (n);
214 } catch (StackOverflowException ex) {
215 throw new Exception ("Error: too deep parser recursion.");
221 this.decompiler = null; // It helps GC
222 return current_script_or_fn;
226 get { return ts.EOF; }
229 bool InsideFunction {
230 get { return nesting_of_function != 0; }
233 Block ParseFunctionBody (AST parent)
235 ++nesting_of_function;
236 Block pn = new Block (parent, new Location (ts.SourceName, ts.LineNumber));
240 while ((tt = ts.PeekToken ()) > Token.EOF && tt != Token.RC) {
242 if (tt == Token.FUNCTION) {
244 n = Function (parent, FunctionType.Statement);
246 n = Statement (parent);
249 } catch (ParserException e) {
252 --nesting_of_function;
257 AST Function (AST parent, FunctionType ft)
259 FunctionType synthetic_type = ft;
261 AST member_expr = null;
263 if (ts.MatchToken (Token.NAME)) {
265 if (!ts.MatchToken (Token.LP)) {
266 if (allow_member_expr_as_function_name) {
267 // Extension to ECMA: if 'function <name>' does not follow
268 // by '(', assume <name> starts memberExpr
269 // FIXME: is StringLiteral the correct AST to build?
270 decompiler.AddName (name);
271 AST member_expr_head = new StringLiteral (null, name,
272 new Location (ts.SourceName, ts.LineNumber));
274 member_expr = MemberExprTail (parent, false, member_expr_head);
276 MustMatchToken (Token.LP, "msg.no.paren.parms");
278 } else if (ts.MatchToken (Token.LP)) {
279 // Anonymous function
283 if (allow_member_expr_as_function_name) {
284 // Note that memberExpr can not start with '(' like
285 // in function (1+2).toString(), because 'function (' already
286 // processed as anonymous function
287 member_expr = MemberExpr (parent, false);
289 MustMatchToken (Token.LP, "msg.no.paren.parms");
292 if (member_expr != null) {
293 synthetic_type = FunctionType.Expression;
294 decompiler.AddToken (Token.ASSIGN);
297 bool nested = InsideFunction;
298 Function fn = CreateFunction (parent, synthetic_type, name);
303 if (nested || nesting_of_with > 0) {
304 // 1. Nested functions are not affected by the dynamic scope flag
305 // as dynamic scope is already a parent of their scope.
306 // 2. Functions defined under the with statement also immune to
307 // this setup, in which case dynamic scope is ignored in favor
309 fn.IgnoreDynamicScope = true;
312 // FIXME: which is old version of Decompiler.MarkFunctionStart
313 int functionSourceStart = decompiler.MarkFunctionStart ((int) synthetic_type);
316 decompiler.AddName (name);
318 int saved_nesting_of_with = nesting_of_with;
321 FormalParameterList _params = new FormalParameterList (new Location (ts.SourceName, ts.LineNumber));
325 decompiler.AddToken (Token.LP);
326 if (!ts.MatchToken (Token.RP)) {
330 decompiler.AddToken (Token.COMMA);
332 MustMatchToken (Token.NAME, "msg.no.parm");
333 string s = ts.GetString;
334 _params.Add (s, String.Empty, new Location (ts.SourceName, ts.LineNumber));
335 decompiler.AddName (s);
336 } while (ts.MatchToken (Token.COMMA));
337 MustMatchToken (Token.RP, "msg.no.paren.after.parms");
339 decompiler.AddToken (Token.RP);
341 MustMatchToken (Token.LC, "msg.no.brace.body");
342 decompiler.AddEOL (Token.LC);
343 body = ParseFunctionBody (fn);
344 MustMatchToken (Token.RC, "msg.no.brace.after.body");
346 decompiler.AddToken (Token.RC);
347 decompiler.MarkFunctionEnd (functionSourceStart);
349 fn.func_obj.source = decompiler.SourceToString (functionSourceStart);
351 if (ft != FunctionType.Expression) {
352 CheckWellTerminatedFunction ();
353 if (member_expr == null)
354 decompiler.AddToken (Token.EOL);
356 decompiler.AddEOL (Token.SEMI);
359 nesting_of_with = saved_nesting_of_with;
362 fn.Init (body, _params);
365 if (member_expr == null) {
369 // FIXME, research about createExprStatementNoReturn
370 if (ft == FunctionType.ExpressionStatement)
375 pn = new Assign (null, member_expr, pn, JSToken.Assign, false, new Location (ts.SourceName, ts.LineNumber));
377 // FIXME, research about createExprStatement
378 if (ft != FunctionType.Expression)
384 Function CreateFunction (AST parent, FunctionType func_type, string name)
387 Location location = new Location (ts.SourceName, ts.LineNumber);
389 if (func_type == FunctionType.Statement)
390 func = new FunctionDeclaration (parent, name, location);
391 else if (func_type == FunctionType.Expression)
392 func = new FunctionExpression (parent, name, location);
393 else if (func_type == FunctionType.ExpressionStatement)
394 throw new NotImplementedException ();
396 throw new Exception ("Unknown FunctionType");
400 AST Statements (AST parent)
403 Block pn = new Block (parent, new Location (ts.SourceName, ts.LineNumber));
404 while ((tt = ts.PeekToken ()) > Token.EOF && tt != Token.RC)
405 pn.Add (Statement (pn));
409 AST Condition (AST parent)
412 MustMatchToken (Token.LP, "msg.no.paren.cond");
413 decompiler.AddToken (Token.LP);
414 pn = Expr (parent, false);
415 MustMatchToken (Token.RP, "msg.no.paren.after.cond");
416 decompiler.AddToken (Token.RP);
420 AST Import (AST parent)
422 System.Text.StringBuilder @namespace = new System.Text.StringBuilder ();
425 MustMatchToken (Token.NAME, "msg.bad.namespace.name");
426 @namespace.Append (ts.GetString);
427 if (ts.MatchToken (Token.DOT))
428 @namespace.Append (".");
432 return new Import (parent, @namespace.ToString (), new Location (ts.SourceName, ts.LineNumber));
435 void CheckWellTerminated ()
437 int tt = ts.PeekTokenSameLine ();
438 if (tt == Token.ERROR || tt == Token.EOF || tt == Token.EOL ||
439 tt == Token.SEMI || tt == Token.RC || tt == Token.FUNCTION)
441 ReportError ("msg.no.semi.stmt");
444 void CheckWellTerminatedFunction ()
446 CheckWellTerminated ();
451 int line_number = ts.LineNumber;
454 tt = ts.PeekTokenSameLine ();
455 if (tt == Token.NAME) {
457 label = ts.GetString;
460 if (line_number == ts.LineNumber)
461 CheckWellTerminated ();
466 AST Statement (AST parent)
469 return StatementHelper (parent);
470 } catch (ParserException e) {
471 // skip to end of statement
475 } while (t != Token.SEMI && t != Token.EOL &&
476 t != Token.EOF && t != Token.ERROR);
478 throw new Exception ("must create expr stm with ");
483 * Whether the "catch (e: e instanceof Exception) { ... }" syntax
487 AST StatementHelper (AST parent)
491 // If skipsemi == true, don't add SEMI + EOL to source at the
492 // end of this statment. For compound statements, IF/FOR etc.
493 bool skip_semi = false;
498 if (tt == Token.IF) {
500 decompiler.AddToken (Token.IF);
501 AST cond = Condition (parent);
503 decompiler.AddEOL (Token.LC);
505 AST if_true = Statement (parent);
508 if (ts.MatchToken (Token.ELSE)) {
509 decompiler.AddToken (Token.RC);
510 decompiler.AddToken (Token.ELSE);
511 decompiler.AddEOL (Token.LC);
512 if_false = Statement (parent);
514 decompiler.AddEOL (Token.RC);
515 pn = new If (parent, cond, if_true, if_false, new Location (ts.SourceName, ts.LineNumber));
516 } else if (tt == Token.SWITCH) {
519 decompiler.AddToken (Token.SWITCH);
521 pn = new Switch (parent, new Location (ts.SourceName, ts.LineNumber));
523 MustMatchToken (Token.LP, "msg.no.paren.switch");
525 decompiler.AddToken (Token.LP);
527 ((Switch) pn).exp = Expr (parent, false);
528 MustMatchToken (Token.RP, "msg.no.paren.after.switch");
530 decompiler.AddToken (Token.RP);
532 MustMatchToken (Token.LC, "msg.no.brace.switch");
534 decompiler.AddEOL (Token.LC);
536 ClauseType clause_type = ClauseType.Case;
538 while ((tt = ts.GetToken ()) != Token.RC && tt != Token.EOF) {
539 if (tt == Token.CASE) {
540 decompiler.AddToken (Token.CASE);
541 cur_case = new Clause (pn, new Location (ts.SourceName, ts.LineNumber));
542 cur_case.exp = Expr (pn, false);
543 decompiler.AddEOL (Token.COLON);
544 if (clause_type == ClauseType.Default)
545 clause_type = ClauseType.CaseAfterDefault;
546 } else if (tt == Token.DEFAULT) {
548 clause_type = ClauseType.Default;
549 decompiler.AddToken (Token.DEFAULT);
550 decompiler.AddEOL (Token.COLON);
553 ReportError ("msg.bad.switch");
555 MustMatchToken (Token.COLON, "msg.no.colon.case");
557 while ((tt = ts.PeekToken ()) != Token.RC && tt != Token.CASE && tt != Token.DEFAULT && tt != Token.EOF) {
558 if (clause_type == ClauseType.Case || clause_type == ClauseType.CaseAfterDefault)
559 cur_case.AddStm (Statement (pn));
560 else if (clause_type == ClauseType.Default)
561 ((Switch) pn).default_clauses.Add (Statement (pn));
563 ((Switch) pn).AddClause (cur_case, clause_type);
565 decompiler.AddEOL (Token.RC);
566 } else if (tt == Token.WHILE) {
568 decompiler.AddToken (Token.WHILE);
569 While w = new While (new Location (ts.SourceName, ts.LineNumber));
570 AST cond = Condition (w);
571 decompiler.AddEOL (Token.LC);
572 AST body = Statement (w);
573 decompiler.AddEOL (Token.RC);
574 w.Init (parent, cond, body);
576 } else if (tt == Token.DO) {
577 decompiler.AddToken (Token.DO);
578 decompiler.AddEOL (Token.LC);
579 int line_number = ts.LineNumber;
580 DoWhile do_while = new DoWhile (new Location (ts.SourceName, line_number));
581 AST body = Statement (do_while);
582 decompiler.AddToken (Token.RC);
583 MustMatchToken (Token.WHILE, "msg.no.while.do");
584 decompiler.AddToken (Token.WHILE);
585 AST cond = Condition (do_while);
586 do_while.Init (parent, body, cond);
588 } else if (tt == Token.FOR) {
590 decompiler.AddToken (Token.FOR);
591 AST init, cond, incr = null, body;
593 MustMatchToken (Token.LP, "msg.no.paren.for");
594 decompiler.AddToken (Token.LP);
595 tt = ts.PeekToken ();
597 if (tt == Token.SEMI)
600 if (tt == Token.VAR) {
601 // set init to a var list or initial
602 ts.GetToken (); // throw away the 'var' token
603 init = Variables (parent, true);
605 init = Expr (parent, true);
608 if (ts.MatchToken (Token.IN)) {
609 decompiler.AddToken (Token.IN);
610 cond = Expr (parent, false); // 'cond' is the object over which we're iterating
613 MustMatchToken (Token.SEMI, "msg.no.semi.for");
614 decompiler.AddToken (Token.SEMI);
616 if (ts.PeekToken () == Token.SEMI)
617 cond = null; // no loop condition
619 cond = Expr (parent, false);
621 MustMatchToken (Token.SEMI, "msg.no.semi.for.cond");
622 decompiler.AddToken (Token.SEMI);
624 if (ts.PeekToken () == Token.RP)
627 incr = Expr (parent, false);
630 MustMatchToken (Token.RP, "msg.no.paren.for.ctrl");
631 decompiler.AddToken (Token.RP);
632 decompiler.AddEOL (Token.LC);
633 body = Statement (pn);
634 decompiler.AddEOL (Token.RC);
636 if (incr == null) // cond could be null if 'in obj' got eaten by the init node.
637 pn = new ForIn (parent, init, cond, body, new Location (ts.SourceName, ts.LineNumber));
639 pn = new For (parent, init, cond, incr, body, new Location (ts.SourceName, ts.LineNumber));
641 } else if (tt == Token.TRY) {
642 int line_number = ts.LineNumber;
644 ArrayList catch_blocks = null;
645 AST finally_block = null;
648 decompiler.AddToken (Token.TRY);
649 decompiler.AddEOL (Token.LC);
651 try_block = Statement (parent);
652 decompiler.AddEOL (Token.RC);
653 catch_blocks = new ArrayList ();
655 bool saw_default_catch = false;
656 int peek = ts.PeekToken ();
658 if (peek == Token.CATCH) {
659 while (ts.MatchToken (Token.CATCH)) {
660 if (saw_default_catch)
661 ReportError ("msg.catch.unreachable");
662 decompiler.AddToken (Token.CATCH);
663 MustMatchToken (Token.LP, "msg.no.paren.catch");
664 decompiler.AddToken (Token.LP);
665 MustMatchToken (Token.NAME, "msg.bad.catchcond");
666 string var_name = ts.GetString;
667 decompiler.AddName (var_name);
668 AST catch_cond = null;
670 if (ts.MatchToken (Token.IF)) {
671 decompiler.AddToken (Token.IF);
672 catch_cond = Expr (parent, false);
674 saw_default_catch = true;
676 MustMatchToken (Token.RP, "msg.bad.catchcond");
677 decompiler.AddToken (Token.RP);
678 MustMatchToken (Token.LC, "msg.no.brace.catchblock");
679 decompiler.AddEOL (Token.LC);
681 catch_blocks.Add (new Catch (var_name, catch_cond,
682 Statements (null), parent, new Location (ts.SourceName, line_number)));
683 MustMatchToken (Token.RC, "msg.no.brace.after.body");
684 decompiler.AddEOL (Token.RC);
686 } else if (peek != Token.FINALLY)
687 MustMatchToken (Token.FINALLY, "msg.try.no.catchfinally");
689 if (ts.MatchToken (Token.FINALLY)) {
690 decompiler.AddToken (Token.FINALLY);
691 decompiler.AddEOL (Token.LC);
692 finally_block = Statement (parent);
693 decompiler.AddEOL (Token.RC);
695 pn = new Try (try_block, catch_blocks, finally_block, parent, new Location (ts.SourceName, ts.LineNumber));
696 } else if (tt == Token.THROW) {
697 int line_number = ts.LineNumber;
698 decompiler.AddToken (Token.THROW);
699 pn = new Throw (Expr (parent, false), new Location (ts.SourceName, ts.LineNumber));
701 if (line_number == ts.LineNumber)
702 CheckWellTerminated ();
703 } else if (tt == Token.BREAK) {
704 decompiler.AddToken (Token.BREAK);
706 // MatchLabel only matches if there is one
707 string label = MatchLabel ();
710 decompiler.AddName (label);
712 pn = new Break (parent, label, new Location (ts.SourceName, ts.LineNumber));
713 } else if (tt == Token.CONTINUE) {
714 decompiler.AddToken (Token.CONTINUE);
716 // MatchLabel only matches if there is one
717 string label = MatchLabel ();
720 decompiler.AddName (label);
722 pn = new Continue (parent, label, new Location (ts.SourceName, ts.LineNumber));
723 } else if (tt == Token.WITH) {
725 decompiler.AddToken (Token.WITH);
726 MustMatchToken (Token.LP, "msg.no.paren.with");
727 decompiler.AddToken (Token.LP);
728 AST obj = Expr (parent, false);
729 MustMatchToken (Token.RP, "msg.no.paren.after.with");
730 decompiler.AddToken (Token.RP);
731 decompiler.AddToken (Token.LC);
735 body = Statement (parent);
739 decompiler.AddEOL (Token.RC);
740 pn = new With (parent, obj, body, new Location (ts.SourceName, ts.LineNumber));
741 } else if (tt == Token.VAR) {
742 int line_number = ts.LineNumber;
743 pn = Variables (parent, false);
744 if (ts.LineNumber == line_number)
745 CheckWellTerminated ();
746 } else if (tt == Token.RETURN) {
748 decompiler.AddToken (Token.RETURN);
749 pn = new Return (new Location (ts.SourceName, ts.LineNumber));
752 ReportError ("msg.bad.return");
754 /* This is ugly, but we don't want to require a semicolon. */
755 ts.allow_reg_exp = true;
756 tt = ts.PeekTokenSameLine ();
757 ts.allow_reg_exp = false;
759 int line_number = ts.LineNumber;
760 if (tt != Token.EOF && tt != Token.EOL && tt != Token.SEMI && tt != Token.RC) {
761 ret_expr = Expr (pn, false);
762 if (ts.LineNumber == line_number)
763 CheckWellTerminated ();
765 ((Return) pn).Init (parent, ret_expr);
766 } else if (tt == Token.LC) {
768 pn = Statements (parent);
769 MustMatchToken (Token.RC, "msg.no.brace.block");
770 } else if (tt == Token.ERROR || tt == Token.EOL || tt == Token.SEMI) {
774 } else if (tt == Token.FUNCTION) {
775 pn = Function (parent, FunctionType.ExpressionStatement);
776 } else if (tt == Token.IMPORT) {
777 decompiler.AddToken (Token.IMPORT);
778 pn = Import (parent);
780 int last_expr_type = tt;
781 int token_number = ts.TokenNumber;
783 int line_number = ts.LineNumber;
785 pn = Expr (parent, false);
787 if (ts.PeekToken () == Token.COLON) {
788 /* check that the last thing the tokenizer returned was a
789 * NAME and that only one token was consumed.
791 if (last_expr_type != Token.NAME || (ts.TokenNumber != token_number))
792 ReportError ("msg.bad.label");
794 ts.GetToken (); // eat the colon
796 string name = ts.GetString;
798 // bind 'Statement (pn)' to the label
799 Labelled labelled = new Labelled (parent, new Location (ts.SourceName, ts.LineNumber));
800 labelled.Init (parent, name, Statement (labelled), new Location (ts.SourceName, ts.LineNumber));
802 // depend on decompiling lookahead to guess that that
803 // last name was a label.
804 decompiler.AddEOL (Token.COLON);
808 // pn = nf.createExprStatement(pn, lineno);
809 if (ts.LineNumber == line_number)
810 CheckWellTerminated ();
812 ts.MatchToken (Token.SEMI);
815 decompiler.AddEOL (Token.SEMI);
820 AST Variables (AST parent, bool in_for_init)
822 VariableStatement pn = new VariableStatement (parent, new Location (ts.SourceName, ts.LineNumber));
824 decompiler.AddToken (Token.VAR);
827 VariableDeclaration name;
829 MustMatchToken (Token.NAME, "msg.bad.var");
830 string s = ts.GetString;
833 decompiler.AddToken (Token.COMMA);
836 decompiler.AddName (s);
837 name = new VariableDeclaration (parent, s, null, null, new Location (ts.SourceName, ts.LineNumber));
839 // ommited check for argument hiding
840 if (ts.MatchToken (Token.ASSIGN)) {
841 decompiler.AddToken (Token.ASSIGN);
842 init = AssignExpr (parent, in_for_init);
847 if (!ts.MatchToken (Token.COMMA))
853 AST Expr (AST parent, bool in_for_init)
855 Expression pn = new Expression (parent, new Location (ts.SourceName, ts.LineNumber));
856 AST init = AssignExpr (parent, in_for_init);
860 throw new Exception ("Expr, L680, AST is null");
862 while (ts.MatchToken (Token.COMMA)) {
863 decompiler.AddToken (Token.COMMA);
864 pn.Add (AssignExpr (parent, in_for_init));
869 AST AssignExpr (AST parent, bool in_for_init)
871 AST pn = CondExpr (parent, in_for_init);
872 int tt = ts.PeekToken ();
874 // omitted: "invalid assignment left-hand side" check.
875 if (tt == Token.ASSIGN) {
877 decompiler.AddToken (Token.ASSIGN);
878 pn = new Assign (parent, pn, AssignExpr (parent, in_for_init), JSToken.Assign, false, new Location (ts.SourceName, ts.LineNumber));
880 } else if (tt == Token.ASSIGNOP) {
882 int op = ts.GetOp ();
883 decompiler.AddAssignOp (op);
884 pn = new Assign (parent, pn, AssignExpr (parent, in_for_init), ToJSToken (op, tt), false,
885 new Location (ts.SourceName, ts.LineNumber));
890 AST CondExpr (AST parent, bool in_for_init)
894 AST pn = OrExpr (parent, in_for_init);
896 if (ts.MatchToken (Token.HOOK)) {
897 decompiler.AddToken (Token.HOOK);
898 if_true = AssignExpr (parent, false);
899 MustMatchToken (Token.COLON, "msg.no.colon.cond");
900 decompiler.AddToken (Token.COLON);
901 if_false = AssignExpr (parent, in_for_init);
902 return new Conditional (parent, pn, if_true, if_false, new Location (ts.SourceName, ts.LineNumber));
907 AST OrExpr (AST parent, bool in_for_init)
909 AST pn = AndExpr (parent, in_for_init);
910 if (ts.MatchToken (Token.OR)) {
911 decompiler.AddToken (Token.OR);
912 return new Binary (parent, pn, OrExpr (parent, in_for_init), JSToken.LogicalOr,
913 new Location (ts.SourceName, ts.LineNumber));
918 AST AndExpr (AST parent, bool in_for_init)
920 AST pn = BitOrExpr (parent, in_for_init);
921 if (ts.MatchToken (Token.AND)) {
922 decompiler.AddToken (Token.AND);
923 return new Binary (parent, pn, AndExpr (parent, in_for_init), JSToken.LogicalAnd,
924 new Location (ts.SourceName, ts.LineNumber));
929 AST BitOrExpr (AST parent, bool in_for_init)
931 AST pn = BitXorExpr (parent, in_for_init);
932 while (ts.MatchToken (Token.BITOR)) {
933 decompiler.AddToken (Token.BITOR);
934 pn = new Binary (parent, pn, BitXorExpr (parent, in_for_init), JSToken.BitwiseOr,
935 new Location (ts.SourceName, ts.LineNumber));
940 AST BitXorExpr (AST parent, bool in_for_init)
942 AST pn = BitAndExpr (parent, in_for_init);
943 while (ts.MatchToken (Token.BITXOR)) {
944 decompiler.AddToken (Token.BITXOR);
945 pn = new Binary (parent, pn, BitAndExpr (parent, in_for_init), JSToken.BitwiseXor,
946 new Location (ts.SourceName, ts.LineNumber));
951 AST BitAndExpr (AST parent, bool in_for_init)
953 AST pn = EqExpr (parent, in_for_init);
954 while (ts.MatchToken (Token.BITAND)) {
955 decompiler.AddToken (Token.BITAND);
956 pn = new Binary (parent, pn, EqExpr (parent, in_for_init), JSToken.BitwiseAnd,
957 new Location (ts.SourceName, ts.LineNumber));
962 AST EqExpr (AST parent, bool in_for_init)
964 AST pn = RelExpr (parent, in_for_init);
965 ArrayList tokens = new ArrayList ();
967 int tt = ts.PeekToken ();
969 if (tt == Token.EQ || tt == Token.NE) {
970 foreach (int token in tokens)
971 decompiler.AddToken (token);
975 pn = new Equality (parent, pn, RelExpr (parent, in_for_init), ToJSToken (tt),
976 new Location (ts.SourceName, ts.LineNumber));
978 } else if (tt == Token.SHEQ || tt == Token.SHNE) {
979 foreach (int token in tokens)
980 decompiler.AddToken (token);
984 pn = new StrictEquality (parent, pn, RelExpr (parent, in_for_init), ToJSToken (tt), new Location (ts.SourceName, ts.LineNumber));
992 AST RelExpr (AST parent, bool in_for_init)
994 AST pn = ShiftExpr (parent);
996 int tt = ts.PeekToken ();
997 if (tt == Token.IN) {
1002 decompiler.AddToken (tt);
1003 pn = new Relational (parent, pn, ShiftExpr (parent), ToJSToken (tt), new Location (ts.SourceName, ts.LineNumber));
1006 } else if (tt == Token.INSTANCEOF || tt == Token.LE || tt == Token.LT || tt == Token.GE || tt == Token.GT) {
1008 decompiler.AddToken (tt);
1009 pn = new Relational (parent, pn, ShiftExpr (parent), ToJSToken (tt), new Location (ts.SourceName, ts.LineNumber));
1017 AST ShiftExpr (AST parent)
1019 AST pn = AddExpr (parent);
1021 int tt = ts.PeekToken ();
1022 if (tt == Token.LSH || tt == Token.URSH || tt == Token.RSH) {
1024 decompiler.AddToken (tt);
1026 JSToken op = JSToken.LeftShift;
1027 if (tt == Token.RSH)
1028 op = JSToken.RightShift;
1029 else if (tt == Token.URSH)
1030 op = JSToken.UnsignedRightShift;
1032 pn = new Binary (parent, pn, AddExpr (parent), op,
1033 new Location (ts.SourceName, ts.LineNumber));
1041 AST AddExpr (AST parent)
1043 AST pn = MulExpr (parent);
1045 int tt = ts.PeekToken ();
1046 if (tt == Token.ADD || tt == Token.SUB) {
1048 decompiler.AddToken (tt);
1049 pn = new Binary (parent, pn, MulExpr (parent), ToJSToken (tt),
1050 new Location (ts.SourceName, ts.LineNumber));
1058 AST MulExpr (AST parent)
1060 AST pn = UnaryExpr (parent);
1062 int tt = ts.PeekToken ();
1063 if (tt == Token.MUL || tt == Token.DIV || tt == Token.MOD) {
1065 decompiler.AddToken (tt);
1066 pn = new Binary (parent, pn, UnaryExpr (parent), ToJSToken (tt),
1067 new Location (ts.SourceName, ts.LineNumber));
1075 AST UnaryExpr (AST parent)
1079 ts.allow_reg_exp = true;
1080 tt = ts.GetToken ();
1081 ts.allow_reg_exp = false;
1083 if (tt == Token.VOID || tt == Token.NOT || tt == Token.BITNOT || tt == Token.TYPEOF ||
1084 tt == Token.ADD || tt == Token.SUB || tt == Token.DELPROP) {
1085 if (tt == Token.VOID || tt == Token.NOT || tt == Token.BITNOT || tt == Token.TYPEOF)
1086 decompiler.AddToken (tt);
1087 else if (tt == Token.ADD)
1088 decompiler.AddToken (Token.POS);
1089 else if (tt == Token.SUB)
1090 decompiler.AddToken (Token.NEG);
1092 decompiler.AddToken (tt);
1094 Unary u = new Unary (parent, ToJSToken (tt), new Location (ts.SourceName, ts.LineNumber));
1095 u.operand = UnaryExpr (u);
1097 } else if (tt == Token.INC || tt == Token.DEC) {
1098 decompiler.AddToken (tt);
1099 return new PostOrPrefixOperator (parent, MemberExpr (parent, true), ToJSToken (tt), true,
1100 new Location (ts.SourceName, ts.LineNumber));
1101 } else if (tt == Token.ERROR) {
1105 int line_number = ts.LineNumber;
1107 AST pn = MemberExpr (parent, true);
1109 /* don't look across a newline boundary for a postfix incop.
1111 * the rhino scanner seems to work differently than the js
1112 * scanner here; in js, it works to have the line number check
1113 * precede the peekToken calls. It'd be better if they had
1114 * similar behavior...
1117 if (((peeked = ts.PeekToken ()) == Token.INC || peeked == Token.DEC) && ts.LineNumber == line_number) {
1118 int pf = ts.GetToken ();
1119 decompiler.AddToken (pf);
1120 return new PostOrPrefixOperator (parent, pn, ToJSToken (peeked), false,
1121 new Location (ts.SourceName, ts.LineNumber));
1125 return new StringLiteral (null, "Error", new Location (ts.SourceName, ts.LineNumber)); // Only reached on error. Try to continue.
1128 JSToken ToJSToken (int tt)
1130 if (tt == Token.DELPROP)
1131 return JSToken.Delete;
1132 else if (tt == Token.VOID)
1133 return JSToken.Void;
1134 else if (tt == Token.TYPEOF)
1135 return JSToken.Typeof;
1136 else if (tt == Token.INC)
1137 return JSToken.Increment;
1138 else if (tt == Token.DEC)
1139 return JSToken.Decrement;
1140 else if (tt == Token.ADD)
1141 return JSToken.Plus;
1142 else if (tt == Token.SUB)
1143 return JSToken.Minus;
1144 else if (tt == Token.BITNOT)
1145 return JSToken.BitwiseNot;
1146 else if (tt == Token.NOT)
1147 return JSToken.LogicalNot;
1148 else if (tt == Token.EQ)
1149 return JSToken.Equal;
1150 else if (tt == Token.NE)
1151 return JSToken.NotEqual;
1152 else if (tt == Token.SHEQ)
1153 return JSToken.StrictEqual;
1154 else if (tt == Token.SHNE)
1155 return JSToken.StrictNotEqual;
1156 else if (tt == Token.MUL)
1157 return JSToken.Multiply;
1158 else if (tt == Token.DIV)
1159 return JSToken.Divide;
1160 else if (tt == Token.MOD)
1161 return JSToken.Modulo;
1162 else if (tt == Token.IN)
1164 else if (tt == Token.INSTANCEOF)
1165 return JSToken.Instanceof;
1166 else if (tt == Token.LE)
1167 return JSToken.LessThanEqual;
1168 else if (tt == Token.LT)
1169 return JSToken.LessThan;
1170 else if (tt == Token.GE)
1171 return JSToken.GreaterThanEqual;
1172 else if (tt == Token.GT)
1173 return JSToken.GreaterThan;
1175 throw new NotImplementedException ();
1179 // Takes care of +=, -=
1181 JSToken ToJSToken (int left, int right)
1183 if (right == Token.ASSIGNOP) {
1184 if (left == Token.ADD)
1185 return JSToken.PlusAssign;
1186 else if (left == Token.SUB)
1187 return JSToken.MinusAssign;
1188 else if (left == Token.MUL)
1189 return JSToken.MultiplyAssign;
1190 else if (left == Token.DIV)
1191 return JSToken.DivideAssign;
1192 else if (left == Token.BITAND)
1193 return JSToken.BitwiseAndAssign;
1194 else if (left == Token.BITOR)
1195 return JSToken.BitwiseOrAssign;
1196 else if (left == Token.BITXOR)
1197 return JSToken.BitwiseXorAssign;
1198 else if (left == Token.MOD)
1199 return JSToken.ModuloAssign;
1200 else if (left == Token.LSH)
1201 return JSToken.LeftShiftAssign;
1202 else if (left == Token.RSH)
1203 return JSToken.RightShiftAssign;
1204 else if (left == Token.URSH)
1205 return JSToken.UnsignedRightShiftAssign;
1207 throw new NotImplementedException ();
1210 void ArgumentList (AST parent, ICallable list)
1213 ts.allow_reg_exp = true;
1214 matched = ts.MatchToken (Token.RP);
1215 ts.allow_reg_exp = false;
1221 decompiler.AddToken (Token.COMMA);
1223 list.AddArg (AssignExpr (parent, false));
1224 } while (ts.MatchToken (Token.COMMA));
1225 MustMatchToken (Token.RP, "msg.no.paren.arg");
1227 decompiler.AddToken (Token.RP);
1230 AST MemberExpr (AST parent, bool allow_call_syntax)
1235 /* Check for new expressions. */
1236 ts.allow_reg_exp = true;
1237 tt = ts.PeekToken ();
1238 ts.allow_reg_exp = false;
1240 if (tt == Token.NEW) {
1241 /* Eat the NEW token. */
1243 decompiler.AddToken (Token.NEW);
1245 /* Make a NEW node to append to. */
1246 pn = new New (parent, MemberExpr (parent, false), new Location (ts.SourceName, ts.LineNumber));
1248 if (ts.MatchToken (Token.LP)) {
1249 decompiler.AddToken (Token.LP);
1250 ArgumentList (parent, (ICallable) pn);
1253 /* XXX there's a check in the C source against
1254 * "too many constructor arguments" - how many
1255 * do we claim to support?
1258 /* Experimental syntax: allow an object literal to follow a new expression,
1259 * which will mean a kind of anonymous class built with the JavaAdapter.
1260 * the object literal will be passed as an additional argument to the constructor.
1263 tt = ts.PeekToken ();
1265 pn = PrimaryExpr (parent);
1267 pn = PrimaryExpr (parent);
1268 return MemberExprTail (pn, allow_call_syntax, pn);
1271 AST MemberExprTail (AST parent, bool allow_call_syntax, AST pn)
1275 while ((tt = ts.GetToken ()) > Token.EOF) {
1276 if (tt == Token.DOT) {
1277 decompiler.AddToken (Token.DOT);
1278 MustMatchToken (Token.NAME, "msg.no.name.after.dot");
1279 string s = ts.GetString;
1280 decompiler.AddName (s);
1281 // FIXME: is 'new Identifier' appropriate here?
1282 pn = new Binary (parent, pn,
1283 new Identifier (parent, ts.GetString, new Location (ts.SourceName, ts.LineNumber)),
1284 JSToken.AccessField, new Location (ts.SourceName, ts.LineNumber));
1285 } else if (tt == Token.LB) {
1286 decompiler.AddToken (Token.LB);
1287 Binary b = new Binary (parent, pn, JSToken.LeftBracket,
1288 new Location (ts.SourceName, ts.LineNumber));
1289 b.right = Expr (b, false);
1291 MustMatchToken (Token.RB, "msg.no.bracket.index");
1292 decompiler.AddToken (Token.RB);
1293 } else if (allow_call_syntax && tt == Token.LP) {
1294 /* make a call node */
1295 decompiler.AddToken (Token.LP);
1296 pn = new Call (parent, pn, new Location (ts.SourceName, ts.LineNumber));
1298 /* Add the arguments to pn, if any are supplied. */
1299 ArgumentList (parent, (ICallable) pn);
1308 AST PrimaryExpr (AST parent)
1313 ts.allow_reg_exp = true;
1314 tt = ts.GetToken ();
1315 ts.allow_reg_exp = false;
1317 if (tt == Token.FUNCTION) {
1318 return Function (parent, FunctionType.Expression);
1319 } else if (tt == Token.LB) {
1320 ASTList elems = new ASTList (parent, new Location (ts.SourceName, ts.LineNumber));
1322 decompiler.AddToken (Token.LB);
1323 bool after_lb_or_comma = true;
1325 ts.allow_reg_exp = true;
1326 tt = ts.PeekToken ();
1327 ts.allow_reg_exp = false;
1329 if (tt == Token.COMMA) {
1331 decompiler.AddToken (Token.COMMA);
1332 if (!after_lb_or_comma)
1333 after_lb_or_comma = true;
1338 } else if (tt == Token.RB) {
1340 decompiler.AddToken (Token.RB);
1343 if (!after_lb_or_comma)
1344 ReportError ("msg.no.bracket.arg");
1345 elems.Add (AssignExpr (parent, false));
1346 after_lb_or_comma = false;
1349 // FIXME: pass a real Context
1350 return new ArrayLiteral (null, elems, skip_count, new Location (ts.SourceName, ts.LineNumber));
1351 } else if (tt == Token.LC) {
1352 Location location = new Location (ts.SourceName, ts.LineNumber);
1353 ArrayList elems = new ArrayList ();
1354 decompiler.AddToken (Token.LC);
1356 if (!ts.MatchToken (Token.RC)) {
1361 ObjectLiteralItem property;
1364 decompiler.AddToken (Token.COMMA);
1368 tt = ts.GetToken ();
1370 if (tt == Token.NAME || tt == Token.STRING) {
1371 string s = ts.GetString;
1372 if (tt == Token.NAME)
1373 decompiler.AddName (s);
1375 decompiler.AddString (s);
1376 property = new ObjectLiteralItem (s);
1377 } else if (tt == Token.NUMBER) {
1378 double n = ts.GetNumber;
1379 decompiler.AddNumber (n);
1380 property = new ObjectLiteralItem (n);
1381 } else if (tt == Token.RC) {
1382 // trailing comma is OK
1384 goto leave_commaloop;
1386 ReportError ("msg.bad.prop");
1387 goto leave_commaloop;
1389 MustMatchToken (Token.COLON, "msg.no.colon.prop");
1390 // OBJLIT is used as ':' in object literal for
1391 // decompilation to solve spacing ambiguity.
1392 decompiler.AddToken (Token.OBJECTLIT);
1393 property.exp = AssignExpr (parent, false);
1394 elems.Add (property);
1395 } while (ts.MatchToken (Token.COMMA));
1396 MustMatchToken (Token.RC, "msg.no.brace.prop");
1401 return new ObjectLiteral (elems, location);
1402 } else if (tt == Token.LP) {
1403 decompiler.AddToken (Token.LP);
1404 pn = Expr (parent, false);
1405 decompiler.AddToken (Token.RP);
1406 MustMatchToken (Token.RP, "msg.no.paren");
1407 decompiler.AddToken (Token.RP);
1409 } else if (tt == Token.NAME) {
1410 string name = ts.GetString;
1411 decompiler.AddName (name);
1412 return new Identifier (parent, name, new Location (ts.SourceName, ts.LineNumber));
1413 } else if (tt == Token.NUMBER) {
1414 double n = ts.GetNumber;
1415 decompiler.AddNumber (n);
1417 Location location = new Location (ts.SourceName, ts.LineNumber);
1419 if (HasNoDecimals (n)) {
1420 if (InRangeOf (n, Byte.MinValue, Byte.MaxValue))
1421 return new ByteConstant (parent, (byte) n, location);
1422 else if (InRangeOf (n, Int16.MinValue, Int16.MaxValue))
1423 return new ShortConstant (parent, (short) n, location);
1424 else if (InRangeOf (n, Int32.MinValue, Int32.MaxValue))
1425 return new IntConstant (parent, (int) n, location);
1426 else if (InRangeOf (n, Int64.MinValue, Int64.MaxValue))
1427 return new LongConstant (parent, (long) n, location);
1429 return new DoubleConstant (parent, n, location);
1431 if (InRangeOf (n, Single.MinValue, Single.MaxValue))
1432 return new FloatConstant (parent, (float) n, location);
1433 else if (InRangeOf (n, Double.MinValue, Double.MaxValue))
1434 return new DoubleConstant (parent, n, location);
1436 return new DoubleConstant (parent, n, location);
1438 } else if (tt == Token.STRING) {
1439 string s = ts.GetString;
1440 decompiler.AddString (s);
1441 return new StringLiteral (null, s, new Location (ts.SourceName, ts.LineNumber));
1442 } else if (tt == Token.REGEXP) {
1443 string flags = ts.reg_exp_flags;
1444 ts.reg_exp_flags = null;
1445 string re = ts.GetString;
1446 decompiler.AddRegexp (re, flags);
1447 return new RegExpLiteral (parent, re, flags, new Location (ts.SourceName, ts.LineNumber));
1448 } else if (tt == Token.NULL) {
1449 decompiler.AddToken (tt);
1450 // FIXME, build the null object;
1452 } else if (tt == Token.THIS) {
1453 decompiler.AddToken (tt);
1454 return new This (parent, new Location (ts.SourceName, ts.LineNumber));
1455 } else if (tt == Token.FALSE || tt == Token.TRUE) {
1456 decompiler.AddToken (tt);
1458 if (tt == Token.FALSE)
1462 return new BooleanConstant (null, v, new Location (ts.SourceName, ts.LineNumber));
1463 } else if (tt == Token.RESERVED) {
1464 ReportError ("msg.reserved.id");
1465 } else if (tt == Token.ERROR) {
1466 /* the scanner or one of its subroutines reported the error. */
1468 ReportError ("msg.syntax");
1469 return null; // should never reach here