Beispiel #1
0
        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
    }