/// <summary> /// Parses a looping expression that is dependent on an expression /// evaluating to true. /// Syntax: /// "While" "{" Expression "}" Statement /// </summary> private void ParseWhile() { // Check we are in a valid scope. if (_currentScope.Type != SymbolType.Function || _currentScope == _globalScope) Error(ErrorCode.InvalidScope, "While statments are only valid inside a function's or event's scope.", false, 0); // Create some jump target symbols. JumpTargetSymbol startJumpTarget = null, endJumpTarget = null; Instruction instruction; // Bind the starting jump target to here. if (_currentPass == 1) { startJumpTarget = new JumpTargetSymbol(_currentScope); endJumpTarget = new JumpTargetSymbol(_currentScope); } // Read in the expression to evaluate ExpectToken(TokenID.CharOpenParenthesis); DataTypeValue expressionDataType = ParseExpression(); ExpectToken(TokenID.CharCloseParenthesis); // Make sure we can cast between the expression type and boolean. if (!CanImplicitlyCast(new DataTypeValue(DataType.Bool, false, false), expressionDataType)) Error(ErrorCode.InvalidCast, "Can't implicitly cast between \"Bool\" and \"" + expressionDataType.ToString() + "\"", false, 0); // Emit evaluation byte code. if (_currentPass == 1) { // Pop the result of the expression into arith register 1. instruction = CreateInstruction(OpCodeByType(expressionDataType, OpCodeType.POP), _currentScope, _currentToken); new Operand(instruction, Register.Arithmetic1); // Cast it to boolean if its not already boolean if (expressionDataType != new DataTypeValue(DataType.Bool, false, false)) { instruction = CreateInstruction(OpCode.CAST_BOOL, _currentScope, _currentToken); new Operand(instruction, Register.Arithmetic1); } // Compare the expression result to 0. instruction = CreateInstruction(OpCode.CMP_BOOL, _currentScope, _currentToken); new Operand(instruction, Register.Arithmetic1); new Operand(instruction, false); // Jump to finish jump target if comparison is equal to false. instruction = CreateInstruction(OpCode.JMP_EQ, _currentScope, _currentToken); new Operand(instruction, endJumpTarget); // Push a new loop tracker onto the loop stack. _loopTrackerStack.Push(new LoopTracker(startJumpTarget, endJumpTarget)); } // Parse the statements contained in this while block. ParseStatement(); // Emit finishing byte code. if (_currentPass == 1) { // Pop the loop tracker of the loop stack. _loopTrackerStack.Pop(); // Jump to the start of the loop. instruction = CreateInstruction(OpCode.JMP, _currentScope, _currentToken); new Operand(instruction, startJumpTarget); // Bind exit jump target. endJumpTarget.Bind(); } }
/// <summary> /// Parses a label, which is used in conjunction with the goto statement to jump /// to specific areas in the code. /// Syntax: /// Identifier ":" /// </summary> private void ParseLabel() { // Check we are in a valid scope. if (_currentScope.Type != SymbolType.Function || _currentScope == _globalScope) Error(ErrorCode.InvalidScope, "Label statments are only valid inside a function's or event's scope.", false, 0); // Create a jump target for this label. JumpTargetSymbol jumpTarget = null; if (_currentPass == 0) { jumpTarget = new JumpTargetSymbol(_currentScope); jumpTarget.Identifier = _currentToken.Ident; } else if (_currentPass == 1) { jumpTarget = _currentScope.FindSymbol(_currentToken.Ident, SymbolType.JumpTarget) as JumpTargetSymbol; jumpTarget.Bind(); } // Read in the colon that ends this label. ExpectToken(TokenID.CharColon); }
/// <summary> /// Parses a switch statement, a switch statement is essentially a /// nicer way of write a list of if/elseif blocks. /// Syntax: /// "Switch" "(" Expression ")" "{" { [ "case" Expresion ":" Statement ] [ "default" ":" Statement ] } "}" /// /// Output byte code example: /// [expression] /// pop a1 /// /// [expression] /// pop a2 /// cmp a2, a1 /// jmp_ne case1exit /// [block] /// jmp exit /// case1exit: /// /// [expression] /// pop a2 /// cmp a2, a1 /// jmp_ne case2exit /// [block] /// jmp exit /// case2exit: /// /// default: /// jmp exit /// /// exit: /// /// </summary> private void ParseSwitch() { // Check we are in a valid scope. if (_currentScope.Type != SymbolType.Function || _currentScope == _globalScope) Error(ErrorCode.InvalidScope, "Switch statements are only valid inside a function's or event's scope.", false, 0); // Declare variables used within this function. JumpTargetSymbol defaultJumpTarget = null, exitJumpTarget = null; JumpTargetSymbol exitCaseJumpTarget = null; Instruction instruction = null; // If we are in pass 2 then create the jump targets. if (_currentPass == 1) { defaultJumpTarget = new JumpTargetSymbol(_currentScope); exitJumpTarget = new JumpTargetSymbol(_currentScope); } // Read in the main expression. ExpectToken(TokenID.CharOpenParenthesis); DataTypeValue expressionDataType = ParseExpression(); ExpectToken(TokenID.CharCloseParenthesis); // Pop the expression into reserved register 3. if (_currentPass == 1) { instruction = CreateInstruction(OpCodeByType(expressionDataType, OpCodeType.POP), _currentScope, _currentToken); new Operand(instruction, Register.Reserved3); } // Read in opening brace. ExpectToken(TokenID.CharOpenBrace); // Create and push a new loop tracker into the tracker stack. if (_currentPass == 1) _loopTrackerStack.Push(new LoopTracker(null, exitJumpTarget)); // Keep looping until we find the end of this switch statement. bool foundDefault = false; while (true) { // If the next token is a closing brace then exit loop. if (LookAheadToken().ID == TokenID.CharCloseBrace) break; // Read in a case block. if (LookAheadToken().ID == TokenID.KeywordCase) { NextToken(); // Read in the case keyword. // Create a exit-case jump target. if (_currentPass == 1) exitCaseJumpTarget = new JumpTargetSymbol(_currentScope); // Read in the case expression. DataTypeValue caseResultDataType = ParseExpression(); // Cast the value into the correct type. if (CanImplicitlyCast(expressionDataType, caseResultDataType) == false) Error(ErrorCode.InvalidCast, "Can't implicitly cast between \"" + expressionDataType.ToString() + "\" and \"" + caseResultDataType.ToString() + "\""); if (_currentPass == 1) { // Pop the value into arith register 1. instruction = CreateInstruction(OpCodeByType(caseResultDataType, OpCodeType.POP), _currentScope, _currentToken); new Operand(instruction, Register.Arithmetic1); // Cast to the correct type if its not already it. if (expressionDataType != caseResultDataType) { instruction = CreateInstruction(OpCodeByType(expressionDataType, OpCodeType.CAST), _currentScope, _currentToken); new Operand(instruction, Register.Arithmetic1); } // Compare the expression and this case's expression. instruction = CreateInstruction(OpCodeByType(expressionDataType, OpCodeType.CMP), _currentScope, _currentToken); new Operand(instruction, Register.Reserved3); new Operand(instruction, Register.Arithmetic1); // If its not equal to the expression then jump to the case // exit jump target. instruction = CreateInstruction(OpCode.JMP_NE, _currentScope, _currentToken); new Operand(instruction, exitCaseJumpTarget); } ExpectToken(TokenID.CharColon); // Read in the colon. // Parse the case statement. ParseStatement(); if (_currentPass == 1) { // Jump to the default block. instruction = CreateInstruction(OpCode.JMP, _currentScope, _currentToken); new Operand(instruction, exitJumpTarget); // Bind the case exit jump target. exitCaseJumpTarget.Bind(); } } // Its not a case so check if its a default block. if (LookAheadToken().ID == TokenID.KeywordDefault) { NextToken(); // Read in the deault keyword. ExpectToken(TokenID.CharColon); // Read in the colon. // Check we haven't already found a default block. if (foundDefault == true) Error(ErrorCode.DuplicateDefault, "Switch statements can't contain duplicate default blocks."); foundDefault = true; if (_currentPass == 1) { // Create a exit-case jump target. exitCaseJumpTarget = new JumpTargetSymbol(_currentScope); // Jump past the default block. instruction = CreateInstruction(OpCode.JMP, _currentScope, _currentToken); new Operand(instruction, exitCaseJumpTarget); // Bind the default jump target. defaultJumpTarget.Bind(); } // Parses the default block's statement. ParseStatement(); if (_currentPass == 1) { // Jump to the exit jump target. instruction = CreateInstruction(OpCode.JMP, _currentScope, _currentToken); new Operand(instruction, exitJumpTarget); // Bind the case exit jump target. exitCaseJumpTarget.Bind(); } } } // Read in closing brace. ExpectToken(TokenID.CharCloseBrace); // Pop the loop tracker off the tracker stack. if (_currentPass == 1) _loopTrackerStack.Pop(); if (_currentPass == 1) { // Jump to the default block if its defined. if (foundDefault == true) { instruction = CreateInstruction(OpCode.JMP, _currentScope, _currentToken); new Operand(instruction, defaultJumpTarget); } // Bind the exit jump target. exitJumpTarget.Bind(); } }
/// <summary> /// Parses a flow-of-control if statement, which will preform the given action /// if the expression evaluates to true. Optional Else and ElseIf block's will be /// preformed if expression evaluates to false. /// Syntax: /// "if" "(" Expression ")" Statement { [ "elseif" Block ] } [ "else" Block ] /// </summary> private void ParseIf() { // Check we are in a valid scope. if (_currentScope.Type != SymbolType.Function || _currentScope == _globalScope) Error(ErrorCode.InvalidScope, "If statments are only valid inside a function's or event's scope.", false, 0); // Generate a false jump target. Instruction instruction = null; JumpTargetSymbol falseJumpTarget = null; JumpTargetSymbol skipFalseJumpTarget = null; // Parse the expression. ExpectToken(TokenID.CharOpenParenthesis); DataTypeValue expressionDataType = ParseExpression(); ExpectToken(TokenID.CharCloseParenthesis); // Make sure we can cast between the expression type and boolean. if (!CanImplicitlyCast(new DataTypeValue(DataType.Bool, false, false), expressionDataType)) Error(ErrorCode.InvalidCast, "Can't implicitly cast between \"Bool\" and \"" + expressionDataType.ToString() + "\"", false, 0); // Generate comparison byte code. if (_currentPass == 1) { // Generate false jump target falseJumpTarget = new JumpTargetSymbol(_currentScope); // Pop the result into arith register 1 instruction = CreateInstruction(OpCodeByType(expressionDataType, OpCodeType.POP), _currentScope, _currentToken); new Operand(instruction, Register.Arithmetic1); // Cast it to boolean if its not already boolean if (expressionDataType != new DataTypeValue(DataType.Bool, false, false)) { instruction = CreateInstruction(OpCode.CAST_BOOL,_currentScope,_currentToken); new Operand(instruction, Register.Arithmetic1); } // Compare result with 0 instruction = CreateInstruction(OpCode.CMP_BOOL, _currentScope, _currentToken); new Operand(instruction, Register.Arithmetic1); new Operand(instruction, false); // Jump to false jump target if comparison is equal. instruction = CreateInstruction(OpCode.JMP_EQ, _currentScope, _currentToken); new Operand(instruction, falseJumpTarget); } // Parse the true statement. ParseStatement(); // Check for the else keyword. if (LookAheadToken().ID == TokenID.KeywordElse) { // Read in the else keyword. NextToken(); if (_currentPass == 1) { // Append an unconditional jump past the false // block to the true block. skipFalseJumpTarget = new JumpTargetSymbol(_currentScope); instruction = CreateInstruction(OpCode.JMP, _currentScope, _currentToken); new Operand(instruction, skipFalseJumpTarget); // Bind the false jump target. falseJumpTarget.Bind(); } // Parse the else statement. ParseStatement(); if (_currentPass == 1) skipFalseJumpTarget.Bind(); } else if (_currentPass == 1) falseJumpTarget.Bind(); }
/// <summary> /// Parses a for loop, which is similar to the while statement but keeps the initialization, /// incrementation and comparison in a more intuative format. /// Syntax: /// "For" "(" Initialization ";" Expression ";" Expression ")" Statement /// /// Output byte code example: /// [initialization] /// start: /// [expression] /// pop a1 /// cmp a1, 0 /// jmp_eq exit /// [statement block] /// [increment] /// jmp start /// /// </summary> private void ParseFor() { // Check we are in a valid scope. if (_currentScope.Type != SymbolType.Function || _currentScope == _globalScope) Error(ErrorCode.InvalidScope, "For statements are only valid inside a function's or event's scope.", false, 0); // Declare some important variables. JumpTargetSymbol startJumpTarget = null; JumpTargetSymbol finishJumpTarget = null; JumpTargetSymbol incrementJumpTarget = null; Instruction instruction = null; // Create jump targets if we are in pass 2. if (_currentPass == 1) { startJumpTarget = new JumpTargetSymbol(_currentScope); incrementJumpTarget = new JumpTargetSymbol(_currentScope); finishJumpTarget = new JumpTargetSymbol(_currentScope); } // Read in the variable declaration. ExpectToken(TokenID.CharOpenParenthesis); NextToken(); if (_currentToken.ID == TokenID.KeywordBool || _currentToken.ID == TokenID.KeywordByte || _currentToken.ID == TokenID.KeywordDouble || _currentToken.ID == TokenID.KeywordObject || _currentToken.ID == TokenID.KeywordFloat || _currentToken.ID == TokenID.KeywordInt || _currentToken.ID == TokenID.KeywordLong || _currentToken.ID == TokenID.KeywordShort || _currentToken.ID == TokenID.KeywordString || _currentToken.ID == TokenID.KeywordVoid || _currentToken.ID == TokenID.KeywordConst || _currentToken.ID == TokenID.KeywordStatic) ParseVariable(); // Its not a variable declaration so read it in as a assignment. else ParseAssignment(); // Bind the starting target. if (_currentPass == 1) startJumpTarget.Bind(); // Parse the expression block. DataTypeValue expressionType = ParseExpression(); ExpectToken(TokenID.CharSemiColon); // Create the expression evaluation instruction's. if (_currentPass == 1) { // Pop the result of the expression into arith register 1. instruction = CreateInstruction(OpCodeByType(expressionType, OpCodeType.POP), _currentScope, _currentToken); new Operand(instruction, Register.Arithmetic1); // Cast it to boolean if its not already boolean. if (expressionType != new DataTypeValue(DataType.Bool, false, false)) { instruction = CreateInstruction(OpCode.CAST_BOOL, _currentScope, _currentToken); new Operand(instruction, Register.Arithmetic1); } // Compare the result of the expression to false. instruction = CreateInstruction(OpCode.CMP_BOOL, _currentScope, _currentToken); new Operand(instruction, Register.Arithmetic1); new Operand(instruction, false); // If its equal jump out of this loop. instruction = CreateInstruction(OpCode.JMP_EQ, _currentScope, _currentToken); new Operand(instruction, finishJumpTarget); } // Skip the increment and parse the block. int incrementTokenIndex = _tokenIndex; Token incrementToken = _currentToken; while (EndOfTokenStream() == false && LookAheadToken().ID != TokenID.CharCloseParenthesis) NextToken(); NextToken(); // Read in closing brace. // Push a loop tracker into the tracker stack. if (_currentPass == 1) _loopTrackerStack.Push(new LoopTracker(incrementJumpTarget, finishJumpTarget)); // Parse the loop's body. ParseStatement(); // Pop the loop tracker of the tracker state. if (_currentPass == 1) _loopTrackerStack.Pop(); // Note down the current token index / token. int finishTokenIndex = _tokenIndex; Token finishToken = _currentToken; // Go back to the increment block and parse it. _tokenIndex = incrementTokenIndex; _currentToken = incrementToken; NextToken(); // Bind the increment jump target. if (_currentPass == 1) incrementJumpTarget.Bind(); // Parse the increment. ParseAssignment(); // Go back to the end. _tokenIndex = finishTokenIndex; _currentToken = finishToken; // Jump to the start block. if (_currentPass == 1) { instruction = CreateInstruction(OpCode.JMP, _currentScope, _currentToken); new Operand(instruction, startJumpTarget); } // Bind the finishing target. if (_currentPass == 1) finishJumpTarget.Bind(); }
/// <summary> /// Parses a do looping statment. The do loop comes in 2 flavours do and do/while; The do /// loop will loop through the statements body for x amount of times, where x is the result /// of the expression. The Do/While loop works just like the While loop but gets evaluated /// at the end rather than the start. /// Syntax: /// "Do" ["(" Expression ")"] Statement [ "While" "(" Expression ")" ] /// </summary> private void ParseDo() { // Check we are in a valid scope. if (_currentScope.Type != SymbolType.Function || _currentScope == _globalScope) Error(ErrorCode.InvalidScope, "Do statments are only valid inside a function's or event's scope.", false, 0); bool whileLoop = true; VariableSymbol doTrackerVariable = null; Instruction instruction = null; // Bind the starting jump target. JumpTargetSymbol startJumpTarget = null, endJumpTarget = null; if (_currentPass == 1) { startJumpTarget = new JumpTargetSymbol(_currentScope); endJumpTarget = new JumpTargetSymbol(_currentScope); } // Check if there is an expression next, if there is its a do loop. if (LookAheadToken().ID == TokenID.CharOpenParenthesis) { // Create an internal variable to count number of loops. if (_currentPass == 0) { doTrackerVariable = new VariableSymbol(_currentScope); doTrackerVariable.Identifier = "$" + _internalVariableIndex++; doTrackerVariable.IsUsed = true; doTrackerVariable.DataType = new DataTypeValue(DataType.Int, false, false); doTrackerVariable.VariableType = VariableType.Local; if (_currentScope.Type == SymbolType.Function) { doTrackerVariable.StackIndex = -(((FunctionSymbol)_currentScope).LocalDataSize + 2); ((FunctionSymbol)_currentScope).LocalDataSize++; } } else { doTrackerVariable = _currentScope.FindSymbol("$" + _internalVariableIndex++, SymbolType.Variable) as VariableSymbol; // Reset the tracker variable to 0. instruction = CreateInstruction(OpCode.MOV_INT, _currentScope, _currentToken); Operand.DirectStackOperand(instruction, doTrackerVariable.StackIndex); new Operand(instruction, (long)0); } // Bind the jump target to here. if (_currentPass == 1) startJumpTarget.Bind(); // Read in do expression. whileLoop = false; NextToken(); DataTypeValue expressionDataType = ParseExpression(); ExpectToken(TokenID.CharCloseParenthesis); // Make sure we can cast between the expression type and boolean. if (!CanImplicitlyCast(new DataTypeValue(DataType.Int, false, false), expressionDataType)) Error(ErrorCode.InvalidCast, "Can't implicitly cast between \"Int\" and \"" + expressionDataType.ToString() + "\"", false, 0); // If we are in pass 2 emit byte code to check value. if (_currentPass == 1) { // Pop the amount of times to loop into the reserved register 1. instruction = CreateInstruction(OpCodeByType(expressionDataType, OpCodeType.POP), _currentScope, _currentToken); new Operand(instruction, Register.Reserved1); // If the value is not a long then cast it into one. if (expressionDataType != new DataTypeValue(DataType.Int, false, false)) { instruction = CreateInstruction(OpCode.CAST_INT, _currentScope, _currentToken); new Operand(instruction, Register.Reserved1); } // Compare the loop tracker to the number of times to loop. instruction = CreateInstruction(OpCode.CMP_INT, _currentScope, _currentToken); Operand.DirectStackOperand(instruction, doTrackerVariable.StackIndex); new Operand(instruction, Register.Reserved1); // If its above or equal to the number of times we want to loop for, then exit. instruction = CreateInstruction(OpCode.JMP_GE, _currentScope, _currentToken); new Operand(instruction, endJumpTarget); // Increment the loop tracker variable. instruction = CreateInstruction(OpCode.INC_INT, _currentScope, _currentToken); Operand.DirectStackOperand(instruction, doTrackerVariable.StackIndex); } } else { // Bind the jump target to here. if (_currentPass == 1) startJumpTarget.Bind(); } // Push a loop tracker onto the loop stack. if (_currentPass == 1) _loopTrackerStack.Push(new LoopTracker(startJumpTarget, endJumpTarget)); // Parse the body of this statement. bool indexerLoop = _insideIndexerLoop; int indexerIndex = _indexerLoopIndex; if (whileLoop == false) { _insideIndexerLoop = true; _indexerLoopIndex = _internalVariableIndex - 1; } ParseStatement(); _insideIndexerLoop = indexerLoop; _indexerLoopIndex = indexerIndex; // Pop the loop tracker of the loop stack if (_currentPass == 1) _loopTrackerStack.Pop(); // If its a while loop then read in the while expression and output evaluation code. if (whileLoop == true) { // Parse the expression. ExpectToken(TokenID.KeywordWhile); ExpectToken(TokenID.CharOpenParenthesis); DataTypeValue expressionDataType = ParseExpression(); ExpectToken(TokenID.CharCloseParenthesis); if (_currentPass == 1) { // Pop the result of the expression into arith register 1. instruction = CreateInstruction(OpCodeByType(expressionDataType, OpCodeType.POP), _currentScope, _currentToken); new Operand(instruction, Register.Arithmetic1); // Cast it to boolean if its not already boolean if (expressionDataType != new DataTypeValue(DataType.Bool, false, false)) { instruction = CreateInstruction(OpCode.CAST_BOOL, _currentScope, _currentToken); new Operand(instruction, Register.Arithmetic1); } // Compare the expression result to 0. instruction = CreateInstruction(OpCode.CMP_BOOL, _currentScope, _currentToken); new Operand(instruction, Register.Arithmetic1); new Operand(instruction, false); // Jump to finish jump target if comparison is equal to false. instruction = CreateInstruction(OpCode.JMP_EQ, _currentScope, _currentToken); new Operand(instruction, endJumpTarget); } } // Create an instruction to jump back to the start of the loop. if (_currentPass == 1) { instruction = CreateInstruction(OpCode.JMP, _currentScope, _currentToken); new Operand(instruction, startJumpTarget); // Bind the end jump target to here. endJumpTarget.Bind(); } }
public Operand(Instruction instruction, JumpTargetSymbol value) { _opType = OperandType.JumpTarget; _jumpTarget = value; instruction[instruction.OperandCount] = this; }
/// <summary> /// Initializes a new instance of this class and sets it start /// and end jump targets as those given. /// </summary> /// <param name="start">Jump target pointing to the start of the loop.</param> /// <param name="end">Jump target pointing to the end of the loop.</param> public LoopTracker(JumpTargetSymbol start, JumpTargetSymbol end) { _startJumpTarget = start; _endJumpTarget = end; }