Fix __HALT_COMPILER() support
Actually halt the compiler, and set __COMPILER_HALT_OFFSET__ constant
Esse commit está contido em:
+14
-4
@@ -528,6 +528,9 @@ interface JsonSerializable {
|
||||
interface Traversable {
|
||||
}
|
||||
|
||||
interface KeyedTraversable extends Traversable {
|
||||
}
|
||||
|
||||
// Do NOT modifiy this doc comment block generated by idl/sysdoc.php
|
||||
/**
|
||||
* ( excerpt from http://php.net/manual/en/class.iterator.php )
|
||||
@@ -592,7 +595,7 @@ interface Iterator extends Traversable {
|
||||
public function valid();
|
||||
}
|
||||
|
||||
interface KeyedIterator extends Iterator {
|
||||
interface KeyedIterator extends Iterator, KeyedTraversable {
|
||||
}
|
||||
|
||||
// Do NOT modifiy this doc comment block generated by idl/sysdoc.php
|
||||
@@ -1217,7 +1220,7 @@ interface IteratorAggregate extends Traversable {
|
||||
interface Iterable extends IteratorAggregate {
|
||||
}
|
||||
|
||||
interface KeyedIterable extends Iterable {
|
||||
interface KeyedIterable extends Iterable, KeyedTraversable {
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
@@ -3813,6 +3816,13 @@ class ReflectionParameter implements Reflector {
|
||||
return '';
|
||||
}
|
||||
|
||||
public function getTypeText() {
|
||||
if (isset($this->info['type_hint'])) {
|
||||
return $this->info['type_hint'];
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
// Do NOT modifiy this doc comment block generated by idl/sysdoc.php
|
||||
/**
|
||||
* ( excerpt from http://php.net/manual/en/reflectionparameter.isarray.php
|
||||
@@ -4180,7 +4190,7 @@ class ReflectionFunctionAbstract {
|
||||
return $count;
|
||||
}
|
||||
|
||||
public function getReturnTypehintText() {
|
||||
public function getReturnTypeText() {
|
||||
if (isset($this->info['return_type'])) {
|
||||
return $this->info['return_type'];
|
||||
}
|
||||
@@ -5422,7 +5432,7 @@ class ReflectionProperty implements Reflector {
|
||||
return $this->info['doc'];
|
||||
}
|
||||
|
||||
public function getTypehintText() {
|
||||
public function getTypeText() {
|
||||
if (isset($this->info['type'])) {
|
||||
return $this->info['type'];
|
||||
}
|
||||
|
||||
@@ -243,6 +243,7 @@ TypePtr ScalarExpression::inferenceImpl(AnalysisResultConstPtr ar,
|
||||
break;
|
||||
|
||||
case T_LINE:
|
||||
case T_COMPILER_HALT_OFFSET:
|
||||
actualType = Type::Int64;
|
||||
break;
|
||||
|
||||
@@ -393,6 +394,7 @@ void ScalarExpression::outputPHP(CodeGenerator &cg, AnalysisResultPtr ar) {
|
||||
case T_NUM_STRING:
|
||||
case T_LNUMBER:
|
||||
case T_DNUMBER:
|
||||
case T_COMPILER_HALT_OFFSET:
|
||||
cg_printf("%s", m_originalValue.c_str());
|
||||
break;
|
||||
case T_NS_C:
|
||||
@@ -450,6 +452,7 @@ Variant ScalarExpression::getVariant() const {
|
||||
case T_NUM_STRING:
|
||||
return String(m_value);
|
||||
case T_LNUMBER:
|
||||
case T_COMPILER_HALT_OFFSET:
|
||||
return strtoll(m_value.c_str(), nullptr, 0);
|
||||
case T_LINE:
|
||||
return String(m_translated).toInt64();
|
||||
@@ -488,14 +491,13 @@ bool ScalarExpression::getString(const std::string *&s) const {
|
||||
}
|
||||
|
||||
bool ScalarExpression::getInt(int64_t &i) const {
|
||||
if (m_type == T_LNUMBER) {
|
||||
if (m_type == T_LNUMBER || m_type == T_COMPILER_HALT_OFFSET) {
|
||||
i = strtoll(m_value.c_str(), nullptr, 0);
|
||||
return true;
|
||||
} else if (m_type == T_LINE) {
|
||||
i = getLocation() ? getLocation()->line1 : 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -508,3 +510,11 @@ bool ScalarExpression::getDouble(double &d) const {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void ScalarExpression::setCompilerHaltOffset(int64_t ofs) {
|
||||
assert(m_type == T_COMPILER_HALT_OFFSET);
|
||||
std::ostringstream ss;
|
||||
ss << ofs;
|
||||
m_value = ss.str();
|
||||
m_originalValue = ss.str();
|
||||
}
|
||||
|
||||
@@ -76,6 +76,8 @@ public:
|
||||
bool getString(const std::string *&s) const;
|
||||
bool getInt(int64_t &i) const;
|
||||
bool getDouble(double &d) const;
|
||||
|
||||
void setCompilerHaltOffset(int64_t ofs);
|
||||
private:
|
||||
int m_type;
|
||||
std::string m_serializedValue;
|
||||
|
||||
+4532
-4378
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
@@ -529,6 +529,7 @@ void Parser::onScalar(Token &out, int type, Token &scalar) {
|
||||
case T_LNUMBER:
|
||||
case T_DNUMBER:
|
||||
case T_LINE:
|
||||
case T_COMPILER_HALT_OFFSET:
|
||||
case T_FUNC_C:
|
||||
case T_CLASS_C:
|
||||
exp = NEW_EXP(ScalarExpression, type, scalar->text());
|
||||
@@ -546,6 +547,12 @@ void Parser::onScalar(Token &out, int type, Token &scalar) {
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
if (type == T_COMPILER_HALT_OFFSET) {
|
||||
// Keep track of this expression for later backpatching
|
||||
// If it doesn't get backpatched (because there was no HALT_COMPILER
|
||||
// then the constant will return (int)"__COMPILER_HALT_OFFSET__" (zero)
|
||||
m_compilerHaltOffsetVec.push_back(exp);
|
||||
}
|
||||
out->exp = exp;
|
||||
}
|
||||
|
||||
@@ -1190,13 +1197,11 @@ void Parser::onMemberModifier(Token &out, Token *modifiers, Token &modifier) {
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// statements
|
||||
|
||||
void Parser::saveParseTree(Token &tree) {
|
||||
if (tree->stmt) {
|
||||
m_tree = dynamic_pointer_cast<StatementList>(tree->stmt);
|
||||
} else {
|
||||
m_tree = NEW_STMT0(StatementList);
|
||||
}
|
||||
void Parser::initParseTree() {
|
||||
m_tree = NEW_STMT0(StatementList);
|
||||
}
|
||||
|
||||
void Parser::finiParseTree() {
|
||||
if (m_staticVars.size()) fixStaticVars();
|
||||
FunctionScopePtr pseudoMain = m_file->setTree(m_ar, m_tree);
|
||||
completeScope(pseudoMain);
|
||||
@@ -1208,28 +1213,46 @@ void Parser::saveParseTree(Token &tree) {
|
||||
pseudoMain->getStmt()->setLocation(loc);
|
||||
}
|
||||
|
||||
void Parser::onHaltCompiler() {
|
||||
if (m_nsState == InsideNamespace && !m_nsFileScope) {
|
||||
error("__HALT_COMPILER() can only be used from the outermost scope");
|
||||
return;
|
||||
}
|
||||
// Backpatch instances of __COMPILER_HALT_OFFSET__
|
||||
for(auto &cho : m_compilerHaltOffsetVec) {
|
||||
cho->setCompilerHaltOffset(m_scanner.getLocation()->cursor);
|
||||
}
|
||||
}
|
||||
|
||||
void Parser::onStatementListStart(Token &out) {
|
||||
out.reset();
|
||||
}
|
||||
|
||||
void Parser::addTopStatement(Token &new_stmt) {
|
||||
addStatement(m_tree, new_stmt->stmt);
|
||||
}
|
||||
|
||||
void Parser::addStatement(Token &out, Token &stmts, Token &new_stmt) {
|
||||
if (!stmts->stmt) {
|
||||
out->stmt = NEW_STMT0(StatementList);
|
||||
} else {
|
||||
out->stmt = stmts->stmt;
|
||||
}
|
||||
addStatement(out->stmt, new_stmt->stmt);
|
||||
}
|
||||
|
||||
void Parser::addStatement(StatementPtr stmt, StatementPtr new_stmt) {
|
||||
assert(!m_prependingStatements.empty());
|
||||
vector<StatementPtr> &prepending = m_prependingStatements.back();
|
||||
if (!prepending.empty()) {
|
||||
assert(prepending.size() == 1);
|
||||
for (unsigned i = 0; i < prepending.size(); i++) {
|
||||
out->stmt->addElement(prepending[i]);
|
||||
stmt->addElement(prepending[i]);
|
||||
}
|
||||
prepending.clear();
|
||||
}
|
||||
if (new_stmt->stmt) {
|
||||
out->stmt->addElement(new_stmt->stmt);
|
||||
if (new_stmt) {
|
||||
stmt->addElement(new_stmt);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "compiler/construct.h"
|
||||
#include "compiler/option.h"
|
||||
#include "compiler/type_annotation.h"
|
||||
#include "compiler/expression/scalar_expression.h"
|
||||
|
||||
#ifdef HPHP_PARSER_NS
|
||||
#undef HPHP_PARSER_NS
|
||||
@@ -123,7 +124,9 @@ public:
|
||||
StatementListPtr getTree() const { return m_tree;}
|
||||
|
||||
// parser handlers
|
||||
void saveParseTree(Token &tree);
|
||||
void initParseTree();
|
||||
void finiParseTree();
|
||||
void onHaltCompiler();
|
||||
void onName(Token &out, Token &name, NameKind kind);
|
||||
void onVariable(Token &out, Token *exprs, Token &var, Token *value,
|
||||
bool constant = false,
|
||||
@@ -202,6 +205,7 @@ public:
|
||||
void onMemberModifier(Token &out, Token *modifiers, Token &modifier);
|
||||
void onStatementListStart(Token &out);
|
||||
void addStatement(Token &out, Token &stmts, Token &new_stmt);
|
||||
void addTopStatement(Token &new_stmt);
|
||||
void onClassStatement(Token &out, Token &stmts, Token &new_stmt) {
|
||||
addStatement(out, stmts, new_stmt);
|
||||
}
|
||||
@@ -290,6 +294,7 @@ private:
|
||||
std::vector<BlockScopePtrVec> m_scopes;
|
||||
std::vector<FunctionContext> m_funcContexts;
|
||||
std::vector<std::vector<StatementPtr> > m_prependingStatements;
|
||||
std::vector<ScalarExpressionPtr> m_compilerHaltOffsetVec;
|
||||
std::string m_clsName; // for T_CLASS_C inside a closure
|
||||
std::string m_funcName;
|
||||
bool m_inTrait;
|
||||
@@ -318,6 +323,8 @@ private:
|
||||
bool hasType(Token &type);
|
||||
|
||||
void checkAssignThis(Token &var);
|
||||
|
||||
void addStatement(StatementPtr stmt, StatementPtr new_stmt);
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
| license@php.net so we can mail you a copy immediately. |
|
||||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
// @generated by idl/class_map.php
|
||||
#include <runtime/base/base_includes.h>
|
||||
#include <runtime/ext/ext.h>
|
||||
namespace HPHP {
|
||||
@@ -1644,6 +1643,7 @@ const int64_t k_T_CLASS_C = 361;
|
||||
const int64_t k_T_CLONE = 298;
|
||||
const int64_t k_T_CLOSE_TAG = 370;
|
||||
const int64_t k_T_COMMENT = 366;
|
||||
const int64_t k_T_COMPILER_HALT_OFFSET = 405;
|
||||
const int64_t k_T_CONCAT_EQUAL = 273;
|
||||
const int64_t k_T_CONST = 335;
|
||||
const int64_t k_T_CONSTANT_ENCAPSED_STRING = 315;
|
||||
@@ -19132,6 +19132,7 @@ const char *g_class_map[] = {
|
||||
"T_CLONE", (const char*)6, "i:298;",
|
||||
"T_CLOSE_TAG", (const char*)6, "i:370;",
|
||||
"T_COMMENT", (const char*)6, "i:366;",
|
||||
"T_COMPILER_HALT_OFFSET", (const char*)6, "i:405;",
|
||||
"T_CONCAT_EQUAL", (const char*)6, "i:273;",
|
||||
"T_CONST", (const char*)6, "i:335;",
|
||||
"T_CONSTANT_ENCAPSED_STRING", (const char*)6, "i:315;",
|
||||
|
||||
@@ -1646,6 +1646,7 @@ extern const int64_t k_T_CLASS_C;
|
||||
extern const int64_t k_T_CLONE;
|
||||
extern const int64_t k_T_CLOSE_TAG;
|
||||
extern const int64_t k_T_COMMENT;
|
||||
extern const int64_t k_T_COMPILER_HALT_OFFSET;
|
||||
extern const int64_t k_T_CONCAT_EQUAL;
|
||||
extern const int64_t k_T_CONST;
|
||||
extern const int64_t k_T_CONSTANT_ENCAPSED_STRING;
|
||||
|
||||
@@ -6509,6 +6509,10 @@
|
||||
"name": "T_COMMENT",
|
||||
"value": 366
|
||||
},
|
||||
{
|
||||
"name": "T_COMPILER_HALT_OFFSET",
|
||||
"value": 405
|
||||
},
|
||||
{
|
||||
"name": "T_CONCAT_EQUAL",
|
||||
"value": 273
|
||||
@@ -7986,4 +7990,4 @@
|
||||
],
|
||||
"classes": [
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -220,8 +220,11 @@ bool TestParserStmt::TestStatementList() {
|
||||
V("<?php function test() {} ; class Test {} __halt_compiler();",
|
||||
"function test() {\n}\nclass Test {\n}\n");
|
||||
|
||||
V("<?php function test() {} ; __halt_compiler(); function test() {}",
|
||||
"function test() {\n}\n");
|
||||
|
||||
V("<?php ; __halt_compiler(); function test() {} class Test {}",
|
||||
"function test() {\n}\nclass Test {\n}\n");
|
||||
"");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -363,6 +363,10 @@ BACKQUOTE_CHARS ("{"*([^$`\\{]|("\\"{ANY_CHAR}))|{BACKQUOTE_LITERAL_DOLLAR})
|
||||
<ST_IN_SCRIPTING>"isset" { SETTOKEN; return T_ISSET;}
|
||||
<ST_IN_SCRIPTING>"empty" { SETTOKEN; return T_EMPTY;}
|
||||
<ST_IN_SCRIPTING>"__halt_compiler" { SETTOKEN; return T_HALT_COMPILER;}
|
||||
<ST_IN_SCRIPTING>"__compiler_halt_offset__" {
|
||||
SETTOKEN;
|
||||
return T_COMPILER_HALT_OFFSET;
|
||||
}
|
||||
<ST_IN_SCRIPTING>"static" { SETTOKEN; return T_STATIC;}
|
||||
<ST_IN_SCRIPTING>"abstract" { SETTOKEN; return T_ABSTRACT;}
|
||||
<ST_IN_SCRIPTING>"final" { SETTOKEN; return T_FINAL;}
|
||||
|
||||
@@ -43,9 +43,6 @@
|
||||
#define YYTOKEN(num, name) name = num
|
||||
#endif
|
||||
YYTOKEN_MAP {
|
||||
#ifndef YYTOKEN_MIN
|
||||
#define YYTOKEN_MIN 258
|
||||
#endif
|
||||
YYTOKEN(258, T_REQUIRE_ONCE),
|
||||
YYTOKEN(259, T_REQUIRE),
|
||||
YYTOKEN(260, T_EVAL),
|
||||
@@ -192,12 +189,9 @@
|
||||
YYTOKEN(401, T_COLLECTION),
|
||||
YYTOKEN(402, T_SHAPE),
|
||||
YYTOKEN(403, T_TYPE),
|
||||
YYTOKEN(404, T_UNRESOLVED_TYPE)
|
||||
YYTOKEN(404, T_UNRESOLVED_TYPE),
|
||||
YYTOKEN(405, T_COMPILER_HALT_OFFSET)
|
||||
};
|
||||
#ifndef YYTOKEN_MAX
|
||||
#define YYTOKEN_MAX 404
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -226,3 +220,9 @@ typedef struct YYLTYPE
|
||||
|
||||
|
||||
|
||||
#ifndef YYTOKEN_MIN
|
||||
#define YYTOKEN_MIN 258
|
||||
#endif
|
||||
#ifndef YYTOKEN_MAX
|
||||
#define YYTOKEN_MAX 405
|
||||
#endif
|
||||
|
||||
@@ -814,17 +814,19 @@ static int yylex(YYSTYPE *token, HPHP::Location *loc, Parser *_p) {
|
||||
%token T_TYPE
|
||||
%token T_UNRESOLVED_TYPE
|
||||
|
||||
%token T_COMPILER_HALT_OFFSET
|
||||
|
||||
%%
|
||||
|
||||
start:
|
||||
top_statement_list { _p->popLabelInfo();
|
||||
_p->saveParseTree($$);}
|
||||
{ _p->initParseTree(); } top_statement_list { _p->popLabelInfo();
|
||||
_p->finiParseTree();}
|
||||
;
|
||||
|
||||
top_statement_list:
|
||||
top_statement_list
|
||||
top_statement { _p->addStatement($$,$1,$2);}
|
||||
| { _p->onStatementListStart($$);}
|
||||
top_statement { _p->addTopStatement($2);}
|
||||
| { }
|
||||
;
|
||||
top_statement:
|
||||
statement { _p->nns($1.num() == T_DECLARE);
|
||||
@@ -833,8 +835,10 @@ top_statement:
|
||||
| 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());
|
||||
| T_HALT_COMPILER '(' ')' ';' { _p->onHaltCompiler();
|
||||
_p->finiParseTree();
|
||||
YYACCEPT;}
|
||||
| T_NAMESPACE namespace_name ';' { _p->onNamespaceStart($2.text(), true);
|
||||
$$.reset();}
|
||||
| T_NAMESPACE namespace_name '{' { _p->onNamespaceStart($2.text());}
|
||||
top_statement_list '}' { _p->onNamespaceEnd(); $$ = $5;}
|
||||
@@ -1859,6 +1863,7 @@ xhp_bareword:
|
||||
| T_FILE { $$ = $1;}
|
||||
| T_DIR { $$ = $1;}
|
||||
| T_NS_C { $$ = $1;}
|
||||
| T_COMPILER_HALT_OFFSET { $$ = $1;}
|
||||
| T_TRAIT { $$ = $1;}
|
||||
| T_TRAIT_C { $$ = $1;}
|
||||
| T_TYPE { $$ = $1;}
|
||||
@@ -1915,6 +1920,7 @@ common_scalar:
|
||||
| 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_COMPILER_HALT_OFFSET { _p->onScalar($$, T_COMPILER_HALT_OFFSET, $1);}
|
||||
| T_START_HEREDOC
|
||||
T_ENCAPSED_AND_WHITESPACE
|
||||
T_END_HEREDOC { _p->onScalar($$, T_CONSTANT_ENCAPSED_STRING, $2);}
|
||||
|
||||
+53438
-52760
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
@@ -25,13 +25,14 @@ namespace HPHP {
|
||||
DECLARE_BOOST_TYPES(Location);
|
||||
class Location {
|
||||
public:
|
||||
Location() : file(""), line0(1), char0(1), line1(1), char1(1) {}
|
||||
Location() : file(""), line0(1), char0(1), line1(1), char1(1), cursor(0) {}
|
||||
|
||||
const char *file;
|
||||
int line0;
|
||||
int char0;
|
||||
int line1;
|
||||
int char1;
|
||||
int cursor;
|
||||
|
||||
void first(int line, char pos) {
|
||||
line0 = line; char0 = pos;
|
||||
|
||||
@@ -114,6 +114,7 @@ LocationPtr ParserBase::getLocation() const {
|
||||
location->char0 = char0();
|
||||
location->line1 = line1();
|
||||
location->char1 = char1();
|
||||
location->cursor = cursor();
|
||||
return location;
|
||||
}
|
||||
|
||||
@@ -297,13 +298,15 @@ void ParserBase::nns(bool declare /* = false */) {
|
||||
}
|
||||
}
|
||||
|
||||
void ParserBase::onNamespaceStart(const std::string &ns) {
|
||||
void ParserBase::onNamespaceStart(const std::string &ns,
|
||||
bool file_scope /* =false */) {
|
||||
if (m_nsState == SeenNonNamespaceStatement) {
|
||||
error("Namespace declaration statement has to be the very first "
|
||||
"statement in the script: %s", getMessage().c_str());
|
||||
return;
|
||||
}
|
||||
m_nsState = InsideNamespace;
|
||||
m_nsFileScope = file_scope;
|
||||
|
||||
m_namespace = ns;
|
||||
}
|
||||
|
||||
@@ -134,6 +134,7 @@ public:
|
||||
int char0() const { return m_loc.char0;}
|
||||
int line1() const { return m_loc.line1;}
|
||||
int char1() const { return m_loc.char1;}
|
||||
int cursor() const { return m_loc.cursor;}
|
||||
|
||||
// called by generated code
|
||||
int scan(ScannerToken *token, Location *loc) {
|
||||
@@ -168,7 +169,7 @@ public:
|
||||
void popLabelInfo();
|
||||
|
||||
// for namespace support
|
||||
void onNamespaceStart(const std::string &ns);
|
||||
void onNamespaceStart(const std::string &ns, bool file_scope = false);
|
||||
void onNamespaceEnd();
|
||||
void onUse(const std::string &ns, const std::string &as);
|
||||
void nns(bool declare = false);
|
||||
@@ -235,6 +236,7 @@ protected:
|
||||
InsideNamespace,
|
||||
};
|
||||
NamespaceState m_nsState;
|
||||
bool m_nsFileScope;
|
||||
std::string m_namespace; // current namespace
|
||||
hphp_string_imap<std::string> m_aliases;
|
||||
|
||||
|
||||
@@ -489,6 +489,8 @@ void Scanner::incLoc(const char *rawText, int rawLeng) {
|
||||
assert(rawText);
|
||||
assert(rawLeng > 0);
|
||||
|
||||
m_loc->cursor += rawLeng;
|
||||
|
||||
switch (m_state) {
|
||||
case Start:
|
||||
break; // scanner set to (1, 1, 1, 1) already
|
||||
|
||||
@@ -144,7 +144,8 @@ struct Parser : ParserBase {
|
||||
|
||||
IMPLEMENT_XHP_ATTRIBUTES;
|
||||
|
||||
void saveParseTree(Token& tree) { X(); }
|
||||
void initParseTree() { X(); }
|
||||
void finiParseTree() { X(); }
|
||||
|
||||
void onName(Token& out, Token& name, NameKind kind) {
|
||||
X(name, kind);
|
||||
@@ -385,6 +386,9 @@ struct Parser : ParserBase {
|
||||
|
||||
void onStatementListStart(Token &out) { X(); }
|
||||
|
||||
void addTopStatement(Token &new_stmt) { X(new_stmt); }
|
||||
void onHaltCompiler() { X(); }
|
||||
|
||||
void addStatement(Token& out, Token& stmts, Token& new_stmt) {
|
||||
X(stmts, new_stmt);
|
||||
}
|
||||
|
||||
Referência em uma Nova Issue
Bloquear um usuário