Files
Mementos/Unity/Alternate Genre Jam/Assets/Ink/InkLibs/InkCompiler/ParsedHierarchy/DivertTarget.cs
2021-06-30 21:39:19 +10:00

173 lines
6.9 KiB
C#

namespace Ink.Parsed
{
public class DivertTarget : Expression
{
public Divert divert;
public DivertTarget (Divert divert)
{
this.divert = AddContent(divert);
}
public override void GenerateIntoContainer (Runtime.Container container)
{
divert.GenerateRuntimeObject();
_runtimeDivert = (Runtime.Divert) divert.runtimeDivert;
_runtimeDivertTargetValue = new Runtime.DivertTargetValue ();
container.AddContent (_runtimeDivertTargetValue);
}
public override void ResolveReferences (Story context)
{
base.ResolveReferences (context);
if( divert.isDone || divert.isEnd )
{
Error("Can't Can't use -> DONE or -> END as variable divert targets", this);
return;
}
Parsed.Object usageContext = this;
while (usageContext && usageContext is Expression) {
bool badUsage = false;
bool foundUsage = false;
var usageParent = usageContext.parent;
if (usageParent is BinaryExpression) {
// Only allowed to compare for equality
var binaryExprParent = usageParent as BinaryExpression;
if (binaryExprParent.opName != "==" && binaryExprParent.opName != "!=") {
badUsage = true;
} else {
if (!(binaryExprParent.leftExpression is DivertTarget || binaryExprParent.leftExpression is VariableReference)) {
badUsage = true;
}
if (!(binaryExprParent.rightExpression is DivertTarget || binaryExprParent.rightExpression is VariableReference)) {
badUsage = true;
}
}
foundUsage = true;
}
else if( usageParent is FunctionCall ) {
var funcCall = usageParent as FunctionCall;
if( !funcCall.isTurnsSince && !funcCall.isReadCount ) {
badUsage = true;
}
foundUsage = true;
}
else if (usageParent is Expression) {
badUsage = true;
foundUsage = true;
}
else if (usageParent is MultipleConditionExpression) {
badUsage = true;
foundUsage = true;
} else if (usageParent is Choice && ((Choice)usageParent).condition == usageContext) {
badUsage = true;
foundUsage = true;
} else if (usageParent is Conditional || usageParent is ConditionalSingleBranch) {
badUsage = true;
foundUsage = true;
}
if (badUsage) {
Error ("Can't use a divert target like that. Did you intend to call '" + divert.target + "' as a function: likeThis(), or check the read count: likeThis, with no arrows?", this);
}
if (foundUsage)
break;
usageContext = usageParent;
}
// Example ink for this case:
//
// VAR x = -> blah
//
// ...which means that "blah" is expected to be a literal stitch target rather
// than a variable name. We can't really intelligently recover from this (e.g. if blah happens to
// contain a divert target itself) since really we should be generating a variable reference
// rather than a concrete DivertTarget, so we list it as an error.
if (_runtimeDivert.hasVariableTarget)
Error ("Since '"+divert.target.dotSeparatedComponents+"' is a variable, it shouldn't be preceded by '->' here.");
// Main resolve
_runtimeDivertTargetValue.targetPath = _runtimeDivert.targetPath;
// Tell hard coded (yet variable) divert targets that they also need to be counted
// TODO: Only detect DivertTargets that are values rather than being used directly for
// read or turn counts. Should be able to detect this by looking for other uses of containerForCounting
var targetContent = this.divert.targetContent;
if (targetContent != null ) {
var target = targetContent.containerForCounting;
if (target != null)
{
// Purpose is known: used directly in TURNS_SINCE(-> divTarg)
var parentFunc = this.parent as FunctionCall;
if( parentFunc && parentFunc.isTurnsSince ) {
target.turnIndexShouldBeCounted = true;
}
// Unknown purpose, count everything
else {
target.visitsShouldBeCounted = true;
target.turnIndexShouldBeCounted = true;
}
}
// Unfortunately not possible:
// https://github.com/inkle/ink/issues/538
//
// VAR func = -> double
//
// === function double(ref x)
// ~ x = x * 2
//
// Because when generating the parameters for a function
// to be called, it needs to know ahead of time when
// compiling whether to pass a variable reference or value.
//
var targetFlow = (targetContent as FlowBase);
if (targetFlow != null && targetFlow.arguments != null)
{
foreach(var arg in targetFlow.arguments) {
if(arg.isByReference)
{
Error("Can't store a divert target to a knot or function that has by-reference arguments ('"+targetFlow.identifier+"' has 'ref "+arg.identifier+"').");
}
}
}
}
}
// Equals override necessary in order to check for CONST multiple definition equality
public override bool Equals (object obj)
{
var otherDivTarget = obj as DivertTarget;
if (otherDivTarget == null) return false;
var targetStr = this.divert.target.dotSeparatedComponents;
var otherTargetStr = otherDivTarget.divert.target.dotSeparatedComponents;
return targetStr.Equals (otherTargetStr);
}
public override int GetHashCode ()
{
var targetStr = this.divert.target.dotSeparatedComponents;
return targetStr.GetHashCode ();
}
Runtime.DivertTargetValue _runtimeDivertTargetValue;
Runtime.Divert _runtimeDivert;
}
}