[core] Simplify CompoundExpression implementation

This commit is contained in:
John Firebaugh
2018-07-25 18:15:24 -07:00
parent 85742b908d
commit bf7785618a
6 changed files with 131 additions and 157 deletions

View File

@@ -5,9 +5,7 @@
#include <mbgl/style/expression/parsing_context.hpp>
#include <mbgl/style/expression/type.hpp>
#include <mbgl/style/expression/value.hpp>
#include <mbgl/util/optional.hpp>
#include <mbgl/util/variant.hpp>
#include <memory>
#include <vector>
@@ -16,115 +14,34 @@ namespace mbgl {
namespace style {
namespace expression {
namespace detail {
struct SignatureBase;
} // namespace detail
/*
CompoundExpression provides a mechanism for implementing an expression
simply by providing a list of pure functions of the form
(const T0& arg0, const T1& arg1, ...) -> Result<U> where T0, T1, ..., U are
member types of mbgl::style::expression::Value.
The majority of expressions specified in the style-spec are implemented in
this fashion (see compound_expression.cpp).
*/
/*
Represents the parameter list for an expression that takes an arbitrary
number of arguments (of a specific type).
*/
struct VarargsType { type::Type type; };
template <typename T>
struct Varargs : std::vector<T> {
template <class... Args>
Varargs(Args&&... args) : std::vector<T>(std::forward<Args>(args)...) {}
};
namespace detail {
// Base class for the Signature<Fn> structs that are used to determine
// each CompoundExpression definition's type::Type data from the type of its
// "evaluate" function.
struct SignatureBase {
SignatureBase(type::Type result_, variant<std::vector<type::Type>, VarargsType> params_, std::string name_) :
result(std::move(result_)),
params(std::move(params_)),
name(std::move(name_))
{}
virtual ~SignatureBase() = default;
virtual std::unique_ptr<Expression> makeExpression(std::vector<std::unique_ptr<Expression>>) const = 0;
type::Type result;
variant<std::vector<type::Type>, VarargsType> params;
std::string name;
};
} // namespace detail
/*
Common base class for CompoundExpression<Signature> instances. Used to
allow downcasting (and access to things like name & parameter list) during
an Expression tree traversal.
*/
class CompoundExpressionBase : public Expression {
class CompoundExpression : public Expression {
public:
CompoundExpressionBase(std::string name_, const detail::SignatureBase& signature) :
Expression(Kind::CompoundExpression, signature.result),
name(std::move(name_)),
params(signature.params)
{}
CompoundExpression(const detail::SignatureBase&, std::vector<std::unique_ptr<Expression>>);
std::string getName() const { return name; }
optional<std::size_t> getParameterCount() const {
return params.match(
[&](const VarargsType&) { return optional<std::size_t>(); },
[&](const std::vector<type::Type>& p) -> optional<std::size_t> { return p.size(); }
);
}
std::string getOperator() const override;
EvaluationResult evaluate(const EvaluationContext& evaluationParams) const override;
std::vector<optional<Value>> possibleOutputs() const override;
void eachChild(const std::function<void(const Expression&)>& visit) const override;
bool operator==(const Expression& e) const override;
std::vector<optional<Value>> possibleOutputs() const override {
return { nullopt };
}
optional<std::size_t> getParameterCount() const;
private:
std::string name;
variant<std::vector<type::Type>, VarargsType> params;
};
template <typename Signature>
class CompoundExpression : public CompoundExpressionBase {
public:
using Args = typename Signature::Args;
CompoundExpression(const std::string& name_,
Signature signature_,
typename Signature::Args args_) :
CompoundExpressionBase(name_, signature_),
signature(signature_),
args(std::move(args_))
{}
EvaluationResult evaluate(const EvaluationContext& evaluationParams) const override {
return signature.apply(evaluationParams, args);
}
void eachChild(const std::function<void(const Expression&)>& visit) const override {
for (const std::unique_ptr<Expression>& e : args) {
visit(*e);
}
}
bool operator==(const Expression& e) const override {
if (e.getKind() == Kind::CompoundExpression) {
auto rhs = static_cast<const CompoundExpression*>(&e);
return getName() == rhs->getName() && Expression::childrenEqual(args, rhs->args);
}
return false;
}
std::string getOperator() const override {
return signature.name;
}
private:
Signature signature;
typename Signature::Args args;
protected:
const detail::SignatureBase& signature;
std::vector<std::unique_ptr<Expression>> args;
};
/*
@@ -136,7 +53,9 @@ struct CompoundExpressionRegistry {
static std::unordered_map<std::string, Definition> definitions;
};
ParseResult parseCompoundExpression(const std::string name, const mbgl::style::conversion::Convertible& value, ParsingContext& ctx);
ParseResult parseCompoundExpression(const std::string name,
const mbgl::style::conversion::Convertible& value,
ParsingContext& ctx);
ParseResult createCompoundExpression(const std::string& name,
std::vector<std::unique_ptr<Expression>> args,

View File

@@ -10,9 +10,9 @@ namespace expression {
template <typename T>
bool isGlobalPropertyConstant(const Expression& expression, const T& properties) {
if (expression.getKind() == Kind::CompoundExpression) {
auto e = static_cast<const CompoundExpressionBase*>(&expression);
auto e = static_cast<const CompoundExpression*>(&expression);
for (const std::string& property : properties) {
if (e->getName() == property) {
if (e->getOperator() == property) {
return false;
}
}

View File

@@ -9,13 +9,50 @@
#include <mbgl/util/ignore.hpp>
#include <mbgl/util/string.hpp>
#include <mbgl/util/platform.hpp>
#include <mbgl/util/variant.hpp>
#include <cmath>
namespace mbgl {
namespace style {
namespace expression {
/*
Represents the parameter list for an expression that takes an arbitrary
number of arguments (of a specific type).
*/
struct VarargsType { type::Type type; };
bool operator==(const VarargsType& lhs, const VarargsType& rhs) {
return lhs.type == rhs.type;
}
template <typename T>
struct Varargs : std::vector<T> {
template <class... Args>
Varargs(Args&&... args) : std::vector<T>(std::forward<Args>(args)...) {}
};
namespace detail {
// Base class for the Signature<Fn> structs that are used to determine
// each CompoundExpression definition's type::Type data from the type of its
// "evaluate" function.
struct SignatureBase {
using Args = std::vector<std::unique_ptr<Expression>>;
SignatureBase(type::Type result_, variant<std::vector<type::Type>, VarargsType> params_, std::string name_) :
result(std::move(result_)),
params(std::move(params_)),
name(std::move(name_))
{}
virtual ~SignatureBase() = default;
virtual EvaluationResult apply(const EvaluationContext&, const Args&) const = 0;
type::Type result;
variant<std::vector<type::Type>, VarargsType> params;
std::string name;
};
/*
The Signature<Fn> structs are wrappers around an "evaluate()" function whose
@@ -44,8 +81,6 @@ struct Signature;
// Simple evaluate function (const T0&, const T1&, ...) -> Result<U>
template <class R, class... Params>
struct Signature<R (Params...)> : SignatureBase {
using Args = std::array<std::unique_ptr<Expression>, sizeof...(Params)>;
Signature(R (*evaluate_)(Params...), std::string name_) :
SignatureBase(
valueTypeToExpressionType<std::decay_t<typename R::Value>>(),
@@ -53,26 +88,23 @@ struct Signature<R (Params...)> : SignatureBase {
std::move(name_)
),
evaluate(evaluate_) {}
EvaluationResult apply(const EvaluationContext& evaluationParameters, const Args& args) const {
EvaluationResult apply(const EvaluationContext& evaluationParameters, const Args& args) const override {
return applyImpl(evaluationParameters, args, std::index_sequence_for<Params...>{});
}
std::unique_ptr<Expression> makeExpression(std::vector<std::unique_ptr<Expression>> args) const override {
typename Signature::Args argsArray;
std::copy_n(std::make_move_iterator(args.begin()), sizeof...(Params), argsArray.begin());
return std::make_unique<CompoundExpression<Signature>>(name, *this, std::move(argsArray));
}
R (*evaluate)(Params...);
private:
template <std::size_t ...I>
EvaluationResult applyImpl(const EvaluationContext& evaluationParameters, const Args& args, std::index_sequence<I...>) const {
const std::array<EvaluationResult, sizeof...(I)> evaluated = {{std::get<I>(args)->evaluate(evaluationParameters)...}};
for (const auto& arg : evaluated) {
if(!arg) return arg.error();
std::array<Value, sizeof...(Params)> evaluated;
for (std::size_t i = 0; i < sizeof...(Params); ++i) {
const EvaluationResult evaluatedArg = args.at(i)->evaluate(evaluationParameters);
if (!evaluatedArg) return evaluatedArg.error();
evaluated[i] = std::move(*evaluatedArg);
}
const R value = evaluate(*fromExpressionValue<std::decay_t<Params>>(*(evaluated[I]))...);
const R value = evaluate(*fromExpressionValue<std::decay_t<Params>>(evaluated[I])...);
if (!value) return value.error();
return *value;
}
@@ -81,8 +113,6 @@ private:
// Varargs evaluate function (const Varargs<T>&) -> Result<U>
template <class R, typename T>
struct Signature<R (const Varargs<T>&)> : SignatureBase {
using Args = std::vector<std::unique_ptr<Expression>>;
Signature(R (*evaluate_)(const Varargs<T>&), std::string name_) :
SignatureBase(
valueTypeToExpressionType<std::decay_t<typename R::Value>>(),
@@ -92,11 +122,7 @@ struct Signature<R (const Varargs<T>&)> : SignatureBase {
evaluate(evaluate_)
{}
std::unique_ptr<Expression> makeExpression(std::vector<std::unique_ptr<Expression>> args) const override {
return std::make_unique<CompoundExpression<Signature>>(name, *this, std::move(args));
};
EvaluationResult apply(const EvaluationContext& evaluationParameters, const Args& args) const {
EvaluationResult apply(const EvaluationContext& evaluationParameters, const Args& args) const override {
Varargs<T> evaluated;
evaluated.reserve(args.size());
for (const auto& arg : args) {
@@ -108,7 +134,7 @@ struct Signature<R (const Varargs<T>&)> : SignatureBase {
if (!value) return value.error();
return *value;
}
R (*evaluate)(const Varargs<T>&);
};
@@ -116,8 +142,6 @@ struct Signature<R (const Varargs<T>&)> : SignatureBase {
// (const EvaluationParams&, const T0&, const T1&, ...) -> Result<U>
template <class R, class... Params>
struct Signature<R (const EvaluationContext&, Params...)> : SignatureBase {
using Args = std::array<std::unique_ptr<Expression>, sizeof...(Params)>;
Signature(R (*evaluate_)(const EvaluationContext&, Params...), std::string name_) :
SignatureBase(
valueTypeToExpressionType<std::decay_t<typename R::Value>>(),
@@ -127,29 +151,24 @@ struct Signature<R (const EvaluationContext&, Params...)> : SignatureBase {
evaluate(evaluate_)
{}
std::unique_ptr<Expression> makeExpression(std::vector<std::unique_ptr<Expression>> args) const override {
typename Signature::Args argsArray;
std::copy_n(std::make_move_iterator(args.begin()), sizeof...(Params), argsArray.begin());
return std::make_unique<CompoundExpression<Signature>>(name, *this, std::move(argsArray));
}
EvaluationResult apply(const EvaluationContext& evaluationParameters, const Args& args) const {
EvaluationResult apply(const EvaluationContext& evaluationParameters, const Args& args) const override {
return applyImpl(evaluationParameters, args, std::index_sequence_for<Params...>{});
}
private:
template <std::size_t ...I>
EvaluationResult applyImpl(const EvaluationContext& evaluationParameters, const Args& args, std::index_sequence<I...>) const {
const std::array<EvaluationResult, sizeof...(I)> evaluated = {{std::get<I>(args)->evaluate(evaluationParameters)...}};
for (const auto& arg : evaluated) {
if(!arg) return arg.error();
std::array<Value, sizeof...(Params)> evaluated;
for (std::size_t i = 0; i < sizeof...(Params); ++i) {
const EvaluationResult evaluatedArg = args.at(i)->evaluate(evaluationParameters);
if (!evaluatedArg) return evaluatedArg.error();
evaluated[i] = std::move(*evaluatedArg);
}
// TODO: assert correct runtime type of each arg value
const R value = evaluate(evaluationParameters, *fromExpressionValue<std::decay_t<Params>>(*(evaluated[I]))...);
const R value = evaluate(evaluationParameters, *fromExpressionValue<std::decay_t<Params>>(evaluated[I])...);
if (!value) return value.error();
return *value;
}
R (*evaluate)(const EvaluationContext&, Params...);
};
@@ -157,8 +176,6 @@ private:
// (const EvaluationContext&, const Varargs<T>&) -> Result<U>
template <class R, typename T>
struct Signature<R (const EvaluationContext&, const Varargs<T>&)> : SignatureBase {
using Args = std::vector<std::unique_ptr<Expression>>;
Signature(R (*evaluate_)(const EvaluationContext&, const Varargs<T>&), std::string name_) :
SignatureBase(
valueTypeToExpressionType<std::decay_t<typename R::Value>>(),
@@ -167,12 +184,8 @@ struct Signature<R (const EvaluationContext&, const Varargs<T>&)> : SignatureBas
),
evaluate(evaluate_)
{}
std::unique_ptr<Expression> makeExpression(std::vector<std::unique_ptr<Expression>> args) const override {
return std::make_unique<CompoundExpression<Signature>>(name, *this, std::move(args));
};
EvaluationResult apply(const EvaluationContext& evaluationParameters, const Args& args) const {
EvaluationResult apply(const EvaluationContext& evaluationParameters, const Args& args) const override {
Varargs<T> evaluated;
evaluated.reserve(args.size());
for (const auto& arg : args) {
@@ -184,7 +197,7 @@ struct Signature<R (const EvaluationContext&, const Varargs<T>&)> : SignatureBas
if (!value) return value.error();
return *value;
}
R (*evaluate)(const EvaluationContext&, const Varargs<T>&);
};
@@ -680,7 +693,7 @@ static ParseResult createCompoundExpression(const Definition& definition,
}
if (signatureContext.getErrors().size() == 0) {
return ParseResult(signature->makeExpression(std::move(args)));
return ParseResult(std::make_unique<CompoundExpression>(*signature, std::move(args)));
}
}
@@ -792,6 +805,48 @@ ParseResult createCompoundExpression(const std::string& name,
return createCompoundExpression(CompoundExpressionRegistry::definitions.at(name), std::move(args), ctx);
}
CompoundExpression::CompoundExpression(const detail::SignatureBase& signature_, std::vector<std::unique_ptr<Expression>> args_) :
Expression(Kind::CompoundExpression, signature_.result),
signature(signature_),
args(std::move(args_))
{}
std::string CompoundExpression::getOperator() const {
return signature.name;
}
EvaluationResult CompoundExpression::evaluate(const EvaluationContext& evaluationParams) const {
return signature.apply(evaluationParams, args);
}
optional<std::size_t> CompoundExpression::getParameterCount() const {
return signature.params.match(
[&](const VarargsType&) { return optional<std::size_t>(); },
[&](const std::vector<type::Type>& p) -> optional<std::size_t> { return p.size(); }
);
}
std::vector<optional<Value>> CompoundExpression::possibleOutputs() const {
return { nullopt };
}
void CompoundExpression::eachChild(const std::function<void(const Expression&)>& visit) const {
for (const std::unique_ptr<Expression>& e : args) {
visit(*e);
}
}
bool CompoundExpression::operator==(const Expression& e) const {
if (e.getKind() == Kind::CompoundExpression) {
auto rhs = static_cast<const CompoundExpression*>(&e);
return signature.name == rhs->signature.name &&
signature.result == rhs->signature.result &&
signature.params == rhs->signature.params &&
Expression::childrenEqual(args, rhs->args);
}
return false;
}
} // namespace expression
} // namespace style
} // namespace mbgl

View File

@@ -34,8 +34,8 @@ optional<variant<const Interpolate*, const Step*, ParsingError>> findZoomCurve(c
case Kind::Interpolate: {
auto curve = static_cast<const Interpolate*>(e);
if (curve->getInput()->getKind() == Kind::CompoundExpression) {
auto z = static_cast<CompoundExpressionBase*>(curve->getInput().get());
if (z && z->getName() == "zoom") {
auto z = static_cast<CompoundExpression*>(curve->getInput().get());
if (z && z->getOperator() == "zoom") {
result = {curve};
}
}
@@ -44,8 +44,8 @@ optional<variant<const Interpolate*, const Step*, ParsingError>> findZoomCurve(c
case Kind::Step: {
auto step = static_cast<const Step*>(e);
if (step->getInput()->getKind() == Kind::CompoundExpression) {
auto z = static_cast<CompoundExpressionBase*>(step->getInput().get());
if (z && z->getName() == "zoom") {
auto z = static_cast<CompoundExpression*>(step->getInput().get());
if (z && z->getOperator() == "zoom") {
result = {step};
}
}

View File

@@ -10,8 +10,8 @@ constexpr static const char filter[] = "filter-";
bool isFeatureConstant(const Expression& expression) {
if (expression.getKind() == Kind::CompoundExpression) {
auto e = static_cast<const CompoundExpressionBase*>(&expression);
const std::string name = e->getName();
auto e = static_cast<const CompoundExpression*>(&expression);
const std::string name = e->getOperator();
optional<std::size_t> parameterCount = e->getParameterCount();
if (name == "get" && parameterCount && *parameterCount == 1) {
return false;

View File

@@ -38,8 +38,8 @@ bool isConstant(const Expression& expression) {
}
if (expression.getKind() == Kind::CompoundExpression) {
auto compound = static_cast<const CompoundExpressionBase*>(&expression);
if (compound->getName() == "error") {
auto compound = static_cast<const CompoundExpression*>(&expression);
if (compound->getOperator() == "error") {
return false;
}
}