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

243 lines
10 KiB
C#

using System.Collections.Generic;
namespace Ink.Parsed
{
public class FunctionCall : Expression
{
public string name { get { return _proxyDivert.target.firstComponent; } }
public Divert proxyDivert { get { return _proxyDivert; } }
public List<Expression> arguments { get { return _proxyDivert.arguments; } }
public Runtime.Divert runtimeDivert { get { return _proxyDivert.runtimeDivert; } }
public bool isChoiceCount { get { return name == "CHOICE_COUNT"; } }
public bool isTurns { get { return name == "TURNS"; } }
public bool isTurnsSince { get { return name == "TURNS_SINCE"; } }
public bool isRandom { get { return name == "RANDOM"; } }
public bool isSeedRandom { get { return name == "SEED_RANDOM"; } }
public bool isListRange { get { return name == "LIST_RANGE"; } }
public bool isListRandom { get { return name == "LIST_RANDOM"; } }
public bool isReadCount { get { return name == "READ_COUNT"; } }
public bool shouldPopReturnedValue;
public FunctionCall (Identifier functionName, List<Expression> arguments)
{
_proxyDivert = new Parsed.Divert(new Path(functionName), arguments);
_proxyDivert.isFunctionCall = true;
AddContent (_proxyDivert);
}
public override void GenerateIntoContainer (Runtime.Container container)
{
var foundList = story.ResolveList (name);
bool usingProxyDivert = false;
if (isChoiceCount) {
if (arguments.Count > 0)
Error ("The CHOICE_COUNT() function shouldn't take any arguments");
container.AddContent (Runtime.ControlCommand.ChoiceCount ());
} else if (isTurns) {
if (arguments.Count > 0)
Error ("The TURNS() function shouldn't take any arguments");
container.AddContent (Runtime.ControlCommand.Turns ());
} else if (isTurnsSince || isReadCount) {
var divertTarget = arguments [0] as DivertTarget;
var variableDivertTarget = arguments [0] as VariableReference;
if (arguments.Count != 1 || (divertTarget == null && variableDivertTarget == null)) {
Error ("The " + name + "() function should take one argument: a divert target to the target knot, stitch, gather or choice you want to check. e.g. TURNS_SINCE(-> myKnot)");
return;
}
if (divertTarget) {
_divertTargetToCount = divertTarget;
AddContent (_divertTargetToCount);
_divertTargetToCount.GenerateIntoContainer (container);
} else {
_variableReferenceToCount = variableDivertTarget;
AddContent (_variableReferenceToCount);
_variableReferenceToCount.GenerateIntoContainer (container);
}
if (isTurnsSince)
container.AddContent (Runtime.ControlCommand.TurnsSince ());
else
container.AddContent (Runtime.ControlCommand.ReadCount ());
} else if (isRandom) {
if (arguments.Count != 2)
Error ("RANDOM should take 2 parameters: a minimum and a maximum integer");
// We can type check single values, but not complex expressions
for (int arg = 0; arg < arguments.Count; arg++) {
if (arguments [arg] is Number) {
var num = arguments [arg] as Number;
if (!(num.value is int)) {
string paramName = arg == 0 ? "minimum" : "maximum";
Error ("RANDOM's " + paramName + " parameter should be an integer");
}
}
arguments [arg].GenerateIntoContainer (container);
}
container.AddContent (Runtime.ControlCommand.Random ());
} else if (isSeedRandom) {
if (arguments.Count != 1)
Error ("SEED_RANDOM should take 1 parameter - an integer seed");
var num = arguments [0] as Number;
if (num && !(num.value is int)) {
Error ("SEED_RANDOM's parameter should be an integer seed");
}
arguments [0].GenerateIntoContainer (container);
container.AddContent (Runtime.ControlCommand.SeedRandom ());
} else if (isListRange) {
if (arguments.Count != 3)
Error ("LIST_RANGE should take 3 parameters - a list, a min and a max");
for (int arg = 0; arg < arguments.Count; arg++)
arguments [arg].GenerateIntoContainer (container);
container.AddContent (Runtime.ControlCommand.ListRange ());
} else if( isListRandom ) {
if (arguments.Count != 1)
Error ("LIST_RANDOM should take 1 parameter - a list");
arguments [0].GenerateIntoContainer (container);
container.AddContent (Runtime.ControlCommand.ListRandom ());
} else if (Runtime.NativeFunctionCall.CallExistsWithName (name)) {
var nativeCall = Runtime.NativeFunctionCall.CallWithName (name);
if (nativeCall.numberOfParameters != arguments.Count) {
var msg = name + " should take " + nativeCall.numberOfParameters + " parameter";
if (nativeCall.numberOfParameters > 1)
msg += "s";
Error (msg);
}
for (int arg = 0; arg < arguments.Count; arg++)
arguments [arg].GenerateIntoContainer (container);
container.AddContent (Runtime.NativeFunctionCall.CallWithName (name));
} else if (foundList != null) {
if (arguments.Count > 1)
Error ("Can currently only construct a list from one integer (or an empty list from a given list definition)");
// List item from given int
if (arguments.Count == 1) {
container.AddContent (new Runtime.StringValue (name));
arguments [0].GenerateIntoContainer (container);
container.AddContent (Runtime.ControlCommand.ListFromInt ());
}
// Empty list with given origin.
else {
var list = new Runtime.InkList ();
list.SetInitialOriginName (name);
container.AddContent (new Runtime.ListValue (list));
}
}
// Normal function call
else {
container.AddContent (_proxyDivert.runtimeObject);
usingProxyDivert = true;
}
// Don't attempt to resolve as a divert if we're not doing a normal function call
if( !usingProxyDivert ) content.Remove (_proxyDivert);
// Function calls that are used alone on a tilda-based line:
// ~ func()
// Should tidy up any returned value from the evaluation stack,
// since it's unused.
if (shouldPopReturnedValue)
container.AddContent (Runtime.ControlCommand.PopEvaluatedValue ());
}
public override void ResolveReferences (Story context)
{
base.ResolveReferences (context);
// If we aren't using the proxy divert after all (e.g. if
// it's a native function call), but we still have arguments,
// we need to make sure they get resolved since the proxy divert
// is no longer in the content array.
if (!content.Contains(_proxyDivert) && arguments != null) {
foreach (var arg in arguments)
arg.ResolveReferences (context);
}
if( _divertTargetToCount ) {
var divert = _divertTargetToCount.divert;
var attemptingTurnCountOfVariableTarget = divert.runtimeDivert.variableDivertName != null;
if( attemptingTurnCountOfVariableTarget ) {
Error("When getting the TURNS_SINCE() of a variable target, remove the '->' - i.e. it should just be TURNS_SINCE("+divert.runtimeDivert.variableDivertName+")");
return;
}
var targetObject = divert.targetContent;
if( targetObject == null ) {
if( !attemptingTurnCountOfVariableTarget ) {
Error("Failed to find target for TURNS_SINCE: '"+divert.target+"'");
}
} else {
targetObject.containerForCounting.turnIndexShouldBeCounted = true;
}
}
else if( _variableReferenceToCount ) {
var runtimeVarRef = _variableReferenceToCount.runtimeVarRef;
if( runtimeVarRef.pathForCount != null ) {
Error("Should be "+name+"(-> "+_variableReferenceToCount.name+"). Usage without the '->' only makes sense for variable targets.");
}
}
}
public static bool IsBuiltIn(string name)
{
if (Runtime.NativeFunctionCall.CallExistsWithName (name))
return true;
return name == "CHOICE_COUNT"
|| name == "TURNS_SINCE"
|| name == "TURNS"
|| name == "RANDOM"
|| name == "SEED_RANDOM"
|| name == "LIST_VALUE"
|| name == "LIST_RANDOM"
|| name == "READ_COUNT";
}
public override string ToString ()
{
var strArgs = string.Join (", ", arguments.ToStringsArray());
return string.Format ("{0}({1})", name, strArgs);
}
Parsed.Divert _proxyDivert;
Parsed.DivertTarget _divertTargetToCount;
Parsed.VariableReference _variableReferenceToCount;
}
}