Newer
Older
TheVengeance-Project-IADE-Unity2D / Assets / Ink / InkLibs / InkCompiler / ParsedHierarchy / Expression.cs
  1. using System.Collections.Generic;
  2. using System.Linq;
  3. namespace Ink.Parsed
  4. {
  5. public abstract class Expression : Parsed.Object
  6. {
  7. public bool outputWhenComplete { get; set; }
  8. public override Runtime.Object GenerateRuntimeObject ()
  9. {
  10. var container = new Runtime.Container ();
  11. // Tell Runtime to start evaluating the following content as an expression
  12. container.AddContent (Runtime.ControlCommand.EvalStart());
  13. GenerateIntoContainer (container);
  14. // Tell Runtime to output the result of the expression evaluation to the output stream
  15. if (outputWhenComplete) {
  16. container.AddContent (Runtime.ControlCommand.EvalOutput());
  17. }
  18. // Tell Runtime to stop evaluating the content as an expression
  19. container.AddContent (Runtime.ControlCommand.EvalEnd());
  20. return container;
  21. }
  22. // When generating the value of a constant expression,
  23. // we can't just keep generating the same constant expression into
  24. // different places where the constant value is referenced, since then
  25. // the same runtime objects would be used in multiple places, which
  26. // is impossible since each runtime object should have one parent.
  27. // Instead, we generate a prototype of the runtime object(s), then
  28. // copy them each time they're used.
  29. public void GenerateConstantIntoContainer(Runtime.Container container)
  30. {
  31. if( _prototypeRuntimeConstantExpression == null ) {
  32. _prototypeRuntimeConstantExpression = new Runtime.Container ();
  33. GenerateIntoContainer (_prototypeRuntimeConstantExpression);
  34. }
  35. foreach (var runtimeObj in _prototypeRuntimeConstantExpression.content) {
  36. container.AddContent (runtimeObj.Copy());
  37. }
  38. }
  39. public abstract void GenerateIntoContainer (Runtime.Container container);
  40. Runtime.Container _prototypeRuntimeConstantExpression;
  41. }
  42. public class BinaryExpression : Expression
  43. {
  44. public Expression leftExpression;
  45. public Expression rightExpression;
  46. public string opName;
  47. public BinaryExpression(Expression left, Expression right, string opName)
  48. {
  49. leftExpression = AddContent(left);
  50. rightExpression = AddContent(right);
  51. this.opName = opName;
  52. }
  53. public override void GenerateIntoContainer(Runtime.Container container)
  54. {
  55. leftExpression.GenerateIntoContainer (container);
  56. rightExpression.GenerateIntoContainer (container);
  57. opName = NativeNameForOp (opName);
  58. container.AddContent(Runtime.NativeFunctionCall.CallWithName(opName));
  59. }
  60. public override void ResolveReferences (Story context)
  61. {
  62. base.ResolveReferences (context);
  63. // Check for the following case:
  64. //
  65. // (not A) ? B
  66. //
  67. // Since this easy to accidentally do:
  68. //
  69. // not A ? B
  70. //
  71. // when you intend:
  72. //
  73. // not (A ? B)
  74. if (NativeNameForOp (opName) == "?") {
  75. var leftUnary = leftExpression as UnaryExpression;
  76. if( leftUnary != null && (leftUnary.op == "not" || leftUnary.op == "!") ) {
  77. Error ("Using 'not' or '!' here negates '"+leftUnary.innerExpression+"' rather than the result of the '?' or 'has' operator. You need to add parentheses around the (A ? B) expression.");
  78. }
  79. }
  80. }
  81. string NativeNameForOp(string opName)
  82. {
  83. if (opName == "and")
  84. return "&&";
  85. if (opName == "or")
  86. return "||";
  87. if (opName == "mod")
  88. return "%";
  89. if (opName == "has")
  90. return "?";
  91. if (opName == "hasnt")
  92. return "!?";
  93. return opName;
  94. }
  95. public override string ToString ()
  96. {
  97. return string.Format ("({0} {1} {2})", leftExpression, opName, rightExpression);
  98. }
  99. }
  100. public class UnaryExpression : Expression
  101. {
  102. public Expression innerExpression;
  103. public string op;
  104. // Attempt to flatten inner expression immediately
  105. // e.g. convert (-(5)) into (-5)
  106. public static Expression WithInner(Expression inner, string op) {
  107. var innerNumber = inner as Number;
  108. if( innerNumber ) {
  109. if( op == "-" ) {
  110. if( innerNumber.value is int ) {
  111. return new Number( -((int)innerNumber.value) );
  112. } else if( innerNumber.value is float ) {
  113. return new Number( -((float)innerNumber.value) );
  114. }
  115. }
  116. else if( op == "!" || op == "not" ) {
  117. if( innerNumber.value is int ) {
  118. return new Number( (int)innerNumber.value == 0 );
  119. } else if( innerNumber.value is float ) {
  120. return new Number( (float)innerNumber.value == 0.0f );
  121. } else if( innerNumber.value is bool ) {
  122. return new Number( !(bool)innerNumber.value );
  123. }
  124. }
  125. throw new System.Exception ("Unexpected operation or number type");
  126. }
  127. // Normal fallback
  128. var unary = new UnaryExpression (inner, op);
  129. return unary;
  130. }
  131. public UnaryExpression(Expression inner, string op)
  132. {
  133. this.innerExpression = AddContent(inner);
  134. this.op = op;
  135. }
  136. public override void GenerateIntoContainer(Runtime.Container container)
  137. {
  138. innerExpression.GenerateIntoContainer (container);
  139. container.AddContent(Runtime.NativeFunctionCall.CallWithName(nativeNameForOp));
  140. }
  141. public override string ToString ()
  142. {
  143. return nativeNameForOp + innerExpression;
  144. }
  145. string nativeNameForOp
  146. {
  147. get {
  148. // Replace "-" with "_" to make it unique (compared to subtraction)
  149. if (op == "-")
  150. return "_";
  151. if (op == "not")
  152. return "!";
  153. return op;
  154. }
  155. }
  156. }
  157. public class IncDecExpression : Expression
  158. {
  159. public Identifier varIdentifier;
  160. public bool isInc;
  161. public Expression expression;
  162. public IncDecExpression(Identifier varIdentifier, bool isInc)
  163. {
  164. this.varIdentifier = varIdentifier;
  165. this.isInc = isInc;
  166. }
  167. public IncDecExpression (Identifier varIdentifier, Expression expression, bool isInc) : this(varIdentifier, isInc)
  168. {
  169. this.expression = expression;
  170. AddContent (expression);
  171. }
  172. public override void GenerateIntoContainer(Runtime.Container container)
  173. {
  174. // x = x + y
  175. // ^^^ ^ ^ ^
  176. // 4 1 3 2
  177. // Reverse polish notation: (x 1 +) (assign to x)
  178. // 1.
  179. container.AddContent (new Runtime.VariableReference (varIdentifier?.name));
  180. // 2.
  181. // - Expression used in the form ~ x += y
  182. // - Simple version: ~ x++
  183. if (expression)
  184. expression.GenerateIntoContainer (container);
  185. else
  186. container.AddContent (new Runtime.IntValue (1));
  187. // 3.
  188. container.AddContent (Runtime.NativeFunctionCall.CallWithName (isInc ? "+" : "-"));
  189. // 4.
  190. _runtimeAssignment = new Runtime.VariableAssignment(varIdentifier?.name, false);
  191. container.AddContent (_runtimeAssignment);
  192. }
  193. public override void ResolveReferences (Story context)
  194. {
  195. base.ResolveReferences (context);
  196. var varResolveResult = context.ResolveVariableWithName(varIdentifier?.name, fromNode: this);
  197. if (!varResolveResult.found) {
  198. Error ("variable for "+incrementDecrementWord+" could not be found: '"+varIdentifier+"' after searching: "+this.descriptionOfScope);
  199. }
  200. _runtimeAssignment.isGlobal = varResolveResult.isGlobal;
  201. if (!(parent is Weave) && !(parent is FlowBase) && !(parent is ContentList)) {
  202. Error ("Can't use " + incrementDecrementWord + " as sub-expression");
  203. }
  204. }
  205. string incrementDecrementWord {
  206. get {
  207. if (isInc)
  208. return "increment";
  209. else
  210. return "decrement";
  211. }
  212. }
  213. public override string ToString ()
  214. {
  215. if (expression)
  216. return varIdentifier + (isInc ? " += " : " -= ") + expression.ToString ();
  217. else
  218. return varIdentifier + (isInc ? "++" : "--");
  219. }
  220. Runtime.VariableAssignment _runtimeAssignment;
  221. }
  222. public class MultipleConditionExpression : Expression
  223. {
  224. public List<Expression> subExpressions {
  225. get {
  226. return this.content.Cast<Expression> ().ToList ();
  227. }
  228. }
  229. public MultipleConditionExpression(List<Expression> conditionExpressions)
  230. {
  231. AddContent (conditionExpressions);
  232. }
  233. public override void GenerateIntoContainer(Runtime.Container container)
  234. {
  235. // A && B && C && D
  236. // => (((A B &&) C &&) D &&) etc
  237. bool isFirst = true;
  238. foreach (var conditionExpr in subExpressions) {
  239. conditionExpr.GenerateIntoContainer (container);
  240. if (!isFirst) {
  241. container.AddContent (Runtime.NativeFunctionCall.CallWithName ("&&"));
  242. }
  243. isFirst = false;
  244. }
  245. }
  246. }
  247. }