/*
* Copyright (c) 1998-2011 Caucho Technology -- all rights reserved
*
* This file is part of Resin(R) Open Source
*
* Each copy or derived work must preserve the copyright notice and this
* notice unmodified.
*
* Resin Open Source is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Resin Open Source is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
* of NON-INFRINGEMENT. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with Resin Open Source; if not, write to the
*
* Free Software Foundation, Inc.
* 59 Temple Place, Suite 330
* Boston, MA 02111-1307 USA
*
* @author Scott Ferguson
*/
package com.caucho.quercus.parser;
import com.caucho.quercus.Location;
import com.caucho.quercus.QuercusContext;
import com.caucho.quercus.QuercusRuntimeException;
import com.caucho.quercus.env.*;
import com.caucho.quercus.expr.*;
import com.caucho.quercus.function.*;
import com.caucho.quercus.program.*;
import com.caucho.quercus.statement.*;
import com.caucho.util.CharBuffer;
import com.caucho.util.IntMap;
import com.caucho.util.L10N;
import com.caucho.vfs.*;
import java.io.CharConversionException;
import java.io.UnsupportedEncodingException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Locale;
/**
* Parses a PHP program.
*/
public class QuercusParser {
private final static L10N L = new L10N(QuercusParser.class);
private final static int M_STATIC = 0x1;
private final static int M_PUBLIC = 0x2;
private final static int M_PROTECTED = 0x4;
private final static int M_PRIVATE = 0x8;
private final static int M_FINAL = 0x10;
private final static int M_ABSTRACT = 0x20;
private final static int M_INTERFACE = 0x40;
private final static int IDENTIFIER = 256;
private final static int STRING = 257;
private final static int LONG = 258;
private final static int DOUBLE = 259;
private final static int LSHIFT = 260;
private final static int RSHIFT = 261;
private final static int PHP_END = 262;
private final static int EQ = 263;
private final static int DEREF = 264;
private final static int LEQ = 268;
private final static int GEQ = 269;
private final static int NEQ = 270;
private final static int EQUALS = 271;
private final static int NEQUALS = 272;
private final static int C_AND = 273;
private final static int C_OR = 274;
private final static int PLUS_ASSIGN = 278;
private final static int MINUS_ASSIGN = 279;
private final static int APPEND_ASSIGN = 280;
private final static int MUL_ASSIGN = 281;
private final static int DIV_ASSIGN = 282;
private final static int MOD_ASSIGN = 283;
private final static int AND_ASSIGN = 284;
private final static int OR_ASSIGN = 285;
private final static int XOR_ASSIGN = 286;
private final static int LSHIFT_ASSIGN = 287;
private final static int RSHIFT_ASSIGN = 288;
private final static int INCR = 289;
private final static int DECR = 290;
private final static int SCOPE = 291;
private final static int ESCAPED_STRING = 292;
private final static int HEREDOC = 293;
private final static int ARRAY_RIGHT = 294;
private final static int SIMPLE_STRING_ESCAPE = 295;
private final static int COMPLEX_STRING_ESCAPE = 296;
private final static int BINARY = 297;
private final static int SIMPLE_BINARY_ESCAPE = 298;
private final static int COMPLEX_BINARY_ESCAPE = 299;
private final static int FIRST_IDENTIFIER_LEXEME = 512;
private final static int ECHO = 512;
private final static int NULL = 513;
private final static int IF = 514;
private final static int WHILE = 515;
private final static int FUNCTION = 516;
private final static int CLASS = 517;
private final static int NEW = 518;
private final static int RETURN = 519;
private final static int VAR = 520;
private final static int PRIVATE = 521;
private final static int PROTECTED = 522;
private final static int PUBLIC = 523;
private final static int FOR = 524;
private final static int DO = 525;
private final static int BREAK = 526;
private final static int CONTINUE = 527;
private final static int ELSE = 528;
private final static int EXTENDS = 529;
private final static int STATIC = 530;
private final static int INCLUDE = 531;
private final static int REQUIRE = 532;
private final static int INCLUDE_ONCE = 533;
private final static int REQUIRE_ONCE = 534;
private final static int UNSET = 535;
private final static int FOREACH = 536;
private final static int AS = 537;
private final static int TEXT = 538;
private final static int ISSET = 539;
private final static int SWITCH = 540;
private final static int CASE = 541;
private final static int DEFAULT = 542;
private final static int EXIT = 543;
private final static int GLOBAL = 544;
private final static int ELSEIF = 545;
private final static int PRINT = 546;
private final static int SYSTEM_STRING = 547;
private final static int SIMPLE_SYSTEM_STRING = 548;
private final static int COMPLEX_SYSTEM_STRING = 549;
private final static int TEXT_ECHO = 550;
private final static int ENDIF = 551;
private final static int ENDWHILE = 552;
private final static int ENDFOR = 553;
private final static int ENDFOREACH = 554;
private final static int ENDSWITCH = 555;
private final static int XOR_RES = 556;
private final static int AND_RES = 557;
private final static int OR_RES = 558;
private final static int LIST = 559;
private final static int THIS = 560;
private final static int TRUE = 561;
private final static int FALSE = 562;
private final static int CLONE = 563;
private final static int INSTANCEOF = 564;
private final static int CONST = 565;
private final static int ABSTRACT = 566;
private final static int FINAL = 567;
private final static int DIE = 568;
private final static int THROW = 569;
private final static int TRY = 570;
private final static int CATCH = 571;
private final static int INTERFACE = 572;
private final static int IMPLEMENTS = 573;
private final static int IMPORT = 574;
private final static int TEXT_PHP = 575;
private final static int NAMESPACE = 576;
private final static int USE = 577;
private final static int LAST_IDENTIFIER_LEXEME = 1024;
private final static IntMap _insensitiveReserved = new IntMap();
private final static IntMap _reserved = new IntMap();
private QuercusContext _quercus;
private Path _sourceFile;
private int _sourceOffset; // offset into the source file for the first line
private ParserLocation _parserLocation = new ParserLocation();
private ExprFactory _factory;
private boolean _hasCr;
private int _peek = -1;
private ReadStream _is;
private String _encoding;
private CharBuffer _sb = new CharBuffer();
private String _namespace = "";
private HashMap<String,String> _namespaceUseMap
= new HashMap<String,String>();
private int _peekToken = -1;
private String _lexeme = "";
private String _heredocEnd = null;
private GlobalScope _globalScope;
private boolean _returnsReference = false;
private Scope _scope;
private InterpretedClassDef _classDef;
private FunctionInfo _function;
private boolean _isTop;
private boolean _isNewExpr;
private boolean _isIfTest;
private int _classesParsed;
private int _functionsParsed;
private ArrayList<String> _loopLabelList = new ArrayList<String>();
private int _labelsCreated;
private String _comment;
public QuercusParser(QuercusContext quercus)
{
_quercus = quercus;
if (quercus == null)
_factory = ExprFactory.create();
else
_factory = quercus.createExprFactory();
_globalScope = new GlobalScope(_factory);
_scope = _globalScope;
}
public QuercusParser(QuercusContext quercus,
Path sourceFile,
ReadStream is)
{
this(quercus);
if (quercus == null || quercus.isUnicodeSemantics())
init(sourceFile, is, "UTF-8");
else
init(sourceFile, is, "ISO-8859-1");
}
private void init(Path sourceFile)
throws IOException
{
init(sourceFile, sourceFile.openRead(), "UTF-8");
}
private void init(Path sourceFile, ReadStream is, String encoding)
{
_is = is;
_encoding = encoding;
if (sourceFile != null) {
_parserLocation.setFileName(sourceFile);
_sourceFile = sourceFile;
}
else {
_parserLocation.setFileName("eval:");
// php/2146
_sourceFile = new NullPath("eval:");
}
_parserLocation.setLineNumber(1);
_peek = -1;
_peekToken = -1;
}
public void setLocation(String fileName, int line)
{
_parserLocation.setFileName(fileName);
_parserLocation.setLineNumber(line);
if (line > 0)
_sourceOffset = 1 - line;
}
public static QuercusProgram parse(QuercusContext quercus,
Path path,
String encoding)
throws IOException
{
ReadStream is = path.openRead();
try {
is.setEncoding(encoding);
QuercusParser parser;
parser = new QuercusParser(quercus, path, is);
return parser.parse();
} finally {
is.close();
}
}
public static QuercusProgram parse(QuercusContext quercus,
Path path,
String encoding,
String fileName,
int line)
throws IOException
{
ReadStream is = path.openRead();
try {
is.setEncoding(encoding);
QuercusParser parser;
parser = new QuercusParser(quercus, path, is);
if (fileName != null && line >= 0)
parser.setLocation(fileName, line);
return parser.parse();
} finally {
is.close();
}
}
public static QuercusProgram parse(QuercusContext quercus,
ReadStream is)
throws IOException
{
QuercusParser parser;
parser = new QuercusParser(quercus, is.getPath(), is);
return parser.parse();
}
public static QuercusProgram parse(QuercusContext quercus,
Path path, ReadStream is)
throws IOException
{
return new QuercusParser(quercus, path, is).parse();
}
public static QuercusProgram parseEval(QuercusContext quercus, String str)
throws IOException
{
Path path = new StringPath(str);
QuercusParser parser = new QuercusParser(quercus, path, path.openRead());
return parser.parseCode();
}
public static QuercusProgram parseEvalExpr(QuercusContext quercus, String str)
throws IOException
{
Path path = new StringPath(str);
QuercusParser parser = new QuercusParser(quercus, path, path.openRead());
return parser.parseCode().createExprReturn();
}
public static AbstractFunction parseFunction(QuercusContext quercus,
String name,
String args,
String code)
throws IOException
{
Path argPath = new StringPath(args);
Path codePath = new StringPath(code);
QuercusParser parser = new QuercusParser(quercus);
Function fun = parser.parseFunction(name, argPath, codePath);
parser.close();
return fun;
}
public boolean isUnicodeSemantics()
{
return _quercus != null && _quercus.isUnicodeSemantics();
}
public static Expr parse(QuercusContext quercus, String str)
throws IOException
{
Path path = new StringPath(str);
return new QuercusParser(quercus, path, path.openRead()).parseExpr();
}
public static Expr parseDefault(String str)
{
try {
Path path = new StringPath(str);
return new QuercusParser(null, path, path.openRead()).parseExpr();
} catch (IOException e) {
throw new QuercusRuntimeException(e);
}
}
public static Expr parseDefault(ExprFactory factory, String str)
{
try {
Path path = new StringPath(str);
QuercusParser parser = new QuercusParser(null, path, path.openRead());
parser._factory = factory;
return parser.parseExpr();
} catch (IOException e) {
throw new QuercusRuntimeException(e);
}
}
/**
* Returns the current filename.
*/
public String getFileName()
{
if (_sourceFile == null)
return null;
else
return _sourceFile.getPath();
}
/**
* Returns the current class name
*/
public String getClassName()
{
if (_classDef != null)
return _classDef.getName();
else
return null;
}
/**
* Returns the current line
*/
public int getLine()
{
return _parserLocation.getLineNumber();
}
public ExprFactory getExprFactory()
{
return _factory;
}
public ExprFactory getFactory()
{
return _factory;
}
public QuercusProgram parse()
throws IOException
{
ClassDef globalClass = null;
_function = getFactory().createFunctionInfo(_quercus, globalClass, "");
_function.setPageMain(true);
// quercus/0b0d
_function.setVariableVar(true);
_function.setUsesSymbolTable(true);
Statement stmt = parseTop();
QuercusProgram program
= new QuercusProgram(_quercus, _sourceFile,
_globalScope.getFunctionMap(),
_globalScope.getFunctionList(),
_globalScope.getClassMap(),
_globalScope.getClassList(),
_function,
stmt);
return program;
/*
com.caucho.vfs.WriteStream out = com.caucho
.vfs.Vfs.lookup("stdout:").openWrite();
out.setFlushOnNewline(true);
stmt.debug(new JavaWriter(out));
*/
}
QuercusProgram parseCode()
throws IOException
{
ClassDef globalClass = null;
_function = getFactory().createFunctionInfo(_quercus, globalClass, "eval");
// XXX: need param or better function name for non-global?
_function.setGlobal(false);
Location location = getLocation();
ArrayList<Statement> stmtList = parseStatementList();
return new QuercusProgram(_quercus, _sourceFile,
_globalScope.getFunctionMap(),
_globalScope.getFunctionList(),
_globalScope.getClassMap(),
_globalScope.getClassList(),
_function,
_factory.createBlock(location, stmtList));
}
public Function parseFunction(String name, Path argPath, Path codePath)
throws IOException
{
ClassDef globalClass = null;
_function = getFactory().createFunctionInfo(_quercus, globalClass, name);
_function.setGlobal(false);
_function.setPageMain(true);
init(argPath);
Arg []args = parseFunctionArgDefinition();
close();
init(codePath);
Statement []statements = parseStatements();
Function fun = _factory.createFunction(Location.UNKNOWN,
name,
_function,
args,
statements);
close();
return fun;
}
/**
* Parses the top page.
*/
Statement parseTop()
throws IOException
{
_isTop = true;
ArrayList<Statement> statements = new ArrayList<Statement>();
Location location = getLocation();
int token = parsePhpText();
if (_lexeme.length() > 0)
statements.add(_factory.createText(location, _lexeme));
if (token == TEXT_ECHO) {
parseEcho(statements);
}
else if (token == TEXT_PHP) {
_peekToken = parseToken();
if (_peekToken == IDENTIFIER && _lexeme.equalsIgnoreCase("php")) {
_peekToken = -1;
}
}
statements.addAll(parseStatementList());
return _factory.createBlock(location, statements);
}
/*
* Parses a statement list.
*/
private Statement []parseStatements()
throws IOException
{
ArrayList<Statement> statementList = parseStatementList();
Statement []statements = new Statement[statementList.size()];
statementList.toArray(statements);
return statements;
}
/**
* Parses a statement list.
*/
private ArrayList<Statement> parseStatementList()
throws IOException
{
ArrayList<Statement> statementList = new ArrayList<Statement>();
while (true) {
Location location = getLocation();
int token = parseToken();
switch (token) {
case -1:
return statementList;
case ';':
break;
case ECHO:
parseEcho(statementList);
break;
case PRINT:
statementList.add(parsePrint());
break;
case UNSET:
parseUnset(statementList);
break;
case ABSTRACT:
case FINAL:
{
_peekToken = token;
int modifiers = 0;
do {
token = parseToken();
switch (token) {
case ABSTRACT:
modifiers |= M_ABSTRACT;
break;
case FINAL:
modifiers |= M_FINAL;
break;
case CLASS:
statementList.add(parseClassDefinition(modifiers));
break;
default:
throw error(L.l("expected 'class' at {0}",
tokenName(token)));
}
} while (token != CLASS);
}
break;
case FUNCTION:
{
Location functionLocation = getLocation();
Function fun = parseFunctionDefinition(M_STATIC);
if (! _isTop) {
statementList.add(
_factory.createFunctionDef(functionLocation, fun));
}
}
break;
case CLASS:
// parseClassDefinition(0);
statementList.add(parseClassDefinition(0));
break;
case INTERFACE:
// parseClassDefinition(M_INTERFACE);
statementList.add(parseClassDefinition(M_INTERFACE));
break;
case CONST:
statementList.addAll(parseConstDefinition());
break;
case IF:
statementList.add(parseIf());
break;
case SWITCH:
statementList.add(parseSwitch());
break;
case WHILE:
statementList.add(parseWhile());
break;
case DO:
statementList.add(parseDo());
break;
case FOR:
statementList.add(parseFor());
break;
case FOREACH:
statementList.add(parseForeach());
break;
case PHP_END:
return statementList;
case RETURN:
statementList.add(parseReturn());
break;
case THROW:
statementList.add(parseThrow());
break;
case BREAK:
statementList.add(parseBreak());
break;
case CONTINUE:
statementList.add(parseContinue());
break;
case GLOBAL:
statementList.add(parseGlobal());
break;
case STATIC:
statementList.add(parseStatic());
break;
case TRY:
statementList.add(parseTry());
break;
case NAMESPACE:
statementList.addAll(parseNamespace());
break;
case USE:
parseUse();
break;
case '{':
{
ArrayList<Statement> enclosedStatementList = parseStatementList();
expect('}');
statementList.addAll(enclosedStatementList);
}
break;
case '}':
case CASE:
case DEFAULT:
case ELSE:
case ELSEIF:
case ENDIF:
case ENDWHILE:
case ENDFOR:
case ENDFOREACH:
case ENDSWITCH:
_peekToken = token;
return statementList;
case TEXT:
if (_lexeme.length() > 0) {
statementList.add(_factory.createText(location, _lexeme));
}
break;
case TEXT_PHP:
if (_lexeme.length() > 0) {
statementList.add(_factory.createText(location, _lexeme));
}
_peekToken = parseToken();
if (_peekToken == IDENTIFIER && _lexeme.equalsIgnoreCase("php")) {
_peekToken = -1;
}
break;
case TEXT_ECHO:
if (_lexeme.length() > 0)
statementList.add(_factory.createText(location, _lexeme));
parseEcho(statementList);
break;
default:
_peekToken = token;
statementList.add(parseExprStatement());
break;
}
}
}
private Statement parseStatement()
throws IOException
{
Location location = getLocation();
int token = parseToken();
switch (token) {
case ';':
return _factory.createNullStatement();
case '{':
location = getLocation();
ArrayList<Statement> statementList = parseStatementList();
expect('}');
return _factory.createBlock(location, statementList);
case IF:
return parseIf();
case SWITCH:
return parseSwitch();
case WHILE:
return parseWhile();
case DO:
return parseDo();
case FOR:
return parseFor();
case FOREACH:
return parseForeach();
case TRY:
return parseTry();
case TEXT:
if (_lexeme.length() > 0) {
return _factory.createText(location, _lexeme);
}
else
return parseStatement();
case TEXT_PHP:
{
Statement stmt = null;
if (_lexeme.length() > 0) {
stmt = _factory.createText(location, _lexeme);
}
_peekToken = parseToken();
if (_peekToken == IDENTIFIER && _lexeme.equalsIgnoreCase("php")) {
_peekToken = -1;
}
if (stmt == null)
stmt = parseStatement();
return stmt;
}
default:
Statement stmt = parseStatementImpl(token);
token = parseToken();
if (token != ';')
_peekToken = token;
return stmt;
}
}
/**
* Parses statements that expect to be terminated by ';'.
*/
private Statement parseStatementImpl(int token)
throws IOException
{
switch (token) {
case ECHO:
{
Location location = getLocation();
ArrayList<Statement> statementList = new ArrayList<Statement>();
parseEcho(statementList);
return _factory.createBlock(location, statementList);
}
case PRINT:
return parsePrint();
case UNSET:
return parseUnset();
case GLOBAL:
return parseGlobal();
case STATIC:
return parseStatic();
case BREAK:
return parseBreak();
case CONTINUE:
return parseContinue();
case RETURN:
return parseReturn();
case THROW:
return parseThrow();
case TRY:
return parseTry();
default:
_peekToken = token;
return parseExprStatement();
/*
default:
throw error(L.l("unexpected token {0}.", tokenName(token)));
*/
}
}
/**
* Parses the echo statement.
*/
private void parseEcho(ArrayList<Statement> statements)
throws IOException
{
Location location = getLocation();
while (true) {
Expr expr = parseTopExpr();
createEchoStatements(location, statements, expr);
int token = parseToken();
if (token != ',') {
_peekToken = token;
return;
}
}
}
/**
* Creates echo statements from an expression.
*/
private void createEchoStatements(Location location,
ArrayList<Statement> statements,
Expr expr)
{
if (expr == null) {
// since AppendExpr.getNext() can be null.
}
else if (expr instanceof BinaryAppendExpr) {
BinaryAppendExpr append = (BinaryAppendExpr) expr;
// XXX: children of append print differently?
createEchoStatements(location, statements, append.getValue());
createEchoStatements(location, statements, append.getNext());
}
else if (expr instanceof LiteralStringExpr) {
LiteralStringExpr string = (LiteralStringExpr) expr;
Statement statement
= _factory.createText(location, string.evalConstant().toString());
statements.add(statement);
}
else {
Statement statement = _factory.createEcho(location, expr);
statements.add(statement);
}
}
/**
* Parses the print statement.
*/
private Statement parsePrint()
throws IOException
{
return _factory.createExpr(getLocation(), parsePrintExpr());
}
/**
* Parses the print statement.
*/
private Expr parsePrintExpr()
throws IOException
{
ArrayList<Expr> args = new ArrayList<Expr>();
args.add(parseTopExpr());
return _factory.createCall(this, "print", args);
}
/**
* Parses the global statement.
*/
private Statement parseGlobal()
throws IOException
{
ArrayList<Statement> statementList = new ArrayList<Statement>();
Location location = getLocation();
while (true) {
Expr expr = parseTopExpr();
if (expr instanceof VarExpr) {
VarExpr var = (VarExpr) expr;
_function.setUsesGlobal(true);
// php/323c
// php/3a6g, php/3a58
//var.getVarInfo().setGlobal();
statementList.add(_factory.createGlobal(location, var));
}
else if (expr instanceof VarVarExpr) {
VarVarExpr var = (VarVarExpr) expr;
statementList.add(_factory.createVarGlobal(location, var));
}
else
throw error(L.l("unknown expr {0} to global", expr));
// statementList.add(new ExprStatement(expr));
int token = parseToken();
if (token != ',') {
_peekToken = token;
return _factory.createBlock(location, statementList);
}
}
}
/**
* Parses the static statement.
*/
private Statement parseStatic()
throws IOException
{
ArrayList<Statement> statementList = new ArrayList<Statement>();
Location location = getLocation();
while (true) {
expect('$');
String name = parseIdentifier();
VarExpr var = _factory.createVar(_function.createVar(name));
Expr init = null;
int token = parseToken();
if (token == '=') {
init = parseExpr();
token = parseToken();
}
// var.getVarInfo().setReference();
if (_classDef != null) {
statementList.add(_factory.createClassStatic(location,
_classDef.getName(),
var,
init));
}
else {
statementList.add(_factory.createStatic(location, var, init));
}
if (token != ',') {
_peekToken = token;
return _factory.createBlock(location, statementList);
}
}
}
/**
* Parses the unset statement.
*/
private Statement parseUnset()
throws IOException
{
Location location = getLocation();
ArrayList<Statement> statementList = new ArrayList<Statement>();
parseUnset(statementList);
return _factory.createBlock(location, statementList);
}
/**
* Parses the unset statement.
*/
private void parseUnset(ArrayList<Statement> statementList)
throws IOException
{
Location location = getLocation();
int token = parseToken();
if (token != '(') {
_peekToken = token;
statementList.add(parseTopExpr().createUnset(_factory, location));
return;
}
do {
// XXX: statementList.add(
// parseTopExpr().createUnset(_factory, getLocation()));
Expr topExpr = parseTopExpr();
statementList.add(topExpr.createUnset(_factory, getLocation()));
} while ((token = parseToken()) == ',');
_peekToken = token;
expect(')');
}
/**
* Parses the if statement
*/
private Statement parseIf()
throws IOException
{
boolean oldTop = _isTop;
_isTop = false;
try {
Location location = getLocation();
expect('(');
_isIfTest = true;
Expr test = parseExpr();
_isIfTest = false;
expect(')');
int token = parseToken();
if (token == ':')
return parseAlternateIf(test, location);
else
_peekToken = token;
Statement trueBlock = null;
trueBlock = parseStatement();
Statement falseBlock = null;
token = parseToken();
if (token == ELSEIF) {
falseBlock = parseIf();
}
else if (token == ELSE) {
falseBlock = parseStatement();
}
else
_peekToken = token;
return _factory.createIf(location, test, trueBlock, falseBlock);
} finally {
_isTop = oldTop;
}
}
/**
* Parses the if statement
*/
private Statement parseAlternateIf(Expr test, Location location)
throws IOException
{
Statement trueBlock = null;
trueBlock = _factory.createBlock(location, parseStatementList());
Statement falseBlock = null;
int token = parseToken();
if (token == ELSEIF) {
Location subLocation = getLocation();
Expr subTest = parseExpr();
expect(':');
falseBlock = parseAlternateIf(subTest, subLocation);
}
else if (token == ELSE) {
expect(':');
falseBlock = _factory.createBlock(getLocation(), parseStatementList());
expect(ENDIF);
}
else {
_peekToken = token;
expect(ENDIF);
}
return _factory.createIf(location, test, trueBlock, falseBlock);
}
/**
* Parses the switch statement
*/
private Statement parseSwitch()
throws IOException
{
Location location = getLocation();
boolean oldTop = _isTop;
_isTop = false;
String label = pushSwitchLabel();
try {
expect('(');
Expr test = parseExpr();
expect(')');
boolean isAlternate = false;
int token = parseToken();
if (token == ':')
isAlternate = true;
else if (token == '{')
isAlternate = false;
else {
_peekToken = token;
expect('{');
}
ArrayList<Expr[]> caseList = new ArrayList<Expr[]>();
ArrayList<BlockStatement> blockList = new ArrayList<BlockStatement>();
ArrayList<Integer> fallThroughList = new ArrayList<Integer>();
BlockStatement defaultBlock = null;
while ((token = parseToken()) == CASE || token == DEFAULT) {
Location caseLocation = getLocation();
ArrayList<Expr> valueList = new ArrayList<Expr>();
boolean isDefault = false;
while (token == CASE || token == DEFAULT) {
if (token == CASE) {
Expr value = parseExpr();
valueList.add(value);
}
else
isDefault = true;
token = parseToken();
if (token == ':') {
}
else if (token == ';') {
// XXX: warning?
}
else
throw error("expected ':' at " + tokenName(token));
token = parseToken();
}
_peekToken = token;
Expr []values = new Expr[valueList.size()];
valueList.toArray(values);
ArrayList<Statement> newBlockList = parseStatementList();
for (int fallThrough : fallThroughList) {
BlockStatement block = blockList.get(fallThrough);
boolean isDefaultBlock = block == defaultBlock;
block = block.append(newBlockList);
blockList.set(fallThrough, block);
if (isDefaultBlock)
defaultBlock = block;
}
BlockStatement block
= _factory.createBlockImpl(caseLocation, newBlockList);
if (values.length > 0) {
caseList.add(values);
blockList.add(block);
}
if (isDefault)
defaultBlock = block;
if (blockList.size() > 0
&& ! fallThroughList.contains(blockList.size() - 1)) {
fallThroughList.add(blockList.size() - 1);
}
if (block.fallThrough() != Statement.FALL_THROUGH)
fallThroughList.clear();
}
_peekToken = token;
if (isAlternate)
expect(ENDSWITCH);
else
expect('}');
return _factory.createSwitch(location, test,
caseList, blockList,
defaultBlock, label);
} finally {
_isTop = oldTop;
popLoopLabel();
}
}
/**
* Parses the 'while' statement
*/
private Statement parseWhile()
throws IOException
{
boolean oldTop = _isTop;
_isTop = false;
String label = pushWhileLabel();
try {
Location location = getLocation();
expect('(');
_isIfTest = true;
Expr test = parseExpr();
_isIfTest = false;
expect(')');
Statement block;
int token = parseToken();
if (token == ':') {
block = _factory.createBlock(getLocation(), parseStatementList());
expect(ENDWHILE);
}
else {
_peekToken = token;
block = parseStatement();
}
return _factory.createWhile(location, test, block, label);
} finally {
_isTop = oldTop;
popLoopLabel();
}
}
/**
* Parses the 'do' statement
*/
private Statement parseDo()
throws IOException
{
boolean oldTop = _isTop;
_isTop = false;
String label = pushDoLabel();
try {
Location location = getLocation();
Statement block = parseStatement();
expect(WHILE);
expect('(');
_isIfTest = true;
Expr test = parseExpr();
_isIfTest = false;
expect(')');
return _factory.createDo(location, test, block, label);
} finally {
_isTop = oldTop;
popLoopLabel();
}
}
/**
* Parses the 'for' statement
*/
private Statement parseFor()
throws IOException
{
boolean oldTop = _isTop;
_isTop = false;
String label = pushForLabel();
try {
Location location = getLocation();
expect('(');
Expr init = null;
int token = parseToken();
if (token != ';') {
_peekToken = token;
init = parseTopCommaExpr();
expect(';');
}
Expr test = null;
token = parseToken();
if (token != ';') {
_peekToken = token;
_isIfTest = true;
test = parseTopCommaExpr();
_isIfTest = false;
expect(';');
}
Expr incr = null;
token = parseToken();
if (token != ')') {
_peekToken = token;
incr = parseTopCommaExpr();
expect(')');
}
Statement block;
token = parseToken();
if (token == ':') {
block = _factory.createBlock(getLocation(), parseStatementList());
expect(ENDFOR);
}
else {
_peekToken = token;
block = parseStatement();
}
return _factory.createFor(location, init, test, incr, block, label);
} finally {
_isTop = oldTop;
popLoopLabel();
}
}
/**
* Parses the 'foreach' statement
*/
private Statement parseForeach()
throws IOException
{
boolean oldTop = _isTop;
_isTop = false;
String label = pushForeachLabel();
try {
Location location = getLocation();
expect('(');
Expr objExpr = parseTopExpr();
expect(AS);
boolean isRef = false;
int token = parseToken();
if (token == '&')
isRef = true;
else
_peekToken = token;
AbstractVarExpr valueExpr = (AbstractVarExpr) parseLeftHandSide();
AbstractVarExpr keyVar = null;
AbstractVarExpr valueVar;
token = parseToken();
if (token == ARRAY_RIGHT) {
if (isRef)
throw error(L.l("key reference is forbidden in foreach"));
keyVar = valueExpr;
token = parseToken();
if (token == '&')
isRef = true;
else
_peekToken = token;
valueVar = (AbstractVarExpr) parseLeftHandSide();
token = parseToken();
}
else
valueVar = valueExpr;
if (token != ')')
throw error(L.l("expected ')' in foreach"));
Statement block;
token = parseToken();
if (token == ':') {
block = _factory.createBlock(getLocation(), parseStatementList());
expect(ENDFOREACH);
}
else {
_peekToken = token;
block = parseStatement();
}
return _factory.createForeach(location, objExpr, keyVar,
valueVar, isRef, block, label);
} finally {
_isTop = oldTop;
popLoopLabel();
}
}
/**
* Parses the try statement
*/
private Statement parseTry()
throws IOException
{
boolean oldTop = _isTop;
_isTop = false;
try {
Location location = getLocation();
Statement block = null;
try {
block = parseStatement();
} finally {
// _scope = oldScope;
}
TryStatement stmt = _factory.createTry(location, block);
int token = parseToken();
while (token == CATCH) {
expect('(');
String id = parseNamespaceIdentifier();
AbstractVarExpr lhs = parseLeftHandSide();
expect(')');
block = parseStatement();
stmt.addCatch(id, lhs, block);
token = parseToken();
}
_peekToken = token;
return stmt;
} finally {
_isTop = oldTop;
}
}
/**
* Parses a function definition
*/
private Function parseFunctionDefinition(int modifiers)
throws IOException
{
boolean oldTop = _isTop;
_isTop = false;
boolean oldReturnsReference = _returnsReference;
FunctionInfo oldFunction = _function;
boolean isAbstract = (modifiers & M_ABSTRACT) != 0;
boolean isStatic = (modifiers & M_STATIC) != 0;
if (_classDef != null && _classDef.isInterface())
isAbstract = true;
try {
_returnsReference = false;
int token = parseToken();
String comment = _comment;
_comment = null;
if (token == '&')
_returnsReference = true;
else
_peekToken = token;
String name;
name = parseIdentifier();
if (_classDef == null) {
name = resolveIdentifier(name);
}
if (isAbstract && ! _scope.isAbstract()) {
if (_classDef != null)
throw error(L.l(
"'{0}' may not be abstract because class {1} is not abstract.",
name, _classDef.getName()));
else
throw error(L.l(
"'{0}' may not be abstract. Abstract functions are only "
+ "allowed in abstract classes.",
name));
}
boolean isConstructor = false;
if (_classDef != null
&& (name.equals(_classDef.getName())
|| name.equals("__constructor"))) {
if (isStatic) {
throw error(L.l(
"'{0}:{1}' may not be static because class constructors "
+ "may not be static",
_classDef.getName(), name));
}
isConstructor = true;
}
_function = getFactory().createFunctionInfo(_quercus, _classDef, name);
_function.setPageStatic(oldTop);
_function.setConstructor(isConstructor);
_function.setReturnsReference(_returnsReference);
Location location = getLocation();
expect('(');
Arg []args = parseFunctionArgDefinition();
expect(')');
if (_classDef != null
&& "__call".equals(name)
&& args.length != 2)
{
throw error(L.l("{0}::{1} must have exactly two arguments defined",
_classDef.getName(), name));
}
Function function;
if (isAbstract) {
expect(';');
function = _factory.createMethodDeclaration(location,
_classDef, name,
_function, args);
}
else {
expect('{');
Statement []statements = null;
Scope oldScope = _scope;
try {
_scope = new FunctionScope(_factory, oldScope);
statements = parseStatements();
} finally {
_scope = oldScope;
}
expect('}');
if (_classDef != null)
function = _factory.createObjectMethod(location,
_classDef,
name, _function,
args, statements);
else
function = _factory.createFunction(location, name,
_function, args,
statements);
}
function.setGlobal(oldTop);
function.setStatic((modifiers & M_STATIC) != 0);
function.setFinal((modifiers & M_FINAL) != 0);
function.setParseIndex(_functionsParsed++);
function.setComment(comment);
if ((modifiers & M_PROTECTED) != 0)
function.setVisibility(Visibility.PROTECTED);
else if ((modifiers & M_PRIVATE) != 0)
function.setVisibility(Visibility.PRIVATE);
_scope.addFunction(name, function, oldTop);
/*
com.caucho.vfs.WriteStream out = com.caucho.vfs
.Vfs.lookup("stdout:").openWrite();
out.setFlushOnNewline(true);
function.debug(new JavaWriter(out));
*/
return function;
} finally {
_returnsReference = oldReturnsReference;
_function = oldFunction;
_isTop = oldTop;
}
}
/**
* Parses a function definition
*/
private Expr parseClosure()
throws IOException
{
boolean oldTop = _isTop;
_isTop = false;
boolean oldReturnsReference = _returnsReference;
FunctionInfo oldFunction = _function;
try {
_returnsReference = false;
int token = parseToken();
String comment = null;
if (token == '&')
_returnsReference = true;
else
_peekToken = token;
String name = "__quercus_closure_" + _functionsParsed;
ClassDef classDef = null;
_function = getFactory().createFunctionInfo(_quercus, classDef, name);
_function.setReturnsReference(_returnsReference);
_function.setClosure(true);
Location location = getLocation();
expect('(');
Arg []args = parseFunctionArgDefinition();
expect(')');
Arg []useArgs;
ArrayList<VarExpr> useVars = new ArrayList<VarExpr>();
token = parseToken();
if (token == USE) {
expect('(');
useArgs = parseFunctionArgDefinition();
for (Arg arg : useArgs) {
VarExpr var = _factory.createVar(
oldFunction.createVar(arg.getName()));
useVars.add(var);
}
expect(')');
}
else {
useArgs = new Arg[0];
_peekToken = token;
}
expect('{');
Statement []statements = null;
Scope oldScope = _scope;
try {
_scope = new FunctionScope(_factory, oldScope);
statements = parseStatements();
} finally {
_scope = oldScope;
}
expect('}');
Function function = _factory.createFunction(location, name,
_function, args,
statements);
function.setParseIndex(_functionsParsed++);
function.setComment(comment);
function.setClosure(true);
function.setClosureUseArgs(useArgs);
_globalScope.addFunction(name, function, oldTop);
return _factory.createClosure(location, function, useVars);
} finally {
_returnsReference = oldReturnsReference;
_function = oldFunction;
_isTop = oldTop;
}
}
private Arg []parseFunctionArgDefinition()
throws IOException
{
LinkedHashMap<String, Arg> argMap = new LinkedHashMap<String, Arg>();
while (true) {
int token = parseToken();
boolean isReference = false;
// php/076b, php/1c02
// XXX: save arg type for type checking upon function call
String expectedClass = null;
if (token != ')'
&& token != '&'
&& token != '$'
&& token != -1) {
_peekToken = token;
expectedClass = parseIdentifier();
token = parseToken();
}
if (token == '&') {
isReference = true;
token = parseToken();
}
if (token != '$') {
_peekToken = token;
break;
}
String argName = parseIdentifier();
Expr defaultExpr = _factory.createRequired();
token = parseToken();
if (token == '=') {
// XXX: actually needs to be primitive
defaultExpr = parseUnary(); // parseTerm(false);
token = parseToken();
}
Arg arg = new Arg(argName, defaultExpr, isReference, expectedClass);
if (argMap.get(argName) != null && _quercus.isStrict()) {
throw error(L.l("aliasing of function argument '{0}'", argName));
}
argMap.put(argName, arg);
VarInfo var = _function.createVar(argName);
if (token != ',') {
_peekToken = token;
break;
}
}
Arg [] args = new Arg[argMap.size()];
argMap.values().toArray(args);
return args;
}
/**
* Parses the 'return' statement
*/
private Statement parseBreak()
throws IOException
{
// commented out for adodb (used by Moodle and others)
// XXX: should only throw fatal error if break statement is reached
// during execution
if (! _isTop && _loopLabelList.size() == 0 && ! _quercus.isLooseParse())
throw error(L.l("cannot 'break' inside a function"));
Location location = getLocation();
int token = parseToken();
switch (token) {
case ';':
_peekToken = token;
return _factory.createBreak(location,
null,
(ArrayList<String>) _loopLabelList.clone());
default:
_peekToken = token;
Expr expr = parseTopExpr();
return _factory.createBreak(location,
expr,
(ArrayList<String>) _loopLabelList.clone());
}
}
/**
* Parses the 'return' statement
*/
private Statement parseContinue()
throws IOException
{
if (! _isTop && _loopLabelList.size() == 0 && ! _quercus.isLooseParse())
throw error(L.l("cannot 'continue' inside a function"));
Location location = getLocation();
int token = parseToken();
switch (token) {
case TEXT_PHP:
case ';':
_peekToken = token;
return _factory
.createContinue(location,
null,
(ArrayList<String>) _loopLabelList.clone());
default:
_peekToken = token;
Expr expr = parseTopExpr();
return _factory
.createContinue(location,
expr,
(ArrayList<String>) _loopLabelList.clone());
}
}
/**
* Parses the 'return' statement
*/
private Statement parseReturn()
throws IOException
{
Location location = getLocation();
int token = parseToken();
switch (token) {
case ';':
_peekToken = token;
return _factory.createReturn(location, _factory.createNull());
default:
_peekToken = token;
Expr expr = parseTopExpr();
/*
if (_returnsReference)
expr = expr.createRef();
else
expr = expr.createCopy();
*/
if (_returnsReference)
return _factory.createReturnRef(location, expr);
else
return _factory.createReturn(location, expr);
}
}
/**
* Parses the 'throw' statement
*/
private Statement parseThrow()
throws IOException
{
Location location = getLocation();
Expr expr = parseExpr();
return _factory.createThrow(location, expr);
}
/**
* Parses a class definition
*/
private Statement parseClassDefinition(int modifiers)
throws IOException
{
String name = parseIdentifier();
name = resolveIdentifier(name);
String comment = _comment;
String parentName = null;
ArrayList<String> ifaceList = new ArrayList<String>();
int token = parseToken();
if (token == EXTENDS) {
if ((modifiers & M_INTERFACE) != 0) {
do {
ifaceList.add(parseNamespaceIdentifier());
token = parseToken();
} while (token == ',');
}
else {
parentName = parseNamespaceIdentifier();
token = parseToken();
}
}
if ((modifiers & M_INTERFACE) == 0 && token == IMPLEMENTS) {
do {
ifaceList.add(parseNamespaceIdentifier());
token = parseToken();
} while (token == ',');
}
_peekToken = token;
InterpretedClassDef oldClass = _classDef;
Scope oldScope = _scope;
try {
_classDef = oldScope.addClass(getLocation(),
name, parentName, ifaceList,
_classesParsed++,
_isTop);
_classDef.setComment(comment);
if ((modifiers & M_ABSTRACT) != 0)
_classDef.setAbstract(true);
if ((modifiers & M_INTERFACE) != 0)
_classDef.setInterface(true);
if ((modifiers & M_FINAL) != 0)
_classDef.setFinal(true);
_scope = new ClassScope(_classDef);
expect('{');
parseClassContents();
expect('}');
return _factory.createClassDef(getLocation(), _classDef);
} finally {
_classDef = oldClass;
_scope = oldScope;
}
}
/**
* Parses a statement list.
*/
private void parseClassContents()
throws IOException
{
while (true) {
_comment = null;
int token = parseToken();
switch (token) {
case ';':
break;
case FUNCTION:
{
Function fun = parseFunctionDefinition(0);
fun.setStatic(false);
break;
}
case CLASS:
parseClassDefinition(0);
break;
/* quercus/0260
case VAR:
parseClassVarDefinition(false);
break;
*/
case CONST:
parseClassConstDefinition();
break;
case PUBLIC:
case PRIVATE:
case PROTECTED:
case STATIC:
case FINAL:
case ABSTRACT:
{
_peekToken = token;
int modifiers = parseModifiers();
int token2 = parseToken();
if (token2 == FUNCTION) {
Function fun = parseFunctionDefinition(modifiers);
}
else {
_peekToken = token2;
parseClassVarDefinition(modifiers);
}
}
break;
case IDENTIFIER:
if (_lexeme.equals("var")) {
parseClassVarDefinition(0);
}
else {
_peekToken = token;
return;
}
break;
case -1:
case '}':
default:
_peekToken = token;
return;
}
}
}
/**
* Parses a function definition
*/
private void parseClassVarDefinition(int modifiers)
throws IOException
{
int token;
do {
expect('$');
String comment = _comment;
String name = parseIdentifier();
token = parseToken();
Expr expr = null;
if (token == '=') {
expr = parseExpr();
}
else {
_peekToken = token;
expr = _factory.createNull();
}
StringValue nameV = createStringValue(name);
if ((modifiers & M_STATIC) != 0) {
((ClassScope) _scope).addStaticVar(nameV, expr, _comment);
}
else if ((modifiers & M_PRIVATE) != 0) {
((ClassScope) _scope).addVar(nameV,
expr,
FieldVisibility.PRIVATE,
comment);
}
else if ((modifiers & M_PROTECTED) != 0) {
((ClassScope) _scope).addVar(nameV,
expr,
FieldVisibility.PROTECTED,
comment);
}
else {
((ClassScope) _scope).addVar(nameV,
expr,
FieldVisibility.PUBLIC,
comment);
}
token = parseToken();
} while (token == ',');
_peekToken = token;
}
/**
* Parses a const definition
*/
private ArrayList<Statement> parseConstDefinition()
throws IOException
{
ArrayList<Statement> constList = new ArrayList<Statement>();
int token;
do {
String name = parseNamespaceIdentifier();
expect('=');
Expr expr = parseExpr();
ArrayList<Expr> args = new ArrayList<Expr>();
args.add(_factory.createString(name));
args.add(expr);
Expr fun = _factory.createCall(this, "define", args);
constList.add(_factory.createExpr(getLocation(), fun));
// _scope.addConstant(name, expr);
token = parseToken();
} while (token == ',');
_peekToken = token;
return constList;
}
/**
* Parses a const definition
*/
private void parseClassConstDefinition()
throws IOException
{
int token;
do {
String name = parseIdentifier();
expect('=');
Expr expr = parseExpr();
((ClassScope) _scope).addConstant(name, expr);
token = parseToken();
} while (token == ',');
_peekToken = token;
}
private int parseModifiers()
throws IOException
{
int token;
int modifiers = 0;
while (true) {
token = parseToken();
switch (token) {
case PUBLIC:
modifiers |= M_PUBLIC;
break;
case PRIVATE:
modifiers |= M_PRIVATE;
break;
case PROTECTED:
modifiers |= M_PROTECTED;
break;
case FINAL:
modifiers |= M_FINAL;
break;
case STATIC:
modifiers |= M_STATIC;
break;
case ABSTRACT:
modifiers |= M_ABSTRACT;
break;
default:
_peekToken = token;
return modifiers;
}
}
}
private ArrayList<Statement> parseNamespace()
throws IOException
{
int token = parseToken();
String var = "";
if (token == IDENTIFIER) {
var = _lexeme;
token = parseToken();
}
if (var.startsWith("\\"))
var = var.substring(1);
String oldNamespace = _namespace;
_namespace = var;
if (token == '{') {
ArrayList<Statement> statementList = parseStatementList();
expect('}');
_namespace = oldNamespace;
return statementList;
}
else if (token == ';') {
return new ArrayList<Statement>();
}
else {
throw error(L.l("namespace must be followed by '{' or ';'"));
}
}
private void parseUse()
throws IOException
{
int token = parseNamespaceIdentifier(read());
String name = _lexeme;
int ns = name.lastIndexOf('\\');
String tail;
if (ns >= 0)
tail = name.substring(ns + 1);
else
tail = name;
if (name.startsWith("\\"))
name = name.substring(1);
token = parseToken();
if (token == ';') {
_namespaceUseMap.put(tail, name);
return;
}
else if (token == AS) {
do {
tail = parseIdentifier();
_namespaceUseMap.put(tail, name);
} while ((token = parseToken()) == ',');
}
_peekToken = token;
expect(';');
}
/**
* Parses an expression statement.
*/
private Statement parseExprStatement()
throws IOException
{
Location location = getLocation();
Expr expr = parseTopExpr();
Statement statement = _factory.createExpr(location, expr);
int token = parseToken();
_peekToken = token;
switch (token) {
case -1:
case ';':
case '}':
case PHP_END:
case TEXT:
case TEXT_PHP:
case TEXT_ECHO:
break;
default:
expect(';');
break;
}
return statement;
}
/**
* Parses a top expression.
*/
private Expr parseTopExpr()
throws IOException
{
return parseExpr();
}
/**
* Parses a top expression.
*/
private Expr parseTopCommaExpr()
throws IOException
{
return parseCommaExpr();
}
/**
* Parses a comma expression.
*/
private Expr parseCommaExpr()
throws IOException
{
Expr expr = parseExpr();
while (true) {
int token = parseToken();
switch (token) {
case ',':
expr = _factory.createComma(expr, parseExpr());
break;
default:
_peekToken = token;
return expr;
}
}
}
/**
* Parses an expression with optional '&'.
*/
private Expr parseRefExpr()
throws IOException
{
int token = parseToken();
boolean isRef = token == '&';
if (! isRef)
_peekToken = token;
Expr expr = parseExpr();
if (isRef)
expr = _factory.createRef(expr);
return expr;
}
/**
* Parses an expression.
*/
private Expr parseExpr()
throws IOException
{
return parseWeakOrExpr();
}
/**
* Parses a logical xor expression.
*/
private Expr parseWeakOrExpr()
throws IOException
{
Expr expr = parseWeakXorExpr();
while (true) {
int token = parseToken();
switch (token) {
case OR_RES:
expr = _factory.createOr(expr, parseWeakXorExpr());
break;
default:
_peekToken = token;
return expr;
}
}
}
/**
* Parses a logical xor expression.
*/
private Expr parseWeakXorExpr()
throws IOException
{
Expr expr = parseWeakAndExpr();
while (true) {
int token = parseToken();
switch (token) {
case XOR_RES:
expr = _factory.createXor(expr, parseWeakAndExpr());
break;
default:
_peekToken = token;
return expr;
}
}
}
/**
* Parses a logical and expression.
*/
private Expr parseWeakAndExpr()
throws IOException
{
Expr expr = parseConditionalExpr();
while (true) {
int token = parseToken();
switch (token) {
case AND_RES:
expr = _factory.createAnd(expr, parseConditionalExpr());
break;
default:
_peekToken = token;
return expr;
}
}
}
/**
* Parses a conditional expression.
*/
private Expr parseConditionalExpr()
throws IOException
{
Expr expr = parseOrExpr();
while (true) {
int token = parseToken();
switch (token) {
case '?':
token = parseToken();
if (token == ':') {
expr = _factory.createShortConditional(expr, parseOrExpr());
}
else {
_peekToken = token;
Expr trueExpr = parseExpr();
expect(':');
// php/33c1
expr = _factory.createConditional(expr, trueExpr, parseOrExpr());
}
break;
default:
_peekToken = token;
return expr;
}
}
}
/**
* Parses a logical or expression.
*/
private Expr parseOrExpr()
throws IOException
{
Expr expr = parseAndExpr();
while (true) {
int token = parseToken();
switch (token) {
case C_OR:
expr = _factory.createOr(expr, parseAndExpr());
break;
default:
_peekToken = token;
return expr;
}
}
}
/**
* Parses a logical and expression.
*/
private Expr parseAndExpr()
throws IOException
{
Expr expr = parseBitOrExpr();
while (true) {
int token = parseToken();
switch (token) {
case C_AND:
expr = _factory.createAnd(expr, parseBitOrExpr());
break;
default:
_peekToken = token;
return expr;
}
}
}
/**
* Parses a bit or expression.
*/
private Expr parseBitOrExpr()
throws IOException
{
Expr expr = parseBitXorExpr();
while (true) {
int token = parseToken();
switch (token) {
case '|':
expr = _factory.createBitOr(expr, parseBitXorExpr());
break;
default:
_peekToken = token;
return expr;
}
}
}
/**
* Parses a bit xor expression.
*/
private Expr parseBitXorExpr()
throws IOException
{
Expr expr = parseBitAndExpr();
while (true) {
int token = parseToken();
switch (token) {
case '^':
expr = _factory.createBitXor(expr, parseBitAndExpr());
break;
default:
_peekToken = token;
return expr;
}
}
}
/**
* Parses a bit and expression.
*/
private Expr parseBitAndExpr()
throws IOException
{
Expr expr = parseEqExpr();
while (true) {
int token = parseToken();
switch (token) {
case '&':
expr = _factory.createBitAnd(expr, parseEqExpr());
break;
default:
_peekToken = token;
return expr;
}
}
}
/**
* Parses a comparison expression.
*/
private Expr parseEqExpr()
throws IOException
{
Expr expr = parseCmpExpr();
int token = parseToken();
switch (token) {
case EQ:
return _factory.createEq(expr, parseCmpExpr());
case NEQ:
return _factory.createNeq(expr, parseCmpExpr());
case EQUALS:
return _factory.createEquals(expr, parseCmpExpr());
case NEQUALS:
return _factory.createNot(_factory.createEquals(expr, parseCmpExpr()));
default:
_peekToken = token;
return expr;
}
}
/**
* Parses a comparison expression.
*/
private Expr parseCmpExpr()
throws IOException
{
Expr expr = parseShiftExpr();
int token = parseToken();
switch (token) {
case '<':
return _factory.createLt(expr, parseShiftExpr());
case '>':
return _factory.createGt(expr, parseShiftExpr());
case LEQ:
return _factory.createLeq(expr, parseShiftExpr());
case GEQ:
return _factory.createGeq(expr, parseShiftExpr());
case INSTANCEOF:
Location location = getLocation();
Expr classNameExpr = parseShiftExpr();
if (classNameExpr instanceof ConstExpr)
return _factory.createInstanceOf(expr, classNameExpr.toString());
else
return _factory.createInstanceOfVar(expr, classNameExpr);
default:
_peekToken = token;
return expr;
}
}
/**
* Parses a left/right shift expression.
*/
private Expr parseShiftExpr()
throws IOException
{
Expr expr = parseAddExpr();
while (true) {
int token = parseToken();
switch (token) {
case LSHIFT:
expr = _factory.createLeftShift(expr, parseAddExpr());
break;
case RSHIFT:
expr = _factory.createRightShift(expr, parseAddExpr());
break;
default:
_peekToken = token;
return expr;
}
}
}
/**
* Parses an add/substract expression.
*/
private Expr parseAddExpr()
throws IOException
{
Expr expr = parseMulExpr();
while (true) {
int token = parseToken();
switch (token) {
case '+':
expr = _factory.createAdd(expr, parseMulExpr());
break;
case '-':
expr = _factory.createSub(expr, parseMulExpr());
break;
case '.':
expr = _factory.createAppend(expr, parseMulExpr());
break;
default:
_peekToken = token;
return expr;
}
}
}
/**
* Parses a multiplication/division expression.
*/
private Expr parseMulExpr()
throws IOException
{
Expr expr = parseAssignExpr();
while (true) {
int token = parseToken();
switch (token) {
case '*':
expr = _factory.createMul(expr, parseAssignExpr());
break;
case '/':
expr = _factory.createDiv(expr, parseAssignExpr());
break;
case '%':
expr = _factory.createMod(expr, parseAssignExpr());
break;
default:
_peekToken = token;
return expr;
}
}
}
/**
* Parses an assignment expression.
*/
private Expr parseAssignExpr()
throws IOException
{
Expr expr = parseUnary();
while (true) {
int token = parseToken();
switch (token) {
case '=':
token = parseToken();
try {
if (token == '&') {
// php/03d6
expr = expr.createAssignRef(this, parseBitOrExpr());
}
else {
_peekToken = token;
if (_isIfTest && _quercus.isStrict()) {
throw error(
"assignment without parentheses inside If/While/For "
+ "test statement; please make sure whether equality "
+ "was intended instead");
}
expr = expr.createAssign(this, parseConditionalExpr());
}
} catch (QuercusParseException e) {
throw e;
} catch (IOException e) {
throw error(e.getMessage());
}
break;
case PLUS_ASSIGN:
if (expr.canRead())
expr = expr.createAssign(this,
_factory.createAdd(expr,
parseConditionalExpr()));
else // php/03d4
expr = expr.createAssign(this, parseConditionalExpr());
break;
case MINUS_ASSIGN:
if (expr.canRead())
expr = expr.createAssign(this,
_factory.createSub(expr,
parseConditionalExpr()));
else
expr = expr.createAssign(this, parseConditionalExpr());
break;
case APPEND_ASSIGN:
if (expr.canRead())
expr = expr.createAssign(this,
_factory.createAppend(expr,
parseConditionalExpr()));
else
expr = expr.createAssign(this, parseConditionalExpr());
break;
case MUL_ASSIGN:
if (expr.canRead())
expr = expr.createAssign(this,
_factory.createMul(expr,
parseConditionalExpr()));
else
expr = expr.createAssign(this, parseConditionalExpr());
break;
case DIV_ASSIGN:
if (expr.canRead())
expr = expr.createAssign(this,
_factory.createDiv(expr,
parseConditionalExpr()));
else
expr = expr.createAssign(this, parseConditionalExpr());
break;
case MOD_ASSIGN:
if (expr.canRead())
expr = expr.createAssign(this,
_factory.createMod(expr,
parseConditionalExpr()));
else
expr = expr.createAssign(this, parseConditionalExpr());
break;
case LSHIFT_ASSIGN:
if (expr.canRead())
expr = expr.createAssign(this,
_factory.createLeftShift(expr,
parseConditionalExpr()));
else
expr = expr.createAssign(this, parseConditionalExpr());
break;
case RSHIFT_ASSIGN:
if (expr.canRead())
expr = expr.createAssign(this,
_factory.createRightShift(expr,
parseConditionalExpr()));
else
expr = expr.createAssign(this, parseConditionalExpr());
break;
case AND_ASSIGN:
if (expr.canRead())
expr = expr.createAssign(this,
_factory.createBitAnd(expr,
parseConditionalExpr()));
else
expr = expr.createAssign(this, parseConditionalExpr());
break;
case OR_ASSIGN:
if (expr.canRead())
expr = expr.createAssign(this,
_factory.createBitOr(expr,
parseConditionalExpr()));
else
expr = expr.createAssign(this, parseConditionalExpr());
break;
case XOR_ASSIGN:
if (expr.canRead())
expr = expr.createAssign(this,
_factory.createBitXor(expr,
parseConditionalExpr()));
else
expr = expr.createAssign(this, parseConditionalExpr());
break;
case INSTANCEOF:
Expr classNameExpr = parseShiftExpr();
if (classNameExpr instanceof ConstExpr)
return _factory.createInstanceOf(expr, classNameExpr.toString());
else
return _factory.createInstanceOfVar(expr, classNameExpr);
default:
_peekToken = token;
return expr;
}
}
}
/**
* Parses unary term.
*
* <pre>
* unary ::= term
* ::= '&' unary
* ::= '-' unary
* ::= '+' unary
* ::= '!' unary
* ::= '~' unary
* ::= '@' unary
* </pre>
*/
private Expr parseUnary()
throws IOException
{
int token = parseToken();
switch (token) {
case '+':
{
Expr expr = parseAssignExpr();
return _factory.createPlus(expr);
}
case '-':
{
Expr expr = parseAssignExpr();
return _factory.createMinus(expr);
}
case '!':
{
Expr expr = parseAssignExpr();
return _factory.createNot(expr);
}
case '~':
{
Expr expr = parseAssignExpr();
return _factory.createBitNot(expr);
}
case '@':
{
Expr expr = parseAssignExpr();
return _factory.createSuppress(expr);
}
case CLONE:
{
Expr expr = parseAssignExpr();
return _factory.createClone(expr);
}
case INCR:
{
Expr expr = parseUnary();
return _factory.createPreIncrement(expr, 1);
}
case DECR:
{
Expr expr = parseUnary();
return _factory.createPreIncrement(expr, -1);
}
default:
_peekToken = token;
return parseTerm(true);
}
}
/**
* Parses a basic term.
*
* <pre>
* term ::= termBase
* ::= term '[' index ']'
* ::= term '{' index '}'
* ::= term '->' name
* ::= term '::' name
* ::= term '(' a1, ..., an ')'
* </pre>
*/
private Expr parseTerm(boolean isParseCall)
throws IOException
{
Expr term = parseTermBase();
while (true) {
int token = parseToken();
switch (token) {
case '[':
{
token = parseToken();
if (token == ']') {
term = _factory.createArrayTail(getLocation(), term);
}
else {
_peekToken = token;
Expr index = parseExpr();
token = parseToken();
term = _factory.createArrayGet(getLocation(), term, index);
}
if (token != ']')
throw expect("']'", token);
}
break;
case '{':
{
Expr index = parseExpr();
expect('}');
term = _factory.createCharAt(term, index);
}
break;
case INCR:
term = _factory.createPostIncrement(term, 1);
break;
case DECR:
term = _factory.createPostIncrement(term, -1);
break;
case DEREF:
term = parseDeref(term);
break;
case SCOPE:
term = parseScope(term);
break;
case '(':
_peek = token;
if (isParseCall)
term = parseCall(term);
else
return term;
break;
default:
_peekToken = token;
return term;
}
}
}
/**
* Parses a basic term.
*
* <pre>
* term ::= termBase
* ::= term '[' index ']'
* ::= term '{' index '}'
* </pre>
*/
private Expr parseTermArray()
throws IOException
{
Expr term = parseTermBase();
while (true) {
int token = parseToken();
switch (token) {
case '[':
{
token = parseToken();
if (token == ']') {
term = _factory.createArrayTail(getLocation(), term);
}
else {
_peekToken = token;
Expr index = parseExpr();
token = parseToken();
term = _factory.createArrayGet(getLocation(), term, index);
}
if (token != ']')
throw expect("']'", token);
}
break;
case '{':
{
Expr index = parseExpr();
expect('}');
term = _factory.createCharAt(term, index);
}
break;
case INCR:
term = _factory.createPostIncrement(term, 1);
break;
case DECR:
term = _factory.createPostIncrement(term, -1);
break;
default:
_peekToken = token;
return term;
}
}
}
/**
* Parses a deref
*
* <pre>
* deref ::= term -> IDENTIFIER
* ::= term -> IDENTIFIER '(' args ')'
* </pre>
*/
private Expr parseDeref(Expr term)
throws IOException
{
String name = null;
Expr nameExpr = null;
int token = parseToken();
if (token == '$') {
// php/09e0
_peekToken = token;
nameExpr = parseTerm(false);
return term.createFieldGet(_factory, nameExpr);
}
else if (token == '{') {
nameExpr = parseExpr();
expect('}');
return term.createFieldGet(_factory, nameExpr);
}
else {
_peekToken = token;
name = parseIdentifier();
return term.createFieldGet(_factory, createStringValue(name));
}
}
/**
* Parses a basic term.
*
* <pre>
* term ::= STRING
* ::= LONG
* ::= DOUBLE
* </pre>
*/
private Expr parseTermBase()
throws IOException
{
int token = parseToken();
switch (token) {
case STRING:
return createString(_lexeme);
case SYSTEM_STRING:
{
ArrayList<Expr> args = new ArrayList<Expr>();
args.add(createString(_lexeme));
return _factory.createCall(this, "shell_exec", args);
}
case SIMPLE_SYSTEM_STRING:
{
ArrayList<Expr> args = new ArrayList<Expr>();
args.add(parseEscapedString(_lexeme, SIMPLE_STRING_ESCAPE, true));
return _factory.createCall(this, "shell_exec", args);
}
case COMPLEX_SYSTEM_STRING:
{
ArrayList<Expr> args = new ArrayList<Expr>();
args.add(parseEscapedString(_lexeme, COMPLEX_STRING_ESCAPE, true));
return _factory.createCall(this, "shell_exec", args);
}
case SIMPLE_STRING_ESCAPE:
case COMPLEX_STRING_ESCAPE:
return parseEscapedString(_lexeme, token, false);
case BINARY:
try {
return createBinary(_lexeme.getBytes("iso-8859-1"));
} catch (Exception e) {
throw new QuercusParseException(e);
}
case SIMPLE_BINARY_ESCAPE:
case COMPLEX_BINARY_ESCAPE:
return parseEscapedString(_lexeme, token, false, false);
case LONG:
{
long value = 0;
double doubleValue = 0;
long sign = 1;
boolean isOverflow = false;
char ch = _lexeme.charAt(0);
int i = 0;
if (ch == '+') {
i++;
} else if (ch == '-') {
sign = -1;
i++;
}
int len = _lexeme.length();
for (; i < len; i++) {
int digit = _lexeme.charAt(i) - '0';
long oldValue = value;
value = value * 10 + digit;
doubleValue = doubleValue * 10 + digit;
if (value < oldValue)
isOverflow = true;
}
if (! isOverflow)
return _factory.createLiteral(LongValue.create(value * sign));
else
return _factory.createLiteral(new DoubleValue(doubleValue * sign));
}
case DOUBLE:
return _factory.createLiteral(
new DoubleValue(Double.parseDouble(_lexeme)));
case NULL:
return _factory.createNull();
case TRUE:
return _factory.createLiteral(BooleanValue.TRUE);
case FALSE:
return _factory.createLiteral(BooleanValue.FALSE);
case '$':
return parseVariable();
case NEW:
return parseNew();
case FUNCTION:
return parseClosure();
case INCLUDE:
return _factory.createInclude(getLocation(), _sourceFile, parseExpr());
case REQUIRE:
return _factory.createRequire(getLocation(), _sourceFile, parseExpr());
case INCLUDE_ONCE:
return _factory.createIncludeOnce(getLocation(),
_sourceFile, parseExpr());
case REQUIRE_ONCE:
return _factory.createRequireOnce(getLocation(),
_sourceFile, parseExpr());
case LIST:
return parseList();
case PRINT:
return parsePrintExpr();
case EXIT:
return parseExit();
case DIE:
return parseDie();
case IDENTIFIER:
case NAMESPACE:
{
if (_lexeme.equals("new"))
return parseNew();
String name = _lexeme;
token = parseToken();
_peekToken = token;
if (token == '(' && ! _isNewExpr) {
// shortcut for common case of static function
return parseCall(name);
}
else
return parseConstant(name);
}
case '(':
{
_isIfTest = false;
Expr expr = parseExpr();
expect(')');
if (expr instanceof ConstExpr) {
String type = ((ConstExpr) expr).getVar();
int ns = type.lastIndexOf('\\');
if (ns >= 0)
type = type.substring(ns + 1);
if ("bool".equalsIgnoreCase(type)
|| "boolean".equalsIgnoreCase(type))
return _factory.createToBoolean(parseAssignExpr());
else if ("int".equalsIgnoreCase(type)
|| "integer".equalsIgnoreCase(type))
return _factory.createToLong(parseAssignExpr());
else if ("float".equalsIgnoreCase(type)
|| "double".equalsIgnoreCase(type)
|| "real".equalsIgnoreCase(type))
return _factory.createToDouble(parseAssignExpr());
else if ("string".equalsIgnoreCase(type))
return _factory.createToString(parseAssignExpr());
else if ("binary".equalsIgnoreCase(type))
return _factory.createToBinary(parseAssignExpr());
else if ("unicode".equalsIgnoreCase(type))
return _factory.createToUnicode(parseAssignExpr());
else if ("object".equalsIgnoreCase(type))
return _factory.createToObject(parseAssignExpr());
else if ("array".equalsIgnoreCase(type))
return _factory.createToArray(parseAssignExpr());
}
return expr;
}
case IMPORT:
{
String importTokenString = _lexeme;
token = parseToken();
if (token == '(') {
_peekToken = token;
return parseCall(importTokenString);
}
else {
_peekToken = token;
return parseImport();
}
}
default:
throw error(L.l("{0} is an unexpected token, expected an expression.",
tokenName(token)));
}
}
/**
* Parses a basic term.
*
* <pre>
* lhs ::= VARIABLE
* ::= lhs '[' expr ']'
* ::= lhs -> FIELD
* </pre>
*/
private AbstractVarExpr parseLeftHandSide()
throws IOException
{
int token = parseToken();
AbstractVarExpr lhs = null;
if (token == '$')
lhs = parseVariable();
else
throw error(L.l("expected variable at {0} as left-hand-side",
tokenName(token)));
while (true) {
token = parseToken();
switch (token) {
case '[':
{
token = parseToken();
if (token == ']') {
lhs = _factory.createArrayTail(getLocation(), lhs);
}
else {
_peekToken = token;
Expr index = parseExpr();
token = parseToken();
lhs = _factory.createArrayGet(getLocation(), lhs, index);
}
if (token != ']')
throw expect("']'", token);
}
break;
case '{':
{
Expr index = parseExpr();
expect('}');
lhs = _factory.createCharAt(lhs, index);
}
break;
case DEREF:
lhs = (AbstractVarExpr) parseDeref(lhs);
break;
default:
_peekToken = token;
return lhs;
}
}
}
private Expr parseScope(Expr classNameExpr)
throws IOException
{
int token = parseToken();
if (isIdentifier(token)) {
return classNameExpr.createClassConst(this, _lexeme);
}
else if (token == '$') {
token = parseToken();
if (isIdentifier(token)) {
return classNameExpr.createClassField(this, _lexeme);
}
else if (token == '{') {
Expr expr = parseExpr();
expect('}');
return classNameExpr.createClassField(this, expr);
}
else {
_peekToken = token;
return classNameExpr.createClassField(this, parseTermBase());
}
}
throw error(L.l("unexpected token '{0}' in class scope expression",
tokenName(token)));
}
private boolean isIdentifier(int token)
{
return token == IDENTIFIER || FIRST_IDENTIFIER_LEXEME <= token;
}
/**
* Parses the next variable
*/
private AbstractVarExpr parseVariable()
throws IOException
{
int token = parseToken();
if (token == THIS) {
return _factory.createThis(_classDef);
}
else if (token == '$') {
_peekToken = token;
// php/0d6c, php/0d6f
return _factory.createVarVar(parseTermArray());
}
else if (token == '{') {
AbstractVarExpr expr = _factory.createVarVar(parseExpr());
expect('}');
return expr;
}
else if (_lexeme == null)
throw error(L.l("Expected identifier at '{0}'", tokenName(token)));
if (_lexeme.indexOf('\\') >= 0) {
throw error(L.l("Namespace is not allowed for variable ${0}", _lexeme));
}
return _factory.createVar(_function.createVar(_lexeme));
}
public Expr createVar(String name)
{
return _factory.createVar(_function.createVar(name));
}
/**
* Parses the next function
*/
private Expr parseCall(String name)
throws IOException
{
if (name.equalsIgnoreCase("array"))
return parseArrayFunction();
ArrayList<Expr> args = parseArgs();
name = resolveIdentifier(name);
return _factory.createCall(this, name, args);
/*
if (name.equals("each")) {
if (args.size() != 1)
throw error(L.l("each requires a single expression"));
// php/1721
// we should let ArrayModule.each() handle it
//return _factory.createEach(args.get(0));
}
*/
}
/**
* Parses the next constant
*/
private Expr parseConstant(String name)
{
if (name.equals("__FILE__")) {
return _factory.createFileNameExpr(_parserLocation.getFileName());
}
else if (name.equals("__DIR__")) {
Path parent = Vfs.lookup(_parserLocation.getFileName()).getParent();
return _factory.createDirExpr(parent.getNativePath());
}
else if (name.equals("__LINE__"))
return _factory.createLong(_parserLocation.getLineNumber());
else if (name.equals("__CLASS__") && _classDef != null)
return createString(_classDef.getName());
else if (name.equals("__FUNCTION__")) {
return createString(_function.getName());
}
else if (name.equals("__METHOD__")) {
if (_classDef != null) {
if (_function.getName().length() != 0)
return createString(_classDef.getName() + "::" + _function.getName());
else
return createString(_classDef.getName());
}
else
return createString(_function.getName());
}
else if (name.equals("__NAMESPACE__")) {
return createString(_namespace);
}
name = resolveIdentifier(name);
if (name.startsWith("\\"))
name = name.substring(1);
return _factory.createConst(name);
}
/**
* Parses the next function
*/
private Expr parseCall(Expr name)
throws IOException
{
return name.createCall(this, getLocation(), parseArgs());
}
private ArrayList<Expr> parseArgs()
throws IOException
{
expect('(');
ArrayList<Expr> args = new ArrayList<Expr>();
int token;
while ((token = parseToken()) > 0 && token != ')') {
boolean isRef = false;
if (token == '&')
isRef = true;
else
_peekToken = token;
Expr expr = parseExpr();
if (isRef)
expr = expr.createRef(this);
args.add(expr);
token = parseToken();
if (token == ')')
break;
else if (token != ',')
throw expect("','", token);
}
return args;
}
public String getSelfClassName()
{
if (_classDef == null)
throw error(L.l("'self' is not valid because there is no active class."));
return _classDef.getName();
}
public String getParentClassName()
{
if (_classDef == null)
throw error(L.l(
"'parent' is not valid because there is no active class."));
return _classDef.getParentName();
}
/**
* Parses the new expression
*/
private Expr parseNew()
throws IOException
{
String name = null;
Expr nameExpr = null;
boolean isNewExpr = _isNewExpr;
_isNewExpr = true;
//nameExpr = parseTermBase();
nameExpr = parseTerm(false);
_isNewExpr = isNewExpr;
// XX: unicode issues?
if (nameExpr.isLiteral() || nameExpr instanceof ConstExpr) {
name = nameExpr.evalConstant().toString();
// php/0957
if ("self".equals(name) && _classDef != null)
name = _classDef.getName();
else if ("parent".equals(name) && getParentClassName() != null)
name = getParentClassName().toString();
else {
// name = resolveIdentifier(name);
}
}
int token = parseToken();
ArrayList<Expr> args = new ArrayList<Expr>();
if (token != '(')
_peekToken = token;
else {
while ((token = parseToken()) > 0 && token != ')') {
_peekToken = token;
args.add(parseExpr());
token = parseToken();
if (token == ')')
break;
else if (token != ',')
throw error(L.l("expected ','"));
}
}
Expr expr;
if (name != null)
expr = _factory.createNew(getLocation(), name, args);
else
expr = _factory.createVarNew(getLocation(), nameExpr, args);
return expr;
}
/**
* Parses the include expression
*/
private Expr parseInclude()
throws IOException
{
Expr name = parseExpr();
return _factory.createInclude(getLocation(), _sourceFile, name);
}
/**
* Parses the list(...) = value expression
*/
private Expr parseList()
throws IOException
{
ListHeadExpr leftVars = parseListHead();
expect('=');
Expr value = parseConditionalExpr();
return _factory.createList(this, leftVars, value);
}
/**
* Parses the list(...) expression
*/
private ListHeadExpr parseListHead()
throws IOException
{
expect('(');
int peek = parseToken();
ArrayList<Expr> leftVars = new ArrayList<Expr>();
while (peek > 0 && peek != ')') {
if (peek == LIST) {
leftVars.add(parseListHead());
peek = parseToken();
}
else if (peek != ',') {
_peekToken = peek;
Expr left = parseTerm(true);
leftVars.add(left);
left.assign(this);
peek = parseToken();
}
else {
leftVars.add(null);
}
if (peek == ',')
peek = parseToken();
else
break;
}
if (peek != ')')
throw error(L.l("expected ')'"));
return _factory.createListHead(leftVars);
}
/**
* Parses the exit/die expression
*/
private Expr parseExit()
throws IOException
{
int token = parseToken();
_peekToken = token;
if (token == '(') {
ArrayList<Expr> args = parseArgs();
if (args.size() > 0)
return _factory.createExit(args.get(0));
else
return _factory.createExit(null);
}
else {
return _factory.createExit(null);
}
}
/**
* Parses the exit/die expression
*/
private Expr parseDie()
throws IOException
{
int token = parseToken();
_peekToken = token;
if (token == '(') {
ArrayList<Expr> args = parseArgs();
if (args.size() > 0)
return _factory.createDie(args.get(0));
else
return _factory.createDie(null);
}
else {
return _factory.createDie(null);
}
}
/**
* Parses the array() expression
*/
private Expr parseArrayFunction()
throws IOException
{
String name = _lexeme;
int token = parseToken();
if (token != '(')
throw error(L.l("Expected '('"));
ArrayList<Expr> keys = new ArrayList<Expr>();
ArrayList<Expr> values = new ArrayList<Expr>();
while ((token = parseToken()) > 0 && token != ')') {
_peekToken = token;
Expr value = parseRefExpr();
token = parseToken();
if (token == ARRAY_RIGHT) {
Expr key = value;
value = parseRefExpr();
keys.add(key);
values.add(value);
token = parseToken();
}
else {
keys.add(null);
values.add(value);
}
if (token == ')')
break;
else if (token != ',')
throw error(L.l("expected ','"));
}
return _factory.createArrayFun(keys, values);
}
/**
* Parses a Quercus import.
*/
private Expr parseImport()
throws IOException
{
boolean isWildcard = false;
boolean isIdentifierStart = true;
StringBuilder sb = new StringBuilder();
while (true) {
int token = parseToken();
if (token == IDENTIFIER) {
sb.append(_lexeme);
token = parseToken();
if (token == '.') {
sb.append('.');
}
else {
_peekToken = token;
break;
}
}
else if (token == '*') {
if (sb.length() > 0)
sb.setLength(sb.length() - 1);
isWildcard = true;
break;
}
else {
throw error(L.l("'{0}' is an unexpected token in import",
tokenName(token)));
}
}
//expect(';');
return _factory.createImport(getLocation(), sb.toString(), isWildcard);
}
/**
* Parses the next token.
*/
private int parseToken()
throws IOException
{
int peekToken = _peekToken;
if (peekToken > 0) {
_peekToken = 0;
return peekToken;
}
while (true) {
int ch = read();
switch (ch) {
case -1:
return -1;
case ' ': case '\t': case '\n': case '\r':
break;
case '#':
while ((ch = readByte()) != '\n' && ch != '\r' && ch >= 0) {
if (ch != '?') {
}
else if ((ch = readByte()) != '>') {
_peek = ch;
}
else {
ch = readByte();
if (ch == '\r')
ch = readByte();
if (ch != '\n')
_peek = ch;
return parsePhpText();
}
}
break;
case '"':
{
String heredocEnd = _heredocEnd;
_heredocEnd = null;
int result = parseEscapedString('"');
_heredocEnd = heredocEnd;
return result;
}
case '`':
{
int token = parseEscapedString('`');
switch (token) {
case STRING:
return SYSTEM_STRING;
case SIMPLE_STRING_ESCAPE:
return SIMPLE_SYSTEM_STRING;
case COMPLEX_STRING_ESCAPE:
return COMPLEX_SYSTEM_STRING;
default:
throw new IllegalStateException();
}
}
case '\'':
parseStringToken('\'');
return STRING;
case ';': case '$': case '(': case ')': case '@':
case '[': case ']': case ',': case '{': case '}':
case '~':
return ch;
case '+':
ch = read();
if (ch == '=')
return PLUS_ASSIGN;
else if (ch == '+')
return INCR;
else
_peek = ch;
return '+';
case '-':
ch = read();
if (ch == '>')
return DEREF;
else if (ch == '=')
return MINUS_ASSIGN;
else if (ch == '-')
return DECR;
else
_peek = ch;
return '-';
case '*':
ch = read();
if (ch == '=')
return MUL_ASSIGN;
else
_peek = ch;
return '*';
case '/':
ch = read();
if (ch == '=')
return DIV_ASSIGN;
else if (ch == '/') {
while (ch >= 0) {
if (ch == '\n' || ch == '\r') {
break;
}
else if (ch == '?') {
ch = readByte();
if (ch == '>') {
ch = readByte();
if (ch == '\r')
ch = readByte();
if (ch != '\n')
_peek = ch;
return parsePhpText();
}
}
else
ch = readByte();
}
break;
}
else if (ch == '*') {
parseMultilineComment();
break;
}
else
_peek = ch;
return '/';
case '%':
ch = read();
if (ch == '=')
return MOD_ASSIGN;
else if (ch == '>') {
ch = read();
if (ch == '\r')
ch = read();
if (ch != '\n')
_peek = ch;
return parsePhpText();
}
else
_peek = ch;
return '%';
case ':':
ch = read();
if (ch == ':')
return SCOPE;
else
_peek = ch;
return ':';
case '=':
ch = read();
if (ch == '=') {
ch = read();
if (ch == '=')
return EQUALS;
else {
_peek = ch;
return EQ;
}
}
else if (ch == '>')
return ARRAY_RIGHT;
else {
_peek = ch;
return '=';
}
case '!':
ch = read();
if (ch == '=') {
ch = read();
if (ch == '=')
return NEQUALS;
else {
_peek = ch;
return NEQ;
}
}
else {
_peek = ch;
return '!';
}
case '&':
ch = read();
if (ch == '&')
return C_AND;
else if (ch == '=')
return AND_ASSIGN;
else {
_peek = ch;
return '&';
}
case '^':
ch = read();
if (ch == '=')
return XOR_ASSIGN;
else
_peek = ch;
return '^';
case '|':
ch = read();
if (ch == '|')
return C_OR;
else if (ch == '=')
return OR_ASSIGN;
else {
_peek = ch;
return '|';
}
case '<':
ch = read();
if (ch == '<') {
ch = read();
if (ch == '=')
return LSHIFT_ASSIGN;
else if (ch == '<') {
return parseHeredocToken();
}
else
_peek = ch;
return LSHIFT;
}
else if (ch == '=')
return LEQ;
else if (ch == '>')
return NEQ;
else if (ch == '/') {
StringBuilder sb = new StringBuilder();
if (! parseTextMatch(sb, "script"))
throw error(L.l("expected 'script' at '{0}'", sb));
expect('>');
return parsePhpText();
}
else
_peek = ch;
return '<';
case '>':
ch = read();
if (ch == '>') {
ch = read();
if (ch == '=')
return RSHIFT_ASSIGN;
else
_peek = ch;
return RSHIFT;
}
else if (ch == '=')
return GEQ;
else
_peek = ch;
return '>';
case '?':
ch = read();
if (ch == '>') {
ch = read();
if (ch == '\r')
ch = read();
if (ch != '\n')
_peek = ch;
return parsePhpText();
}
else
_peek = ch;
return '?';
case '.':
ch = read();
if (ch == '=')
return APPEND_ASSIGN;
_peek = ch;
if ('0' <= ch && ch <= '9')
return parseNumberToken('.');
else
return '.';
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
return parseNumberToken(ch);
default:
if (ch == 'b') {
int ch2 = read();
if (ch2 == '\'') {
parseStringToken('\'', false);
return BINARY;
}
else if (ch2 == '"') {
int token = parseEscapedString('"', false);
switch (token) {
case STRING:
return BINARY;
case SIMPLE_STRING_ESCAPE:
return SIMPLE_BINARY_ESCAPE;
case COMPLEX_STRING_ESCAPE:
return COMPLEX_BINARY_ESCAPE;
default:
return token;
}
}
else
_peek = ch2;
}
return parseNamespaceIdentifier(ch);
}
}
}
private String parseIdentifier()
throws IOException
{
int token = _peekToken;
_peekToken = -1;
if (token <= 0)
token = parseIdentifier(read());
if (token != IDENTIFIER && token < FIRST_IDENTIFIER_LEXEME)
throw error(L.l("expected identifier at {0}.", tokenName(token)));
if (_lexeme.indexOf('\\') >= 0) {
throw error(L.l("namespace identifier is not allowed at '{0}'",
_lexeme));
}
else if (_peek == '\\') {
throw error(L.l("namespace identifier is not allowed at '{0}\\'",
_lexeme));
}
return _lexeme;
}
private String parseNamespaceIdentifier()
throws IOException
{
int token = _peekToken;
_peekToken = -1;
if (token <= 0)
token = parseNamespaceIdentifier(read());
if (token == IDENTIFIER)
return resolveIdentifier(_lexeme);
else if (FIRST_IDENTIFIER_LEXEME <= token)
return resolveIdentifier(_lexeme);
else
throw error(L.l("expected identifier at {0}.", tokenName(token)));
}
public String getSystemFunctionName(String name)
{
int p = name.lastIndexOf('\\');
if (p < 0)
return name;
String systemName = name.substring(p + 1);
if (_quercus.findFunction(systemName) != null)
return systemName;
else
return null;
}
private String resolveIdentifier(String id)
{
if (id.startsWith("\\"))
return id.substring(1);
int ns = id.indexOf('\\');
if (ns > 0) {
String prefix = id.substring(0, ns);
String use = _namespaceUseMap.get(prefix);
if (use != null)
return use + id.substring(ns);
else if (_namespace.equals(""))
return id;
else
return _namespace + "\\" + id;
}
else {
String use = _namespaceUseMap.get(id);
if (use != null)
return use;
else if (_namespace.equals(""))
return id;
else
return _namespace + '\\' + id;
}
}
private int parseIdentifier(int ch)
throws IOException
{
for (; Character.isWhitespace(ch); ch = read()) {
}
if (isIdentifierStart(ch)) {
_sb.setLength(0);
_sb.append((char) ch);
for (ch = read(); isIdentifierPart(ch); ch = read()) {
_sb.append((char) ch);
}
_peek = ch;
return lexemeToToken();
}
throw error("expected identifier at " + (char) ch);
}
private int parseNamespaceIdentifier(int ch)
throws IOException
{
for (; Character.isWhitespace(ch); ch = read()) {
}
if (isNamespaceIdentifierStart(ch)) {
_sb.setLength(0);
_sb.append((char) ch);
for (ch = read(); isNamespaceIdentifierPart(ch); ch = read()) {
_sb.append((char) ch);
}
_peek = ch;
return lexemeToToken();
}
throw error("unknown lexeme:" + (char) ch);
}
private int lexemeToToken()
throws IOException
{
_lexeme = _sb.toString();
// the 'static' reserved keyword vs late static binding (static::$a)
if (_peek == ':' && "static".equals(_lexeme))
return IDENTIFIER;
int reserved = _reserved.get(_lexeme);
if (reserved > 0)
return reserved;
reserved = _insensitiveReserved.get(_lexeme.toLowerCase(Locale.ENGLISH));
if (reserved > 0)
return reserved;
else
return IDENTIFIER;
}
/**
* Parses a multiline comment.
*/
private void parseMultilineComment()
throws IOException
{
int ch = readByte();
if (ch == '*') {
_sb.setLength(0);
_sb.append('/');
_sb.append('*');
do {
if (ch != '*') {
_sb.append((char) ch);
}
else if ((ch = readByte()) == '/') {
_sb.append('*');
_sb.append('/');
_comment = _sb.toString();
return;
}
else {
_sb.append('*');
_peek = ch;
}
} while ((ch = readByte()) >= 0);
_comment = _sb.toString();
}
else if (ch >= 0) {
do {
if (ch != '*') {
}
else if ((ch = readByte()) == '/')
return;
else
_peek = ch;
} while ((ch = readByte()) >= 0);
}
}
/**
* Parses quercus text
*/
private int parsePhpText()
throws IOException
{
StringBuilder sb = new StringBuilder();
int ch = read();
while (ch > 0) {
if (ch == '<') {
int ch2;
int ch3;
if ((ch = read()) == 's' || ch == 'S') {
_peek = ch;
if (parseScriptBegin(sb)) {
_lexeme = sb.toString();
return TEXT;
}
ch = read();
}
else if (ch == '%') {
if ((ch = read()) == '=') {
_lexeme = sb.toString();
return TEXT_ECHO;
}
else if (Character.isWhitespace(ch)) {
_lexeme = sb.toString();
return TEXT;
}
}
else if (ch != '?') {
sb.append('<');
}
else if ((ch = read()) == '=') {
_lexeme = sb.toString();
return TEXT_ECHO;
}
else {
_lexeme = sb.toString();
_peek = ch;
if (ch == 'p' || ch == 'P')
return TEXT_PHP;
else
return TEXT;
}
}
else {
sb.append((char) ch);
ch = read();
}
}
_lexeme = sb.toString();
return TEXT;
}
/**
* Parses the <script language="quercus"> opening
*/
private boolean parseScriptBegin(StringBuilder sb)
throws IOException
{
int begin = sb.length();
sb.append('<');
if (! parseTextMatch(sb, "script"))
return false;
parseWhitespace(sb);
if (! parseTextMatch(sb, "language="))
return false;
int openingParentheses = read();
if(openingParentheses == '\'' || openingParentheses == '"'){
if (! parseTextMatch(sb, "php")){
sb.append((char) openingParentheses);
return false;
}
int closingParentheses = read();
if(openingParentheses != closingParentheses){
sb.append((char) closingParentheses);
return false;
}
}
parseWhitespace(sb);
int ch = read();
if (ch == '>') {
sb.setLength(begin);
return true;
}
else {
_peek = ch;
return false;
}
}
private boolean parseTextMatch(StringBuilder sb, String text)
throws IOException
{
int len = text.length();
for (int i = 0; i < len; i++) {
int ch = read();
if (ch < 0)
return false;
if (Character.toLowerCase(ch) != text.charAt(i)) {
_peek = ch;
return false;
}
else
sb.append((char) ch);
}
return true;
}
private void parseWhitespace(StringBuilder sb)
throws IOException
{
int ch;
while (Character.isWhitespace((ch = read()))) {
sb.append((char) ch);
}
_peek = ch;
}
private void parseStringToken(int end)
throws IOException
{
parseStringToken(end, isUnicodeSemantics());
}
/**
* Parses the next string token.
*/
private void parseStringToken(int end, boolean isUnicode)
throws IOException
{
_sb.setLength(0);
int ch;
for (ch = read(); ch >= 0 && ch != end; ch = read()) {
if (ch == '\\') {
ch = read();
if (isUnicode) {
if (ch == 'u') {
int value = parseUnicodeEscape(false);
if (value < 0) {
_sb.append('\\');
_sb.append('u');
}
else
_sb.append((char) value); // Character.toChars(value));
continue;
}
else if (ch == 'U') {
int value = parseUnicodeEscape(true);
if (value < 0) {
_sb.append('\\');
_sb.append('U');
}
else
_sb.append((char) value); // Character.toChars(value));
continue;
}
}
if (end == '"') {
_sb.append('\\');
if (ch >= 0)
_sb.append((char) ch);
}
else {
switch (ch) {
case '\'': case '\\':
_sb.append((char) ch);
break;
default:
_sb.append('\\');
_sb.append((char) ch);
break;
}
}
}
else
_sb.append((char) ch);
}
_lexeme = _sb.toString();
}
/**
* Parses the next heredoc token.
*/
private int parseHeredocToken()
throws IOException
{
_sb.setLength(0);
int ch;
// eat whitespace
while ((ch = read()) >= 0 && (ch == ' ' || ch == '\t')) {
}
_peek = ch;
while ((ch = read()) >= 0 && ch != '\r' && ch != '\n') {
_sb.append((char) ch);
}
_heredocEnd = _sb.toString();
if (ch == '\n') {
}
else if (ch == '\r') {
ch = read();
if (ch != '\n')
_peek = ch;
}
else
_peek = ch;
return parseEscapedString('"');
}
/**
* Parses the next string
* XXX: parse as Unicode if and only if unicode.semantics is on.
*/
private Expr parseEscapedString(String prefix, int token, boolean isSystem)
throws IOException
{
return parseEscapedString(prefix, token, isSystem, true);
}
/**
* Parses the next string
*/
private Expr parseEscapedString(String prefix,
int token,
boolean isSystem,
boolean isUnicode)
throws IOException
{
Expr expr;
if (isUnicode)
expr = createString(prefix);
else {
// XXX: getBytes isn't correct
expr = createBinary(prefix.getBytes("iso-8859-1"));
}
while (true) {
Expr tail;
if (token == COMPLEX_STRING_ESCAPE
|| token == COMPLEX_BINARY_ESCAPE) {
tail = parseExpr();
expect('}');
}
else if (token == SIMPLE_STRING_ESCAPE
|| token == SIMPLE_BINARY_ESCAPE) {
int ch = read();
_sb.setLength(0);
for (; isIdentifierPart(ch); ch = read()) {
_sb.append((char) ch);
}
_peek = ch;
String varName = _sb.toString();
if (varName.equals("this"))
tail = _factory.createThis(_classDef);
else
tail = _factory.createVar(_function.createVar(varName));
// php/013n
if (((ch = read()) == '[' || ch == '-')) {
if (ch == '[') {
tail = parseSimpleArrayTail(tail);
ch = read();
}
else {
if ((ch = read()) != '>') {
tail = _factory.createAppend(tail, createString("-"));
}
else if (isIdentifierPart(ch = read())) {
_sb.clear();
for (; isIdentifierPart(ch); ch = read()) {
_sb.append((char) ch);
}
tail = tail.createFieldGet(_factory,
createStringValue(_sb.toString()));
}
else {
tail = _factory.createAppend(tail, createString("->"));
}
_peek = ch;
}
}
_peek = ch;
}
else
throw error("unexpected token");
expr = _factory.createAppend(expr, tail);
if (isSystem)
token = parseEscapedString('`');
else
token = parseEscapedString('"');
if (_sb.length() > 0) {
Expr string;
if (isUnicode)
string = createString(_sb.toString());
else
string = createBinary(_sb.toString().getBytes("iso-8859-1"));
expr = _factory.createAppend(expr, string);
}
if (token == STRING)
return expr;
}
}
/**
* Parses the next string
*/
private Expr parseSimpleArrayTail(Expr tail)
throws IOException
{
int ch = read();
_sb.clear();
if (ch == '$') {
for (ch = read(); isIdentifierPart(ch); ch = read()) {
_sb.append((char) ch);
}
VarExpr var = _factory.createVar(_function.createVar(_sb.toString()));
tail = _factory.createArrayGet(getLocation(), tail, var);
}
else if ('0' <= ch && ch <= '9') {
long index = ch - '0';
for (ch = read();
'0' <= ch && ch <= '9';
ch = read()) {
index = 10 * index + ch - '0';
}
tail = _factory.createArrayGet(getLocation(),
tail, _factory.createLong(index));
}
else if (isIdentifierPart(ch)) {
for (; isIdentifierPart(ch); ch = read()) {
_sb.append((char) ch);
}
Expr constExpr = _factory.createConst(_sb.toString());
tail = _factory.createArrayGet(getLocation(), tail, constExpr);
}
else
throw error(L.l("Unexpected character at {0}",
String.valueOf((char) ch)));
if (ch != ']')
throw error(L.l("Expected ']' at {0}",
String.valueOf((char) ch)));
return tail;
}
private Expr createString(String lexeme)
{
// XXX: see QuercusParser.parseDefault for _quercus == null
if (isUnicodeSemantics())
return _factory.createUnicode(lexeme);
else
return _factory.createString(lexeme);
}
private StringValue createStringValue(String lexeme)
{
// XXX: see QuercusParser.parseDefault for _quercus == null
if (isUnicodeSemantics())
return new UnicodeBuilderValue(lexeme);
else
return new ConstStringValue(lexeme);
}
private Expr createBinary(byte []bytes)
throws IOException
{
// XXX: see QuercusParser.parseDefault for _quercus == null
// php/0ch1, php/0350
// return _factory.createBinary(bytes);
if (isUnicodeSemantics())
return _factory.createBinary(bytes);
else {
try {
return _factory.createString(
new String(bytes, 0, bytes.length, "iso-8859-1"));
} catch (UnsupportedEncodingException e) {
throw new QuercusParseException(e);
}
}
}
/**
* XXX: parse as Unicode if and only if unicode.semantics is on.
*/
private int parseEscapedString(char end)
throws IOException
{
return parseEscapedString(end, isUnicodeSemantics());
}
/**
* Parses the next string
*/
private int parseEscapedString(char end, boolean isUnicode)
throws IOException
{
_sb.setLength(0);
int ch;
while ((ch = read()) > 0) {
if (_heredocEnd == null && ch == end) {
_lexeme = _sb.toString();
return STRING;
}
else if (ch == '\\') {
ch = read();
switch (ch) {
case '0': case '1': case '2': case '3':
_sb.append((char) parseOctalEscape(ch));
break;
case 't':
_sb.append('\t');
break;
case 'r':
_sb.append('\r');
break;
case 'n':
_sb.append('\n');
break;
case '"':
case '`':
if (_heredocEnd != null)
_sb.append('\\');
_sb.append((char) ch);
break;
case '$':
case '\\':
_sb.append((char) ch);
break;
case 'x': {
int value = parseHexEscape();
if (value >= 0)
_sb.append((char) value);
else {
_sb.append('\\');
_sb.append('x');
}
break;
}
case 'u':
if (isUnicode) {
int result = parseUnicodeEscape(false);
if (result < 0) {
_sb.append('\\');
_sb.append('u');
}
else
_sb.append(Character.toChars(result));
}
else {
_sb.append('\\');
_sb.append((char) ch);
}
break;
case 'U':
if (isUnicode) {
int result = parseUnicodeEscape(true);
if (result < 0) {
_sb.append('\\');
_sb.append('U');
}
else
_sb.append(Character.toChars(result));
}
else {
_sb.append('\\');
_sb.append((char) ch);
}
break;
case '{':
ch = read();
_peek = ch;
if (ch == '$' && _heredocEnd == null)
_sb.append('{');
else
_sb.append("\\{");
break;
default:
_sb.append('\\');
_sb.append((char) ch);
break;
}
}
else if (ch == '$') {
ch = read();
if (ch == '{') {
_peek = '$';
_lexeme = _sb.toString();
return COMPLEX_STRING_ESCAPE;
}
else if (isIdentifierStart(ch)) {
_peek = ch;
_lexeme = _sb.toString();
return SIMPLE_STRING_ESCAPE;
}
else {
_sb.append('$');
_peek = ch;
}
}
else if (ch == '{') {
ch = read();
if (ch == '$') {
_peek = ch;
_lexeme = _sb.toString();
return COMPLEX_STRING_ESCAPE;
}
else {
_peek = ch;
_sb.append('{');
}
}
/* quercus/013c
else if ((ch == '\r' || ch == '\n') && _heredocEnd == null)
throw error(L.l("unexpected newline in string."));
*/
else {
_sb.append((char) ch);
if (_heredocEnd == null || ! _sb.endsWith(_heredocEnd)) {
}
else if (
_sb.length() == _heredocEnd.length()
|| _sb.charAt(_sb.length() - _heredocEnd.length() - 1) == '\n'
|| _sb.charAt(_sb.length() - _heredocEnd.length() - 1) == '\r'
) {
_sb.setLength(_sb.length() - _heredocEnd.length());
if (_sb.length() > 0 && _sb.charAt(_sb.length() - 1) == '\n')
_sb.setLength(_sb.length() - 1);
if (_sb.length() > 0 && _sb.charAt(_sb.length() - 1) == '\r')
_sb.setLength(_sb.length() - 1);
_heredocEnd = null;
_lexeme = _sb.toString();
return STRING;
}
}
}
_lexeme = _sb.toString();
return STRING;
}
private boolean isNamespaceIdentifierStart(int ch)
{
return isIdentifierStart(ch) || ch == '\\';
}
private boolean isIdentifierStart(int ch)
{
if (ch < 0)
return false;
else
return (ch >= 'a' && ch <= 'z'
|| ch >= 'A' && ch <= 'Z'
|| ch == '_'
|| Character.isLetter(ch));
}
private boolean isNamespaceIdentifierPart(int ch)
{
return isIdentifierPart(ch) || ch == '\\';
}
private boolean isIdentifierPart(int ch)
{
if (ch < 0)
return false;
else
return (ch >= 'a' && ch <= 'z'
|| ch >= 'A' && ch <= 'Z'
|| ch >= '0' && ch <= '9'
|| ch == '_'
|| Character.isLetterOrDigit(ch));
}
private int parseOctalEscape(int ch)
throws IOException
{
int value = ch - '0';
ch = read();
if (ch < '0' || ch > '7') {
_peek = ch;
return value;
}
value = 8 * value + ch - '0';
ch = read();
if (ch < '0' || ch > '7') {
_peek = ch;
return value;
}
value = 8 * value + ch - '0';
return value;
}
private int parseHexEscape()
throws IOException
{
int value = 0;
int ch = read();
if ('0' <= ch && ch <= '9')
value = 16 * value + ch - '0';
else if ('a' <= ch && ch <= 'f')
value = 16 * value + 10 + ch - 'a';
else if ('A' <= ch && ch <= 'F')
value = 16 * value + 10 + ch - 'A';
else {
_peek = ch;
return -1;
}
ch = read();
if ('0' <= ch && ch <= '9')
value = 16 * value + ch - '0';
else if ('a' <= ch && ch <= 'f')
value = 16 * value + 10 + ch - 'a';
else if ('A' <= ch && ch <= 'F')
value = 16 * value + 10 + ch - 'A';
else {
_peek = ch;
return value;
}
return value;
}
private int parseUnicodeEscape(boolean isLongForm)
throws IOException
{
int codePoint = parseHexEscape();
if (codePoint < 0)
return -1;
int low = parseHexEscape();
if (low < 0)
return codePoint;
codePoint = codePoint * 256 + low;
if (isLongForm) {
low = parseHexEscape();
if (low < 0)
return codePoint;
codePoint = codePoint * 256 + low;
}
return codePoint;
}
/**
* Parses the next number.
*/
private int parseNumberToken(int ch)
throws IOException
{
int ch0 = ch;
if (ch == '0') {
ch = read();
if (ch == 'x' || ch == 'X')
return parseHex();
else if (ch == '0')
return parseNumberToken(ch);
else {
_peek = ch;
ch = '0';
}
}
_sb.setLength(0);
int token = LONG;
for (; '0' <= ch && ch <= '9'; ch = read()) {
_sb.append((char) ch);
}
if (ch == '.') {
token = DOUBLE;
_sb.append((char) ch);
for (ch = read(); '0' <= ch && ch <= '9'; ch = read()) {
_sb.append((char) ch);
}
}
if (ch == 'e' || ch == 'E') {
token = DOUBLE;
_sb.append((char) ch);
ch = read();
if (ch == '+' || ch == '-') {
_sb.append((char) ch);
ch = read();
}
if ('0' <= ch && ch <= '9') {
for (; '0' <= ch && ch <= '9'; ch = read()) {
_sb.append((char) ch);
}
}
else
throw error(L.l("illegal exponent"));
}
_peek = ch;
if (ch0 == '0' && token == LONG) {
int len = _sb.length();
int value = 0;
for (int i = 0; i < len; i++) {
ch = _sb.charAt(i);
if ('0' <= ch && ch <= '7')
value = value * 8 + ch - '0';
else
break;
}
_lexeme = String.valueOf(value);
}
else {
_lexeme = _sb.toString();
}
return token;
}
/**
* Parses the next as hex
*/
private int parseHex()
throws IOException
{
long value = 0;
double dValue = 0;
while (true) {
int ch = read();
if ('0' <= ch && ch <= '9') {
value = 16 * value + ch - '0';
dValue = 16 * dValue + ch - '0';
}
else if ('a' <= ch && ch <= 'f') {
value = 16 * value + ch - 'a' + 10;
dValue = 16 * dValue + ch - 'a' + 10;
}
else if ('A' <= ch && ch <= 'F') {
value = 16 * value + ch - 'A' + 10;
dValue = 16 * dValue + ch - 'A' + 10;
}
else {
_peek = ch;
break;
}
}
if (value == dValue) {
_lexeme = String.valueOf(value);
return LONG;
}
else {
_lexeme = String.valueOf(dValue);
return DOUBLE;
}
}
/**
* Parses the next as octal
*/
private int parseOctal(int ch)
throws IOException
{
long value = 0;
double dValue = 0;
while (true) {
if ('0' <= ch && ch <= '7') {
value = 8 * value + ch - '0';
dValue = 8 * dValue + ch - '0';
}
else {
while ('0' <= ch && ch <= '9') {
ch = read();
}
_peek = ch;
break;
}
ch = read();
}
if (value == dValue) {
_lexeme = String.valueOf(value);
return LONG;
}
else {
_lexeme = String.valueOf(dValue);
return DOUBLE;
}
}
private void expect(int expect)
throws IOException
{
int token = parseToken();
if (token != expect)
throw error(L.l("expected {0} at {1}",
tokenName(expect),
tokenName(token)));
}
/**
* Reads the next character.
*/
private int read()
throws IOException
{
int peek = _peek;
if (peek >= 0) {
_peek = -1;
return peek;
}
try {
int ch = _is.readChar();
if (ch == '\r') {
_parserLocation.incrementLineNumber();
_hasCr = true;
}
else if (ch == '\n' && ! _hasCr) {
_parserLocation.incrementLineNumber();
}
else
_hasCr = false;
return ch;
} catch (CharConversionException e) {
throw new QuercusParseException(getFileName() + ":" + getLine()
+ ": " + e
+ "\nCheck that the script-encoding setting matches the "
+ "source file's encoding", e);
} catch (IOException e) {
throw new IOExceptionWrapper(
getFileName() + ":" + getLine() + ":" + e, e);
}
}
/*
* Reads the next byte.
*/
private int readByte()
throws IOException
{
int peek = _peek;
if (peek >= 0) {
_peek = -1;
return peek;
}
try {
int ch;
// XXX: should really be handled by ReadStream
// php/001b
if (_encoding == null)
ch = _is.read();
else
ch = _is.readChar();
if (ch == '\r') {
_parserLocation.incrementLineNumber();
_hasCr = true;
}
else if (ch == '\n' && ! _hasCr)
_parserLocation.incrementLineNumber();
else
_hasCr = false;
return ch;
} catch (IOException e) {
throw new IOExceptionWrapper(
getFileName() + ":" + getLine() + ":" + e, e);
}
}
/**
* Returns an error.
*/
private QuercusParseException expect(String expected, int token)
{
return error(L.l("expected {0} at {1}", expected, tokenName(token)));
}
/**
* Returns an error.
*/
public QuercusParseException error(String msg)
{
int lineNumber = _parserLocation.getLineNumber();
int lines = 5;
int first = lines / 2;
String []sourceLines = Env.getSourceLine(_sourceFile,
lineNumber - first + _sourceOffset,
lines);
if (sourceLines != null
&& sourceLines.length > 0) {
StringBuilder sb = new StringBuilder();
String shortFile = _parserLocation.getFileName();
int p = shortFile.lastIndexOf('/');
if (p > 0)
shortFile = shortFile.substring(p + 1);
sb.append(_parserLocation.toString())
.append(msg)
.append(" in");
for (int i = 0; i < sourceLines.length; i++) {
if (sourceLines[i] == null)
continue;
sb.append("\n");
sb.append(shortFile)
.append(":")
.append(lineNumber - first + i)
.append(": ")
.append(sourceLines[i]);
}
return new QuercusParseException(sb.toString());
}
else
return new QuercusParseException(_parserLocation.toString() + msg);
}
/**
* Returns the token name.
*/
private String tokenName(int token)
{
switch (token) {
case -1:
return "end of file";
case '\'':
return "'";
case AS: return "'as'";
case TRUE: return "true";
case FALSE: return "false";
case AND_RES: return "'and'";
case OR_RES: return "'or'";
case XOR_RES: return "'xor'";
case C_AND: return "'&&'";
case C_OR: return "'||'";
case IF: return "'if'";
case ELSE: return "'else'";
case ELSEIF: return "'elseif'";
case ENDIF: return "'endif'";
case WHILE: return "'while'";
case ENDWHILE: return "'endwhile'";
case DO: return "'do'";
case FOR: return "'for'";
case ENDFOR: return "'endfor'";
case FOREACH: return "'foreach'";
case ENDFOREACH: return "'endforeach'";
case SWITCH: return "'switch'";
case ENDSWITCH: return "'endswitch'";
case ECHO: return "'echo'";
case PRINT: return "'print'";
case LIST: return "'list'";
case CASE: return "'case'";
case DEFAULT: return "'default'";
case CLASS: return "'class'";
case INTERFACE: return "'interface'";
case EXTENDS: return "'extends'";
case IMPLEMENTS: return "'implements'";
case RETURN: return "'return'";
case DIE: return "'die'";
case EXIT: return "'exit'";
case THROW: return "'throw'";
case CLONE: return "'clone'";
case INSTANCEOF: return "'instanceof'";
case SIMPLE_STRING_ESCAPE: return "string";
case COMPLEX_STRING_ESCAPE: return "string";
case REQUIRE: return "'require'";
case REQUIRE_ONCE: return "'require_once'";
case PRIVATE: return "'private'";
case PROTECTED: return "'protected'";
case PUBLIC: return "'public'";
case STATIC: return "'static'";
case FINAL: return "'final'";
case ABSTRACT: return "'abstract'";
case CONST: return "'const'";
case GLOBAL: return "'global'";
case FUNCTION: return "'function'";
case THIS: return "'this'";
case ARRAY_RIGHT: return "'=>'";
case LSHIFT: return "'<<'";
case IDENTIFIER:
return "'" + _lexeme + "'";
case LONG:
return "integer (" + _lexeme + ")";
case DOUBLE:
return "double (" + _lexeme + ")";
case TEXT:
return "TEXT (token " + token + ")";
case STRING:
return "string(" + _lexeme + ")";
case TEXT_ECHO:
return "<?=";
case SCOPE:
return "SCOPE (" + _lexeme + ")";
case NAMESPACE:
return "NAMESPACE";
case USE:
return "USE";
default:
if (32 <= token && token < 127)
return "'" + (char) token + "'";
else
return "(token " + token + ")";
}
}
/**
* The location from which the last token was read.
* @return
*/
public Location getLocation()
{
return _parserLocation.getLocation();
}
private String pushWhileLabel()
{
return pushLoopLabel(createWhileLabel());
}
private String pushDoLabel()
{
return pushLoopLabel(createDoLabel());
}
private String pushForLabel()
{
return pushLoopLabel(createForLabel());
}
private String pushForeachLabel()
{
return pushLoopLabel(createForeachLabel());
}
private String pushSwitchLabel()
{
return pushLoopLabel(createSwitchLabel());
}
private String pushLoopLabel(String label)
{
_loopLabelList.add(label);
return label;
}
private String popLoopLabel()
{
int size = _loopLabelList.size();
if (size == 0)
return null;
else
return _loopLabelList.remove(size - 1);
}
private String createWhileLabel()
{
return "while_" + _labelsCreated++;
}
private String createDoLabel()
{
return "do_" + _labelsCreated++;
}
private String createForLabel()
{
return "for_" + _labelsCreated++;
}
private String createForeachLabel()
{
return "foreach_" + _labelsCreated++;
}
private String createSwitchLabel()
{
return "switch_" + _labelsCreated++;
}
/*
* Returns true if this is a switch label.
*/
public static boolean isSwitchLabel(String label)
{
return label != null && label.startsWith("switch");
}
public void close()
{
ReadStream is = _is;
_is = null;
if (is != null)
is.close();
}
private class ParserLocation {
private int _lineNumber = 1;
private String _fileName;
private String _userPath;
private String _lastClassName;
private String _lastFunctionName;
private Location _location;
public int getLineNumber()
{
return _lineNumber;
}
public void setLineNumber(int lineNumber)
{
_lineNumber = lineNumber;
_location = null;
}
public void incrementLineNumber()
{
_lineNumber++;
_location = null;
}
public String getFileName()
{
return _fileName;
}
public void setFileName(String fileName)
{
_fileName = fileName;
_userPath = fileName;
_location = null;
}
public void setFileName(Path path)
{
// php/600a
// need to return proper Windows paths (for joomla)
_fileName = path.getNativePath();
_userPath = path.getUserPath();
}
public String getUserPath()
{
return _userPath;
}
public Location getLocation()
{
String currentFunctionName
= (_function == null || _function.isPageMain()
? null
: _function.getName());
String currentClassName = _classDef == null ? null : _classDef.getName();
if (_location != null) {
if (!equals(currentFunctionName, _lastFunctionName))
_location = null;
else if (!equals(currentClassName, _lastClassName))
_location = null;
}
if (_location == null)
_location = new Location(_fileName, _lineNumber,
currentClassName, currentFunctionName);
_lastFunctionName = currentFunctionName;
_lastClassName = currentClassName;
return _location;
}
private boolean equals(String s1, String s2)
{
return (s1 == null || s2 == null) ? s1 == s2 : s1.equals(s2);
}
@Override
public String toString()
{
return _fileName + ":" + _lineNumber + ": ";
}
}
static {
_insensitiveReserved.put("echo", ECHO);
_insensitiveReserved.put("print", PRINT);
_insensitiveReserved.put("if", IF);
_insensitiveReserved.put("else", ELSE);
_insensitiveReserved.put("elseif", ELSEIF);
_insensitiveReserved.put("do", DO);
_insensitiveReserved.put("while", WHILE);
_insensitiveReserved.put("for", FOR);
_insensitiveReserved.put("function", FUNCTION);
_insensitiveReserved.put("class", CLASS);
_insensitiveReserved.put("new", NEW);
_insensitiveReserved.put("return", RETURN);
_insensitiveReserved.put("break", BREAK);
_insensitiveReserved.put("continue", CONTINUE);
// quercus/0260
// _insensitiveReserved.put("var", VAR);
_insensitiveReserved.put("this", THIS);
_insensitiveReserved.put("private", PRIVATE);
_insensitiveReserved.put("protected", PROTECTED);
_insensitiveReserved.put("public", PUBLIC);
_insensitiveReserved.put("and", AND_RES);
_insensitiveReserved.put("xor", XOR_RES);
_insensitiveReserved.put("or", OR_RES);
_insensitiveReserved.put("extends", EXTENDS);
_insensitiveReserved.put("static", STATIC);
_insensitiveReserved.put("include", INCLUDE);
_insensitiveReserved.put("require", REQUIRE);
_insensitiveReserved.put("include_once", INCLUDE_ONCE);
_insensitiveReserved.put("require_once", REQUIRE_ONCE);
_insensitiveReserved.put("unset", UNSET);
_insensitiveReserved.put("foreach", FOREACH);
_insensitiveReserved.put("as", AS);
_insensitiveReserved.put("switch", SWITCH);
_insensitiveReserved.put("case", CASE);
_insensitiveReserved.put("default", DEFAULT);
_insensitiveReserved.put("die", DIE);
_insensitiveReserved.put("exit", EXIT);
_insensitiveReserved.put("global", GLOBAL);
_insensitiveReserved.put("list", LIST);
_insensitiveReserved.put("endif", ENDIF);
_insensitiveReserved.put("endwhile", ENDWHILE);
_insensitiveReserved.put("endfor", ENDFOR);
_insensitiveReserved.put("endforeach", ENDFOREACH);
_insensitiveReserved.put("endswitch", ENDSWITCH);
_insensitiveReserved.put("true", TRUE);
_insensitiveReserved.put("false", FALSE);
_insensitiveReserved.put("null", NULL);
_insensitiveReserved.put("clone", CLONE);
_insensitiveReserved.put("instanceof", INSTANCEOF);
_insensitiveReserved.put("const", CONST);
_insensitiveReserved.put("final", FINAL);
_insensitiveReserved.put("abstract", ABSTRACT);
_insensitiveReserved.put("throw", THROW);
_insensitiveReserved.put("try", TRY);
_insensitiveReserved.put("catch", CATCH);
_insensitiveReserved.put("interface", INTERFACE);
_insensitiveReserved.put("implements", IMPLEMENTS);
_insensitiveReserved.put("import", IMPORT);
// backward compatibility issues
_insensitiveReserved.put("namespace", NAMESPACE);
_insensitiveReserved.put("use", USE);
}
}