private bool EvaluateLogical(LogicalBlock logicalBlock) { if (logicalBlock.Field.Content.Contains("AND")) { return(GetBoolValue(logicalBlock.LeftValue) && GetBoolValue(logicalBlock.RightValue)); } if (logicalBlock.Field.Content.Contains("OR")) { return(GetBoolValue(logicalBlock.LeftValue) || GetBoolValue(logicalBlock.RightValue)); } if (logicalBlock.Field.Content.Contains("GT")) { return(GetNumericValue(logicalBlock.LeftValue) > GetNumericValue(logicalBlock.RightValue)); } if (logicalBlock.Field.Content.Contains("LT")) { return(GetNumericValue(logicalBlock.LeftValue) < GetNumericValue(logicalBlock.RightValue)); } if (logicalBlock.Field.Content.Contains("GTE")) { return(GetNumericValue(logicalBlock.LeftValue) >= GetNumericValue(logicalBlock.RightValue)); } if (logicalBlock.Field.Content.Contains("LTE")) { return(GetNumericValue(logicalBlock.LeftValue) <= GetNumericValue(logicalBlock.RightValue)); } if (logicalBlock.Field.Content.Contains("NEQ")) { return(GetNumericValue(logicalBlock.LeftValue) != GetNumericValue(logicalBlock.RightValue)); } return(GetNumericValue(logicalBlock.LeftValue) == GetNumericValue(logicalBlock.RightValue)); }
// Performs actions defined by the Node // If the Node is part of core functionality of the language, e.g. assigning to variables, performing arithmetic, etc. then handle it here. // Otherwise, continue to child implementations of this method. public virtual ExecutionStatus ExecuteNode(NodeBase node) { if (node == null) { programRunning = false; currentNode = program.programStart; return(new ExecutionStatus { success = false, handover = false }); } switch (CheckNodeType(node)) { // Handlers for different commands case NodeType.ProgramStart: Logger.Log("Program starting!"); processingDone = true; InitSymTable(); // TODO: this doesn't actually reset the buffer? outputBuffer = ""; return(new ExecutionStatus { success = true, handover = false }); case NodeType.AssignValue: AssignValue assignValue = node.GetComponent <AssignValue>(); SetSymbol(assignValue.leftHand, assignValue.rightHand); return(new ExecutionStatus { success = true, handover = false }); case NodeType.ArithmeticOperationBase: // Arithmetic only takes a tick when it gets executed currentNode = (NodeBase)currentNode.nextNode; return(ExecuteNode(currentNode)); case NodeType.ProgramEnd: processingDone = true; programRunning = false; return(new ExecutionStatus { success = true, handover = false }); case NodeType.FunctionCallBase: string funcName = node.GetComponent <FunctionCallBase>().functionName; if (BaseControllerFunctions().ContainsKey(funcName)) { // TODO: passing a copy of symbolTable here might consume too much memory. Make static? functions[funcName].DynamicInvoke(node.GetComponent <FunctionCallBase>().GetRawParameters(symbolTable)); Logger.Log($"Found base function {funcName}"); return(new ExecutionStatus { success = true, handover = false }); } Logger.Log($"Couldn't find base function {funcName}"); break; case NodeType.LogicalBlock: case NodeType.WhileLoop: case NodeType.ElseBlock: // Make sure all nodes in the block body have their ownerLoop assigned node.GetComponent <LogicalBlock>().PropagateOwnership(); if (DistanceCheck()) { GameObject.Find("OutputRenderer").transform.Find("Canvas").GetComponentInChildren <Text>().text = ((CodeBlock)currentNode).SerializeBlockHeader(); } //new WaitForSeconds((float)tickTime bool evaluatedResult = false; // ElseBlocks should activate when its associated LogicalBlocks evaluate as false. if (node.GetComponent <ElseBlock>()) { if (node.PrevNodeObject.GetComponent <LogicalBlock>() && !node.PrevNodeObject.GetComponent <ElseBlock>() && !node.PrevNodeObject.GetComponent <WhileLoop>()) { evaluatedResult = !node.PrevNodeObject.GetComponent <LogicalBlock>().evaluatedResult; } } else { evaluatedResult = node.GetComponent <LogicalBlock>().condition.Evaluate(ref symbolTable); } node.GetComponent <LogicalBlock>().evaluatedResult = evaluatedResult; if (evaluatedResult && ((!node.GetComponent <WhileLoop>()) || (node.GetComponent <WhileLoop>() && !node.GetComponent <WhileLoop>().breakNow))) { NodeBase nodeToFollow = (NodeBase)(node.GetComponent <LogicalBlock>().firstBodyNode); if (nodeToFollow != null) { specialNextNode = nodeToFollow; } else { specialNextNode = (NodeBase)currentNode.nextNode; } //return ExecuteNode(currentNode); timeSinceTick = -tickTime; return(new ExecutionStatus { success = true, handover = false }); } break; case NodeType.AllocateArray: if (node.GetComponent <AllocateArray>()) { int count = -1; // Check if entered size was a valid >= 0 integer. // TODO: unexpected behaviour when allocating with size == 0 Logger.Log($"Allocating array with count {(string)node.GetComponent<AllocateArray>().GetRawParameters(symbolTable)[0]}"); if (int.TryParse((string)node.GetComponent <AllocateArray>().GetRawParameters(symbolTable)[0], out count)) { string arrName = node.GetComponent <AllocateArray>().parameters[1].Value; if (string.IsNullOrWhiteSpace(arrName)) { // TODO: error too? return(new ExecutionStatus { success = false, handover = false }); } for (int i = 0; i < count; i++) { Logger.Log($"Adding array element \"{arrName}[{i}]\""); symbolTable.Add($"{arrName}[{i}]", new FunctionParameter { Value = "None" }); } // Only initialise elements in the symbol table if size was provided as a literal if (int.TryParse(node.GetComponent <AllocateArray>().parameters[0].Value, out count)) { for (int i = 2; i < 2 + count; i++) { FunctionParameter listElement = node.GetComponent <AllocateArray>().parameters[i]; SetSymbol(new FunctionParameter { Value = listElement.Name }, new FunctionParameter { Value = string.IsNullOrWhiteSpace(listElement.Value) ? "None" : listElement.Value }); } } return(new ExecutionStatus { success = true, handover = false }); } } break; case NodeType.Continue: if (node.GetComponent <Continue>()) { // Find the while loop LogicalBlock owner = node.ownerLoop; while (owner != null) { WhileLoop loop = owner.GetComponent <WhileLoop>(); if (loop != null) { currentNode = loop; return(ExecuteNode(currentNode)); } else { owner = owner.ownerLoop; } } if (owner == null) { return(new ExecutionStatus { success = false, handover = false }); } } break; case NodeType.Break: if (node.GetComponent <Break>()) { // Find the while loop LogicalBlock owner = node.ownerLoop; while (owner != null) { WhileLoop loop = owner.GetComponent <WhileLoop>(); if (loop != null) { loop.breakNow = true; currentNode = loop; return(ExecuteNode(currentNode)); } else { owner = owner.ownerLoop; } } if (owner == null) { return(new ExecutionStatus { success = false, handover = false }); } } break; } return(new ExecutionStatus { success = true, handover = true }); }
// Returns false if ExecuteFrame needs to proceed to next node on the next call. public virtual bool ExecuteFrame() { // TODO: move this to a separate method and leave ExecuteFrame as an abstract function? bool canTick = timeSinceTick >= tickTime; if (canTick && (processingDone || firstTick)) { timeSinceTick = 0.0; if (currentNode != null && currentNode.gameObject.name != "ProgramEnd") { if (!waitForNextTick) { string currentLine = ""; bool errorOut = false; try { ExecuteNode(currentNode); currentLine = currentNode.GetComponent <CodeBlock>() ? ((CodeBlock)currentNode).SerializeBlockHeader() : ((IProgramNode)currentNode).Serialize(); } catch (Exception ex) { if (ex is FormatException || ex is KeyNotFoundException) { // TODO: Notify user of error. Probably something to do with input value or variable. if (currentNode != null && currentNode.Serialize() != null) { Logger.LogError($"CODE '{currentNode.Serialize()}' contains invalid symbol or value."); } else { Logger.LogError($"Invalid use of symbol or value caused an error in the code."); } } if (currentNode != null) { Logger.LogError($"{currentNode.name} caused an error!"); if (currentNode.Serialize() != null) { Logger.LogError($"{currentNode.name} code: {currentNode.Serialize()}"); } } else { Logger.LogError("currentNode caused an error!"); } Logger.LogError($"Exception was: {ex.Message}"); Logger.LogError($"Exception stack trace: {ex.StackTrace}"); currentLine = "ERROR!"; editorUi.GetComponent <EditorProgram>().errorNode = currentNode.gameObject; programRunning = false; currentNode = program.programStart; errorOut = true; } if (transform.Find("CurrentLine")) { if (!editorUi.GetComponent <EditorProgram>().EditorActive) { transform.Find("CurrentLine").gameObject.SetActive(true); } else { transform.Find("CurrentLine").gameObject.SetActive(false); } } if (DistanceCheck()) { GameObject.Find("OutputRenderer").transform.Find("Canvas").GetComponentInChildren <Text>().text = currentLine; } if (errorOut) { return(false); } } else { waitForNextTick = false; } firstTick = false; if (processingDone) { if (specialNextNode != null) { Logger.Log($"Passing specialNextNode '{specialNextNode.name}'"); currentNode = specialNextNode; specialNextNode = null; } // Regular flow else if (currentNode.nextNode != null) { Logger.Log($"Continuing from nextNode {((NodeBase)currentNode.nextNode).name}"); currentNode = currentNode.NextNodeObject.GetComponent <NodeBase>(); } // Reached end of loop else if (currentNode.nextNode == null && currentNode.ownerLoop != null) { if (currentNode.ownerLoop.GetComponent <WhileLoop>()) { Logger.Log($"Continuing from WhileLoop {((WhileLoop)currentNode.ownerLoop).name}"); currentNode = currentNode.ownerLoop; } else if (currentNode.ownerLoop.GetComponent <LogicalBlock>()) { LogicalBlock ownerIf = currentNode.ownerLoop; NodeBase nodeAfterIf = (NodeBase)ownerIf.nextNode; while (nodeAfterIf == null && ownerIf != null) { ownerIf = ownerIf.ownerLoop; if (ownerIf) { nodeAfterIf = (NodeBase)ownerIf.nextNode; } } Logger.Log($"Continuing from IfStatement {(nodeAfterIf ? nodeAfterIf.name : "<null>")}"); currentNode = nodeAfterIf; } } Logger.Log($"Moving to next node: {(currentNode ? currentNode.name : "<null node>")}"); return(false); } } else if (currentNode != null && currentNode.gameObject.name == "ProgramEnd") { ProgramEndCallback(); programRunning = false; currentNode = program.programStart; GameObject.Find("OutputRenderer").transform.Find("Canvas").GetComponentInChildren <Text>().text = ""; if (transform.Find("CurrentLine")) { transform.Find("CurrentLine").gameObject.SetActive(false); } } else // There was an error. Go back to ProgramStart and stop the program execution. { programRunning = false; currentNode = program.programStart; GameObject.Find("OutputRenderer").transform.Find("Canvas").GetComponentInChildren <Text>().text = "ERROR!"; if (transform.Find("CurrentLine")) { transform.Find("CurrentLine").gameObject.SetActive(true); } return(false); } } return(true); // good to continue with any child implementations of this method }