view dmdscript_tango/parse.d @ 3:8363a4bf6a8f

rename package: dmdscript to dmdscript_tango
author saaadel
date Sun, 24 Jan 2010 18:33:05 +0200
parents 55c2951c07be
children
line wrap: on
line source


/* Digital Mars DMDScript source code.
 * Copyright (c) 2000-2002 by Chromium Communications
 * D version Copyright (c) 2004-2005 by Digital Mars
 * All Rights Reserved
 * written by Walter Bright
 * www.digitalmars.com
 * Use at your own risk. There is no warranty, express or implied.
 * License for redistribution is by the GNU General Public License in gpl.txt.
 *
 * A binary, non-exclusive license for commercial use can be
 * purchased from www.digitalmars.com/dscript/buy.html.
 *
 * DMDScript is implemented in the D Programming Language,
 * www.digitalmars.com/d/
 *
 * For a C++ implementation of DMDScript, including COM support,
 * see www.digitalmars.com/dscript/cppscript.html.
 */


module dmdscript_tango.parse;

import dmdscript_tango.script;
import dmdscript_tango.lexer;
import dmdscript_tango.functiondefinition;
import dmdscript_tango.expression;
import dmdscript_tango.statement;
import dmdscript_tango.identifier;
import dmdscript_tango.ir;
import dmdscript_tango.textgen.errmsgs;

class Parser : Lexer
{
    uint flags;

    enum
    {
	normal		= 0,
	initial		= 1,

	allowIn		= 0,
	noIn		= 2,

	// Flag if we're in the for statement header, as
	// automatic semicolon insertion is suppressed inside it.
	inForHeader	= 4,
    }

    FunctionDefinition lastnamedfunc;


    this(char[] sourcename, tchar[] base, int useStringtable)
    {
	//writefln("Parser.this(base = '%s')", base);
	super(sourcename, base, useStringtable);
	nextToken();		// start up the scanner
    }

    ~this()
    {
	lastnamedfunc = null;
    }


    /**********************************************
     * Return !=0 on error, and fill in *perrinfo.
     */

    static int parseFunctionDefinition(out FunctionDefinition pfd,
	    d_string params, d_string bdy, out ErrInfo perrinfo)
    {
	Parser p;
	Identifier*[] parameters;
	TopStatement[] topstatements;
	FunctionDefinition fd = null;
	int result;

	p = new Parser("anonymous", params, 0);

	// Parse FormalParameterList
	while (p.token.value != TOKeof)
	{
	    if (p.token.value != TOKidentifier)
	    {
		p.error(errmsgtbl[ERR_FPL_EXPECTED_IDENTIFIER], p.token.toString());
		goto Lreturn;
	    }
	    parameters ~= p.token.ident;
	    p.nextToken();
	    if (p.token.value == TOKcomma)
		p.nextToken();
	    else if (p.token.value == TOKeof)
		break;
	    else
	    {
		p.error(errmsgtbl[ERR_FPL_EXPECTED_COMMA], p.token.toString());
		goto Lreturn;
	    }
	}
	if (p.errinfo.message)
	    goto Lreturn;

	delete p;

	// Parse StatementList
	p = new Parser("anonymous", bdy, 0);
	for (;;)
	{   TopStatement ts;

	    if (p.token.value == TOKeof)
		break;
	    ts = p.parseStatement();
	    topstatements ~= ts;
	}

	fd = new FunctionDefinition(0, 0, null, parameters, topstatements);
	fd.isanonymous = 1;

    Lreturn:
	pfd = fd;
	perrinfo = p.errinfo;
	result = (p.errinfo.message != null);
	delete p;
	p = null;
	return result;
    }

    /**********************************************
     * Return !=0 on error, and fill in *perrinfo.
     */

    int parseProgram(out TopStatement[] topstatements, ErrInfo *perrinfo)
    {
	topstatements = parseTopStatements();
	check(TOKeof);
	//writef("parseProgram done\n");
	*perrinfo = errinfo;
	//clearstack();
	return errinfo.message != null;
    }

