/* * GDevelop Core * Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights * reserved. This project is released under the MIT License. */ #include "GDCore/IDE/Events/EventsContextAnalyzer.h" #include #include #include #include "GDCore/Events/Event.h" #include "GDCore/Events/EventsList.h" #include "GDCore/Events/Parsers/ExpressionParser2.h" #include "GDCore/Events/Parsers/ExpressionParser2NodePrinter.h" #include "GDCore/Events/Parsers/ExpressionParser2NodeWorker.h" #include "GDCore/Extensions/Metadata/ExpressionMetadata.h" #include "GDCore/Extensions/Metadata/InstructionMetadata.h" #include "GDCore/Extensions/Metadata/MetadataProvider.h" #include "GDCore/Extensions/Metadata/ParameterMetadataTools.h" #include "GDCore/IDE/Events/ExpressionValidator.h" #include "GDCore/Project/Layout.h" #include "GDCore/Project/Project.h" #include "GDCore/String.h" namespace gd { /** * \brief Go through the nodes and report any object found. * * \see gd::ExpressionParser2 */ class GD_CORE_API ExpressionObjectsAnalyzer : public ExpressionParser2NodeWorker { public: ExpressionObjectsAnalyzer(EventsContext& context_) : context(context_){}; virtual ~ExpressionObjectsAnalyzer(){}; protected: void OnVisitSubExpressionNode(SubExpressionNode& node) override { node.expression->Visit(*this); } void OnVisitOperatorNode(OperatorNode& node) override { node.leftHandSide->Visit(*this); node.rightHandSide->Visit(*this); } void OnVisitUnaryOperatorNode(UnaryOperatorNode& node) override { node.factor->Visit(*this); } void OnVisitNumberNode(NumberNode& node) override {} void OnVisitTextNode(TextNode& node) override {} void OnVisitVariableNode(VariableNode& node) override { if (node.child) node.child->Visit(*this); } void OnVisitVariableAccessorNode(VariableAccessorNode& node) override { if (node.child) node.child->Visit(*this); } void OnVisitVariableBracketAccessorNode( VariableBracketAccessorNode& node) override { node.expression->Visit(*this); if (node.child) node.child->Visit(*this); } void OnVisitIdentifierNode(IdentifierNode& node) override { if (gd::ParameterMetadata::IsObject(node.type)) { context.AddObjectName(node.identifierName); } } void OnVisitObjectFunctionNameNode(ObjectFunctionNameNode& node) override { if (!node.objectName.empty()) { context.AddObjectName(node.objectName); if (!node.behaviorFunctionName.empty()) { context.AddBehaviorName(node.objectName, node.objectFunctionOrBehaviorName); } } } void OnVisitFunctionCallNode(FunctionCallNode& node) override { if (!node.objectName.empty()) { context.AddObjectName(node.objectName); if (!node.behaviorName.empty()) { context.AddBehaviorName(node.objectName, node.behaviorName); } } for (auto& parameter : node.parameters) { parameter->Visit(*this); } } void OnVisitEmptyNode(EmptyNode& node) override {} private: EventsContext& context; }; bool EventsContextAnalyzer::DoVisitInstruction(gd::Instruction& instruction, bool isCondition) { const gd::InstructionMetadata& instrInfo = isCondition ? MetadataProvider::GetConditionMetadata( platform, instruction.GetType()) : MetadataProvider::GetActionMetadata(platform, instruction.GetType()); gd::ParameterMetadataTools::IterateOverParameters( instruction.GetParameters(), instrInfo.parameters, [this](const gd::ParameterMetadata& parameterMetadata, const gd::String& parameterValue, const gd::String& lastObjectName) { AnalyzeParameter(platform, project, layout, parameterMetadata, parameterValue, context, lastObjectName); }); return false; } void EventsContextAnalyzer::AnalyzeParameter( const gd::Platform& platform, const gd::ObjectsContainer& project, const gd::ObjectsContainer& layout, const gd::ParameterMetadata& metadata, const gd::Expression& parameter, EventsContext& context, const gd::String& lastObjectName) { const auto& value = parameter.GetPlainString(); const auto& type = metadata.GetType(); if (ParameterMetadata::IsObject(type)) { context.AddObjectName(value); } else if (ParameterMetadata::IsExpression("number", type)) { gd::ExpressionParser2 parser(platform, project, layout); auto node = parser.ParseExpression("number", value); ExpressionObjectsAnalyzer analyzer(context); node->Visit(analyzer); } else if (ParameterMetadata::IsExpression("string", type)) { gd::ExpressionParser2 parser(platform, project, layout); auto node = parser.ParseExpression("string", value); ExpressionObjectsAnalyzer analyzer(context); node->Visit(analyzer); } else if (ParameterMetadata::IsBehavior(type)) { context.AddBehaviorName(lastObjectName, value); } } void EventsContext::AddObjectName(const gd::String& objectOrGroupName) { for (auto& realObjectName : ExpandObjectsName(objectOrGroupName)) { objectNames.insert(realObjectName); } referencedObjectOrGroupNames.insert(objectOrGroupName); } void EventsContext::AddBehaviorName(const gd::String& objectOrGroupName, const gd::String& behaviorName) { for (auto& realObjectName : ExpandObjectsName(objectOrGroupName)) { objectOrGroupBehaviorNames[realObjectName].insert(behaviorName); } objectOrGroupBehaviorNames[objectOrGroupName].insert(behaviorName); } std::vector EventsContext::ExpandObjectsName( const gd::String& objectName) { // Note: this logic is duplicated in EventsCodeGenerator::ExpandObjectsName std::vector realObjects; if (project.GetObjectGroups().Has(objectName)) realObjects = project.GetObjectGroups().Get(objectName).GetAllObjectsNames(); else if (layout.GetObjectGroups().Has(objectName)) realObjects = layout.GetObjectGroups().Get(objectName).GetAllObjectsNames(); else realObjects.push_back(objectName); // Ensure that all returned objects actually exists. for (std::size_t i = 0; i < realObjects.size();) { if (!layout.HasObjectNamed(realObjects[i]) && !project.HasObjectNamed(realObjects[i])) realObjects.erase(realObjects.begin() + i); else ++i; } return realObjects; } } // namespace gd