/// <summary> /// Assigns the expression on the right to the local variable on the left /// </summary> public override void PerformOperation() { base.PerformOperation(); // If we have fewer than 2 objects on the stack we cannot perform this operator Debug.Assert(CelesteStack.StackSize >= 2, "Not enough elements on the stack for equality operator"); Scope scope = CelesteStack.CurrentScope; CelesteObject rhs = CelesteStack.Pop(); CelesteObject lhs = CelesteStack.Pop(); Debug.Assert(lhs.IsReference()); // All functions are references so check this condition first if (rhs.IsFunction()) { Debug.Assert(lhs.IsFunction(), "A function can only be assigned to another function"); Function lhsFunc = lhs.AsFunction(); Function rhsFunc = rhs.AsFunction(); Debug.Assert(CelesteStack.CurrentScope != lhsFunc.FunctionScope, "Cannot reassign functions inside their own scope"); lhsFunc.FunctionScope = rhsFunc.FunctionScope; lhsFunc.ParameterNames = rhsFunc.ParameterNames; lhsFunc.Ref = rhsFunc.Ref; // This is equivalent to setting their implementation to be the same } else if (rhs.IsReference()) { lhs.Value = rhs.Value; } else { lhs.AsReference().Value = rhs.Value; } }
/// <summary> /// Removes the the object at the top of the stack and performs the not operation. /// Then, pushes the result of the equality on to the top of the stack. /// </summary> public override void PerformOperation() { base.PerformOperation(); // If we have fewer than 1 object on the stack we cannot perform this operation Debug.Assert(CelesteStack.StackSize >= 1, "Not enough elements on the stack for the '!' operator"); CelesteObject rhs = CelesteStack.Pop(); // The stack will wrap our result in a CelesteObject, so just push the actual result of the equality bool result = false; Reference rhsRef = rhs.AsReference(); // Check to see whether our rhs is a reference if (rhsRef != null) { // Returns the result of the reference being equal to null result = rhsRef.Value == null; } else { // Else returns the logical opposite of the bool value result = (rhs.Value == null) || ((rhs.Value is bool) && !(bool)rhs.Value); } // We then finally push the result of the equality test onto the stack CelesteStack.Push(result); }
/// <summary> /// Iterates through the list of conditions and performs each one in turn. Checks the top element of the stack to see /// if a true value has been pushed on. If it has it performs the corresponding compiled statement in that KVP. /// </summary> public override void PerformOperation() { base.PerformOperation(); CelesteStack.CurrentScope = FlowScope; Debug.Assert(ConditionsAndBodies.Count > 0, "No conditions or functions specified for flow control"); foreach (KeyValuePair <CompiledStatement, CompiledStatement> condBody in ConditionsAndBodies) { condBody.Key.PerformOperation(); Debug.Assert(CelesteStack.StackSize > 0, "No true or false value pushed onto stack"); CelesteObject celObject = CelesteStack.Pop(); if (celObject.IsBool()) { if (celObject.As <bool>()) { condBody.Value.PerformOperation(); break; } } else { Debug.Fail("Condition must be evaluatable to a bool value"); } } CelesteStack.Scopes.Remove(FlowScope); CelesteStack.CurrentScope = FlowScope.ParentScope; }
/// <summary> /// Removes the two objects at the top of the stack and divides them together. /// Then, pushes the result on to the top of the stack. /// </summary> public override void PerformOperation() { base.PerformOperation(); // If we have fewer than 2 objects on the stack we cannot do this Debug.Assert(CelesteStack.StackSize >= 2, "Not enough elements on the stack for divide operator"); CelesteObject rhs = CelesteStack.Pop(); CelesteObject lhs = CelesteStack.Pop(); // The stack will wrap our division result in a CelesteObject, so just push the actual value of the division if (lhs.IsNumber() && rhs.IsNumber()) { float rhsAsFloat = rhs.As <float>(); if (rhsAsFloat != 0) { CelesteStack.Push(lhs.As <float>() / rhsAsFloat); } else { Debug.Fail("Division by zero"); } } else { Debug.Fail("Invalid parameters to add operation."); } }
public override void PerformOperation() { // Set up our parameters if required if (ParameterNames.Count > 0) { Debug.Assert(ChildCount > 0, "Fatal error in function call - no arguments to process"); CompiledStatement thisCallsParams = ChildCompiledStatements[0]; ChildCompiledStatements.RemoveAt(0); // This will run through each stored parameter and add them to the stack thisCallsParams.PerformOperation(); } // Set up our parameters - read them off the stack with the first parameter at the top for (int i = 0; i < ParameterNames.Count; i++) { Debug.Assert(CelesteStack.StackSize > 0, "Insufficient parameters to function"); CelesteObject input = CelesteStack.Pop(); Variable functionParameter = FunctionScope.GetLocalVariable(ParameterNames[i], ScopeSearchOption.kThisScope); if (input.IsReference()) { functionParameter.Value = input.ValueImpl; } else { (functionParameter.Value as Reference).Value = input.Value; } } CelesteStack.CurrentScope = FunctionScope; // Performs all the operators on the children first foreach (CompiledStatement statement in FuncImpl.ChildCompiledStatements) { statement.PerformOperation(); if (statement is ReturnKeyword) { // Stop iterating through if we have hit a ReturnKeyword in our function break; } } CelesteStack.Scopes.Remove(FunctionScope); CelesteStack.CurrentScope = FunctionScope.ParentScope; }
/// <summary> /// Removes the two objects at the top of the stack and performs logical operations them. /// Then, pushes the result of the operation on to the top of the stack. /// </summary> public sealed override void PerformOperation() { base.PerformOperation(); // If we have fewer than 2 objects on the stack we cannot perform this operation Debug.Assert(CelesteStack.StackSize >= 2, "Not enough elements on the stack for the equality operator"); CelesteObject rhs = CelesteStack.Pop(); CelesteObject lhs = CelesteStack.Pop(); // The stack will wrap our result in a CelesteObject, so just push the actual result of the operation bool result = false; Reference lhsRef = lhs.AsReference(); Reference rhsRef = rhs.AsReference(); // Check to see whether our lhs is a reference if (lhsRef != null) { if (rhsRef != null) { // If the rhs is a reference too, we compare references result = ReferenceReferenceOperation(lhsRef, rhsRef); } else { // Otherwise we compare the value of the rhs with the value of the lhs' referenced object result = ReferenceValueOperation(lhsRef, rhs.Value); } } else { if (rhsRef != null) { // If the rhs is a reference, we compare the value of the rhs' referenced object with the value of the lhs result = ReferenceValueOperation(rhsRef, lhs.Value); } else { // Otherwise we compare the value of the rhs with the value of the lhs - they are both values result = ValueValueOperation(lhs.Value, rhs.Value); } } // We then finally push the result of the equality test onto the stack CelesteStack.Push(result); }
/// <summary> /// Removes the two objects at the top of the stack and multiplies them together. /// Then, pushes the result on to the top of the stack. /// </summary> public override void PerformOperation() { base.PerformOperation(); // If we have fewer than 2 objects on the stack we are el-bonerino-ed Debug.Assert(CelesteStack.StackSize >= 2, "Not enough elements on the stack for multiply operator"); CelesteObject rhs = CelesteStack.Pop(); CelesteObject lhs = CelesteStack.Pop(); // The stack will wrap our multiplication result in a CelesteObject, so just push the actual value of the multiplication if (lhs.IsNumber() && rhs.IsNumber()) { CelesteStack.Push(lhs.As <float>() * rhs.As <float>()); } else { Debug.Fail("Invalid parameters to add operation."); } }
// When we compile we need to check which end the increment is on and then check for a variable name accordingly /// <summary> /// Pops the first element on the top of the stack and checks to see if it is a reference to a number. /// If it is, it increments the number and then pushes the object back onto the stack. /// </summary> public override void PerformOperation() { base.PerformOperation(); Debug.Assert(CelesteStack.StackSize > 0, "Must be an object on the stack for the increment operator"); CelesteObject rhs = CelesteStack.Pop(); if (rhs.IsNumber()) { float newFloat = rhs.As <float>(); newFloat++; rhs.Value = newFloat; } else { Debug.Fail("Increment operator can only be applied to a variable with a numerical value"); } CelesteStack.Push(rhs); }
/// <summary> /// Removes the two objects at the top of the stack and subtracts the top most object on the stack from the other. /// Then, pushes the result on to the top of the stack. /// </summary> public override void PerformOperation() { base.PerformOperation(); // If we have fewer than 2 objects on the stack we are el-bonerino-ed Debug.Assert(CelesteStack.StackSize >= 2, "Not enough elements on the stack for subtract operator"); CelesteObject rhs = CelesteStack.Pop(); CelesteObject lhs = CelesteStack.Pop(); // The stack will wrap our subtraction result in a CelesteObject, so just push the actual value of the subtraction if (lhs.IsNumber() && rhs.IsNumber()) { CelesteStack.Push(lhs.As <float>() - rhs.As <float>()); } else if (lhs.IsString() && rhs.IsString()) { string lhsAsString = lhs.As <string>(); string rhsAsString = rhs.As <string>(); if (lhsAsString.Contains(rhsAsString)) { // Push the lhs string having removed the rhs string CelesteStack.Push(lhsAsString.Remove(lhsAsString.IndexOf(rhsAsString), rhsAsString.Length)); } else { // If our lhs does not contain our rhs, we just push the original unaltered lhs string CelesteStack.Push(lhsAsString); } } else if (lhs.IsList() && rhs.IsList()) { // The subtract operator for lists removes all elements in the first list who are equal to an element in the second, either by reference or value List <object> lhsList = lhs.AsList <object>(); List <object> rhsList = rhs.AsList <object>(); // Remove ALL occurrences in the list of any object in the rhs list - otherwise there is non-deterministic behaviour in which instance to remove lhsList.RemoveAll(x => rhsList.Exists(y => y.Equals(x) || y.ValueEquals(x))); CelesteStack.Push(lhs); } else if (lhs.IsTable() && rhs.IsList()) { // Subtraction of tables does not make sense // What does make sense, is subtracting elements in a table using a list of keys // Subtracting a list from a table will remove any elements in the table with a matching key as an element in our list Dictionary <object, object> lhsTable = lhs.AsTable(); List <object> rhsList = rhs.AsList <object>(); foreach (object obj in rhsList) { if (lhsTable.Contains(new KeyValuePair <object, object>(obj, null), new TableKeyComparer())) { lhsTable.Remove(lhsTable.First(x => x.Key.Equals(obj) || x.Key.ValueEquals(obj))); } } CelesteStack.Push(lhs); } else { Debug.Fail("Invalid parameters to subtract operation."); } }
/// <summary> /// Removes the two objects at the top of the stack and adds them together. /// Then, pushes the result on to the top of the stack. /// </summary> public override void PerformOperation() { base.PerformOperation(); // If we have fewer than 2 objects on the stack we are el-bonerino-ed Debug.Assert(CelesteStack.StackSize >= 2, "Not enough elements on the stack for add operator"); CelesteObject rhs = CelesteStack.Pop(); CelesteObject lhs = CelesteStack.Pop(); // The stack will wrap our addition result in a CelesteObject, so just push the actual value of the addition if (lhs.IsNumber() && rhs.IsNumber()) { CelesteStack.Push(lhs.As <float>() + rhs.As <float>()); } else if (lhs.IsString() && rhs.IsString()) { CelesteStack.Push(lhs.As <string>() + rhs.As <string>()); } else if (lhs.IsList() && rhs.IsList()) { lhs.AsList <object>().AddRange(rhs.AsList <object>()); CelesteStack.Push(lhs); } else if (lhs.IsTable() && rhs.IsTable()) { Dictionary <object, object> lhsTable = lhs.AsTable(); Dictionary <object, object> rhsTable = rhs.AsTable(); // Tables can be added, but only if they are all indexed by number or string completely // No other way can be used because the reference type of 'object' makes value equality difficult // These two types of adding are the only two we can realistically support // Check whether our lhs table has all number keys if (lhsTable.Keys.Count(x => x is float) == lhsTable.Keys.Count) { // If it does, check the rhs table too if (rhsTable.Keys.Count(x => x is float) == rhsTable.Keys.Count) { // We now go through and see if any keys overlap in the two tables if (lhsTable.Intersect(rhsTable, new TableKeyComparer()).Count() == 0) { foreach (KeyValuePair <object, object> pair in rhsTable) { lhsTable.Add((float)pair.Key, pair.Value); } } else { Debug.Fail("Invalid parameters to add operation. Right hand side table has at least one key the same as the left hand side table"); } } else { Debug.Fail("Invalid parameters to add operation. Right hand side table has inconsistent key types (should be numbers)"); } } // Check whether our lhs table has all string keys else if (lhsTable.Keys.Count(x => x is string) == lhsTable.Keys.Count) { // If it does, check the rhs table too if (rhsTable.Keys.Count(x => x is string) == rhsTable.Keys.Count) { // We now go through and see if any keys overlap in the two tables if (lhsTable.Intersect(rhsTable, new TableKeyComparer()).Count() == 0) { foreach (KeyValuePair <object, object> pair in rhsTable) { lhsTable.Add((string)pair.Key, pair.Value); } } else { Debug.Fail("Invalid parameters to add operation. Right hand side table has at least one key the same as the left hand side table"); } } else { Debug.Fail("Invalid parameters to add operation. Right hand side table has inconsistent key types (should be strings)"); } } else { Debug.Fail("Invalid parameters to add operation. Left hand side table has inconsistent key types (should be numbers or strings)"); } CelesteStack.Push(lhs); } else { Debug.Fail("Invalid parameters to add operation."); } }
/// <summary> /// Pushes an already created Celeste object onto the stack /// </summary> /// <param name="celObject"></param> public static void Push(CelesteObject celObject) { CelStack.Push(celObject); }