    TopStatement[] parseTopStatements()
    {
	TopStatement[] topstatements;
	TopStatement ts;

	//writefln("parseTopStatements()");
	for (;;)
	{
	    switch (token.value)
	    {
		case TOKfunction:
		    ts = parseFunction(0);
		    topstatements ~= ts;
		    break;

		case TOKeof:
		    return topstatements;

		case TOKrbrace:
		    return topstatements;

		default:
		    ts = parseStatement();
		    topstatements ~= ts;
		    break;
	    }
	}
	assert(0);
    }

    /***************************
     * flag:
     *	0	Function statement
     *	1	Function literal
     */

    TopStatement parseFunction(int flag)
    {   Identifier* name;
	Identifier*[] parameters;
	TopStatement[] topstatements;
	FunctionDefinition f;
	Expression e = null;
	Loc loc;

	//writef("parseFunction()\n");
	loc = currentline;
	nextToken();
	name = null;
	if (token.value == TOKidentifier)
	{
	    name = token.ident;
	    nextToken();

	    if (!flag && token.value == TOKdot)
	    {
		// Regard:
		//	function A.B() { }
		// as:
		//	A.B = function() { }
		// This is not ECMA, but a jscript feature

		e = new IdentifierExpression(loc, name);
		name = null;

		while (token.value == TOKdot)
		{
		    nextToken();
		    if (token.value == TOKidentifier)
		    {	e = new DotExp(loc, e, token.ident);
			nextToken();
		    }
		    else
		    {
			error(errmsgtbl[ERR_EXPECTED_IDENTIFIER_2PARAM], ".", token.toString());
			break;
		    }
		}
	    }
	}

	check(TOKlparen);
	if (token.value == TOKrparen)
	    nextToken();
	else
	{
	    for (;;)
	    {
		if (token.value == TOKidentifier)
		{
		    parameters ~= token.ident;
		    nextToken();
		    if (token.value == TOKcomma)
		    {   nextToken();
			continue;
		    }
		    if (!check(TOKrparen))
			break;
		}
		else
		    error(ERR_EXPECTED_IDENTIFIER);
		break;
	    }
	}

	check(TOKlbrace);
	topstatements = parseTopStatements();
	check(TOKrbrace);

	f = new FunctionDefinition(loc, 0, name, parameters, topstatements);

	lastnamedfunc = f;

	//writef("parseFunction() done\n");
	if (!e)
	    return f;

	// Construct:
	//	A.B = function() { }

	Expression e2 = new FunctionLiteral(loc, f);

	e = new AssignExp(loc, e, e2);

	Statement s = new ExpStatement(loc, e);

	return s;
    }

    /*****************************************
     */

