private NovelScript ParseScript(NovelScript script, NovelParserBlock block, ParentBlockType parentType, int nestLevel) { if (parentType == ParentBlockType.Function) { ContainsUserReturn = false; } //Prepeare variable scope Variables.AddScope(); //For each content in block for (int i = 0; i < block.Content.Count(); i++) { //Get the phrase var phrase = block.Content[i]; //Setting up the line for debbugging purposes ParsedLine = phrase.CodeLine; //If its another block if (phrase is NovelParserBlock) { var localBlock = phrase as NovelParserBlock; var header = localBlock.Header; //If header is the function signature if (IsFunctionSignature(header)) { //If the function signatures are nested if (parentType != ParentBlockType.StartBlock) { throw new NovelException("Function signatures cannot be nested", ParsedFile, ParsedLine); } //Get the function object var function = ParseFunctionSignature(header, script.Instructions.Count); //Add function to list script.FunctionList.Add(function); Variables.AddScope(); //Add arguments to stack for (int j = 0; j < function.ParameterNameList.Count; j++) { Variables.AddVaraible(function.ParameterNameList[j]); } //Parse the local block script = ParseScript(script, localBlock, ParentBlockType.Function, nestLevel + 1); Variables.RemoveScope(); } //If header indicates that this is condition start else if (IsCondition(header)) { //Check if condition is inside function block or another condition block if (parentType != ParentBlockType.Function && parentType != ParentBlockType.Condition) { throw new NovelException("Condition has to be in function", ParsedFile, ParsedLine); } //Getting the condition level int conditionLevel = -1; List <NovelInstruction> instructions = new List <NovelInstruction>(); //Get the novel condition var condition = ParseCondition(header, out conditionLevel, ref instructions); //TODO check boolean type compatibility //Check if the last term element is logical type //var terms = condition.Expression.Term; //var lastTerm = terms[terms.Count - 1]; //if( !(lastTerm is NovelLogicOperator) ) // throw new NovelException("Condition does not contain logic expression", ParsedFile, ParsedLine); //Getting last type of condition (if, else if, else) int lastLevel = Conditions.GetLastConditionLevel(nestLevel); //Check if the upcoming condition is higher than //those in condition scope, or if those are else if if (conditionLevel > lastLevel || (conditionLevel == 1 && lastLevel == 1)) { //If lastLevel is "If" or "Else if" then if (lastLevel == 0 || lastLevel == 1) { script.Instructions.Add(new NovelJump()); } int firstInstruction = script.Instructions.Count; foreach (var inst in instructions) { script.Instructions.Add(inst); } Conditions.AddCondition(nestLevel, firstInstruction, script.Instructions.Count, conditionLevel, ConditionScope.ConditionType.Normal); script.Instructions.Add(condition); } else { throw new NovelException("Invalid type of conditional instruction in this context", ParsedFile, ParsedLine); } ParsedScript = ParseScript(script, localBlock, ParentBlockType.Condition, nestLevel + 1); } //If header indicates that this is a loop else if (IsWhileLoop(header)) { //Check if condition is inside function block or another condition block if (parentType != ParentBlockType.Function && parentType != ParentBlockType.Condition) { throw new NovelException("Condition has to be in function", ParsedFile, ParsedLine); } List <NovelInstruction> instructions = new List <NovelInstruction>(); var condition = ParseWhileLoop(header, ref instructions); //Get the index of where the local variables for condition start int firstInstructionOfCndIndex = script.Instructions.Count; //Add all instructions foreach (var instruction in instructions) { script.Instructions.Add(instruction); } //Add stuff to condition scope Conditions.AddCondition(nestLevel, firstInstructionOfCndIndex, script.Instructions.Count, 1, ConditionScope.ConditionType.Loop); //Add condition at the end script.Instructions.Add(condition); ParsedScript = ParseScript(script, localBlock, ParentBlockType.Condition, nestLevel + 1); } } //Or if its a plain text else if (phrase is NovelParserText) { //If condition scope contains if (Conditions.GetLastConditionLevel(nestLevel) > -1) { UpdateConditions(nestLevel); } var localText = phrase as NovelParserText; var instruction = localText.Instruction; //If is assignment expression //if (IsAssigment(instruction)) //{ // //Parse assignment (supports initialization) // var instructions = ParseAssignment(instruction); // foreach (var inst in instructions) // script.Instructions.Add(inst); //} if (IsReturn(instruction)) { var instructions = ParseReturn(instruction); foreach (var inst in instructions) { script.Instructions.Add(inst); } ContainsUserReturn = true; } //If its other kind of expression else { //Parse expression (supports initialization) var instructions = ParseExpression(instruction); foreach (var inst in instructions) { script.Instructions.Add(inst); } } } //TODO find a better way to this //Now we have to handle returning values //If the last instruction of this block isn't the return value then return default } //If the end of the block then end condition UpdateConditions(nestLevel); //Return value should cause local variables to unstack if (parentType == ParentBlockType.Function) { //If contains returns in function but last instruction is not a return if (ContainsUserReturn == true && !(script.Instructions[script.Instructions.Count() - 1] is NovelReturn)) { throw new NovelException("Function does not return a value.", ParsedFile, ParsedLine); } //If last instruction isn't return else if (!(script.Instructions[script.Instructions.Count() - 1] is NovelReturn)) { script.Instructions.Add(new NovelReturn(new NovelExpressionLiteral((int)0))); } } //No return so manual unstacking else { //Unstack local variables var stackVars = Variables.GetScopeStackVariablesCount(); if (stackVars > 0) { foreach (var v in Variables.GetScopeStackVariables()) { Console.WriteLine(v); } script.Instructions.Add(new NovelExpandStack(-stackVars)); } } //Remove scope before return Variables.RemoveScope(); return(script); }