-
Notifications
You must be signed in to change notification settings - Fork 477
/
FunctionCall.cs
227 lines (180 loc) · 9.63 KB
/
FunctionCall.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
using System.Collections.Generic;
namespace Ink.Parsed
{
internal class FunctionCall : Expression
{
public string name { get { return _proxyDivert.target.firstComponent; } }
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 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 isReadCount { get { return name == "READ_COUNT"; } }
public bool shouldPopReturnedValue;
public FunctionCall (string 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);
if (isChoiceCount) {
if (arguments.Count > 0)
Error ("The CHOICE_COUNT() function shouldn't take any arguments");
container.AddContent (Runtime.ControlCommand.ChoiceCount ());
} 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 (!story.countAllVisits) {
Error ("Attempting to get TURNS_SINCE for a variable target without -c compiler option. You need the compiler switch turned on so that it can track turn counts for everything, not just those you directly reference.");
}
}
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_VALUE 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 ());
// Don't attempt to resolve as a divert
content.Remove (_proxyDivert);
} 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));
// Don't attempt to resolve as a divert
content.Remove (_proxyDivert);
} 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));
}
// Don't attempt to resolve as a divert
content.Remove (_proxyDivert);
}
// Normal function call
else {
container.AddContent (_proxyDivert.runtimeObject);
}
// 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 == "RANDOM"
|| name == "SEED_RANDOM"
|| name == "LIST_VALUE"
|| name == "READ_COUNT";
}
public override string ToString ()
{
var strArgs = string.Join (", ", arguments);
return string.Format ("{0}({1})", name, strArgs);
}
Parsed.Divert _proxyDivert;
Parsed.DivertTarget _divertTargetToCount;
Parsed.VariableReference _variableReferenceToCount;
}
}