    Statement parseStatement()
    {   Statement s;
	Token *t;
	Loc loc;

	//writefln("parseStatement()");
	loc = currentline;
	switch (token.value)
	{
	    case TOKidentifier:
	    case TOKthis:
		// Need to look ahead to see if it is a declaration, label, or expression
		t = peek(&token);
		if (t.value == TOKcolon && token.value == TOKidentifier)
		{   // It's a label
		    Identifier *ident;

		    ident = token.ident;
		    nextToken();
		    nextToken();
		    s = parseStatement();
		    s = new LabelStatement(loc, ident, s);
		}
		else if (t.value == TOKassign ||
			 t.value == TOKdot ||
			 t.value == TOKlbracket)
		{   Expression exp;

		    exp = parseExpression();
		    parseOptionalSemi();
		    s = new ExpStatement(loc, exp);
		}
		else
		{   Expression exp;

		    exp = parseExpression(initial);
		    parseOptionalSemi();
		    s = new ExpStatement(loc, exp);
		}
		break;

	    case TOKreal:
	    case TOKstring:
	    case TOKdelete:
	    case TOKlparen:
	    case TOKplusplus:
	    case TOKminusminus:
	    case TOKplus:
	    case TOKminus:
	    case TOKnot:
	    case TOKtilde:
	    case TOKtypeof:
	    case TOKnull:
	    case TOKnew:
	    case TOKtrue:
	    case TOKfalse:
	    case TOKvoid:
	    {   Expression exp;

		exp = parseExpression(initial);
		parseOptionalSemi();
		s = new ExpStatement(loc, exp);
		break;
	    }

	    case TOKvar:
	    {
		Identifier *ident;
		Expression init;
		VarDeclaration v;
		VarStatement vs;

		vs = new VarStatement(loc);
		s = vs;

		nextToken();
		for (;;)
		{   loc = currentline;

		    if (token.value != TOKidentifier)
		    {
			error(errmsgtbl[ERR_EXPECTED_IDENTIFIER_PARAM], token.toString());
			break;
		    }
		    ident = token.ident;
		    init = null;
		    nextToken();
		    if (token.value == TOKassign)
		    {   uint flags_save;

			nextToken();
			flags_save = flags;
			flags &= ~initial;
			init = parseAssignExp();
			flags = flags_save;
		    }
		    v = new VarDeclaration(loc, ident, init);
		    vs.vardecls ~= v;
		    if (token.value != TOKcomma)
			break;
		    nextToken();
		}
		if (!(flags & inForHeader))
		    parseOptionalSemi();
		break;
	    }

	    case TOKlbrace:
	    {   BlockStatement bs;

		nextToken();
		bs = new BlockStatement(loc);
		while (token.value != TOKrbrace)
		{
		    if (token.value == TOKeof)
		    {   /* { */
			error(ERR_UNTERMINATED_BLOCK);
			break;
		    }
		    bs.statements ~= parseStatement();
		}
		s = bs;
		nextToken();

		// The following is to accommodate the jscript bug:
		//	if (i) {return(0);}; else ...
		if (token.value == TOKsemicolon)
		    nextToken();

		break;
	    }

	    case TOKif:
	    {   Expression condition;
		Statement ifbody;
		Statement elsebody;

		nextToken();
		condition = parseParenExp();
		ifbody = parseStatement();
		if (token.value == TOKelse)
		{
		    nextToken();
		    elsebody = parseStatement();
		}
		else
		    elsebody = null;
		s = new IfStatement(loc, condition, ifbody, elsebody);
		break;
	    }

	    case TOKswitch:
	    {   Expression condition;
		Statement bdy;

		nextToken();
		condition = parseParenExp();
		bdy = parseStatement();
		s = new SwitchStatement(loc, condition, bdy);
		break;
	    }

	    case TOKcase:
	    {   Expression exp;

		nextToken();
		exp = parseExpression();
		check(TOKcolon);
		s = new CaseStatement(loc, exp);
		break;
	    }

	    case TOKdefault:
		nextToken();
		check(TOKcolon);
		s = new DefaultStatement(loc);
		break;

	    case TOKwhile:
	    {   Expression condition;
		Statement bdy;

		nextToken();
		condition = parseParenExp();
		bdy = parseStatement();
		s = new WhileStatement(loc, condition, bdy);
		break;
	    }

	    case TOKsemicolon:
		nextToken();
		s = new EmptyStatement(loc);
		break;

	    case TOKdo:
	    {   Statement bdy;
		Expression condition;

		nextToken();
		bdy = parseStatement();
		check(TOKwhile);
		condition = parseParenExp();
		parseOptionalSemi();
		s = new DoStatement(loc, bdy, condition);
		break;
	    }

	    case TOKfor:
	    {
		Statement init;
		Statement bdy;

		nextToken();
		flags |= inForHeader;
		check(TOKlparen);
		if (token.value == TOKvar)
		{
		    init = parseStatement();
		}
		else
		{   Expression e;

		    e = parseOptionalExpression(noIn);
		    init = e ? new ExpStatement(loc, e) : null;
		}

		if (token.value == TOKsemicolon)
		{   Expression condition;
		    Expression increment;

		    nextToken();
		    condition = parseOptionalExpression();
		    check(TOKsemicolon);
		    increment = parseOptionalExpression();
		    check(TOKrparen);
		    flags &= ~inForHeader;

		    bdy = parseStatement();
		    s = new ForStatement(loc, init, condition, increment, bdy);
		}
		else if (token.value == TOKin)
		{   Expression inexp;
		    VarStatement vs;

		    // Check that there's only one VarDeclaration
		    // in init.
		    if (init.st == VARSTATEMENT)
		    {
			vs = cast(VarStatement)init;
			if (vs.vardecls.length != 1)
			    error(errmsgtbl[ERR_TOO_MANY_IN_VARS], vs.vardecls.length);
		    }

		    nextToken();
		    inexp = parseExpression();
		    check(TOKrparen);
		    flags &= ~inForHeader;
		    bdy = parseStatement();
		    s = new ForInStatement(loc, init, inexp, bdy);
		}
		else
		{
		    error(errmsgtbl[ERR_IN_EXPECTED], token.toString());
		    s = null;
		}
		break;
	    }

	    case TOKwith:
	    {   Expression exp;
		Statement bdy;

		nextToken();
		exp = parseParenExp();
		bdy = parseStatement();
		s = new WithStatement(loc, exp, bdy);
		break;
	    }

	    case TOKbreak:
	    {   Identifier *ident;

		nextToken();
		if (token.sawLineTerminator && token.value != TOKsemicolon)
		{   // Assume we saw a semicolon
		    ident = null;
		}
		else
		{
		    if (token.value == TOKidentifier)
		    {   ident = token.ident;
			nextToken();
		    }
		    else
			ident = null;
		    parseOptionalSemi();
		}
		s = new BreakStatement(loc, ident);
		break;
	    }

	    case TOKcontinue:
	    {   Identifier *ident;

		nextToken();
		if (token.sawLineTerminator && token.value != TOKsemicolon)
		{   // Assume we saw a semicolon
		    ident = null;
		}
		else
		{
		    if (token.value == TOKidentifier)
		    {   ident = token.ident;
			nextToken();
		    }
		    else
			ident = null;
		    parseOptionalSemi();
		}
		s = new ContinueStatement(loc, ident);
		break;
	    }

	    case TOKgoto:
	    {   Identifier *ident;

		nextToken();
		if (token.value != TOKidentifier)
		{   error(errmsgtbl[ERR_GOTO_LABEL_EXPECTED], token.toString());
		    s = null;
		    break;
		}
		ident = token.ident;
		nextToken();
		parseOptionalSemi();
		s = new GotoStatement(loc, ident);
		break;
	    }

	    case TOKreturn:
	    {   Expression exp;

		nextToken();
		if (token.sawLineTerminator && token.value != TOKsemicolon)
		{   // Assume we saw a semicolon
		    s = new ReturnStatement(loc, null);
		}
		else
		{   exp = parseOptionalExpression();
		    parseOptionalSemi();
		    s = new ReturnStatement(loc, exp);
		}
		break;
	    }

	    case TOKthrow:
	    {   Expression exp;

		nextToken();
		exp = parseExpression();
		parseOptionalSemi();
		s = new ThrowStatement(loc, exp);
		break;
	    }

	    case TOKtry:
	    {   Statement bdy;
		Identifier *catchident;
		Statement catchbody;
		Statement finalbody;

		nextToken();
		bdy = parseStatement();
		if (token.value == TOKcatch)
		{
		    nextToken();
		    check(TOKlparen);
		    catchident = null;
		    if (token.value == TOKidentifier)
			catchident = token.ident;
		    check(TOKidentifier);
		    check(TOKrparen);
		    catchbody = parseStatement();
		}
		else
		{   catchident = null;
		    catchbody = null;
		}

		if (token.value == TOKfinally)
		{   nextToken();
		    finalbody = parseStatement();
		}
		else
		    finalbody = null;

		if (!catchbody && !finalbody)
		{   error(ERR_TRY_CATCH_EXPECTED);
		    s = null;
		}
		else
		{
		    s = new TryStatement(loc, bdy, catchident, catchbody, finalbody);
		}
		break;
	    }

	    default:
		error(errmsgtbl[ERR_STATEMENT_EXPECTED], token.toString());
		nextToken();
		s = null;
		break;
	}

	//writefln("parseStatement() done");
	return s;
    }



