Arquivos
hhvm/hphp/util/parser/hphp.y
T
jdelong 6faa7cbea1 @override-unit-failures Initial support for <?hh typedefs and shapes
Adds runtime support for non-class typehints.  Typedefs are
introduced using type statements, and autoloaded via a new autoload
map entry.  Shapes are parsed but the structure is currently thrown
away and treated as arrays at runtime.  This extends the NamedEntity
structure to sometimes cache 'NameDefs', which are either Typedef*'s
or Class*'s.  VerifyParamType now has to check for typedefs if an
object fails a class check, or when checking non-Object types against
a non-primitive type name that isn't a class.
2013-04-09 13:01:46 -07:00

2452 linhas
90 KiB
Plaintext

%{
#include "parser.h"
#include <util/util.h>
#include <util/logger.h>
// macros for bison
#define YYSTYPE HPHP::HPHP_PARSER_NS::Token
#define YYSTYPE_IS_TRIVIAL 1
#define YYLTYPE HPHP::Location
#define YYLTYPE_IS_TRIVIAL 1
#define YYERROR_VERBOSE
#define YYINITDEPTH 500
#define YYLEX_PARAM _p
#ifdef yyerror
#undef yyerror
#endif
#define yyerror(loc,p,msg) p->fatal(loc,msg)
#ifdef YYLLOC_DEFAULT
# undef YYLLOC_DEFAULT
#endif
#define YYRHSLOC(Rhs, K) ((Rhs)[K])
#define YYLLOC_DEFAULT(Current, Rhs, N) \
do \
if (YYID (N)) { \
(Current).first(YYRHSLOC (Rhs, 1)); \
(Current).last (YYRHSLOC (Rhs, N)); \
} else { \
(Current).line0 = (Current).line1 = YYRHSLOC (Rhs, 0).line1; \
(Current).char0 = (Current).char1 = YYRHSLOC (Rhs, 0).char1; \
} \
while (YYID (0)); \
_p->setRuleLocation(&Current);
#define YYCOPY(To, From, Count) \
do { \
YYSIZE_T yyi; \
for (yyi = 0; yyi < (Count); yyi++) { \
(To)[yyi] = (From)[yyi]; \
} \
if (From != From ## a) { \
YYSTACK_FREE (From); \
} \
} \
while (YYID (0))
#define YYCOPY_RESET(To, From, Count) \
do \
{ \
YYSIZE_T yyi; \
for (yyi = 0; yyi < (Count); yyi++) { \
(To)[yyi] = (From)[yyi]; \
(From)[yyi].reset(); \
} \
if (From != From ## a) { \
YYSTACK_FREE (From); \
} \
} \
while (YYID (0))
#define YYTOKEN_RESET(From, Count) \
do \
{ \
YYSIZE_T yyi; \
for (yyi = 0; yyi < (Count); yyi++) { \
(From)[yyi].reset(); \
} \
if (From != From ## a) { \
YYSTACK_FREE (From); \
} \
} \
while (YYID (0))
# define YYSTACK_RELOCATE_RESET(Stack_alloc, Stack) \
do \
{ \
YYSIZE_T yynewbytes; \
YYCOPY_RESET (&yyptr->Stack_alloc, Stack, yysize); \
Stack = &yyptr->Stack_alloc; \
yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \
yyptr += yynewbytes / sizeof (*yyptr); \
} \
while (YYID (0))
#define YYSTACK_CLEANUP \
YYTOKEN_RESET (yyvs, yystacksize); \
if (yyvs != yyvsa) { \
YYSTACK_FREE (yyvs); \
} \
if (yyls != yylsa) { \
YYSTACK_FREE (yyls); \
} \
// macros for rules
#define BEXP(e...) _p->onBinaryOpExp(e);
#define UEXP(e...) _p->onUnaryOpExp(e);
using namespace HPHP::HPHP_PARSER_NS;
///////////////////////////////////////////////////////////////////////////////
// helpers
static void scalar_num(Parser *_p, Token &out, const char *num) {
Token t;
t.setText(num);
_p->onScalar(out, T_LNUMBER, t);
}
static void scalar_num(Parser *_p, Token &out, int num) {
Token t;
t.setText(boost::lexical_cast<std::string>(num));
_p->onScalar(out, T_LNUMBER, t);
}
static void scalar_null(Parser *_p, Token &out) {
Token tnull; tnull.setText("null");
_p->onConstantValue(out, tnull);
}
///////////////////////////////////////////////////////////////////////////////
// converting constant declartion to "define(name, value);"
static void on_constant(Parser *_p, Token &out, Token *stmts,
Token &name, Token &value) {
Token sname; _p->onScalar(sname, T_CONSTANT_ENCAPSED_STRING, name);
Token fname; fname.setText("define");
Token params1; _p->onCallParam(params1, NULL, sname, 0);
Token params2; _p->onCallParam(params2, &params1, value, 0);
Token call; _p->onCall(call, 0, fname, params2, 0);
Token scall; _p->onExpStatement(scall, call);
Token stmts0;
if (!stmts) {
_p->onStatementListStart(stmts0);
stmts = &stmts0;
}
_p->addStatement(out, *stmts, scall);
}
///////////////////////////////////////////////////////////////////////////////
// continuation transformations
void prepare_generator(Parser *_p, Token &stmt, Token &params) {
// 1. add prologue and epilogue to original body and store it back to "stmt"
{
// hphp_unpack_continuation()
Token empty;
Token cname; cname.setText("hphp_unpack_continuation");
Token unpack; _p->onCall(unpack, false, cname, empty, NULL, true);
Token sunpack; _p->onExpStatement(sunpack, unpack);
Token stmts0; _p->onStatementListStart(stmts0);
Token stmts1; _p->addStatement(stmts1, stmts0, sunpack);
Token stmts2; _p->addStatement(stmts2, stmts1, stmt);
stmt.reset();
_p->finishStatement(stmt, stmts2); stmt = 1;
}
// 2. prepare a single continuation parameter list and store it in "params"
{
Token type; type.setText("Continuation");
Token var; var.setText(CONTINUATION_OBJECT_NAME);
params.reset();
type.reset();
_p->onParam(params, NULL, type, var, false, NULL, NULL);
}
}
// create a generator function with original name and parameters
void create_generator(Parser *_p, Token &out, Token &params,
Token &name, const std::string &closureName,
const char *clsname, Token *modifiers, bool getArgs,
Token &origGenFunc, bool isHhvm, Token *attr) {
_p->pushFuncLocation();
if (clsname) {
_p->onMethodStart(name, *modifiers, false);
} else {
_p->onFunctionStart(name, false);
}
Token scont;
{
Token cname;
if (isHhvm) {
Token cn; cn.setText(clsname ? "__CLASS__" : "");
_p->onScalar(
cname,
clsname ? T_CLASS_C : T_CONSTANT_ENCAPSED_STRING,
cn);
} else {
Token cn; cn.setText(clsname ? clsname : "");
_p->onScalar(cname, T_CONSTANT_ENCAPSED_STRING, cn);
}
Token fn; fn.setText(closureName);
Token fname; _p->onScalar(fname, T_CONSTANT_ENCAPSED_STRING, fn);
Token ofn; ofn.setText(clsname ? "__METHOD__" : "__FUNCTION__");
Token oname; _p->onScalar(oname, clsname ? T_METHOD_C : T_FUNC_C, ofn);
Token param1; _p->onCallParam(param1, NULL, cname, false);
_p->onCallParam(param1, &param1, fname, false);
_p->onCallParam(param1, &param1, oname, false);
if (getArgs) {
Token cname; cname.setText("func_get_args");
Token empty;
Token call; _p->onCall(call, false, cname, empty, NULL);
_p->onCallParam(param1, &param1, call, false);
}
Token cname0; cname0.setText("hphp_create_continuation");
Token call; _p->onCall(call, false, cname0, param1, NULL, true);
Token ret; _p->onReturn(ret, &call);
Token stmts0; _p->onStatementListStart(stmts0);
Token stmts1; _p->addStatement(stmts1, stmts0, ret);
_p->finishStatement(scont, stmts1); scont = 1;
}
Token ret, ref;
ret.setText("Continuation");
ret.setCheck();
if (clsname) {
Token closure;
_p->onMethod(closure, *modifiers, ret, ref, name, params, scont, attr);
origGenFunc = closure;
Token stmts0; _p->onStatementListStart(stmts0);
Token stmts1; _p->addStatement(stmts1, stmts0, closure);
Token stmts2; _p->addStatement(stmts2, stmts1, out);
_p->finishStatement(out, stmts2); out = 1;
} else {
out.reset();
_p->onFunction(out, modifiers, ret, ref, name, params, scont, attr);
origGenFunc = out;
}
}
///////////////////////////////////////////////////////////////////////////////
static void user_attribute_check(Parser *_p) {
if (!_p->enableHipHopSyntax()) {
HPHP_PARSER_ERROR("User attributes are not enabled", _p);
}
}
static void finally_statement(Parser *_p) {
if (!_p->enableFinallyStatement()) {
HPHP_PARSER_ERROR("Finally statement is not enabled", _p);
}
}
static void constant_ae(Parser *_p, Token &out, Token &value) {
const std::string& valueStr = value.text();
if (valueStr.size() < 3 || valueStr.size() > 5 ||
(strcasecmp("true", valueStr.c_str()) != 0 &&
strcasecmp("false", valueStr.c_str()) != 0 &&
strcasecmp("null", valueStr.c_str()) != 0 &&
strcasecmp("inf", valueStr.c_str()) != 0 &&
strcasecmp("nan", valueStr.c_str()) != 0)) {
HPHP_PARSER_ERROR("User-defined constants are not allowed in user "
"attribute expressions", _p);
}
_p->onConstantValue(out, value);
}
///////////////////////////////////////////////////////////////////////////////
/**
* XHP functions: They are defined here, so different parsers don't have to
* handle XHP rules at all.
*/
static void xhp_tag(Parser *_p, Token &out, Token &label, Token &body) {
if (!_p->enableXHP()) {
HPHP_PARSER_ERROR("XHP: not enabled", _p);
}
if (!body.text().empty() && body.text() != label.text()) {
HPHP_PARSER_ERROR("XHP: mismatched tag: '%s' not the same as '%s'",
_p, body.text().c_str(), label.text().c_str());
}
label.xhpLabel();
Token name; _p->onName(name, label, Parser::StringName);
_p->onNewObject(out, name, body);
}
static void xhp_attribute(Parser *_p, Token &out, Token &type, Token &label,
Token &def, Token &req) {
/**
* The basic builtin types "bool", "int", "double", and "string" all map to
* T_STRING in the parser, and the parser uses always uses type code 5 for
* T_STRING. However, XHP uses different type codes for these basic builtin
* types, so we need to fix up the type code here to make XHP happy.
*/
if (type.num() == 5 && type.text().size() >= 3 && type.text().size() <= 7) {
switch (type.text()[0]) {
case 'b':
if ((type.text().size() == 4 &&
strcasecmp(type.text().c_str(), "bool") == 0) ||
(type.text().size() == 7 &&
strcasecmp(type.text().c_str(), "boolean") == 0)) {
type.reset();
type.setNum(2);
}
break;
case 'd':
if (type.text().size() == 6 &&
strcasecmp(type.text().c_str(), "double") == 0) {
type.reset();
type.setNum(8);
}
break;
case 'f':
if (type.text().size() == 5 &&
strcasecmp(type.text().c_str(), "float") == 0) {
type.reset();
type.setNum(8);
}
break;
case 'i':
if ((type.text().size() == 3 &&
strcasecmp(type.text().c_str(), "int") == 0) ||
(type.text().size() == 7 &&
strcasecmp(type.text().c_str(), "integer") == 0)) {
type.reset();
type.setNum(3);
}
break;
case 'r':
if (type.text().size() == 4 &&
strcasecmp(type.text().c_str(), "real") == 0) {
type.reset();
type.setNum(8);
}
break;
case 's':
if (type.text().size() == 6 &&
strcasecmp(type.text().c_str(), "string") == 0) {
type.reset();
type.setNum(1);
}
break;
default:
break;
}
}
Token num; scalar_num(_p, num, type.num());
Token arr1; _p->onArrayPair(arr1, 0, 0, num, 0);
Token arr2;
switch (type.num()) {
case 5: /* class */ {
Token cls; _p->onScalar(cls, T_CONSTANT_ENCAPSED_STRING, type);
_p->onArrayPair(arr2, &arr1, 0, cls, 0);
break;
}
case 7: /* enum */ {
Token arr; _p->onArray(arr, type);
_p->onArrayPair(arr2, &arr1, 0, arr, 0);
break;
}
default: {
Token tnull; scalar_null(_p, tnull);
_p->onArrayPair(arr2, &arr1, 0, tnull, 0);
break;
}
}
Token arr3; _p->onArrayPair(arr3, &arr2, 0, def, 0);
Token arr4; _p->onArrayPair(arr4, &arr3, 0, req, 0);
_p->onArray(out, arr4);
out.setText(label);
}
static void xhp_attribute_list(Parser *_p, Token &out, Token *list,
Token &decl) {
if (decl.num() == 0) {
decl.xhpLabel();
if (list) {
out = *list;
out.setText(list->text() + ":" + decl.text()); // avoiding vector<string>
} else {
out.setText(decl);
}
} else {
Token name; _p->onScalar(name, T_CONSTANT_ENCAPSED_STRING, decl);
_p->onArrayPair(out, list, &name, decl, 0);
if (list) {
out.setText(list->text());
} else {
out.setText("");
}
}
}
static void xhp_attribute_stmt(Parser *_p, Token &out, Token &attributes) {
if (!_p->enableXHP()) {
HPHP_PARSER_ERROR("XHP: not enabled", _p);
}
Token modifiers;
Token fname; fname.setText("__xhpAttributeDeclaration");
{
Token m;
Token m1; m1.setNum(T_PROTECTED); _p->onMemberModifier(m, NULL, m1);
Token m2; m2.setNum(T_STATIC); _p->onMemberModifier(modifiers, &m, m2);
}
_p->pushFuncLocation();
_p->onMethodStart(fname, modifiers);
std::vector<std::string> classes;
HPHP::Util::split(':', attributes.text().c_str(), classes, true);
Token arrAttributes; _p->onArray(arrAttributes, attributes);
Token dummy;
Token stmts0;
{
_p->onStatementListStart(stmts0);
}
Token stmts1;
{
// static $_ = -1;
Token one; scalar_num(_p, one, "1");
Token mone; UEXP(mone, one, '-', 1);
Token var; var.set(T_VARIABLE, "_");
Token decl; _p->onStaticVariable(decl, 0, var, &mone);
Token sdecl; _p->onStatic(sdecl, decl);
_p->addStatement(stmts1, stmts0, sdecl);
}
Token stmts2;
{
// if ($_ === -1) {
// $_ = array_merge(parent::__xhpAttributeDeclaration(),
// attributes);
// }
Token parent; parent.set(T_STRING, "parent");
Token cls; _p->onName(cls, parent, Parser::StringName);
Token fname; fname.setText("__xhpAttributeDeclaration");
Token param1; _p->onCall(param1, 0, fname, dummy, &cls);
Token params1; _p->onCallParam(params1, NULL, param1, 0);
for (unsigned int i = 0; i < classes.size(); i++) {
Token parent; parent.set(T_STRING, classes[i]);
Token cls; _p->onName(cls, parent, Parser::StringName);
Token fname; fname.setText("__xhpAttributeDeclaration");
Token param; _p->onCall(param, 0, fname, dummy, &cls);
Token params; _p->onCallParam(params, &params1, param, 0);
params1 = params;
}
Token params2; _p->onCallParam(params2, &params1, arrAttributes, 0);
Token name; name.set(T_STRING, "array_merge");
Token call; _p->onCall(call, 0, name, params2, NULL);
Token tvar; tvar.set(T_VARIABLE, "_");
Token var; _p->onSimpleVariable(var, tvar);
Token assign; _p->onAssign(assign, var, call, 0);
Token exp; _p->onExpStatement(exp, assign);
Token block; _p->onBlock(block, exp);
Token tvar2; tvar2.set(T_VARIABLE, "_");
Token var2; _p->onSimpleVariable(var2, tvar2);
Token one; scalar_num(_p, one, "1");
Token mone; UEXP(mone, one, '-', 1);
Token cond; BEXP(cond, var2, mone, T_IS_IDENTICAL);
Token dummy1, dummy2;
Token sif; _p->onIf(sif, cond, block, dummy1, dummy2);
_p->addStatement(stmts2, stmts1, sif);
}
Token stmts3;
{
// return $_;
Token tvar; tvar.set(T_VARIABLE, "_");
Token var; _p->onSimpleVariable(var, tvar);
Token ret; _p->onReturn(ret, &var);
_p->addStatement(stmts3, stmts2, ret);
}
Token stmt;
{
_p->finishStatement(stmt, stmts3);
stmt = 1;
}
{
Token params, ret, ref; ref = 1;
_p->onMethod(out, modifiers, ret, ref, fname, params, stmt, 0, false);
}
}
static void xhp_collect_attributes(Parser *_p, Token &out, Token &stmts) {
Token *attr = _p->xhpGetAttributes();
if (attr) {
Token stmt;
xhp_attribute_stmt(_p, stmt, *attr);
_p->onClassStatement(out, stmts, stmt);
} else {
out = stmts;
}
}
static void xhp_category_stmt(Parser *_p, Token &out, Token &categories) {
if (!_p->enableXHP()) {
HPHP_PARSER_ERROR("XHP: not enabled", _p);
}
Token fname; fname.setText("__xhpCategoryDeclaration");
Token m1; m1.setNum(T_PROTECTED);
Token modifiers; _p->onMemberModifier(modifiers, 0, m1);
_p->pushFuncLocation();
_p->onMethodStart(fname, modifiers);
Token stmts0;
{
_p->onStatementListStart(stmts0);
}
Token stmts1;
{
// static $_ = categories;
Token arr; _p->onArray(arr, categories);
Token var; var.set(T_VARIABLE, "_");
Token decl; _p->onStaticVariable(decl, 0, var, &arr);
Token sdecl; _p->onStatic(sdecl, decl);
_p->addStatement(stmts1, stmts0, sdecl);
}
Token stmts2;
{
// return $_;
Token tvar; tvar.set(T_VARIABLE, "_");
Token var; _p->onSimpleVariable(var, tvar);
Token ret; _p->onReturn(ret, &var);
_p->addStatement(stmts2, stmts1, ret);
}
Token stmt;
{
_p->finishStatement(stmt, stmts2);
stmt = 1;
}
{
Token params, ret, ref; ref = 1;
_p->onMethod(out, modifiers, ret, ref, fname, params, stmt, 0, false);
}
}
static void xhp_children_decl_tag(Parser *_p, Token &arr, Token &tag) {
Token num; scalar_num(_p, num, tag.num());
Token arr1; _p->onArrayPair(arr1, &arr, 0, num, 0);
Token name;
if (tag.num() == 3 || tag.num() == 4) {
_p->onScalar(name, T_CONSTANT_ENCAPSED_STRING, tag);
} else if (tag.num() >= 0) {
scalar_null(_p, name);
} else {
HPHP_PARSER_ERROR("XHP: unknown children declaration", _p);
}
Token arr2; _p->onArrayPair(arr2, &arr1, 0, name, 0);
arr = arr2;
}
static void xhp_children_decl(Parser *_p, Token &out, Token &op1, int op,
Token *op2) {
Token num; scalar_num(_p, num, op);
Token arr; _p->onArrayPair(arr, 0, 0, num, 0);
if (op2) {
Token arr1; _p->onArrayPair(arr1, &arr, 0, op1, 0);
Token arr2; _p->onArrayPair(arr2, &arr1, 0, *op2, 0);
_p->onArray(out, arr2);
} else {
xhp_children_decl_tag(_p, arr, op1);
_p->onArray(out, arr);
}
}
static void xhp_children_paren(Parser *_p, Token &out, Token exp, int op) {
Token num; scalar_num(_p, num, op);
Token arr1; _p->onArrayPair(arr1, 0, 0, num, 0);
Token num5; scalar_num(_p, num5, 5);
Token arr2; _p->onArrayPair(arr2, &arr1, 0, num5, 0);
Token arr3; _p->onArrayPair(arr3, &arr2, 0, exp, 0);
_p->onArray(out, arr3);
}
static void xhp_children_stmt(Parser *_p, Token &out, Token &children) {
if (!_p->enableXHP()) {
HPHP_PARSER_ERROR("XHP: not enabled", _p);
}
Token fname; fname.setText("__xhpChildrenDeclaration");
Token m1; m1.setNum(T_PROTECTED);
Token modifiers; _p->onMemberModifier(modifiers, 0, m1);
_p->pushFuncLocation();
_p->onMethodStart(fname, modifiers);
Token stmts0;
{
_p->onStatementListStart(stmts0);
}
Token stmts1;
{
// static $_ = children;
Token arr;
if (children.num() == 2) {
arr = children;
} else if (children.num() >= 0) {
scalar_num(_p, arr, children.num());
} else {
HPHP_PARSER_ERROR("XHP: XHP unknown children declaration", _p);
}
Token var; var.set(T_VARIABLE, "_");
Token decl; _p->onStaticVariable(decl, 0, var, &arr);
Token sdecl; _p->onStatic(sdecl, decl);
_p->addStatement(stmts1, stmts0, sdecl);
}
Token stmts2;
{
// return $_;
Token tvar; tvar.set(T_VARIABLE, "_");
Token var; _p->onSimpleVariable(var, tvar);
Token ret; _p->onReturn(ret, &var);
_p->addStatement(stmts2, stmts1, ret);
}
Token stmt;
{
_p->finishStatement(stmt, stmts2);
stmt = 1;
}
{
Token params, ret, ref; ref = 1;
_p->onMethod(out, modifiers, ret, ref, fname, params, stmt, 0, false);
}
}
/* This is called from strict-mode productions (sm_*) to throw an
* error if we're not in strict mode */
static void only_in_strict_mode(Parser *_p) {
if (!_p->scanner().isStrictMode()) {
HPHP_PARSER_ERROR("Syntax only allowed in strict mode", _p);
}
}
static void only_in_hphp_syntax(Parser *_p) {
if (!_p->enableHipHopSyntax()) {
HPHP_PARSER_ERROR("Syntax only allowed with -v Eval.EnableHipHopSyntax=true", _p);
}
}
// Shapes may not have leading integers in key names, considered as a
// parse time error. This is because at runtime they are currently
// hphp arrays, which will treat leading integer keys as numbers.
static void validate_shape_keyname(Token& tok, Parser* _p) {
if (tok.text().empty()) {
HPHP_PARSER_ERROR("Shape key names may not be empty", _p);
}
if (isdigit(tok.text()[0])) {
HPHP_PARSER_ERROR("Shape key names may not start with integers", _p);
}
}
///////////////////////////////////////////////////////////////////////////////
static int yylex(YYSTYPE *token, HPHP::Location *loc, Parser *_p) {
return _p->scan(token, loc);
}
%}
%expect 2
%define api.pure
%parse-param {HPHP::HPHP_PARSER_NS::Parser *_p}
%left T_INCLUDE T_INCLUDE_ONCE T_EVAL T_REQUIRE T_REQUIRE_ONCE
%left ','
%left T_LOGICAL_OR
%left T_LOGICAL_XOR
%left T_LOGICAL_AND
%right T_PRINT
%left '=' T_PLUS_EQUAL T_MINUS_EQUAL T_MUL_EQUAL T_DIV_EQUAL T_CONCAT_EQUAL T_MOD_EQUAL T_AND_EQUAL T_OR_EQUAL T_XOR_EQUAL T_SL_EQUAL T_SR_EQUAL
%left '?' ':'
%left T_BOOLEAN_OR
%left T_BOOLEAN_AND
%left '|'
%left '^'
%left '&'
%nonassoc T_IS_EQUAL T_IS_NOT_EQUAL T_IS_IDENTICAL T_IS_NOT_IDENTICAL
%nonassoc '<' T_IS_SMALLER_OR_EQUAL '>' T_IS_GREATER_OR_EQUAL
%left T_SL T_SR
%left '+' '-' '.'
%left '*' '/' '%'
%right '!'
%nonassoc T_INSTANCEOF
%right '~' T_INC T_DEC T_INT_CAST T_DOUBLE_CAST T_STRING_CAST T_ARRAY_CAST T_OBJECT_CAST T_BOOL_CAST T_UNSET_CAST '@'
%right '['
%nonassoc T_NEW T_CLONE
%token T_EXIT
%token T_IF
%left T_ELSEIF
%left T_ELSE
%left T_ENDIF
%token T_LNUMBER
%token T_DNUMBER
%token T_STRING
%token T_STRING_VARNAME
%token T_VARIABLE
%token T_NUM_STRING
%token T_INLINE_HTML
%token T_CHARACTER
%token T_BAD_CHARACTER
%token T_ENCAPSED_AND_WHITESPACE
%token T_CONSTANT_ENCAPSED_STRING
%token T_ECHO
%token T_DO
%token T_WHILE
%token T_ENDWHILE
%token T_FOR
%token T_ENDFOR
%token T_FOREACH
%token T_ENDFOREACH
%token T_DECLARE
%token T_ENDDECLARE
%token T_AS
%token T_SWITCH
%token T_ENDSWITCH
%token T_CASE
%token T_DEFAULT
%token T_BREAK
%token T_GOTO
%token T_CONTINUE
%token T_FUNCTION
%token T_CONST
%token T_RETURN
%token T_TRY
%token T_CATCH
%token T_THROW
%token T_USE
%token T_GLOBAL
%right T_STATIC T_ABSTRACT T_FINAL T_PRIVATE T_PROTECTED T_PUBLIC
%token T_VAR
%token T_UNSET
%token T_ISSET
%token T_EMPTY
%token T_HALT_COMPILER
%token T_CLASS
%token T_INTERFACE
%token T_EXTENDS
%token T_IMPLEMENTS
%token T_OBJECT_OPERATOR
%token T_DOUBLE_ARROW
%token T_LIST
%token T_ARRAY
%token T_CLASS_C
%token T_METHOD_C
%token T_FUNC_C
%token T_LINE
%token T_FILE
%token T_COMMENT
%token T_DOC_COMMENT
%token T_OPEN_TAG
%token T_OPEN_TAG_WITH_ECHO
%token T_CLOSE_TAG
%token T_WHITESPACE
%token T_START_HEREDOC
%token T_END_HEREDOC
%token T_DOLLAR_OPEN_CURLY_BRACES
%token T_CURLY_OPEN
%token T_PAAMAYIM_NEKUDOTAYIM
%token T_NAMESPACE
%token T_NS_C
%token T_DIR
%token T_NS_SEPARATOR
%token T_YIELD
%token T_XHP_LABEL
%token T_XHP_TEXT
%token T_XHP_ATTRIBUTE
%token T_XHP_CATEGORY
%token T_XHP_CATEGORY_LABEL
%token T_XHP_CHILDREN
%token T_XHP_ENUM
%token T_XHP_REQUIRED
%token T_TRAIT
%token T_INSTEADOF
%token T_TRAIT_C
%token T_VARARG
%token T_STRICT_ERROR
%token T_FINALLY
%token T_XHP_TAG_LT
%token T_XHP_TAG_GT
%token T_TYPELIST_LT
%token T_TYPELIST_GT
%token T_UNRESOLVED_LT
%token T_COLLECTION
%token T_SHAPE
%token T_TYPE
%token T_UNRESOLVED_TYPE
%%
start:
top_statement_list { _p->popLabelInfo();
_p->saveParseTree($$);}
;
top_statement_list:
top_statement_list
top_statement { _p->addStatement($$,$1,$2);}
| { _p->onStatementListStart($$);}
;
top_statement:
statement { _p->nns($1.num() == T_DECLARE);
$$ = $1;}
| function_declaration_statement { _p->nns(); $$ = $1;}
| class_declaration_statement { _p->nns(); $$ = $1;}
| trait_declaration_statement { _p->nns(); $$ = $1;}
| sm_typedef_statement { $$ = $1; }
| T_HALT_COMPILER '(' ')' ';' { $$.reset();}
| T_NAMESPACE namespace_name ';' { _p->onNamespaceStart($2.text());
$$.reset();}
| T_NAMESPACE namespace_name '{' { _p->onNamespaceStart($2.text());}
top_statement_list '}' { _p->onNamespaceEnd(); $$ = $5;}
| T_NAMESPACE '{' { _p->onNamespaceStart("");}
top_statement_list '}' { _p->onNamespaceEnd(); $$ = $4;}
| T_USE use_declarations ';' { _p->nns(); $$.reset();}
| constant_declaration ';' { _p->nns();
_p->finishStatement($$, $1); $$ = 1;}
;
ident:
T_STRING { $$ = $1;}
| T_XHP_ATTRIBUTE { $$ = $1;}
| T_XHP_CATEGORY { $$ = $1;}
| T_XHP_CHILDREN { $$ = $1;}
| T_XHP_REQUIRED { $$ = $1;}
| T_XHP_ENUM { $$ = $1;}
;
use_declarations:
use_declarations ','
use_declaration { }
| use_declaration { }
;
use_declaration:
namespace_name { _p->onUse($1.text(),"");}
| T_NS_SEPARATOR namespace_name { _p->onUse($2.text(),"");}
| namespace_name T_AS ident { _p->onUse($1.text(),$3.text());}
| T_NS_SEPARATOR namespace_name
T_AS ident { _p->onUse($2.text(),$4.text());}
;
namespace_name:
ident { $$ = $1;}
| namespace_name T_NS_SEPARATOR
ident { $$ = $1 + $2 + $3;}
;
namespace_string_base:
namespace_name { $$ = $1; $$ = 1;}
| T_NS_SEPARATOR namespace_name { $$ = $2; $$ = 0;}
| T_NAMESPACE T_NS_SEPARATOR
namespace_name { $$.setText(_p->nsDecl($3.text()));
$$ = 0;}
;
namespace_string:
namespace_string_base { if ($1.num())
$1.setText(_p->resolve($1.text(),0));
$$ = $1;}
;
namespace_string_typeargs:
namespace_string_base
sm_typeargs_opt { if ($1.num())
$1.setText(_p->resolve($1.text(),0));
$$ = $1;}
;
class_namespace_string_typeargs:
namespace_string_base
sm_typeargs_opt { if ($1.num())
$1.setText(_p->resolve($1.text(),1));
$$ = $1;}
;
constant_declaration:
constant_declaration ','
sm_name_with_type '=' static_scalar { $3.setText(_p->nsDecl($3.text()));
on_constant(_p,$$,&$1,$3,$5);}
| T_CONST sm_name_with_type '=' static_scalar { $2.setText(_p->nsDecl($2.text()));
on_constant(_p,$$, 0,$2,$4);}
;
inner_statement_list:
inner_statement_list
inner_statement { _p->addStatement($$,$1,$2);}
| { _p->onStatementListStart($$);}
;
inner_statement:
statement { $$ = $1;}
| function_declaration_statement { $$ = $1;}
| class_declaration_statement { $$ = $1;}
| trait_declaration_statement { $$ = $1;}
;
statement:
'{' inner_statement_list '}' { _p->onBlock($$, $2);}
| T_IF parenthesis_expr
statement
elseif_list
else_single { _p->onIf($$,$2,$3,$4,$5);}
| T_IF parenthesis_expr ':'
inner_statement_list
new_elseif_list
new_else_single
T_ENDIF ';' { _p->onIf($$,$2,$4,$5,$6);}
| T_WHILE parenthesis_expr { _p->pushLabelScope();}
while_statement { _p->popLabelScope();
_p->onWhile($$,$2,$4);}
| T_DO { _p->pushLabelScope();}
statement T_WHILE parenthesis_expr
';' { _p->popLabelScope();
_p->onDo($$,$3,$5);}
| T_FOR '(' for_expr ';'
for_expr ';' for_expr ')' { _p->pushLabelScope();}
for_statement { _p->popLabelScope();
_p->onFor($$,$3,$5,$7,$10);}
| T_SWITCH parenthesis_expr { _p->pushLabelScope();}
switch_case_list { _p->popLabelScope();
_p->onSwitch($$,$2,$4);}
| T_BREAK ';' { _p->onBreak($$, NULL);}
| T_BREAK expr ';' { _p->onBreak($$, &$2);}
| T_CONTINUE ';' { _p->onContinue($$, NULL);}
| T_CONTINUE expr ';' { _p->onContinue($$, &$2);}
| T_RETURN ';' { _p->onReturn($$, NULL);}
| T_RETURN expr ';' { _p->onReturn($$, &$2);}
| T_YIELD T_BREAK ';' { _p->onYieldBreak($$);}
| T_GLOBAL global_var_list ';' { _p->onGlobal($$, $2);}
| T_STATIC static_var_list ';' { _p->onStatic($$, $2);}
| T_ECHO expr_list ';' { _p->onEcho($$, $2, 0);}
| T_UNSET '(' variable_list ')' ';' { _p->onUnset($$, $3);}
| ';' { $$.reset();}
| T_INLINE_HTML { _p->onEcho($$, $1, 1);}
| T_FOREACH '(' expr
T_AS foreach_variable
foreach_optional_arg ')' { _p->pushLabelScope();}
foreach_statement { _p->popLabelScope();
_p->onForEach($$,$3,$5,$6,$9);}
| T_DECLARE '(' declare_list ')'
declare_statement { _p->onBlock($$, $5); $$ = T_DECLARE;}
| T_TRY '{'
inner_statement_list '}'
T_CATCH '('
fully_qualified_class_name
T_VARIABLE ')' '{'
inner_statement_list '}'
additional_catches
optional_finally { _p->onTry($$,$3,$7,$8,$11,$13,$14);}
| T_TRY '{'
inner_statement_list '}'
finally { _p->onTry($$, $3, $5);}
| T_THROW expr ';' { _p->onThrow($$, $2);}
| T_GOTO ident ';' { _p->onGoto($$, $2, true);
_p->addGoto($2.text(),
_p->getLocation(),
&$$); }
| expr ';' { _p->onExpStatement($$, $1);}
| yield_expr ';' { _p->onExpStatement($$, $1);}
| yield_assign_expr ';' { _p->onExpStatement($$, $1);}
| yield_list_assign_expr ';' { _p->onExpStatement($$, $1);}
| ident ':' { _p->onLabel($$, $1);
_p->addLabel($1.text(),
_p->getLocation(),
&$$); }
;
additional_catches:
additional_catches
T_CATCH '('
fully_qualified_class_name
T_VARIABLE ')'
'{'
inner_statement_list '}' { _p->onCatch($$, $1, $4, $5, $8);}
| { $$.reset();}
;
finally:
{ finally_statement(_p);}
T_FINALLY '{'
inner_statement_list '}' { _p->onFinally($$, $4);}
;
optional_finally:
finally
| { $$.reset();}
;
is_reference:
'&' { $$ = 1;}
| { $$.reset();}
;
function_loc:
T_FUNCTION { _p->pushFuncLocation();}
;
function_declaration_statement:
function_loc
is_reference sm_name_with_typevar { $3.setText(_p->nsDecl($3.text()));
_p->onFunctionStart($3);
_p->pushLabelInfo();}
'(' parameter_list ')'
sm_opt_return_type
'{' inner_statement_list '}' { Token t; t.reset();
_p->onFunction($$,0,t,$2,$3,$6,$10,0);
_p->popLabelInfo();
_p->popTypeScope();}
| non_empty_user_attributes function_loc
is_reference sm_name_with_typevar { $4.setText(_p->nsDecl($4.text()));
_p->onFunctionStart($4);
_p->pushLabelInfo();}
'(' parameter_list ')'
sm_opt_return_type
'{' inner_statement_list '}' { Token t; t.reset();
_p->onFunction($$,0,t,$3,$4,$7,$11,&$1);
_p->popLabelInfo();
_p->popTypeScope();}
;
class_declaration_statement:
class_entry_type
class_decl_name { $2.setText(_p->nsDecl($2.text()));
_p->onClassStart($1.num(),$2);}
extends_from implements_list '{'
class_statement_list '}' { Token stmts;
if (_p->peekClass()) {
xhp_collect_attributes(_p,stmts,$7);
} else {
stmts = $7;
}
_p->onClass($$,$1.num(),$2,$4,$5,
stmts,0);
if (_p->peekClass()) {
_p->xhpResetAttributes();
}
_p->popClass();
_p->popTypeScope();}
| non_empty_user_attributes
class_entry_type
class_decl_name { $3.setText(_p->nsDecl($3.text()));
_p->onClassStart($2.num(),$3);}
extends_from implements_list '{'
class_statement_list '}' { Token stmts;
if (_p->peekClass()) {
xhp_collect_attributes(_p,stmts,$8);
} else {
stmts = $8;
}
_p->onClass($$,$2.num(),$3,$5,$6,
stmts,&$1);
if (_p->peekClass()) {
_p->xhpResetAttributes();
}
_p->popClass();
_p->popTypeScope();}
| T_INTERFACE
interface_decl_name { $2.setText(_p->nsDecl($2.text()));
_p->onClassStart(T_INTERFACE,$2);}
interface_extends_list '{'
class_statement_list '}' { _p->onInterface($$,$2,$4,$6,0);
_p->popClass();
_p->popTypeScope();}
| non_empty_user_attributes
T_INTERFACE
interface_decl_name { $3.setText(_p->nsDecl($3.text()));
_p->onClassStart(T_INTERFACE,$3);}
interface_extends_list '{'
class_statement_list '}' { _p->onInterface($$,$3,$5,$7,&$1);
_p->popClass();
_p->popTypeScope();}
;
trait_declaration_statement:
T_TRAIT
trait_decl_name { $2.setText(_p->nsDecl($2.text()));
_p->onClassStart(T_TRAIT, $2);}
'{' class_statement_list '}' { Token t_ext, t_imp;
t_ext.reset(); t_imp.reset();
_p->onClass($$,T_TRAIT,$2,t_ext,t_imp,
$5, 0);
_p->popClass();
_p->popTypeScope();}
| non_empty_user_attributes
T_TRAIT
trait_decl_name { $3.setText(_p->nsDecl($3.text()));
_p->onClassStart(T_TRAIT, $3);}
'{' class_statement_list '}' { Token t_ext, t_imp;
t_ext.reset(); t_imp.reset();
_p->onClass($$,T_TRAIT,$3,t_ext,t_imp,
$6, &$1);
_p->popClass();
_p->popTypeScope();}
;
class_decl_name:
sm_name_with_typevar { _p->pushClass(false); $$ = $1;}
| T_XHP_LABEL { $1.xhpLabel(); _p->pushTypeScope();
_p->pushClass(true); $$ = $1;}
;
interface_decl_name:
sm_name_with_typevar { _p->pushClass(false); $$ = $1;}
;
trait_decl_name:
sm_name_with_typevar { _p->pushClass(false); $$ = $1;}
;
class_entry_type:
T_CLASS { $$ = T_CLASS;}
| T_ABSTRACT T_CLASS { $$ = T_ABSTRACT;}
| T_FINAL T_CLASS { $$ = T_FINAL;}
;
extends_from:
T_EXTENDS
fully_qualified_class_name { $$ = $2;}
| { $$.reset();}
;
implements_list:
T_IMPLEMENTS interface_list { $$ = $2;}
| { $$.reset();}
;
interface_extends_list:
T_EXTENDS interface_list { $$ = $2;}
| { $$.reset();}
;
interface_list:
fully_qualified_class_name { _p->onInterfaceName($$, NULL, $1);}
| interface_list ','
fully_qualified_class_name { _p->onInterfaceName($$, &$1, $3);}
;
trait_list:
fully_qualified_class_name { _p->onTraitName($$, NULL, $1);}
| trait_list ','
fully_qualified_class_name { _p->onTraitName($$, &$1, $3);}
;
foreach_optional_arg:
T_DOUBLE_ARROW foreach_variable { $$ = $2;}
| { $$.reset();}
;
foreach_variable:
variable { $$ = $1;}
| '&' variable { $$ = $2; $$ = 1;}
;
for_statement:
statement { $$ = $1;}
| ':' inner_statement_list
T_ENDFOR ';' { $$ = $2;}
;
foreach_statement:
statement { $$ = $1;}
| ':' inner_statement_list
T_ENDFOREACH ';' { $$ = $2;}
;
while_statement:
statement { $$ = $1;}
| ':' inner_statement_list
T_ENDWHILE ';' { $$ = $2;}
;
declare_statement:
statement { $$ = $1;}
| ':' inner_statement_list
T_ENDDECLARE ';' { $$ = $2;}
;
declare_list:
ident '=' static_scalar
| declare_list ','
ident '=' static_scalar
;
switch_case_list:
'{' case_list '}' { $$ = $2;}
| '{' ';' case_list '}' { $$ = $3;}
| ':' case_list T_ENDSWITCH ';' { $$ = $2;}
| ':' ';' case_list T_ENDSWITCH ';' { $$ = $3;}
;
case_list:
case_list T_CASE expr
case_separator
inner_statement_list { _p->onCase($$,$1,&$3,$5);}
| case_list T_DEFAULT case_separator
inner_statement_list { _p->onCase($$,$1,NULL,$4);}
| { $$.reset();}
;
case_separator:
':' { $$.reset();}
| ';' { $$.reset();}
;
elseif_list:
elseif_list T_ELSEIF parenthesis_expr
statement { _p->onElseIf($$,$1,$3,$4);}
| { $$.reset();}
;
new_elseif_list:
new_elseif_list T_ELSEIF
parenthesis_expr ':'
inner_statement_list { _p->onElseIf($$,$1,$3,$5);}
| { $$.reset();}
;
else_single:
T_ELSE statement { $$ = $2;}
| { $$.reset();}
;
new_else_single:
T_ELSE ':' inner_statement_list { $$ = $3;}
| { $$.reset();}
;
parameter_list:
non_empty_parameter_list ',' T_VARARG
{ only_in_strict_mode(_p); $$ = $1; }
| non_empty_parameter_list
possible_comma_in_hphp_syntax { $$ = $1;}
| T_VARARG { only_in_strict_mode(_p); $$.reset(); }
| { $$.reset();}
;
non_empty_parameter_list:
optional_user_attributes
sm_type_opt T_VARIABLE { _p->onParam($$,NULL,$2,$3,0,NULL,&$1);}
| optional_user_attributes
sm_type_opt '&' T_VARIABLE { _p->onParam($$,NULL,$2,$4,1,NULL,&$1);}
| optional_user_attributes
sm_type_opt '&' T_VARIABLE
'=' static_scalar { _p->onParam($$,NULL,$2,$4,1,&$6,&$1);}
| optional_user_attributes
sm_type_opt T_VARIABLE
'=' static_scalar { _p->onParam($$,NULL,$2,$3,0,&$5,&$1);}
| non_empty_parameter_list ','
optional_user_attributes
sm_type_opt T_VARIABLE { _p->onParam($$,&$1,$4,$5,0,NULL,&$3);}
| non_empty_parameter_list ','
optional_user_attributes
sm_type_opt '&' T_VARIABLE { _p->onParam($$,&$1,$4,$6,1,NULL,&$3);}
| non_empty_parameter_list ','
optional_user_attributes
sm_type_opt '&' T_VARIABLE
'=' static_scalar { _p->onParam($$,&$1,$4,$6,1,&$8,&$3);}
| non_empty_parameter_list ','
optional_user_attributes
sm_type_opt T_VARIABLE
'=' static_scalar { _p->onParam($$,&$1,$4,$5,0,&$7,&$3);}
;
function_call_parameter_list:
non_empty_fcall_parameter_list
possible_comma_in_hphp_syntax { $$ = $1;}
| { $$.reset();}
;
non_empty_fcall_parameter_list:
expr { _p->onCallParam($$,NULL,$1,0);}
| '&' variable { _p->onCallParam($$,NULL,$2,1);}
| non_empty_fcall_parameter_list ','
expr { _p->onCallParam($$,&$1,$3,0);}
| non_empty_fcall_parameter_list ','
'&' variable { _p->onCallParam($$,&$1,$4,1);}
;
global_var_list:
global_var_list ',' global_var { _p->onGlobalVar($$, &$1, $3);}
| global_var { _p->onGlobalVar($$, NULL, $1);}
;
global_var:
T_VARIABLE { $$ = $1;}
| '$' variable { $$ = $2; $$ = 1;}
| '$' '{' expr '}' { $$ = $3; $$ = 1;}
;
static_var_list:
static_var_list ',' T_VARIABLE { _p->onStaticVariable($$,&$1,$3,0);}
| static_var_list ',' T_VARIABLE
'=' static_scalar { _p->onStaticVariable($$,&$1,$3,&$5);}
| T_VARIABLE { _p->onStaticVariable($$,0,$1,0);}
| T_VARIABLE '=' static_scalar { _p->onStaticVariable($$,0,$1,&$3);}
;
class_statement_list:
class_statement_list
class_statement { _p->onClassStatement($$, $1, $2);}
| { $$.reset();}
;
class_statement:
variable_modifiers { _p->onClassVariableModifer($1);}
class_variable_declaration ';' { _p->onClassVariableStart
($$,&$1,$3,NULL);}
| non_empty_member_modifiers
sm_type { _p->onClassVariableModifer($1);}
class_variable_declaration ';' { _p->onClassVariableStart
($$,&$1,$4,&$2);}
| class_constant_declaration ';' { _p->onClassVariableStart
($$,NULL,$1,NULL);}
| method_modifiers function_loc
is_reference sm_name_with_typevar '('
{ _p->onMethodStart($4, $1);
_p->pushLabelInfo();}
parameter_list ')'
sm_opt_return_type
method_body
{ Token t; t.reset();
_p->onMethod($$,$1,t,$3,$4,$7,$10,0);
_p->popLabelInfo();
_p->popTypeScope();}
| non_empty_user_attributes
method_modifiers function_loc
is_reference sm_name_with_typevar '('
{ _p->onMethodStart($5, $2);
_p->pushLabelInfo();}
parameter_list ')'
sm_opt_return_type
method_body
{ Token t; t.reset();
_p->onMethod($$,$2,t,$4,$5,$8,$11,&$1);
_p->popLabelInfo();
_p->popTypeScope();}
| T_XHP_ATTRIBUTE
xhp_attribute_stmt ';' { _p->xhpSetAttributes($2);}
| T_XHP_CATEGORY
xhp_category_stmt ';' { xhp_category_stmt(_p,$$,$2);}
| T_XHP_CHILDREN
xhp_children_stmt ';' { xhp_children_stmt(_p,$$,$2);}
| T_USE trait_list ';' { Token t; t.reset();
_p->onTraitUse($$,$2,t); }
| T_USE trait_list '{'
trait_rules '}' { _p->onTraitUse($$,$2,$4); }
;
trait_rules:
trait_rules trait_precedence_rule { _p->onTraitRule($$,$1,$2); }
| trait_rules trait_alias_rule { _p->onTraitRule($$,$1,$2); }
| /* empty */ { $$.reset(); }
;
trait_precedence_rule:
class_namespace_string_typeargs
T_PAAMAYIM_NEKUDOTAYIM
ident
T_INSTEADOF trait_list ';' { _p->onTraitPrecRule($$,$1,$3,$5);}
;
trait_alias_rule:
trait_alias_rule_method T_AS
method_modifiers ident ';' { _p->onTraitAliasRuleModify($$,$1,$3,
$4);}
| trait_alias_rule_method T_AS
non_empty_member_modifiers ';' { Token t; t.reset();
_p->onTraitAliasRuleModify($$,$1,$3,
t);}
;
trait_alias_rule_method:
class_namespace_string_typeargs
T_PAAMAYIM_NEKUDOTAYIM
ident { _p->onTraitAliasRuleStart($$,$1,$3);}
| ident { Token t; t.reset();
_p->onTraitAliasRuleStart($$,t,$1);}
;
xhp_attribute_stmt:
xhp_attribute_decl { xhp_attribute_list(_p,$$,
_p->xhpGetAttributes(),$1);}
| xhp_attribute_stmt ','
xhp_attribute_decl { xhp_attribute_list(_p,$$, &$1,$3);}
;
xhp_attribute_decl:
xhp_attribute_decl_type
xhp_label_ws
xhp_attribute_default
xhp_attribute_is_required { xhp_attribute(_p,$$,$1,$2,$3,$4);
$$ = 1;}
| T_XHP_LABEL { $$ = $1; $$ = 0;}
;
xhp_attribute_decl_type:
T_ARRAY { $$ = 4;}
| fully_qualified_class_name { /* This case handles all types other
than "array", "var" and "enum".
For now we just use type code 5;
later xhp_attribute() will fix up
the type code as appropriate. */
$$ = 5; $$.setText($1);}
| T_VAR { $$ = 6;}
| T_XHP_ENUM '{'
xhp_attribute_enum '}' { $$ = $3; $$ = 7;}
;
xhp_attribute_enum:
common_scalar { _p->onArrayPair($$, 0,0,$1,0);}
| xhp_attribute_enum ','
common_scalar { _p->onArrayPair($$,&$1,0,$3,0);}
;
xhp_attribute_default:
'=' static_scalar { $$ = $2;}
| { scalar_null(_p, $$);}
;
xhp_attribute_is_required:
'@' T_XHP_REQUIRED { scalar_num(_p, $$, "1");}
| { scalar_num(_p, $$, "0");}
;
xhp_category_stmt:
xhp_category_decl { Token t; scalar_num(_p, t, "1");
_p->onArrayPair($$,0,&$1,t,0);}
| xhp_category_stmt ','
xhp_category_decl { Token t; scalar_num(_p, t, "1");
_p->onArrayPair($$,&$1,&$3,t,0);}
;
xhp_category_decl:
T_XHP_CATEGORY_LABEL { _p->onScalar($$,
T_CONSTANT_ENCAPSED_STRING, $1);}
;
xhp_children_stmt:
xhp_children_paren_expr { $$ = $1; $$ = 2;}
| ident { $$ = -1;
if ($1.same("any")) $$ = 1;}
| T_EMPTY { $$ = 0;}
;
xhp_children_paren_expr:
'(' xhp_children_decl_expr ')' { xhp_children_paren(_p, $$, $2, 0);}
| '(' xhp_children_decl_expr ')' '*' { xhp_children_paren(_p, $$, $2, 1);}
| '(' xhp_children_decl_expr ')' '?' { xhp_children_paren(_p, $$, $2, 2);}
| '(' xhp_children_decl_expr ')' '+' { xhp_children_paren(_p, $$, $2, 3);}
;
xhp_children_decl_expr:
xhp_children_paren_expr { $$ = $1;}
| xhp_children_decl_tag { xhp_children_decl(_p,$$,$1,0, 0);}
| xhp_children_decl_tag '*' { xhp_children_decl(_p,$$,$1,1, 0);}
| xhp_children_decl_tag '?' { xhp_children_decl(_p,$$,$1,2, 0);}
| xhp_children_decl_tag '+' { xhp_children_decl(_p,$$,$1,3, 0);}
| xhp_children_decl_expr ','
xhp_children_decl_expr { xhp_children_decl(_p,$$,$1,4,&$3);}
| xhp_children_decl_expr '|'
xhp_children_decl_expr { xhp_children_decl(_p,$$,$1,5,&$3);}
;
xhp_children_decl_tag:
ident { $$ = -1;
if ($1.same("any")) $$ = 1; else
if ($1.same("pcdata")) $$ = 2;}
| T_XHP_LABEL { $1.xhpLabel(); $$ = $1; $$ = 3;}
| T_XHP_CATEGORY_LABEL { $1.xhpLabel(0); $$ = $1; $$ = 4;}
;
method_body:
';' { $$.reset();}
| '{' inner_statement_list '}' { _p->finishStatement($$, $2); $$ = 1;}
;
variable_modifiers:
non_empty_member_modifiers { $$ = $1;}
| T_VAR { $$.reset();}
;
method_modifiers:
non_empty_member_modifiers { $$ = $1;}
| { $$.reset();}
;
non_empty_member_modifiers:
member_modifier { _p->onMemberModifier($$,NULL,$1);}
| non_empty_member_modifiers
member_modifier { _p->onMemberModifier($$,&$1,$2);}
;
member_modifier:
T_PUBLIC { $$ = T_PUBLIC;}
| T_PROTECTED { $$ = T_PROTECTED;}
| T_PRIVATE { $$ = T_PRIVATE;}
| T_STATIC { $$ = T_STATIC;}
| T_ABSTRACT { $$ = T_ABSTRACT;}
| T_FINAL { $$ = T_FINAL;}
;
class_variable_declaration:
class_variable_declaration ','
T_VARIABLE { _p->onClassVariable($$,&$1,$3,0);}
| class_variable_declaration ','
T_VARIABLE '=' static_scalar { _p->onClassVariable($$,&$1,$3,&$5);}
| T_VARIABLE { _p->onClassVariable($$,0,$1,0);}
| T_VARIABLE '=' static_scalar { _p->onClassVariable($$,0,$1,&$3);}
;
class_constant_declaration:
class_constant_declaration ','
sm_name_with_type '=' static_scalar { _p->onClassConstant($$,&$1,$3,$5);}
| T_CONST sm_name_with_type '=' static_scalar { _p->onClassConstant($$,0,$2,$4);}
;
new_expr:
T_NEW class_name_reference
ctor_arguments { _p->onNewObject($$, $2, $3);}
| '(' new_expr ')' { $$ = $2;}
;
parenthesis_expr:
'(' expr ')' { $$ = $2;}
;
expr_list:
expr_list ',' expr { _p->onExprListElem($$, &$1, $3);}
| expr { _p->onExprListElem($$, NULL, $1);}
;
for_expr:
expr_list { $$ = $1;}
| { $$.reset();}
;
yield_expr:
T_YIELD expr { _p->onYield($$, $2);}
;
yield_assign_expr:
variable '=' yield_expr { _p->onAssign($$, $1, $3, 0, true);}
;
yield_list_assign_expr:
T_LIST '(' assignment_list ')'
'=' yield_expr { _p->onListAssignment($$, $3, &$6, true);}
;
expr:
expr_no_variable { $$ = $1;}
| variable { $$ = $1;}
| new_expr { $$ = $1;}
expr_no_variable:
T_LIST '(' assignment_list ')'
'=' expr { _p->onListAssignment($$, $3, &$6);}
| variable '=' expr { _p->onAssign($$, $1, $3, 0);}
| variable '=' '&' variable { _p->onAssign($$, $1, $4, 1);}
| variable '=' '&' T_NEW
class_name_reference
ctor_arguments { _p->onAssignNew($$,$1,$5,$6);}
| T_CLONE expr { UEXP($$,$2,T_CLONE,1);}
| variable T_PLUS_EQUAL expr { BEXP($$,$1,$3,T_PLUS_EQUAL);}
| variable T_MINUS_EQUAL expr { BEXP($$,$1,$3,T_MINUS_EQUAL);}
| variable T_MUL_EQUAL expr { BEXP($$,$1,$3,T_MUL_EQUAL);}
| variable T_DIV_EQUAL expr { BEXP($$,$1,$3,T_DIV_EQUAL);}
| variable T_CONCAT_EQUAL expr { BEXP($$,$1,$3,T_CONCAT_EQUAL);}
| variable T_MOD_EQUAL expr { BEXP($$,$1,$3,T_MOD_EQUAL);}
| variable T_AND_EQUAL expr { BEXP($$,$1,$3,T_AND_EQUAL);}
| variable T_OR_EQUAL expr { BEXP($$,$1,$3,T_OR_EQUAL);}
| variable T_XOR_EQUAL expr { BEXP($$,$1,$3,T_XOR_EQUAL);}
| variable T_SL_EQUAL expr { BEXP($$,$1,$3,T_SL_EQUAL);}
| variable T_SR_EQUAL expr { BEXP($$,$1,$3,T_SR_EQUAL);}
| variable T_INC { UEXP($$,$1,T_INC,0);}
| T_INC variable { UEXP($$,$2,T_INC,1);}
| variable T_DEC { UEXP($$,$1,T_DEC,0);}
| T_DEC variable { UEXP($$,$2,T_DEC,1);}
| expr T_BOOLEAN_OR expr { BEXP($$,$1,$3,T_BOOLEAN_OR);}
| expr T_BOOLEAN_AND expr { BEXP($$,$1,$3,T_BOOLEAN_AND);}
| expr T_LOGICAL_OR expr { BEXP($$,$1,$3,T_LOGICAL_OR);}
| expr T_LOGICAL_AND expr { BEXP($$,$1,$3,T_LOGICAL_AND);}
| expr T_LOGICAL_XOR expr { BEXP($$,$1,$3,T_LOGICAL_XOR);}
| expr '|' expr { BEXP($$,$1,$3,'|');}
| expr '&' expr { BEXP($$,$1,$3,'&');}
| expr '^' expr { BEXP($$,$1,$3,'^');}
| expr '.' expr { BEXP($$,$1,$3,'.');}
| expr '+' expr { BEXP($$,$1,$3,'+');}
| expr '-' expr { BEXP($$,$1,$3,'-');}
| expr '*' expr { BEXP($$,$1,$3,'*');}
| expr '/' expr { BEXP($$,$1,$3,'/');}
| expr '%' expr { BEXP($$,$1,$3,'%');}
| expr T_SL expr { BEXP($$,$1,$3,T_SL);}
| expr T_SR expr { BEXP($$,$1,$3,T_SR);}
| '+' expr %prec T_INC { UEXP($$,$2,'+',1);}
| '-' expr %prec T_INC { UEXP($$,$2,'-',1);}
| '!' expr { UEXP($$,$2,'!',1);}
| '~' expr { UEXP($$,$2,'~',1);}
| expr T_IS_IDENTICAL expr { BEXP($$,$1,$3,T_IS_IDENTICAL);}
| expr T_IS_NOT_IDENTICAL expr { BEXP($$,$1,$3,T_IS_NOT_IDENTICAL);}
| expr T_IS_EQUAL expr { BEXP($$,$1,$3,T_IS_EQUAL);}
| expr T_IS_NOT_EQUAL expr { BEXP($$,$1,$3,T_IS_NOT_EQUAL);}
| expr '<' expr { BEXP($$,$1,$3,'<');}
| expr T_IS_SMALLER_OR_EQUAL expr { BEXP($$,$1,$3,
T_IS_SMALLER_OR_EQUAL);}
| expr '>' expr { BEXP($$,$1,$3,'>');}
| expr T_IS_GREATER_OR_EQUAL expr { BEXP($$,$1,$3,
T_IS_GREATER_OR_EQUAL);}
| expr T_INSTANCEOF
class_name_reference { BEXP($$,$1,$3,T_INSTANCEOF);}
| '(' expr_no_variable ')' { $$ = $2;}
| expr '?' expr ':' expr { _p->onQOp($$, $1, &$3, $5);}
| expr '?' ':' expr { _p->onQOp($$, $1, 0, $4);}
| internal_functions { $$ = $1;}
| T_INT_CAST expr { UEXP($$,$2,T_INT_CAST,1);}
| T_DOUBLE_CAST expr { UEXP($$,$2,T_DOUBLE_CAST,1);}
| T_STRING_CAST expr { UEXP($$,$2,T_STRING_CAST,1);}
| T_ARRAY_CAST expr { UEXP($$,$2,T_ARRAY_CAST,1);}
| T_OBJECT_CAST expr { UEXP($$,$2,T_OBJECT_CAST,1);}
| T_BOOL_CAST expr { UEXP($$,$2,T_BOOL_CAST,1);}
| T_UNSET_CAST expr { UEXP($$,$2,T_UNSET_CAST,1);}
| T_EXIT exit_expr { UEXP($$,$2,T_EXIT,1);}
| '@' expr { UEXP($$,$2,'@',1);}
| scalar { $$ = $1; }
| array_literal { $$ = $1; }
| shape_literal { $$ = $1; }
| '`' backticks_expr '`' { _p->onEncapsList($$,'`',$2);}
| T_PRINT expr { UEXP($$,$2,T_PRINT,1);}
| function_loc
is_reference '(' { Token t; _p->onClosureStart(t);
_p->pushLabelInfo();}
parameter_list ')'
sm_opt_return_type lexical_vars
'{' inner_statement_list '}' { Token u; u.reset();
_p->onClosure($$,u,$2,$5,$8,$10,0);
_p->popLabelInfo();}
| T_STATIC function_loc
is_reference '(' { Token t; _p->onClosureStart(t);
_p->pushLabelInfo();}
parameter_list ')'
sm_opt_return_type lexical_vars
'{' inner_statement_list '}' { Token u; u.reset();
_p->onClosure($$,u,$3,$6,$9,$11,1);
_p->popLabelInfo();}
| xhp_tag { $$ = $1;}
| dim_expr { $$ = $1;}
| collection_literal { $$ = $1;}
;
non_empty_shape_pair_list:
non_empty_shape_pair_list ','
T_CONSTANT_ENCAPSED_STRING
T_DOUBLE_ARROW
expr { validate_shape_keyname($3, _p);
_p->onArrayPair($$,&$1,&$3,$5,0); }
| T_CONSTANT_ENCAPSED_STRING
T_DOUBLE_ARROW
expr { validate_shape_keyname($1, _p);
_p->onArrayPair($$, 0,&$1,$3,0); }
;
non_empty_static_shape_pair_list:
non_empty_static_shape_pair_list ','
T_CONSTANT_ENCAPSED_STRING
T_DOUBLE_ARROW
static_scalar { validate_shape_keyname($3, _p);
_p->onArrayPair($$,&$1,&$3,$5,0); }
| T_CONSTANT_ENCAPSED_STRING
T_DOUBLE_ARROW
static_scalar { validate_shape_keyname($1, _p);
_p->onArrayPair($$, 0,&$1,$3,0); }
;
shape_pair_list:
non_empty_shape_pair_list
possible_comma { $$ = $1; }
| { $$.reset(); }
;
static_shape_pair_list:
non_empty_static_shape_pair_list
possible_comma { $$ = $1; }
| { $$.reset(); }
;
shape_literal:
T_SHAPE '(' shape_pair_list ')' { only_in_strict_mode(_p);
_p->onArray($$, $3, T_ARRAY); }
;
array_literal:
T_ARRAY '(' array_pair_list ')' { _p->onArray($$,$3,T_ARRAY);}
;
collection_literal:
fully_qualified_class_name
'{' collection_init '}' { Token t;
_p->onName(t,$1,Parser::StringName);
BEXP($$,t,$3,T_COLLECTION);}
;
static_collection_literal:
fully_qualified_class_name
'{' static_collection_init '}' { Token t;
_p->onName(t,$1,Parser::StringName);
BEXP($$,t,$3,T_COLLECTION);}
;
dim_expr:
dim_expr
'[' dim_offset ']' { _p->onRefDim($$, $1, $3);}
| dim_expr_base
'[' dim_offset ']' { _p->onRefDim($$, $1, $3);}
;
dim_expr_base:
array_literal { $$ = $1;}
| class_constant { $$ = $1;}
| '(' expr_no_variable ')' { $$ = $2;}
;
lexical_vars:
T_USE '('
lexical_var_list
possible_comma_in_hphp_syntax
')' { $$ = $3;}
| { $$.reset();}
;
lexical_var_list:
lexical_var_list ',' T_VARIABLE { _p->onClosureParam($$,&$1,$3,0);}
| lexical_var_list ',' '&'T_VARIABLE { _p->onClosureParam($$,&$1,$4,1);}
| T_VARIABLE { _p->onClosureParam($$, 0,$1,0);}
| '&' T_VARIABLE { _p->onClosureParam($$, 0,$2,1);}
;
xhp_tag:
T_XHP_TAG_LT
T_XHP_LABEL
xhp_tag_body
T_XHP_TAG_GT { xhp_tag(_p,$$,$2,$3);}
;
xhp_tag_body:
xhp_attributes '/' { Token t1; _p->onArray(t1,$1);
Token t2; _p->onArray(t2,$2);
_p->onCallParam($1,NULL,t1,0);
_p->onCallParam($$, &$1,t2,0);
$$.setText("");}
| xhp_attributes T_XHP_TAG_GT
xhp_children T_XHP_TAG_LT '/'
xhp_opt_end_label { _p->onArray($4,$1);
_p->onArray($5,$3);
_p->onCallParam($2,NULL,$4,0);
_p->onCallParam($$, &$2,$5,0);
$$.setText($6.text());}
;
xhp_opt_end_label:
{ $$.reset(); $$.setText("");}
| T_XHP_LABEL { $$.reset(); $$.setText($1);}
;
xhp_attributes:
xhp_attributes
xhp_attribute_name '='
xhp_attribute_value { _p->onArrayPair($$,&$1,&$2,$4,0);}
| { $$.reset();}
;
xhp_children:
xhp_children xhp_child { _p->onArrayPair($$,&$1,0,$2,0);}
| { $$.reset();}
;
xhp_attribute_name:
T_XHP_LABEL { _p->onScalar($$,
T_CONSTANT_ENCAPSED_STRING, $1);}
;
xhp_attribute_value:
T_XHP_TEXT { $1.xhpDecode();
_p->onScalar($$,
T_CONSTANT_ENCAPSED_STRING, $1);}
| '{' expr '}' { $$ = $2;}
;
xhp_child:
T_XHP_TEXT { $$.reset();
if ($1.htmlTrim()) {
$1.xhpDecode();
_p->onScalar($$,
T_CONSTANT_ENCAPSED_STRING, $1);
}
}
| '{' expr '}' { $$ = $2; }
| xhp_tag { $$ = $1; }
;
xhp_label_ws:
xhp_bareword { $$ = $1;}
| xhp_label_ws ':'
xhp_bareword { $$ = $1 + ":" + $3;}
| xhp_label_ws '-'
xhp_bareword { $$ = $1 + "-" + $3;}
;
xhp_bareword:
ident { $$ = $1;}
| T_EXIT { $$ = $1;}
| T_FUNCTION { $$ = $1;}
| T_CONST { $$ = $1;}
| T_RETURN { $$ = $1;}
| T_YIELD { $$ = $1;}
| T_TRY { $$ = $1;}
| T_CATCH { $$ = $1;}
| T_FINALLY { $$ = $1;}
| T_THROW { $$ = $1;}
| T_IF { $$ = $1;}
| T_ELSEIF { $$ = $1;}
| T_ENDIF { $$ = $1;}
| T_ELSE { $$ = $1;}
| T_WHILE { $$ = $1;}
| T_ENDWHILE { $$ = $1;}
| T_DO { $$ = $1;}
| T_FOR { $$ = $1;}
| T_ENDFOR { $$ = $1;}
| T_FOREACH { $$ = $1;}
| T_ENDFOREACH { $$ = $1;}
| T_DECLARE { $$ = $1;}
| T_ENDDECLARE { $$ = $1;}
| T_INSTANCEOF { $$ = $1;}
| T_AS { $$ = $1;}
| T_SWITCH { $$ = $1;}
| T_ENDSWITCH { $$ = $1;}
| T_CASE { $$ = $1;}
| T_DEFAULT { $$ = $1;}
| T_BREAK { $$ = $1;}
| T_CONTINUE { $$ = $1;}
| T_GOTO { $$ = $1;}
| T_ECHO { $$ = $1;}
| T_PRINT { $$ = $1;}
| T_CLASS { $$ = $1;}
| T_INTERFACE { $$ = $1;}
| T_EXTENDS { $$ = $1;}
| T_IMPLEMENTS { $$ = $1;}
| T_NEW { $$ = $1;}
| T_CLONE { $$ = $1;}
| T_VAR { $$ = $1;}
| T_EVAL { $$ = $1;}
| T_INCLUDE { $$ = $1;}
| T_INCLUDE_ONCE { $$ = $1;}
| T_REQUIRE { $$ = $1;}
| T_REQUIRE_ONCE { $$ = $1;}
| T_NAMESPACE { $$ = $1;}
| T_USE { $$ = $1;}
| T_GLOBAL { $$ = $1;}
| T_ISSET { $$ = $1;}
| T_EMPTY { $$ = $1;}
| T_HALT_COMPILER { $$ = $1;}
| T_STATIC { $$ = $1;}
| T_ABSTRACT { $$ = $1;}
| T_FINAL { $$ = $1;}
| T_PRIVATE { $$ = $1;}
| T_PROTECTED { $$ = $1;}
| T_PUBLIC { $$ = $1;}
| T_UNSET { $$ = $1;}
| T_LIST { $$ = $1;}
| T_ARRAY { $$ = $1;}
| T_LOGICAL_OR { $$ = $1;}
| T_LOGICAL_AND { $$ = $1;}
| T_LOGICAL_XOR { $$ = $1;}
| T_CLASS_C { $$ = $1;}
| T_FUNC_C { $$ = $1;}
| T_METHOD_C { $$ = $1;}
| T_LINE { $$ = $1;}
| T_FILE { $$ = $1;}
| T_DIR { $$ = $1;}
| T_NS_C { $$ = $1;}
| T_TRAIT { $$ = $1;}
| T_TRAIT_C { $$ = $1;}
| T_TYPE { $$ = $1;}
;
simple_function_call:
namespace_string_typeargs '('
function_call_parameter_list ')' { _p->onCall($$,0,$1,$3,NULL);}
;
fully_qualified_class_name:
class_namespace_string_typeargs { $$ = $1;}
| T_XHP_LABEL { $1.xhpLabel(); $$ = $1;}
;
static_class_name:
fully_qualified_class_name { _p->onName($$,$1,Parser::StringName);}
| T_STATIC { _p->onName($$,$1,Parser::StaticName);}
| reference_variable { _p->onName($$,$1,
Parser::StaticClassExprName);}
;
class_name_reference:
fully_qualified_class_name { _p->onName($$,$1,Parser::StringName);}
| T_STATIC { _p->onName($$,$1,Parser::StaticName);}
| variable_no_calls { _p->onName($$,$1,Parser::ExprName);}
;
exit_expr:
'(' ')' { $$.reset();}
| parenthesis_expr { $$ = $1;}
| { $$.reset();}
;
backticks_expr:
/* empty */ { $$.reset();}
| T_ENCAPSED_AND_WHITESPACE { _p->addEncap($$, NULL, $1, 0);}
| encaps_list { $$ = $1;}
ctor_arguments:
'('
function_call_parameter_list ')' { $$ = $2;}
| { $$.reset();}
;
common_scalar:
T_LNUMBER { _p->onScalar($$, T_LNUMBER, $1);}
| T_DNUMBER { _p->onScalar($$, T_DNUMBER, $1);}
| T_CONSTANT_ENCAPSED_STRING { _p->onScalar($$,
T_CONSTANT_ENCAPSED_STRING, $1);}
| T_LINE { _p->onScalar($$, T_LINE, $1);}
| T_FILE { _p->onScalar($$, T_FILE, $1);}
| T_DIR { _p->onScalar($$, T_DIR, $1);}
| T_CLASS_C { _p->onScalar($$, T_CLASS_C, $1);}
| T_TRAIT_C { _p->onScalar($$, T_TRAIT_C, $1);}
| T_METHOD_C { _p->onScalar($$, T_METHOD_C, $1);}
| T_FUNC_C { _p->onScalar($$, T_FUNC_C, $1);}
| T_NS_C { _p->onScalar($$, T_NS_C, $1);}
| T_START_HEREDOC
T_ENCAPSED_AND_WHITESPACE
T_END_HEREDOC { _p->onScalar($$, T_CONSTANT_ENCAPSED_STRING, $2);}
| T_START_HEREDOC
T_END_HEREDOC { $$.setText(""); _p->onScalar($$, T_CONSTANT_ENCAPSED_STRING, $$);}
;
static_scalar:
common_scalar { $$ = $1;}
| namespace_string { _p->onConstantValue($$, $1);}
| '+' static_scalar { UEXP($$,$2,'+',1);}
| '-' static_scalar { UEXP($$,$2,'-',1);}
| T_ARRAY '('
static_array_pair_list ')' { _p->onArray($$,$3,T_ARRAY); }
| T_SHAPE '('
static_shape_pair_list ')' { only_in_strict_mode(_p);
_p->onArray($$,$3,T_ARRAY); }
| static_class_constant { $$ = $1;}
| static_collection_literal { $$ = $1;}
;
static_class_constant:
class_namespace_string_typeargs
T_PAAMAYIM_NEKUDOTAYIM
ident { _p->onClassConst($$, $1, $3, 1);}
| T_XHP_LABEL T_PAAMAYIM_NEKUDOTAYIM
ident { $1.xhpLabel();
_p->onClassConst($$, $1, $3, 1);}
;
scalar:
namespace_string { _p->onConstantValue($$, $1);}
| T_STRING_VARNAME { _p->onConstantValue($$, $1);}
| class_constant { $$ = $1;}
| common_scalar { $$ = $1;}
| '"' encaps_list '"' { _p->onEncapsList($$,'"',$2);}
| '\'' encaps_list '\'' { _p->onEncapsList($$,'\'',$2);}
| T_START_HEREDOC encaps_list
T_END_HEREDOC { _p->onEncapsList($$,T_START_HEREDOC,
$2);}
;
static_array_pair_list:
non_empty_static_array_pair_list
possible_comma { $$ = $1;}
| { $$.reset();}
;
possible_comma:
',' { $$.reset();}
| { $$.reset();}
;
possible_comma_in_hphp_syntax:
',' { only_in_hphp_syntax(_p); $$.reset();}
| { $$.reset();}
;
non_empty_static_array_pair_list:
non_empty_static_array_pair_list
',' static_scalar T_DOUBLE_ARROW
static_scalar { _p->onArrayPair($$,&$1,&$3,$5,0);}
| non_empty_static_array_pair_list
',' static_scalar { _p->onArrayPair($$,&$1, 0,$3,0);}
| static_scalar T_DOUBLE_ARROW
static_scalar { _p->onArrayPair($$, 0,&$1,$3,0);}
| static_scalar { _p->onArrayPair($$, 0, 0,$1,0);}
;
common_scalar_ae:
T_LNUMBER { _p->onScalar($$, T_LNUMBER, $1);}
| T_DNUMBER { _p->onScalar($$, T_DNUMBER, $1);}
| T_CONSTANT_ENCAPSED_STRING { _p->onScalar($$,
T_CONSTANT_ENCAPSED_STRING, $1);}
| T_START_HEREDOC
T_ENCAPSED_AND_WHITESPACE
T_END_HEREDOC { _p->onScalar($$, T_CONSTANT_ENCAPSED_STRING, $2);}
| T_START_HEREDOC
T_END_HEREDOC { $$.setText(""); _p->onScalar($$, T_CONSTANT_ENCAPSED_STRING, $$);}
;
static_numeric_scalar_ae:
T_LNUMBER { _p->onScalar($$,T_LNUMBER,$1);}
| T_DNUMBER { _p->onScalar($$,T_DNUMBER,$1);}
| ident { constant_ae(_p,$$,$1);}
;
static_scalar_ae:
common_scalar_ae { $$ = $1;}
| ident { constant_ae(_p,$$,$1);}
| '+' static_numeric_scalar_ae { UEXP($$,$2,'+',1);}
| '-' static_numeric_scalar_ae { UEXP($$,$2,'-',1);}
| T_ARRAY '('
static_array_pair_list_ae ')' { _p->onArray($$,$3,T_ARRAY);}
| T_SHAPE '('
static_shape_pair_list_ae ')' { only_in_strict_mode(_p);
_p->onArray($$,$3,T_ARRAY); }
;
static_array_pair_list_ae:
non_empty_static_array_pair_list_ae
possible_comma { $$ = $1;}
| { $$.reset();}
;
non_empty_static_array_pair_list_ae:
non_empty_static_array_pair_list_ae
',' static_scalar_ae T_DOUBLE_ARROW
static_scalar_ae { _p->onArrayPair($$,&$1,&$3,$5,0);}
| non_empty_static_array_pair_list_ae
',' static_scalar_ae { _p->onArrayPair($$,&$1, 0,$3,0);}
| static_scalar_ae T_DOUBLE_ARROW
static_scalar_ae { _p->onArrayPair($$, 0,&$1,$3,0);}
| static_scalar_ae { _p->onArrayPair($$, 0, 0,$1,0);}
;
non_empty_static_scalar_list_ae:
non_empty_static_scalar_list_ae
',' static_scalar_ae { _p->onArrayPair($$,&$1, 0,$3,0);}
| static_scalar_ae { _p->onArrayPair($$, 0, 0,$1,0);}
;
static_shape_pair_list_ae:
non_empty_static_shape_pair_list_ae
possible_comma { $$ = $1; }
| { $$.reset(); }
;
non_empty_static_shape_pair_list_ae:
non_empty_static_shape_pair_list_ae
',' T_CONSTANT_ENCAPSED_STRING
T_DOUBLE_ARROW static_scalar_ae { validate_shape_keyname($3, _p);
_p->onArrayPair($$,&$1,&$3,$5,0); }
| T_CONSTANT_ENCAPSED_STRING
T_DOUBLE_ARROW
static_scalar_ae { validate_shape_keyname($1, _p);
_p->onArrayPair($$, 0,&$1,$3,0); }
;
static_scalar_list_ae:
non_empty_static_scalar_list_ae
possible_comma { $$ = $1;}
| { $$.reset();}
;
attribute_static_scalar_list:
'(' static_scalar_list_ae ')' { _p->onArray($$,$2,T_ARRAY);}
| { Token t; t.reset();
_p->onArray($$,t,T_ARRAY);}
;
non_empty_user_attribute_list:
non_empty_user_attribute_list
',' ident
attribute_static_scalar_list { _p->onUserAttribute($$,&$1,$3,$4);}
| ident
attribute_static_scalar_list { _p->onUserAttribute($$, 0,$1,$2);}
;
user_attribute_list:
{ user_attribute_check(_p);}
non_empty_user_attribute_list
possible_comma { $$ = $2;}
;
non_empty_user_attributes:
T_SL user_attribute_list T_SR { $$ = $2;}
;
optional_user_attributes:
non_empty_user_attributes { $$ = $1;}
| { $$.reset();}
;
property_access:
property_access_without_variables { $$ = $1;}
| T_OBJECT_OPERATOR
variable_without_objects { $$ = $2;}
;
property_access_without_variables:
T_OBJECT_OPERATOR ident { $$ = $2;}
| T_OBJECT_OPERATOR '{' expr '}' { $$ = $3;}
;
array_access:
'[' dim_offset ']' { $$ = $2;}
| '{' expr '}' { $$ = $2;}
;
dimmable_variable_access:
dimmable_variable array_access { _p->onRefDim($$, $1, $2);}
| '(' new_expr ')' array_access { _p->onRefDim($$, $2, $4);}
;
dimmable_variable_no_calls_access:
dimmable_variable_no_calls
array_access { _p->onRefDim($$, $1, $2);}
| '(' new_expr ')' array_access { _p->onRefDim($$, $2, $4);}
;
variable:
variable_without_objects { $$ = $1;}
| simple_function_call { $$ = $1;}
| object_method_call { $$ = $1;}
| class_method_call { $$ = $1;}
| dimmable_variable_access { $$ = $1;}
| variable property_access { _p->onObjectProperty($$,$1,$2);}
| '(' new_expr ')' property_access { _p->onObjectProperty($$,$2,$4);}
| static_class_name
T_PAAMAYIM_NEKUDOTAYIM
variable_without_objects { _p->onStaticMember($$,$1,$3);}
| callable_variable '('
function_call_parameter_list ')' { _p->onCall($$,1,$1,$3,NULL);}
| '(' variable ')' { $$ = $2;}
;
dimmable_variable:
simple_function_call { $$ = $1;}
| object_method_call { $$ = $1;}
| class_method_call { $$ = $1;}
| dimmable_variable_access { $$ = $1;}
| variable
property_access_without_variables { _p->onObjectProperty($$,$1,$2);}
| '(' new_expr ')'
property_access_without_variables { _p->onObjectProperty($$,$2,$4);}
| callable_variable '('
function_call_parameter_list ')' { _p->onCall($$,1,$1,$3,NULL);}
| '(' variable ')' { $$ = $2;}
;
callable_variable:
variable_without_objects { $$ = $1;}
| dimmable_variable_access { $$ = $1;}
| '(' variable ')' { $$ = $2;}
;
object_method_call:
variable T_OBJECT_OPERATOR
ident sm_typeargs_opt '('
function_call_parameter_list ')' { _p->onObjectMethodCall($$,$1,$3,$6);}
| variable T_OBJECT_OPERATOR
variable_without_objects '('
function_call_parameter_list ')' { _p->onObjectMethodCall($$,$1,$3,$5);}
| variable T_OBJECT_OPERATOR
'{' expr '}' '('
function_call_parameter_list ')' { _p->onObjectMethodCall($$,$1,$4,$7);}
| '(' new_expr ')' T_OBJECT_OPERATOR
ident sm_typeargs_opt '('
function_call_parameter_list ')' { _p->onObjectMethodCall($$,$2,$5,$8);}
| '(' new_expr ')' T_OBJECT_OPERATOR
variable_without_objects '('
function_call_parameter_list ')' { _p->onObjectMethodCall($$,$2,$5,$7);}
| '(' new_expr ')' T_OBJECT_OPERATOR
'{' expr '}' '('
function_call_parameter_list ')' { _p->onObjectMethodCall($$,$2,$6,$9);}
;
class_method_call:
static_class_name
T_PAAMAYIM_NEKUDOTAYIM
ident sm_typeargs_opt '('
function_call_parameter_list ')' { _p->onCall($$,0,$3,$6,&$1);}
| static_class_name
T_PAAMAYIM_NEKUDOTAYIM
variable_without_objects '('
function_call_parameter_list ')' { _p->onCall($$,1,$3,$5,&$1);}
;
variable_without_objects:
reference_variable { $$ = $1;}
| simple_indirect_reference
reference_variable { _p->onIndirectRef($$,$1,$2);}
;
reference_variable:
reference_variable
'[' dim_offset ']' { _p->onRefDim($$, $1, $3);}
| reference_variable '{' expr '}' { _p->onRefDim($$, $1, $3);}
| compound_variable { $$ = $1;}
;
compound_variable:
T_VARIABLE { _p->onSimpleVariable($$, $1);}
| '$' '{' expr '}' { _p->onDynamicVariable($$, $3, 0);}
;
dim_offset:
expr { $$ = $1;}
| { $$.reset();}
;
simple_indirect_reference:
'$' { $$ = 1;}
| simple_indirect_reference '$' { $$++;}
;
variable_no_calls:
variable_without_objects { $$ = $1;}
| dimmable_variable_no_calls_access { $$ = $1;}
| variable_no_calls property_access { _p->onObjectProperty($$,$1,$2);}
| '(' new_expr ')' property_access { _p->onObjectProperty($$,$2,$4);}
| static_class_name
T_PAAMAYIM_NEKUDOTAYIM
variable_without_objects { _p->onStaticMember($$,$1,$3);}
| '(' variable ')' { $$ = $2;}
;
dimmable_variable_no_calls:
| dimmable_variable_no_calls_access { $$ = $1;}
| variable_no_calls
property_access_without_variables { _p->onObjectProperty($$,$1,$2);}
| '(' new_expr ')'
property_access_without_variables { _p->onObjectProperty($$,$2,$4);}
| '(' variable ')' { $$ = $2;}
;
assignment_list:
assignment_list ',' { _p->onAListVar($$,&$1,NULL);}
| assignment_list ',' variable { _p->onAListVar($$,&$1,&$3);}
| assignment_list ','
T_LIST '(' assignment_list ')' { _p->onAListSub($$,&$1,$5);}
| { _p->onAListVar($$,NULL,NULL);}
| variable { _p->onAListVar($$,NULL,&$1);}
| T_LIST '(' assignment_list ')' { _p->onAListSub($$,NULL,$3);}
;
array_pair_list:
non_empty_array_pair_list
possible_comma { $$ = $1;}
| { $$.reset();}
;
non_empty_array_pair_list:
non_empty_array_pair_list
',' expr T_DOUBLE_ARROW expr { _p->onArrayPair($$,&$1,&$3,$5,0);}
| non_empty_array_pair_list ',' expr { _p->onArrayPair($$,&$1, 0,$3,0);}
| expr T_DOUBLE_ARROW expr { _p->onArrayPair($$, 0,&$1,$3,0);}
| expr { _p->onArrayPair($$, 0, 0,$1,0);}
| non_empty_array_pair_list
',' expr T_DOUBLE_ARROW
'&' variable { _p->onArrayPair($$,&$1,&$3,$6,1);}
| non_empty_array_pair_list ','
'&' variable { _p->onArrayPair($$,&$1, 0,$4,1);}
| expr T_DOUBLE_ARROW '&' variable { _p->onArrayPair($$, 0,&$1,$4,1);}
| '&' variable { _p->onArrayPair($$, 0, 0,$2,1);}
;
collection_init:
non_empty_collection_init
possible_comma { $$ = $1;}
| { _p->onEmptyCollection($$);}
;
non_empty_collection_init:
non_empty_collection_init
',' expr T_DOUBLE_ARROW expr { _p->onCollectionPair($$,&$1,&$3,$5);}
| non_empty_collection_init ',' expr { _p->onCollectionPair($$,&$1, 0,$3);}
| expr T_DOUBLE_ARROW expr { _p->onCollectionPair($$, 0,&$1,$3);}
| expr { _p->onCollectionPair($$, 0, 0,$1);}
;
static_collection_init:
non_empty_static_collection_init
possible_comma { $$ = $1;}
| { _p->onEmptyCollection($$);}
;
non_empty_static_collection_init:
non_empty_static_collection_init
',' static_scalar T_DOUBLE_ARROW
static_scalar { _p->onCollectionPair($$,&$1,&$3,$5);}
| non_empty_static_collection_init
',' static_scalar { _p->onCollectionPair($$,&$1, 0,$3);}
| static_scalar T_DOUBLE_ARROW
static_scalar { _p->onCollectionPair($$, 0,&$1,$3);}
| static_scalar { _p->onCollectionPair($$, 0, 0,$1);}
;
encaps_list:
encaps_list encaps_var { _p->addEncap($$, &$1, $2, -1);}
| encaps_list
T_ENCAPSED_AND_WHITESPACE { _p->addEncap($$, &$1, $2, 0);}
| encaps_var { _p->addEncap($$, NULL, $1, -1);}
| T_ENCAPSED_AND_WHITESPACE
encaps_var { _p->addEncap($$, NULL, $1, 0);
_p->addEncap($$, &$$, $2, -1); }
;
encaps_var:
T_VARIABLE { _p->onSimpleVariable($$, $1);}
| T_VARIABLE '['
encaps_var_offset ']' { _p->encapRefDim($$, $1, $3);}
| T_VARIABLE T_OBJECT_OPERATOR
ident { _p->encapObjProp($$, $1, $3);}
| T_DOLLAR_OPEN_CURLY_BRACES
expr '}' { _p->onDynamicVariable($$, $2, 1);}
| T_DOLLAR_OPEN_CURLY_BRACES
T_STRING_VARNAME '[' expr ']' '}' { _p->encapArray($$, $2, $4);}
| T_CURLY_OPEN variable '}' { $$ = $2;}
;
encaps_var_offset:
ident { $$ = $1; $$ = T_STRING;}
| T_NUM_STRING { $$ = $1; $$ = T_NUM_STRING;}
| T_VARIABLE { $$ = $1; $$ = T_VARIABLE;}
;
internal_functions:
T_ISSET '(' variable_list ')' { UEXP($$,$3,T_ISSET,1);}
| T_EMPTY '(' variable ')' { UEXP($$,$3,T_EMPTY,1);}
| T_INCLUDE expr { UEXP($$,$2,T_INCLUDE,1);}
| T_INCLUDE_ONCE expr { UEXP($$,$2,T_INCLUDE_ONCE,1);}
| T_EVAL '(' expr ')' { UEXP($$,$3,T_EVAL,1);}
| T_REQUIRE expr { UEXP($$,$2,T_REQUIRE,1);}
| T_REQUIRE_ONCE expr { UEXP($$,$2,T_REQUIRE_ONCE,1);}
;
variable_list:
variable { _p->onExprListElem($$, NULL, $1);}
| variable_list ',' variable { _p->onExprListElem($$, &$1, $3);}
;
class_constant:
static_class_name
T_PAAMAYIM_NEKUDOTAYIM ident { _p->onClassConst($$, $1, $3, 0);}
;
/* strict-mode productions -- these allow some extra stuff in strict
* mode, but simplify down to the original thing
*/
sm_typedef_statement:
T_TYPE ident '=' sm_type ';' { only_in_strict_mode(_p);
_p->onTypedef($$, $2, $4); }
;
sm_name_with_type: /* foo -> int foo */
ident { $$ = $1; }
| sm_type ident { only_in_strict_mode(_p); $$ = $2; }
;
sm_name_with_typevar: /* foo -> foo<X,Y>; this adds a typevar scope
* and must be followed by a call to
* popTypeScope() */
ident { _p->pushTypeScope(); $$ = $1; }
| ident
T_TYPELIST_LT
sm_typevar_list
T_TYPELIST_GT { _p->pushTypeScope(); $$ = $1;
only_in_strict_mode(_p); }
;
sm_typeargs_opt:
T_TYPELIST_LT
sm_type_list
T_TYPELIST_GT { only_in_strict_mode(_p); $$ = $2; }
| { $$.reset(); }
;
sm_type_list:
sm_type
| sm_type_list ',' sm_type
;
sm_func_type_list:
sm_type_list ',' T_VARARG { $$.reset(); }
| sm_type_list { $$.reset(); }
| T_VARARG { $$.reset(); }
| { $$.reset(); }
;
sm_opt_return_type:
{ $$.reset(); }
| ':' sm_type { only_in_strict_mode(_p); $$ = $2; }
;
sm_typevar_list:
ident ',' sm_typevar_list { _p->addTypeVar($1.text()); }
| ident { _p->addTypeVar($1.text()); }
| ident T_AS sm_shape_type
sm_typevar_list
| ident T_AS ident ','
sm_typevar_list { _p->addTypeVar($1.text()); }
| ident T_AS ident { _p->addTypeVar($1.text()); }
| ident T_AS sm_shape_type
;
sm_shape_member_type:
T_CONSTANT_ENCAPSED_STRING
T_DOUBLE_ARROW
sm_type { validate_shape_keyname($1, _p); }
;
sm_non_empty_shape_member_list:
sm_non_empty_shape_member_list ','
sm_shape_member_type
| sm_shape_member_type
;
sm_shape_member_list:
sm_non_empty_shape_member_list
possible_comma { $$ = $1; }
| /* empty */
{}
sm_shape_type:
T_SHAPE
'(' sm_shape_member_list ')' { only_in_strict_mode(_p);
$$.setText("array"); }
;
/* extends non_empty_type_decl with some more types */
sm_type:
/* double-optional types will be rejected by the typechecker; we
* already allow plenty of nonsense types anyway */
'?' sm_type { only_in_strict_mode(_p); $$.reset(); }
| '@' sm_type { only_in_strict_mode(_p); $$.reset(); }
| ident sm_typeargs_opt { $$ = $1;
/* if the type annotation is a bound
typevar we have to strip it */
if (_p->scanner().isStrictMode() &&
(_p->isTypeVar($$.text()) ||
!$$.text().compare("mixed") ||
!$$.text().compare("this")
)) {
$$.reset();
}
}
| T_ARRAY { $$.setText("array"); }
| sm_shape_type { $$ = $1; }
| T_ARRAY T_TYPELIST_LT sm_type
T_TYPELIST_GT { only_in_strict_mode(_p);
$$.setText("array"); }
| T_ARRAY T_TYPELIST_LT sm_type ','
sm_type T_TYPELIST_GT { only_in_strict_mode(_p);
$$.setText("array"); }
| T_XHP_LABEL { $1.xhpLabel(); $$ = $1; }
| '(' T_FUNCTION
'(' sm_func_type_list ')'
':' sm_type ')' { only_in_strict_mode(_p); $$.reset(); }
| '(' sm_type_list ',' sm_type ')' { only_in_strict_mode(_p);
$$.setText("array"); }
;
sm_type_opt:
sm_type { $$ = $1; }
| { $$.reset(); }
;
%%
bool Parser::parseImpl() {
return yyparse(this) == 0;
}