Newer
Older
TheVengeance-Project-IADE-Unity2D / Assets / Ink / InkLibs / InkCompiler / ParsedHierarchy / VariableReference.cs
  1. using System.Collections.Generic;
  2. using System.Linq;
  3. namespace Ink.Parsed
  4. {
  5. public class VariableReference : Expression
  6. {
  7. // - Normal variables have a single item in their "path"
  8. // - Knot/stitch names for read counts are actual dot-separated paths
  9. // (though this isn't actually used at time of writing)
  10. // - List names are dot separated: listName.itemName (or just itemName)
  11. public string name { get; private set; }
  12. public Identifier identifier {
  13. get {
  14. // Merging the list of identifiers into a single identifier.
  15. // Debug metadata is also merged.
  16. if (pathIdentifiers == null || pathIdentifiers.Count == 0) {
  17. return null;
  18. }
  19. if( _singleIdentifier == null ) {
  20. var name = string.Join (".", path.ToArray());
  21. var firstDebugMetadata = pathIdentifiers.First().debugMetadata;
  22. var debugMetadata = pathIdentifiers.Aggregate(firstDebugMetadata, (acc, id) => acc.Merge(id.debugMetadata));
  23. _singleIdentifier = new Identifier { name = name, debugMetadata = debugMetadata };
  24. }
  25. return _singleIdentifier;
  26. }
  27. }
  28. Identifier _singleIdentifier;
  29. public List<Identifier> pathIdentifiers;
  30. public List<string> path { get; private set; }
  31. // Only known after GenerateIntoContainer has run
  32. public bool isConstantReference;
  33. public bool isListItemReference;
  34. public Runtime.VariableReference runtimeVarRef { get { return _runtimeVarRef; } }
  35. public VariableReference (List<Identifier> pathIdentifiers)
  36. {
  37. this.pathIdentifiers = pathIdentifiers;
  38. this.path = pathIdentifiers.Select(id => id?.name).ToList();
  39. this.name = string.Join (".", pathIdentifiers);
  40. }
  41. public override void GenerateIntoContainer (Runtime.Container container)
  42. {
  43. Expression constantValue = null;
  44. // If it's a constant reference, just generate the literal expression value
  45. // It's okay to access the constants at code generation time, since the
  46. // first thing the ExportRuntime function does it search for all the constants
  47. // in the story hierarchy, so they're all available.
  48. if ( story.constants.TryGetValue (name, out constantValue) ) {
  49. constantValue.GenerateConstantIntoContainer (container);
  50. isConstantReference = true;
  51. return;
  52. }
  53. _runtimeVarRef = new Runtime.VariableReference (name);
  54. // List item reference?
  55. // Path might be to a list (listName.listItemName or just listItemName)
  56. if (path.Count == 1 || path.Count == 2) {
  57. string listItemName = null;
  58. string listName = null;
  59. if (path.Count == 1) listItemName = path [0];
  60. else {
  61. listName = path [0];
  62. listItemName = path [1];
  63. }
  64. var listItem = story.ResolveListItem (listName, listItemName, this);
  65. if (listItem) {
  66. isListItemReference = true;
  67. }
  68. }
  69. container.AddContent (_runtimeVarRef);
  70. }
  71. public override void ResolveReferences (Story context)
  72. {
  73. base.ResolveReferences (context);
  74. // Work is already done if it's a constant or list item reference
  75. if (isConstantReference || isListItemReference) {
  76. return;
  77. }
  78. // Is it a read count?
  79. var parsedPath = new Path (pathIdentifiers);
  80. Parsed.Object targetForCount = parsedPath.ResolveFromContext (this);
  81. if (targetForCount) {
  82. targetForCount.containerForCounting.visitsShouldBeCounted = true;
  83. // If this is an argument to a function that wants a variable to be
  84. // passed by reference, then the Parsed.Divert will have generated a
  85. // Runtime.VariablePointerValue instead of allowing this object
  86. // to generate its RuntimeVariableReference. This only happens under
  87. // error condition since we shouldn't be passing a read count by
  88. // reference, but we don't want it to crash!
  89. if (_runtimeVarRef == null) return;
  90. _runtimeVarRef.pathForCount = targetForCount.runtimePath;
  91. _runtimeVarRef.name = null;
  92. // Check for very specific writer error: getting read count and
  93. // printing it as content rather than as a piece of logic
  94. // e.g. Writing {myFunc} instead of {myFunc()}
  95. var targetFlow = targetForCount as FlowBase;
  96. if (targetFlow && targetFlow.isFunction) {
  97. // Is parent context content rather than logic?
  98. if ( parent is Weave || parent is ContentList || parent is FlowBase) {
  99. Warning ("'" + targetFlow.identifier + "' being used as read count rather than being called as function. Perhaps you intended to write " + targetFlow.name + "()");
  100. }
  101. }
  102. return;
  103. }
  104. // Couldn't find this multi-part path at all, whether as a divert
  105. // target or as a list item reference.
  106. if (path.Count > 1) {
  107. var errorMsg = "Could not find target for read count: " + parsedPath;
  108. if (path.Count <= 2)
  109. errorMsg += ", or couldn't find list item with the name " + string.Join (",", path.ToArray());
  110. Error (errorMsg);
  111. return;
  112. }
  113. if (!context.ResolveVariableWithName (this.name, fromNode: this).found) {
  114. Error("Unresolved variable: "+this.ToString(), this);
  115. }
  116. }
  117. public override string ToString ()
  118. {
  119. return string.Join(".", path.ToArray());
  120. }
  121. Runtime.VariableReference _runtimeVarRef;
  122. }
  123. }