    Expression parseOptionalExpression(uint flags = 0)
    {
	Expression e;

	if (token.value == TOKsemicolon || token.value == TOKrparen)
	    e = null;
	else
	    e = parseExpression(flags);
	return e;
    }

    // Follow ECMA 7.8.1 rules for inserting semicolons
    void parseOptionalSemi()
    {
	if (token.value != TOKeof &&
	    token.value != TOKrbrace &&
	    !(token.sawLineTerminator && (flags & inForHeader) == 0)
	   )
	    check(TOKsemicolon);
    }

    int check(TOK value)
    {
	if (token.value != value)
	{
	    error(errmsgtbl[ERR_EXPECTED_GENERIC], token.toString(), Token.toString(value));
	    return 0;
	}
	nextToken();
	return 1;
    }

    /********************************* Expression Parser ***************************/


    Expression parseParenExp()
    {   Expression e;

	check(TOKlparen);
	e = parseExpression();
	check(TOKrparen);
	return e;
    }

    Expression parsePrimaryExp(int innew)
    {   Expression e;
	Loc loc;

	loc = currentline;
	switch (token.value)
	{
	    case TOKthis:
		e = new ThisExpression(loc);
		nextToken();
		break;

	    case TOKnull:
		e = new NullExpression(loc);
		nextToken();
		break;

	    case TOKtrue:
		e = new BooleanExpression(loc, 1);
		nextToken();
		break;

	    case TOKfalse:
		e = new BooleanExpression(loc, 0);
		nextToken();
		break;

	    case TOKreal:
		e = new RealExpression(loc, token.realvalue);
		nextToken();
		break;

	    case TOKstring:
		e = new StringExpression(loc, token.string);
		token.string = null;	// release to gc
		nextToken();
		break;

	    case TOKregexp:
		e = new RegExpLiteral(loc, token.string);
		token.string = null;	// release to gc
		nextToken();
		break;

	    case TOKidentifier:
		e = new IdentifierExpression(loc, token.ident);
		token.ident = null;		// release to gc
		nextToken();
		break;

	    case TOKlparen:
		e = parseParenExp();
		break;

	    case TOKlbracket:
		e = parseArrayLiteral();
		break;

	    case TOKlbrace:
		if (flags & initial)
		{
		    error(ERR_OBJ_LITERAL_IN_INITIALIZER);
		    nextToken();
		    return null;
		}
		e = parseObjectLiteral();
		break;

	    case TOKfunction:
    //	    if (flags & initial)
    //		goto Lerror;
		e = parseFunctionLiteral();
		break;

	    case TOKnew:
	    {   Expression newarg;
		Expression[] arguments;

		nextToken();
		newarg = parsePrimaryExp(1);
		arguments = parseArguments();
		e = new NewExp(loc, newarg, arguments);
		break;
	    }

	    default:
    //	Lerror:
		error(errmsgtbl[ERR_EXPECTED_EXPRESSION], token.toString());
		nextToken();
		return null;
	}
	return parsePostExp(e, innew);
    }

