Permalink
Browse files

Parser support for pipe (|>) operator

Summary:
Introduces the hack-only pipe operator. The pipe operator evaluates the LHS
into an unnamed temporary local accessible in the RHS via the special `$$`
variable. Pipe expressions can be nested, but outer temporaries are not
accessible in nested expressions. The `|>` operator has lower precedence than
`?:` and higher precedence than yield/await and the various assignment
operators.

Reviewed By: alexmalyshev

Differential Revision: D2842828

fb-gh-sync-id: a4f4a365fa9ba6809c338d47ed0b339b2ff31698
  • Loading branch information...
paulbiss authored and hhvm-bot committed Jan 21, 2016
1 parent 50c82ad commit 992c816e19428ad3bae026702a0755827a3cba9c
@@ -3813,6 +3813,16 @@ bool EmitterVisitor::visit(ConstructPtr node) {
return true;
}
if (op == T_PIPE) {
Id pipeVar = emitVisitAndSetUnnamedL(e, b->getExp1());
allocPipeLocal(pipeVar);
visit(b->getExp2());
releasePipeLocal(pipeVar);
emitPushAndFreeUnnamedL(e, pipeVar, m_ue.bcPos());
e.PopC();
return true;
}
visit(b->getExp1());
emitConvertToCellOrLoc(e);
visit(b->getExp2());
@@ -4576,6 +4586,16 @@ bool EmitterVisitor::visit(ConstructPtr node) {
not_reached();
}
case Construct::KindOfPipeVariable: {
if (auto pipeVar = getPipeLocal()) {
emitVirtualLocal(*pipeVar);
return true;
}
throw IncludeTimeFatalException(
node, "Pipe variables must occur only in the RHS of pipe expressions");
}
case Construct::KindOfSimpleVariable: {
auto sv = static_pointer_cast<SimpleVariable>(node);
if (sv->isThis()) {
@@ -623,6 +623,16 @@ class EmitterVisitor {
int defI;
};
void allocPipeLocal(Id pipeVar) { m_pipeVars.emplace(pipeVar); }
void releasePipeLocal(Id pipeVar) {
assert(!m_pipeVars.empty() && m_pipeVars.top() == pipeVar);
m_pipeVars.pop();
}
folly::Optional<Id> getPipeLocal() {
if (m_pipeVars.empty()) return folly::none;
return m_pipeVars.top();
}
private:
static constexpr size_t kMinStringSwitchCases = 8;
@@ -667,6 +677,8 @@ class EmitterVisitor {
// Unnamed local variables used by the "finally router" logic
Id m_stateLocal;
Id m_retLocal;
// stack of nested unnamed pipe variables
std::stack<Id> m_pipeVars;
public:
bool checkIfStackEmpty(const char* forInstruction) const;
@@ -113,6 +113,7 @@ class IParseHandler {
x(AssignmentExpression, Store) \
x(SimpleVariable, Load) \
x(DynamicVariable, Load) \
x(PipeVariable, Load) \
x(StaticMemberExpression, Load) \
x(ArrayElementExpression, Load) \
x(DynamicFunctionCall, Call) \
@@ -175,6 +175,12 @@ int BinaryOpExpression::getLocalEffects() const {
}
break;
}
case T_PIPE:
if (!m_exp1->isScalar() || !m_exp2->isScalar()) {
effect = UnknownEffect;
m_canThrow = true;
}
break;
default:
break;
}
@@ -622,6 +628,7 @@ void BinaryOpExpression::outputPHP(CodeGenerator &cg, AnalysisResultPtr ar) {
case T_IS_GREATER_OR_EQUAL: cg_printf(" >= "); break;
case T_SPACESHIP: cg_printf(" <=> "); break;
case T_INSTANCEOF: cg_printf(" instanceof "); break;
case T_PIPE: cg_printf(" |> "); break;
case T_COLLECTION: {
auto el = static_pointer_cast<ExpressionList>(m_exp2);
if (el->getCount() == 0) {
@@ -65,6 +65,7 @@ using namespace HPHP;
static ListAssignment::RHSKind GetRHSKind(ExpressionPtr rhs) {
switch (rhs->getKindOf()) {
case Construct::KindOfSimpleVariable:
case Construct::KindOfPipeVariable:
case Construct::KindOfDynamicVariable:
case Construct::KindOfArrayElementExpression:
case Construct::KindOfObjectPropertyExpression:
@@ -106,6 +107,7 @@ static ListAssignment::RHSKind GetRHSKind(ExpressionPtr rhs) {
case Construct::KindOfBinaryOpExpression: {
auto b = static_pointer_cast<BinaryOpExpression>(rhs);
if (b->getOp() == T_PIPE) return GetRHSKind(b->getExp2());
if (b->isAssignmentOp() ||
b->getOp() == '+' ||
b->getOp() == T_COLLECTION) {
@@ -0,0 +1,35 @@
/*
+----------------------------------------------------------------------+
| HipHop for PHP |
+----------------------------------------------------------------------+
| Copyright (c) 2010-2016 Facebook, Inc. (http://www.facebook.com) |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
*/
#include "hphp/compiler/expression/pipe_variable.h"
using namespace HPHP;
PipeVariable::PipeVariable(EXPRESSION_CONSTRUCTOR_PARAMETERS)
: Expression(EXPRESSION_CONSTRUCTOR_PARAMETER_VALUES(PipeVariable))
{}
ExpressionPtr PipeVariable::clone() {
auto const exp = std::make_shared<PipeVariable>(*this);
Expression::deepCopy(exp);
return exp;
}
void PipeVariable::analyzeProgram(AnalysisResultPtr ar) {}
void PipeVariable::outputPHP(CodeGenerator& cg, AnalysisResultPtr ar) {
cg_printf("$$");
}
@@ -0,0 +1,35 @@
/*
+----------------------------------------------------------------------+
| HipHop for PHP |
+----------------------------------------------------------------------+
| Copyright (c) 2010-2016 Facebook, Inc. (http://www.facebook.com) |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
*/
#ifndef incl_HPHP_PIPE_VARIABLE_H_
#define incl_HPHP_PIPE_VARIABLE_H_
#include "hphp/compiler/expression/expression.h"
namespace HPHP {
///////////////////////////////////////////////////////////////////////////////
DECLARE_BOOST_TYPES(PipeVariable);
struct PipeVariable : Expression {
PipeVariable(EXPRESSION_CONSTRUCTOR_PARAMETERS);
DECLARE_BASE_EXPRESSION_VIRTUAL_FUNCTIONS;
};
///////////////////////////////////////////////////////////////////////////////
}
#endif // incl_HPHP_PIPE_VARIABLE_H_
@@ -24,6 +24,7 @@
#include "hphp/compiler/expression/assignment_expression.h"
#include "hphp/compiler/expression/simple_variable.h"
#include "hphp/compiler/expression/dynamic_variable.h"
#include "hphp/compiler/expression/pipe_variable.h"
#include "hphp/compiler/expression/static_member_expression.h"
#include "hphp/compiler/expression/array_element_expression.h"
#include "hphp/compiler/expression/dynamic_function_call.h"
@@ -422,11 +423,18 @@ void Parser::onSimpleVariable(Token &out, Token &var) {
out->exp = NEW_EXP(SimpleVariable, var->text());
}
void Parser::onPipeVariable(Token &out) {
out->exp = NEW_EXP(PipeVariable);
}
void Parser::onDynamicVariable(Token &out, Token &expr, bool encap) {
out->exp = getDynamicVariable(expr->exp, encap);
}
void Parser::onIndirectRef(Token &out, Token &refCount, Token &var) {
if (var->exp->is(Expression::KindOfPipeVariable)) {
PARSE_ERROR("Cannot take indirect reference to a pipe variable");
}
out->exp = var->exp;
for (int i = 0; i < refCount->num(); i++) {
out->exp = createDynamicVariable(out->exp);
@@ -159,6 +159,7 @@ class Parser : public ParserBase {
void onClassAbstractConstant(Token &out, Token *exprs, Token &var);
void onClassTypeConstant(Token &out, Token &var, Token &value);
void onSimpleVariable(Token &out, Token &var);
void onPipeVariable(Token &out);
void onSynthesizedVariable(Token &out, Token &var) {
onSimpleVariable(out, var);
}
Oops, something went wrong.

0 comments on commit 992c816

Please sign in to comment.