mirror of
https://github.com/Ratstail91/Mementos.git
synced 2025-11-29 02:24:28 +11:00
153 lines
6.1 KiB
C#
153 lines
6.1 KiB
C#
using System.Collections.Generic;
|
|
using System.Linq;
|
|
|
|
namespace Ink.Parsed
|
|
{
|
|
public class VariableReference : Expression
|
|
{
|
|
// - Normal variables have a single item in their "path"
|
|
// - Knot/stitch names for read counts are actual dot-separated paths
|
|
// (though this isn't actually used at time of writing)
|
|
// - List names are dot separated: listName.itemName (or just itemName)
|
|
public string name { get; private set; }
|
|
|
|
public Identifier identifier {
|
|
get {
|
|
// Merging the list of identifiers into a single identifier.
|
|
// Debug metadata is also merged.
|
|
if (pathIdentifiers == null || pathIdentifiers.Count == 0) {
|
|
return null;
|
|
}
|
|
|
|
if( _singleIdentifier == null ) {
|
|
var name = string.Join (".", path.ToArray());
|
|
var firstDebugMetadata = pathIdentifiers.First().debugMetadata;
|
|
var debugMetadata = pathIdentifiers.Aggregate(firstDebugMetadata, (acc, id) => acc.Merge(id.debugMetadata));
|
|
_singleIdentifier = new Identifier { name = name, debugMetadata = debugMetadata };
|
|
}
|
|
|
|
return _singleIdentifier;
|
|
}
|
|
}
|
|
Identifier _singleIdentifier;
|
|
|
|
public List<Identifier> pathIdentifiers;
|
|
public List<string> path { get; private set; }
|
|
|
|
// Only known after GenerateIntoContainer has run
|
|
public bool isConstantReference;
|
|
public bool isListItemReference;
|
|
|
|
public Runtime.VariableReference runtimeVarRef { get { return _runtimeVarRef; } }
|
|
|
|
public VariableReference (List<Identifier> pathIdentifiers)
|
|
{
|
|
this.pathIdentifiers = pathIdentifiers;
|
|
this.path = pathIdentifiers.Select(id => id?.name).ToList();
|
|
this.name = string.Join (".", pathIdentifiers);
|
|
}
|
|
|
|
public override void GenerateIntoContainer (Runtime.Container container)
|
|
{
|
|
Expression constantValue = null;
|
|
|
|
// If it's a constant reference, just generate the literal expression value
|
|
// It's okay to access the constants at code generation time, since the
|
|
// first thing the ExportRuntime function does it search for all the constants
|
|
// in the story hierarchy, so they're all available.
|
|
if ( story.constants.TryGetValue (name, out constantValue) ) {
|
|
constantValue.GenerateConstantIntoContainer (container);
|
|
isConstantReference = true;
|
|
return;
|
|
}
|
|
|
|
_runtimeVarRef = new Runtime.VariableReference (name);
|
|
|
|
// List item reference?
|
|
// Path might be to a list (listName.listItemName or just listItemName)
|
|
if (path.Count == 1 || path.Count == 2) {
|
|
string listItemName = null;
|
|
string listName = null;
|
|
|
|
if (path.Count == 1) listItemName = path [0];
|
|
else {
|
|
listName = path [0];
|
|
listItemName = path [1];
|
|
}
|
|
|
|
var listItem = story.ResolveListItem (listName, listItemName, this);
|
|
if (listItem) {
|
|
isListItemReference = true;
|
|
}
|
|
}
|
|
|
|
container.AddContent (_runtimeVarRef);
|
|
}
|
|
|
|
public override void ResolveReferences (Story context)
|
|
{
|
|
base.ResolveReferences (context);
|
|
|
|
// Work is already done if it's a constant or list item reference
|
|
if (isConstantReference || isListItemReference) {
|
|
return;
|
|
}
|
|
|
|
// Is it a read count?
|
|
var parsedPath = new Path (pathIdentifiers);
|
|
Parsed.Object targetForCount = parsedPath.ResolveFromContext (this);
|
|
if (targetForCount) {
|
|
|
|
targetForCount.containerForCounting.visitsShouldBeCounted = true;
|
|
|
|
// If this is an argument to a function that wants a variable to be
|
|
// passed by reference, then the Parsed.Divert will have generated a
|
|
// Runtime.VariablePointerValue instead of allowing this object
|
|
// to generate its RuntimeVariableReference. This only happens under
|
|
// error condition since we shouldn't be passing a read count by
|
|
// reference, but we don't want it to crash!
|
|
if (_runtimeVarRef == null) return;
|
|
|
|
_runtimeVarRef.pathForCount = targetForCount.runtimePath;
|
|
_runtimeVarRef.name = null;
|
|
|
|
// Check for very specific writer error: getting read count and
|
|
// printing it as content rather than as a piece of logic
|
|
// e.g. Writing {myFunc} instead of {myFunc()}
|
|
var targetFlow = targetForCount as FlowBase;
|
|
if (targetFlow && targetFlow.isFunction) {
|
|
|
|
// Is parent context content rather than logic?
|
|
if ( parent is Weave || parent is ContentList || parent is FlowBase) {
|
|
Warning ("'" + targetFlow.identifier + "' being used as read count rather than being called as function. Perhaps you intended to write " + targetFlow.name + "()");
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
// Couldn't find this multi-part path at all, whether as a divert
|
|
// target or as a list item reference.
|
|
if (path.Count > 1) {
|
|
var errorMsg = "Could not find target for read count: " + parsedPath;
|
|
if (path.Count <= 2)
|
|
errorMsg += ", or couldn't find list item with the name " + string.Join (",", path.ToArray());
|
|
Error (errorMsg);
|
|
return;
|
|
}
|
|
|
|
if (!context.ResolveVariableWithName (this.name, fromNode: this).found) {
|
|
Error("Unresolved variable: "+this.ToString(), this);
|
|
}
|
|
}
|
|
|
|
public override string ToString ()
|
|
{
|
|
return string.Join(".", path.ToArray());
|
|
}
|
|
|
|
Runtime.VariableReference _runtimeVarRef;
|
|
}
|
|
}
|
|
|