3
0

ExpressionEvaluationSystemComponent.cpp 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #include <AzCore/Debug/Profiler.h>
  9. #include <AzCore/Serialization/EditContext.h>
  10. #include <AzCore/Serialization/EditContextConstants.inl>
  11. #include <AzCore/Serialization/Json/RegistrationContext.h>
  12. #include <AzCore/Serialization/SerializeContext.h>
  13. #include <ExpressionEngine/InternalTypes.h>
  14. #include <ExpressionEngine/MathOperators/MathExpressionOperators.h>
  15. #include <ExpressionEngine/Utils.h>
  16. #include <ExpressionEvaluationSystemComponent.h>
  17. #include <ExpressionPrimitivesSerializers.inl>
  18. #include <ElementInformationSerializer.inl>
  19. AZ_DEFINE_BUDGET(ExpressionEvaluation);
  20. namespace ExpressionEvaluation
  21. {
  22. namespace StructuralParsers
  23. {
  24. class InternalExpressionElementParser
  25. : public ExpressionElementParser
  26. {
  27. public:
  28. AZ_CLASS_ALLOCATOR(InternalExpressionElementParser, AZ::SystemAllocator);
  29. InternalExpressionElementParser()
  30. // Just consume spaces, tabs, or commas
  31. : m_whiteSpaceRegex(R"(^[ ,]+)")
  32. {
  33. }
  34. ExpressionParserId GetParserId() const override
  35. {
  36. return InternalTypes::Interfaces::InternalParser;
  37. }
  38. ParseResult ParseElement(const AZStd::string& inputText, size_t offset) const override
  39. {
  40. ParseResult result;
  41. AZStd::smatch match;
  42. if (AZStd::regex_search(&inputText.at(offset), match, m_whiteSpaceRegex))
  43. {
  44. result.m_charactersConsumed = match[0].length();
  45. }
  46. else if (inputText.at(offset) == '(')
  47. {
  48. result.m_charactersConsumed = 1;
  49. result.m_element.m_id = InternalTypes::OpenParen;
  50. result.m_element.m_priority = std::numeric_limits<int>::min();
  51. }
  52. else if (inputText.at(offset) == ')')
  53. {
  54. result.m_charactersConsumed = 1;
  55. result.m_element.m_id = InternalTypes::CloseParen;
  56. result.m_element.m_priority = std::numeric_limits<int>::min();
  57. }
  58. return result;
  59. }
  60. void EvaluateToken(const ElementInformation& parseResult, ExpressionResultStack& evaluationStack) const override
  61. {
  62. AZ_UNUSED(parseResult);
  63. AZ_UNUSED(evaluationStack);
  64. AZ_Error("ExpressionEngine", false, "IgnoredSymbolParser should not be used to evaluate tokens.");
  65. }
  66. private:
  67. AZStd::regex m_whiteSpaceRegex;
  68. };
  69. }
  70. ////////////////////////////////////////
  71. // ExpressionEvaluationSystemComponent
  72. ////////////////////////////////////////
  73. static bool ExpressionTokenConverter(AZ::SerializeContext& serializeContext, AZ::SerializeContext::DataElementNode& rootElement)
  74. {
  75. if (rootElement.GetVersion() < 1)
  76. {
  77. AZ::Crc32 interfaceId;
  78. rootElement.GetChildData(AZ_CRC("InterfaceId", 0x221346a5), interfaceId);
  79. rootElement.RemoveElementByName(AZ_CRC("InterfaceId", 0x221346a5));
  80. rootElement.AddElementWithData<unsigned int>(serializeContext, "ParserId", static_cast<unsigned int>(interfaceId));
  81. }
  82. return true;
  83. }
  84. void ExpressionEvaluationSystemComponent::Reflect(AZ::ReflectContext* context)
  85. {
  86. if (AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context))
  87. {
  88. serialize->Class<ExpressionEvaluationSystemComponent, AZ::Component>()
  89. ->Version(0);
  90. // Only Serializing the information we need to
  91. serialize->Class<ElementInformation>()
  92. ->Version(0)
  93. ->Field("Id", &ElementInformation::m_id)
  94. ->Field("ExtraData", &ElementInformation::m_extraStore)
  95. ;
  96. serialize->Class<ExpressionToken>()
  97. ->Version(1, ExpressionTokenConverter)
  98. ->Field("ParserId", &ExpressionToken::m_parserId)
  99. ->Field("TokenInformation", &ExpressionToken::m_information)
  100. ;
  101. serialize->Class<VariableDescriptor>()
  102. ->Version(0)
  103. ->Field("DisplayName", &VariableDescriptor::m_displayName)
  104. ->Field("NameHash", &VariableDescriptor::m_nameHash)
  105. ;
  106. serialize->Class<ExpressionTree::VariableDescriptor>()
  107. ->Version(0)
  108. ->Field("SupportedTypes", &ExpressionTree::VariableDescriptor::m_supportedTypes)
  109. ->Field("Value", &ExpressionTree::VariableDescriptor::m_value)
  110. ;
  111. serialize->Class<ExpressionTree>()
  112. ->Version(0)
  113. ->Field("Variables", &ExpressionTree::m_variables)
  114. ->Field("VariableDisplayOrder", &ExpressionTree::m_orderedVariables)
  115. ->Field("Tokens", &ExpressionTree::m_tokens)
  116. ;
  117. if (AZ::EditContext* ec = serialize->GetEditContext())
  118. {
  119. ec->Class<ExpressionEvaluationSystemComponent>("ExpressionEvaluationGem", "[Description of functionality provided by this System Component]")
  120. ->ClassElement(AZ::Edit::ClassElements::EditorData, "")
  121. ->Attribute(AZ::Edit::Attributes::AutoExpand, true)
  122. ;
  123. }
  124. }
  125. if (AZ::JsonRegistrationContext* jsonContext = azrtti_cast<AZ::JsonRegistrationContext*>(context))
  126. {
  127. jsonContext->Serializer<AZ::ExpressionTreeVariableDescriptorSerializer>()->HandlesType<ExpressionTree::VariableDescriptor>();
  128. jsonContext->Serializer<AZ::ElementInformationSerializer>()->HandlesType<ElementInformation>();
  129. }
  130. }
  131. void ExpressionEvaluationSystemComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
  132. {
  133. provided.push_back(AZ_CRC("ExpressionEvaluationGemService", 0xad59526b));
  134. }
  135. void ExpressionEvaluationSystemComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
  136. {
  137. incompatible.push_back(AZ_CRC("ExpressionEvaluationGemService", 0xad59526b));
  138. }
  139. void ExpressionEvaluationSystemComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required)
  140. {
  141. AZ_UNUSED(required);
  142. }
  143. void ExpressionEvaluationSystemComponent::GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent)
  144. {
  145. AZ_UNUSED(dependent);
  146. }
  147. ExpressionEvaluationSystemComponent::~ExpressionEvaluationSystemComponent()
  148. {
  149. for (auto internalParser : m_internalParsers)
  150. {
  151. delete internalParser;
  152. }
  153. for (auto parserPair : m_elementInterfaces)
  154. {
  155. delete parserPair.second;
  156. }
  157. }
  158. void ExpressionEvaluationSystemComponent::Init()
  159. {
  160. m_internalParsers.emplace_back(aznew StructuralParsers::InternalExpressionElementParser());
  161. m_internalParsers.emplace_back(aznew VariableParser());
  162. RegisterExpressionInterface(aznew NumericPrimitiveParser());
  163. RegisterExpressionInterface(aznew MathExpressionOperators());
  164. RegisterExpressionInterface(aznew BooleanPrimitiveParser());
  165. }
  166. void ExpressionEvaluationSystemComponent::Activate()
  167. {
  168. ExpressionEvaluationRequestBus::Handler::BusConnect();
  169. }
  170. void ExpressionEvaluationSystemComponent::Deactivate()
  171. {
  172. ExpressionEvaluationRequestBus::Handler::BusDisconnect();
  173. }
  174. void ExpressionEvaluationSystemComponent::RegisterExpressionInterface(ExpressionElementParser* elementParser)
  175. {
  176. auto interfaceIter = m_elementInterfaces.find(elementParser->GetParserId());
  177. if (interfaceIter != m_elementInterfaces.end())
  178. {
  179. delete elementParser;
  180. }
  181. else
  182. {
  183. m_elementInterfaces[elementParser->GetParserId()] = elementParser;
  184. }
  185. }
  186. void ExpressionEvaluationSystemComponent::RemoveExpressionInterface(ExpressionParserId parserId)
  187. {
  188. auto interfaceIter = m_elementInterfaces.find(parserId);
  189. if (interfaceIter != m_elementInterfaces.end())
  190. {
  191. delete interfaceIter->second;
  192. m_elementInterfaces.erase(interfaceIter);
  193. }
  194. }
  195. ParseOutcome ExpressionEvaluationSystemComponent::ParseExpression(AZStd::string_view expressionString) const
  196. {
  197. return ParseRestrictedExpression({}, expressionString);
  198. }
  199. ParseInPlaceOutcome ExpressionEvaluationSystemComponent::ParseExpressionInPlace(AZStd::string_view expressionString, ExpressionTree& expressionTree) const
  200. {
  201. return ParseRestrictedExpressionInPlace({}, expressionString, expressionTree);
  202. }
  203. ParseOutcome ExpressionEvaluationSystemComponent::ParseRestrictedExpression(const AZStd::unordered_set<ExpressionParserId>& availableParsers, AZStd::string_view expressionString) const
  204. {
  205. ExpressionTree expressionTree;
  206. AZ::Outcome<void, ParsingError> result = ParseRestrictedExpressionInPlace(availableParsers, expressionString, expressionTree);
  207. if (result)
  208. {
  209. return AZ::Success(expressionTree);
  210. }
  211. return AZ::Failure(result.GetError());
  212. }
  213. AZ::Outcome<void, ParsingError> ExpressionEvaluationSystemComponent::ParseRestrictedExpressionInPlace(const AZStd::unordered_set<ExpressionParserId>& parsers, AZStd::string_view expressionString, ExpressionTree& expressionTree) const
  214. {
  215. AZ_PROFILE_FUNCTION(ExpressionEvaluation);
  216. expressionTree.ClearTree();
  217. size_t offset = 0;
  218. size_t lastOffset = 0;
  219. size_t endpoint = expressionString.length();
  220. AZStd::vector< ExpressionToken > operatorStack;
  221. // Pre-reserve a bunch of space using the size of the string as a rough metric.
  222. // Should likely be too large assuming variables are used.
  223. operatorStack.reserve(expressionString.size() / 2);
  224. AZStd::vector< ExpressionElementParser*> parserList;
  225. parserList.reserve(m_internalParsers.size() + parsers.size());
  226. parserList.insert(parserList.begin(), m_internalParsers.begin(), m_internalParsers.end());
  227. if (!parsers.empty())
  228. {
  229. for (const auto& interfaceId : parsers)
  230. {
  231. auto interfaceIter = m_elementInterfaces.find(interfaceId);
  232. if (interfaceIter != m_elementInterfaces.end())
  233. {
  234. parserList.emplace_back(interfaceIter->second);
  235. }
  236. }
  237. }
  238. else
  239. {
  240. parserList.reserve(parserList.size() + m_elementInterfaces.size());
  241. for (auto interfacePair : m_elementInterfaces)
  242. {
  243. parserList.emplace_back(interfacePair.second);
  244. }
  245. }
  246. AZStd::stack<size_t> openParenOffsetStack;
  247. // We want to make sure our parsing makes logical sense(i.e. goes in the pattern of Value Operator Value Operator Value)
  248. // Otherwise elements might not function correctly
  249. bool expectOperator = false;
  250. // This is using a ShuntingYard Algorithm to sort out the expression into Reverse Polish Notation.
  251. while (offset < endpoint)
  252. {
  253. for (ExpressionElementParser* parser : parserList)
  254. {
  255. ExpressionElementParser::ParseResult result = parser->ParseElement(expressionString, offset);
  256. // Handle any tree elements that might have been returned.
  257. if (result.m_element.m_id >= 0)
  258. {
  259. ExpressionToken expressionToken;
  260. expressionToken.m_parserId = parser->GetParserId();
  261. expressionToken.m_information = AZStd::move(result.m_element);
  262. // Handle all of the internal elements
  263. if (expressionToken.m_parserId == InternalTypes::Interfaces::InternalParser)
  264. {
  265. if (result.m_element.m_id == InternalTypes::OpenParen)
  266. {
  267. if (expectOperator)
  268. {
  269. return ReportUnexpectedSymbol(expressionString, offset, result.m_charactersConsumed);
  270. }
  271. operatorStack.emplace_back(AZStd::move(expressionToken));
  272. openParenOffsetStack.push(offset);
  273. }
  274. else if (result.m_element.m_id == InternalTypes::CloseParen)
  275. {
  276. // Handling the weird case of () being the first element in an expression. Silly, but valid.
  277. // If nothing has been added to the tree, we don't want to error on a close paren.
  278. if (!expectOperator && expressionTree.GetTreeSize() != 0)
  279. {
  280. return ReportUnexpectedSymbol(expressionString, offset, result.m_charactersConsumed);
  281. }
  282. bool foundOpenParen = false;
  283. while (!operatorStack.empty())
  284. {
  285. auto searchExpressionToken = operatorStack.back();
  286. operatorStack.pop_back();
  287. if (searchExpressionToken.m_parserId == InternalTypes::Interfaces::InternalParser)
  288. {
  289. if (searchExpressionToken.m_information.m_id == InternalTypes::OpenParen)
  290. {
  291. foundOpenParen = true;
  292. openParenOffsetStack.pop();
  293. break;
  294. }
  295. }
  296. else
  297. {
  298. expressionTree.PushElement(AZStd::move(searchExpressionToken));
  299. }
  300. }
  301. if (!foundOpenParen)
  302. {
  303. return ReportUnexpectedSymbol(expressionString, offset, result.m_charactersConsumed);
  304. }
  305. }
  306. else if (expressionToken.m_information.m_id == InternalTypes::Variable)
  307. {
  308. if (expectOperator)
  309. {
  310. return ReportUnexpectedValue(expressionString, offset, result.m_charactersConsumed);
  311. }
  312. VariableDescriptor descriptor = Utils::GetAnyValue<VariableDescriptor>(expressionToken.m_information.m_extraStore);
  313. expressionTree.RegisterVariable(descriptor.m_displayName);
  314. expressionTree.PushElement(AZStd::move(expressionToken));
  315. expectOperator = true;
  316. }
  317. else
  318. {
  319. ParsingError parsingError;
  320. parsingError.m_offsetIndex = offset;
  321. parsingError.m_errorString = AZStd::string::format("Unknown internal tree element with id %i", expressionToken.m_information.m_id);
  322. return AZ::Failure(parsingError);
  323. }
  324. }
  325. else if (expressionToken.m_information.m_allowOnOperatorStack)
  326. {
  327. if (!expectOperator)
  328. {
  329. return ReportUnexpectedOperator(expressionString, offset, result.m_charactersConsumed);
  330. }
  331. if (operatorStack.empty())
  332. {
  333. operatorStack.emplace_back(AZStd::move(expressionToken));
  334. }
  335. else
  336. {
  337. int currentPriority = expressionToken.m_information.m_priority;
  338. while (!operatorStack.empty())
  339. {
  340. const ExpressionToken& lastExpressionToken = operatorStack.back();
  341. int lastPriority = lastExpressionToken.m_information.m_priority;
  342. if (lastPriority < currentPriority)
  343. {
  344. break;
  345. }
  346. else if (lastExpressionToken.m_information.m_associativity == ElementInformation::OperatorAssociativity::Left)
  347. {
  348. ExpressionToken tempToken = lastExpressionToken;
  349. operatorStack.pop_back();
  350. expressionTree.PushElement(AZStd::move(tempToken));
  351. }
  352. }
  353. operatorStack.emplace_back(AZStd::move(expressionToken));
  354. }
  355. expectOperator = false;
  356. }
  357. else
  358. {
  359. if (expectOperator)
  360. {
  361. return ReportUnexpectedValue(expressionString, offset, result.m_charactersConsumed);
  362. }
  363. expressionTree.PushElement(AZStd::move(expressionToken));
  364. expectOperator = true;
  365. }
  366. }
  367. // Increment out character by the amount of space consumed.
  368. // Then restart the parsing loop
  369. if (result.m_charactersConsumed > 0)
  370. {
  371. offset += result.m_charactersConsumed;
  372. break;
  373. }
  374. }
  375. if (offset == lastOffset)
  376. {
  377. return ReportUnknownCharacter(expressionString, offset);
  378. }
  379. lastOffset = offset;
  380. }
  381. if (!expectOperator && lastOffset > 0)
  382. {
  383. return ReportMissingValue(offset);
  384. }
  385. if (!openParenOffsetStack.empty())
  386. {
  387. size_t initialOffset = openParenOffsetStack.top();
  388. AZStd::string unbalancedParensString;
  389. AZStd::vector<size_t> reversedList;
  390. while (!openParenOffsetStack.empty())
  391. {
  392. reversedList.push_back(openParenOffsetStack.top());
  393. openParenOffsetStack.pop();
  394. }
  395. for (auto reverseIter = reversedList.rbegin(); reverseIter != reversedList.rend(); ++reverseIter)
  396. {
  397. if (!unbalancedParensString.empty())
  398. {
  399. unbalancedParensString.append(", ");
  400. }
  401. unbalancedParensString.append(AZStd::to_string((*reverseIter)));
  402. }
  403. return ReportUnbalancedParen(initialOffset, unbalancedParensString);
  404. }
  405. while (!operatorStack.empty())
  406. {
  407. ExpressionToken token = operatorStack.back();
  408. operatorStack.pop_back();
  409. expressionTree.PushElement(AZStd::move(token));
  410. }
  411. return AZ::Success();
  412. }
  413. EvaluateStringOutcome ExpressionEvaluationSystemComponent::EvaluateExpression(AZStd::string_view expression) const
  414. {
  415. ParseOutcome treeOutcome = ParseExpression(expression);
  416. if (!treeOutcome.IsSuccess())
  417. {
  418. ParsingError parsingError = treeOutcome.GetError();
  419. return AZ::Failure(treeOutcome.GetError());
  420. }
  421. return AZ::Success(Evaluate(treeOutcome.GetValue()));
  422. }
  423. ExpressionResult ExpressionEvaluationSystemComponent::Evaluate(const ExpressionTree& expressionTree) const
  424. {
  425. AZ_PROFILE_FUNCTION(ExpressionEvaluation);
  426. ExpressionResultStack resultStack;
  427. for (auto expressionToken : expressionTree.GetTokens())
  428. {
  429. // Empty one is reserved for internal elements(literals and variables)
  430. if (expressionToken.m_parserId == InternalTypes::Interfaces::InternalParser)
  431. {
  432. if (expressionToken.m_information.m_id == InternalTypes::Variable)
  433. {
  434. VariableDescriptor variableDescriptor = Utils::GetAnyValue<VariableDescriptor>(expressionToken.m_information.m_extraStore);
  435. AZStd::any variable = expressionTree.GetVariable(variableDescriptor.m_nameHash);
  436. resultStack.emplace(AZStd::move(variable));
  437. }
  438. }
  439. else
  440. {
  441. auto interfaceIter = m_elementInterfaces.find(expressionToken.m_parserId);
  442. if (interfaceIter != m_elementInterfaces.end())
  443. {
  444. interfaceIter->second->EvaluateToken(expressionToken.m_information, resultStack);
  445. }
  446. else
  447. {
  448. break;
  449. }
  450. }
  451. }
  452. AZ_Error("ExpressionEngine", resultStack.size() == 1, "Expression Tree should evaluate down to a single result. %i results found.", resultStack.size());
  453. return resultStack.PopAndReturn();
  454. }
  455. AZ::Outcome<void, ParsingError> ExpressionEvaluationSystemComponent::ReportMissingValue(size_t offset) const
  456. {
  457. ParsingError parsingError;
  458. parsingError.m_offsetIndex = offset;
  459. parsingError.m_errorString = "Parsing completed after processing an Operator and not upon a value, invalid expression.";
  460. return AZ::Failure(parsingError);
  461. }
  462. AZ::Outcome<void, ParsingError> ExpressionEvaluationSystemComponent::ReportUnexpectedOperator(const AZStd::string& parseString, size_t offset, size_t charactersConsumed) const
  463. {
  464. AZStd::string substring = parseString.substr(offset, charactersConsumed);
  465. ParsingError parsingError;
  466. parsingError.m_offsetIndex = offset;
  467. parsingError.m_errorString = AZStd::string::format("Unexpected Operator '%s' found at character %zu. Expected a Value.", substring.c_str(), offset);
  468. return AZ::Failure(parsingError);
  469. }
  470. AZ::Outcome<void, ParsingError> ExpressionEvaluationSystemComponent::ReportUnexpectedValue(const AZStd::string& parseString, size_t offset, size_t charactersConsumed) const
  471. {
  472. AZStd::string substring = parseString.substr(offset, charactersConsumed);
  473. ParsingError parsingError;
  474. parsingError.m_offsetIndex = offset;
  475. parsingError.m_errorString = AZStd::string::format("Unexpected Value '%s' found at character %zu. Expected an Operator or end of expression.", substring.c_str(), offset);
  476. return AZ::Failure(parsingError);
  477. }
  478. AZ::Outcome<void, ParsingError> ExpressionEvaluationSystemComponent::ReportUnexpectedSymbol(const AZStd::string& parseString, size_t offset, size_t charactersConsumed) const
  479. {
  480. AZStd::string substring = parseString.substr(offset, charactersConsumed);
  481. ParsingError parsingError;
  482. parsingError.m_offsetIndex = offset;
  483. parsingError.m_errorString = AZStd::string::format("Unexpected Symbol '%s' found at character %zu.", substring.c_str(), offset);
  484. return AZ::Failure(parsingError);
  485. }
  486. AZ::Outcome<void, ParsingError> ExpressionEvaluationSystemComponent::ReportUnknownCharacter(const AZStd::string& parseString, size_t offset) const
  487. {
  488. ParsingError parsingError;
  489. parsingError.m_offsetIndex = offset;
  490. parsingError.m_errorString = AZStd::string::format("Unknown character '%c' found in expression.", parseString.at(offset));
  491. return AZ::Failure(parsingError);
  492. }
  493. AZ::Outcome<void, ParsingError> ExpressionEvaluationSystemComponent::ReportUnbalancedParen(size_t offset, const AZStd::string& openParenOffsetString) const
  494. {
  495. ParsingError parsingError;
  496. parsingError.m_offsetIndex = offset;
  497. parsingError.m_errorString = AZStd::string::format("Unbalanced ( found at character(s) '%s' in expression.", openParenOffsetString.c_str());
  498. return AZ::Failure(parsingError);
  499. }
  500. }