Files
o3de/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Translation/GraphToLua.cpp
T
2021-04-20 14:44:51 -07:00

2395 lines
98 KiB
C++

/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include "GraphToLua.h"
#include <AzCore/RTTI/BehaviorContextUtilities.h>
#include <AzCore/ScriptCanvas/ScriptCanvasOnDemandNames.h>
#include <AzCore/std/sort.h>
#include <ScriptCanvas/Core/Node.h>
#include <ScriptCanvas/Core/Nodeable.h>
#include <ScriptCanvas/Data/Data.h>
#include <ScriptCanvas/Debugger/ValidationEvents/DataValidation/ScopedDataConnectionEvent.h>
#include <ScriptCanvas/Debugger/ValidationEvents/GraphTranslationValidation/GraphTranslationValidations.h>
#include <ScriptCanvas/Debugger/ValidationEvents/ParsingValidation/ParsingValidations.h>
#include <ScriptCanvas/Execution/Interpreted/ExecutionInterpretedAPI.h>
#include <ScriptCanvas/Grammar/AbstractCodeModel.h>
#include <ScriptCanvas/Grammar/ParsingMetaData.h>
#include <ScriptCanvas/Grammar/ParsingUtilities.h>
#include <ScriptCanvas/Grammar/Primitives.h>
#include <ScriptCanvas/Grammar/PrimitivesExecution.h>
#include "GraphToLuaUtility.h"
#include "TranslationContext.h"
#include "TranslationContextBus.h"
namespace GraphToLuaCpp
{
AZStd::string ToDependencyTableName(AZStd::string_view fileName)
{
return AZStd::string::format("%s%s", ScriptCanvas::Grammar::ToSafeName(fileName).c_str(), ScriptCanvas::Grammar::k_DependencySuffix);
}
AZStd::string FileNameToTableName(AZStd::string_view fileName)
{
return ScriptCanvas::Grammar::ToSafeName(fileName);
}
}
namespace ScriptCanvas
{
namespace Translation
{
const static size_t k_DefaultLoopLimit = 1000;
Configuration CreateLuaConfig([[maybe_unused]] const Grammar::AbstractCodeModel& source)
{
Configuration configuration;
configuration.m_blockCommentClose = "--]]";
configuration.m_blockCommentOpen = "--[[";
configuration.m_dependencyDelimiter = "/";
configuration.m_executionStateName = "executionState";
configuration.m_executionStateEntityIdName = "m_entityId";
configuration.m_executionStateEntityIdRef = "executionState:GetEntityId()";
configuration.m_executionStateReferenceGraph = "self.executionState";
configuration.m_executionStateReferenceLocal = configuration.m_executionStateName;
configuration.m_executionStateScriptCanvasIdName = "m_scriptCanvasId";
configuration.m_executionStateScriptCanvasIdRef = "executionState:GetScriptCanvasId()";
configuration.m_functionBlockClose = "end";
configuration.m_functionBlockOpen = "";
configuration.m_lexicalScopeDelimiter = ".";
configuration.m_lexicalScopeVariable = ".";
configuration.m_namespaceClose = "}";
configuration.m_namespaceOpen = "{";
configuration.m_scopeClose = "do";
configuration.m_scopeOpen = "end";
configuration.m_singleLineComment = "--";
configuration.m_suffix = Grammar::k_internalRuntimeSuffix;
return configuration;
}
CheckConversion::CheckConversion(Writer& writer, Grammar::VariableConstPtr source, const Grammar::ConversionByIndex& conversions, size_t index)
: m_writer(writer)
, m_source(source)
, m_conversions(conversions)
, m_index(index)
{
CheckConversionStringPre(m_writer, m_source, m_conversions, m_index);
}
CheckConversion::~CheckConversion()
{
CheckConversionStringPost(m_writer, m_source, m_conversions, m_index);
}
GraphToLua::GraphToLua(const Grammar::AbstractCodeModel& source)
: GraphToX(CreateLuaConfig(source), source)
{
SystemRequestBus::BroadcastResult(m_systemConfiguration, &SystemRequests::GetSystemComponentConfiguration);
MarkTranslationStart();
RequestBus::BroadcastResult(m_context, &RequestTraits::GetTranslationContext);
AZ_Assert(m_context, "Nothing is possible without the context");
m_tableName = GraphToLuaCpp::FileNameToTableName(m_model.GetSource().m_name);
m_tableName += m_configuration.m_suffix;
const auto& parsedInputs = m_model.GetRuntimeInputs();
m_runtimeInputs.CopyFrom(parsedInputs);
auto executionCharacteristics = m_model.GetExecutionCharacteristics();
auto hasNoOnGraphStart = !m_model.GetInterface().HasOnGraphStart();
if (executionCharacteristics == Grammar::ExecutionCharacteristics::Pure)
{
if (hasNoOnGraphStart)
{
m_runtimeInputs.m_executionSelection = Grammar::ExecutionStateSelection::InterpretedPure;
}
else
{
m_runtimeInputs.m_executionSelection = Grammar::ExecutionStateSelection::InterpretedPureOnGraphStart;
}
}
else
{
if (hasNoOnGraphStart)
{
m_runtimeInputs.m_executionSelection = Grammar::ExecutionStateSelection::InterpretedObject;
}
else
{
m_runtimeInputs.m_executionSelection = Grammar::ExecutionStateSelection::InterpretedObjectOnGraphStart;
}
}
WriteHeader();
TranslateDependencies();
TranslateClassOpen();
TranslateBody(BuildConfiguration::Release);
TranslateBody(BuildConfiguration::Performance);
TranslateBody(BuildConfiguration::Debug);
TranslateClassClose();
MarkTranslationStop();
}
const AZStd::string& GraphToLua::FindAbbreviation(AZStd::string_view dependency) const
{
return m_context->FindAbbreviation(dependency);
}
const AZStd::string& GraphToLua::FindLibrary(AZStd::string_view dependency) const
{
return m_context->FindLibrary(dependency);
}
AZStd::string_view GraphToLua::GetOperatorString(Grammar::ExecutionTreeConstPtr execution)
{
switch (execution->GetSymbol())
{
case Grammar::Symbol::OperatorAddition:
return execution->GetInput(0).m_value->m_datum.GetType() == Data::Type::String() ? " .. " : " + ";
case Grammar::Symbol::OperatorDivision:
return " / ";
case Grammar::Symbol::OperatorMultiplication:
return " * ";
case Grammar::Symbol::OperatorSubraction:
return " - ";
default:
AddError(execution, aznew Internal::ParseError(execution->GetNodeId(), ParseErrors::UntranslatedArithmetic));
return "";
}
}
bool GraphToLua::IsDebugInfoWritten() const
{
return m_executionConfig == BuildConfiguration::Debug
&& m_functionBlockConfig == FunctionBlockConfig::Traced;
}
GraphToLua::IsNamed GraphToLua::IsInputNamed(Grammar::VariableConstPtr input, Grammar::ExecutionTreeConstPtr execution)
{
return input->m_source != execution || input->m_requiresCreationFunction ? IsNamed::Yes : IsNamed::No;
}
AZStd::pair<GraphToLua::NilCheck, AZStd::string> GraphToLua::IsReturnValueNilCheckRequired(ScriptCanvas::Grammar::ExecutionTreeConstPtr execution)
{
if (execution->GetEventType() != ScriptCanvas::EventType::Count)
{
auto localOutputPtr = execution->GetLocalOutput();
if (localOutputPtr && !localOutputPtr->empty())
{
const AZStd::vector<AZStd::pair<const Slot*, Grammar::OutputAssignmentConstPtr>>& localOutput = *localOutputPtr;
if (localOutput.size() == 1)
{
auto& slotOutputPair = localOutput[0];
if (IsValueType(slotOutputPair.second->m_source->m_datum.GetType()))
{
return { NilCheck::Single, Grammar::ToTypeSafeEBusResultName(slotOutputPair.second->m_source->m_datum.GetType()) };
}
}
else
{
const Grammar::FunctionCallDefaultMetaData* metaData = azrtti_cast<const Grammar::FunctionCallDefaultMetaData*>(execution->GetMetaData().get());
if (!metaData)
{
AddError(execution, aznew Internal::ParseError(execution->GetNodeId(), ParseErrors::MetaDataRequiredForEventCall));
return { NilCheck::None, "" };
}
if (metaData->multiReturnType.IsNull())
{
AddError(execution, aznew Internal::ParseError(execution->GetNodeId(), ParseErrors::MetaDataNeedsTupleTypeIdForEventCall));
return { NilCheck::None, "" };
}
return { NilCheck::Multiple, Execution::CreateStringFastFromId(metaData->multiReturnType) };
}
}
}
return { NilCheck::None, "" };
}
TargetResult GraphToLua::MoveResult()
{
TargetResult result;
result.m_text = m_dotLua.MoveOutput();
result.m_runtimeInputs = AZStd::move(m_runtimeInputs);
result.m_debugMap = m_model.GetDebugMap();
result.m_subgraphInterface = m_model.GetInterface();
result.m_duration = GetTranslationDuration();
return result;
}
void GraphToLua::OpenFunctionBlock(Writer& writer)
{
writer.Indent();
}
AZStd::string GraphToLua::SanitizeFunctionCallName(AZStd::string_view name)
{
AZStd::string sanitized(name);
AZ::RemovePropertyNameArtifacts(sanitized);
return Grammar::ToIdentifier(sanitized);
}
AZ::Outcome<TargetResult, ErrorList> GraphToLua::Translate(const Grammar::AbstractCodeModel& model)
{
GraphToLua translation(model);
if (translation.IsSuccessfull())
{
return AZ::Success(translation.MoveResult());
}
else
{
ErrorList errors = { AZStd::string("provide translation failure details") };
return AZ::Failure(errors);
}
}
void GraphToLua::TranslateBody()
{
TranslateStaticInitialization();
TranslateConstruction();
TranslateDestruction();
TranslateExecutionTrees();
}
void GraphToLua::TranslateBody(BuildConfiguration configuration)
{
m_executionConfig = configuration;
if (configuration == BuildConfiguration::Release)
{
m_dotLua.WriteLine("-- release configuration, no debug information available, no performance markers");
m_dotLua.WriteLine("if _G.%s then", Grammar::k_InterpretedConfigurationRelease);
m_dotLua.WriteNewLine();
}
TranslateBody();
if (configuration == BuildConfiguration::Release)
{
m_dotLua.WriteNewLine();
m_dotLua.WriteLine("-- performance configuration, no debug information available, performance markers in place");
m_dotLua.WriteLine("elseif _G.%s then", Grammar::k_InterpretedConfigurationPerformance);
m_dotLua.WriteNewLine();
}
else if (configuration == BuildConfiguration::Performance)
{
m_dotLua.WriteNewLine();
m_dotLua.WriteLine("-- debug configuration, debug information available upon when tracing is requested, no performance markers in place");
m_dotLua.WriteLine("else");
m_dotLua.WriteNewLine();
}
else
{
m_dotLua.WriteNewLine();
m_dotLua.WriteLine("-- end debug configuration");
m_dotLua.WriteLine("end");
}
}
void GraphToLua::TranslateClassClose()
{
m_dotLua.WriteNewLine();
m_dotLua.Write("return %s", m_tableName.data());
}
void GraphToLua::TranslateClassOpen()
{
m_dotLua.WriteLine("local %s = {}", m_tableName.data());
m_dotLua.WriteNewLine();
}
void GraphToLua::TranslateConstruction()
{
if (m_model.IsPerEntityDataRequired())
{
TranslateInheritance();
}
}
void GraphToLua::TranslateDependencies()
{
const auto& dependencies = m_model.GetOrderedDependencies().source;
for (const auto& dependency : dependencies.nativeLibraries)
{
if (dependency.size() == 1)
{
auto& library = FindLibrary(dependency[0]);
if (!library.empty())
{
auto& abbreviation = FindAbbreviation(dependency[0]);
if (!abbreviation.empty())
{
m_dotLua.WriteLine("local %s = %s", abbreviation.data(), library.data());
}
}
}
}
for (const auto& dependency : dependencies.userSubgraphs)
{
if (dependency.empty())
{
continue;
}
if (dependency.size() == 1)
{
m_dotLua.WriteLine("local %s%s = require'%s%s'"
, GraphToLuaCpp::ToDependencyTableName(*dependency.begin()).data()
, Grammar::k_executionStateVariableName
, dependency.begin()->data());
}
else
{
auto dependencyIter = dependency.begin();
auto dependencySentinel = dependency.end();
m_dotLua.Write("local %s = require'%s"
, GraphToLuaCpp::ToDependencyTableName(dependency.back()).data()
, dependencyIter->data());
for (++dependencyIter; dependencyIter != dependencySentinel; ++dependencyIter)
{
m_dotLua.Write("%s%s", m_configuration.m_dependencyDelimiter.data(), dependencyIter->data());
}
m_dotLua.WriteLine("'");
}
}
m_dotLua.WriteNewLine();
}
void GraphToLua::TranslateDestruction()
{
}
void GraphToLua::TranslateExecutionTreeChildPost(Grammar::ExecutionTreeConstPtr execution, const Grammar::ExecutionChild& /*child*/, size_t index, size_t /*rootIndex*/)
{
switch (execution->GetSymbol())
{
case Grammar::Symbol::Cycle:
case Grammar::Symbol::IfCondition:
case Grammar::Symbol::RandomSwitch:
case Grammar::Symbol::Switch:
m_dotLua.Outdent();
break;
case Grammar::Symbol::ForEach:
WriteForEachChildPost(execution, index);
break;
case Grammar::Symbol::While:
if (index == 0)
{
m_dotLua.Outdent();
m_dotLua.WriteLineIndented("end");
}
break;
default:
break;
}
}
void GraphToLua::TranslateExecutionTreeChildPre(Grammar::ExecutionTreeConstPtr execution, const Grammar::ExecutionChild& child, size_t index, size_t /*rootIndex*/)
{
const auto symbol = execution->GetSymbol();
switch (symbol)
{
case Grammar::Symbol::ForEach:
if (index == 0)
{
WriteInfiniteLoopCheckPre(execution);
WriteForEachChildPre(execution);
WriteInfiniteLoopCheckPost(execution);
}
break;
case Grammar::Symbol::IfCondition:
if (index == 0)
{
m_dotLua.Indent();
WriteDebugInfoOut(execution, 0, "if-true-out");
}
else
{
m_dotLua.WriteLineIndented("else");
m_dotLua.Indent();
WriteDebugInfoOut(execution, 1, "if-false-out");
}
break;
case Grammar::Symbol::Cycle:
case Grammar::Symbol::RandomSwitch:
case Grammar::Symbol::Switch:
if (index > 0)
{
m_dotLua.WriteIndented("elseif ");
}
else
{
WritePreFirstCaseSwitch(execution, symbol);
m_dotLua.WriteIndented("if ");
}
WriteConditionalCaseSwitch(execution, symbol, child, index);
m_dotLua.WriteLine(" then");
m_dotLua.Indent();
if (symbol == Grammar::Symbol::Cycle)
{
WriteCycleBegin(execution);
}
WriteDebugInfoOut(execution, index, "switch-out TranslateExecutionTreeChildPre");
break;
case Grammar::Symbol::While:
if (index == 0)
{
WriteInfiniteLoopCheckPre(execution);
WriteDebugInfoIn(execution, "while-in TranslateExecutionTreeChildPre");
m_dotLua.WriteIndented("while ");
WriteFunctionCallInput(execution);
m_dotLua.WriteLine(" do");
m_dotLua.Indent();
WriteInfiniteLoopCheckPost(execution);
}
break;
default:
break;
}
}
void GraphToLua::TranslateExecutionTreeEntry(Grammar::ExecutionTreeConstPtr execution, size_t index)
{
TranslateExecutionTreeEntryPre(execution, index);
TranslateExecutionTreeEntryRecurse(execution, index);
TranslateExecutionTreeEntryPost(execution, index);
}
void GraphToLua::TranslateExecutionTreeEntryPost(Grammar::ExecutionTreeConstPtr execution, size_t /*index*/)
{
const auto symbol = execution->GetSymbol();
switch (execution->GetSymbol())
{
case Grammar::Symbol::Cycle:
case Grammar::Symbol::RandomSwitch:
case Grammar::Symbol::Switch:
WriteSwitchEnd(symbol);
break;
case Grammar::Symbol::IfCondition:
m_dotLua.WriteLineIndented("end");
break;
default:
break;
}
}
void GraphToLua::TranslateExecutionTreeEntryPre(Grammar::ExecutionTreeConstPtr execution, size_t /*index*/)
{
switch (execution->GetSymbol())
{
case Grammar::Symbol::IfCondition:
// will write if the debug info is valid
WriteDebugInfoIn(execution, "if-in !prefaced by expression TranslateExecutionTreeEntryPre");
m_dotLua.WriteIndented("if ");
WriteFunctionCallInput(execution);
m_dotLua.WriteLine(" then");
break;
default:
break;
}
}
void GraphToLua::TranslateExecutionTreeEntryRecurse(Grammar::ExecutionTreeConstPtr execution, size_t index)
{
switch (execution->GetSymbol())
{
case Grammar::Symbol::Break:
m_dotLua.WriteLineIndented("break");
break;
case Grammar::Symbol::UserOut:
TranslateExecutionTreeUserOutCall(execution);
break;
case Grammar::Symbol::CompareEqual:
case Grammar::Symbol::CompareGreater:
case Grammar::Symbol::CompareGreaterEqual:
case Grammar::Symbol::CompareLess:
case Grammar::Symbol::CompareLessEqual:
case Grammar::Symbol::CompareNotEqual:
case Grammar::Symbol::IsNull:
case Grammar::Symbol::LogicalAND:
case Grammar::Symbol::LogicalNOT:
case Grammar::Symbol::LogicalOR:
case Grammar::Symbol::FunctionCall:
case Grammar::Symbol::OperatorAddition:
case Grammar::Symbol::OperatorDivision:
case Grammar::Symbol::OperatorMultiplication:
case Grammar::Symbol::OperatorSubraction:
case Grammar::Symbol::VariableAssignment:
TranslateExecutionTreeFunctionCall(execution);
break;
case Grammar::Symbol::VariableDeclaration:
{
auto variable = execution->GetInput(0).m_value;
m_dotLua.WriteLineIndented("local %s = %s", variable->m_name.data(), ToValueString(variable->m_datum, m_configuration).data());
break;
}
default:
break;
}
for (size_t childIndex = 0; childIndex < execution->GetChildrenCount(); ++childIndex)
{
const auto& child = execution->GetChild(childIndex);
if (child.m_execution && !child.m_execution->IsInternalOut())
{
TranslateExecutionTreeChildPre(execution, child, childIndex, index + 1);
TranslateExecutionTreeEntry(child.m_execution, index + 1);
TranslateExecutionTreeChildPost(execution, child, childIndex, index + 1);
}
}
}
void GraphToLua::TranslateExecutionTreeFunctionCall(Grammar::ExecutionTreeConstPtr execution)
{
TranslateNodeableOuts(execution);
WriteDebugInfoIn(execution, "TranslateExecutionTreeFunctionCall begin");
m_dotLua.WriteIndent();
WriteLocalOutputInitialization(execution);
const bool isWrittenOutputPossible = execution->GetChildrenCount() == 1;
if (isWrittenOutputPossible)
{
WriteVariableWrite(execution, execution->GetChild(0).m_output);
}
if (IsLogicalExpression(execution))
{
WriteLogicalExpression(execution);
m_dotLua.WriteNewLine();
}
else if (IsVariableGet(execution))
{
WriteVariableRead(execution->GetInput(0).m_value);
m_dotLua.WriteNewLine();
}
else if (IsVariableSet(execution) || execution->GetSymbol() == Grammar::Symbol::VariableAssignment)
{
WriteFunctionCallInput(execution);
m_dotLua.WriteNewLine();
}
else if (IsExecutedPropertyExtraction(execution))
{
WriteFunctionCallInput(execution);
m_dotLua.Write(".");
m_dotLua.WriteLine(execution->GetExecutedPropertyExtraction()->m_name);
}
else if (IsWrittenMathExpression(execution))
{
WriteWrittenMathExpression(execution);
m_dotLua.WriteNewLine();
}
else if (IsOperatorArithmetic(execution))
{
WriteOperatorArithmetic(execution);
m_dotLua.WriteNewLine();
}
else if (IsEventConnectCall(execution))
{
WriteEventConnectCall(execution);
}
else if (IsEventDisconnectCall(execution))
{
WriteEventDisconnectCall(execution, PostDisconnectAction::SetToNil);
}
else if (Grammar::IsGlobalPropertyRead(execution))
{
WriteGlobalPropertyRead(execution);
}
else
{
const bool isNullCheckRequired = Grammar::IsFunctionCallNullCheckRequired(execution);
if (isNullCheckRequired)
{
WriteFunctionCallNullCheckPre(execution);
m_dotLua.WriteIndent();
}
WriteFunctionCallOfNode(execution);
m_dotLua.WriteLine(")");
if (isNullCheckRequired)
{
WriteFunctionCallNullCheckPost(execution);
}
}
if (isWrittenOutputPossible)
{
WriteOnVariableWritten(execution, execution->GetChild(0).m_output);
}
WriteOutputAssignments(execution);
WriteDebugInfoOut(execution, 0, "TranslateExecutionTreeFunctionCall end");
}
void GraphToLua::TranslateExecutionTrees()
{
if (auto start = m_model.GetStart())
{
TranslateFunction(start, IsNamed::Yes);
m_dotLua.WriteNewLine();
}
auto functions = m_model.GetFunctions();
if (!functions.empty())
{
m_dotLua.WriteNewLine();
for (auto function : functions)
{
TranslateFunction(function, IsNamed::Yes);
m_dotLua.WriteNewLine();
}
}
}
void GraphToLua::TranslateExecutionTreeUserOutCall(Grammar::ExecutionTreeConstPtr execution)
{
auto outCallIndexOptional = execution->GetOutCallIndex();
if (!outCallIndexOptional)
{
AddError(nullptr, aznew Internal::ParseError(execution->GetNodeId(), "Execution did not return required out call index"));
return;
}
const size_t outIndex = *outCallIndexOptional;
m_dotLua.WriteIndented("%s(self, %zu", Grammar::k_NodeableCallInterpretedOut, outIndex);
if (execution->GetInputCount() > 0)
{
m_dotLua.Write(", ");
WriteFunctionCallInput(execution);
}
m_dotLua.WriteLine(") -- %s", execution->GetName().data());
}
void GraphToLua::TranslateFunction(Grammar::ExecutionTreeConstPtr execution, IsNamed lex)
{
// get the signature of the function
// start a block with the signature
// translate the block, with the parameter information passed in
TranslateFunctionDefinition(execution, lex);
if (m_executionConfig == BuildConfiguration::Debug)
{
m_dotLua.Indent();
if (execution->IsPure())
{
m_dotLua.WriteLineIndented("if %s(%s) then", Grammar::k_DebugIsTracedName, m_configuration.m_executionStateReferenceLocal.data());
}
else
{
m_dotLua.WriteLineIndented("if %s(%s) then", Grammar::k_DebugIsTracedName, m_configuration.m_executionStateReferenceGraph.data());
}
TranslateFunctionBlock(execution, FunctionBlockConfig::Traced, lex);
m_dotLua.WriteLineIndented("else");
}
TranslateFunctionBlock(execution, FunctionBlockConfig::Ignored, lex);
if (m_executionConfig == BuildConfiguration::Debug)
{
m_dotLua.WriteLineIndented("end");
m_dotLua.Outdent();
}
m_dotLua.WriteIndented("end");
}
void GraphToLua::TranslateFunctionBlock(Grammar::ExecutionTreeConstPtr functionBlock, IsNamed /*lex*/)
{
ScopedIndent indent(m_dotLua);
if (!functionBlock->IsPure())
{
m_dotLua.WriteLineIndented("local %s = %s", m_configuration.m_executionStateName.data(), m_configuration.m_executionStateReferenceGraph.data());
}
if (functionBlock->IsInfiniteLoopDetectionPoint())
{
WriteInfiniteLoopCheckPre(functionBlock);
}
WriteDebugInfoOut(functionBlock, 0, "TranslateFunctionBlock begin");
WriteLocalInputCreation(functionBlock);
WriteOutputAssignments(functionBlock);
WriteLocalVariableInitializion(functionBlock);
WriteReturnValueInitialization(functionBlock);
if (functionBlock->GetChildrenCount() > 0)
{
TranslateExecutionTreeEntry(functionBlock->GetChild(0).m_execution, 0);
}
if (functionBlock->IsInfiniteLoopDetectionPoint())
{
WriteInfiniteLoopCheckPost(functionBlock);
}
WriteReturnStatement(functionBlock);
}
void GraphToLua::TranslateFunctionBlock(Grammar::ExecutionTreeConstPtr functionBlock, FunctionBlockConfig config, IsNamed lex)
{
if (m_executionConfig == BuildConfiguration::Debug || config == FunctionBlockConfig::Ignored)
{
m_functionBlockConfig = config;
TranslateFunctionBlock(functionBlock, lex);
}
}
void GraphToLua::TranslateFunctionDefinition(Grammar::ExecutionTreeConstPtr execution, IsNamed isNamed)
{
m_dotLua.WriteIndented("function");
if (isNamed == IsNamed::Yes)
{
m_dotLua.Write(" %s", m_tableName.data());
// function TableName
m_dotLua.Write(execution->IsPure() ? "." : ":" );
// function TableName. OR function TableName:
m_dotLua.Write(execution->GetName());
}
m_dotLua.Write("(");
if (execution->IsStart() && execution->IsPure())
{
m_dotLua.Write(Grammar::k_executionStateVariableName);
WriteConstructionInput();
}
else
{
if (execution->GetChildrenCount() > 0)
{
const auto& output = execution->GetChild(0).m_output;
int inputIndex = 0;
if (isNamed == IsNamed::Yes)
{
if (execution->IsPure())
{
m_dotLua.Write(Grammar::k_executionStateVariableName);
if (!output.empty())
{
m_dotLua.Write(", ");
}
}
else if (!m_model.IsUserNodeable())
{
inputIndex = 1;
}
}
else if (execution->IsPure())
{
m_dotLua.Write(Grammar::k_executionStateVariableName);
if (!output.empty())
{
m_dotLua.Write(", ");
}
}
// function[name](arg0
if (inputIndex < output.size())
{
m_dotLua.Write(output[inputIndex].second->m_source->m_name.data());
++inputIndex;
}
// function[name](arg0
for (; inputIndex < output.size(); ++inputIndex)
{
m_dotLua.Write(", %s", output[inputIndex].second->m_source->m_name.data());
// function[name](arg0, ..., argN
}
}
}
m_dotLua.WriteLine(")");
// function function[name](arg0, ..., argN) end
}
void GraphToLua::TranslateEBusEvents(Grammar::EBusHandlingConstPtr ebusHandling, AZStd::string_view leftValue)
{
for (const auto& nameAndEventThread : ebusHandling->m_events)
{
const Grammar::ExecutionTreeConstPtr& eventThread = nameAndEventThread.second;
const bool hasResults = eventThread->HasReturnValues();
AZStd::optional<size_t> eventIndex = ebusHandling->m_node->GetEventIndex(nameAndEventThread.first);
if (!eventIndex)
{
AddError(nullptr, aznew Internal::ParseError(ebusHandling->m_node->GetEntityId(), AZStd::string::format("EBus handler did not return a valid index for event %s", nameAndEventThread.first.c_str())));
return;
}
m_dotLua.WriteNewLine();
m_dotLua.WriteLineIndented("%s(%s%s, %zu, -- %s"
, hasResults ? Grammar::k_EBusHandlerHandleEventResultName : Grammar::k_EBusHandlerHandleEventName
, leftValue.data()
, ebusHandling->m_handlerName.data()
, *eventIndex
, eventThread->GetName().data());
m_dotLua.Indent();
TranslateFunction(eventThread, IsNamed::No);
m_dotLua.WriteLine(")");
m_dotLua.Outdent();
}
}
void GraphToLua::TranslateEBusHandlerCreation(Grammar::EBusHandlingConstPtr ebusHandling, AZStd::string_view leftValue)
{
if (ebusHandling->m_startsConnected)
{
if (ebusHandling->m_isAddressed)
{
m_dotLua.WriteIndented("%.*s%s = %s(%s, '%s', \""
, static_cast<int>(leftValue.size()), leftValue.data()
, ebusHandling->m_handlerName.data()
, Grammar::k_EBusHandlerCreateAndConnectToName
, Grammar::k_executionStateVariableName
, ebusHandling->m_ebusName.data());
m_dotLua.Write(Execution::CreateStringFastFromId(ebusHandling->m_startingAdress->m_datum.GetType().GetAZType()).data());
m_dotLua.Write("\", ");
m_dotLua.Write(AZStd::string::format("%s%s", leftValue.data(), ebusHandling->m_startingAdress->m_name.data()));
m_dotLua.WriteLine(")");
}
else
{
m_dotLua.WriteLineIndented("%.*s%s = %s(%s, '%s')"
, static_cast<int>(leftValue.size()), leftValue.data()
, ebusHandling->m_handlerName.data()
, Grammar::k_EBusHandlerCreateAndConnectName
, Grammar::k_executionStateVariableName
, ebusHandling->m_ebusName.data());
}
}
else
{
m_dotLua.WriteLineIndented("%.*s%s = %s(%s, '%s')"
, static_cast<int>(leftValue.size()), leftValue.data()
, ebusHandling->m_handlerName.data()
, Grammar::k_EBusHandlerCreateName
, Grammar::k_executionStateVariableName
, ebusHandling->m_ebusName.data());
}
}
void GraphToLua::TranslateEBusHandling(AZStd::string_view leftValue)
{
const auto& handlingByNode = m_model.GetEBusHandlings();
for (const auto& eventHandling : handlingByNode)
{
TranslateEBusHandlerCreation(eventHandling, leftValue);
TranslateEBusEvents(eventHandling, leftValue);
m_dotLua.WriteNewLine();
}
}
void GraphToLua::TranslateNodeableParse()
{
for (auto& nodeAndParse : m_model.GetNodeableParse())
{
for (auto onInputChange : nodeAndParse->m_onInputChanges)
{
TranslateExecutionTreeFunctionCall(onInputChange);
}
for (auto& out : nodeAndParse->m_latents)
{
m_dotLua.WriteNewLine();
TranslateNodeableOut(out.second);
}
if (!nodeAndParse->m_latents.empty())
{
m_dotLua.WriteNewLine();
}
}
}
void GraphToLua::TranslateInheritance()
{
if (m_model.IsUserNodeable())
{
// setmetatable(Subgraph, { __index = Nodeable })
m_dotLua.WriteLine("setmetatable(%s, { __index = %s })", m_tableName.c_str(), Grammar::k_NodeableUserBaseClassName);
// local SubgraphInstance_MT = { __index = Subgraph }
m_dotLua.WriteLine("local %s%s = { __index = %s }", m_tableName.c_str(), Grammar::k_MetaTableSuffix, m_tableName.c_str());
}
else
{
m_dotLua.WriteLine("%s.__index = %s", m_tableName.c_str(), m_tableName.c_str());
}
m_dotLua.WriteNewLine();
m_dotLua.Write("function %s.new(executionState", m_tableName.c_str());
WriteConstructionInput();
m_dotLua.WriteLine(")");
OpenFunctionBlock(m_dotLua);
{
if (m_model.IsUserNodeable())
{
// local self = OverrideNodeableMetatable(Nodeable(), SubgraphInstance_MT)
m_dotLua.WriteLineIndented("local self = %s(%s(%s), %s%s)"
, Grammar::k_OverrideNodeableMetatableName
, Grammar::k_NodeableUserBaseClassName
, Grammar::k_executionStateVariableName
, m_tableName.c_str()
, Grammar::k_MetaTableSuffix);
// initialize outs to no-ops
const auto& outKeys = m_model.GetInterface().GetOutKeys();
if (!outKeys.empty())
{
m_dotLua.WriteLineIndented("%s(self, %zu)", Grammar::k_InitializeNodeableOutKeys, outKeys.size());
}
}
else
{
m_dotLua.WriteLineIndented("local self = setmetatable({}, %s)", m_tableName.c_str());
}
m_dotLua.WriteLineIndented("self.%s = %s", Grammar::k_executionStateVariableName, Grammar::k_executionStateVariableName);
TranslateVariableInitialization("self.");
m_dotLua.WriteLineIndented("return self");
}
CloseFunctionBlock(m_dotLua);
m_dotLua.WriteNewLine();
}
void GraphToLua::TranslateNodeableOut(Grammar::ExecutionTreeConstPtr execution)
{
auto outCallIndexOptional = execution->GetOutCallIndex();
if (!outCallIndexOptional)
{
AddError(nullptr, aznew Internal::ParseError(execution->GetNodeId(), "Execution did not return required out call index"));
return;
}
const size_t outIndex = *outCallIndexOptional;
// #functions2 remove-execution-out-hash
const auto setExecutionOutName = Grammar::IsUserFunctionDefinition(execution)
? Grammar::k_NodeableSetExecutionOutUserSubgraphName
: execution->HasReturnValues()
? Grammar::k_NodeableSetExecutionOutResultName
: Grammar::k_NodeableSetExecutionOutName;
m_dotLua.WriteLineIndented("%s(self.%s, %zu, -- %s"
, setExecutionOutName
, execution->GetNodeable()->m_name.data()
, outIndex
, execution->GetName().data());
m_dotLua.Indent();
TranslateFunction(execution, IsNamed::No);
m_dotLua.WriteLine(")");
m_dotLua.Outdent();
}
void GraphToLua::TranslateNodeableOuts(Grammar::ExecutionTreeConstPtr execution)
{
const auto outs = execution->GetInternalOuts();
for (const auto& out : outs)
{
m_dotLua.WriteNewLine();
TranslateNodeableOut(out);
}
if (!outs.empty())
{
m_dotLua.WriteNewLine();
}
}
void GraphToLua::TranslateStaticInitialization()
{
if (m_runtimeInputs.m_staticVariables.empty())
{
return;
}
m_dotLua.WriteIndented("function %s.%s(self, ", m_tableName.c_str(), Grammar::k_InitializeStaticsName);
WriteStaticInitializerInput(IsLeadingCommaRequired::No);
m_dotLua.WriteLine(")");
m_dotLua.Indent();
const auto& staticSources = m_model.GetStaticVariablesNames();
for (const auto& staticSource : staticSources)
{
m_dotLua.WriteLineIndented("rawset(self, '%s', %s)", staticSource.second.c_str(), staticSource.first->m_name.c_str());
}
m_dotLua.Outdent();
m_dotLua.WriteLine("end");
}
void GraphToLua::TranslateVariableInitialization(AZStd::string_view leftValue)
{
const auto& staticVariableNames = m_model.GetStaticVariablesNames();
auto& variables = m_model.GetVariables();
for (auto& variable : variables)
{
if (variable->m_isDebugOnly && m_executionConfig != BuildConfiguration::Debug)
{
continue;
}
if (m_model.IsUserNodeable(variable))
{
auto nodeableName = variable->m_name;
if (nodeableName.starts_with(Grammar::k_memberNamePrefix))
{
nodeableName.erase(0, AZStd::string_view(Grammar::k_memberNamePrefix).size());
}
if (nodeableName.ends_with(Grammar::k_reservedWordProtection))
{
nodeableName.resize(nodeableName.size() - AZStd::string_view(Grammar::k_reservedWordProtection).size());
}
if (auto stringOptional = m_model.FindNodeableSimpleName(variable))
{
nodeableName = GraphToLuaCpp::ToDependencyTableName(*stringOptional);
}
AZStd::optional<AZStd::pair<size_t, Grammar::DependencyInfo>> indexInfo = m_model.CheckUserNodeableDependencyConstructionIndex(variable);
if (indexInfo && indexInfo->second.requiresCtorParams)
{
if (indexInfo->second.requiresCtorParamsForDependencies)
{
// indexInfo->second.requiresCtorParamsForDependencies
// self.nonLeafDependency = NonLeafDependency.new(executionState, UnpackDependencyArgs(executionState, dependentAssets, 7))
// -- has more dependencies, index, known from compile time, pushes the correct asset further down construction
m_dotLua.WriteLineIndented("%s%s = %s.new(%s, %s(%s, %s, %zu))"
, leftValue.data()
, variable->m_name.data()
, nodeableName.data()
, Grammar::k_executionStateVariableName
, Grammar::k_UnpackDependencyConstructionArgsFunctionName
, Grammar::k_executionStateVariableName
, Grammar::k_DependentAssetsArgName
, indexInfo->first);
}
else // vs.
{
// !indexInfo->second.hasMoreDependencies
// self.leafDependency = LeafDependency.new(executionState, UnpackDependencyArgsLeaf(executionState, dependentAssets, 10))
// -- has NO more dependencies, index, known from compile time
m_dotLua.WriteLineIndented("%s%s = %s.new(%s, %s(%s, %s, %zu))"
, leftValue.data()
, variable->m_name.data()
, nodeableName.data()
, Grammar::k_executionStateVariableName
, Grammar::k_UnpackDependencyConstructionArgsLeafFunctionName
, Grammar::k_executionStateVariableName
, Grammar::k_DependentAssetsArgName
, indexInfo->first);
}
}
else
{
// self.leaf = Lef.new(executionState)
m_dotLua.WriteLineIndented("%s%s = %s.new(%s)"
, leftValue.data()
, variable->m_name.data()
, nodeableName.data()
, Grammar::k_executionStateVariableName);
}
}
else if (variable->m_isMember)
{
const auto constructionRequirement = ParseConstructionRequirement(variable);
switch (constructionRequirement)
{
case Grammar::VariableConstructionRequirement::InputEntityId:
case Grammar::VariableConstructionRequirement::InputVariable:
m_dotLua.WriteLineIndented("%s%s = %s", leftValue.data(), variable->m_name.data(), variable->m_name.data());
break;
case Grammar::VariableConstructionRequirement::None:
m_dotLua.WriteLineIndented("%s%s = %s", leftValue.data(), variable->m_name.data(), ToValueString(variable->m_datum, m_configuration).data());
break;
case Grammar::VariableConstructionRequirement::InputNodeable:
m_dotLua.WriteLineIndented("%s:InitializeExecutionState(%s)", variable->m_name.data(), Grammar::k_executionStateVariableName);
m_dotLua.WriteLineIndented("%s:%s()", variable->m_name.c_str(), Grammar::k_InitializeExecutionOutByRequiredCountName);
m_dotLua.WriteLineIndented("%s%s = %s", leftValue.data(), variable->m_name.data(), variable->m_name.data());
break;
case Grammar::VariableConstructionRequirement::Static:
{
auto iter = AZStd::find_if(staticVariableNames.begin(), staticVariableNames.end(), [&variable](auto& candidate) { return candidate.first == variable; });
if (iter != staticVariableNames.end())
{
m_dotLua.WriteLineIndented("%s%s = %s(rawget(%s, '%s'))", leftValue.data(), iter->first->m_name.c_str(), Grammar::k_CloneSourceFunctionName, m_tableName.c_str(), iter->second.c_str());
}
else
{
AddError(nullptr, aznew Internal::ParseError(AZ::EntityId(), "Missing static name for variable that requires static initializer"));
}
break;
}
default:
break;
}
}
}
// translate the event handling...initialize to nil, check for nil before disconnecting
TranslateEBusHandling(leftValue);
TranslateNodeableParse();
}
void GraphToLua::WriteConditionalCaseSwitch(Grammar::ExecutionTreeConstPtr execution, Grammar::Symbol symbol, const Grammar::ExecutionChild& child, size_t index)
{
if (symbol == Grammar::Symbol::RandomSwitch)
{
auto controlValue = execution->GetInput(execution->GetInputCount() - 2).m_value;
auto weightX = execution->GetInput(execution->GetChildrenCount() + index).m_value->m_name.data();
m_dotLua.Write("%s <= %s"
, controlValue->m_name.data()
, weightX);
}
else
{
WriteFunctionCallInput(execution);
m_dotLua.Write(" == ");
m_dotLua.Write(Grammar::SlotNameToIndexString(*child.m_slot));
}
}
void GraphToLua::WriteConstructionInput()
{
WriteConstructionDependencyArgs();
WriteConstructionArgs();
}
void GraphToLua::WriteConstructionArgs()
{
AZStd::vector<Grammar::VariableConstPtr> constructionArguments = m_model.CombineVariableLists(m_runtimeInputs.m_nodeables, m_runtimeInputs.m_variables, m_runtimeInputs.m_entityIds);
if (!constructionArguments.empty())
{
for (auto& constructionArgument : constructionArguments)
{
m_dotLua.Write(", %s", constructionArgument->m_name.data());
}
}
}
void GraphToLua::WriteConstructionDependencyArgs()
{
if (m_model.GetInterface().RequiresConstructionParametersForDependencies())
{
m_dotLua.Write(", %s", Grammar::k_DependentAssetsArgName);
}
}
void GraphToLua::WriteCycleBegin(Grammar::ExecutionTreeConstPtr execution)
{
auto variable = execution->GetInput(0).m_value;
m_dotLua.WriteIndent();
WriteVariableReference(variable);
m_dotLua.Write(" = (");
WriteVariableReference(variable);
m_dotLua.WriteLine(" + 1) %% %d", execution->GetChildrenCount());
}
void GraphToLua::WriteDebugInfoIn(Grammar::ExecutionTreeConstPtr execution, AZStd::string_view reason)
{
WriteDebugInfoIn(execution, reason, execution->GetInputCount());
}
void GraphToLua::WriteDebugInfoIn(Grammar::ExecutionTreeConstPtr execution, AZStd::string_view reason, size_t inputCountOverride)
{
if (IsDebugInfoWritten())
{
if (auto* debugIndex = m_model.GetDebugInfoInIndex(execution))
{
if (!m_model.IsPureLibrary())
{
m_dotLua.WriteIndented("%s(executionState, %d", Grammar::k_DebugSignalInName, *debugIndex);
}
else
{
m_dotLua.WriteIndented("%s(executionState, '%s', %d", Grammar::k_DebugSignalInSubgraphName, m_model.GetSourceString().data(), *debugIndex);
}
if (execution->GetInputCount() > 0)
{
m_dotLua.Write(", ");
WriteFunctionCallInput(execution, inputCountOverride);
}
m_dotLua.WriteLine(") -- %s", reason.data());
}
}
}
void GraphToLua::WriteDebugInfoOut(Grammar::ExecutionTreeConstPtr execution, size_t childIndex, AZStd::string_view reason)
{
if (IsDebugInfoWritten())
{
if (auto* debugIndex = m_model.GetDebugInfoOutIndex(execution, childIndex))
{
if (!m_model.IsPureLibrary())
{
m_dotLua.WriteIndented("%s(executionState, %d", Grammar::k_DebugSignalOutName, *debugIndex);
}
else
{
m_dotLua.WriteIndented("%s(executionState, '%s', %d", Grammar::k_DebugSignalOutSubgraphName, m_model.GetSourceString().data(), *debugIndex);
}
const auto& output = execution->GetChild(childIndex).m_output;
for (size_t index(0), sentinel(output.size()); index < sentinel; ++index)
{
m_dotLua.Write(", ");
WriteVariableReference(output[index].second->m_source);
}
m_dotLua.WriteLine(") -- %s", reason.data());
}
}
}
void GraphToLua::WriteDebugInfoReturn(Grammar::ExecutionTreeConstPtr execution, AZStd::string_view reason)
{
if (IsDebugInfoWritten())
{
if (auto* debugIndex = m_model.GetDebugInfoReturnIndex(execution))
{
if (!m_model.IsPureLibrary())
{
m_dotLua.WriteIndented("%s(executionState, %d", Grammar::k_DebugSignalReturnName, *debugIndex);
}
else
{
m_dotLua.WriteIndented("%s(executionState, '%s', %d", Grammar::k_DebugSignalReturnSubgraphName, m_model.GetSourceString().data(), *debugIndex);
}
for (size_t index(0), sentinel(execution->GetReturnValueCount()); index < sentinel; ++index)
{
m_dotLua.Write(", ");
WriteVariableReference(execution->GetReturnValue(index).second->m_source);
}
m_dotLua.WriteLine(") -- %s", reason.data());
}
}
}
void GraphToLua::WriteDebugInfoVariableChange(Grammar::ExecutionTreeConstPtr execution, Grammar::OutputAssignmentConstPtr output)
{
if (IsDebugInfoWritten())
{
if (auto* debugIndex = m_model.GetDebugInfoVariableSetIndex(output))
{
if (!m_model.IsPureLibrary())
{
m_dotLua.WriteIndented("%s(executionState, %d, ", Grammar::k_DebugVariableChangeName, *debugIndex);
WriteVariableReference(output->m_source);
m_dotLua.WriteLine(")");
}
else
{
m_dotLua.WriteIndented("%s(executionState, '%s', %d, ", Grammar::k_DebugVariableChangeSubgraphName, m_model.GetSourceString().data(), *debugIndex);
WriteVariableReference(output->m_source);
m_dotLua.WriteLine(")");
}
}
for (size_t assignmentIndex(0); assignmentIndex < output->m_assignments.size(); ++assignmentIndex)
{
auto& assignment = output->m_assignments[assignmentIndex];
if (auto* debugIndex = m_model.GetDebugInfoVariableAssignmentIndex(output, assignmentIndex))
{
if (!m_model.IsPureLibrary())
{
m_dotLua.WriteIndented("%s(executionState, %d, ", Grammar::k_DebugVariableChangeName, *debugIndex);
WriteVariableReference(assignment);
m_dotLua.WriteLine(")");
}
else
{
m_dotLua.WriteIndented("%s(executionState, '%s', %d, ", Grammar::k_DebugVariableChangeSubgraphName, m_model.GetSourceString().data(), *debugIndex);
WriteVariableReference(assignment);
m_dotLua.WriteLine(")");
}
}
}
}
}
void GraphToLua::WriteEventConnectCall(Grammar::ExecutionTreeConstPtr execution)
{
auto eventHandling = m_model.GetEventHandling(execution->GetId().m_node);
if (!eventHandling)
{
AddError(execution, aznew Internal::ParseError(execution->GetNodeId(), ParseErrors::BadEventHandlingAccounting));
return;
}
WriteEventDisconnectCall(execution, PostDisconnectAction::None);
m_dotLua.WriteIndent();
WriteVariableReference(eventHandling->m_handler);
m_dotLua.Write(" = ");
WriteEventConnectCaller(execution, eventHandling);
m_dotLua.WriteLine(":%s(", Grammar::k_AzEventHandlerConnectName);
m_dotLua.Indent();
TranslateFunction(eventHandling->m_eventHandlerFunction, IsNamed::No);
m_dotLua.WriteLine(")");
m_dotLua.Outdent();
}
void GraphToLua::WriteEventConnectCaller(Grammar::ExecutionTreeConstPtr execution, Grammar::EventHandlingConstPtr)
{
auto parent = execution->GetParent();
if (!parent)
{
AddError(execution, aznew Internal::ParseError(execution->GetNodeId(), ParseErrors::EventNodeConnectMissingParent));
return;
}
if (parent->GetChildrenCount() != 1)
{
AddError(execution, aznew Internal::ParseError(execution->GetNodeId(), ParseErrors::EventNodeConnectMissingChild));
return;
}
const auto& output = parent->GetChild(0).m_output;
if (output.size() != 1)
{
AddError(execution, aznew Internal::ParseError(execution->GetNodeId(), ParseErrors::EventNodeConnectMissingChildOutput));
return;
}
const auto& firstOutput = output[0].second;
if (firstOutput->m_source->m_source != parent)
{
AddError(execution, aznew Internal::ParseError(execution->GetNodeId(), ParseErrors::EventNodeConnectMissingChildOutputSourceNotSet));
return;
}
if (firstOutput->m_source->m_isMember)
{
AddError(execution, aznew Internal::ParseError(execution->GetNodeId(), ParseErrors::EventNodeConnectMissingChildOutputNotLocal));
return;
}
m_dotLua.Write(firstOutput->m_source->m_name.c_str());
}
void GraphToLua::WriteEventDisconnectCall(Grammar::ExecutionTreeConstPtr execution, PostDisconnectAction postAction)
{
WriteFunctionCallNullCheckPre(execution);
m_dotLua.WriteIndent();
WriteFunctionCallOfNode(execution, Grammar::k_AzEventHandlerDisconnectName, 1);
m_dotLua.WriteLine(")");
if (postAction == PostDisconnectAction::SetToNil)
{
m_dotLua.WriteIndent();
WriteVariableReference(execution->GetInput(0).m_value);
m_dotLua.WriteLine(" = nil");
}
WriteFunctionCallNullCheckPost(execution);
}
void GraphToLua::WriteFunctionCallNamespace(Grammar::ExecutionTreeConstPtr execution)
{
const Grammar::LexicalScope lexicalScope = execution->GetNameLexicalScope();
switch (lexicalScope.m_type)
{
case Grammar::LexicalScopeType::Class:
case Grammar::LexicalScopeType::Namespace:
if (!lexicalScope.m_namespaces.empty())
{
if (IsUserFunctionCall(execution) && !lexicalScope.m_namespaces.back().empty())
{
AZStd::string dependencyTableName = GraphToLuaCpp::ToDependencyTableName(lexicalScope.m_namespaces.back());
m_dotLua.Write("%s%.*s", dependencyTableName.c_str(),
aznumeric_cast<int>(m_configuration.m_lexicalScopeDelimiter.size()), m_configuration.m_lexicalScopeDelimiter.data());
}
else
{
const AZStd::string resolvedScope = ResolveScope(lexicalScope.m_namespaces);
auto& abbreviation = FindAbbreviation(resolvedScope);
if (!abbreviation.empty())
{
m_dotLua.Write("%s%.*s", abbreviation.c_str(),
aznumeric_cast<int>(m_configuration.m_lexicalScopeDelimiter.size()), m_configuration.m_lexicalScopeDelimiter.data());
}
else if (!resolvedScope.empty())
{
m_dotLua.Write("%s%.*s", resolvedScope.c_str(),
aznumeric_cast<int>(m_configuration.m_lexicalScopeDelimiter.size()), m_configuration.m_lexicalScopeDelimiter.data());
}
}
}
break;
case Grammar::LexicalScopeType::Variable:
{
WriteFunctionCallInput(execution, 0, IsFormatStringInput::No);
m_dotLua.Write(m_configuration.m_lexicalScopeVariable);
}
break;
default:
break;
}
}
void GraphToLua::WriteFunctionCallNullCheckPost(Grammar::ExecutionTreeConstPtr execution)
{
m_dotLua.Outdent();
m_dotLua.WriteLineIndented("end");
}
void GraphToLua::WriteFunctionCallNullCheckPre(Grammar::ExecutionTreeConstPtr execution)
{
m_dotLua.Write("if ");
WriteVariableReference(execution->GetInput(0).m_value);
m_dotLua.WriteLine(" ~= nil then");
m_dotLua.Indent();
}
void GraphToLua::WriteFunctionCallOfNode(Grammar::ExecutionTreeConstPtr execution, AZStd::string nameOverride, size_t inputOverride)
{
const AZStd::string name = nameOverride.empty() ? execution->GetName() : nameOverride;
if (name.empty())
{
AddError(execution, aznew InvalidFunctionCallNameValidation(execution->GetId().m_node->GetEntityId(), execution->GetId().m_slot->GetId()));
return;
}
const auto isNilCheckRequired = IsReturnValueNilCheckRequired(execution);
if (isNilCheckRequired.first == NilCheck::Single)
{
m_dotLua.Write("%s(", isNilCheckRequired.second.data());
}
else if (isNilCheckRequired.first == NilCheck::Multiple)
{
m_dotLua.Write("%s(", Grammar::k_TypeSafeEBusMultipleResultsName);
}
WriteFunctionCallNamespace(execution);
switch (execution->GetEventType())
{
case ScriptCanvas::EventType::Broadcast:
m_dotLua.Write("Broadcast.%s(", Grammar::ToIdentifier(name).data());
break;
case ScriptCanvas::EventType::BroadcastQueue:
m_dotLua.Write("QueueBroadcast.%s(", Grammar::ToIdentifier(name).data());
break;
case ScriptCanvas::EventType::Event:
m_dotLua.Write("Event.%s(", Grammar::ToIdentifier(name).data());
break;
case ScriptCanvas::EventType::EventQueue:
m_dotLua.Write("QueueEvent.%s(", Grammar::ToIdentifier(name).data());
break;
case ScriptCanvas::EventType::Count:
m_dotLua.Write("%s(", Grammar::ToIdentifier(name).data());
break;
default:
AddError(execution, aznew InvalidFunctionCallNameValidation(execution->GetId().m_node->GetEntityId(), execution->GetId().m_slot->GetId()));
break;
}
// #functions2 pure on graph start nodes with dependencies can only be added to the graph as variables
// if (execution->IsStart() && execution->IsPure())
// {
// WriteFunctionCallInputOfChildStart(execution);
// }
// else
{
WriteFunctionCallInput(execution, inputOverride);
}
if (isNilCheckRequired.first == NilCheck::Single)
{
m_dotLua.Write(")");
}
else if (isNilCheckRequired.first == NilCheck::Multiple)
{
m_dotLua.Write("), \"%s\"", isNilCheckRequired.second.data());
}
}
void GraphToLua::WriteForEachChildPost(Grammar::ExecutionTreeConstPtr execution, size_t index)
{
using namespace Grammar;
if (index != 0)
{
return;
}
auto meta = AZStd::rtti_pointer_cast<const ForEachMetaData>(execution->GetMetaData());
// nextFunc(iter)
m_dotLua.WriteLineIndented("%s(%s)"
, meta->m_nextFunctionVariableName.c_str()
, meta->m_iteratorVariableName.c_str());
m_dotLua.Outdent();
m_dotLua.WriteLineIndented("end");
}
void GraphToLua::WriteForEachChildPre(Grammar::ExecutionTreeConstPtr execution)
{
using namespace Grammar;
WriteDebugInfoIn(execution, "for-each-body WriteForEachChildPre");
auto meta = AZStd::rtti_pointer_cast<const ForEachMetaData>(execution->GetMetaData());
auto sourceVariable = execution->GetInput(0).m_value;
/***
* \note resist the temptation to put to much of these function calls in the ACM.
* They do not belong there. The ACM represents the grammar of Script Canvas (and the syntactical sugar of the nodes).
* The ACM does NOT represent the grammar of the back-ends. Don't let it overly accommodate them.
*/
// local iter = source:Iterate_VM()
m_dotLua.WriteIndented("local %s = ", meta->m_iteratorVariableName.c_str());
WriteVariableReference(sourceVariable);
m_dotLua.WriteLine(":%s()", AZ::k_iteratorConstructorName);
if (meta->m_isKeyRequired)
{
// local getKeyFunc = iter.GetKey
m_dotLua.WriteLineIndented("local %s = %s.%s"
, meta->m_keyFunctionVariableName.c_str()
, meta->m_iteratorVariableName.c_str()
, AZ::k_iteratorGetKeyName);
}
// local getValueFunc = iter.GetValue
m_dotLua.WriteLineIndented("local %s = %s.%s"
, meta->m_valueFunctionVariableName.c_str()
, meta->m_iteratorVariableName.c_str()
, AZ::k_iteratorModValueName);
// local isNotAtEndFunc = iter.IsNotAtEnd
m_dotLua.WriteLineIndented("local %s = %s.%s"
, meta->m_isNotAtEndFunctionVariableName.c_str()
, meta->m_iteratorVariableName.c_str()
, AZ::k_iteratorIsNotAtEndName);
// local nextFunc = iter.Next
m_dotLua.WriteLineIndented("local %s = %s.%s"
, meta->m_nextFunctionVariableName.c_str()
, meta->m_iteratorVariableName.c_str()
, AZ::k_iteratorNextName);
// while isNotAtEndFunc(iter) do
m_dotLua.WriteLineIndented("while %s(%s) do"
, meta->m_isNotAtEndFunctionVariableName.c_str()
, meta->m_iteratorVariableName.c_str());
m_dotLua.Indent();
}
void GraphToLua::WriteFunctionCallInput(Grammar::ExecutionTreeConstPtr execution)
{
WriteFunctionCallInput(execution, execution->GetInputCount());
}
void GraphToLua::WriteFunctionCallInput(Grammar::ExecutionTreeConstPtr execution, size_t inputCountOverride)
{
const size_t inputCount = execution->GetInputCount();
const size_t inputMax = AZStd::min(inputCount, inputCountOverride != inputCount ? inputCountOverride : inputCount);
const size_t startingIndex = WriteFunctionCallInputThisPointer(execution);
if (startingIndex < inputMax)
{
if (startingIndex > 0)
{
m_dotLua.Write(", ");
}
const IsFormatStringInput convertToStrings = (execution->GetId().m_node && execution->GetId().m_node->ConvertsInputToStrings()) ? IsFormatStringInput::Yes : IsFormatStringInput::No;
WriteFunctionCallInput(execution, startingIndex, convertToStrings);
for (size_t i = startingIndex + 1; i < inputMax; ++i)
{
m_dotLua.Write(", ");
WriteFunctionCallInput(execution, i, convertToStrings);
}
}
}
void GraphToLua::WriteFunctionCallInput(Grammar::ExecutionTreeConstPtr execution, size_t index, IsFormatStringInput isformatStingInput)
{
auto canWriteValue = [&](auto isformatStingInput, auto input)->bool
{
return isformatStingInput == IsFormatStringInput::No
|| input->m_datum.GetType() == Data::Type::Number()
|| input->m_datum.GetType() == Data::Type::String();
};
const auto isNamed = IsInputNamed(execution->GetInput(index).m_value, execution);
auto& input = execution->GetInput(index).m_value;
if (isNamed == IsNamed::No)
{
// just write the value
if (canWriteValue(isformatStingInput, input))
{
CheckConversion converter(m_dotLua, execution->GetInput(index).m_value, execution->GetConversions(), index);
m_dotLua.Write(ToValueString(input->m_datum, m_configuration));
}
else
{
if (!input->m_datum.Empty())
{
m_dotLua.Write("tostring(");
CheckConversion converter(m_dotLua, execution->GetInput(index).m_value, execution->GetConversions(), index);
m_dotLua.Write("%s)", ToValueString(input->m_datum, m_configuration).data());
}
else
{
m_dotLua.Write("''");
}
}
}
else
{
// write the by name reference
if (canWriteValue(isformatStingInput, input))
{
CheckConversion converter(m_dotLua, execution->GetInput(index).m_value, execution->GetConversions(), index);
WriteVariableReference(input);
}
else
{
m_dotLua.Write("tostring(");
CheckConversion converter(m_dotLua, execution->GetInput(index).m_value, execution->GetConversions(), index);
WriteVariableReference(input);
m_dotLua.Write(")");
}
}
}
// #functions2 pure on graph start nodes with dependencies can only by added to the graph as variables
// void GraphToLua::WriteFunctionCallInputOfChildStart(Grammar::ExecutionTreeConstPtr execution)
// {
// // auto pureStartVariable = m_model.GetPureStartVariable(execution);
// // and follow similar path in this function: TranslateVariableInitialization
// // if (AZStd::optional<AZStd::pair<size_t, Grammar::DependencyInfo>> indexInfo = m_model.CheckUserNodeableDependencyConstructionIndex(variable))
// }
size_t GraphToLua::WriteFunctionCallInputThisPointer(Grammar::ExecutionTreeConstPtr execution)
{
if (IsUserFunctionCallPure(execution))
{
m_dotLua.Write("%s", Grammar::k_executionStateVariableName);
if (execution->GetInputCount() > 0)
{
m_dotLua.Write(", ");
}
}
else if (execution->GetId().m_node)
{
const auto eventHandlingType = Grammar::CheckEventHandlingType(execution);
if (eventHandlingType == Grammar::EventHandingType::Event || eventHandlingType == Grammar::EventHandingType::EBus)
{
if (eventHandlingType == Grammar::EventHandingType::EBus)
{
m_dotLua.Write("self.%s", execution->GetInput(0).m_value->m_datum.GetAs<AZStd::string>()->data());
if (execution->GetInputCount() > 1)
{
// the address is supplied, Lua needs to know the type, the value will be written right after this call
auto azType = execution->GetInput(1).m_value->m_datum.GetType().GetAZType();
m_dotLua.Write(", \'%s\'", Execution::CreateStringFastFromId(azType).data());
}
}
else
{
m_dotLua.Write("self.%s", execution->GetInput(0).m_value->m_name.c_str());
}
return 1;
}
}
return 0;
}
void GraphToLua::WriteGlobalPropertyRead(Grammar::ExecutionTreeConstPtr execution)
{
m_dotLua.WriteLine(SanitizeFunctionCallName(execution->GetName()));
}
void GraphToLua::WriteHeader()
{
// no one will ever the see header or the do not modify, so these will not be necessary
// WriteCopyright(m_dotLua);
// WriteDoNotModify(m_dotLua);
// m_dotLua.WriteNewLine();
// m_dotLua.WriteLine("local sch = ScriptCanvasHost");
// m_dotLua.WriteNewLine();
}
void GraphToLua::WriteInfiniteLoopCheckPost(Grammar::ExecutionTreeConstPtr execution)
{
if (m_executionConfig == BuildConfiguration::Debug)
{
if (auto controlVariable = m_model.GetImplicitVariable(execution))
{
if (controlVariable->m_isMember)
{
const char* variableName = controlVariable->m_name.data();
m_dotLua.WriteLineIndented("self.%s = self.%s - 1", variableName, variableName);
}
else
{
const auto loopLimit = m_systemConfiguration.m_maxIterationsForInfiniteLoopDetection;
const char* variableName = controlVariable->m_name.data();
m_dotLua.WriteLineIndented("if %s < %d then", variableName, loopLimit > 0 ? loopLimit : k_DefaultLoopLimit);
m_dotLua.Indent();
m_dotLua.WriteLineIndented("%s = %s + 1", variableName, variableName);
m_dotLua.Outdent();
m_dotLua.WriteLineIndented("else");
m_dotLua.Indent();
m_dotLua.WriteLineIndented("%s(executionState, \"%s: Hit runtime loop limit in block: %s, symbol: %s\")"
, Grammar::k_DebugRuntimeErrorName
, m_tableName.c_str()
, execution->GetName().c_str()
, Grammar::GetSymbolName(execution->GetSymbol()));
m_dotLua.Outdent();
m_dotLua.WriteLineIndented("end");
}
}
else
{
AddError(execution, aznew Internal::ParseError(execution->GetNodeId(), ParseErrors::MissingInfiniteLoopDetectionVariable));
}
}
}
void GraphToLua::WriteInfiniteLoopCheckPre(Grammar::ExecutionTreeConstPtr execution)
{
if (m_executionConfig == BuildConfiguration::Debug)
{
if (auto controlVariable = m_model.GetImplicitVariable(execution))
{
if (controlVariable->m_isMember)
{
const auto loopLimit = m_systemConfiguration.m_maxHandlerStackDepth;
const char* variableName = controlVariable->m_name.data();
m_dotLua.WriteLineIndented("if self.%s < %d then", variableName, loopLimit > 0 ? loopLimit : k_DefaultLoopLimit);
m_dotLua.Indent();
m_dotLua.WriteLineIndented("self.%s = self.%s + 1", variableName, variableName);
m_dotLua.Outdent();
m_dotLua.WriteLineIndented("else");
m_dotLua.Indent();
m_dotLua.WriteLineIndented("%s(executionState, \"%s: Hit max handler stack depth in %s: \")", Grammar::k_DebugRuntimeErrorName, m_tableName.c_str(), execution->GetRoot()->GetName().c_str());
m_dotLua.Outdent();
m_dotLua.WriteLineIndented("end");
}
else
{
m_dotLua.WriteLineIndented("local %s = 0", controlVariable->m_name.data());
}
}
else
{
AddError(execution, aznew Internal::ParseError(execution->GetNodeId(), ParseErrors::MissingInfiniteLoopDetectionVariable));
}
}
}
void GraphToLua::WriteLocalInputCreation(Grammar::ExecutionTreeConstPtr functionBlock)
{
const auto& staticVariables = m_model.GetStaticVariablesNames(functionBlock);
for (const auto& intitializer : staticVariables)
{
AZ_Assert(!Grammar::IsCodeConstructable(intitializer.first), "parsing of local input creation requirement failed");
m_dotLua.WriteLineIndented("local %s = %s(rawget(%s, '%s'))", intitializer.first->m_name.c_str(), Grammar::k_CloneSourceFunctionName, m_tableName.c_str(), intitializer.second.c_str());
}
}
void GraphToLua::WriteLocalOutputInitialization(Grammar::ExecutionTreeConstPtr execution)
{
if (auto output = execution->GetLocalOutput())
{
if (output->size() <= 1)
{
// it will be done one line
return;
}
for (const auto& outputIter : *output)
{
if (outputIter.second->m_source->m_source == execution)
{
// until a need arises, don't bother initializing at start, wait for the first assignment
// m_dotLua.WriteLine("local %s = %s", output->m_source->m_name.data(), ToValueString(output->m_source->m_datum, m_configuration).data());
m_dotLua.WriteLine("local %s", outputIter.second->m_source->m_name.data());
m_dotLua.WriteIndent();
}
}
if (!Grammar::IsUserFunctionCall(execution))
{
m_dotLua.WriteLine("local %s", AddMultiReturnName().data());
m_dotLua.WriteIndent();
}
}
}
void GraphToLua::WriteLocalVariableInitializion(Grammar::ExecutionTreeConstPtr execution)
{
if (const auto& localDeclaredVariables = m_model.GetLocalVariables(execution))
{
for (const auto& variable : *localDeclaredVariables)
{
const auto requirement = ParseConstructionRequirement(variable);
if (requirement == Grammar::VariableConstructionRequirement::None || (requirement != Grammar::VariableConstructionRequirement::Static && !execution->IsStart()))
{
m_dotLua.WriteLineIndented("local %s = %s", variable->m_name.data(), ToValueString(variable->m_datum, m_configuration).data());
}
}
}
}
void GraphToLua::WriteFloatingPointErrorNumberEqualityComparision(Grammar::ExecutionTreeConstPtr execution)
{
m_dotLua.Write("math.abs(");
WriteFunctionCallInput(execution, 0, IsFormatStringInput::No);
m_dotLua.Write(" - ");
WriteFunctionCallInput(execution, 1, IsFormatStringInput::No);
if (execution->GetSymbol() == Grammar::Symbol::CompareEqual)
{
// math.abs(lhs - rhs) <= 0.000001
m_dotLua.Write(") <= %s", Grammar::k_LuaEpsilonString);
}
else
{
// math.abs(lhs - rhs) > 0.000001
m_dotLua.Write(") > %s", Grammar::k_LuaEpsilonString);
}
}
void GraphToLua::WriteLogicalExpression(Grammar::ExecutionTreeConstPtr execution)
{
if (execution->GetSymbol() == Grammar::Symbol::IsNull)
{
WriteFunctionCallInput(execution, 0, IsFormatStringInput::No);
m_dotLua.Write(" == nil ");
}
else if (execution->GetSymbol() == Grammar::Symbol::LogicalNOT)
{
m_dotLua.Write("not ");
WriteFunctionCallInput(execution, 0, IsFormatStringInput::No);
}
else
{
if (Grammar::IsFloatingPointNumberEqualityComparison(execution))
{
WriteFloatingPointErrorNumberEqualityComparision(execution);
}
else
{
WriteFunctionCallInput(execution, 0, IsFormatStringInput::No);
switch (execution->GetSymbol())
{
case Grammar::Symbol::CompareEqual:
m_dotLua.Write(" == ");
break;
case Grammar::Symbol::CompareGreater:
m_dotLua.Write(" > ");
break;
case Grammar::Symbol::CompareGreaterEqual:
m_dotLua.Write(" >= ");
break;
case Grammar::Symbol::CompareLess:
m_dotLua.Write(" < ");
break;
case Grammar::Symbol::CompareLessEqual:
m_dotLua.Write(" <= ");
break;
case Grammar::Symbol::CompareNotEqual:
m_dotLua.Write(" ~= ");
break;
case Grammar::Symbol::LogicalAND:
m_dotLua.Write(" and ");
break;
case Grammar::Symbol::LogicalOR:
m_dotLua.Write(" or ");
break;
default:
break;
}
WriteFunctionCallInput(execution, 1, IsFormatStringInput::No);
}
}
}
void GraphToLua::WritePreFirstCaseSwitch(Grammar::ExecutionTreeConstPtr execution, Grammar::Symbol symbol)
{
m_dotLua.WriteLineIndented("--[[ begin switch for Grammar::%s --]]", Grammar::GetSymbolName(symbol));
if (symbol == Grammar::Symbol::RandomSwitch)
{
size_t randomCount = execution->GetChildrenCount();
auto controlValue = execution->GetInput(execution->GetInputCount() - 2).m_value;
auto runningTotalName = execution->GetInput(execution->GetInputCount() - 1).m_value->m_name;
// local runningTotal = 0
m_dotLua.WriteLineIndented("local %s = 0", runningTotalName.data());
for (size_t index = 0; index < randomCount; ++index)
{
auto weightX = execution->GetInput(randomCount + index).m_value->m_name.data();
// local weightX = runningTotal + inputX
m_dotLua.WriteIndented("local %s = %s + "
, weightX
, runningTotalName.data());
WriteFunctionCallInput(execution, index, IsFormatStringInput::No);
m_dotLua.WriteNewLine();
// runningTotal = weightX
m_dotLua.WriteLineIndented("%s = %s", runningTotalName.data(), weightX);
}
// local switchControl = RandomSwitchControlNumberFunctionName(runningTotal)
m_dotLua.WriteLineIndented("local %s = %s(%s)"
, controlValue->m_name.data()
, Grammar::k_GetRandomSwitchControlNumberName
, runningTotalName.data());
WriteDebugInfoIn(execution, "random-in TranslateExecutionTreeChildPre", randomCount);
}
else
{
WriteDebugInfoIn(execution, "switch-in TranslateExecutionTreeChildPre");
}
}
void GraphToLua::WriteOnVariableWritten(Grammar::ExecutionTreeConstPtr execution, const AZStd::vector<AZStd::pair<const Slot*, Grammar::OutputAssignmentConstPtr>>& output)
{
if (!output.empty())
{
auto firstOutput = output[0].second;
if (!(output.size() == 1 && firstOutput->m_source->m_source == execution))
{
if (output.size() > 1)
{
if (!Grammar::IsUserFunctionCall(execution))
{
const auto& multiReturnName = GetMultiReturnName();
for (int i = 0; i < output.size(); ++i)
{
m_dotLua.WriteIndent();
if (output[i].second->m_source->m_isMember && !m_model.GetVariableHandling(output[i].second->m_source).empty())
{
// variable required inequality check
m_dotLua.Write("local %s_Copy", output[i].second->m_source->m_name.data());
}
else
{
WriteVariableReference(output[i].second->m_source);
}
m_dotLua.WriteLine(" = %s:Get%d()", multiReturnName.data(), i);
WriteOnVariableWritten(output[i].second->m_source);
}
}
}
else
{
WriteOnVariableWritten(firstOutput->m_source);
}
}
}
}
bool GraphToLua::WriteOnVariableWritten(Grammar::VariableConstPtr variable)
{
auto variableHandlingSet = m_model.GetVariableHandling(variable);
if (variableHandlingSet.empty())
{
return false;
}
if (variable->m_isMember)
{
m_dotLua.WriteLineIndented("if self.%s ~= %s_Copy then", variable->m_name.data(), variable->m_name.data());
m_dotLua.Indent();
m_dotLua.WriteLineIndented("self.%s = %s_Copy", variable->m_name.data(), variable->m_name.data());
}
for (auto handling : variableHandlingSet)
{
const bool requiresConnectionControl = handling->RequiresConnectionControl();
if (requiresConnectionControl)
{
// \todo handle pure here execution if it ever comes up
m_dotLua.WriteLineIndented("if self.%s then", handling->m_connectionVariable->m_name.data());
m_dotLua.Indent();
}
m_dotLua.WriteLineIndented("self:%s()", handling->m_function->GetName().data());
if (requiresConnectionControl)
{
m_dotLua.Outdent();
m_dotLua.WriteLineIndented("end");
}
}
if (variable->m_isMember)
{
m_dotLua.Outdent();
m_dotLua.WriteLineIndented("end");
}
return true;
}
void GraphToLua::WriteOperatorArithmetic(Grammar::ExecutionTreeConstPtr execution)
{
// \todo write safety check code for all divisors against near zero division
const auto count = execution->GetInputCount();
if (count < 2)
{
AddError(execution, aznew Internal::ParseError(execution->GetNodeId(), ParseErrors::NotEnoughInputForArithmeticOperator));
return;
}
const AZStd::string_view operatorString = GetOperatorString(execution);
for (size_t i(0); i < (count - 1); ++i)
{
m_dotLua.Write("(");
}
// write operand 0 + operand 1
WriteFunctionCallInput(execution, 0, IsFormatStringInput::No);
m_dotLua.Write(operatorString);
WriteFunctionCallInput(execution, 1, IsFormatStringInput::No);
m_dotLua.Write(")");
for (size_t i(2); i < count; ++i)
{
m_dotLua.Write(operatorString);
WriteFunctionCallInput(execution, i, IsFormatStringInput::No);
m_dotLua.Write(")");
}
}
void GraphToLua::WriteOutputAssignments(Grammar::ExecutionTreeConstPtr execution)
{
if (const auto output = execution->GetLocalOutput())
{
WriteOutputAssignments(execution, *output);
}
}
void GraphToLua::WriteOutputAssignments(Grammar::ExecutionTreeConstPtr execution, const AZStd::vector<AZStd::pair<const Slot*, Grammar::OutputAssignmentConstPtr>>& output)
{
for (auto outputIter : output)
{
for (size_t i(0); i < outputIter.second->m_assignments.size(); ++i)
{
auto& assignment = outputIter.second->m_assignments[i];
const bool variableRequiresInequalityCheck = assignment->m_isMember && !m_model.GetVariableHandling(assignment).empty();
m_dotLua.WriteIndent();
if (variableRequiresInequalityCheck)
{
m_dotLua.Write("local %s_Copy = ", assignment->m_name.data());
}
else
{
WriteVariableReference(assignment);
m_dotLua.Write(" = ");
}
WriteVariableReadConvertible(outputIter.second->m_sourceConversions, i, outputIter.second->m_source);
m_dotLua.WriteNewLine();
WriteOnVariableWritten(assignment);
}
WriteDebugInfoVariableChange(execution, outputIter.second);
}
}
void GraphToLua::WriteReturnStatement(Grammar::ExecutionTreeConstPtr execution)
{
if (execution->HasReturnValues() && !execution->HasExplicitUserOutCalls())
{
WriteDebugInfoReturn(execution, "WriteReturnStatement");
m_dotLua.WriteIndented("return ");
WriteVariableReference(execution->GetReturnValue(0).second->m_source);
for (int i = 1; i < execution->GetReturnValueCount(); ++i)
{
m_dotLua.Write(", ");
WriteVariableReference(execution->GetReturnValue(i).second->m_source);
}
m_dotLua.WriteNewLine();
}
}
void GraphToLua::WriteReturnValueInitialization(Grammar::ExecutionTreeConstPtr execution)
{
if (execution->HasReturnValues())
{
for (size_t index(0), sentinel(execution->GetReturnValueCount()); index < sentinel; ++index)
{
const auto& slotAndReturnValue = execution->GetReturnValue(index);
const auto& returnValue = slotAndReturnValue.second;
if (returnValue->m_isNewValue)
{
if (returnValue->m_initializationValue)
{
m_dotLua.WriteIndented("local %s = ", returnValue->m_source->m_name.data());
WriteVariableRead(returnValue->m_initializationValue);
m_dotLua.WriteNewLine();
}
else
{
m_dotLua.WriteLineIndented("local %s = %s", returnValue->m_source->m_name.data(), ToValueString(returnValue->m_source->m_datum, m_configuration).data());
}
}
}
}
}
void GraphToLua::WriteSwitchEnd(Grammar::Symbol symbol)
{
m_dotLua.WriteLineIndented("else");
m_dotLua.Indent();
m_dotLua.WriteLineIndented("--[[ report an error --]]");
m_dotLua.Outdent();
m_dotLua.WriteLineIndented("end");
m_dotLua.WriteLineIndented("--[[ end switch for Grammar::%s --]]", Grammar::GetSymbolName(symbol));
}
void GraphToLua::WriteStaticInitializerInput(IsLeadingCommaRequired commaRequired)
{
AZ_Assert(!m_runtimeInputs.m_staticVariables.empty(), "don't write static initialization without needing to");
if (commaRequired == IsLeadingCommaRequired::Yes)
{
m_dotLua.Write(", ");
}
auto& variables = m_model.GetStaticVariablesNames();
m_dotLua.Write(variables.front().first->m_name.data());
for (size_t i = 1; i < variables.size(); ++i)
{
m_dotLua.Write(", %s", variables[i].first->m_name.data());
}
}
void GraphToLua::WriteVariableRead(Grammar::VariableConstPtr variable)
{
AZ_Assert(variable, "non valid variable");
WriteVariableReference(variable);
if (IsReferenceInLuaAndValueInScriptCanvas(variable->m_datum.GetType()))
{
m_dotLua.Write(":Clone()");
}
}
void GraphToLua::WriteVariableReadConvertible(const Grammar::ConversionByIndex& conversions, size_t index, Grammar::VariableConstPtr source)
{
auto iter = conversions.find(index);
if (iter != conversions.end())
{
CheckConversion converter(m_dotLua, source, conversions, index);
WriteVariableReference(source);
}
else
{
WriteVariableRead(source);
}
}
void GraphToLua::WriteVariableReference(Grammar::VariableConstPtr variable)
{
AZ_Assert(variable, "non valid variable");
if (variable->m_isMember)
{
m_dotLua.Write("self.");
}
m_dotLua.Write(variable->m_name.data());
}
void GraphToLua::WriteVariableWrite(Grammar::ExecutionTreeConstPtr execution, const AZStd::vector<AZStd::pair<const Slot*, Grammar::OutputAssignmentConstPtr>>& output)
{
if (!output.empty())
{
if (output.size() > 1)
{
if (Grammar::IsUserFunctionCall(execution))
{
auto firstOutput = output[0].second;
WriteVariableReference(firstOutput->m_source);
for (size_t multiReturnIndex(1), sentinel(output.size()); multiReturnIndex < sentinel; ++ multiReturnIndex)
{
m_dotLua.Write(", ");
WriteVariableReference(output[multiReturnIndex].second->m_source);
}
m_dotLua.Write(" = ");
}
else
{
const auto& multiReturnName = GetMultiReturnName();
m_dotLua.Write("%s = ", multiReturnName.data());
}
}
else
{
auto firstOutput = output[0].second;
const bool variableRequiresInequalityCheck = firstOutput->m_source->m_isMember && !m_model.GetVariableHandling(firstOutput->m_source).empty();
if (firstOutput->m_source->m_source == execution)
{
AZ_Assert(!firstOutput->m_source->m_isMember, "this should never be true");
m_dotLua.Write("local %s = ", firstOutput->m_source->m_name.data());
}
else if (variableRequiresInequalityCheck)
{
m_dotLua.Write("local %s_Copy = ", firstOutput->m_source->m_name.data());
}
else
{
WriteVariableReference(firstOutput->m_source);
m_dotLua.Write(" = ");
}
}
}
}
void GraphToLua::WriteWrittenMathExpression(Grammar::ExecutionTreeConstPtr execution)
{
auto meta = AZStd::rtti_pointer_cast<const Grammar::MathExpressionMetaData>(execution->GetMetaData());
AZ_Assert(meta, "math expression needs meta data");
size_t inputIndex = 0;
const size_t inputStringSentinel = meta->m_expressionString.size();
const auto& inputString = meta->m_expressionString;
for (size_t inputStringIndex = 0; inputStringIndex < inputStringSentinel; inputStringIndex++)
{
auto character = inputString[inputStringIndex];
if (character == '@')
{
WriteFunctionCallInput(execution, inputIndex, IsFormatStringInput::No);
++inputIndex;
}
else
{
AZStd::string oneChar;
oneChar.push_back(character);
m_dotLua.Write(oneChar);
}
}
}
}
}