    Expression[] parseArguments()
    {
	Expression[] arguments = null;

	if (token.value == TOKlparen)
	{
	    nextToken();
	    if (token.value != TOKrparen)
	    {
		for (;;)
		{   Expression arg;

		    arg = parseAssignExp();
		    arguments ~= arg;
		    if (token.value == TOKrparen)
			break;
		    if (!check(TOKcomma))
			break;
		}
	    }
	    nextToken();
	}
	return arguments;
    }

    Expression parseArrayLiteral()
    {
	Expression e;
	Expression[] elements;
	Loc loc;

	//writef("parseArrayLiteral()\n");
	loc = currentline;
	check(TOKlbracket);
	if (token.value != TOKrbracket)
	{
	    for (;;)
	    {
		if (token.value == TOKcomma)
		    // Allow things like [1,2,,,3,]
		    // Like Explorer 4, and unlike Netscape, the
		    // trailing , indicates another null element.
		    elements ~= cast(Expression)null;
		else if (token.value == TOKrbracket)
		{
		    elements ~= cast(Expression)null;
		    break;
		}
		else
		{   e = parseAssignExp();
		    elements ~= e;
		    if (token.value != TOKcomma)
			break;
		}
		nextToken();
	    }
	}
	check(TOKrbracket);
	e = new ArrayLiteral(loc, elements);
	return e;
    }

