Refactor JS parser

This commit is contained in:
Alexey Andreev
2017-05-17 14:55:14 +03:00
parent 3a3720db8d
commit 1d1da326be
5 changed files with 360 additions and 568 deletions
@@ -15,14 +15,13 @@
*/ */
package com.google.gwt.dev.js; package com.google.gwt.dev.js;
import org.jetbrains.kotlin.js.backend.ast.*;
import org.jetbrains.kotlin.js.backend.ast.JsBooleanLiteral;
import org.jetbrains.kotlin.js.backend.ast.metadata.HasMetadata;
import com.google.gwt.dev.js.parserExceptions.JsParserException; import com.google.gwt.dev.js.parserExceptions.JsParserException;
import com.google.gwt.dev.js.rhino.*; import com.google.gwt.dev.js.rhino.Node;
import com.google.gwt.dev.js.rhino.TokenStream;
import com.intellij.util.SmartList; import com.intellij.util.SmartList;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.js.backend.ast.*;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@@ -40,8 +39,7 @@ public class JsAstMapper {
} }
private static JsParserException createParserException(String msg, Node offender) { private static JsParserException createParserException(String msg, Node offender) {
CodePosition position = new CodePosition(offender.getLineno(), 0); return new JsParserException("Parser encountered internal error: " + msg, offender.getPosition());
return new JsParserException("Parser encountered internal error: " + msg, position);
} }
private JsNode map(Node node) throws JsParserException { private JsNode map(Node node) throws JsParserException {
@@ -254,7 +252,7 @@ public class JsAstMapper {
private JsExpression mapAssignmentVariant(Node asgNode) private JsExpression mapAssignmentVariant(Node asgNode)
throws JsParserException { throws JsParserException {
switch (asgNode.getIntDatum()) { switch (asgNode.getOperation()) {
case TokenStream.NOP: case TokenStream.NOP:
return mapBinaryOperation(JsBinaryOperator.ASG, asgNode); return mapBinaryOperation(JsBinaryOperator.ASG, asgNode);
@@ -293,7 +291,7 @@ public class JsAstMapper {
default: default:
throw createParserException("Unknown assignment operator variant: " throw createParserException("Unknown assignment operator variant: "
+ asgNode.getIntDatum(), asgNode); + asgNode.getOperation(), asgNode);
} }
} }
@@ -434,7 +432,7 @@ public class JsAstMapper {
} }
private JsExpression mapEqualityVariant(Node eqNode) throws JsParserException { private JsExpression mapEqualityVariant(Node eqNode) throws JsParserException {
switch (eqNode.getIntDatum()) { switch (eqNode.getOperation()) {
case TokenStream.EQ: case TokenStream.EQ:
return mapBinaryOperation(JsBinaryOperator.EQ, eqNode); return mapBinaryOperation(JsBinaryOperator.EQ, eqNode);
@@ -461,18 +459,13 @@ public class JsAstMapper {
default: default:
throw createParserException("Unknown equality operator variant: " throw createParserException("Unknown equality operator variant: "
+ eqNode.getIntDatum(), eqNode); + eqNode.getOperation(), eqNode);
} }
} }
private JsExpression mapExpression(Node exprNode) throws JsParserException { private JsExpression mapExpression(Node exprNode) throws JsParserException {
JsNode unknown = map(exprNode); JsNode unknown = map(exprNode);
if (unknown instanceof HasMetadata) {
HasMetadata metadataContainer = (HasMetadata) unknown;
metadataContainer.setData("line", exprNode.getLineno());
}
if (unknown instanceof JsExpression) { if (unknown instanceof JsExpression) {
return (JsExpression) unknown; return (JsExpression) unknown;
} }
@@ -611,16 +604,8 @@ public class JsAstMapper {
JsExpression toQualifier = mapExpression(from1); JsExpression toQualifier = mapExpression(from1);
JsNameRef toNameRef; JsNameRef toNameRef;
if (from2 != null) { toNameRef = mapAsPropertyNameRef(from2);
toNameRef = mapAsPropertyNameRef(from2);
}
else {
// Special properties don't have a second expression.
//
Object obj = getPropNode.getProp(Node.SPECIAL_PROP_PROP);
assert (obj instanceof String);
toNameRef = scopeContext.referenceFor((String) obj);
}
toNameRef.setQualifier(toQualifier); toNameRef.setQualifier(toQualifier);
return toNameRef; return toNameRef;
@@ -649,14 +634,14 @@ public class JsAstMapper {
private JsExpression mapIncDecFixity(JsUnaryOperator op, Node node) private JsExpression mapIncDecFixity(JsUnaryOperator op, Node node)
throws JsParserException { throws JsParserException {
switch (node.getIntDatum()) { switch (node.getOperation()) {
case TokenStream.PRE: case TokenStream.PRE:
return mapPrefixOperation(op, node); return mapPrefixOperation(op, node);
case TokenStream.POST: case TokenStream.POST:
return mapPostfixOperation(op, node); return mapPostfixOperation(op, node);
default: default:
throw createParserException( throw createParserException(
"Unknown prefix/postfix variant: " + node.getIntDatum(), node); "Unknown prefix/postfix variant: " + node.getOperation(), node);
} }
} }
@@ -761,7 +746,7 @@ public class JsAstMapper {
} }
private static JsExpression mapPrimary(Node node) throws JsParserException { private static JsExpression mapPrimary(Node node) throws JsParserException {
switch (node.getIntDatum()) { switch (node.getOperation()) {
case TokenStream.THIS: case TokenStream.THIS:
return new JsThisRef(); return new JsThisRef();
@@ -778,7 +763,7 @@ public class JsAstMapper {
return new JsNameRef("undefined"); return new JsNameRef("undefined");
default: default:
throw createParserException("Unknown primary: " + node.getIntDatum(), throw createParserException("Unknown primary: " + node.getOperation(),
node); node);
} }
} }
@@ -799,7 +784,7 @@ public class JsAstMapper {
private JsExpression mapRelationalVariant(Node relNode) private JsExpression mapRelationalVariant(Node relNode)
throws JsParserException { throws JsParserException {
switch (relNode.getIntDatum()) { switch (relNode.getOperation()) {
case TokenStream.LT: case TokenStream.LT:
return mapBinaryOperation(JsBinaryOperator.LT, relNode); return mapBinaryOperation(JsBinaryOperator.LT, relNode);
@@ -820,7 +805,7 @@ public class JsAstMapper {
default: default:
throw createParserException("Unknown relational operator variant: " throw createParserException("Unknown relational operator variant: "
+ relNode.getIntDatum(), relNode); + relNode.getOperation(), relNode);
} }
} }
@@ -864,7 +849,7 @@ public class JsAstMapper {
} }
private JsExpression mapShiftVariant(Node shiftNode) throws JsParserException { private JsExpression mapShiftVariant(Node shiftNode) throws JsParserException {
switch (shiftNode.getIntDatum()) { switch (shiftNode.getOperation()) {
case TokenStream.LSH: case TokenStream.LSH:
return mapBinaryOperation(JsBinaryOperator.SHL, shiftNode); return mapBinaryOperation(JsBinaryOperator.SHL, shiftNode);
@@ -876,18 +861,13 @@ public class JsAstMapper {
default: default:
throw createParserException("Unknown equality operator variant: " throw createParserException("Unknown equality operator variant: "
+ shiftNode.getIntDatum(), shiftNode); + shiftNode.getOperation(), shiftNode);
} }
} }
private JsStatement mapStatement(Node nodeStmt) throws JsParserException { private JsStatement mapStatement(Node nodeStmt) throws JsParserException {
JsNode unknown = map(nodeStmt); JsNode unknown = map(nodeStmt);
if (unknown instanceof HasMetadata) {
HasMetadata metadataContainer = (HasMetadata) unknown;
metadataContainer.setData("line", nodeStmt.getLineno());
}
if (unknown != null) { if (unknown != null) {
if (unknown instanceof JsStatement) { if (unknown instanceof JsStatement) {
return (JsStatement) unknown; return (JsStatement) unknown;
@@ -1050,7 +1030,7 @@ public class JsAstMapper {
} }
private JsExpression mapUnaryVariant(Node unOp) throws JsParserException { private JsExpression mapUnaryVariant(Node unOp) throws JsParserException {
switch (unOp.getIntDatum()) { switch (unOp.getOperation()) {
case TokenStream.SUB: case TokenStream.SUB:
return mapPrefixOperation(JsUnaryOperator.NEG, unOp); return mapPrefixOperation(JsUnaryOperator.NEG, unOp);
@@ -1077,7 +1057,7 @@ public class JsAstMapper {
default: default:
throw createParserException( throw createParserException(
"Unknown unary operator variant: " + unOp.getIntDatum(), unOp); "Unknown unary operator variant: " + unOp.getOperation(), unOp);
} }
} }
@@ -41,7 +41,6 @@ package com.google.gwt.dev.js.rhino;
* @see Node * @see Node
*/ */
public class IRFactory { public class IRFactory {
public IRFactory(TokenStream ts) { public IRFactory(TokenStream ts) {
this.ts = ts; this.ts = ts;
} }
@@ -49,77 +48,70 @@ public class IRFactory {
/** /**
* Script (for associating file/url names with toplevel scripts.) * Script (for associating file/url names with toplevel scripts.)
*/ */
public Object createScript(Object body, String sourceName, public Node createScript(Node body)
int baseLineno, int endLineno, Object source)
{ {
Node result = new Node(TokenStream.SCRIPT); Node result = new Node(TokenStream.SCRIPT);
Node children = ((Node) body).getFirstChild(); Node children = body.getFirstChild();
if (children != null) if (children != null)
result.addChildrenToBack(children); result.addChildrenToBack(children);
result.putProp(Node.SOURCENAME_PROP, sourceName);
result.putIntProp(Node.BASE_LINENO_PROP, baseLineno);
result.putIntProp(Node.END_LINENO_PROP, endLineno);
if (source != null)
result.putProp(Node.SOURCE_PROP, source);
return result; return result;
} }
/** /**
* Leaf * Leaf
*/ */
public Object createLeaf(int nodeType) { public Node createLeaf(int nodeType, CodePosition location) {
return new Node(nodeType); return new Node(nodeType, location);
} }
public Object createLeaf(int nodeType, int nodeOp) { public Node createLeaf(int nodeType, int nodeOp, CodePosition location) {
return new Node(nodeType, nodeOp); return new Node(nodeType, nodeOp, location);
} }
public int getLeafType(Object leaf) { public int getLeafType(Node leaf) {
Node n = (Node) leaf; return leaf.getType();
return n.getType();
} }
/** /**
* Statement leaf nodes. * Statement leaf nodes.
*/ */
public Object createSwitch(int lineno) { public Node createSwitch(CodePosition location) {
return new Node(TokenStream.SWITCH, lineno); return new Node(TokenStream.SWITCH, location);
} }
public Object createVariables(int lineno) { public Node createVariables(CodePosition location) {
return new Node(TokenStream.VAR, lineno); return new Node(TokenStream.VAR, location);
} }
public Object createExprStatement(Object expr, int lineno) { public Node createExprStatement(Object expr, CodePosition location) {
return new Node(TokenStream.EXPRSTMT, (Node) expr, lineno); return new Node(TokenStream.EXPRSTMT, (Node) expr, location);
} }
/** /**
* Name * Name
*/ */
public Object createName(String name) { public Node createName(String name, CodePosition location) {
return Node.newString(TokenStream.NAME, name); return Node.newString(TokenStream.NAME, name, location);
} }
/** /**
* String (for literals) * String (for literals)
*/ */
public Object createString(String string) { public Node createString(String string, CodePosition location) {
return Node.newString(string); return Node.newString(string, location);
} }
/** /**
* Number (for literals) * Number (for literals)
*/ */
public Object createNumber(int number) { public Node createNumber(int number, CodePosition location) {
return Node.newNumber(number); return Node.newNumber(number, location);
} }
public Object createNumber(double number) { public Node createNumber(double number, CodePosition location) {
return Node.newNumber(number); return Node.newNumber(number, location);
} }
/** /**
@@ -130,52 +122,47 @@ public class IRFactory {
* @param stmts the statements in the catch clause * @param stmts the statements in the catch clause
* @param lineno the starting line number of the catch clause * @param lineno the starting line number of the catch clause
*/ */
public Object createCatch(String varName, Object catchCond, Object stmts, public Node createCatch(Node varName, Node catchCond, Node stmts, CodePosition location) {
int lineno)
{
if (catchCond == null) { if (catchCond == null) {
catchCond = new Node(TokenStream.PRIMARY, TokenStream.TRUE); catchCond = new Node(TokenStream.PRIMARY, TokenStream.TRUE, location);
} }
return new Node(TokenStream.CATCH, (Node)createName(varName), return new Node(TokenStream.CATCH, varName, catchCond, stmts, location);
(Node)catchCond, (Node)stmts, lineno);
} }
/** /**
* Throw * Throw
*/ */
public Object createThrow(Object expr, int lineno) { public Node createThrow(Node expr, CodePosition location) {
return new Node(TokenStream.THROW, (Node)expr, lineno); return new Node(TokenStream.THROW, expr, location);
} }
/** /**
* Return * Return
*/ */
public Object createReturn(Object expr, int lineno) { public Node createReturn(Node expr, CodePosition location) {
return expr == null return expr == null
? new Node(TokenStream.RETURN, lineno) ? new Node(TokenStream.RETURN, location)
: new Node(TokenStream.RETURN, (Node)expr, lineno); : new Node(TokenStream.RETURN, expr, location);
} }
/** /**
* Label * Label
*/ */
public Object createLabel(String label, int lineno) { public Node createLabel(Node label, CodePosition location) {
Node result = new Node(TokenStream.LABEL, lineno); Node result = new Node(TokenStream.LABEL, location);
Node name = Node.newString(TokenStream.NAME, label); result.addChildToBack(label);
result.addChildToBack(name);
return result; return result;
} }
/** /**
* Break (possibly labeled) * Break (possibly labeled)
*/ */
public Object createBreak(String label, int lineno) { public Node createBreak(Node label, CodePosition location) {
Node result = new Node(TokenStream.BREAK, lineno); Node result = new Node(TokenStream.BREAK, location);
if (label == null) { if (label == null) {
return result; return result;
} else { } else {
Node name = Node.newString(TokenStream.NAME, label); result.addChildToBack(label);
result.addChildToBack(name);
return result; return result;
} }
} }
@@ -183,13 +170,12 @@ public class IRFactory {
/** /**
* Continue (possibly labeled) * Continue (possibly labeled)
*/ */
public Object createContinue(String label, int lineno) { public Node createContinue(Node label, CodePosition location) {
Node result = new Node(TokenStream.CONTINUE, lineno); Node result = new Node(TokenStream.CONTINUE, location);
if (label == null) { if (label == null) {
return result; return result;
} else { } else {
Node name = Node.newString(TokenStream.NAME, label); result.addChildToBack(label);
result.addChildToBack(name);
return result; return result;
} }
} }
@@ -197,8 +183,8 @@ public class IRFactory {
/** /**
* debugger * debugger
*/ */
public Object createDebugger(int lineno) { public Node createDebugger(CodePosition location) {
Node result = new Node(TokenStream.DEBUGGER, lineno); Node result = new Node(TokenStream.DEBUGGER, location);
return result; return result;
} }
@@ -207,81 +193,54 @@ public class IRFactory {
* Creates the empty statement block * Creates the empty statement block
* Must make subsequent calls to add statements to the node * Must make subsequent calls to add statements to the node
*/ */
public Object createBlock(int lineno) { public Node createBlock(CodePosition location) {
return new Node(TokenStream.BLOCK, lineno); return new Node(TokenStream.BLOCK, location);
} }
public Object createFunction(String name, Object args, Object statements, public Node createFunction(Node name, Node args, Node statements, CodePosition location) {
String sourceName, int baseLineno, if (name == null) {
int endLineno, Object source, name = createName("", location);
boolean isExpr) }
{ return new Node(TokenStream.FUNCTION, name, args, statements, location);
Node f = new Node(TokenStream.FUNCTION,
Node.newString(TokenStream.NAME,
name == null ? "" : name),
(Node)args, (Node)statements, baseLineno);
f.putProp(Node.SOURCENAME_PROP, sourceName);
f.putIntProp(Node.BASE_LINENO_PROP, baseLineno);
f.putIntProp(Node.END_LINENO_PROP, endLineno);
if (source != null)
f.putProp(Node.SOURCE_PROP, source);
return f;
}
/**
* Add a child to the back of the given node. This function
* breaks the Factory abstraction, but it removes a requirement
* from implementors of Node.
*/
public void addChildToBack(Object parent, Object child) {
((Node)parent).addChildToBack((Node)child);
} }
/** /**
* While * While
*/ */
public Object createWhile(Object cond, Object body, int lineno) { public Node createWhile(Node cond, Node body, CodePosition location) {
return new Node(TokenStream.WHILE, (Node)cond, (Node)body, lineno); return new Node(TokenStream.WHILE, cond, body, location);
} }
/** /**
* DoWhile * DoWhile
*/ */
public Object createDoWhile(Object body, Object cond, int lineno) { public Node createDoWhile(Node body, Node cond, CodePosition location) {
return new Node(TokenStream.DO, (Node)body, (Node)cond, lineno); return new Node(TokenStream.DO, body, cond, location);
} }
/** /**
* For * For
*/ */
public Object createFor(Object init, Object test, Object incr, public Node createFor(Node init, Node test, Node incr, Node body, CodePosition location) {
Object body, int lineno) return new Node(TokenStream.FOR, init, test, incr, body, location);
{
return new Node(TokenStream.FOR, (Node)init, (Node)test, (Node)incr,
(Node)body);
} }
/** /**
* For .. In * For .. In
* *
*/ */
public Object createForIn(Object lhs, Object obj, Object body, int lineno) { public Node createForIn(Node lhs, Node obj, Node body, CodePosition location) {
return new Node(TokenStream.FOR, (Node)lhs, (Node)obj, (Node)body); return new Node(TokenStream.FOR, lhs, obj, body, location);
} }
/** /**
* Try/Catch/Finally * Try/Catch/Finally
*/ */
public Object createTryCatchFinally(Object tryblock, Object catchblocks, public Node createTryCatchFinally(Node tryblock, Node catchblocks, Node finallyblock, CodePosition location) {
Object finallyblock, int lineno)
{
if (finallyblock == null) { if (finallyblock == null) {
return new Node(TokenStream.TRY, (Node)tryblock, (Node)catchblocks); return new Node(TokenStream.TRY, tryblock, catchblocks, location);
} }
return new Node(TokenStream.TRY, (Node)tryblock, return new Node(TokenStream.TRY, tryblock, catchblocks, finallyblock, location);
(Node)catchblocks, (Node)finallyblock);
} }
/** /**
@@ -291,81 +250,70 @@ public class IRFactory {
/** /**
* With * With
*/ */
public Object createWith(Object obj, Object body, int lineno) { public Node createWith(Node obj, Node body, CodePosition location) {
return new Node(TokenStream.WITH, (Node)obj, (Node)body, lineno); return new Node(TokenStream.WITH, obj, body, location);
} }
/** /**
* Array Literal * Array Literal
*/ */
public Object createArrayLiteral(Object obj) { public Node createArrayLiteral(Node obj) {
return obj; return obj;
} }
/** /**
* Object Literals * Object Literals
*/ */
public Object createObjectLiteral(Object obj) { public Node createObjectLiteral(Node obj) {
return obj; return obj;
} }
/** /**
* Regular expressions * Regular expressions
*/ */
public Object createRegExp(String string, String flags) { public Node createRegExp(String string, String flags, CodePosition location) {
return flags.length() == 0 return flags.length() == 0
? new Node(TokenStream.REGEXP, ? new Node(TokenStream.REGEXP,
Node.newString(string)) Node.newString(string, location), location)
: new Node(TokenStream.REGEXP, : new Node(TokenStream.REGEXP,
Node.newString(string), Node.newString(string, location),
Node.newString(flags)); Node.newString(flags, location),
location);
} }
/** /**
* If statement * If statement
*/ */
public Object createIf(Object cond, Object ifTrue, Object ifFalse, public Node createIf(Node cond, Node ifTrue, Node ifFalse, CodePosition location) {
int lineno)
{
if (ifFalse == null) if (ifFalse == null)
return new Node(TokenStream.IF, (Node)cond, (Node)ifTrue, lineno); return new Node(TokenStream.IF, cond, ifTrue, location);
return new Node(TokenStream.IF, (Node)cond, (Node)ifTrue, (Node)ifFalse, lineno); return new Node(TokenStream.IF, cond, ifTrue, ifFalse, location);
} }
public Object createTernary(Object cond, Object ifTrue, Object ifFalse) { public Node createTernary(Node cond, Node ifTrue, Node ifFalse, CodePosition location) {
return new Node(TokenStream.HOOK, return new Node(TokenStream.HOOK, cond, ifTrue, ifFalse, location);
(Node)cond, (Node)ifTrue, (Node)ifFalse);
} }
/** /**
* Unary * Unary
*/ */
public Object createUnary(int nodeType, Object child) { public Node createUnary(int nodeType, Node child, CodePosition location) {
Node childNode = (Node) child; return new Node(nodeType, child, location);
return new Node(nodeType, childNode);
} }
public Object createUnary(int nodeType, int nodeOp, Object child) { public Node createUnary(int nodeType, int nodeOp, Node child, CodePosition location) {
return new Node(nodeType, (Node)child, nodeOp); return new Node(nodeType, child, nodeOp, location);
} }
/** /**
* Binary * Binary
*/ */
public Object createBinary(int nodeType, Object left, Object right) { public Node createBinary(int nodeType, Node left, Node right, CodePosition location) {
Node temp;
switch (nodeType) { switch (nodeType) {
case TokenStream.DOT: case TokenStream.DOT:
nodeType = TokenStream.GETPROP; nodeType = TokenStream.GETPROP;
Node idNode = (Node) right; right.setType(TokenStream.STRING);
idNode.setType(TokenStream.STRING);
String id = idNode.getString();
if (id.equals("__proto__") || id.equals("__parent__")) {
Node result = new Node(nodeType, (Node) left);
result.putProp(Node.SPECIAL_PROP_PROP, id);
return result;
}
break; break;
case TokenStream.LB: case TokenStream.LB:
@@ -373,22 +321,17 @@ public class IRFactory {
nodeType = TokenStream.GETELEM; nodeType = TokenStream.GETELEM;
break; break;
} }
return new Node(nodeType, (Node)left, (Node)right); return new Node(nodeType, left, right, location);
} }
public Object createBinary(int nodeType, int nodeOp, Object left, public Node createBinary(int nodeType, int nodeOp, Node left, Node right, CodePosition location) {
Object right)
{
if (nodeType == TokenStream.ASSIGN) { if (nodeType == TokenStream.ASSIGN) {
return createAssignment(nodeOp, (Node) left, (Node) right, return createAssignment(nodeOp, left, right, location);
null, false);
} }
return new Node(nodeType, (Node) left, (Node) right, nodeOp); return new Node(nodeType, left, right, nodeOp, location);
} }
public Object createAssignment(int nodeOp, Node left, Node right, public Node createAssignment(int nodeOp, Node left, Node right, CodePosition location) {
Class convert, boolean postfix)
{
int nodeType = left.getType(); int nodeType = left.getType();
switch (nodeType) { switch (nodeType) {
case TokenStream.NAME: case TokenStream.NAME:
@@ -401,7 +344,7 @@ public class IRFactory {
reportError("msg.bad.lhs.assign"); reportError("msg.bad.lhs.assign");
} }
return new Node(TokenStream.ASSIGN, left, right, nodeOp); return new Node(TokenStream.ASSIGN, left, right, nodeOp, location);
} }
private void reportError(String msgResource) { private void reportError(String msgResource) {
@@ -46,13 +46,13 @@ public class Node implements Cloneable {
private static class NumberNode extends Node { private static class NumberNode extends Node {
NumberNode(int number) { NumberNode(int number, CodePosition location) {
super(TokenStream.NUMBER_INT); super(TokenStream.NUMBER_INT, location);
this.number = number; this.number = number;
} }
NumberNode(double number) { NumberNode(double number, CodePosition position) {
super(TokenStream.NUMBER); super(TokenStream.NUMBER, position);
this.number = number; this.number = number;
} }
@@ -61,19 +61,13 @@ public class Node implements Cloneable {
return this.number; return this.number;
} }
@Override
public boolean equals(Object o) {
return o instanceof NumberNode
&& getDouble() == ((NumberNode) o).getDouble();
}
private double number; private double number;
} }
private static class StringNode extends Node { private static class StringNode extends Node {
StringNode(int type, String str) { StringNode(int type, String str, CodePosition position) {
super(type); super(type, position);
if (null == str) { if (null == str) {
throw new IllegalArgumentException("StringNode: str is null"); throw new IllegalArgumentException("StringNode: str is null");
} }
@@ -99,13 +93,6 @@ public class Node implements Cloneable {
this.str = str; this.str = str;
} }
@Override
public boolean equals(Object o) {
if (!(o instanceof StringNode)) { return false; }
return o instanceof StringNode
&& this.str.equals(((StringNode) o).str);
}
private String str; private String str;
} }
@@ -113,30 +100,46 @@ public class Node implements Cloneable {
type = nodeType; type = nodeType;
} }
public Node(int nodeType, Node child) { public Node(int nodeType, Node child, CodePosition position) {
type = nodeType; type = nodeType;
first = last = child; first = last = child;
child.next = null; child.next = null;
this.position = position;
} }
public Node(int nodeType, Node left, Node right) { public Node(int nodeType, Node child, int operation, CodePosition position) {
type = nodeType;
first = last = child;
child.next = null;
this.operation = operation;
this.position = position;
}
public Node(int nodeType, Node left, Node right, CodePosition position) {
this(nodeType, left, right, -1, position);
}
public Node(int nodeType, Node left, Node right, int operation, CodePosition position) {
type = nodeType; type = nodeType;
first = left; first = left;
last = right; last = right;
left.next = right; left.next = right;
right.next = null; right.next = null;
this.operation = operation;
this.position = position;
} }
public Node(int nodeType, Node left, Node mid, Node right) { public Node(int nodeType, Node left, Node mid, Node right, CodePosition position) {
type = nodeType; type = nodeType;
first = left; first = left;
last = right; last = right;
left.next = mid; left.next = mid;
mid.next = right; mid.next = right;
right.next = null; right.next = null;
this.position = position;
} }
public Node(int nodeType, Node left, Node mid, Node mid2, Node right) { public Node(int nodeType, Node left, Node mid, Node mid2, Node right, CodePosition position) {
type = nodeType; type = nodeType;
first = left; first = left;
last = right; last = right;
@@ -144,6 +147,7 @@ public class Node implements Cloneable {
mid.next = mid2; mid.next = mid2;
mid2.next = right; mid2.next = right;
right.next = null; right.next = null;
this.position = position;
} }
public Node(int nodeType, Node[] children) { public Node(int nodeType, Node[] children) {
@@ -166,40 +170,31 @@ public class Node implements Cloneable {
} }
} }
public Node(int nodeType, int value) { public Node(int nodeType, CodePosition position) {
type = nodeType; type = nodeType;
intDatum = value; this.position = position;
} }
public Node(int nodeType, Node child, int value) { public Node(int nodeType, int operation, CodePosition position) {
this(nodeType, child); type = nodeType;
intDatum = value; this.operation = operation;
this.position = position;
} }
public Node(int nodeType, Node left, Node right, int value) { public static Node newNumber(int number, CodePosition position) {
this(nodeType, left, right); return new NumberNode(number, position);
intDatum = value;
} }
public Node(int nodeType, Node left, Node mid, Node right, int value) { public static Node newNumber(double number, CodePosition position) {
this(nodeType, left, mid, right); return new NumberNode(number, position);
intDatum = value;
} }
public static Node newNumber(int number) { public static Node newString(String str, CodePosition position) {
return new NumberNode(number); return new StringNode(TokenStream.STRING, str, position);
} }
public static Node newNumber(double number) { public static Node newString(int type, String str, CodePosition position) {
return new NumberNode(number); return new StringNode(type, str, position);
}
public static Node newString(String str) {
return new StringNode(TokenStream.STRING, str);
}
public static Node newString(int type, String str) {
return new StringNode(type, str);
} }
public int getType() { public int getType() {
@@ -210,10 +205,6 @@ public class Node implements Cloneable {
this.type = type; this.type = type;
} }
public int getIntDatum() {
return this.intDatum;
}
public Node getFirstChild() { public Node getFirstChild() {
return first; return first;
} }
@@ -353,73 +344,12 @@ public class Node implements Cloneable {
return null; return null;
} }
public Object getProp(int propType) {
if (props == null)
return null;
return props.getObject(propType);
}
public void putProp(int propType, Object prop) {
if (prop == null) {
removeProp(propType);
}
else {
if (props == null) {
props = new UintMap(2);
}
props.put(propType, prop);
}
}
public void putIntProp(int propType, int prop) {
if (props == null)
props = new UintMap(2);
props.put(propType, prop);
}
public void removeProp(int propType) {
if (props != null) {
props.remove(propType);
}
}
public int getOperation() { public int getOperation() {
switch (type) { return operation;
case TokenStream.EQOP:
case TokenStream.RELOP:
case TokenStream.UNARYOP:
case TokenStream.PRIMARY:
return intDatum;
}
Context.codeBug();
return 0;
} }
public int getLineno() { public CodePosition getPosition() {
if (hasLineno()) { return position;
return intDatum;
}
return -1;
}
private boolean hasLineno() {
switch (type) {
case TokenStream.EXPRSTMT:
case TokenStream.BLOCK:
case TokenStream.VAR:
case TokenStream.WHILE:
case TokenStream.DO:
case TokenStream.SWITCH:
case TokenStream.CATCH:
case TokenStream.THROW:
case TokenStream.RETURN:
case TokenStream.BREAK:
case TokenStream.CONTINUE:
case TokenStream.WITH:
case TokenStream.IF:
return true;
}
return false;
} }
/** Can only be called when <tt>getType() == TokenStream.NUMBER</tt> */ /** Can only be called when <tt>getType() == TokenStream.NUMBER</tt> */
@@ -437,22 +367,6 @@ public class Node implements Cloneable {
throw new UnsupportedOperationException(this + " is not a string node"); throw new UnsupportedOperationException(this + " is not a string node");
} }
/**
* Not usefully implemented.
*/
@Override
public final int hashCode() {
assert false : "hashCode not designed";
return 42; // any arbitrary constant will do
}
@Override
public boolean equals(Object o) {
if (!(o instanceof Node)) { return false; }
return hasLineno()
|| this.getIntDatum() == ((Node) o).getIntDatum();
}
@Override @Override
public String toString() { public String toString() {
if (Context.printTrees) { if (Context.printTrees) {
@@ -480,57 +394,19 @@ public class Node implements Cloneable {
break; break;
} }
} }
if (intDatum != -1) { if (operation != -1) {
sb.append(' '); sb.append(' ');
sb.append(intDatum); sb.append(operation);
}
if (props == null)
return sb.toString();
int[] keys = props.getKeys();
for (int i = 0; i != keys.length; ++i) {
int key = keys[i];
sb.append(" [");
sb.append(propToString(key));
sb.append(": ");
switch (key) {
case FIXUPS_PROP : // can't add this as it recurses
sb.append("fixups property");
break;
case SOURCE_PROP : // can't add this as it has unprintables
sb.append("source property");
break;
case TARGETBLOCK_PROP : // can't add this as it recurses
sb.append("target block property");
break;
case LASTUSE_PROP : // can't add this as it is dull
sb.append("last use property");
break;
default :
Object obj = props.getObject(key);
if (obj != null) {
sb.append(obj.toString());
} else {
sb.append(props.getExistingInt(key));
}
break;
}
sb.append(']');
} }
return sb.toString(); return sb.toString();
} }
return null; return null;
} }
public void setIsSyntheticBlock(boolean val) {
isSyntheticBlock = val;
}
int type; // type of the node; TokenStream.NAME for example int type; // type of the node; TokenStream.NAME for example
Node next; // next sibling Node next; // next sibling
private Node first; // first element of a linked list of children private Node first; // first element of a linked list of children
private Node last; // last element of a linked list of children private Node last; // last element of a linked list of children
private int intDatum = -1; // encapsulated int data; depends on type private CodePosition position;
private UintMap props; private int operation;
private boolean isSyntheticBlock = false;
} }
@@ -86,19 +86,17 @@ public class Parser extends Observable {
* will be returned. (The parse failure will result in a call to the current * will be returned. (The parse failure will result in a call to the current
* Context's ErrorReporter.) * Context's ErrorReporter.)
*/ */
public Object parse(TokenStream ts) throws IOException { public Node parse(TokenStream ts) throws IOException {
this.ok = true; this.ok = true;
sourceTop = 0; sourceTop = 0;
functionNumber = 0; functionNumber = 0;
int tt; // last token from getToken(); int tt; // last token from getToken();
int baseLineno = ts.getLineno(); // line number where source starts
/* /*
* so we have something to add nodes to until we've collected all the source * so we have something to add nodes to until we've collected all the source
*/ */
Object tempBlock = nf.createLeaf(TokenStream.BLOCK); Node tempBlock = nf.createLeaf(TokenStream.BLOCK, ts.tokenPosition);
((Node) tempBlock).setIsSyntheticBlock(true);
while (true) { while (true) {
ts.flags |= TokenStream.TSF_REGEXP; ts.flags |= TokenStream.TSF_REGEXP;
@@ -111,7 +109,7 @@ public class Parser extends Observable {
if (tt == TokenStream.FUNCTION) { if (tt == TokenStream.FUNCTION) {
try { try {
nf.addChildToBack(tempBlock, function(ts, false)); tempBlock.addChildToBack(function(ts, false));
} }
catch (JavaScriptException e) { catch (JavaScriptException e) {
this.ok = false; this.ok = false;
@@ -120,7 +118,7 @@ public class Parser extends Observable {
} }
else { else {
ts.ungetToken(tt); ts.ungetToken(tt);
nf.addChildToBack(tempBlock, statement(ts)); tempBlock.addChildToBack(statement(ts));
} }
} }
@@ -129,9 +127,7 @@ public class Parser extends Observable {
return null; return null;
} }
Object pn = nf.createScript(tempBlock, ts.getSourceName(), baseLineno, ts.getLineno(), null); return nf.createScript(tempBlock);
((Node) pn).setIsSyntheticBlock(true);
return pn;
} }
/* /*
@@ -139,21 +135,21 @@ public class Parser extends Observable {
* to be needed for tree generation... it'd only be useful for checking * to be needed for tree generation... it'd only be useful for checking
* argument hiding, which I'm not doing anyway... * argument hiding, which I'm not doing anyway...
*/ */
private Object parseFunctionBody(TokenStream ts) throws IOException { private Node parseFunctionBody(TokenStream ts) throws IOException {
int oldflags = ts.flags; int oldflags = ts.flags;
ts.flags &= ~(TokenStream.TSF_RETURN_EXPR | TokenStream.TSF_RETURN_VOID); ts.flags &= ~(TokenStream.TSF_RETURN_EXPR | TokenStream.TSF_RETURN_VOID);
ts.flags |= TokenStream.TSF_FUNCTION; ts.flags |= TokenStream.TSF_FUNCTION;
Object pn = nf.createBlock(ts.getLineno()); Node pn = nf.createBlock(ts.tokenPosition);
try { try {
int tt; int tt;
while ((tt = ts.peekToken()) > TokenStream.EOF && tt != TokenStream.RC) { while ((tt = ts.peekToken()) > TokenStream.EOF && tt != TokenStream.RC) {
if (tt == TokenStream.FUNCTION) { if (tt == TokenStream.FUNCTION) {
ts.getToken(); ts.getToken();
nf.addChildToBack(pn, function(ts, false)); pn.addChildToBack(function(ts, false));
} }
else { else {
nf.addChildToBack(pn, statement(ts)); pn.addChildToBack(statement(ts));
} }
} }
} }
@@ -170,31 +166,30 @@ public class Parser extends Observable {
return pn; return pn;
} }
private Object function(TokenStream ts, boolean isExpr) throws IOException, JavaScriptException { private Node function(TokenStream ts, boolean isExpr) throws IOException, JavaScriptException {
notifyObservers(new ParserEvents.OnFunctionParsingStart()); notifyObservers(new ParserEvents.OnFunctionParsingStart());
int baseLineno = ts.getLineno(); // line number where source starts CodePosition basePosition = ts.tokenPosition;
String name; Node nameNode;
Object memberExprNode = null; Node memberExprNode = null;
if (ts.matchToken(TokenStream.NAME)) { if (ts.matchToken(TokenStream.NAME)) {
name = ts.getString(); nameNode = nf.createName(ts.getString(), basePosition);
if (!ts.matchToken(TokenStream.LP)) { if (!ts.matchToken(TokenStream.LP)) {
if (Context.getContext().hasFeature(Context.FEATURE_MEMBER_EXPR_AS_FUNCTION_NAME)) { if (Context.getContext().hasFeature(Context.FEATURE_MEMBER_EXPR_AS_FUNCTION_NAME)) {
// Extension to ECMA: if 'function <name>' does not follow // Extension to ECMA: if 'function <name>' does not follow
// by '(', assume <name> starts memberExpr // by '(', assume <name> starts memberExpr
Object memberExprHead = nf.createName(name); Node memberExprHead = nameNode;
name = null; nameNode = null;
memberExprNode = memberExprTail(ts, false, memberExprHead); memberExprNode = memberExprTail(ts, false, memberExprHead, basePosition);
} }
mustMatchToken(ts, TokenStream.LP, "msg.no.paren.parms"); mustMatchToken(ts, TokenStream.LP, "msg.no.paren.parms");
} }
} }
else if (ts.matchToken(TokenStream.LP)) { else if (ts.matchToken(TokenStream.LP)) {
// Anonymous function // Anonymous function
name = null; nameNode = null;
} }
else { else {
name = null;
if (Context.getContext().hasFeature(Context.FEATURE_MEMBER_EXPR_AS_FUNCTION_NAME)) { if (Context.getContext().hasFeature(Context.FEATURE_MEMBER_EXPR_AS_FUNCTION_NAME)) {
// Note that memberExpr can not start with '(' like // Note that memberExpr can not start with '(' like
// in (1+2).toString, because 'function (' already // in (1+2).toString, because 'function (' already
@@ -202,6 +197,7 @@ public class Parser extends Observable {
memberExprNode = memberExpr(ts, false); memberExprNode = memberExpr(ts, false);
} }
mustMatchToken(ts, TokenStream.LP, "msg.no.paren.parms"); mustMatchToken(ts, TokenStream.LP, "msg.no.paren.parms");
nameNode = null;
} }
++functionNumber; ++functionNumber;
@@ -210,17 +206,18 @@ public class Parser extends Observable {
// function to parent source // function to parent source
int savedSourceTop = sourceTop; int savedSourceTop = sourceTop;
int savedFunctionNumber = functionNumber; int savedFunctionNumber = functionNumber;
Object args; Node args;
Object body; Node body;
try { try {
functionNumber = 0; functionNumber = 0;
args = nf.createLeaf(TokenStream.LP); args = nf.createLeaf(TokenStream.LP, ts.tokenPosition);
if (!ts.matchToken(TokenStream.GWT)) { if (!ts.matchToken(TokenStream.GWT)) {
do { do {
CodePosition namePosition = ts.tokenPosition;
mustMatchToken(ts, TokenStream.NAME, "msg.no.parm"); mustMatchToken(ts, TokenStream.NAME, "msg.no.parm");
String s = ts.getString(); String s = ts.getString();
nf.addChildToBack(args, nf.createName(s)); args.addChildToBack(nf.createName(s, namePosition));
} }
while (ts.matchToken(TokenStream.COMMA)); while (ts.matchToken(TokenStream.COMMA));
@@ -237,10 +234,9 @@ public class Parser extends Observable {
functionNumber = savedFunctionNumber; functionNumber = savedFunctionNumber;
} }
Object pn = nf.createFunction(name, args, body, ts.getSourceName(), Node pn = nf.createFunction(nameNode, args, body, basePosition);
baseLineno, ts.getLineno(), null, isExpr || memberExprNode != null);
if (memberExprNode != null) { if (memberExprNode != null) {
pn = nf.createBinary(TokenStream.ASSIGN, TokenStream.NOP, memberExprNode, pn); pn = nf.createBinary(TokenStream.ASSIGN, TokenStream.NOP, memberExprNode, pn, basePosition);
} }
// Add EOL but only if function is not part of expression, in which // Add EOL but only if function is not part of expression, in which
@@ -253,19 +249,19 @@ public class Parser extends Observable {
return pn; return pn;
} }
private Object statements(TokenStream ts) throws IOException { private Node statements(TokenStream ts) throws IOException {
Object pn = nf.createBlock(ts.getLineno()); Node pn = nf.createBlock(ts.tokenPosition);
int tt; int tt;
while ((tt = ts.peekToken()) > TokenStream.EOF && tt != TokenStream.RC) { while ((tt = ts.peekToken()) > TokenStream.EOF && tt != TokenStream.RC) {
nf.addChildToBack(pn, statement(ts)); pn.addChildToBack(statement(ts));
} }
return pn; return pn;
} }
private Object condition(TokenStream ts) throws IOException, JavaScriptException { private Node condition(TokenStream ts) throws IOException, JavaScriptException {
Object pn; Node pn;
mustMatchToken(ts, TokenStream.LP, "msg.no.paren.cond"); mustMatchToken(ts, TokenStream.LP, "msg.no.paren.cond");
pn = expr(ts, false); pn = expr(ts, false);
mustMatchToken(ts, TokenStream.GWT, "msg.no.paren.after.cond"); mustMatchToken(ts, TokenStream.GWT, "msg.no.paren.after.cond");
@@ -297,7 +293,8 @@ public class Parser extends Observable {
} }
// match a NAME; return null if no match. // match a NAME; return null if no match.
private String matchLabel(TokenStream ts) throws IOException, JavaScriptException { private Node matchLabel(TokenStream ts) throws IOException, JavaScriptException {
CodePosition position = ts.tokenPosition;
int lineno = ts.getLineno(); int lineno = ts.getLineno();
String label = null; String label = null;
@@ -312,23 +309,23 @@ public class Parser extends Observable {
wellTerminated(ts, TokenStream.ERROR); wellTerminated(ts, TokenStream.ERROR);
} }
return label; return label != null ? nf.createString(label, position) : null;
} }
private Object statement(TokenStream ts) throws IOException { private Node statement(TokenStream ts) throws IOException {
CodePosition position = ts.lastPosition;
try { try {
return statementHelper(ts); return statementHelper(ts);
} }
catch (JavaScriptException e) { catch (JavaScriptException e) {
// skip to end of statement // skip to end of statement
int lineno = ts.getLineno();
int t; int t;
do { do {
t = ts.getToken(); t = ts.getToken();
} }
while (t != TokenStream.SEMI && t != TokenStream.EOL while (t != TokenStream.SEMI && t != TokenStream.EOL
&& t != TokenStream.EOF && t != TokenStream.ERROR); && t != TokenStream.EOF && t != TokenStream.ERROR);
return nf.createExprStatement(nf.createName("error"), lineno); return nf.createExprStatement(nf.createName("error", position), position);
} }
} }
@@ -337,47 +334,47 @@ public class Parser extends Observable {
* implemented. * implemented.
*/ */
private Object statementHelper(TokenStream ts) throws IOException, JavaScriptException { private Node statementHelper(TokenStream ts) throws IOException, JavaScriptException {
Object pn; Node pn;
int tt; int tt;
int lastExprType; // For wellTerminated int lastExprType; // For wellTerminated
tt = ts.getToken(); tt = ts.getToken();
CodePosition position = ts.tokenPosition;
switch (tt) { switch (tt) {
case TokenStream.IF: { case TokenStream.IF: {
int lineno = ts.getLineno(); Node cond = condition(ts);
Object cond = condition(ts); Node ifTrue = statement(ts);
Object ifTrue = statement(ts); Node ifFalse = null;
Object ifFalse = null;
if (ts.matchToken(TokenStream.ELSE)) { if (ts.matchToken(TokenStream.ELSE)) {
ifFalse = statement(ts); ifFalse = statement(ts);
} }
pn = nf.createIf(cond, ifTrue, ifFalse, lineno); pn = nf.createIf(cond, ifTrue, ifFalse, position);
break; break;
} }
case TokenStream.SWITCH: { case TokenStream.SWITCH: {
pn = nf.createSwitch(ts.getLineno()); pn = nf.createSwitch(position);
Object cur_case = null; // to kill warning Node curCase = null; // to kill warning
Object case_statements; Node caseStatements;
mustMatchToken(ts, TokenStream.LP, "msg.no.paren.switch"); mustMatchToken(ts, TokenStream.LP, "msg.no.paren.switch");
nf.addChildToBack(pn, expr(ts, false)); pn.addChildToBack(expr(ts, false));
mustMatchToken(ts, TokenStream.GWT, "msg.no.paren.after.switch"); mustMatchToken(ts, TokenStream.GWT, "msg.no.paren.after.switch");
mustMatchToken(ts, TokenStream.LC, "msg.no.brace.switch"); mustMatchToken(ts, TokenStream.LC, "msg.no.brace.switch");
while ((tt = ts.getToken()) != TokenStream.RC && tt != TokenStream.EOF) { while ((tt = ts.getToken()) != TokenStream.RC && tt != TokenStream.EOF) {
switch (tt) { switch (tt) {
case TokenStream.CASE: case TokenStream.CASE:
cur_case = nf.createUnary(TokenStream.CASE, expr(ts, false)); curCase = nf.createUnary(TokenStream.CASE, expr(ts, false), ts.tokenPosition);
break; break;
case TokenStream.DEFAULT: case TokenStream.DEFAULT:
cur_case = nf.createLeaf(TokenStream.DEFAULT); curCase = nf.createLeaf(TokenStream.DEFAULT, ts.tokenPosition);
// XXX check that there isn't more than one default // XXX check that there isn't more than one default
break; break;
@@ -387,60 +384,56 @@ public class Parser extends Observable {
} }
mustMatchToken(ts, TokenStream.COLON, "msg.no.colon.case"); mustMatchToken(ts, TokenStream.COLON, "msg.no.colon.case");
case_statements = nf.createLeaf(TokenStream.BLOCK); caseStatements = nf.createLeaf(TokenStream.BLOCK, null);
((Node) case_statements).setIsSyntheticBlock(true);
while ((tt = ts.peekToken()) != TokenStream.RC && tt != TokenStream.CASE while ((tt = ts.peekToken()) != TokenStream.RC && tt != TokenStream.CASE
&& tt != TokenStream.DEFAULT && tt != TokenStream.EOF) { && tt != TokenStream.DEFAULT && tt != TokenStream.EOF) {
nf.addChildToBack(case_statements, statement(ts)); caseStatements.addChildToBack(statement(ts));
} }
// assert cur_case // assert cur_case
nf.addChildToBack(cur_case, case_statements); if (curCase != null) {
curCase.addChildToBack(caseStatements);
}
nf.addChildToBack(pn, cur_case); pn.addChildToBack(curCase);
} }
break; break;
} }
case TokenStream.WHILE: { case TokenStream.WHILE: {
int lineno = ts.getLineno(); Node cond = condition(ts);
Object cond = condition(ts); Node body = statement(ts);
Object body = statement(ts);
pn = nf.createWhile(cond, body, lineno); pn = nf.createWhile(cond, body, position);
break; break;
} }
case TokenStream.DO: { case TokenStream.DO: {
int lineno = ts.getLineno(); Node body = statement(ts);
Object body = statement(ts);
mustMatchToken(ts, TokenStream.WHILE, "msg.no.while.do"); mustMatchToken(ts, TokenStream.WHILE, "msg.no.while.do");
Object cond = condition(ts); Node cond = condition(ts);
pn = nf.createDoWhile(body, cond, lineno); pn = nf.createDoWhile(body, cond, position);
break; break;
} }
case TokenStream.FOR: { case TokenStream.FOR: {
int lineno = ts.getLineno(); Node init; // Node init is also foo in 'foo in Object'
Node cond; // Node cond is also object in 'foo in Object'
Object init; // Node init is also foo in 'foo in Object' Node incr = null; // to kill warning
Object cond; // Node cond is also object in 'foo in Object' Node body;
Object incr = null; // to kill warning
Object body;
mustMatchToken(ts, TokenStream.LP, "msg.no.paren.for"); mustMatchToken(ts, TokenStream.LP, "msg.no.paren.for");
tt = ts.peekToken(); tt = ts.peekToken();
if (tt == TokenStream.SEMI) { if (tt == TokenStream.SEMI) {
init = nf.createLeaf(TokenStream.VOID); init = nf.createLeaf(TokenStream.VOID, null);
} }
else { else {
if (tt == TokenStream.VAR) { if (tt == TokenStream.VAR) {
// set init to a var list or initial // set init to a var list or initial
ts.getToken(); // throw away the 'var' token ts.getToken(); // throw away the 'var' token
init = variables(ts, true); init = variables(ts, true, ts.tokenPosition);
} }
else { else {
init = expr(ts, true); init = expr(ts, true);
@@ -457,7 +450,7 @@ public class Parser extends Observable {
mustMatchToken(ts, TokenStream.SEMI, "msg.no.semi.for"); mustMatchToken(ts, TokenStream.SEMI, "msg.no.semi.for");
if (ts.peekToken() == TokenStream.SEMI) { if (ts.peekToken() == TokenStream.SEMI) {
// no loop condition // no loop condition
cond = nf.createLeaf(TokenStream.VOID); cond = nf.createLeaf(TokenStream.VOID, null);
} }
else { else {
cond = expr(ts, false); cond = expr(ts, false);
@@ -465,7 +458,7 @@ public class Parser extends Observable {
mustMatchToken(ts, TokenStream.SEMI, "msg.no.semi.for.cond"); mustMatchToken(ts, TokenStream.SEMI, "msg.no.semi.for.cond");
if (ts.peekToken() == TokenStream.GWT) { if (ts.peekToken() == TokenStream.GWT) {
incr = nf.createLeaf(TokenStream.VOID); incr = nf.createLeaf(TokenStream.VOID, null);
} }
else { else {
incr = expr(ts, false); incr = expr(ts, false);
@@ -477,24 +470,22 @@ public class Parser extends Observable {
if (incr == null) { if (incr == null) {
// cond could be null if 'in obj' got eaten by the init node. // cond could be null if 'in obj' got eaten by the init node.
pn = nf.createForIn(init, cond, body, lineno); pn = nf.createForIn(init, cond, body, position);
} }
else { else {
pn = nf.createFor(init, cond, incr, body, lineno); pn = nf.createFor(init, cond, incr, body, position);
} }
break; break;
} }
case TokenStream.TRY: { case TokenStream.TRY: {
int lineno = ts.getLineno(); Node tryblock;
Node catchblocks;
Object tryblock; Node finallyblock = null;
Object catchblocks;
Object finallyblock = null;
tryblock = statement(ts); tryblock = statement(ts);
catchblocks = nf.createLeaf(TokenStream.BLOCK); catchblocks = nf.createLeaf(TokenStream.BLOCK, null);
boolean sawDefaultCatch = false; boolean sawDefaultCatch = false;
int peek = ts.peekToken(); int peek = ts.peekToken();
@@ -503,12 +494,13 @@ public class Parser extends Observable {
if (sawDefaultCatch) { if (sawDefaultCatch) {
reportError(ts, "msg.catch.unreachable"); reportError(ts, "msg.catch.unreachable");
} }
CodePosition catchPosition = ts.tokenPosition;
mustMatchToken(ts, TokenStream.LP, "msg.no.paren.catch"); mustMatchToken(ts, TokenStream.LP, "msg.no.paren.catch");
mustMatchToken(ts, TokenStream.NAME, "msg.bad.catchcond"); mustMatchToken(ts, TokenStream.NAME, "msg.bad.catchcond");
String varName = ts.getString(); Node varName = nf.createName(ts.getString(), ts.tokenPosition);
Object catchCond = null; Node catchCond = null;
if (ts.matchToken(TokenStream.IF)) { if (ts.matchToken(TokenStream.IF)) {
catchCond = expr(ts, false); catchCond = expr(ts, false);
} }
@@ -519,7 +511,7 @@ public class Parser extends Observable {
mustMatchToken(ts, TokenStream.GWT, "msg.bad.catchcond"); mustMatchToken(ts, TokenStream.GWT, "msg.bad.catchcond");
mustMatchToken(ts, TokenStream.LC, "msg.no.brace.catchblock"); mustMatchToken(ts, TokenStream.LC, "msg.no.brace.catchblock");
nf.addChildToBack(catchblocks, nf.createCatch(varName, catchCond, statements(ts), ts.getLineno())); catchblocks.addChildToBack(nf.createCatch(varName, catchCond, statements(ts), catchPosition));
mustMatchToken(ts, TokenStream.RC, "msg.no.brace.after.body"); mustMatchToken(ts, TokenStream.RC, "msg.no.brace.after.body");
} }
@@ -532,37 +524,32 @@ public class Parser extends Observable {
finallyblock = statement(ts); finallyblock = statement(ts);
} }
pn = nf.createTryCatchFinally(tryblock, catchblocks, finallyblock, lineno); pn = nf.createTryCatchFinally(tryblock, catchblocks, finallyblock, position);
break; break;
} }
case TokenStream.THROW: { case TokenStream.THROW: {
int lineno = ts.getLineno(); int lineno = ts.getLineno();
pn = nf.createThrow(expr(ts, false), lineno); pn = nf.createThrow(expr(ts, false), position);
if (lineno == ts.getLineno()) { if (lineno == ts.getLineno()) {
wellTerminated(ts, TokenStream.ERROR); wellTerminated(ts, TokenStream.ERROR);
} }
break; break;
} }
case TokenStream.BREAK: { case TokenStream.BREAK: {
int lineno = ts.getLineno();
// matchLabel only matches if there is one // matchLabel only matches if there is one
String label = matchLabel(ts); Node label = matchLabel(ts);
pn = nf.createBreak(label, lineno); pn = nf.createBreak(label, position);
break; break;
} }
case TokenStream.CONTINUE: { case TokenStream.CONTINUE: {
int lineno = ts.getLineno();
// matchLabel only matches if there is one // matchLabel only matches if there is one
String label = matchLabel(ts); Node label = matchLabel(ts);
pn = nf.createContinue(label, lineno); pn = nf.createContinue(label, position);
break; break;
} }
case TokenStream.DEBUGGER: { case TokenStream.DEBUGGER: {
int lineno = ts.getLineno(); pn = nf.createDebugger(position);
pn = nf.createDebugger(lineno);
break; break;
} }
case TokenStream.WITH: { case TokenStream.WITH: {
@@ -572,25 +559,24 @@ public class Parser extends Observable {
reportError(ts, "msg.jsni.unsupported.with"); reportError(ts, "msg.jsni.unsupported.with");
int lineno = ts.getLineno();
mustMatchToken(ts, TokenStream.LP, "msg.no.paren.with"); mustMatchToken(ts, TokenStream.LP, "msg.no.paren.with");
Object obj = expr(ts, false); Node obj = expr(ts, false);
mustMatchToken(ts, TokenStream.GWT, "msg.no.paren.after.with"); mustMatchToken(ts, TokenStream.GWT, "msg.no.paren.after.with");
Object body = statement(ts); Node body = statement(ts);
pn = nf.createWith(obj, body, lineno); pn = nf.createWith(obj, body, position);
break; break;
} }
case TokenStream.VAR: { case TokenStream.VAR: {
int lineno = ts.getLineno(); int lineno = ts.getLineno();
pn = variables(ts, false); pn = variables(ts, false, position);
if (ts.getLineno() == lineno) { if (ts.getLineno() == lineno) {
wellTerminated(ts, TokenStream.ERROR); wellTerminated(ts, TokenStream.ERROR);
} }
break; break;
} }
case TokenStream.RETURN: { case TokenStream.RETURN: {
Object retExpr = null; Node retExpr = null;
int lineno = 0; int lineno;
// bail if we're not in a (toplevel) function // bail if we're not in a (toplevel) function
if ((!insideFunction) && ((ts.flags & TokenStream.TSF_FUNCTION) == 0)) { if ((!insideFunction) && ((ts.flags & TokenStream.TSF_FUNCTION) == 0)) {
reportError(ts, "msg.bad.return"); reportError(ts, "msg.bad.return");
@@ -614,7 +600,7 @@ public class Parser extends Observable {
} }
// XXX ASSERT pn // XXX ASSERT pn
pn = nf.createReturn(retExpr, lineno); pn = nf.createReturn(retExpr, position);
break; break;
} }
case TokenStream.LC: case TokenStream.LC:
@@ -626,7 +612,7 @@ public class Parser extends Observable {
// Fall thru, to have a node for error recovery to work on // Fall thru, to have a node for error recovery to work on
case TokenStream.EOL: case TokenStream.EOL:
case TokenStream.SEMI: case TokenStream.SEMI:
pn = nf.createLeaf(TokenStream.VOID); pn = nf.createLeaf(TokenStream.VOID, ts.tokenPosition);
break; break;
default: { default: {
@@ -653,12 +639,12 @@ public class Parser extends Observable {
* follows: nf.addChildToBack(pn, statement(ts)); * follows: nf.addChildToBack(pn, statement(ts));
*/ */
String name = ts.getString(); String name = ts.getString();
pn = nf.createLabel(name, lineno); pn = nf.createLabel(nf.createString(name, position), position);
// bruce: added to make it easier to bind labels to the // bruce: added to make it easier to bind labels to the
// statements they modify // statements they modify
// //
nf.addChildToBack(pn, statement(ts)); pn.addChildToBack(statement(ts));
// depend on decompiling lookahead to guess that that // depend on decompiling lookahead to guess that that
// last name was a label. // last name was a label.
@@ -671,7 +657,7 @@ public class Parser extends Observable {
} }
} }
pn = nf.createExprStatement(pn, lineno); pn = nf.createExprStatement(pn, position);
/* /*
* Check explicitly against (multi-line) function statement. * Check explicitly against (multi-line) function statement.
@@ -693,15 +679,15 @@ public class Parser extends Observable {
return pn; return pn;
} }
private Object variables(TokenStream ts, boolean inForInit) throws IOException, JavaScriptException { private Node variables(TokenStream ts, boolean inForInit, CodePosition position) throws IOException, JavaScriptException {
Object pn = nf.createVariables(ts.getLineno()); Node pn = nf.createVariables(position);
while (true) { while (true) {
Object name; Node name;
Object init; Node init;
mustMatchToken(ts, TokenStream.NAME, "msg.bad.var"); mustMatchToken(ts, TokenStream.NAME, "msg.bad.var");
String s = ts.getString(); String s = ts.getString();
name = nf.createName(s); name = nf.createName(s, ts.tokenPosition);
// omitted check for argument hiding // omitted check for argument hiding
@@ -711,9 +697,9 @@ public class Parser extends Observable {
} }
init = assignExpr(ts, inForInit); init = assignExpr(ts, inForInit);
nf.addChildToBack(name, init); name.addChildToBack(init);
} }
nf.addChildToBack(pn, name); pn.addChildToBack(name);
if (!ts.matchToken(TokenStream.COMMA)) { if (!ts.matchToken(TokenStream.COMMA)) {
break; break;
} }
@@ -721,93 +707,90 @@ public class Parser extends Observable {
return pn; return pn;
} }
private Object expr(TokenStream ts, boolean inForInit) throws IOException, JavaScriptException { private Node expr(TokenStream ts, boolean inForInit) throws IOException, JavaScriptException {
Object pn = assignExpr(ts, inForInit); Node pn = assignExpr(ts, inForInit);
while (ts.matchToken(TokenStream.COMMA)) { while (ts.matchToken(TokenStream.COMMA)) {
pn = nf.createBinary(TokenStream.COMMA, pn, assignExpr(ts, inForInit)); pn = nf.createBinary(TokenStream.COMMA, pn, assignExpr(ts, inForInit), pn.getPosition());
} }
return pn; return pn;
} }
private Object assignExpr(TokenStream ts, boolean inForInit) throws IOException, JavaScriptException { private Node assignExpr(TokenStream ts, boolean inForInit) throws IOException, JavaScriptException {
Object pn = condExpr(ts, inForInit); Node pn = condExpr(ts, inForInit);
if (ts.matchToken(TokenStream.ASSIGN)) { if (ts.matchToken(TokenStream.ASSIGN)) {
// omitted: "invalid assignment left-hand side" check. // omitted: "invalid assignment left-hand side" check.
pn = nf.createBinary(TokenStream.ASSIGN, ts.getOp(), pn, assignExpr(ts, inForInit)); pn = nf.createBinary(TokenStream.ASSIGN, ts.getOp(), pn, assignExpr(ts, inForInit), pn.getPosition());
} }
return pn; return pn;
} }
private Object condExpr(TokenStream ts, boolean inForInit) throws IOException, JavaScriptException { private Node condExpr(TokenStream ts, boolean inForInit) throws IOException, JavaScriptException {
Object ifTrue; Node pn = orExpr(ts, inForInit);
Object ifFalse;
Object pn = orExpr(ts, inForInit);
if (ts.matchToken(TokenStream.HOOK)) { if (ts.matchToken(TokenStream.HOOK)) {
ifTrue = assignExpr(ts, false); Node ifTrue = assignExpr(ts, false);
mustMatchToken(ts, TokenStream.COLON, "msg.no.colon.cond"); mustMatchToken(ts, TokenStream.COLON, "msg.no.colon.cond");
ifFalse = assignExpr(ts, inForInit); Node ifFalse = assignExpr(ts, inForInit);
return nf.createTernary(pn, ifTrue, ifFalse); return nf.createTernary(pn, ifTrue, ifFalse, pn.getPosition());
} }
return pn; return pn;
} }
private Object orExpr(TokenStream ts, boolean inForInit) throws IOException, JavaScriptException { private Node orExpr(TokenStream ts, boolean inForInit) throws IOException, JavaScriptException {
Object pn = andExpr(ts, inForInit); Node pn = andExpr(ts, inForInit);
if (ts.matchToken(TokenStream.OR)) { while (ts.matchToken(TokenStream.OR)) {
pn = nf.createBinary(TokenStream.OR, pn, orExpr(ts, inForInit)); pn = nf.createBinary(TokenStream.OR, pn, andExpr(ts, inForInit), pn.getPosition());
} }
return pn; return pn;
} }
private Object andExpr(TokenStream ts, boolean inForInit) throws IOException, JavaScriptException { private Node andExpr(TokenStream ts, boolean inForInit) throws IOException, JavaScriptException {
Object pn = bitOrExpr(ts, inForInit); Node pn = bitOrExpr(ts, inForInit);
if (ts.matchToken(TokenStream.AND)) { while (ts.matchToken(TokenStream.AND)) {
pn = nf.createBinary(TokenStream.AND, pn, andExpr(ts, inForInit)); pn = nf.createBinary(TokenStream.AND, pn, bitOrExpr(ts, inForInit), pn.getPosition());
} }
return pn; return pn;
} }
private Object bitOrExpr(TokenStream ts, boolean inForInit) throws IOException, JavaScriptException { private Node bitOrExpr(TokenStream ts, boolean inForInit) throws IOException, JavaScriptException {
Object pn = bitXorExpr(ts, inForInit); Node pn = bitXorExpr(ts, inForInit);
while (ts.matchToken(TokenStream.BITOR)) { while (ts.matchToken(TokenStream.BITOR)) {
pn = nf.createBinary(TokenStream.BITOR, pn, bitXorExpr(ts, inForInit)); pn = nf.createBinary(TokenStream.BITOR, pn, bitXorExpr(ts, inForInit), pn.getPosition());
} }
return pn; return pn;
} }
private Object bitXorExpr(TokenStream ts, boolean inForInit) throws IOException, JavaScriptException { private Node bitXorExpr(TokenStream ts, boolean inForInit) throws IOException, JavaScriptException {
Object pn = bitAndExpr(ts, inForInit); Node pn = bitAndExpr(ts, inForInit);
while (ts.matchToken(TokenStream.BITXOR)) { while (ts.matchToken(TokenStream.BITXOR)) {
pn = nf.createBinary(TokenStream.BITXOR, pn, bitAndExpr(ts, inForInit)); pn = nf.createBinary(TokenStream.BITXOR, pn, bitAndExpr(ts, inForInit), pn.getPosition());
} }
return pn; return pn;
} }
private Object bitAndExpr(TokenStream ts, boolean inForInit) throws IOException, JavaScriptException { private Node bitAndExpr(TokenStream ts, boolean inForInit) throws IOException, JavaScriptException {
Object pn = eqExpr(ts, inForInit); Node pn = eqExpr(ts, inForInit);
while (ts.matchToken(TokenStream.BITAND)) { while (ts.matchToken(TokenStream.BITAND)) {
pn = nf.createBinary(TokenStream.BITAND, pn, eqExpr(ts, inForInit)); pn = nf.createBinary(TokenStream.BITAND, pn, eqExpr(ts, inForInit), pn.getPosition());
} }
return pn; return pn;
} }
private Object eqExpr(TokenStream ts, boolean inForInit) throws IOException, JavaScriptException { private Node eqExpr(TokenStream ts, boolean inForInit) throws IOException, JavaScriptException {
Object pn = relExpr(ts, inForInit); Node pn = relExpr(ts, inForInit);
while (ts.matchToken(TokenStream.EQOP)) { while (ts.matchToken(TokenStream.EQOP)) {
pn = nf.createBinary(TokenStream.EQOP, ts.getOp(), pn, relExpr(ts, inForInit)); pn = nf.createBinary(TokenStream.EQOP, ts.getOp(), pn, relExpr(ts, inForInit), pn.getPosition());
} }
return pn; return pn;
} }
private Object relExpr(TokenStream ts, boolean inForInit) throws IOException, JavaScriptException { private Node relExpr(TokenStream ts, boolean inForInit) throws IOException, JavaScriptException {
Object pn = shiftExpr(ts); Node pn = shiftExpr(ts);
while (ts.matchToken(TokenStream.RELOP)) { while (ts.matchToken(TokenStream.RELOP)) {
int op = ts.getOp(); int op = ts.getOp();
if (inForInit && op == TokenStream.IN) { if (inForInit && op == TokenStream.IN) {
@@ -815,66 +798,67 @@ public class Parser extends Observable {
break; break;
} }
pn = nf.createBinary(TokenStream.RELOP, op, pn, shiftExpr(ts)); pn = nf.createBinary(TokenStream.RELOP, op, pn, shiftExpr(ts), pn.getPosition());
} }
return pn; return pn;
} }
private Object shiftExpr(TokenStream ts) throws IOException, JavaScriptException { private Node shiftExpr(TokenStream ts) throws IOException, JavaScriptException {
Object pn = addExpr(ts); Node pn = addExpr(ts);
while (ts.matchToken(TokenStream.SHOP)) { while (ts.matchToken(TokenStream.SHOP)) {
pn = nf.createBinary(TokenStream.SHOP, ts.getOp(), pn, addExpr(ts)); pn = nf.createBinary(TokenStream.SHOP, ts.getOp(), pn, addExpr(ts), pn.getPosition());
} }
return pn; return pn;
} }
private Object addExpr(TokenStream ts) throws IOException, JavaScriptException { private Node addExpr(TokenStream ts) throws IOException, JavaScriptException {
int tt; int tt;
Object pn = mulExpr(ts); Node pn = mulExpr(ts);
while ((tt = ts.getToken()) == TokenStream.ADD || tt == TokenStream.SUB) { while ((tt = ts.getToken()) == TokenStream.ADD || tt == TokenStream.SUB) {
// flushNewLines // flushNewLines
pn = nf.createBinary(tt, pn, mulExpr(ts)); pn = nf.createBinary(tt, pn, mulExpr(ts), pn.getPosition());
} }
ts.ungetToken(tt); ts.ungetToken(tt);
return pn; return pn;
} }
private Object mulExpr(TokenStream ts) throws IOException, JavaScriptException { private Node mulExpr(TokenStream ts) throws IOException, JavaScriptException {
int tt; int tt;
Object pn = unaryExpr(ts); Node pn = unaryExpr(ts);
while ((tt = ts.peekToken()) == TokenStream.MUL || tt == TokenStream.DIV || tt == TokenStream.MOD) { while ((tt = ts.peekToken()) == TokenStream.MUL || tt == TokenStream.DIV || tt == TokenStream.MOD) {
tt = ts.getToken(); tt = ts.getToken();
pn = nf.createBinary(tt, pn, unaryExpr(ts)); pn = nf.createBinary(tt, pn, unaryExpr(ts), pn.getPosition());
} }
return pn; return pn;
} }
private Object unaryExpr(TokenStream ts) throws IOException, JavaScriptException { private Node unaryExpr(TokenStream ts) throws IOException, JavaScriptException {
int tt; int tt;
ts.flags |= TokenStream.TSF_REGEXP; ts.flags |= TokenStream.TSF_REGEXP;
tt = ts.getToken(); tt = ts.getToken();
ts.flags &= ~TokenStream.TSF_REGEXP; ts.flags &= ~TokenStream.TSF_REGEXP;
CodePosition position = ts.tokenPosition;
switch (tt) { switch (tt) {
case TokenStream.UNARYOP: case TokenStream.UNARYOP:
return nf.createUnary(TokenStream.UNARYOP, ts.getOp(), unaryExpr(ts)); return nf.createUnary(TokenStream.UNARYOP, ts.getOp(), unaryExpr(ts), position);
case TokenStream.ADD: case TokenStream.ADD:
case TokenStream.SUB: case TokenStream.SUB:
return nf.createUnary(TokenStream.UNARYOP, tt, unaryExpr(ts)); return nf.createUnary(TokenStream.UNARYOP, tt, unaryExpr(ts), position);
case TokenStream.INC: case TokenStream.INC:
case TokenStream.DEC: case TokenStream.DEC:
return nf.createUnary(tt, TokenStream.PRE, memberExpr(ts, true)); return nf.createUnary(tt, TokenStream.PRE, memberExpr(ts, true), position);
case TokenStream.DELPROP: case TokenStream.DELPROP:
return nf.createUnary(TokenStream.DELPROP, unaryExpr(ts)); return nf.createUnary(TokenStream.DELPROP, unaryExpr(ts), position);
case TokenStream.ERROR: case TokenStream.ERROR:
break; break;
@@ -884,7 +868,7 @@ public class Parser extends Observable {
int lineno = ts.getLineno(); int lineno = ts.getLineno();
Object pn = memberExpr(ts, true); Node pn = memberExpr(ts, true);
/* /*
* don't look across a newline boundary for a postfix incop. * don't look across a newline boundary for a postfix incop.
@@ -897,21 +881,21 @@ public class Parser extends Observable {
if (((peeked = ts.peekToken()) == TokenStream.INC || peeked == TokenStream.DEC) if (((peeked = ts.peekToken()) == TokenStream.INC || peeked == TokenStream.DEC)
&& ts.getLineno() == lineno) { && ts.getLineno() == lineno) {
int pf = ts.getToken(); int pf = ts.getToken();
return nf.createUnary(pf, TokenStream.POST, pn); return nf.createUnary(pf, TokenStream.POST, pn, position);
} }
return pn; return pn;
} }
return nf.createName("err"); // Only reached on error. Try to continue. return nf.createName("err", position); // Only reached on error. Try to continue.
} }
private Object argumentList(TokenStream ts, Object listNode) throws IOException, JavaScriptException { private Node argumentList(TokenStream ts, Node listNode) throws IOException, JavaScriptException {
boolean matched; boolean matched;
ts.flags |= TokenStream.TSF_REGEXP; ts.flags |= TokenStream.TSF_REGEXP;
matched = ts.matchToken(TokenStream.GWT); matched = ts.matchToken(TokenStream.GWT);
ts.flags &= ~TokenStream.TSF_REGEXP; ts.flags &= ~TokenStream.TSF_REGEXP;
if (!matched) { if (!matched) {
do { do {
nf.addChildToBack(listNode, assignExpr(ts, false)); listNode.addChildToBack(assignExpr(ts, false));
} }
while (ts.matchToken(TokenStream.COMMA)); while (ts.matchToken(TokenStream.COMMA));
@@ -920,10 +904,11 @@ public class Parser extends Observable {
return listNode; return listNode;
} }
private Object memberExpr(TokenStream ts, boolean allowCallSyntax) throws IOException, JavaScriptException { private Node memberExpr(TokenStream ts, boolean allowCallSyntax) throws IOException, JavaScriptException {
int tt; int tt;
Object pn; Node pn;
CodePosition position = ts.tokenPosition;
/* Check for new expressions. */ /* Check for new expressions. */
ts.flags |= TokenStream.TSF_REGEXP; ts.flags |= TokenStream.TSF_REGEXP;
@@ -934,8 +919,8 @@ public class Parser extends Observable {
ts.getToken(); ts.getToken();
/* Make a NEW node to append to. */ /* Make a NEW node to append to. */
pn = nf.createLeaf(TokenStream.NEW); pn = nf.createLeaf(TokenStream.NEW, position);
nf.addChildToBack(pn, memberExpr(ts, false)); pn.addChildToBack(memberExpr(ts, false));
if (ts.matchToken(TokenStream.LP)) { if (ts.matchToken(TokenStream.LP)) {
/* Add the arguments to pn, if any are supplied. */ /* Add the arguments to pn, if any are supplied. */
@@ -955,19 +940,19 @@ public class Parser extends Observable {
*/ */
tt = ts.peekToken(); tt = ts.peekToken();
if (tt == TokenStream.LC) { if (tt == TokenStream.LC) {
nf.addChildToBack(pn, primaryExpr(ts)); pn.addChildToBack(primaryExpr(ts));
} }
} }
else { else {
pn = primaryExpr(ts); pn = primaryExpr(ts);
} }
return memberExprTail(ts, allowCallSyntax, pn); return memberExprTail(ts, allowCallSyntax, pn, position);
} }
private Object memberExprTail( private Node memberExprTail(
TokenStream ts, boolean allowCallSyntax, TokenStream ts, boolean allowCallSyntax,
Object pn Node pn, CodePosition position
) throws IOException, JavaScriptException { ) throws IOException, JavaScriptException {
lastExprEndLine = ts.getLineno(); lastExprEndLine = ts.getLineno();
int tt; int tt;
@@ -976,7 +961,7 @@ public class Parser extends Observable {
ts.treatKeywordAsIdentifier = true; ts.treatKeywordAsIdentifier = true;
mustMatchToken(ts, TokenStream.NAME, "msg.no.name.after.dot"); mustMatchToken(ts, TokenStream.NAME, "msg.no.name.after.dot");
ts.treatKeywordAsIdentifier = false; ts.treatKeywordAsIdentifier = false;
pn = nf.createBinary(TokenStream.DOT, pn, nf.createName(ts.getString())); pn = nf.createBinary(TokenStream.DOT, pn, nf.createName(ts.getString(), ts.tokenPosition), position);
/* /*
* pn = nf.createBinary(ts.DOT, pn, memberExpr(ts)) is the version in * pn = nf.createBinary(ts.DOT, pn, memberExpr(ts)) is the version in
* Brendan's IR C version. Not in ECMA... does it reflect the 'new' * Brendan's IR C version. Not in ECMA... does it reflect the 'new'
@@ -985,14 +970,14 @@ public class Parser extends Observable {
lastExprEndLine = ts.getLineno(); lastExprEndLine = ts.getLineno();
} }
else if (tt == TokenStream.LB) { else if (tt == TokenStream.LB) {
pn = nf.createBinary(TokenStream.LB, pn, expr(ts, false)); pn = nf.createBinary(TokenStream.LB, pn, expr(ts, false), position);
mustMatchToken(ts, TokenStream.RB, "msg.no.bracket.index"); mustMatchToken(ts, TokenStream.RB, "msg.no.bracket.index");
lastExprEndLine = ts.getLineno(); lastExprEndLine = ts.getLineno();
} }
else if (allowCallSyntax && tt == TokenStream.LP) { else if (allowCallSyntax && tt == TokenStream.LP) {
/* make a call node */ /* make a call node */
pn = nf.createUnary(TokenStream.CALL, pn); pn = nf.createUnary(TokenStream.CALL, pn, position);
/* Add the arguments to pn, if any are supplied. */ /* Add the arguments to pn, if any are supplied. */
pn = argumentList(ts, pn); pn = argumentList(ts, pn);
@@ -1007,13 +992,14 @@ public class Parser extends Observable {
return pn; return pn;
} }
public Object primaryExpr(TokenStream ts) throws IOException, JavaScriptException { public Node primaryExpr(TokenStream ts) throws IOException, JavaScriptException {
int tt; int tt;
Object pn; Node pn;
ts.flags |= TokenStream.TSF_REGEXP; ts.flags |= TokenStream.TSF_REGEXP;
tt = ts.getToken(); tt = ts.getToken();
CodePosition position = ts.tokenPosition;
ts.flags &= ~TokenStream.TSF_REGEXP; ts.flags &= ~TokenStream.TSF_REGEXP;
switch (tt) { switch (tt) {
@@ -1022,7 +1008,7 @@ public class Parser extends Observable {
return function(ts, true); return function(ts, true);
case TokenStream.LB: { case TokenStream.LB: {
pn = nf.createLeaf(TokenStream.ARRAYLIT); pn = nf.createLeaf(TokenStream.ARRAYLIT, position);
ts.flags |= TokenStream.TSF_REGEXP; ts.flags |= TokenStream.TSF_REGEXP;
boolean matched = ts.matchToken(TokenStream.RB); boolean matched = ts.matchToken(TokenStream.RB);
@@ -1039,10 +1025,10 @@ public class Parser extends Observable {
} }
if (tt == TokenStream.COMMA) { if (tt == TokenStream.COMMA) {
nf.addChildToBack(pn, nf.createLeaf(TokenStream.PRIMARY, TokenStream.UNDEFINED)); pn.addChildToBack(nf.createLeaf(TokenStream.PRIMARY, TokenStream.UNDEFINED, position));
} }
else { else {
nf.addChildToBack(pn, assignExpr(ts, false)); pn.addChildToBack(assignExpr(ts, false));
} }
} }
while (ts.matchToken(TokenStream.COMMA)); while (ts.matchToken(TokenStream.COMMA));
@@ -1053,28 +1039,28 @@ public class Parser extends Observable {
} }
case TokenStream.LC: { case TokenStream.LC: {
pn = nf.createLeaf(TokenStream.OBJLIT); pn = nf.createLeaf(TokenStream.OBJLIT, position);
if (!ts.matchToken(TokenStream.RC)) { if (!ts.matchToken(TokenStream.RC)) {
commaloop: commaloop:
do { do {
Object property; Node property;
tt = ts.getToken(); tt = ts.getToken();
switch (tt) { switch (tt) {
// map NAMEs to STRINGs in object literal context. // map NAMEs to STRINGs in object literal context.
case TokenStream.NAME: case TokenStream.NAME:
case TokenStream.STRING: case TokenStream.STRING:
property = nf.createString(ts.getString()); property = nf.createString(ts.getString(), ts.tokenPosition);
break; break;
case TokenStream.NUMBER_INT: case TokenStream.NUMBER_INT:
int n = (int) ts.getNumber(); int n = (int) ts.getNumber();
property = nf.createNumber(n); property = nf.createNumber(n, ts.tokenPosition);
break; break;
case TokenStream.NUMBER: case TokenStream.NUMBER:
double d = ts.getNumber(); double d = ts.getNumber();
property = nf.createNumber(d); property = nf.createNumber(d, ts.tokenPosition);
break; break;
case TokenStream.RC: case TokenStream.RC:
// trailing comma is OK. // trailing comma is OK.
@@ -1088,8 +1074,8 @@ public class Parser extends Observable {
// OBJLIT is used as ':' in object literal for // OBJLIT is used as ':' in object literal for
// decompilation to solve spacing ambiguity. // decompilation to solve spacing ambiguity.
nf.addChildToBack(pn, property); pn.addChildToBack(property);
nf.addChildToBack(pn, assignExpr(ts, false)); pn.addChildToBack(assignExpr(ts, false));
} }
while (ts.matchToken(TokenStream.COMMA)); while (ts.matchToken(TokenStream.COMMA));
@@ -1112,29 +1098,29 @@ public class Parser extends Observable {
case TokenStream.NAME: case TokenStream.NAME:
String name = ts.getString(); String name = ts.getString();
return nf.createName(name); return nf.createName(name, position);
case TokenStream.NUMBER_INT: case TokenStream.NUMBER_INT:
int n = (int) ts.getNumber(); int n = (int) ts.getNumber();
return nf.createNumber(n); return nf.createNumber(n, position);
case TokenStream.NUMBER: case TokenStream.NUMBER:
double d = ts.getNumber(); double d = ts.getNumber();
return nf.createNumber(d); return nf.createNumber(d, position);
case TokenStream.STRING: case TokenStream.STRING:
String s = ts.getString(); String s = ts.getString();
return nf.createString(s); return nf.createString(s, position);
case TokenStream.REGEXP: { case TokenStream.REGEXP: {
String flags = ts.regExpFlags; String flags = ts.regExpFlags;
ts.regExpFlags = null; ts.regExpFlags = null;
String re = ts.getString(); String re = ts.getString();
return nf.createRegExp(re, flags); return nf.createRegExp(re, flags, position);
} }
case TokenStream.PRIMARY: case TokenStream.PRIMARY:
return nf.createLeaf(TokenStream.PRIMARY, ts.getOp()); return nf.createLeaf(TokenStream.PRIMARY, ts.getOp(), position);
case TokenStream.ERROR: case TokenStream.ERROR:
/* the scanner or one of its subroutines reported the error. */ /* the scanner or one of its subroutines reported the error. */
@@ -514,8 +514,7 @@ public class TokenStream {
return true; return true;
// didn't match, push back token // didn't match, push back token
tokenno--; ungetToken(token);
this.pushbackToken = token;
return false; return false;
} }
@@ -526,6 +525,8 @@ public class TokenStream {
throw new RuntimeException(message); throw new RuntimeException(message);
} }
this.pushbackToken = tt; this.pushbackToken = tt;
lastPosition = secondToLastPosition;
lastTokenPosition = tokenPosition;
tokenno--; tokenno--;
} }
@@ -533,6 +534,8 @@ public class TokenStream {
int result = getToken(); int result = getToken();
this.pushbackToken = result; this.pushbackToken = result;
lastPosition = secondToLastPosition;
lastTokenPosition = tokenPosition;
tokenno--; tokenno--;
return result; return result;
} }
@@ -583,13 +586,14 @@ public class TokenStream {
} }
public int getToken() throws IOException { public int getToken() throws IOException {
int c; lastTokenPosition = tokenPosition;
do { int c;
c = getTokenHelper(); do {
} while (c == RETRY_TOKEN); c = getTokenHelper();
} while (c == RETRY_TOKEN);
updatePosition(); updatePosition();
return c; return c;
} }
private int getTokenHelper() throws IOException { private int getTokenHelper() throws IOException {
@@ -613,6 +617,7 @@ public class TokenStream {
} }
} while (isJSSpace(c) || c == '\n'); } while (isJSSpace(c) || c == '\n');
tokenPosition = new CodePosition(in.getLineno(), Math.max(in.getOffset() - 1, 0));
if (c == EOF_CHAR) if (c == EOF_CHAR)
return EOF; return EOF;
if (c != '-' && c != '\n') if (c != '-' && c != '\n')
@@ -694,7 +699,7 @@ public class TokenStream {
} }
in.unread(); in.unread();
String str = getStringFromBuffer(); String str = getStringFromBuffer();
if (!containsEscape && !treatKeywordAsIdentifier) { if (!containsEscape && !treatKeywordAsIdentifier) {
// OPT we shouldn't have to make a string (object!) to // OPT we shouldn't have to make a string (object!) to
// check if it's a keyword. // check if it's a keyword.
@@ -1475,6 +1480,8 @@ public class TokenStream {
CodePosition secondToLastPosition; CodePosition secondToLastPosition;
CodePosition lastPosition; CodePosition lastPosition;
CodePosition tokenPosition;
CodePosition lastTokenPosition;
private int op; private int op;
public boolean treatKeywordAsIdentifier; public boolean treatKeywordAsIdentifier;