From 7e00bb9cb6ae6656465cf0c1394cb04b6cbf36bf Mon Sep 17 00:00:00 2001 From: Radoslaw Cybulski Date: Tue, 19 Dec 2017 14:38:51 +0100 Subject: [PATCH] Adds support for executing parsed batch test Change-Id: I59aa25a4142b1ed331db78a7136603ca8ea48973 --- src/batch/EvaluationContext.cpp | 216 +++++++++++ src/batch/EvaluationContext.hpp | 69 ++++ src/batch/EvaluationValue.cpp | 613 +++++++++++++++++++++++++++++++ src/batch/EvaluationValue.hpp | 372 +++++++++++++++++++ src/batch/Evaluator.cpp | 148 ++++++++ src/batch/Evaluator.hpp | 31 ++ tests/no-ui-scenarios/BatchExecTests.cpp | 59 +++ 7 files changed, 1508 insertions(+) create mode 100644 src/batch/EvaluationContext.cpp create mode 100644 src/batch/EvaluationContext.hpp create mode 100644 src/batch/EvaluationValue.cpp create mode 100644 src/batch/EvaluationValue.hpp diff --git a/src/batch/EvaluationContext.cpp b/src/batch/EvaluationContext.cpp new file mode 100644 index 0000000..0d7d95e --- /dev/null +++ b/src/batch/EvaluationContext.cpp @@ -0,0 +1,216 @@ +/* + * Copyright 2017 Samsung Electronics Co., Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "EvaluationContext.hpp" + +namespace std +{ + template <> struct hash> { + size_t operator()(std::pair p) const + { + return ((unsigned char)p.first << 8) | (unsigned char)p.second; + } + }; +} +struct EvaluationContext::GlobalContext { + ExecutorInterface &executionInterface; +}; + +EvaluationContext::EvaluationContext(ExecutorInterface &ei) +{ + initializeGlobalContext(ei); +} + +EvaluationContext::EvaluationContext(EvaluationContext &parent) : + parent(&parent) +{ + globalContext = this->parent->globalContext; +} + +void EvaluationContext::initializeGlobalContext(ExecutorInterface &ei) +{ + globalContext = std::make_shared(GlobalContext{ ei }); +} + +ExecutorInterface &EvaluationContext::executionInterface() const +{ + return globalContext->executionInterface; +} + +void EvaluationContext::setVariable(const std::string &ident, EvaluationValue val) +{ + variables[ident] = std::move(val); +} + +EvaluationValue EvaluationContext::getVariable(const Optional &loc, const std::string &ident) +{ + EvaluationContext *self = this; + while (self) { + auto it = variables.find(ident); + if (it != variables.end()) return it->second; + self = self->parent; + } + try { + return globalContext->executionInterface.getVariableByName(ident); + } catch (EvaluationFailure &e) { + if (loc) e.setLocationIfMissing(*loc); + throw; + } +} + +EvaluationValue EvaluationContext::callFunction(const Optional &loc, EvaluationValue function, std::vector values) +{ + Optional v; + + switch (function.kind()) { + case EvaluationValue::Kind::FUNCTION: + try { + return function.asFunctionType()(*this, std::move(values)); + } catch (EvaluationFailure &e) { + if (loc) e.setLocationIfMissing(*loc); + throw; + } + case EvaluationValue::Kind::STRING: + case EvaluationValue::Kind::INTEGER: + case EvaluationValue::Kind::DOUBLE: + case EvaluationValue::Kind::UIELEMENT: + case EvaluationValue::Kind::BOOLEAN: + case EvaluationValue::Kind::POINT: + case EvaluationValue::Kind::EMPTY: + case EvaluationValue::Kind::VECTOR: + case EvaluationValue::Kind::SET: + case EvaluationValue::Kind::DICT: + break; + } + throw EvaluationFailure{loc} << "value of kind " << EvaluationValue::toString(function.kind()) << + " is not callable"; +} + +EvaluationValue ExecutorInterface::getVariableByName(const std::string &name) +{ + throw EvaluationFailure{} << "unknown variable '" << name << "'"; +} + +EvaluationValue ExecutorInterface::setAttribute(const EvaluationValue &self, const std::string &name, EvaluationValue value) +{ + throw EvaluationFailure{} << "value of kind " << EvaluationValue::toString(self.kind()) << + " doesn't accept attribute '" << name << "'"; +} + +EvaluationValue ExecutorInterface::getAttribute(const EvaluationValue &self, const std::string &name) +{ + throw EvaluationFailure{} << "value of kind " << EvaluationValue::toString(self.kind()) << + " doesn't have attribute '" << name << "'"; +} + +std::shared_ptr ExecutorInterface::convert(Point pt) +{ + throw EvaluationFailure{} << "no at-spi object found at position " << pt.toString(); +} + +std::shared_ptr ExecutorInterface::convert(const std::string &txt) +{ + throw EvaluationFailure{} << "no at-spi object found with name '" << txt << "'"; +} + +EvaluationValue EvaluationContext::getAttribute(const Optional &loc, EvaluationValue self, const std::string &identifier) +{ + try { + return globalContext->executionInterface.getAttribute(self, identifier); + } catch (EvaluationFailure &e) { + if (loc) e.setLocationIfMissing(*loc); + throw; + } +} + +EvaluationValue EvaluationContext::setAttribute(const Optional &loc, EvaluationValue self, const std::string &identifier, EvaluationValue value) +{ + try { + return globalContext->executionInterface.setAttribute(self, identifier, std::move(value)); + } catch (EvaluationFailure &e) { + if (loc) e.setLocationIfMissing(*loc); + throw; + } +} + +EvaluationValue EvaluationContext::convert(const Optional &loc, const EvaluationValue &value, EvaluationValue::Kind targetType) +{ + if (value.kind() == targetType) return value; + + try { + switch (value.kind()) { + case EvaluationValue::Kind::FUNCTION: + case EvaluationValue::Kind::STRING: + return detail::ConvertTo::convert(*this, value); + case EvaluationValue::Kind::INTEGER: + return detail::ConvertTo::convert(*this, value); + case EvaluationValue::Kind::DOUBLE: + return detail::ConvertTo::convert(*this, value); + case EvaluationValue::Kind::UIELEMENT: + return detail::ConvertTo>::convert(*this, value); + case EvaluationValue::Kind::BOOLEAN: + return detail::ConvertTo::convert(*this, value); + case EvaluationValue::Kind::POINT: + return detail::ConvertTo::convert(*this, value); + case EvaluationValue::Kind::EMPTY: + return {}; + case EvaluationValue::Kind::VECTOR: + case EvaluationValue::Kind::SET: + case EvaluationValue::Kind::DICT: + break; + } + } catch (EvaluationFailure &e) { + if (loc) e.setLocationIfMissing(*loc); + throw; + } + throw EvaluationFailure{loc} << "can't convert from " << EvaluationValue::toString(value.kind()) << + " to " << EvaluationValue::toString(EvaluationValue::Kind::FUNCTION); +} + +EvaluationValue EvaluationContext::evaluateAccessGet(const Optional &loc, const std::vector &args) +{ + std::ostringstream tmp; + tmp << EvaluationValue::toString(args[0].kind()) << "["; + for (auto i = 1u; i < args.size(); ++i) { + if (i != 1) tmp << ", "; + tmp << EvaluationValue::toString(args[i].kind()); + } + tmp << "]"; + throw EvaluationFailure{loc} << "unable to evaluate " << tmp.str(); +} + +EvaluationValue EvaluationContext::evaluateAccessSet(const Optional &loc, std::vector args) +{ + std::ostringstream tmp; + tmp << EvaluationValue::toString(args[0].kind()) << "["; + for (auto i = 1; i < (int)args.size() - 1; ++i) { + if (i != 1) tmp << ", "; + tmp << EvaluationValue::toString(args[i].kind()); + } + tmp << "] = " << EvaluationValue::toString(args.back().kind()); + throw EvaluationFailure{loc} << "unable to evaluate " << tmp.str(); +} + + +EvaluationValue EvaluationContext::evaluateMinus(const Optional &loc, const EvaluationValue &self) +{ + throw EvaluationFailure{loc} << "unable to negate value of type " << EvaluationValue::toString(self.kind()); +} + +bool EvaluationContext::evaluateIn(const Optional &loc, const EvaluationValue &self, const EvaluationValue &value) +{ + throw EvaluationFailure{loc} << "value of type " << EvaluationValue::toString(self.kind()) << " is not a collection"; +} diff --git a/src/batch/EvaluationContext.hpp b/src/batch/EvaluationContext.hpp new file mode 100644 index 0000000..a21288c --- /dev/null +++ b/src/batch/EvaluationContext.hpp @@ -0,0 +1,69 @@ +/* + * Copyright 2017 Samsung Electronics Co., Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TEST_EVALUATION_CONTEXT +#define TEST_EVALUATION_CONTEXT + +#include "../Optional.hpp" +#include "EvaluationValue.hpp" +#include "Lexer.hpp" +#include +#include +#include +#include +#include + +class UIElement; + +struct ExecutorInterface { + virtual ~ExecutorInterface() = default; + + virtual EvaluationValue getVariableByName(const std::string &name); + virtual EvaluationValue setAttribute(const EvaluationValue &self, const std::string &name, EvaluationValue value); + virtual EvaluationValue getAttribute(const EvaluationValue &self, const std::string &name); + virtual std::shared_ptr convert(Point pt); + virtual std::shared_ptr convert(const std::string &); + virtual std::shared_ptr getVisibleRoot() = 0; + virtual std::ostream &outputStream() = 0; +}; + +class EvaluationContext +{ + struct GlobalContext; +public: + explicit EvaluationContext(ExecutorInterface &ei); + explicit EvaluationContext(EvaluationContext &parent); + + void setVariable(const std::string &ident, EvaluationValue val); + EvaluationValue getVariable(const Optional &loc, const std::string &ident); + EvaluationValue callFunction(const Optional &loc, EvaluationValue function, std::vector values); + EvaluationValue getAttribute(const Optional &loc, EvaluationValue self, const std::string &identifier); + EvaluationValue setAttribute(const Optional &loc, EvaluationValue self, const std::string &identifier, EvaluationValue value); + EvaluationValue convert(const Optional &loc, const EvaluationValue &value, EvaluationValue::Kind targetType); + EvaluationValue evaluateAccessGet(const Optional &loc, const std::vector &args); + EvaluationValue evaluateAccessSet(const Optional &loc, std::vector args); + bool evaluateIn(const Optional &loc, const EvaluationValue &self, const EvaluationValue &value); + EvaluationValue evaluateMinus(const Optional &loc, const EvaluationValue &self); + + ExecutorInterface &executionInterface() const; +private: + void initializeGlobalContext(ExecutorInterface &ei); + std::unordered_map variables; + EvaluationContext *parent = nullptr; + std::shared_ptr globalContext; +}; + +#endif diff --git a/src/batch/EvaluationValue.cpp b/src/batch/EvaluationValue.cpp new file mode 100644 index 0000000..9b893e5 --- /dev/null +++ b/src/batch/EvaluationValue.cpp @@ -0,0 +1,613 @@ +/* + * Copyright 2017 Samsung Electronics Co., Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "EvaluationValue.hpp" +#include "EvaluationContext.hpp" +#include "../UniversalSwitch.hpp" +#include + +EvaluationFailure::EvaluationFailure(Optional loc) : location_(std::move(loc)) +{ +} + +void EvaluationFailure::setLocationIfMissing(TokenLocation loc) +{ + if (!location_) location_ = std::move(loc); +} + +std::string EvaluationFailure::message() const +{ + return text->str(); +} + +bool EvaluationFailure::hasLocation() const +{ + return bool(location_); +} + +TokenLocation EvaluationFailure::location() +{ + ASSERT(hasLocation()); + return *location_; +} + +std::string EvaluationValue::toString(Kind k) +{ + switch (k) { + case Kind::EMPTY: + return "EMPTY"; + case Kind::STRING: + return "STRING"; + case Kind::INTEGER: + return "INTEGER"; + case Kind::DOUBLE: + return "DOUBLE"; + case Kind::UIELEMENT: + return "UIELEMENT"; + case Kind::BOOLEAN: + return "BOOLEAN"; + case Kind::POINT: + return "POINT"; + case Kind::FUNCTION: + return "FUNCTION"; + case Kind::VECTOR: + return "VECTOR"; + case Kind::SET: + return "SET"; + case Kind::DICT: + return "DICT"; + } + return ""; +} + +EvaluationValue::EvaluationValue() +{ +} + +EvaluationValue::Data::Data(int t) : integer(t) { } +EvaluationValue::Data::Data(double t) : double_(t) { } +EvaluationValue::Data::Data(bool t) : boolean(t) { } +EvaluationValue::Data::Data(std::string t) : string(std::move(t)) { } +EvaluationValue::Data::Data(std::shared_ptr t) : uiElement(std::move(t)) { } +EvaluationValue::Data::Data(Point t) : point(t) { } +EvaluationValue::Data::Data(FunctionType t) : function(std::move(t.function)) { } +EvaluationValue::Data::Data(std::shared_ptr t) : vector(std::move(t)) { } +EvaluationValue::Data::Data(std::shared_ptr t) : set(std::move(t)) { } +EvaluationValue::Data::Data(std::shared_ptr t) : dict(std::move(t)) { } + +EvaluationValue::EvaluationValue(std::nullptr_t) {} +EvaluationValue::EvaluationValue(int integer) : data(integer), kind_(Kind::INTEGER) { } +EvaluationValue::EvaluationValue(double double_) : data(double_), kind_(Kind::DOUBLE) { } +EvaluationValue::EvaluationValue(bool boolean) : data(boolean), kind_(Kind::BOOLEAN) { } +EvaluationValue::EvaluationValue(std::string string) : data(std::move(string)), kind_(Kind::STRING) { } +EvaluationValue::EvaluationValue(std::shared_ptr e) : data(std::move(e)), kind_(Kind::UIELEMENT) +{ + ASSERT(data.uiElement); +} +EvaluationValue::EvaluationValue(Point p) : data(p), kind_(Kind::POINT) { } +EvaluationValue::EvaluationValue(FunctionType f) : data(std::move(f)), kind_(Kind::FUNCTION) +{ + ASSERT(data.function); +} +EvaluationValue::EvaluationValue(FunctionType::Type f) : EvaluationValue(FunctionType{std::move(f)}) { } +EvaluationValue::EvaluationValue(VectorType f) : data(std::make_shared(std::move(f))), kind_(Kind::VECTOR) { } +EvaluationValue::EvaluationValue(SetType f) : data(std::make_shared(std::move(f))), kind_(Kind::SET) { } +EvaluationValue::EvaluationValue(DictType f) : data(std::make_shared(std::move(f))), kind_(Kind::DICT) { } + +size_t EvaluationValueKey::calculateHash(const EvaluationValue &self) +{ + switch (self.kind()) { + case EvaluationValue::Kind::STRING: + return std::hash()(self.asString()); + case EvaluationValue::Kind::INTEGER: + return std::hash()(self.asInteger()); + case EvaluationValue::Kind::DOUBLE: + return std::hash()(self.asDouble()); + case EvaluationValue::Kind::UIELEMENT: + return std::hash()(Atspi::getUniqueId(self.asUiElement()->getObject())); + case EvaluationValue::Kind::BOOLEAN: + return std::hash()(self.asBoolean()); + case EvaluationValue::Kind::POINT: + return std::hash()(self.asPoint().x) ^ std::hash()(self.asPoint().y); + case EvaluationValue::Kind::EMPTY: + return 0; + case EvaluationValue::Kind::FUNCTION: + case EvaluationValue::Kind::SET: + case EvaluationValue::Kind::VECTOR: + case EvaluationValue::Kind::DICT: + break; + } + throw EvaluationFailure{} << "can't calculate hash for value of type " << EvaluationValue::toString(self.kind()); +} + +EvaluationValueKey::EvaluationValueKey(EvaluationValue val) : + value(std::make_shared(std::move(val))) +{ + hash = calculateHash(getValue()); +} + +EvaluationValueKey::EvaluationValueKey(EvaluationValue val, size_t hash) : + value(std::make_shared(std::move(val))), hash(hash) +{} + +bool EvaluationValueKey::operator == (const EvaluationValueKey &k) const +{ + return getValue() == k.getValue(); +} + +const std::string &EvaluationValue::asString() const +{ + return data.string; +} +int EvaluationValue::asInteger() const +{ + return data.integer; +} +const std::shared_ptr &EvaluationValue::asUiElement() const +{ + return data.uiElement; +} +bool EvaluationValue::asBoolean() const +{ + return data.boolean; +} +Point EvaluationValue::asPoint() const +{ + return data.point; +} +double EvaluationValue::asDouble() const +{ + return data.double_; +} +const EvaluationValue::FunctionType::Type &EvaluationValue::asFunctionType() const +{ + return data.function; +} +EvaluationValue::VectorType &EvaluationValue::asVector() const +{ + return *data.vector; +} +EvaluationValue::SetType &EvaluationValue::asSet() const +{ + return *data.set; +} +EvaluationValue::DictType &EvaluationValue::asDict() const +{ + return *data.dict; +} + +std::ostream &operator << (std::ostream &s, const EvaluationValue &v) +{ + switch (v.kind()) { + case EvaluationValue::Kind::EMPTY: + s << "{}"; + break; + case EvaluationValue::Kind::STRING: + s << "'" << v.data.string << "'"; + break; + case EvaluationValue::Kind::INTEGER: + s << v.data.integer; + break; + case EvaluationValue::Kind::DOUBLE: + s << v.data.double_; + break; + case EvaluationValue::Kind::UIELEMENT: + s << Atspi::getUniqueId(v.data.uiElement->getObject()); + break; + case EvaluationValue::Kind::BOOLEAN: + s << (v.data.boolean ? "true" : "false"); + break; + case EvaluationValue::Kind::POINT: + s << "<" << v.data.point.x << ", " << v.data.point.y << ">"; + break; + case EvaluationValue::Kind::FUNCTION: + s << "functor"; + break; + case EvaluationValue::Kind::VECTOR: { + s << "["; + bool first = true; + for (auto &a : *v.data.vector) { + if (first) { + s << " "; + first = false; + } else { + s << ", "; + } + s << a; + } + s << " ]"; + break; + } + case EvaluationValue::Kind::SET: { + s << "{"; + bool first = true; + for (auto &a : *v.data.set) { + if (first) { + s << " "; + first = false; + } else { + s << ", "; + } + s << a; + } + s << " }"; + break; + } + case EvaluationValue::Kind::DICT: { + s << "{"; + bool first = true; + for (auto &a : *v.data.dict) { + if (first) { + s << " "; + first = false; + } else { + s << ", "; + } + s << "{ " << a.first << ": " << a.second << " }"; + } + s << " }"; + break; + } + } + return s; +} + +std::ostream &operator << (std::ostream &s, const EvaluationValueKey &v) +{ + return s << v.getValue(); +} + +EvaluationValue::EvaluationValue(const EvaluationValue &v) +{ + copyFrom(v); +} +EvaluationValue::EvaluationValue(EvaluationValue &&v) +{ + moveFrom(v); +} +EvaluationValue::~EvaluationValue() +{ + destroy(); +} +EvaluationValue &EvaluationValue::operator = (const EvaluationValue &v) +{ + copyFrom(v); + return *this; +} +EvaluationValue &EvaluationValue::operator = (EvaluationValue &&v) +{ + moveFrom(v); + return *this; +} + +EvaluationValue::Kind EvaluationValue::kind() const +{ + return kind_; +} + +void EvaluationValue::destroy() +{ + switch (kind()) { + case Kind::EMPTY: + break; + case Kind::STRING: + data.string = {}; + break; + case Kind::INTEGER: + break; + case Kind::DOUBLE: + break; + case Kind::UIELEMENT: + data.uiElement = {}; + break; + case Kind::BOOLEAN: + break; + case Kind::POINT: + break; + case Kind::FUNCTION: + data.function = {}; + break; + case Kind::VECTOR: + data.vector = {}; + break; + case Kind::SET: + data.set = {}; + break; + case Kind::DICT: + data.dict = {}; + break; + } + kind_ = Kind::EMPTY; +} + +void EvaluationValue::copyFrom(const EvaluationValue &src) +{ + if (this != &src) { + destroy(); + kind_ = src.kind(); + switch (kind()) { + case Kind::EMPTY: + break; + case Kind::STRING: + data.string = src.data.string; + break; + case Kind::INTEGER: + data.integer = src.data.integer; + break; + case Kind::DOUBLE: + data.double_ = src.data.double_; + break; + case Kind::UIELEMENT: + data.uiElement = src.data.uiElement; + break; + case Kind::BOOLEAN: + data.boolean = src.data.boolean; + break; + case Kind::POINT: + data.point = src.data.point; + break; + case Kind::FUNCTION: + data.function = src.data.function; + break; + case Kind::VECTOR: + data.vector = src.data.vector; + break; + case Kind::SET: + data.set = src.data.set; + break; + case Kind::DICT: + data.dict = src.data.dict; + break; + } + } +} + +void EvaluationValue::moveFrom(EvaluationValue &src) +{ + if (this != &src) { + destroy(); + kind_ = src.kind(); + switch (kind()) { + case Kind::EMPTY: + break; + case Kind::STRING: + data.string = std::move(src.data.string); + break; + case Kind::INTEGER: + data.integer = src.data.integer; + break; + case Kind::DOUBLE: + data.double_ = src.data.double_; + break; + case Kind::UIELEMENT: + data.uiElement = std::move(src.data.uiElement); + break; + case Kind::BOOLEAN: + data.boolean = src.data.boolean; + break; + case Kind::POINT: + data.point = src.data.point; + break; + case Kind::FUNCTION: + data.function = std::move(src.data.function); + break; + case Kind::VECTOR: + data.vector = std::move(src.data.vector); + break; + case Kind::SET: + data.set = std::move(src.data.set); + break; + case Kind::DICT: + data.dict = std::move(src.data.dict); + break; + } + } +} + +bool EvaluationValue::operator == (const EvaluationValue &other) const +{ + if (kind() != other.kind()) { + if (kind() == Kind::INTEGER && other.kind() == Kind::DOUBLE) + return static_cast(asInteger()) == other.asDouble(); + if (kind() == Kind::DOUBLE && other.kind() == Kind::INTEGER) + return static_cast(other.asInteger()) == asDouble(); + return false; + } + switch (kind()) { + case Kind::EMPTY: + return true; + case Kind::STRING: + return data.string == other.data.string; + case Kind::INTEGER: + return data.integer == other.data.integer; + case Kind::DOUBLE: + return data.double_ == other.data.double_; + case Kind::UIELEMENT: + return Atspi::getUniqueId(data.uiElement->getObject()) == Atspi::getUniqueId(other.data.uiElement->getObject()); + case Kind::BOOLEAN: + return data.boolean == other.data.boolean; + case Kind::POINT: + return data.point == other.data.point; + case Kind::VECTOR: + return *data.vector == *other.data.vector; + case Kind::SET: + return *data.set == *other.data.set; + case Kind::DICT: + return *data.dict == *other.data.dict; + case Kind::FUNCTION: + throw EvaluationFailure{} << "can't compare " << EvaluationValue::toString(kind()) << + " with " << EvaluationValue::toString(other.kind()); + }; + return false; +} + +bool EvaluationValue::operator < (const EvaluationValue &other) const +{ + if (kind() != other.kind()) { + if (kind() == Kind::INTEGER && other.kind() == Kind::DOUBLE) + return static_cast(asInteger()) < other.asDouble(); + if (kind() == Kind::DOUBLE && other.kind() == Kind::INTEGER) + return asDouble() < static_cast(other.asInteger()); + } else { + switch (kind()) { + case Kind::EMPTY: + break; + case Kind::STRING: + return data.string < other.data.string; + case Kind::INTEGER: + return data.integer < other.data.integer; + case Kind::DOUBLE: + return data.double_ < other.data.double_; + case Kind::UIELEMENT: + break; + case Kind::BOOLEAN: + break; + case Kind::POINT: + break; + case Kind::FUNCTION: + break; + case Kind::VECTOR: + for (auto i = 0u; i < std::min(asVector().size(), other.asVector().size()); ++i) { + if (asVector()[i] < other.asVector()[i]) return true; + if (!(asVector()[i] == other.asVector()[i])) return false; + } + return asVector().size() < other.asVector().size(); + case Kind::SET: + break; + case Kind::DICT: + break; + }; + } + throw EvaluationFailure{} << "can't order values of type " << EvaluationValue::toString(kind()) << + " and " << EvaluationValue::toString(other.kind()); +} + +EvaluationValue::operator bool () const +{ + switch (kind()) { + case EvaluationValue::Kind::INTEGER: + return asInteger() != 0; + case EvaluationValue::Kind::DOUBLE: + return asDouble() != 0; + case EvaluationValue::Kind::BOOLEAN: + return asBoolean(); + case EvaluationValue::Kind::STRING: + return !asString().empty(); + case EvaluationValue::Kind::UIELEMENT: + return true; + case EvaluationValue::Kind::FUNCTION: + return true; + case EvaluationValue::Kind::POINT: + return true; + case EvaluationValue::Kind::VECTOR: + return !asVector().empty(); + case EvaluationValue::Kind::SET: + return !asSet().empty(); + case EvaluationValue::Kind::DICT: + return !asDict().empty(); + case EvaluationValue::Kind::EMPTY: + return false; + } + ASSERT(0); // every type has a true / false value in boolean context + return false; +} + +bool EvaluationValue::operator != (const EvaluationValue &other) const +{ + return !(*this == other); +} + +bool EvaluationValue::operator > (const EvaluationValue &other) const +{ + return (other < *this); +} + +bool EvaluationValue::operator <= (const EvaluationValue &other) const +{ + return !(other < *this); +} + +bool EvaluationValue::operator >= (const EvaluationValue &other) const +{ + return !(*this < other); +} + +namespace detail +{ + int ConvertTo::convert(EvaluationContext &ec, EvaluationValue e) + { + if (e.kind() == EvaluationValue::Kind::INTEGER) return e.asInteger(); + if (e.kind() == EvaluationValue::Kind::DOUBLE) { + if (static_cast(static_cast(e.asDouble())) == e.asDouble()) { + return static_cast(e.asDouble()); + } + } else if (e.kind() == EvaluationValue::Kind::STRING) { + auto text = e.asString(); + size_t pos = 0; + auto value = std::stoi(text, &pos); + if (pos == text.size()) + return value; + } + throw EvaluationFailure{} << "can't convert from " << EvaluationValue::toString(e.kind()) << + " to " << EvaluationValue::toString(EvaluationValue::Kind::INTEGER); + } + double ConvertTo::convert(EvaluationContext &ec, EvaluationValue e) + { + if (e.kind() == EvaluationValue::Kind::INTEGER) return e.asInteger(); + if (e.kind() == EvaluationValue::Kind::DOUBLE) return e.asDouble(); + if (e.kind() == EvaluationValue::Kind::STRING) { + auto text = e.asString(); + size_t pos = 0; + auto value = std::stod(text, &pos); + if (pos == text.size()) + return value; + } + throw EvaluationFailure{} << "can't convert from " << EvaluationValue::toString(e.kind()) << + " to " << EvaluationValue::toString(EvaluationValue::Kind::DOUBLE); + } + std::string ConvertTo::convert(EvaluationContext &ec, EvaluationValue e) + { + if (e.kind() == EvaluationValue::Kind::INTEGER) return std::to_string(e.asInteger()); + if (e.kind() == EvaluationValue::Kind::BOOLEAN) return e.asBoolean() ? "true" : "false"; + if (e.kind() == EvaluationValue::Kind::STRING) return e.asString(); + if (e.kind() == EvaluationValue::Kind::POINT) + return "<" + std::to_string(e.asPoint().x) + ", " + std::to_string(e.asPoint().y) + ">"; + throw EvaluationFailure{} << "can't convert from " << EvaluationValue::toString(e.kind()) << + " to " << EvaluationValue::toString(EvaluationValue::Kind::STRING); + } + bool ConvertTo::convert(EvaluationContext &ec, EvaluationValue e) + { + return bool(e); + } + std::shared_ptr ConvertTo>::convert(EvaluationContext &ec, EvaluationValue e) + { + if (e.kind() == EvaluationValue::Kind::UIELEMENT) return std::move(e.asUiElement()); + if (e.kind() == EvaluationValue::Kind::POINT) return ec.executionInterface().convert(e.asPoint()); + if (e.kind() == EvaluationValue::Kind::STRING) return ec.executionInterface().convert(e.asString()); + throw EvaluationFailure{} << "can't convert from " << EvaluationValue::toString(e.kind()) << + " to " << EvaluationValue::toString(EvaluationValue::Kind::UIELEMENT); + } + EvaluationValue ConvertTo::convert(EvaluationContext &ec, EvaluationValue e) + { + return std::move(e); + } + Point ConvertTo::convert(EvaluationContext &ec, EvaluationValue e) + { + if (e.kind() == EvaluationValue::Kind::POINT) return e.asPoint(); + throw EvaluationFailure{} << "can't convert from " << EvaluationValue::toString(e.kind()) << + " to " << EvaluationValue::toString(EvaluationValue::Kind::POINT); + } +}; diff --git a/src/batch/EvaluationValue.hpp b/src/batch/EvaluationValue.hpp new file mode 100644 index 0000000..e46406a --- /dev/null +++ b/src/batch/EvaluationValue.hpp @@ -0,0 +1,372 @@ +/* + * Copyright 2017 Samsung Electronics Co., Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TEST_EVALUATION_VALUE +#define TEST_EVALUATION_VALUE + +#include "Lexer.hpp" + +#include "../Atspi.hpp" +#include "../UIElement.hpp" +#include "../Geometry.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class EvaluationValue; +class EvaluationContext; + +class EvaluationFailure +{ +public: + EvaluationFailure() = default; + EvaluationFailure(Optional); + + void setLocationIfMissing(TokenLocation); + std::string message() const; + bool hasLocation() const; + TokenLocation location(); + template EvaluationFailure &operator << (T &&t) + { + (*text) << std::forward(t); + return *this; + } +private: + std::shared_ptr text = std::make_shared(); + Optional location_; +}; + +namespace detail +{ + // TODO: check licence + template struct sequence {}; + template struct sequence_gen : sequence_gen < N - 1, N - 1, S... > {}; + template struct sequence_gen<0, S...> { + typedef sequence type; + }; + template struct apply_helper { + const std::function &c; + A &a; + const std::tuple &args; + + template auto apply_2(sequence) + { + return c(a, std::get(args)...); + } + auto apply_1() + { + return apply_2(typename sequence_gen::type()); + } + }; + template auto apply(const std::function &c, A &&a, const std::tuple &args) + { + apply_helper ah { c, std::forward(a), args }; + return ah.apply_1(); + } + template struct ConvertTo; + template <> struct ConvertTo { + static int convert(EvaluationContext &ec, EvaluationValue e); + }; + template <> struct ConvertTo { + static double convert(EvaluationContext &ec, EvaluationValue e); + }; + template <> struct ConvertTo { + static std::string convert(EvaluationContext &ec, EvaluationValue e); + }; + template <> struct ConvertTo { + static bool convert(EvaluationContext &ec, EvaluationValue e); + }; + template <> struct ConvertTo> { + static std::shared_ptr convert(EvaluationContext &ec, EvaluationValue e); + }; + template <> struct ConvertTo { + static EvaluationValue convert(EvaluationContext &ec, EvaluationValue e); + }; + template <> struct ConvertTo { + static Point convert(EvaluationContext &ec, EvaluationValue e); + }; + template struct ConvertTo> { + static std::vector convert(EvaluationContext &ec, EvaluationValue e); + }; + template struct ConvertTo> { + static std::unordered_set convert(EvaluationContext &ec, EvaluationValue e); + }; + template struct ConvertTo> { + static std::unordered_map convert(EvaluationContext &ec, EvaluationValue e); + }; + template struct Converter { + template static void convert(EvaluationContext &ce, + std::tuple &dst, std::vector &sourceArgs) + { + using T = typename std::tuple_element>::type; + try { + std::get(dst) = ConvertTo::type>::convert(ce, std::move(sourceArgs[I])); + } catch (EvaluationFailure &e) { + e << ", when converting " << (I + 1) << " argument"; + throw; + } + Converter < I + 1, S >::convert(ce, dst, sourceArgs); + } + }; + template struct Converter { + template static void convert(EvaluationContext &, std::tuple &, std::vector &) + { + } + }; +} + +class EvaluationValue; +class EvaluationValueKey +{ +public: + EvaluationValueKey() = default; + EvaluationValueKey(EvaluationValue val); + EvaluationValueKey(EvaluationValue val, size_t hash); + + const EvaluationValue &getValue() const + { + return *value; + } + operator const EvaluationValue &() const + { + return *value; + } + + size_t getHash() const + { + return hash; + } + + bool operator == (const EvaluationValueKey &k) const; + + friend std::ostream &operator << (std::ostream &s, const EvaluationValue &v); + + static size_t calculateHash(const EvaluationValue &v); +private: + std::shared_ptr value; + size_t hash = 0; +}; + +namespace std +{ + template <> struct hash { + size_t operator()(const EvaluationValueKey &v) const + { + return v.getHash(); + } + }; +} + +class EvaluationValue +{ +public: + class FunctionType + { + public: + using Type = std::function)>; + + FunctionType() = default; + FunctionType(Type t) : function(std::move(t)) { } + + explicit operator bool () const + { + return bool(function); + } + auto operator()(EvaluationContext &ec, std::vector args) const + { + return function(ec, std::move(args)); + } + Type function; + }; + template class UserFunctionType : public FunctionType + { + public: + UserFunctionType(std::function f) : + FunctionType(constructConvertingArgsFunction(std::move(f))) { } + private: + static Type constructConvertingArgsFunction(std::function f) + { + return [f = std::move(f)](EvaluationContext & ec, std::vector sourceArgs) -> EvaluationValue { + std::tuple args; + detail::Converter<0, sizeof...(ARGS)>::convert(ec, args, sourceArgs); + return detail::apply(f, ec, args); + }; + } + }; + using VectorType = std::vector; + using SetType = std::unordered_set; + using DictType = std::unordered_map; + + enum class Kind : unsigned char { + EMPTY = 0, + STRING, INTEGER, DOUBLE, UIELEMENT, BOOLEAN, POINT, FUNCTION, VECTOR, SET, DICT + }; + + static std::string toString(Kind k); + + EvaluationValue(); + EvaluationValue(std::nullptr_t); + EvaluationValue(const EvaluationValue &); + EvaluationValue(EvaluationValue &&); + EvaluationValue(int integer); + EvaluationValue(bool boolean); + EvaluationValue(double double_); + EvaluationValue(std::string); + EvaluationValue(std::shared_ptr); + EvaluationValue(Point); + EvaluationValue(VectorType); + EvaluationValue(SetType); + EvaluationValue(DictType); + EvaluationValue(FunctionType); + EvaluationValue(FunctionType::Type); + template EvaluationValue(const std::vector &v) : EvaluationValue(VectorType{ v.begin(), v.end() }) { } + template EvaluationValue(const std::unordered_set &v) : EvaluationValue(SetType{ v.begin(), v.end() }) { } + template EvaluationValue(const std::unordered_map &v) : EvaluationValue(DictType{ v.begin(), v.end() }) { } + ~EvaluationValue(); + + EvaluationValue &operator = (const EvaluationValue &); + EvaluationValue &operator = (EvaluationValue &&); + + Kind kind() const; + const std::string &asString() const; + int asInteger() const; + const std::shared_ptr &asUiElement() const; + bool asBoolean() const; + Point asPoint() const; + double asDouble() const; + const FunctionType::Type &asFunctionType() const; + VectorType &asVector() const; + SetType &asSet() const; + DictType &asDict() const; + + bool operator == (const EvaluationValue &other) const; + bool operator < (const EvaluationValue &other) const; + bool operator != (const EvaluationValue &other) const; + bool operator > (const EvaluationValue &other) const; + bool operator <= (const EvaluationValue &other) const; + bool operator >= (const EvaluationValue &other) const; + + explicit operator bool () const; + + friend std::ostream &operator << (std::ostream &s, const EvaluationValue &v); +private: + void destroy(); + void copyFrom(const EvaluationValue &src); + void moveFrom(EvaluationValue &src); + + // TODO: turn it into c++17 std::variant, when we upgrade to c++17 + // although current version ain't optimal, it's not big of a deal + // as it's only used for testing and any DBUS call will be + // order of magnitude slower + struct Data { + std::string string; + int integer = 0; + std::shared_ptr uiElement; + bool boolean = false; + double double_ = 0; + Point point; + FunctionType::Type function; + std::shared_ptr vector; + std::shared_ptr set; + std::shared_ptr dict; + + Data() = default; + Data(int integer); + Data(bool boolean); + Data(std::string); + Data(double); + Data(std::shared_ptr); + Data(Point); + Data(FunctionType); + Data(std::shared_ptr); + Data(std::shared_ptr); + Data(std::shared_ptr); + } data; + Kind kind_ = Kind::EMPTY; +}; + +namespace detail +{ + template std::vector ConvertTo>::convert(EvaluationContext &ec, EvaluationValue e) + { + std::vector result; + + if (e.kind() == EvaluationValue::Kind::VECTOR) { + result.reserve(e.asVector().size()); + for (auto &e : e.asVector()) { + result.push_back(ConvertTo::convert(ec, e)); + } + } else if (e.kind() == EvaluationValue::Kind::SET) { + result.reserve(e.asSet().size()); + for (auto &e : e.asSet()) { + result.push_back(ConvertTo::convert(ec, e)); + } + } else if (e.kind() == EvaluationValue::Kind::DICT) { + result.reserve(e.asDict().size()); + for (auto &e : e.asDict()) { + result.push_back(ConvertTo::convert(ec, e.first)); + } + } else throw EvaluationFailure{} << "can't convert from " << EvaluationValue::toString(e.kind()) << + " to " << EvaluationValue::toString(EvaluationValue::Kind::VECTOR); + return std::move(result); + } + template std::unordered_set ConvertTo>::convert(EvaluationContext &ec, EvaluationValue e) + { + std::unordered_set result; + + if (e.kind() == EvaluationValue::Kind::VECTOR) { + result.reserve(e.asVector().size()); + for (auto &e : e.asVector()) { + result.insert(ConvertTo::convert(ec, e)); + } + } else if (e.kind() == EvaluationValue::Kind::SET) { + result.reserve(e.asSet().size()); + for (auto &e : e.asSet()) { + result.insert(ConvertTo::convert(ec, e)); + } + } else if (e.kind() == EvaluationValue::Kind::DICT) { + result.reserve(e.asDict().size()); + for (auto &e : e.asDict()) { + result.insert(ConvertTo::convert(ec, e.first)); + } + } else throw EvaluationFailure{} << "can't convert from " << EvaluationValue::toString(e.kind()) << + " to " << EvaluationValue::toString(EvaluationValue::Kind::VECTOR); + return std::move(result); + } + template std::unordered_map ConvertTo>::convert(EvaluationContext &ec, EvaluationValue e) + { + std::unordered_map result; + + if (e.kind() == EvaluationValue::Kind::DICT) { + result.reserve(e.asDict().size()); + for (auto &e : e.asDict()) { + auto k = ConvertTo::convert(ec, e.first); + auto v = ConvertTo::convert(ec, e.second); + result.insert({ k, v }); + } + } else throw EvaluationFailure{} << "can't convert from " << EvaluationValue::toString(e.kind()) << + " to " << EvaluationValue::toString(EvaluationValue::Kind::VECTOR); + return std::move(result); + } +} + +#endif diff --git a/src/batch/Evaluator.cpp b/src/batch/Evaluator.cpp index ca3b994..cfc9c22 100644 --- a/src/batch/Evaluator.cpp +++ b/src/batch/Evaluator.cpp @@ -15,6 +15,8 @@ */ #include "Evaluator.hpp" +#include "EvaluationValue.hpp" +#include "EvaluationContext.hpp" #include "Lexer.hpp" #include "../UniversalSwitchLog.hpp" @@ -30,6 +32,11 @@ TokenLocation Evaluator::location() const IdentifierEvaluator::IdentifierEvaluator(TokenLocation tokenLocation, std::string identifier) : ExpressionEvaluator(std::move(tokenLocation)), identifier(std::move(identifier)) { } +EvaluationValue IdentifierEvaluator::evaluate(EvaluationContext &ec) const +{ + return ec.getVariable(location(), identifier); +} + static std::ostringstream &printLocationAndIndent(std::ostringstream &os, const TokenLocation &loc, unsigned int depth) { std::string indent(static_cast(depth), ' '); @@ -45,6 +52,11 @@ void IdentifierEvaluator::printSelfInfo(std::ostringstream &os, unsigned int dep AttributeEvaluator::AttributeEvaluator(TokenLocation tokenLocation, ExprPtr self, std::string identifier) : ExpressionEvaluator(std::move(tokenLocation)), self(std::move(self)), identifier(std::move(identifier)) { } +EvaluationValue AttributeEvaluator::evaluate(EvaluationContext &ec) const +{ + return ec.getAttribute(location(), self->evaluate(ec), identifier); +} + void AttributeEvaluator::printSelfInfo(std::ostringstream &os, unsigned int depth) const { printLocationAndIndent(os, location(), depth) << "attribute " << identifier << "\n"; @@ -56,6 +68,11 @@ void AttributeEvaluator::printSelfInfo(std::ostringstream &os, unsigned int dept AttributeSetterEvaluator::AttributeSetterEvaluator(TokenLocation tokenLocation, ExprPtr self, std::string identifier, ExprPtr value) : ExpressionEvaluator(std::move(tokenLocation)), self(std::move(self)), value(std::move(value)), identifier(std::move(identifier)) { } +EvaluationValue AttributeSetterEvaluator::evaluate(EvaluationContext &ec) const +{ + return ec.setAttribute(location(), self->evaluate(ec), identifier, value->evaluate(ec)); +} + void AttributeSetterEvaluator::printSelfInfo(std::ostringstream &os, unsigned int depth) const { printLocationAndIndent(os, location(), depth) << "attribute setter " << identifier << "\n"; @@ -69,6 +86,13 @@ void AttributeSetterEvaluator::printSelfInfo(std::ostringstream &os, unsigned in SetterEvaluator::SetterEvaluator(TokenLocation tokenLocation, std::string identifier, ExprPtr value) : ExpressionEvaluator(std::move(tokenLocation)), value(std::move(value)), identifier(std::move(identifier)) { } +EvaluationValue SetterEvaluator::evaluate(EvaluationContext &ec) const +{ + auto v = value->evaluate(ec); + ec.setVariable(identifier, v); + return v; +} + void SetterEvaluator::printSelfInfo(std::ostringstream &os, unsigned int depth) const { printLocationAndIndent(os, location(), depth) << "setter " << identifier << "\n"; @@ -80,6 +104,13 @@ void SetterEvaluator::printSelfInfo(std::ostringstream &os, unsigned int depth) PointEvaluator::PointEvaluator(TokenLocation tokenLocation, ExprPtr x, ExprPtr y) : ExpressionEvaluator(std::move(tokenLocation)), x(std::move(x)), y(std::move(y)) { } +EvaluationValue PointEvaluator::evaluate(EvaluationContext &ec) const +{ + auto xInt = detail::ConvertTo::convert(ec, x->evaluate(ec)); + auto yInt = detail::ConvertTo::convert(ec, y->evaluate(ec)); + return Point{ xInt, yInt }; +} + void PointEvaluator::printSelfInfo(std::ostringstream &os, unsigned int depth) const { printLocationAndIndent(os, location(), depth) << "point\n"; @@ -93,6 +124,11 @@ void PointEvaluator::printSelfInfo(std::ostringstream &os, unsigned int depth) c IntegerEvaluator::IntegerEvaluator(TokenLocation tokenLocation, int value) : ExpressionEvaluator(std::move(tokenLocation)), value(value) { } +EvaluationValue IntegerEvaluator::evaluate(EvaluationContext &ec) const +{ + return value; +} + void IntegerEvaluator::printSelfInfo(std::ostringstream &os, unsigned int depth) const { printLocationAndIndent(os, location(), depth) << "integer " << value << "\n"; @@ -102,6 +138,11 @@ void IntegerEvaluator::printSelfInfo(std::ostringstream &os, unsigned int depth) DoubleEvaluator::DoubleEvaluator(TokenLocation tokenLocation, double value) : ExpressionEvaluator(std::move(tokenLocation)), value(value) { } +EvaluationValue DoubleEvaluator::evaluate(EvaluationContext &ec) const +{ + return value; +} + void DoubleEvaluator::printSelfInfo(std::ostringstream &os, unsigned int depth) const { printLocationAndIndent(os, location(), depth) << "double " << value << "\n"; @@ -111,6 +152,11 @@ void DoubleEvaluator::printSelfInfo(std::ostringstream &os, unsigned int depth) StringEvaluator::StringEvaluator(TokenLocation tokenLocation, std::string value) : ExpressionEvaluator(std::move(tokenLocation)), value(std::move(value)) { } +EvaluationValue StringEvaluator::evaluate(EvaluationContext &ec) const +{ + return EvaluationValue{ value }; +} + void StringEvaluator::printSelfInfo(std::ostringstream &os, unsigned int depth) const { printLocationAndIndent(os, location(), depth) << "string '" << value << "'\n"; @@ -120,6 +166,31 @@ void StringEvaluator::printSelfInfo(std::ostringstream &os, unsigned int depth) ArraySetDictEvaluator::ArraySetDictEvaluator(TokenLocation tokenLocation, ExprPtrs values, Kind kind) : ExpressionEvaluator(std::move(tokenLocation)), values(std::move(values)), kind(kind) { } +EvaluationValue ArraySetDictEvaluator::evaluate(EvaluationContext &ec) const +{ + std::vector vals; + for (auto &v : values) + vals.push_back(v->evaluate(ec)); + + if (kind == Kind::VECTOR) + return { std::move(vals) }; + + if (kind == Kind::SET) { + EvaluationValue::SetType tmp; + for (auto &v : vals) { + tmp.insert(std::move(v)); + } + return std::move(tmp); + } + + ASSERT(kind == Kind::DICT); + EvaluationValue::DictType tmp; + for (auto i = 0u; i < vals.size(); i += 2) { + tmp.insert({ std::move(vals[i]), std::move(vals[i + 1]) }); + } + return std::move(tmp); +} + void ArraySetDictEvaluator::printSelfInfo(std::ostringstream &os, unsigned int depth) const { printLocationAndIndent(os, location(), depth); @@ -153,6 +224,28 @@ void ArraySetDictEvaluator::printSelfInfo(std::ostringstream &os, unsigned int d OperatorEvaluator::OperatorEvaluator(TokenLocation tokenLocation, ExprPtrs args, Kind kind) : ExpressionEvaluator(std::move(tokenLocation)), args(std::move(args)), kind(kind) { } +EvaluationValue OperatorEvaluator::evaluate(EvaluationContext &ec) const +{ + std::vector vals; + for (auto &a : args) + vals.push_back(a->evaluate(ec)); + + switch (kind) { + case Kind::IN: + return ec.evaluateIn(location(), vals[1], vals[0]); + case Kind::NOT: + return !bool(vals[0]); + case Kind::MINUS: + return ec.evaluateMinus(location(), vals[0]); + case Kind::GET: + return ec.evaluateAccessGet(location(), vals); + case Kind::SET: + return ec.evaluateAccessSet(location(), std::move(vals)); + } + ASSERT(0); + throw EvaluationFailure{} << "unknown operator"; +} + void OperatorEvaluator::printSelfInfo(std::ostringstream &os, unsigned int depth) const { printLocationAndIndent(os, location(), depth) << "operator "; @@ -187,6 +280,34 @@ void OperatorEvaluator::printSelfInfo(std::ostringstream &os, unsigned int depth CompOperatorEvaluator::CompOperatorEvaluator(TokenLocation tokenLocation, ExprPtrs args, std::vector kinds, std::vector locations) : ExpressionEvaluator(std::move(tokenLocation)), args(std::move(args)), kinds(kinds), locations(std::move(locations)) { } +EvaluationValue CompOperatorEvaluator::evaluate(EvaluationContext &ec) const +{ + auto cmp = [](const EvaluationValue & l, const EvaluationValue & r, Kind kind) { + switch (kind) { + case Kind::EQ: + return l == r; + case Kind::NE: + return l != r; + case Kind::LT: + return l < r; + case Kind::GT: + return l > r; + case Kind::LE: + return l <= r; + case Kind::GE: + return l >= r; + } + return false; + }; + auto l = args[0]->evaluate(ec); + for (auto i = 0u; i < kinds.size(); ++i) { + auto r = args[i + 1]->evaluate(ec); + if (!cmp(l, r, kinds[i])) return false; + l = std::move(r); + } + return true; +} + void CompOperatorEvaluator::printSelfInfo(std::ostringstream &os, unsigned int depth) const { printLocationAndIndent(os, location(), depth) << "operators"; @@ -231,6 +352,11 @@ void CompOperatorEvaluator::printSelfInfo(std::ostringstream &os, unsigned int d BooleanEvaluator::BooleanEvaluator(TokenLocation tokenLocation, bool value) : ExpressionEvaluator(std::move(tokenLocation)), value(std::move(value)) { } +EvaluationValue BooleanEvaluator::evaluate(EvaluationContext &ec) const +{ + return value; +} + void BooleanEvaluator::printSelfInfo(std::ostringstream &os, unsigned int depth) const { printLocationAndIndent(os, location(), depth) << "boolean " << (value ? "true" : "false") << "\n"; @@ -240,6 +366,16 @@ void BooleanEvaluator::printSelfInfo(std::ostringstream &os, unsigned int depth) CallEvaluator::CallEvaluator(TokenLocation tokenLocation, ExprPtr function, ExprPtrs args) : ExpressionEvaluator(tokenLocation), function(std::move(function)), args(std::move(args)) { } +EvaluationValue CallEvaluator::evaluate(EvaluationContext &ec) const +{ + std::vector values; + values.reserve(args.size()); + for (auto &arg : args) { + values.push_back(arg->evaluate(ec)); + } + return ec.callFunction(location(), function->evaluate(ec), std::move(values)); +} + void CallEvaluator::printSelfInfo(std::ostringstream &os, unsigned int depth) const { printLocationAndIndent(os, location(), depth) << "call\n"; @@ -255,6 +391,11 @@ void CallEvaluator::printSelfInfo(std::ostringstream &os, unsigned int depth) co ExpressionAsStatementEvaluator::ExpressionAsStatementEvaluator(TokenLocation tokenLocation, ExprPtr expr) : StatementEvaluator(tokenLocation), expr(std::move(expr)) { } +void ExpressionAsStatementEvaluator::evaluate(EvaluationContext &ec) const +{ + expr->evaluate(ec); +} + void ExpressionAsStatementEvaluator::printSelfInfo(std::ostringstream &os, unsigned int depth) const { expr->printSelfInfo(os, depth); @@ -265,6 +406,13 @@ void ExpressionAsStatementEvaluator::printSelfInfo(std::ostringstream &os, unsig BlockEvaluator::BlockEvaluator(TokenLocation tokenLocation, StatPtrs evals) : StatementEvaluator(tokenLocation), evals(std::move(evals)) { } +void BlockEvaluator::evaluate(EvaluationContext &parentEc) const +{ + EvaluationContext ec { parentEc }; + for (auto &b : evals) + b->evaluate(ec); +} + void BlockEvaluator::printSelfInfo(std::ostringstream &os, unsigned int depth) const { printLocationAndIndent(os, location(), depth) << "block\n"; diff --git a/src/batch/Evaluator.hpp b/src/batch/Evaluator.hpp index 5ef120a..ad3c9c9 100644 --- a/src/batch/Evaluator.hpp +++ b/src/batch/Evaluator.hpp @@ -59,6 +59,14 @@ class ExpressionEvaluator : public Evaluator { public: using Evaluator::Evaluator; + + /** + * @brief Virtual evaluation function. + * + * Calling this function will evaluate given expression, including it's all children, + * and return produced value. Might possibly raise an EvaluationFailure exception. + */ + virtual EvaluationValue evaluate(EvaluationContext &ec) const = 0; }; /** @@ -68,6 +76,14 @@ class StatementEvaluator : public Evaluator { public: using Evaluator::Evaluator; + + /** + * @brief Virtual evaluation function. + * + * Calling this function will evaluate given statement, including it's all children. + * Might possibly raise an EvaluationFailure exception. + */ + virtual void evaluate(EvaluationContext &ec) const = 0; }; using ExprPtr = std::shared_ptr; @@ -91,6 +107,7 @@ class IdentifierEvaluator : public ExpressionEvaluator public: IdentifierEvaluator(TokenLocation location_, std::string identifier); + EvaluationValue evaluate(EvaluationContext &ec) const override; void printSelfInfo(std::ostringstream &os, unsigned int depth) const override; }; @@ -108,6 +125,7 @@ class AttributeEvaluator : public ExpressionEvaluator public: AttributeEvaluator(TokenLocation location_, ExprPtr self, std::string identifier); + EvaluationValue evaluate(EvaluationContext &ec) const override; void printSelfInfo(std::ostringstream &os, unsigned int depth) const override; }; @@ -125,6 +143,7 @@ class AttributeSetterEvaluator : public ExpressionEvaluator public: AttributeSetterEvaluator(TokenLocation location_, ExprPtr self, std::string identifier, ExprPtr value); + EvaluationValue evaluate(EvaluationContext &ec) const override; void printSelfInfo(std::ostringstream &os, unsigned int depth) const override; }; @@ -141,6 +160,7 @@ class SetterEvaluator : public ExpressionEvaluator public: SetterEvaluator(TokenLocation location_, std::string identifier, ExprPtr value); + EvaluationValue evaluate(EvaluationContext &ec) const override; void printSelfInfo(std::ostringstream &os, unsigned int depth) const override; }; @@ -156,6 +176,7 @@ class PointEvaluator : public ExpressionEvaluator public: PointEvaluator(TokenLocation location_, ExprPtr x, ExprPtr y); + EvaluationValue evaluate(EvaluationContext &ec) const override; void printSelfInfo(std::ostringstream &os, unsigned int depth) const override; }; @@ -168,6 +189,7 @@ class IntegerEvaluator : public ExpressionEvaluator public: IntegerEvaluator(TokenLocation location_, int value); + EvaluationValue evaluate(EvaluationContext &ec) const override; void printSelfInfo(std::ostringstream &os, unsigned int depth) const override; }; @@ -180,6 +202,7 @@ class DoubleEvaluator : public ExpressionEvaluator public: DoubleEvaluator(TokenLocation location_, double value); + EvaluationValue evaluate(EvaluationContext &ec) const override; void printSelfInfo(std::ostringstream &os, unsigned int depth) const override; }; @@ -192,6 +215,7 @@ class BooleanEvaluator : public ExpressionEvaluator public: BooleanEvaluator(TokenLocation location_, bool value); + EvaluationValue evaluate(EvaluationContext &ec) const override; void printSelfInfo(std::ostringstream &os, unsigned int depth) const override; }; @@ -204,6 +228,7 @@ class StringEvaluator : public ExpressionEvaluator public: StringEvaluator(TokenLocation location_, std::string value); + EvaluationValue evaluate(EvaluationContext &ec) const override; void printSelfInfo(std::ostringstream &os, unsigned int depth) const override; }; @@ -222,6 +247,7 @@ public: }; ArraySetDictEvaluator(TokenLocation location_, ExprPtrs values, Kind kind); + EvaluationValue evaluate(EvaluationContext &ec) const override; void printSelfInfo(std::ostringstream &os, unsigned int depth) const override; private: ExprPtrs values; @@ -242,6 +268,7 @@ class CallEvaluator : public ExpressionEvaluator public: CallEvaluator(TokenLocation location_, ExprPtr function, ExprPtrs args); + EvaluationValue evaluate(EvaluationContext &ec) const override; void printSelfInfo(std::ostringstream &os, unsigned int depth) const override; }; @@ -260,6 +287,7 @@ public: }; OperatorEvaluator(TokenLocation location_, ExprPtrs args, Kind kind); + EvaluationValue evaluate(EvaluationContext &ec) const override; void printSelfInfo(std::ostringstream &os, unsigned int depth) const override; private: ExprPtrs args; @@ -283,6 +311,7 @@ public: }; CompOperatorEvaluator(TokenLocation location_, ExprPtrs args, std::vector kinds, std::vector locations); + EvaluationValue evaluate(EvaluationContext &ec) const override; void printSelfInfo(std::ostringstream &os, unsigned int depth) const override; private: ExprPtrs args; @@ -299,6 +328,7 @@ class ExpressionAsStatementEvaluator : public StatementEvaluator public: ExpressionAsStatementEvaluator(TokenLocation location_, ExprPtr expr); + void evaluate(EvaluationContext &ec) const override; void printSelfInfo(std::ostringstream &os, unsigned int depth) const override; }; @@ -311,6 +341,7 @@ class BlockEvaluator : public StatementEvaluator public: BlockEvaluator(TokenLocation location_, StatPtrs evals); + void evaluate(EvaluationContext &ec) const override; void printSelfInfo(std::ostringstream &os, unsigned int depth) const override; }; diff --git a/tests/no-ui-scenarios/BatchExecTests.cpp b/tests/no-ui-scenarios/BatchExecTests.cpp index 75bce65..9da70a2 100644 --- a/tests/no-ui-scenarios/BatchExecTests.cpp +++ b/tests/no-ui-scenarios/BatchExecTests.cpp @@ -17,6 +17,41 @@ #include #include "batch/Lexer.hpp" #include "batch/Parser.hpp" +#include "batch/EvaluationContext.hpp" +#include "batch/Evaluator.hpp" +#include + +using VarsType = std::unordered_map; +using ActsType = std::unordered_map; + +struct TestExecutor : ExecutorInterface { + TestExecutor(VarsType variables = {}, ActsType activities = {}, + std::function(Point)> pointToUI = {}, + std::function(const std::string &)> stringToUI = {}) : + variables(std::move(variables)), activities(std::move(activities)), + pointToUI(std::move(pointToUI)), stringToUI(std::move(stringToUI)) { } + + EvaluationValue getVariableByName(const std::string &name) override + { + auto it = variables.find(name); + if (it == variables.end()) throw EvaluationFailure{} << "no variable " << name; + return it->second; + } + std::shared_ptr getVisibleRoot() override + { + ASSERT(0); + return {}; + } + std::ostream &outputStream() override + { + return std::cout; + } +private: + VarsType variables; + ActsType activities; + std::function(Point)> pointToUI; + std::function(const std::string &)> stringToUI; +}; TEST(TestExec, simpleLexer) { @@ -83,6 +118,30 @@ TEST(TestExec, simpleParser) } ASSERT_TRUE(errors.empty()); ASSERT_TRUE(result); + + std::string result2; + auto vars = VarsType { + { + "foo", EvaluationValue::UserFunctionType{ + [&](EvaluationContext &, int v1, std::string v2, std::string v3, bool v4) -> EvaluationValue { + return std::to_string(v1) + "," + v2 + "," + v3 + "," + (v4 ? "true" : "false"); + } + } + }, + { + "bar", EvaluationValue::UserFunctionType{ + [&](EvaluationContext &, std::string value) -> EvaluationValue { + result2 = value; + return nullptr; + } + } + }, + }; + TestExecutor exec(std::move(vars)); + EvaluationContext ec { exec }; + ASSERT_NO_THROW(result->evaluate(ec)); + ASSERT_TRUE(!result2.empty()); + ASSERT_EQ(result2, "1,qwe,rty,true"); } int main(int argc, char *argv[]) -- 2.7.4