    Expression parseObjectLiteral()
    {
	Expression e;
	Field[] fields;
	Loc loc;

	//writef("parseObjectLiteral()\n");
	loc = currentline;
	check(TOKlbrace);
	if (token.value == TOKrbrace)
	    nextToken();
	else
	{
	    for (;;)
	    {   Field f;
		Identifier* ident;

		if (token.value != TOKidentifier)
		{   error(ERR_EXPECTED_IDENTIFIER);
		    break;
		}
		ident = token.ident;
		nextToken();
		check(TOKcolon);
		f = new Field(ident, parseAssignExp());
		fields ~= f;
		if (token.value != TOKcomma)
		    break;
		nextToken();
	    }
	    check(TOKrbrace);
	}
	e = new ObjectLiteral(loc, fields);
	return e;
    }

    Expression parseFunctionLiteral()
    {   FunctionDefinition f;
	Loc loc;

	loc = currentline;
	f = cast(FunctionDefinition)parseFunction(1);
	return new FunctionLiteral(loc, f);
    }

    Expression parsePostExp(Expression e, int innew)
    {   Loc loc;

	for (;;)
	{
	    loc = currentline;
	    //loc = (Loc)token.ptr;
	    switch (token.value)
	    {
		case TOKdot:
		    nextToken();
		    if (token.value == TOKidentifier)
		    {
			e = new DotExp(loc, e, token.ident);
		    }
		    else
		    {
			error(errmsgtbl[ERR_EXPECTED_IDENTIFIER_2PARAM], ".", token.toString());
			return e;
		    }
		    break;

		case TOKplusplus:
		    if (token.sawLineTerminator && !(flags & inForHeader))
			goto Linsert;
		    e = new PostIncExp(loc, e);
		    break;

		case TOKminusminus:
		    if (token.sawLineTerminator && !(flags & inForHeader))
		    {
		    Linsert:
			// insert automatic semicolon
			insertSemicolon(token.sawLineTerminator);
			return e;
		    }
		    e = new PostDecExp(loc, e);
		    break;

		case TOKlparen:
		{   // function call
		    Expression[] arguments;

		    if (innew)
			return e;
		    arguments = parseArguments();
		    e = new CallExp(loc, e, arguments);
		    continue;
		}

		case TOKlbracket:
		{   // array dereference
		    Expression index;

		    nextToken();
		    index = parseExpression();
		    check(TOKrbracket);
		    e = new ArrayExp(loc, e, index);
		    continue;
		}

		default:
		    return e;
	    }
	    nextToken();
	}
	assert(0);
    }

    Expression parseUnaryExp()
    {   Expression e;
	Loc loc;

	loc = currentline;
	switch (token.value)
	{
	    case TOKplusplus:
		nextToken();
		e = parseUnaryExp();
		e = new PreExp(loc, IRpreinc, e);
		break;

	    case TOKminusminus:
		nextToken();
		e = parseUnaryExp();
		e = new PreExp(loc, IRpredec, e);
		break;

	    case TOKminus:
		nextToken();
		e = parseUnaryExp();
		e = new XUnaExp(loc, TOKneg, IRneg, e);
		break;

	    case TOKplus:
		nextToken();
		e = parseUnaryExp();
		e = new XUnaExp(loc, TOKpos, IRpos, e);
		break;

	    case TOKnot:
		nextToken();
		e = parseUnaryExp();
		e = new NotExp(loc, e);
		break;

	    case TOKtilde:
		nextToken();
		e = parseUnaryExp();
		e = new XUnaExp(loc, TOKtilde, IRcom, e);
		break;

	    case TOKdelete:
		nextToken();
		e = parsePrimaryExp(0);
		e = new DeleteExp(loc, e);
		break;

	    case TOKtypeof:
		nextToken();
		e = parseUnaryExp();
		e = new XUnaExp(loc, TOKtypeof, IRtypeof, e);
		break;

	    case TOKvoid:
		nextToken();
		e = parseUnaryExp();
		e = new XUnaExp(loc, TOKvoid, IRundefined, e);
		break;

	    default:
		e = parsePrimaryExp(0);
		break;
	}
	return e;
    }

