/// <summary> /// Parses a lower level expression. The lower level is responsible for parsing /// any sub terms and all operators apart from +- arithmetic operators. /// </summary> /// <returns>The data type this expression evaluates to.</returns> private DataTypeValue ParseTermExpression() { // Parse the left hand factor expression. DataTypeValue factorDataType1 = ParseSubTermExpression(); // Parse any subsequent factor expressions. while (true) { // Check that the next token is an arithmatic operator. Token operatorToken = LookAheadToken(); if (operatorToken.ID != TokenID.OpDivide && operatorToken.ID != TokenID.OpModulus && operatorToken.ID != TokenID.OpMultiply && operatorToken.ID != TokenID.OpBitwiseXOr && operatorToken.ID != TokenID.OpBitwiseSHR && operatorToken.ID != TokenID.OpBitwiseSHL && operatorToken.ID != TokenID.OpBitwiseOr && operatorToken.ID != TokenID.OpBitwiseAnd) break; NextToken(); // Parse the right hand term expression. DataTypeValue factorDataType2 = ParseSubTermExpression(); // Check the new data type can be cast to the main data type if (CanImplicitlyCast(factorDataType1, factorDataType2) == true) { // Check the data types are valid with this operator. if (OperatorDataTypeValid(factorDataType1, operatorToken.ID) == false) Error(ErrorCode.InvalidDataType, "Operator \"" + operatorToken.Ident + "\" can't be applied to data type \"" + factorDataType1.ToString() + "\"", false, 1); // We only want to emit byte code on the second pass. if (_currentPass == 0) continue; // Pop the result of the first term into arithmetic // register 1. Instruction instruction = CreateInstruction(OpCodeByType(factorDataType2, OpCodeType.POP), _currentScope, _currentToken); new Operand(instruction, Register.Arithmetic2); // Pop the result of the second term into arithmetic // register 2. instruction = CreateInstruction(OpCodeByType(factorDataType1, OpCodeType.POP), _currentScope, _currentToken); Operand op1 = new Operand(instruction, Register.Arithmetic1); // Cast rvalue into lvalues type. if (factorDataType1 != factorDataType2) { instruction = CreateInstruction(OpCodeByType(factorDataType1, OpCodeType.CAST), _currentScope, _currentToken); new Operand(instruction, Register.Arithmetic2); } // Create the arithmatic instruction based on the operator. switch (operatorToken.ID) { case TokenID.OpDivide: instruction = CreateInstruction(OpCodeByType(factorDataType1, OpCodeType.DIV), _currentScope, _currentToken); break; case TokenID.OpModulus: instruction = CreateInstruction(OpCode.MOD_INT, _currentScope, _currentToken); break; case TokenID.OpMultiply: instruction = CreateInstruction(OpCodeByType(factorDataType1, OpCodeType.MUL), _currentScope, _currentToken); break; case TokenID.OpBitwiseXOr: instruction = CreateInstruction(OpCode.BIT_XOR_INT, _currentScope, _currentToken); break; case TokenID.OpBitwiseSHR: instruction = CreateInstruction(OpCode.BIT_SHL_INT, _currentScope, _currentToken); break; case TokenID.OpBitwiseSHL: instruction = CreateInstruction(OpCode.BIT_SHR_INT, _currentScope, _currentToken); break; case TokenID.OpBitwiseOr: instruction = CreateInstruction(OpCode.BIT_OR_INT, _currentScope, _currentToken); break; case TokenID.OpBitwiseAnd: instruction = CreateInstruction(OpCode.BIT_AND_INT, _currentScope, _currentToken); break; } new Operand(instruction, Register.Arithmetic1); new Operand(instruction, Register.Arithmetic2); // Push the result onto the stack. instruction = CreateInstruction(OpCodeByType(factorDataType1, OpCodeType.PUSH), _currentScope, _currentToken); op1 = new Operand(instruction, Register.Arithmetic1); } else Error(ErrorCode.InvalidCast, "Can't implicitly cast between \"" + factorDataType1.ToString() + "\" and \"" + factorDataType2.ToString() + "\"", false, 1); } return factorDataType1; }
/// <summary> /// Parses a middle level expression. The middle level is responsible for parsing /// any terms and the +- arithmetic operators. // </summary> /// <returns>The data type this expression evaluates to.</returns> private DataTypeValue ParseLeafExpression() { // Parse the left hand term expression. DataTypeValue termDataType1 = ParseTermExpression(); // Parse any subsequent term expressions. while (true) { // Check that the next token is an arithmatic + or -. Token operatorToken = LookAheadToken(); if (operatorToken.ID != TokenID.OpAdd && operatorToken.ID != TokenID.OpSub) break; NextToken(); // Parse the right hand term expression. DataTypeValue termDataType2 = ParseTermExpression(); // Check the new data type can be cast to the main data type if (CanImplicitlyCast(termDataType1, termDataType2) == true) { // Check the data types are valid with this operator. if (OperatorDataTypeValid(termDataType1, operatorToken.ID) == false) Error(ErrorCode.InvalidDataType, "Operator \"" + operatorToken.Ident + "\" can't be applied to data type \"" + termDataType1.ToString() + "\"", false, 1); // We only want to emit byte code on the second pass. if (_currentPass == 0) continue; // Pop the result of the second term into arithmetic // register 1. Instruction instruction = CreateInstruction(OpCodeByType(termDataType2, OpCodeType.POP), _currentScope, _currentToken); new Operand(instruction, Register.Arithmetic2); // Pop the result of the first term into arithmetic // register 2. instruction = CreateInstruction(OpCodeByType(termDataType1, OpCodeType.POP), _currentScope, _currentToken); Operand op1 = new Operand(instruction, Register.Arithmetic1); // Cast rvalue into lvalues type. if (termDataType1 != termDataType2) { instruction = CreateInstruction(OpCodeByType(termDataType1, OpCodeType.CAST), _currentScope, _currentToken); new Operand(instruction, Register.Arithmetic2); } // Create the arithmatic instruction based on the operator. switch (operatorToken.ID) { case TokenID.OpAdd: instruction = CreateInstruction(OpCodeByType(termDataType1, OpCodeType.ADD), _currentScope, _currentToken); break; case TokenID.OpSub: instruction = CreateInstruction(OpCodeByType(termDataType1, OpCodeType.SUB), _currentScope, _currentToken); break; } new Operand(instruction, Register.Arithmetic1); new Operand(instruction, Register.Arithmetic2); // Push the result onto the stack. instruction = CreateInstruction(OpCodeByType(termDataType1, OpCodeType.PUSH), _currentScope, _currentToken); op1 = new Operand(instruction, Register.Arithmetic1); } else Error(ErrorCode.InvalidCast, "Can't implicitly cast between \"" + termDataType1.ToString() + "\" and \"" + termDataType2.ToString() + "\"", false, 1); } return termDataType1; }
/// <summary> /// Parses a return statement, which will break out of a function call. /// Syntax: /// return [ Expression ] ";" /// </summary> private void ParseReturn() { // Check we are in a valid scope. if (_currentScope.Type != SymbolType.Function || _currentScope == _globalScope) Error(ErrorCode.InvalidScope, "Returns are only valid inside a function's or event's scope.", false, 0); if (_currentScope.Type == SymbolType.Function) { FunctionSymbol functionSymbol = (FunctionSymbol)_currentScope; // Check if there is an return expression after this. if (LookAheadToken().ID != TokenID.CharSemiColon) { // Check that the function is expecting a return value. if (functionSymbol.ReturnType.DataType == DataType.Void) Error(ErrorCode.ExpectingReturnValue, "Void function can't return a value.", false, 0); // Parse the return value's expression. DataTypeValue returnValueDataType = ParseExpression(); // Check the return value can be implicitly cast to the given type. if (CanImplicitlyCast(functionSymbol.ReturnType, returnValueDataType) == true) { if (_currentPass == 1) { // Pop the return value into the return register. Instruction instruction = CreateInstruction(OpCodeByType(returnValueDataType, OpCodeType.POP), _currentScope, _currentToken); Operand op1 = new Operand(instruction, Register.Return); // Cast return value into correct type. if (functionSymbol.ReturnType != returnValueDataType) { instruction = CreateInstruction(OpCodeByType(functionSymbol.ReturnType, OpCodeType.CAST), _currentScope, _currentToken); new Operand(instruction, Register.Return); } } } else Error(ErrorCode.InvalidCast, "Can't implicitly cast between \"" + functionSymbol.ReturnType.ToString() + "\" and \"" + returnValueDataType.ToString() + "\"", false, 0); } else { // Check if we need a return value or not. if (functionSymbol.ReturnType.DataType != DataType.Void) Error(ErrorCode.ExpectingReturnValue, "Function expects return value.", false, 0); if (_currentPass == 1) { // Nullify the return register incase it contains a // value from a previous function call. Instruction instruction = CreateInstruction(OpCode.CAST_NULL, functionSymbol, _currentToken); new Operand(instruction, Register.Return); } } // Create return instruction. if (_currentPass == 1) CreateInstruction(OpCode.RETURN, _currentScope, _currentToken); } // Read in semi-colon at end of declaration. ExpectToken(TokenID.CharSemiColon); }
/// <summary> /// Creates a new instruction index operand. /// </summary> /// <param name="instruction">Instruction to add operand to.</param> /// <param name="instrIndex">Index of instruction this operand should point to.</param> /// <returns>New operand instance.</returns> public static Operand InstrIndexOperand(Instruction instruction, int instrIndex) { Operand op = new Operand(instruction, OperandType.InstrIndex); op.InstrIndex = instrIndex; return op; }
/// <summary> /// Creates a new symbol index operand. /// </summary> /// <param name="instruction">Instruction to add operand to.</param> /// <param name="symbolIndex">Index of symbol this operand should point to.</param> /// <returns>New operand instance.</returns> public static Operand SymbolIndexOperand(Instruction instruction, int symbolIndex) { Operand op = new Operand(instruction, OperandType.SymbolIndex); op.SymbolIndex = symbolIndex; return op; }
/// <summary> /// Creates a new indirect stack operand. /// </summary> /// <param name="instruction">Instruction to add operand to.</param> /// <param name="register">Register containing stack slot this operand should point to.</param> /// <returns>New operand instance.</returns> public static Operand IndirectStackOperand(Instruction instruction, Register register) { Operand op = new Operand(instruction, OperandType.IndirectStack); op.Register = register; return op; }
/// <summary> /// Creates a new indirect stack indexed operand. /// </summary> /// <param name="instruction">Instruction to add operand to.</param> /// <param name="register">Register containing stack slot this operand should point to.</param> /// <param name="offsetRegister">Register containing index offset this operand should point to.</param> /// <returns>New operand instance.</returns> public static Operand IndirectStackIndexedOperand(Instruction instruction, Register register, Register offsetRegister) { Operand op = new Operand(instruction, OperandType.IndirectStackIndexed); op.Register = register; op.OffsetRegister = offsetRegister; return op; }
/// <summary> /// Creates a new direct stack operand. /// </summary> /// <param name="instruction">Instruction to add operand to.</param> /// <param name="stackIndex">Index of stack slot this operand should point to.</param> /// <returns>New operand instance.</returns> public static Operand DirectStackOperand(Instruction instruction, int stackIndex) { Operand op = new Operand(instruction, OperandType.DirectStack); op.StackIndex = stackIndex; return op; }
/// <summary> /// Creates a new direct stack index operand. /// </summary> /// <param name="instruction">Instruction to add operand to.</param> /// <param name="stackIndex">Index of stack slot this operand should point to.</param> /// <param name="register">Register containing index offset.</param> /// <returns>New operand instance.</returns> public static Operand DirectStackIndexedOperand(Instruction instruction, int stackIndex, Register regiser) { Operand op = new Operand(instruction, OperandType.DirectStackIndexed); op.StackIndex = stackIndex; op.OffsetRegister = regiser; return op; }
/// <summary> /// Creates a new direct memory operand. /// </summary> /// <param name="instruction">Instruction to add operand to.</param> /// <param name="memoryIndex">Index of memory slot this operand should point to.</param> /// <returns>New operand instance.</returns> public static Operand DirectMemoryOperand(Instruction instruction, int memoryIndex) { Operand op = new Operand(instruction, OperandType.DirectMemory); op.MemoryIndex = memoryIndex; return op; }