    Expression parseMulExp()
    {   Expression e;
	Expression e2;
	Loc loc;

	loc = currentline;
	e = parseUnaryExp();
	for (;;)
	{
	    switch (token.value)
	    {
		case TOKmultiply:
		    nextToken();
		    e2 = parseUnaryExp();
		    e = new XBinExp(loc, TOKmultiply, IRmul, e, e2);
		    continue;

		case TOKregexp:
		    // Rescan as if it was a "/"
		    rescan();
		case TOKdivide:
		    nextToken();
		    e2 = parseUnaryExp();
		    e = new XBinExp(loc, TOKdivide, IRdiv, e, e2);
		    continue;

		case TOKpercent:
		    nextToken();
		    e2 = parseUnaryExp();
		    e = new XBinExp(loc, TOKpercent, IRmod, e, e2);
		    continue;

		default:
		    break;
	    }
	    break;
	}
	return e;
    }

    Expression parseAddExp()
    {   Expression e;
	Expression e2;
	Loc loc;

	loc = currentline;
	e = parseMulExp();
	for (;;)
	{
	    switch (token.value)
	    {
		case TOKplus:
		    nextToken();
		    e2 = parseMulExp();
		    e = new AddExp(loc, e, e2);
		    continue;

		case TOKminus:
		    nextToken();
		    e2 = parseMulExp();
		    e = new XBinExp(loc, TOKminus, IRsub, e, e2);
		    continue;

		default:
		    break;
	    }
	    break;
	}
	return e;
    }

    Expression parseShiftExp()
    {   Expression e;
	Expression e2;
	Loc loc;

	loc = currentline;
	e = parseAddExp();
	for (;;)
	{   uint ircode;
	    TOK op = token.value;

	    switch (op)
	    {
		case TOKshiftleft:	ircode = IRshl;		goto L1;
		case TOKshiftright:	ircode = IRshr;		goto L1;
		case TOKushiftright:	ircode = IRushr;	goto L1;

		L1: nextToken();
		    e2 = parseAddExp();
		    e = new XBinExp(loc, op, ircode, e, e2);
		    continue;

		default:
		    break;
	    }
	    break;
	}
	return e;
    }

    Expression parseRelExp()
    {   Expression e;
	Expression e2;
	Loc loc;

	loc = currentline;
	e = parseShiftExp();
	for (;;)
	{   uint ircode;
	    TOK op = token.value;

	    switch (op)
	    {
		case TOKless:		ircode = IRclt;	goto L1;
		case TOKlessequal:	ircode = IRcle;	goto L1;
		case TOKgreater:	ircode = IRcgt;	goto L1;
		case TOKgreaterequal:	ircode = IRcge;	goto L1;

		L1:
		    nextToken();
		    e2 = parseShiftExp();
		    e = new CmpExp(loc, op, ircode, e, e2);
		    continue;

		case TOKinstanceof:
		    nextToken();
		    e2 = parseShiftExp();
		    e = new XBinExp(loc, TOKinstanceof, IRinstance, e, e2);
		    continue;

		case TOKin:
		    if (flags & noIn)
			break;		// disallow
		    nextToken();
		    e2 = parseShiftExp();
		    e = new InExp(loc, e, e2);
		    continue;

		default:
		    break;
	    }
	    break;
	}
	return e;
    }

    Expression parseEqualExp()
    {   Expression e;
	Expression e2;
	Loc loc;

	loc = currentline;
	e = parseRelExp();
	for (;;)
	{   uint ircode;
	    TOK op = token.value;

	    switch (op)
	    {
		case TOKequal:	     ircode = IRceq;	    goto L1;
		case TOKnotequal:    ircode = IRcne;	    goto L1;
		case TOKidentity:    ircode = IRcid;	    goto L1;
		case TOKnonidentity: ircode = IRcnid;	    goto L1;

		L1:
		    nextToken();
		    e2 = parseRelExp();
		    e = new CmpExp(loc, op, ircode, e, e2);
		    continue;

		default:
		    break;
	    }
	    break;
	}
	return e;
    }

    Expression parseAndExp()
    {   Expression e;
	Expression e2;
	Loc loc;

	loc = currentline;
	e = parseEqualExp();
	while (token.value == TOKand)
	{
	    nextToken();
	    e2 = parseEqualExp();
	    e = new XBinExp(loc, TOKand, IRand, e, e2);
	}
	return e;
    }

    Expression parseXorExp()
    {   Expression e;
	Expression e2;
	Loc loc;

	loc = currentline;
	e = parseAndExp();
	while (token.value == TOKxor)
	{
	    nextToken();
	    e2 = parseAndExp();
	    e = new XBinExp(loc, TOKxor, IRxor, e, e2);
	}
	return e;
    }

    Expression parseOrExp()
    {   Expression e;
	Expression e2;
	Loc loc;

	loc = currentline;
	e = parseXorExp();
	while (token.value == TOKor)
	{
	    nextToken();
	    e2 = parseXorExp();
	    e = new XBinExp(loc, TOKor, IRor, e, e2);
	}
	return e;
    }

    Expression parseAndAndExp()
    {   Expression e;
	Expression e2;
	Loc loc;

	loc = currentline;
	e = parseOrExp();
	while (token.value == TOKandand)
	{
	    nextToken();
	    e2 = parseOrExp();
	    e = new AndAndExp(loc, e, e2);
	}
	return e;
    }

    Expression parseOrOrExp()
    {   Expression e;
	Expression e2;
	Loc loc;

	loc = currentline;
	e = parseAndAndExp();
	while (token.value == TOKoror)
	{
	    nextToken();
	    e2 = parseAndAndExp();
	    e = new OrOrExp(loc, e, e2);
	}
	return e;
    }

    Expression parseCondExp()
    {   Expression e;
	Expression e1;
	Expression e2;
	Loc loc;

	loc = currentline;
	e = parseOrOrExp();
	if (token.value == TOKquestion)
	{
	    nextToken();
	    e1 = parseAssignExp();
	    check(TOKcolon);
	    e2 = parseAssignExp();
	    e = new CondExp(loc, e, e1, e2);
	}
	return e;
    }

    Expression parseAssignExp()
    {   Expression e;
	Expression e2;
	Loc loc;

	loc = currentline;
	e = parseCondExp();
	for (;;)
	{   uint ircode;
	    TOK op = token.value;

	    switch (op)
	    {
		case TOKassign:
		    nextToken();
		    e2 = parseAssignExp();
		    e = new AssignExp(loc, e, e2);
		    continue;

		case TOKplusass:
		    nextToken();
		    e2 = parseAssignExp();
		    e = new AddAssignExp(loc, e, e2);
		    continue;

		case TOKminusass:	ircode = IRsub;	 goto L1;
		case TOKmultiplyass:	ircode = IRmul;	 goto L1;
		case TOKdivideass:	ircode = IRdiv;	 goto L1;
		case TOKpercentass:	ircode = IRmod;	 goto L1;
		case TOKandass:		ircode = IRand;	 goto L1;
		case TOKorass:		ircode = IRor;	 goto L1;
		case TOKxorass:		ircode = IRxor;	 goto L1;
		case TOKshiftleftass:	ircode = IRshl;	 goto L1;
		case TOKshiftrightass:	ircode = IRshr;	 goto L1;
		case TOKushiftrightass:	ircode = IRushr; goto L1;

		L1: nextToken();
		    e2 = parseAssignExp();
		    e = new BinAssignExp(loc, op, ircode, e, e2);
		    continue;

		default:
		    break;
	    }
	    break;
	}
	return e;
    }

    Expression parseExpression(uint flags = 0)
    {   Expression e;
	Expression e2;
	Loc loc;
	uint flags_save;

	//writefln("Parser.parseExpression()");
	flags_save = this.flags;
	this.flags = flags;
	loc = currentline;
	e = parseAssignExp();
	while (token.value == TOKcomma)
	{
	    nextToken();
	    e2 = parseAssignExp();
	    e = new CommaExp(loc, e, e2);
	}
	this.flags = flags_save;
	return e;
    }

}

/********************************* ***************************/