/// <summary> /// Returns true if the given data type is valid for the given operator. /// </summary> /// <param name="type">Data type to check operator against.</param> /// <param name="opToken">Operator to check validitity for.</param> /// <returns>True if the given data type is valid for the given operator.</returns> private bool OperatorDataTypeValid(DataTypeValue type, TokenID opToken) { if (type.DataType == DataType.Invalid) return false; if (type.IsArray == true && opToken != TokenID.OpAssign && opToken != TokenID.OpEqual && opToken != TokenID.OpNotEqual) return false; switch (opToken) { case TokenID.OpAdd: return (type.DataType == DataType.Object ? false : true); case TokenID.OpAssign: return true; case TokenID.OpAssignAdd: return (type.DataType == DataType.Object ? false : true); case TokenID.OpAssignBitwiseAnd: return (type.DataType != DataType.Int ? false : true); case TokenID.OpAssignBitwiseOr: return (type.DataType != DataType.Int ? false : true); case TokenID.OpAssignBitwiseNot: return (type.DataType != DataType.Int ? false : true); case TokenID.OpAssignBitwiseSHL: return (type.DataType != DataType.Int ? false : true); case TokenID.OpAssignBitwiseSHR: return (type.DataType != DataType.Int ? false : true); case TokenID.OpAssignBitwiseXOr: return (type.DataType != DataType.Int ? false : true); case TokenID.OpAssignDivide: return (type.DataType == DataType.Object || type.DataType == DataType.String || type.DataType == DataType.Null ? false : true); case TokenID.OpAssignModulus: return (type.DataType != DataType.Int ? false : true); case TokenID.OpAssignMultiply: return (type.DataType == DataType.Object || type.DataType == DataType.String || type.DataType == DataType.Null ? false : true); case TokenID.OpAssignSub: return (type.DataType == DataType.Object || type.DataType == DataType.String || type.DataType == DataType.Null ? false : true); case TokenID.OpBitwiseAnd: return (type.DataType != DataType.Int ? false : true); case TokenID.OpBitwiseOr: return (type.DataType != DataType.Int ? false : true); case TokenID.OpBitwiseSHL: return (type.DataType != DataType.Int ? false : true); case TokenID.OpBitwiseSHR: return (type.DataType != DataType.Int ? false : true); case TokenID.OpBitwiseXOr: return (type.DataType != DataType.Int ? false : true); case TokenID.OpBitwiseNot: return (type.DataType != DataType.Int ? false : true); case TokenID.OpDecrement: return (type.DataType == DataType.Object || type.DataType == DataType.String || type.DataType == DataType.Null ? false : true); case TokenID.OpDivide: return (type.DataType == DataType.Object || type.DataType == DataType.String || type.DataType == DataType.Null ? false : true); case TokenID.OpEqual: return true; case TokenID.OpGreater: return (type.DataType == DataType.Object || type.DataType == DataType.String || type.DataType == DataType.Null ? false : true); case TokenID.OpGreaterEqual: return (type.DataType == DataType.Object || type.DataType == DataType.String || type.DataType == DataType.Null ? false : true); case TokenID.OpIncrement: return (type.DataType == DataType.Object || type.DataType == DataType.String || type.DataType == DataType.Null ? false : true); case TokenID.OpLess: return (type.DataType == DataType.Object || type.DataType == DataType.String || type.DataType == DataType.Null ? false : true); case TokenID.OpLessEqual: return (type.DataType == DataType.Object || type.DataType == DataType.String || type.DataType == DataType.Null ? false : true); case TokenID.OpLogicalAnd: return (type.DataType != DataType.Bool ? false : true); case TokenID.OpLogicalOr: return (type.DataType != DataType.Bool ? false : true); case TokenID.OpLogicalNot: return (type.DataType != DataType.Bool ? false : true); case TokenID.OpModulus: return (type.DataType != DataType.Int ? false : true); case TokenID.OpMultiply: return (type.DataType == DataType.Object || type.DataType == DataType.String || type.DataType == DataType.Null ? false : true); case TokenID.OpNotEqual: return true; case TokenID.OpSub: return (type.DataType == DataType.Object || type.DataType == DataType.String || type.DataType == DataType.Null ? false : true); default: return false; } }
/// <summary> /// Checks if a given parameter data type can be applied to this parameter. /// </summary> /// <param name="index">Index of parameter to check.</param> /// <returns>Boolean describing if the data type is valid.</returns> public bool CheckParameterTypeValid(int index, DataTypeValue value) { if (index >= _symbolList.Count) return false; VariableSymbol symbol = _symbolList[index] as VariableSymbol; return (symbol.DataType == value || value.DataType == DataType.Null);//|| ScriptCompiler.CanImplicitlyCast(symbol.DataType, value)); }
/// <summary> /// Nullifys all values in this instance. /// </summary> public void Clear() { _booleanLiteral = false; _byteLiteral = 0; _doubleLiteral = 0; _floatLiteral = 0; _integerLiteral = 0; _longLiteral = 0; _memoryIndex = -1; _shortLiteral = 0; _stringLiteral = ""; _objectIndex = -1; _referenceCount = 0; _valueType = RuntimeValueType.Invalid; _dataType = null; }
/// <summary> /// Parses a single statement. A statement encompesses the highest level of parsing, things like /// the while loop, the if statement and the return statement ... ecetera. /// </summary> private void ParseStatement() { // Check is is an empty statement. if (LookAheadToken() != null && LookAheadToken().ID == TokenID.CharSemiColon) { ExpectToken(TokenID.CharSemiColon); return; } // Check if there is an open block next, and if so parse it. if (LookAheadToken().ID == TokenID.CharOpenBrace) { ParseBlock(TokenID.CharOpenBrace, TokenID.CharCloseBrace); return; } // Check if we have a chunk of meta data next. bool foundThisParse = true; if (LookAheadToken().ID == TokenID.CharOpenBracket) { _lastMetaDataSymbol = null; foundThisParse = true; ParseMetaData(); } // Emit a enter statement opcode if in debug. bool isBreakpoint = (LookAheadToken().ID == TokenID.KeywordBreakpoint); if ((_compileFlags & CompileFlags.Debug) != 0 && _currentPass == 1 && isBreakpoint == false) CreateInstruction(OpCode.ENTER_STATEMENT, _currentScope, (Token)_tokenList[_tokenIndex]); // Parse based on starting token. switch (NextToken().ID) { // General statements. case TokenID.KeywordDo: ParseDo(); break; case TokenID.KeywordEnum: ParseEnum(); break; case TokenID.KeywordIf: ParseIf(); break; case TokenID.KeywordLock: ParseLock(); break; case TokenID.KeywordAtom: ParseAtom(); break; case TokenID.KeywordReturn: ParseReturn(); break; case TokenID.KeywordSwitch: ParseSwitch(); break; case TokenID.KeywordState: ParseState(); break; case TokenID.KeywordWhile: ParseWhile(); break; case TokenID.KeywordFor: ParseFor(); break; case TokenID.KeywordGoto: ParseGoto(); break; case TokenID.KeywordBreak: ParseBreak(); break; case TokenID.KeywordContinue: ParseContinue(); break; case TokenID.KeywordGotoState: ParseGotoState(); break; case TokenID.KeywordBreakpoint: ParseBreakPoint(); break; case TokenID.KeywordNamespace: ParseNamespace(); break; case TokenID.KeywordEditor: case TokenID.KeywordEngine: ParseState(); break; /* case TokenID.KeywordStruct: ParseStruct(); break; case TokenID.KeywordClass: ParseClass(); break; */ // Variable / function declarations. case TokenID.KeywordBool: case TokenID.KeywordByte: case TokenID.KeywordDouble: case TokenID.KeywordObject: case TokenID.KeywordFloat: case TokenID.KeywordInt: case TokenID.KeywordLong: case TokenID.KeywordShort: case TokenID.KeywordString: case TokenID.KeywordVoid: case TokenID.KeywordConst: case TokenID.KeywordStatic: case TokenID.KeywordProperty: case TokenID.KeywordImport: case TokenID.KeywordExport: case TokenID.KeywordThread: case TokenID.KeywordPublic: case TokenID.KeywordProtected: case TokenID.KeywordPrivate: case TokenID.KeywordEvent: case TokenID.KeywordConsole: int offset = 0; if (_currentToken.ID == TokenID.KeywordPublic || _currentToken.ID == TokenID.KeywordProtected || _currentToken.ID == TokenID.KeywordPrivate) offset = 1; if (_currentToken.ID == TokenID.KeywordConsole || _currentToken.ID == TokenID.KeywordEvent || _currentToken.ID == TokenID.KeywordExport || _currentToken.ID == TokenID.KeywordImport || _currentToken.ID == TokenID.KeywordThread || LookAheadToken(2 + offset).ID == TokenID.CharOpenParenthesis) ParseFunction(); // If its not parenthesis its a variable declaration. else ParseVariable(); break; // Assignments and function calls. case TokenID.TypeIdentifier: // Check if its a label. if (LookAheadToken().ID == TokenID.CharColon) ParseLabel(); // See if its a function or assignment. else { int tokenIndex = _tokenIndex; Token currentToken = _currentToken; ResolveMemberScope(); // Ignore any array accessors. if (LookAheadToken().ID == TokenID.CharOpenBracket) { NextToken(); int depth = 1; while (depth != 0) { NextToken(); if (_currentToken.ID == TokenID.CharOpenBracket) depth++; else if (_currentToken.ID == TokenID.CharCloseBracket) depth--; } //ExpectToken(TokenID.CharCloseBracket); } // Function if (LookAheadToken().ID == TokenID.CharOpenParenthesis) { string functionName = _currentToken.Ident; _tokenIndex = tokenIndex; _currentToken = currentToken; // Check we are in a valid scope. if (_currentScope.Type != SymbolType.Function || _currentScope == _globalScope) Error(ErrorCode.InvalidScope, "Function calls are only valid inside a function's or event's scope.", false, 0); DataTypeValue functionDataType = ParseFunctionCall(); // Do we have a member call after? if (LookAheadToken().ID == TokenID.OpMemberResolver) { NextToken(); // If its not an object how can we access it? if (functionDataType != new DataTypeValue(DataType.Object, false, false)) Error(ErrorCode.IllegalResolve, "Member accessing can only be preformed on objects.", false, 1); // Parse the cast. ExpectToken(TokenID.CharOpenParenthesis); if (NextToken().IsDataType == false) Error(ErrorCode.InvalidCast, "Attempt to cast member to unknown type.", false, 0); DataTypeValue castDataType = new DataTypeValue(DataTypeFromKeywordToken(_currentToken.ID), false, false); // Is it an array? if (LookAheadToken().ID == TokenID.CharCloseBracket) { NextToken(); ExpectToken(TokenID.CharCloseBracket); castDataType.IsArray = true; } ExpectToken(TokenID.CharCloseParenthesis); ExpectToken(TokenID.TypeIdentifier); if (LookAheadToken().ID == TokenID.CharOpenParenthesis) ParseMemberFunctionCall(_currentToken.Ident, castDataType); else ParseMemberAssignment(_currentToken.Ident, castDataType); } ExpectToken(TokenID.CharSemiColon); } // Member function / assignment? else if (LookAheadToken().ID == TokenID.OpMemberResolver) { ParseMemberAccessor(); ExpectToken(TokenID.CharSemiColon); } // Assignment. else { _tokenIndex = tokenIndex; _currentToken = currentToken; ParseAssignment(); ExpectToken(TokenID.CharSemiColon); } } break; // Its none of the above its invalid. default: Error(ErrorCode.UnexpectedToken, "Encountered unexpected token \"" + _currentToken.ToString() + "\"", false, 0); break; } // Emit a exit statement opcode if in debug. if ((_compileFlags & CompileFlags.Debug) != 0 && _currentPass == 1 && isBreakpoint == false) CreateInstruction(OpCode.EXIT_STATEMENT, _currentScope, _currentToken); // If we read in any meta data associated with this statement then destroy it now. if (_metaDataList.Count > 0 && foundThisParse == true) { Symbol symbolToAttachTo = null; if (_lastMetaDataSymbol != null) symbolToAttachTo = _lastMetaDataSymbol; else symbolToAttachTo = _currentScope; if (_currentPass == 0) { foreach (MetaDataSymbol metaDataSymbol in _metaDataList) { metaDataSymbol.Scope = symbolToAttachTo; symbolToAttachTo.AddSymbol(metaDataSymbol); } } _metaDataList.Clear(); } }
/// <summary> /// Parses a lower level expression. The lower level is responsible for parsing /// any factors and the -> operator. /// </summary> /// <returns>The data type this expression evaluates to.</returns> private DataTypeValue ParseSubTermExpression() { // Parse the left hand factor expression. DataTypeValue factorDataType1 = ParseFactorExpression(); // Parse any subsequent factor expressions. while (true) { // Check that the next token is an member resolver operator. Token operatorToken = LookAheadToken(); if (operatorToken.ID != TokenID.OpMemberResolver) break; NextToken(); // If its not an object how can we access it? if (factorDataType1.DataType != DataType.Object) Error(ErrorCode.IllegalResolve, "Member accessing can only be preformed on objects.", false, 1); // ARRAY ACCESSING! // Parse the cast. ExpectToken(TokenID.CharOpenParenthesis); if (NextToken().IsDataType == false) Error(ErrorCode.InvalidCast, "Attempt to cast member to unknown type.", false, 0); DataTypeValue castDataType = new DataTypeValue(DataTypeFromKeywordToken(_currentToken.ID), false, false); // Is it an array? if (LookAheadToken().ID == TokenID.CharCloseBracket) { NextToken(); ExpectToken(TokenID.CharCloseBracket); castDataType.IsArray = true; } ExpectToken(TokenID.CharCloseParenthesis); // Set the factor data type as the casts value. factorDataType1 = castDataType; // Read in the indentifier of the member. string identifier = ExpectToken(TokenID.TypeIdentifier).Ident; // Parse The member that is being called. if (LookAheadToken().ID != TokenID.CharOpenParenthesis) { // Are we going to index it? bool parsingArray = false; DataTypeValue arrayIndexValue = new DataTypeValue(DataType.Invalid, false, false); if (LookAheadToken().ID == TokenID.CharOpenBracket) { parsingArray = true; arrayIndexValue = ParseExpression(); ExpectToken(TokenID.CharCloseBracket); } // Create the symbol. VariableSymbol variableSymbol = null; if (_currentPass == 0) { variableSymbol = _memberScope.FindVariableSymbol(identifier, castDataType) as VariableSymbol; if (variableSymbol == null) { variableSymbol = new VariableSymbol(_memberScope); variableSymbol.Identifier = identifier; variableSymbol.DataType = castDataType; variableSymbol.VariableType = VariableType.Member; } } else if (_currentPass == 1) variableSymbol = _memberScope.FindVariableSymbol(identifier, castDataType) as VariableSymbol; if (_currentPass == 1) { Instruction instruction = null; // Pop the index into arith 1. if (parsingArray == true) { instruction = CreateInstruction(OpCodeByType(arrayIndexValue, OpCodeType.POP), _currentScope, _currentToken); new Operand(instruction, Register.Arithmetic1); } // Pop the object into the member register. instruction = CreateInstruction(OpCode.POP_OBJECT, _currentScope, _currentToken); new Operand(instruction, Register.Arithmetic2); // Call the GET_MEMBER opCode. if (parsingArray) { instruction = CreateInstruction(OpCode.GET_MEMBER, _currentScope, _currentToken); new Operand(instruction, Register.Arithmetic2); new Operand(instruction, variableSymbol); } else { instruction = CreateInstruction(OpCode.GET_MEMBER_INDEXED, _currentScope, _currentToken); new Operand(instruction, Register.Arithmetic2); new Operand(instruction, variableSymbol); new Operand(instruction, Register.Arithmetic1); } // Push the returned value. instruction = CreateInstruction(OpCodeByType(castDataType, OpCodeType.PUSH), _currentScope, _currentToken); new Operand(instruction, Register.Return); } } else { #region Function calling // Parse the function call and remember the return type. ParseMemberFunctionCall(identifier, castDataType); // Push the return value of the function onto the stack. if (_currentPass == 1) { Instruction instruction = CreateInstruction(OpCodeByType(castDataType, OpCodeType.PUSH), _currentScope, _currentToken); new Operand(instruction, Register.Return); } #endregion } } return factorDataType1; }
/// <summary> /// Parses the lowest level of an expression. The lowest level is respolsible /// for parsing things such as literal values and function calls, it also /// deals with unary, prefix and postfix operators. /// </summary> /// <returns>The data type this expression evaluates to.</returns> private DataTypeValue ParseFactorExpression() { // Declare a variable to store the data type in. DataTypeValue factorDataType = new DataTypeValue(DataType.Invalid, false, false); StringSymbol stringSymbol = null; #region Unary op // Check if there is a unary operator pending. bool unaryOpPending = false; Token unaryOpToken = LookAheadToken(); if (unaryOpToken.ID == TokenID.OpAdd || unaryOpToken.ID == TokenID.OpSub || unaryOpToken.ID == TokenID.OpLogicalNot || unaryOpToken.ID == TokenID.OpBitwiseNot || unaryOpToken.ID == TokenID.OpIncrement || unaryOpToken.ID == TokenID.OpDecrement) { unaryOpPending = true; NextToken(); } #endregion #region Cast Pending // Check if there is a explicit cast pending. bool castPending = false; DataTypeValue castType = new DataTypeValue(DataType.Invalid, false, false); if (LookAheadToken().ID == TokenID.CharOpenParenthesis) { castType.DataType = DataTypeFromKeywordToken(LookAheadToken(2).ID); if (castType.DataType != DataType.Invalid) { NextToken(); // Read in open parenthesis. NextToken(); // Read in cast keyword. // Check for array. if (LookAheadToken().ID == TokenID.CharOpenBracket) { NextToken(); castType.IsArray = true; ExpectToken(TokenID.CharCloseBracket); } ExpectToken(TokenID.CharCloseParenthesis); // Read in closing parenthesis. castPending = true; } } #endregion // Determine what factor type we are looking at // and act accordingly. NextToken(); switch (_currentToken.ID) { #region Indexer case TokenID.KeywordIndexer: // Check we are inside an indexer loop. if (_insideIndexerLoop == false) Error(ErrorCode.InvalidIndexer, "The Indexer keyword can only be used inside a valid indexable loop."); // Set the factor data type as an integer (an indexer is always an integer). factorDataType = new DataTypeValue(DataType.Int, false, false); // Generate byte code to push indexer onto the stack. if (_currentPass == 1) { VariableSymbol indexerVariable = _currentScope.FindSymbol("$" + _indexerLoopIndex, SymbolType.Variable) as VariableSymbol; Instruction instruction = CreateInstruction(OpCodeByType(factorDataType, OpCodeType.PUSH), _currentScope, _currentToken); Operand.DirectStackOperand(instruction, indexerVariable.StackIndex); } break; #endregion #region New case TokenID.KeywordNew: // Read in identifier specifying what we wish to create a new // instance of. NextToken(); // Check if we are detailing with a data type. DataTypeValue dataType = new DataTypeValue(DataTypeFromKeywordToken(_currentToken.ID), true, false); factorDataType = dataType; if (dataType.DataType != DataType.Invalid) { // Check this is an array. if (LookAheadToken().ID == TokenID.CharOpenBracket) { // Read in opening bracket. NextToken(); // Parse the size expression. DataTypeValue indexDataType = ParseExpression(); // Make sure we can convert it to an integer index. if (CanImplicitlyCast(new DataTypeValue(DataType.Int, false, false), indexDataType)) { // If we are in pass 2 create the byte code to allocate a // new array on the heap and associate it with this variable. if (_currentPass == 1) { // Pop the index value into arith register 2. Instruction instruction = CreateInstruction(OpCodeByType(indexDataType, OpCodeType.POP), _currentScope, _currentToken); new Operand(instruction, Register.Arithmetic2); // Cast? // Allocate enough space on the heap for the array. instruction = CreateInstruction(OpCodeByType(dataType, OpCodeType.ALLOCATE_HEAP), _currentScope, _currentToken); new Operand(instruction, Register.Arithmetic2); new Operand(instruction, Register.Reserved4); // Push the memory index onto the stack. instruction = CreateInstruction(OpCodeByType(dataType, OpCodeType.PUSH), _currentScope, _currentToken); new Operand(instruction, Register.Reserved4); } } else Error(ErrorCode.InvalidCast, "Can't implicitly cast between \"Int\" and \"" + indexDataType.ToString() + "\"", false, 0); // Read in closing bracket. ExpectToken(TokenID.CharCloseBracket); if (LookAheadToken().ID == TokenID.CharOpenBrace) { ExpectToken(TokenID.CharOpenBrace); int index = 0; while (true) { // Parse the expression DataTypeValue valueType = ParseExpression(); if (CanImplicitlyCast(new DataTypeValue(dataType.DataType, false, false), valueType) == true) { // Its valid so insert. if (_currentPass == 1) { Instruction instruction = null; // Pop the value into arith 2. instruction = CreateInstruction(OpCodeByType(valueType, OpCodeType.POP), _currentScope, _currentToken); new Operand(instruction, Register.Arithmetic2); // Pop the memory index off the stack. instruction = CreateInstruction(OpCodeByType(dataType, OpCodeType.POP), _currentScope, _currentToken); new Operand(instruction, Register.Reserved4); // Do we need to cast first? if (dataType.DataType != valueType.DataType) { // Cast value. instruction = CreateInstruction(OpCodeByType(new DataTypeValue(dataType.DataType, false, false), OpCodeType.CAST), _currentScope, _currentToken); new Operand(instruction, Register.Arithmetic2); } // Move index into arith 3. instruction = CreateInstruction(OpCode.MOV_INT, _currentScope, _currentToken); new Operand(instruction, Register.Arithmetic3); new Operand(instruction, index); // Insert! instruction = CreateInstruction(OpCodeByType(new DataTypeValue(dataType.DataType, false, false), OpCodeType.MOV), _currentScope, _currentToken); Operand.IndirectMemoryIndexedOperand(instruction, Register.Reserved4, Register.Arithmetic3); new Operand(instruction, Register.Arithmetic2); // Push the memory index back onto the stack. instruction = CreateInstruction(OpCodeByType(dataType, OpCodeType.PUSH), _currentScope, _currentToken); new Operand(instruction, Register.Reserved4); } } else Error(ErrorCode.InvalidDataType, "Unable to implicitly cast from type \"" + DataTypeFromTypeToken(_currentToken.ID).ToString() + "\" to type \"" + dataType.DataType.ToString() + "\".", false, 1); // Read in comma or break out of loop if there isn't one. if (LookAheadToken().ID == TokenID.CharComma) { NextToken(); index++; } else break; } ExpectToken(TokenID.CharCloseBrace); } } else Error(ErrorCode.InvalidFactor, "Primitive data types must currently be created as arrays.", false,0); } else Error(ErrorCode.InvalidFactor, "New keyword can currently only be used to create new primitive data arrays.", false,0); break; #endregion #region Boolean case TokenID.TypeBoolean: factorDataType = new DataTypeValue(DataType.Bool, false, false); if (_currentPass == 1) { Instruction instruction = CreateInstruction(OpCodeByType(factorDataType, OpCodeType.PUSH), _currentScope, _currentToken); new Operand(instruction, bool.Parse(_currentToken.Ident)); } break; #endregion #region Byte case TokenID.TypeByte: factorDataType = new DataTypeValue(DataType.Byte, false, false); if (_currentPass == 1) { Instruction instruction = CreateInstruction(OpCodeByType(factorDataType, OpCodeType.PUSH), _currentScope, _currentToken); new Operand(instruction, byte.Parse(_currentToken.Ident)); } break; #endregion #region Double case TokenID.TypeDouble: factorDataType = new DataTypeValue(DataType.Double, false, false); if (_currentPass == 1) { Instruction instruction = CreateInstruction(OpCodeByType(factorDataType, OpCodeType.PUSH), _currentScope, _currentToken); new Operand(instruction, double.Parse(_currentToken.Ident)); } break; #endregion #region Float case TokenID.TypeFloat: factorDataType = new DataTypeValue(DataType.Float, false, false); if (_currentPass == 1) { Instruction instruction = CreateInstruction(OpCodeByType(factorDataType, OpCodeType.PUSH), _currentScope, _currentToken); new Operand(instruction, float.Parse(_currentToken.Ident)); } break; #endregion #region Integer case TokenID.TypeInteger: factorDataType = new DataTypeValue(DataType.Int, false, false); if (_currentPass == 1) { Instruction instruction = CreateInstruction(OpCodeByType(factorDataType, OpCodeType.PUSH), _currentScope, _currentToken); if (_currentToken.Ident.Length > 2 && _currentToken.Ident.Substring(0, 2) == "0x") new Operand(instruction, Convert.ToInt32(_currentToken.Ident, 16)); else new Operand(instruction, int.Parse(_currentToken.Ident)); } break; #endregion #region Long case TokenID.TypeLong: factorDataType = new DataTypeValue(DataType.Long, false, false); if (_currentPass == 1) { Instruction instruction = CreateInstruction(OpCodeByType(factorDataType, OpCodeType.PUSH), _currentScope, _currentToken); new Operand(instruction, Convert.ToInt64(_currentToken.Ident)); } break; #endregion #region Short case TokenID.TypeShort: factorDataType = new DataTypeValue(DataType.Short, false, false); if (_currentPass == 1) { Instruction instruction = CreateInstruction(OpCodeByType(factorDataType, OpCodeType.PUSH), _currentScope, _currentToken); new Operand(instruction, short.Parse(_currentToken.Ident)); } break; #endregion #region Null case TokenID.TypeNull: factorDataType = new DataTypeValue(DataType.Null, false, false); if (_currentPass == 1) { Instruction instruction = CreateInstruction(OpCodeByType(factorDataType, OpCodeType.PUSH), _currentScope, _currentToken); } break; #endregion #region String case TokenID.TypeString: // Always add strings to the global scope, this stops them // being duplicated in multiple scopes. factorDataType = new DataTypeValue(DataType.String, false, false); // Check for a pre-existing string with the same value. foreach (Symbol subSymbol in _globalScope.Symbols) { if (subSymbol.Identifier == null || subSymbol.Type != SymbolType.String) continue; if (subSymbol.Identifier == _currentToken.Ident) { stringSymbol = subSymbol as StringSymbol; break; } } if (stringSymbol == null) stringSymbol = new StringSymbol(_globalScope, _currentToken.Ident); if (_currentPass == 1) { Instruction instruction = CreateInstruction(OpCodeByType(factorDataType, OpCodeType.PUSH), _currentScope, _currentToken); new Operand(instruction, stringSymbol); } break; #endregion #region Identifier case TokenID.TypeIdentifier: VariableSymbol symbol = null; string symbolIdent = _currentToken.Ident; int tokenIndex = _tokenIndex; Token currentToken = _currentToken; Symbol resolvedScope = ResolveMemberScope(); symbol = resolvedScope == null ? null : resolvedScope.FindSymbol(_currentToken.Ident, SymbolType.Variable) as VariableSymbol; if (symbol != null) { // Tag the variable as used so we don't end up throwing up a warning. symbol.IsUsed = true; // Set the factor data type. factorDataType = new DataTypeValue(symbol.DataType.DataType, symbol.DataType.IsArray, symbol.DataType.IsReference); if (symbol.IsConstant == true && symbol.ConstToken != null) { switch (symbol.DataType.DataType) { case DataType.Bool: factorDataType = new DataTypeValue(DataType.Bool, false, false); if (_currentPass == 1) { Instruction instruction = CreateInstruction(OpCodeByType(factorDataType, OpCodeType.PUSH), _currentScope, _currentToken); new Operand(instruction, bool.Parse(symbol.ConstToken.Ident)); } break; case DataType.Byte: factorDataType = new DataTypeValue(DataType.Byte, false, false); if (_currentPass == 1) { Instruction instruction = CreateInstruction(OpCodeByType(factorDataType, OpCodeType.PUSH), _currentScope, _currentToken); new Operand(instruction, byte.Parse(symbol.ConstToken.Ident)); } break; case DataType.Double: factorDataType = new DataTypeValue(DataType.Double, false, false); if (_currentPass == 1) { Instruction instruction = CreateInstruction(OpCodeByType(factorDataType, OpCodeType.PUSH), _currentScope, _currentToken); new Operand(instruction, double.Parse(symbol.ConstToken.Ident)); } break; case DataType.Float: factorDataType = new DataTypeValue(DataType.Float, false, false); if (_currentPass == 1) { Instruction instruction = CreateInstruction(OpCodeByType(factorDataType, OpCodeType.PUSH), _currentScope, _currentToken); new Operand(instruction, float.Parse(symbol.ConstToken.Ident)); } break; case DataType.Int: factorDataType = new DataTypeValue(DataType.Int, false, false); if (_currentPass == 1) { Instruction instruction = CreateInstruction(OpCodeByType(factorDataType, OpCodeType.PUSH), _currentScope, _currentToken); if (symbol.ConstToken.Ident.Length > 2 && symbol.ConstToken.Ident.Substring(0, 2) == "0x") new Operand(instruction, Convert.ToInt32(symbol.ConstToken.Ident, 16)); else new Operand(instruction, int.Parse(symbol.ConstToken.Ident)); } break; case DataType.Long: factorDataType = new DataTypeValue(DataType.Long, false, false); if (_currentPass == 1) { Instruction instruction = CreateInstruction(OpCodeByType(factorDataType, OpCodeType.PUSH), _currentScope, _currentToken); new Operand(instruction, Convert.ToInt64(symbol.ConstToken.Ident)); } break; case DataType.Short: factorDataType = new DataTypeValue(DataType.Short, false, false); if (_currentPass == 1) { Instruction instruction = CreateInstruction(OpCodeByType(factorDataType, OpCodeType.PUSH), _currentScope, _currentToken); new Operand(instruction, short.Parse(symbol.ConstToken.Ident)); } break; case DataType.String: factorDataType = new DataTypeValue(DataType.String, false, false); stringSymbol = _globalScope.FindSymbol(symbol.ConstToken.Ident, SymbolType.String) as StringSymbol; if (stringSymbol == null) stringSymbol = new StringSymbol(_globalScope, symbol.ConstToken.Ident); if (_currentPass == 1) { Instruction instruction = CreateInstruction(OpCodeByType(factorDataType, OpCodeType.PUSH), _currentScope, _currentToken); new Operand(instruction, stringSymbol); } break; } } else { // Check if we are parsing an array. if (LookAheadToken().ID == TokenID.CharOpenBracket) { // As the array is indexed we can remove array from the factor data type. factorDataType.IsArray = false; // Read in open bracket. NextToken(); // Make sure it really is an array. if (symbol.IsArray == false) Error(ErrorCode.InvalidArrayIndex, "Non-array variables can not be indexed.", false, 0); // Check that the next token is not a closing brace otherwise // its an invalid index. if (LookAheadToken().ID == TokenID.CharCloseBracket) Error(ErrorCode.InvalidArrayIndex, "Array's must be indexed.", false, 0); // Parse the index expression. DataTypeValue indexDataType = ParseExpression(); ExpectToken(TokenID.CharCloseBracket); // Check the index data type can be cast to an integer index. if (CanImplicitlyCast(new DataTypeValue(DataType.Int, false, false), indexDataType) == true) { // Emit the value retrieval's byte code. if (_currentPass == 1) { // Pop the index value into register 1. Instruction instruction = CreateInstruction(OpCodeByType(indexDataType, OpCodeType.POP), _currentScope, _currentToken); new Operand(instruction, Register.Reserved1); // Cast index value to integer if (indexDataType.DataType != DataType.Int) { instruction = CreateInstruction(OpCode.CAST_INT, _currentScope, _currentToken); new Operand(instruction, Register.Reserved1); } // Move the array's memory slot into reserved2 register. instruction = CreateInstruction(OpCode.MOV_MEMORY_INDEX, _currentScope, _currentToken); new Operand(instruction, Register.Reserved2); if (symbol.VariableType == VariableType.Constant || symbol.VariableType == VariableType.Global) Operand.DirectMemoryOperand(instruction, symbol.MemoryIndex); else Operand.DirectStackOperand(instruction, symbol.StackIndex); // Push the array's data onto the stack indexed // by the register we just poped the index into. instruction = CreateInstruction(OpCodeByType(factorDataType, OpCodeType.PUSH), _currentScope, _currentToken); Operand.IndirectMemoryIndexedOperand(instruction, Register.Reserved2, Register.Reserved1); } #region ++ / -- Parsing // Check if there is a increment (++) or decrement (--) operator following this value.. if (LookAheadToken().ID == TokenID.OpIncrement) { // Read in increment token. NextToken(); // Check the data types are valid with this operator. if (OperatorDataTypeValid(symbol.DataType, _currentToken.ID) == false) Error(ErrorCode.InvalidDataType, "Operator \"" + _currentToken.Ident + "\" can't be applied to data type \"" + symbol.DataType.ToString() + "\"", false, 0); // Make sure the symbol is not constant. if (symbol.IsConstant == true) Error(ErrorCode.IllegalAssignment, "Can't modify constant value.", false, 0); // Increment the variable. if (_currentPass == 1) { // Take the assumtion that the values are still in the register from the previous code. Instruction instruction = CreateInstruction(OpCodeByType(symbol.DataType, OpCodeType.INC), _currentScope, _currentToken); Operand.IndirectMemoryIndexedOperand(instruction, Register.Reserved2, Register.Reserved1); } } else if (LookAheadToken().ID == TokenID.OpDecrement) { // Read in increment token. NextToken(); // Check the data types are valid with this operator. if (OperatorDataTypeValid(symbol.DataType, _currentToken.ID) == false) Error(ErrorCode.InvalidDataType, "Operator \"" + _currentToken.Ident + "\" can't be applied to data type \"" + symbol.DataType.ToString() + "\"", false, 0); // Make sure the symbol is not constant. if (symbol.IsConstant == true) Error(ErrorCode.IllegalAssignment, "Can't modify constant value.", false, 0); // Decrement the variable. if (_currentPass == 1) { // Take the assumtion that the values are still in the register from the previous code. Instruction instruction = CreateInstruction(OpCodeByType(symbol.DataType, OpCodeType.DEC), _currentScope, _currentToken); Operand.IndirectMemoryIndexedOperand(instruction, Register.Reserved2, Register.Reserved1); } } #endregion } else Error(ErrorCode.InvalidCast, "Can't implicitly cast between \"Int\" and \"" + indexDataType.ToString() + "\"", false, 0); } // Its not an array so parse it as a reqular variable. else { // Push the variable's value onto stack. if (_currentPass == 1) { Instruction instruction = CreateInstruction(OpCodeByType(symbol.DataType, OpCodeType.PUSH), _currentScope, _currentToken); if (symbol.VariableType == VariableType.Global || symbol.VariableType == VariableType.Constant) Operand.DirectMemoryOperand(instruction, symbol.MemoryIndex); else Operand.DirectStackOperand(instruction, symbol.StackIndex); } #region ++ / -- Parsing // Check if there is a increment (++) or decrement (--) operator following this value.. if (LookAheadToken().ID == TokenID.OpIncrement) { // Read in increment token. NextToken(); // Check the data types are valid with this operator. if (OperatorDataTypeValid(symbol.DataType, _currentToken.ID) == false) Error(ErrorCode.InvalidDataType, "Operator \"" + _currentToken.Ident + "\" can't be applied to data type \"" + symbol.DataType.ToString() + "\"", false, 0); // Make sure the symbol is not constant. if (symbol.IsConstant == true) Error(ErrorCode.IllegalAssignment, "Can't modify constant value.", false, 0); // Increment the variable. if (_currentPass == 1) { Instruction instruction = CreateInstruction(OpCodeByType(symbol.DataType, OpCodeType.INC), _currentScope, _currentToken); if (symbol.VariableType == VariableType.Global || symbol.VariableType == VariableType.Constant) Operand.DirectMemoryOperand(instruction, symbol.MemoryIndex); else Operand.DirectStackOperand(instruction, symbol.StackIndex); } } else if (LookAheadToken().ID == TokenID.OpDecrement) { // Read in increment token. NextToken(); // Check the data types are valid with this operator. if (OperatorDataTypeValid(symbol.DataType, _currentToken.ID) == false) Error(ErrorCode.InvalidDataType, "Operator \"" + _currentToken.Ident + "\" can't be applied to data type \"" + symbol.DataType.ToString() + "\"", false, 0); // Make sure the symbol is not constant. if (symbol.IsConstant == true) Error(ErrorCode.IllegalAssignment, "Can't modify constant value.", false, 0); // Decrement the variable. if (_currentPass == 1) { Instruction instruction = CreateInstruction(OpCodeByType(symbol.DataType, OpCodeType.DEC), _currentScope, _currentToken); if (symbol.VariableType == VariableType.Global || symbol.VariableType == VariableType.Constant) Operand.DirectMemoryOperand(instruction, symbol.MemoryIndex); else Operand.DirectStackOperand(instruction, symbol.StackIndex); } } #endregion } } } // It look for opening parenthesis which would make it a function call. else if (EndOfTokenStream() == false && LookAheadToken().ID == TokenID.CharOpenParenthesis) { #region Function calling _tokenIndex = tokenIndex; _currentToken = currentToken; // Parse the function call and remember the return type. DataTypeValue functionReturnType = ParseFunctionCall(); // Push the return value of the function onto the stack. if (_currentPass == 1) { Instruction instruction = CreateInstruction(OpCodeByType(functionReturnType, OpCodeType.PUSH), _currentScope, _currentToken); new Operand(instruction, Register.Return); } // Set the factors data type. factorDataType = functionReturnType; #endregion } // WTF! Is this? else { Error(ErrorCode.UndeclaredVariable, "Encountered attempt to access an undeclared symbol \"" + symbolIdent + "\".", false, 1); } break; #endregion #region Sub expression case TokenID.CharOpenParenthesis: factorDataType = ParseExpression(); ExpectToken(TokenID.CharCloseParenthesis); break; #endregion default: Error(ErrorCode.InvalidFactor, "Invalid factor.", false, 0); break; } // Create instructions for unary operator if in second pass. if (unaryOpPending == true && _currentPass == 1) { // Check the data types are valid with this operator. if (OperatorDataTypeValid(factorDataType, unaryOpToken.ID) == false) Error(ErrorCode.InvalidDataType, "Operator \"" + unaryOpToken.Ident + "\" can't be apply to data type \"" + factorDataType.ToString() + "\""); // Pop the value of this value out of the stack // and preform action on it. Instruction instruction = CreateInstruction(OpCodeByType(factorDataType, OpCodeType.POP), _currentScope, _currentToken); new Operand(instruction, Register.Arithmetic1); // Create instruction based on operation code. switch (unaryOpToken.ID) { case TokenID.OpAdd: instruction = CreateInstruction(OpCodeByType(factorDataType, OpCodeType.ABS), _currentScope, _currentToken); break; case TokenID.OpSub: instruction = CreateInstruction(OpCodeByType(factorDataType, OpCodeType.NEG), _currentScope, _currentToken); break; case TokenID.OpLogicalNot: instruction = CreateInstruction(OpCode.LOGICAL_NOT, _currentScope, _currentToken); break; case TokenID.OpBitwiseNot: instruction = CreateInstruction(OpCode.BIT_NOT_INT, _currentScope, _currentToken); break; case TokenID.OpIncrement: instruction = CreateInstruction(OpCodeByType(factorDataType, OpCodeType.INC), _currentScope, _currentToken); break; case TokenID.OpDecrement: instruction = CreateInstruction(OpCodeByType(factorDataType, OpCodeType.DEC), _currentScope, _currentToken); break; } new Operand(instruction, Register.Arithmetic1); // Push the value back onto the stack (as long as its not the logical not, which does that itself). if (unaryOpToken.ID != TokenID.OpLogicalNot) { instruction = CreateInstruction(OpCodeByType(factorDataType, OpCodeType.PUSH), _currentScope, _currentToken); new Operand(instruction, Register.Arithmetic1); } } // If a cast is pending then convert the result to the given type. if (castPending == true && factorDataType != castType) { if (_currentPass == 1) { // Pop the result out of the stack. Instruction instruction = CreateInstruction(OpCodeByType(factorDataType, OpCodeType.POP), _currentScope, _currentToken); new Operand(instruction, Register.Arithmetic1); // Cast it to the given type. instruction = CreateInstruction(OpCodeByType(castType, OpCodeType.CAST), _currentScope, _currentToken); new Operand(instruction, Register.Arithmetic1); // Push the value back onto the stack. instruction = CreateInstruction(OpCodeByType(castType, OpCodeType.PUSH), _currentScope, _currentToken); new Operand(instruction, Register.Arithmetic1); } factorDataType = castType; } return factorDataType; }
/// <summary> /// Parses a member assignment. /// </summary> private void ParseMemberAssignment(string identifier, DataTypeValue returnDataType) { // Check we are in a valid scope. if (_currentScope.Type != SymbolType.Function || _currentScope == _globalScope) Error(ErrorCode.InvalidScope, "Assignments are only valid inside a function's or event's scope.", false, 0); #region Variable retrieving // If we are in pass 2 try and retrieve the variable. VariableSymbol variableSymbol = null; DataTypeValue dataType = returnDataType; if (_currentPass == 0) { variableSymbol = _memberScope.FindVariableSymbol(identifier, returnDataType) as VariableSymbol; if (variableSymbol == null) { variableSymbol = new VariableSymbol(_memberScope); variableSymbol.Identifier = identifier; variableSymbol.DataType = returnDataType; variableSymbol.VariableType = VariableType.Member; if (variableSymbol.VariableType == VariableType.Constant) Error(ErrorCode.IllegalAssignment, "Encountered attempt to assign to a constant variable \"" + identifier + "\"."); } } else if (_currentPass == 1) variableSymbol = _memberScope.FindVariableSymbol(identifier, returnDataType) as VariableSymbol; #endregion #region Array index parsing // Check if we are assigning to an array. bool isArray = false; DataTypeValue indexDataType = null; if (LookAheadToken().ID == TokenID.CharOpenBracket) { // Make sure variable is an array if we are in pass 2. if (_currentPass == 1 && variableSymbol.DataType.IsArray == false) Error(ErrorCode.InvalidArrayIndex, "Non-array variables can not be indexed."); // As its an array and its index we can remove the array declaration from // the data type. dataType.IsArray = false; // Read in opening bracket and check expression is valid NextToken(); if (LookAheadToken().ID == TokenID.CharCloseBracket) Error(ErrorCode.InvalidArrayIndex, "Array's must be indexed.", false, 0); // Read in expression and closing bracket. indexDataType = ParseExpression(); ExpectToken(TokenID.CharCloseBracket); isArray = true; } #endregion #region Operator parsing // Check if the operator is a valid assignment operator. NextToken(); if (_currentToken.ID != TokenID.OpAssign && _currentToken.ID != TokenID.OpAssignAdd && _currentToken.ID != TokenID.OpAssignBitwiseAnd && _currentToken.ID != TokenID.OpAssignBitwiseNot && _currentToken.ID != TokenID.OpAssignBitwiseOr && _currentToken.ID != TokenID.OpAssignBitwiseSHL && _currentToken.ID != TokenID.OpAssignBitwiseSHR && _currentToken.ID != TokenID.OpAssignBitwiseXOr && _currentToken.ID != TokenID.OpAssignDivide && _currentToken.ID != TokenID.OpAssignModulus && _currentToken.ID != TokenID.OpAssignMultiply && _currentToken.ID != TokenID.OpAssignSub && _currentToken.ID != TokenID.OpIncrement && _currentToken.ID != TokenID.OpDecrement) Error(ErrorCode.IllegalAssignmentOperator, "Encountered attempt to use an illegal assignment operator."); Token operatorToken = _currentToken; Instruction instruction = null; DataTypeValue expressionType = new DataTypeValue(DataType.Invalid, false, false); #endregion // Check the data types are valid with this operator. if (_currentPass == 1 && OperatorDataTypeValid(variableSymbol.DataType, operatorToken.ID) == false) Error(ErrorCode.InvalidDataType, "Operator \"" + operatorToken.Ident + "\" can't be applied to data type \"" + variableSymbol.DataType.ToString() + "\""); #region Value byte code emission and parsing // Read in expression if it is not a increment (++) or decrement (--) operator. if (operatorToken.ID != TokenID.OpIncrement && operatorToken.ID != TokenID.OpDecrement) { // Parse the expression. expressionType = ParseExpression(); // Pop the expression into reserved register 3. if (_currentPass == 1) { // Pop the value of into reserved register 3. instruction = CreateInstruction(OpCodeByType(expressionType, OpCodeType.POP), _currentScope, _currentToken); new Operand(instruction, Register.Reserved3); // Cast the value to a valid type. if (dataType != expressionType) { instruction = CreateInstruction(OpCodeByType(dataType, OpCodeType.CAST), _currentScope, _currentToken); new Operand(instruction, Register.Reserved3); } } // If we are an array check that we have been indexed, unless we // are assigning null to it. if (_currentPass == 1 && variableSymbol.DataType.IsArray == true && isArray == false && expressionType.DataType != DataType.Null && expressionType.IsArray != true) Error(ErrorCode.InvalidArrayIndex, "Arrays must be indexed."); } else { expressionType = new DataTypeValue(DataType.Int, false, false); } #endregion // Check we can cast to the expression result to the variables data type. if (_currentPass == 1 && CanImplicitlyCast(dataType, expressionType) == false) Error(ErrorCode.InvalidCast, "Can't implicitly cast between \"" + dataType.ToString() + "\" and \"" + expressionType.ToString() + "\""); #region Array index byte code emission // If this is an array pop the index into reserved register 2. if (isArray == true && _currentPass == 1) { // Pop the index into reserved register 2. instruction = CreateInstruction(OpCodeByType(indexDataType, OpCodeType.POP), _currentScope, _currentToken); new Operand(instruction, Register.Reserved2); } #endregion #region Assignment byte code emission // Create assignment byte code if we are in pass 2. if (_currentPass == 1) { // Pop the object into the member register. instruction = CreateInstruction(OpCode.POP_OBJECT, _currentScope, _currentToken); new Operand(instruction, Register.Arithmetic1); // Call the GET_MEMBER opCode. if (returnDataType.IsArray) { instruction = CreateInstruction(OpCode.GET_MEMBER, _currentScope, _currentToken); new Operand(instruction, Register.Arithmetic1); new Operand(instruction, variableSymbol); } else { instruction = CreateInstruction(OpCode.GET_MEMBER_INDEXED, _currentScope, _currentToken); new Operand(instruction, Register.Arithmetic2); new Operand(instruction, variableSymbol); new Operand(instruction, Register.Arithmetic2); } switch (operatorToken.ID) { case TokenID.OpAssign: instruction = CreateInstruction(OpCodeByType(dataType, OpCodeType.MOV), _currentScope, _currentToken); break; case TokenID.OpAssignAdd: instruction = CreateInstruction(OpCodeByType(dataType, OpCodeType.ADD), _currentScope, _currentToken); break; case TokenID.OpAssignBitwiseAnd: instruction = CreateInstruction(OpCode.BIT_AND_INT, _currentScope, _currentToken); break; case TokenID.OpAssignBitwiseNot: instruction = CreateInstruction(OpCode.BIT_NOT_INT, _currentScope, _currentToken); break; case TokenID.OpAssignBitwiseOr: instruction = CreateInstruction(OpCode.BIT_OR_INT, _currentScope, _currentToken); break; case TokenID.OpAssignBitwiseSHL: instruction = CreateInstruction(OpCode.BIT_SHL_INT, _currentScope, _currentToken); break; case TokenID.OpAssignBitwiseSHR: instruction = CreateInstruction(OpCode.BIT_SHR_INT, _currentScope, _currentToken); break; case TokenID.OpAssignBitwiseXOr: instruction = CreateInstruction(OpCode.BIT_XOR_INT, _currentScope, _currentToken); break; case TokenID.OpAssignDivide: instruction = CreateInstruction(OpCodeByType(dataType, OpCodeType.DIV), _currentScope, _currentToken); break; case TokenID.OpAssignModulus: instruction = CreateInstruction(OpCode.MOD_INT, _currentScope, _currentToken); break; case TokenID.OpAssignMultiply: instruction = CreateInstruction(OpCodeByType(dataType, OpCodeType.MUL), _currentScope, _currentToken); break; case TokenID.OpAssignSub: instruction = CreateInstruction(OpCodeByType(dataType, OpCodeType.SUB), _currentScope, _currentToken); break; case TokenID.OpIncrement: instruction = CreateInstruction(OpCodeByType(dataType, OpCodeType.INC), _currentScope, _currentToken); break; case TokenID.OpDecrement: instruction = CreateInstruction(OpCodeByType(dataType, OpCodeType.DEC), _currentScope, _currentToken); break; } // Generate destination operands based on if variable is array. new Operand(instruction, Register.Return); // Generate the source operand if we are not dealing with the ++ and -- operators. if (operatorToken.ID != TokenID.OpIncrement && operatorToken.ID != TokenID.OpDecrement) new Operand(instruction, Register.Reserved3); // Call the SET_MEMBER opCode. if (returnDataType.IsArray) { instruction = CreateInstruction(OpCode.SET_MEMBER, _currentScope, _currentToken); new Operand(instruction, Register.Arithmetic1); new Operand(instruction, variableSymbol); new Operand(instruction, Register.Return); } else { instruction = CreateInstruction(OpCode.GET_MEMBER_INDEXED, _currentScope, _currentToken); new Operand(instruction, Register.Arithmetic2); new Operand(instruction, variableSymbol); new Operand(instruction, Register.Return); new Operand(instruction, Register.Arithmetic2); } } #endregion }
/// <summary> /// Creates a new instance of this class with the given data. /// </summary> /// <param name="name">Identifier used inside a script to call this function</param> /// <param name="returnType">Data type that this function returns to the script</param> /// <param name="parameterTypes">Parameters that this function accepts</param> public NativeFunctionInfo(string name, string returnType, string parameterTypes) { _name = name; _returnType = DataTypeValue.FromMnemonic(returnType); if (parameterTypes != "") { string[] parameterTypesSplit = parameterTypes.Split(new Char[] { ',' }); _parameterTypes = new DataTypeValue[parameterTypesSplit.Length]; for (int i = 0; i < parameterTypesSplit.Length; i++) { if (parameterTypesSplit[i].EndsWith("[]")) { _parameterTypes[i] = DataTypeValue.FromMnemonic(parameterTypesSplit[i].Substring(0, parameterTypesSplit[i].Length - 2)); _parameterTypes[i].IsArray = true; } else _parameterTypes[i] = DataTypeValue.FromMnemonic(parameterTypesSplit[i]); } } else _parameterTypes = new DataTypeValue[0]; }
/// <summary> /// Invokes a export function from a given script. /// </summary> /// <param name="symbol">Symbol of function to call.</param> /// <param name="toThread">Thread to call function.</param> /// <param name="fromThread">Thread to call function from.</param> /// <returns>Memory index of new array.</returns> public void InvokeExportFunction(FunctionSymbol symbol, ScriptThread toThread, ScriptThread fromThread) { // Can't invoke functions if debugging :S. if (_debugger != null && _debugger.AllowInvokedFunctions == false && toThread == this) return; // Find the functions parameter types. DataTypeValue[] parameterTypes = new DataTypeValue[symbol.ParameterCount]; for (int i = 0; i < symbol.ParameterCount; i++) parameterTypes[i] = ((VariableSymbol)symbol.Symbols[i]).DataType; // Push any parameters we have been given. for (int i = 0; i < parameterTypes.Length; i++) { RuntimeValue parameter = fromThread._runtimeStack[(fromThread._runtimeStack.TopIndex - parameterTypes.Length) + i]; parameter.DataType = parameterTypes[i]; if (parameterTypes[i].IsArray == true) toThread.PassParameterArray(CopyValueArrayFromThread(parameter, toThread, fromThread)); else toThread.PassParameter(CopyValueFromThread(parameter, toThread, fromThread)); } // Call the function. toThread.InvokeFunction(symbol.Identifier, true, true); // Copy the return register. fromThread._registers[(int)Register.Return].DataType = symbol.ReturnType; if (symbol.ReturnType.IsArray == true) SetMemoryIndexValue(fromThread._registers[(int)Register.Return], CopyValueArrayFromThread(toThread._registers[(int)Register.Return], fromThread, toThread)); else fromThread._registers[(int)Register.Return] = CopyValueFromThread(toThread._registers[(int)Register.Return], fromThread, toThread); }
/// <summary> /// Allocates a chunk of data on the heap. /// </summary> /// <param name="size">Size of data to allocate.</param> /// <param name="type">Data type the allocated memory should store.</param> /// <param name="value">Data type this heap is stored as (differs from the type paramter 'type' as it keeps track of arrays and references to)</param> /// <returns>Starting index of data on the heap.</returns> public int AllocateHeap(int size, RuntimeValueType type, DataTypeValue value) { // Check if there is any space on the stack. // Ignore the constant and global memory index as they are persistant. int allocatableIndex = 0, allocatableSize = 0; // First element is reserved to capture null-to-zero-cast-to-memory-index (XD) errors. for (int i = 1; i < _memoryHeap.Length; i++) { if (_memoryHeap[i] != null) { allocatableSize = 0; allocatableIndex = i + 1; if (_memoryHeap[i].ValueType == RuntimeValueType.MemoryBoundry) { i += _memoryHeap[i].IntegerLiteral; allocatableIndex = i + 1; continue; } i++; continue; } else allocatableSize++; if (allocatableSize >= (size + 1)) break; } if (allocatableSize < (size + 1))// || (allocatableIndex + allocatableSize) >= _memoryHeap.Length) { CollectGarbage(); // Desperatly try and free some space :S. if (allocatableSize + _lastCollectionMemoryCount < (size + 1)) { // If all else fails, resize. Its better than crashing. Array.Resize(ref _memoryHeap, _memoryHeap.Length * 2 < _memoryHeap.Length + size ? _memoryHeap.Length + size : _memoryHeap.Length * 2); // Double the size. DebugLogger.WriteLog("Memory heap of script '"+_url.ToString()+"' was resized due to overflow."); return AllocateHeap(size, type, value); } else return AllocateHeap(size, type, value); } // Actually allocate the data. for (int i = allocatableIndex; i <= (allocatableIndex + allocatableSize); i++) _memoryHeap[i] = new RuntimeValue(type); // Set the first data block as a memory boundry so the GC can look after it. _memoryHeap[allocatableIndex].ValueType = RuntimeValueType.MemoryBoundry; _memoryHeap[allocatableIndex].IntegerLiteral = size; _memoryHeap[allocatableIndex].ReferenceCount = 0; _memoryHeap[allocatableIndex].DataType = value; return allocatableIndex + 1; }
/// <summary> /// Checks if a given parameter data type can be applied to this parameter. /// </summary> /// <param name="index">Index of parameter to check.</param> /// <returns>Boolean describing if the data type is valid.</returns> public bool CheckParameterTypeValid(int index, DataTypeValue value) { return (_parameterTypes[index] == value || value.DataType == DataType.Null); }
/// <summary> /// Copys the given processes data to this process. /// </summary> /// <param name="vm">Virtual machine this oricess us associated with.</param> /// <param name="process">Process to recieve data from.</param> public ScriptProcess(VirtualMachine vm, ScriptProcess process) { HighPreformanceTimer timer = new HighPreformanceTimer(); // Store virtual machine for layer use. _virtualMachine = vm; // Load the given script into this process. process.CopyTo(this); // Create a default thread that should run this process. ScriptThread newThread = new ScriptThread(this); // Attach console commands / exported command to this thread. // TODO: What if they already exist? foreach (Symbol symbol in _symbolList) if (symbol != null && symbol.Type == SymbolType.Function) { if (((FunctionSymbol)symbol).IsConsole == true) new ScriptConsoleCommand((FunctionSymbol)symbol, newThread); else if (((FunctionSymbol)symbol).IsExport == true) new ScriptExportFunction((FunctionSymbol)symbol, newThread); else if (((FunctionSymbol)symbol).IsImport == true) { // Find the functions parameter types. DataTypeValue[] parameterTypes = new DataTypeValue[((FunctionSymbol)symbol).ParameterCount]; for (int i = 0; i < ((FunctionSymbol)symbol).ParameterCount; i++) parameterTypes[(((FunctionSymbol)symbol).ParameterCount - 1) - i] = ((VariableSymbol)symbol.Symbols[i]).DataType; ((FunctionSymbol)symbol).NativeFunction = _virtualMachine.FindNativeFunction(symbol.Identifier, parameterTypes); } } }
/// <summary> /// Initializes a new instance of this class. /// </summary> /// <param name="identifier">Identifier used to call this function from a script.</param> /// <param name="nativeFunctionDelegate">Delegate of function you wish to be called when function is called from the script.</param> /// <param name="returnType">Data type that this function returns.</param> /// <param name="parameterTypes">Array of parameter data types this function uses.</param> public NativeFunction(string identifier, FunctionDelegate nativeFunctionDelegate, DataTypeValue returnType, DataTypeValue[] parameterTypes) { _identifier = identifier; _delegate = nativeFunctionDelegate; _returnType = returnType; _parameterTypes = parameterTypes; }
/// <summary> /// Nullifys all values in this instance. /// </summary> public void Clear() { _values.BooleanLiteral = false; _values.ByteLiteral = 0; _values.DoubleLiteral = 0; _values.FloatLiteral = 0; _values.IntegerLiteral = 0; _values.LongLiteral = 0; _memoryIndex = -1; _values.ShortLiteral = 0; _stringLiteralValue = ""; _objectIndex = -1; _referenceCount = 0; _valueType = RuntimeValueType.Invalid; _dataType = null; }
/// <summary> /// Parses an variable assignment. A variable assignment basically just /// places a given value into a previously declared variables memory space. /// Syntax: /// Identifier AssignmentOperator Expression /// </summary> private void ParseAssignment() { // Check we are in a valid scope. if (_currentScope.Type != SymbolType.Function || _currentScope == _globalScope) Error(ErrorCode.InvalidScope, "Assignments are only valid inside a function's or event's scope.", false, 0); // Retrieve the variables identifier and save it for later on. Symbol resolvedScope = ResolveMemberScope(); string variableIdentifier = _currentToken.Ident; #region Variable retrieving // If we are in pass 2 try and retrieve the variable. VariableSymbol variableSymbol = null; DataTypeValue dataType = new DataTypeValue(DataType.Invalid, false, false); if (_currentPass == 1) { variableSymbol = resolvedScope.FindSymbol(variableIdentifier, SymbolType.Variable) as VariableSymbol; if (variableSymbol == null) Error(ErrorCode.UndeclaredVariable, "Encountered attempt to assign a value to an undeclared variable \"" + variableIdentifier + "\"."); if (variableSymbol.VariableType == VariableType.Constant) Error(ErrorCode.IllegalAssignment, "Encountered attempt to assign to a constant variable \"" + variableIdentifier + "\"."); variableSymbol.IsUsed = true; dataType.DataType = variableSymbol.DataType.DataType; dataType.IsArray = variableSymbol.DataType.IsArray; dataType.IsReference = variableSymbol.DataType.IsReference; } #endregion #region Array index parsing // Check if we are assigning to an array. bool isArray = false; DataTypeValue indexDataType = null; if (LookAheadToken().ID == TokenID.CharOpenBracket) { // Make sure variable is an array if we are in pass 2. if (_currentPass == 1 && variableSymbol.IsArray == false) Error(ErrorCode.InvalidArrayIndex, "Non-array variables can not be indexed."); // As its an array and its index we can remove the array declaration from // the data type. dataType.IsArray = false; // Read in opening bracket and check expression is valid NextToken(); if (LookAheadToken().ID == TokenID.CharCloseBracket) Error(ErrorCode.InvalidArrayIndex, "Array's must be indexed.", false, 0); // Read in expression and closing bracket. indexDataType = ParseExpression(); ExpectToken(TokenID.CharCloseBracket); // Check the expression is of a valid data type. if (CanImplicitlyCast(new DataTypeValue(DataType.Int, false, false), indexDataType) == false) Error(ErrorCode.InvalidCast, "Can't implicitly cast between \"Int\" and \"" + indexDataType.ToString() + "\""); isArray = true; } #endregion #region Operator parsing // Check if the operator is a valid assignment operator. NextToken(); if (_currentToken.ID != TokenID.OpAssign && _currentToken.ID != TokenID.OpAssignAdd && _currentToken.ID != TokenID.OpAssignBitwiseAnd && _currentToken.ID != TokenID.OpAssignBitwiseNot && _currentToken.ID != TokenID.OpAssignBitwiseOr && _currentToken.ID != TokenID.OpAssignBitwiseSHL && _currentToken.ID != TokenID.OpAssignBitwiseSHR && _currentToken.ID != TokenID.OpAssignBitwiseXOr && _currentToken.ID != TokenID.OpAssignDivide && _currentToken.ID != TokenID.OpAssignModulus && _currentToken.ID != TokenID.OpAssignMultiply && _currentToken.ID != TokenID.OpAssignSub && _currentToken.ID != TokenID.OpIncrement && _currentToken.ID != TokenID.OpDecrement) Error(ErrorCode.IllegalAssignmentOperator, "Encountered attempt to use an illegal assignment operator."); Token operatorToken = _currentToken; Instruction instruction = null; DataTypeValue expressionType = new DataTypeValue(DataType.Invalid, false, false); #endregion // Check the data types are valid with this operator. if (_currentPass == 1 && OperatorDataTypeValid(variableSymbol.DataType, operatorToken.ID) == false) Error(ErrorCode.InvalidDataType, "Operator \"" + operatorToken.Ident + "\" can't be applied to data type \"" + variableSymbol.DataType.ToString() + "\""); #region Value byte code emission and parsing // Read in expression if it is not a increment (++) or decrement (--) operator. if (operatorToken.ID != TokenID.OpIncrement && operatorToken.ID != TokenID.OpDecrement) { // Parse the expression. expressionType = ParseExpression(); // Pop the expression into reserved register 3. if (_currentPass == 1) { // If we are an array deallocate our previous memory. /* if (variableSymbol.DataType.IsArray == true) { // Move the memory index into arith 2. instruction = CreateInstruction(OpCode.MOV_MEMORY_INDEX, _currentScope, _currentToken); new Operand(instruction, Register.Arithmetic2); if (variableSymbol.VariableType == VariableType.Constant || variableSymbol.VariableType == VariableType.Global) Operand.DirectMemoryOperand(instruction, variableSymbol.MemoryIndex); else Operand.DirectStackOperand(instruction, variableSymbol.StackIndex); // Allocate enough space on the heap for the array. instruction = CreateInstruction(OpCode.DEALLOCATE_HEAP, _currentScope, _currentToken); new Operand(instruction, Register.Arithmetic2); } */ // Pop the value of into reserved register 3. instruction = CreateInstruction(OpCodeByType(expressionType, OpCodeType.POP), _currentScope, _currentToken); new Operand(instruction, Register.Reserved3); // Cast the value to a valid type. if (dataType != expressionType) { instruction = CreateInstruction(OpCodeByType(dataType, OpCodeType.CAST), _currentScope, _currentToken); new Operand(instruction, Register.Reserved3); } } // If we are an array check that we have been indexed, unless we // are assigning null to it. if (_currentPass == 1 && variableSymbol.IsArray == true && isArray == false && expressionType.DataType != DataType.Null && expressionType.IsArray != true) Error(ErrorCode.InvalidArrayIndex, "Arrays must be indexed."); } else { expressionType = new DataTypeValue(DataType.Int, false, false); } #endregion // Check we can cast to the expression result to the variables data type. if (_currentPass == 1 && CanImplicitlyCast(dataType, expressionType) == false) Error(ErrorCode.InvalidCast, "Can't implicitly cast between \"" + variableSymbol.DataType.ToString() + "\" and \"" + expressionType.ToString() + "\""); #region Array index byte code emission // If this is an array pop the index into reserved register 2. if (isArray == true && _currentPass == 1) { // Pop the index into reserved regu=ister 2. instruction = CreateInstruction(OpCodeByType(indexDataType, OpCodeType.POP), _currentScope, _currentToken); new Operand(instruction, Register.Reserved2); // Cast it to an integer index if its of another type. if (indexDataType != new DataTypeValue(DataType.Int, false, false)) { instruction = CreateInstruction(OpCode.CAST_INT, _currentScope, _currentToken); new Operand(instruction, Register.Reserved2); } // Move the array memory slot into reserved register 1. instruction = CreateInstruction(OpCode.MOV_MEMORY_INDEX, _currentScope, _currentToken); new Operand(instruction, Register.Reserved1); if (variableSymbol.VariableType == VariableType.Constant || variableSymbol.VariableType == VariableType.Global) Operand.DirectMemoryOperand(instruction, variableSymbol.MemoryIndex); else Operand.DirectStackOperand(instruction, variableSymbol.StackIndex); } #endregion #region Assignment byte code emission // Create assignment byte code if we are in pass 2. if (_currentPass == 1) { switch (operatorToken.ID) { case TokenID.OpAssign: instruction = CreateInstruction(OpCodeByType(dataType, OpCodeType.MOV), _currentScope, _currentToken); break; case TokenID.OpAssignAdd: instruction = CreateInstruction(OpCodeByType(dataType, OpCodeType.ADD), _currentScope, _currentToken); break; case TokenID.OpAssignBitwiseAnd: instruction = CreateInstruction(OpCode.BIT_AND_INT, _currentScope, _currentToken); break; case TokenID.OpAssignBitwiseNot: instruction = CreateInstruction(OpCode.BIT_NOT_INT, _currentScope, _currentToken); break; case TokenID.OpAssignBitwiseOr: instruction = CreateInstruction(OpCode.BIT_OR_INT, _currentScope, _currentToken); break; case TokenID.OpAssignBitwiseSHL: instruction = CreateInstruction(OpCode.BIT_SHL_INT, _currentScope, _currentToken); break; case TokenID.OpAssignBitwiseSHR: instruction = CreateInstruction(OpCode.BIT_SHR_INT, _currentScope, _currentToken); break; case TokenID.OpAssignBitwiseXOr: instruction = CreateInstruction(OpCode.BIT_XOR_INT, _currentScope, _currentToken); break; case TokenID.OpAssignDivide: instruction = CreateInstruction(OpCodeByType(dataType, OpCodeType.DIV), _currentScope, _currentToken); break; case TokenID.OpAssignModulus: instruction = CreateInstruction(OpCode.MOD_INT, _currentScope, _currentToken); break; case TokenID.OpAssignMultiply: instruction = CreateInstruction(OpCodeByType(dataType, OpCodeType.MUL), _currentScope, _currentToken); break; case TokenID.OpAssignSub: instruction = CreateInstruction(OpCodeByType(dataType, OpCodeType.SUB), _currentScope, _currentToken); break; case TokenID.OpIncrement: instruction = CreateInstruction(OpCodeByType(dataType, OpCodeType.INC), _currentScope, _currentToken); break; case TokenID.OpDecrement: instruction = CreateInstruction(OpCodeByType(dataType, OpCodeType.DEC), _currentScope, _currentToken); break; } // Generate destination operands based on if variable is array. if (isArray == true) Operand.IndirectMemoryIndexedOperand(instruction, Register.Reserved1, Register.Reserved2); else { if (variableSymbol.VariableType == VariableType.Global || variableSymbol.VariableType == VariableType.Constant) Operand.DirectMemoryOperand(instruction, variableSymbol.MemoryIndex); else Operand.DirectStackOperand(instruction, variableSymbol.StackIndex); } // Generate the source operand if we are not dealing with the ++ and -- operators. if (operatorToken.ID != TokenID.OpIncrement && operatorToken.ID != TokenID.OpDecrement) new Operand(instruction, Register.Reserved3); } #endregion }
/// <summary> /// Runs this threads byte code. /// </summary> /// <param name="timeslice">Amount of time this thread is allowed to run for.</param> /// <returns>1 if the script has been forced to stop by a latency instruction, 2 if it has been forced to stop by a stack base marker or 3 if by an exit instruction.</returns> public int Run(int timeslice) { // Check that this thread is actually running. if (_isRunning == false) return 5; // Check the pause timer of this current thread. if (_isPaused == true) { if (_pauseTimer.DurationMillisecond <= _pauseLength) return 0; _isPaused = false; } // Check we are not at the end of this script, if we are then if (_instructionPointer >= _process.Instructions.Length) return 0; // If the debugger is running then we can't continue :S. if (_debugger != null && _debugger.RunScript == false) return 6; // Keep running until our timeslice runs out. int ticks = Environment.TickCount; // Don't use a high speed timer - To slow. //int secondsTimer = Environment.TickCount; //int instrCount = 0; //HighPreformanceTimer instructionTimer = new HighPreformanceTimer(); //float[] opCodeTimes = new float[Enum.GetNames(typeof(OpCode)).Length]; while (true) { #if !DEBUG try { #endif //instructionTimer.Restart(); // Checks if this script is still in its timeslice. if (timeslice != -1 && (Environment.TickCount - ticks) > timeslice && _priority != Priority.RealTime && _inAtom == false) return 0; // General exit stuff. else if (_process == null || _callStack.Count == 0 || _instructionPointer >= _process.Instructions.Length || _isPaused == true || _isRunning == false || _isWaiting == true) return 5; // Debugger else if (_debugger != null && _debugger.RunScript == false) return 6; // Retrieve's and execute's the next instruction in the list. int originalPointer = _instructionPointer; // Execute instruction and return result if neccessary. RuntimeValue op1, op2, stack1; RuntimeInstruction instruction = _process.Instructions[_instructionPointer]; //if (Environment.TickCount - secondsTimer > 10) //{ // secondsTimer = Environment.TickCount; //if (_process.Url == "media\\scripts\\LTTP 2.0.fs") // System.Console.WriteLine(_process.Url+":"+(instrCount * 100) + " instrs/ps"); // instrCount = 0; //} //else // instrCount++; // This was originally a seperate function but to speed it up a bit it has been inlined. #region Instruction Execution switch (instruction.OpCode) { case OpCode.NOP: break; #region POP case OpCode.POP_OBJECT: op1 = ResolveOperand(instruction, 0); op1.ValueType = RuntimeValueType.Object; stack1 = _runtimeStack.Pop(); SetObjectValue(op1, stack1.ObjectIndex); if (stack1.ObjectIndex != -1) SetObjectValue(stack1, -1); break; case OpCode.POP_BOOL: op1 = ResolveOperand(instruction, 0); op1.ValueType = RuntimeValueType.BooleanLiteral; op1.BooleanLiteral = _runtimeStack.Pop().BooleanLiteral; break; case OpCode.POP_BYTE: op1 = ResolveOperand(instruction, 0); op1.ValueType = RuntimeValueType.ByteLiteral; op1.ByteLiteral = _runtimeStack.Pop().ByteLiteral; break; case OpCode.POP_INT: op1 = ResolveOperand(instruction, 0); op1.ValueType = RuntimeValueType.IntegerLiteral; op1.IntegerLiteral = _runtimeStack.Pop().IntegerLiteral; break; case OpCode.POP_SHORT: op1 = ResolveOperand(instruction, 0); op1.ValueType = RuntimeValueType.ShortLiteral; op1.ShortLiteral = _runtimeStack.Pop().ShortLiteral; break; case OpCode.POP_FLOAT: op1 = ResolveOperand(instruction, 0); op1.ValueType = RuntimeValueType.FloatLiteral; op1.FloatLiteral = _runtimeStack.Pop().FloatLiteral; break; case OpCode.POP_STRING: op1 = ResolveOperand(instruction, 0); op1.ValueType = RuntimeValueType.StringLiteral; op1.StringLiteral = _runtimeStack.Pop().StringLiteral; break; case OpCode.POP_LONG: op1 = ResolveOperand(instruction, 0); op1.ValueType = RuntimeValueType.LongLiteral; op1.LongLiteral = _runtimeStack.Pop().LongLiteral; break; case OpCode.POP_DOUBLE: op1 = ResolveOperand(instruction, 0); op1.ValueType = RuntimeValueType.DoubleLiteral; op1.DoubleLiteral = _runtimeStack.Pop().DoubleLiteral; break; case OpCode.POP_MEMORY_INDEX: op1 = ResolveOperand(instruction, 0); op1.ValueType = RuntimeValueType.Invalid; op2 = _runtimeStack.Pop(); // op1.MemoryIndex = op2.MemoryIndex; op1.StackIndex = op2.StackIndex; SetMemoryIndexValue(op1, op2.MemoryIndex); if (op2.MemoryIndex != -1) SetMemoryIndexValue(op2, -1); break; case OpCode.POP_NULL: _runtimeStack.Pop(); // Pop value of stack, we are not actually // going to be using it so ignore it. if (ResolveOperand(instruction, 0).ObjectIndex != -1) SetObjectValue(ResolveOperand(instruction, 0), -1); else if (ResolveOperand(instruction, 0).MemoryIndex != -1) SetMemoryIndexValue(ResolveOperand(instruction, 0), -1); ResolveOperand(instruction, 0).Clear(); // Nullify value. break; case OpCode.POP_DESTROY: stack1 = _runtimeStack.Pop(); if (stack1.ObjectIndex != -1) SetObjectValue(stack1, -1); else if (stack1.MemoryIndex != -1) SetMemoryIndexValue(stack1, -1); _runtimeStack.Pop(); break; #endregion #region PUSH case OpCode.PUSH_OBJECT: SetObjectValue(_runtimeStack.PushEmpty(RuntimeValueType.Object), ResolveOperand(instruction, 0).ObjectIndex); break; case OpCode.PUSH_BOOL: _runtimeStack.PushEmpty(RuntimeValueType.BooleanLiteral).BooleanLiteral = ResolveOperand(instruction, 0).BooleanLiteral; break; case OpCode.PUSH_BYTE: _runtimeStack.PushEmpty(RuntimeValueType.ByteLiteral).ByteLiteral = ResolveOperand(instruction, 0).ByteLiteral; break; case OpCode.PUSH_INT: _runtimeStack.PushEmpty(RuntimeValueType.IntegerLiteral).IntegerLiteral = ResolveOperand(instruction, 0).IntegerLiteral; break; case OpCode.PUSH_SHORT: _runtimeStack.PushEmpty(RuntimeValueType.ShortLiteral).ShortLiteral = ResolveOperand(instruction, 0).ShortLiteral; break; case OpCode.PUSH_FLOAT: _runtimeStack.PushEmpty(RuntimeValueType.FloatLiteral).FloatLiteral = ResolveOperand(instruction, 0).FloatLiteral; break; case OpCode.PUSH_STRING: _runtimeStack.PushEmpty(RuntimeValueType.StringLiteral).StringLiteral = ResolveOperand(instruction, 0).StringLiteral; break; case OpCode.PUSH_LONG: _runtimeStack.PushEmpty(RuntimeValueType.LongLiteral).LongLiteral = ResolveOperand(instruction, 0).LongLiteral; break; case OpCode.PUSH_DOUBLE: _runtimeStack.PushEmpty(RuntimeValueType.DoubleLiteral).DoubleLiteral = ResolveOperand(instruction, 0).DoubleLiteral; break; case OpCode.PUSH_MEMORY_INDEX: op1 = ResolveOperand(instruction, 0); op2 = _runtimeStack.PushEmpty(RuntimeValueType.Invalid); //op2.MemoryIndex = op1.MemoryIndex; op2.StackIndex = op1.StackIndex; SetMemoryIndexValue(op2, op1.MemoryIndex); break; case OpCode.PUSH_NULL: _runtimeStack.PushEmpty(RuntimeValueType.Invalid).Clear(); break; #endregion #region CAST // Value casting op codes. case OpCode.CAST_NULL: if (ResolveOperand(instruction, 0).ObjectIndex != -1) SetObjectValue(ResolveOperand(instruction, 0), -1); else if (ResolveOperand(instruction, 0).MemoryIndex != -1) SetMemoryIndexValue(ResolveOperand(instruction, 0), -1); ResolveOperand(instruction, 0).Clear(); break; case OpCode.CAST_BOOL: op1 = ResolveOperand(instruction, 0); switch (op1.ValueType) { case RuntimeValueType.Object: op1.BooleanLiteral = op1.ObjectIndex == 0 ? false : true; SetObjectValue(op1, -1); break; case RuntimeValueType.ByteLiteral: op1.BooleanLiteral = op1.ByteLiteral == 0 ? false : true; break; case RuntimeValueType.DoubleLiteral: op1.BooleanLiteral = op1.DoubleLiteral == 0 ? false : true; break; case RuntimeValueType.FloatLiteral: op1.BooleanLiteral = op1.FloatLiteral == 0 ? false : true; break; case RuntimeValueType.IntegerLiteral: op1.BooleanLiteral = op1.IntegerLiteral == 0 ? false : true; break; case RuntimeValueType.LongLiteral: op1.BooleanLiteral = op1.LongLiteral == 0 ? false : true; break; case RuntimeValueType.ShortLiteral: op1.BooleanLiteral = op1.ShortLiteral == 0 ? false : true; break; case RuntimeValueType.StringLiteral: op1.BooleanLiteral = (op1.StringLiteral == "1" || op1.StringLiteral.ToLower() == "true" ? true : false); break; } op1.ValueType = RuntimeValueType.BooleanLiteral; break; case OpCode.CAST_BYTE: op1 = ResolveOperand(instruction, 0); switch (op1.ValueType) { case RuntimeValueType.Object: op1.ByteLiteral = (byte)op1.ObjectIndex; SetObjectValue(op1, -1); break; case RuntimeValueType.BooleanLiteral: op1.ByteLiteral = byte.Parse(op1.BooleanLiteral.ToString()); break; case RuntimeValueType.DoubleLiteral: op1.ByteLiteral = (byte)op1.DoubleLiteral; break; case RuntimeValueType.FloatLiteral: op1.ByteLiteral = (byte)op1.FloatLiteral; break; case RuntimeValueType.IntegerLiteral: op1.ByteLiteral = (byte)op1.IntegerLiteral; break; case RuntimeValueType.LongLiteral: op1.ByteLiteral = (byte)op1.LongLiteral; break; case RuntimeValueType.ShortLiteral: op1.ByteLiteral = (byte)op1.ShortLiteral; break; case RuntimeValueType.StringLiteral: op1.ByteLiteral = op1.StringLiteral == "" ? (byte)0 : (op1.StringLiteral.IndexOf('.') >= 0 ? (byte)float.Parse(op1.StringLiteral) : byte.Parse(op1.StringLiteral)); break; } op1.ValueType = RuntimeValueType.ByteLiteral; break; case OpCode.CAST_INT: op1 = ResolveOperand(instruction, 0); switch (op1.ValueType) { case RuntimeValueType.Object: op1.IntegerLiteral = (int)op1.ObjectIndex; SetObjectValue(op1, -1); break; case RuntimeValueType.BooleanLiteral: op1.IntegerLiteral = (op1.BooleanLiteral == true ? 1 : 0); break; case RuntimeValueType.ByteLiteral: op1.IntegerLiteral = (int)op1.ByteLiteral; break; case RuntimeValueType.DoubleLiteral: op1.IntegerLiteral = (int)op1.DoubleLiteral; break; case RuntimeValueType.FloatLiteral: op1.IntegerLiteral = (int)op1.FloatLiteral; break; case RuntimeValueType.LongLiteral: op1.IntegerLiteral = (int)op1.LongLiteral; break; case RuntimeValueType.ShortLiteral: op1.IntegerLiteral = (int)op1.ShortLiteral; break; case RuntimeValueType.StringLiteral: op1.IntegerLiteral = op1.StringLiteral == "" ? 0 : (op1.StringLiteral.IndexOf('.') >= 0 ? (int)float.Parse(op1.StringLiteral) : int.Parse(op1.StringLiteral)); break; } op1.ValueType = RuntimeValueType.IntegerLiteral; break; case OpCode.CAST_SHORT: op1 = ResolveOperand(instruction, 0); switch (op1.ValueType) { case RuntimeValueType.Object: op1.ShortLiteral = (short)op1.ObjectIndex; SetObjectValue(op1, -1); break; case RuntimeValueType.BooleanLiteral: op1.ShortLiteral = (short)(op1.BooleanLiteral == true ? 1 : 0); break; case RuntimeValueType.ByteLiteral: op1.ShortLiteral = (short)op1.ByteLiteral; break; case RuntimeValueType.DoubleLiteral: op1.ShortLiteral = (short)op1.DoubleLiteral; break; case RuntimeValueType.FloatLiteral: op1.ShortLiteral = (short)op1.FloatLiteral; break; case RuntimeValueType.IntegerLiteral: op1.ShortLiteral = (short)op1.IntegerLiteral; break; case RuntimeValueType.LongLiteral: op1.ShortLiteral = (short)op1.LongLiteral; break; case RuntimeValueType.StringLiteral: op1.ShortLiteral = op1.StringLiteral == "" ? (short)0 : (op1.StringLiteral.IndexOf('.') >= 0 ? (short)float.Parse(op1.StringLiteral) : short.Parse(op1.StringLiteral)); break; } op1.ValueType = RuntimeValueType.ShortLiteral; break; case OpCode.CAST_FLOAT: op1 = ResolveOperand(instruction, 0); switch (op1.ValueType) { case RuntimeValueType.FloatLiteral: op1.FloatLiteral = (float)op1.ObjectIndex; SetObjectValue(op1, -1); break; case RuntimeValueType.BooleanLiteral: op1.FloatLiteral = (op1.BooleanLiteral == true ? 1 : 0); break; case RuntimeValueType.ByteLiteral: op1.FloatLiteral = (float)op1.ByteLiteral; break; case RuntimeValueType.DoubleLiteral: op1.FloatLiteral = (float)op1.DoubleLiteral; break; case RuntimeValueType.IntegerLiteral: op1.FloatLiteral = (float)op1.IntegerLiteral; break; case RuntimeValueType.LongLiteral: op1.FloatLiteral = (float)op1.LongLiteral; break; case RuntimeValueType.ShortLiteral: op1.FloatLiteral = (float)op1.ShortLiteral; break; case RuntimeValueType.StringLiteral: op1.FloatLiteral = op1.StringLiteral == "" ? 0 : float.Parse(op1.StringLiteral); break; } op1.ValueType = RuntimeValueType.FloatLiteral; break; case OpCode.CAST_DOUBLE: op1 = ResolveOperand(instruction, 0); switch (op1.ValueType) { case RuntimeValueType.Object: op1.DoubleLiteral = (double)op1.ObjectIndex; SetObjectValue(op1, -1); break; case RuntimeValueType.BooleanLiteral: op1.DoubleLiteral = (op1.BooleanLiteral == true ? 1 : 0); break; case RuntimeValueType.ByteLiteral: op1.DoubleLiteral = (double)op1.ByteLiteral; break; case RuntimeValueType.FloatLiteral: op1.DoubleLiteral = (double)op1.FloatLiteral; break; case RuntimeValueType.IntegerLiteral: op1.DoubleLiteral = (double)op1.IntegerLiteral; break; case RuntimeValueType.LongLiteral: op1.DoubleLiteral = (double)op1.LongLiteral; break; case RuntimeValueType.ShortLiteral: op1.DoubleLiteral = (double)op1.ShortLiteral; break; case RuntimeValueType.StringLiteral: op1.DoubleLiteral = op1.StringLiteral == "" ? 0 : double.Parse(op1.StringLiteral); break; } op1.ValueType = RuntimeValueType.DoubleLiteral; break; case OpCode.CAST_LONG: op1 = ResolveOperand(instruction, 0); switch (op1.ValueType) { case RuntimeValueType.Object: op1.LongLiteral = (long)op1.ObjectIndex; SetObjectValue(op1, -1); break; case RuntimeValueType.BooleanLiteral: op1.LongLiteral = (op1.BooleanLiteral == true ? 1 : 0); break; case RuntimeValueType.ByteLiteral: op1.LongLiteral = (long)op1.ByteLiteral; break; case RuntimeValueType.DoubleLiteral: op1.LongLiteral = (long)op1.DoubleLiteral; break; case RuntimeValueType.FloatLiteral: op1.LongLiteral = (long)op1.FloatLiteral; break; case RuntimeValueType.IntegerLiteral: op1.LongLiteral = (long)op1.IntegerLiteral; break; case RuntimeValueType.ShortLiteral: op1.LongLiteral = (long)op1.ShortLiteral; break; case RuntimeValueType.StringLiteral: op1.LongLiteral = op1.StringLiteral == "" ? 0 : (op1.StringLiteral.IndexOf('.') >= 0 ? (long)float.Parse(op1.StringLiteral) : long.Parse(op1.StringLiteral)); break; } op1.ValueType = RuntimeValueType.LongLiteral; break; case OpCode.CAST_Object: op1 = ResolveOperand(instruction, 0); switch (op1.ValueType) { case RuntimeValueType.BooleanLiteral: SetObjectValue(op1, (op1.BooleanLiteral == true ? 1 : 0)); break; case RuntimeValueType.ByteLiteral: SetObjectValue(op1, op1.ByteLiteral); break; case RuntimeValueType.DoubleLiteral: SetObjectValue(op1, (int)op1.DoubleLiteral); break; case RuntimeValueType.FloatLiteral: SetObjectValue(op1, (int)op1.FloatLiteral); break; case RuntimeValueType.IntegerLiteral: SetObjectValue(op1, op1.IntegerLiteral); break; case RuntimeValueType.LongLiteral: SetObjectValue(op1, (int)op1.LongLiteral); break; case RuntimeValueType.ShortLiteral: SetObjectValue(op1, op1.ShortLiteral); break; case RuntimeValueType.StringLiteral: SetObjectValue(op1, (op1.StringLiteral != "" ? (op1.StringLiteral.IndexOf('.') >= 0 ? (int)float.Parse(op1.StringLiteral) : int.Parse(op1.StringLiteral)) : 0)); break; } op1.ValueType = RuntimeValueType.Object; break; case OpCode.CAST_STRING: op1 = ResolveOperand(instruction, 0); switch (op1.ValueType) { case RuntimeValueType.Object: op1.StringLiteral = op1.ObjectIndex.ToString(); SetObjectValue(op1, -1); break; case RuntimeValueType.BooleanLiteral: op1.StringLiteral = (op1.BooleanLiteral == true ? "1" : "0"); break; case RuntimeValueType.ByteLiteral: op1.StringLiteral = op1.ByteLiteral.ToString(); break; case RuntimeValueType.DoubleLiteral: op1.StringLiteral = op1.DoubleLiteral.ToString(); break; case RuntimeValueType.FloatLiteral: op1.StringLiteral = op1.FloatLiteral.ToString(); break; case RuntimeValueType.IntegerLiteral: op1.StringLiteral = op1.IntegerLiteral.ToString(); break; case RuntimeValueType.LongLiteral: op1.StringLiteral = op1.LongLiteral.ToString(); break; case RuntimeValueType.ShortLiteral: op1.StringLiteral = op1.ShortLiteral.ToString(); break; } op1.ValueType = RuntimeValueType.StringLiteral; break; #endregion #region MOV // Arithmetic op codes. case OpCode.MOV_BOOL: op1 = ResolveOperand(instruction, 0); op1.ValueType = RuntimeValueType.BooleanLiteral; op1.BooleanLiteral = ResolveOperand(instruction, 1).BooleanLiteral; break; case OpCode.MOV_BYTE: op1 = ResolveOperand(instruction, 0); op1.ValueType = RuntimeValueType.ByteLiteral; op1.ByteLiteral = ResolveOperand(instruction, 1).ByteLiteral; break; case OpCode.MOV_INT: op1 = ResolveOperand(instruction, 0); op1.ValueType = RuntimeValueType.IntegerLiteral; op1.IntegerLiteral = ResolveOperand(instruction, 1).IntegerLiteral; break; case OpCode.MOV_SHORT: op1 = ResolveOperand(instruction, 0); op1.ValueType = RuntimeValueType.ShortLiteral; op1.ShortLiteral = ResolveOperand(instruction, 1).ShortLiteral; break; case OpCode.MOV_FLOAT: op1 = ResolveOperand(instruction, 0); op1.ValueType = RuntimeValueType.FloatLiteral; op1.FloatLiteral = ResolveOperand(instruction, 1).FloatLiteral; break; case OpCode.MOV_DOUBLE: op1 = ResolveOperand(instruction, 0); op1.ValueType = RuntimeValueType.DoubleLiteral; op1.DoubleLiteral = ResolveOperand(instruction, 1).DoubleLiteral; break; case OpCode.MOV_LONG: op1 = ResolveOperand(instruction, 0); op1.ValueType = RuntimeValueType.LongLiteral; op1.LongLiteral = ResolveOperand(instruction, 1).LongLiteral; break; case OpCode.MOV_OBJECT: op1 = ResolveOperand(instruction, 0); op1.ValueType = RuntimeValueType.Object; SetObjectValue(op1, ResolveOperand(instruction, 1).ObjectIndex); break; case OpCode.MOV_NULL: if (ResolveOperand(instruction, 0).ObjectIndex != -1) SetObjectValue(ResolveOperand(instruction, 0), -1); else if (ResolveOperand(instruction, 0).MemoryIndex != -1) SetMemoryIndexValue(ResolveOperand(instruction, 0), -1); ResolveOperand(instruction, 0).Clear(); break; case OpCode.MOV_STRING: op1 = ResolveOperand(instruction, 0); op1.ValueType = RuntimeValueType.StringLiteral; op1.StringLiteral = ResolveOperand(instruction, 1).StringLiteral; break; case OpCode.MOV_MEMORY_INDEX: op1 = ResolveOperand(instruction, 0); op2 = ResolveOperand(instruction, 1); //op1.MemoryIndex = op2.MemoryIndex; op1.StackIndex = op2.StackIndex; SetMemoryIndexValue(op1, op2.MemoryIndex); break; #endregion #region MUL case OpCode.MUL_BYTE: ResolveOperand(instruction, 0).ByteLiteral *= ResolveOperand(instruction, 1).ByteLiteral; break; case OpCode.MUL_INT: ResolveOperand(instruction, 0).IntegerLiteral *= ResolveOperand(instruction, 1).IntegerLiteral; break; case OpCode.MUL_SHORT: ResolveOperand(instruction, 0).ShortLiteral *= ResolveOperand(instruction, 1).ShortLiteral; break; case OpCode.MUL_FLOAT: ResolveOperand(instruction, 0).FloatLiteral *= ResolveOperand(instruction, 1).FloatLiteral; break; case OpCode.MUL_DOUBLE: ResolveOperand(instruction, 0).DoubleLiteral *= ResolveOperand(instruction, 1).DoubleLiteral; break; case OpCode.MUL_LONG: ResolveOperand(instruction, 0).LongLiteral *= ResolveOperand(instruction, 1).LongLiteral; break; #endregion #region DIV case OpCode.DIV_BYTE: ResolveOperand(instruction, 0).ByteLiteral /= ResolveOperand(instruction, 1).ByteLiteral; break; case OpCode.DIV_INT: ResolveOperand(instruction, 0).IntegerLiteral /= ResolveOperand(instruction, 1).IntegerLiteral; break; case OpCode.DIV_SHORT: ResolveOperand(instruction, 0).ShortLiteral /= ResolveOperand(instruction, 1).ShortLiteral; break; case OpCode.DIV_FLOAT: ResolveOperand(instruction, 0).FloatLiteral /= ResolveOperand(instruction, 1).FloatLiteral; break; case OpCode.DIV_DOUBLE: ResolveOperand(instruction, 0).DoubleLiteral /= ResolveOperand(instruction, 1).DoubleLiteral; break; case OpCode.DIV_LONG: ResolveOperand(instruction, 0).LongLiteral /= ResolveOperand(instruction, 1).LongLiteral; break; #endregion #region ADD case OpCode.ADD_STRING: ResolveOperand(instruction, 0).StringLiteral += ResolveOperand(instruction, 1).StringLiteral; break; case OpCode.ADD_BYTE: ResolveOperand(instruction, 0).ByteLiteral += ResolveOperand(instruction, 1).ByteLiteral; break; case OpCode.ADD_INT: ResolveOperand(instruction, 0).IntegerLiteral += ResolveOperand(instruction, 1).IntegerLiteral; break; case OpCode.ADD_SHORT: ResolveOperand(instruction, 0).ShortLiteral += ResolveOperand(instruction, 1).ShortLiteral; break; case OpCode.ADD_FLOAT: ResolveOperand(instruction, 0).FloatLiteral += ResolveOperand(instruction, 1).FloatLiteral; break; case OpCode.ADD_DOUBLE: ResolveOperand(instruction, 0).DoubleLiteral += ResolveOperand(instruction, 1).DoubleLiteral; break; case OpCode.ADD_LONG: ResolveOperand(instruction, 0).LongLiteral += ResolveOperand(instruction, 1).LongLiteral; break; #endregion #region SUB case OpCode.SUB_BYTE: ResolveOperand(instruction, 0).ByteLiteral -= ResolveOperand(instruction, 1).ByteLiteral; break; case OpCode.SUB_INT: ResolveOperand(instruction, 0).IntegerLiteral -= ResolveOperand(instruction, 1).IntegerLiteral; break; case OpCode.SUB_SHORT: ResolveOperand(instruction, 0).ShortLiteral -= ResolveOperand(instruction, 1).ShortLiteral; break; case OpCode.SUB_FLOAT: ResolveOperand(instruction, 0).FloatLiteral -= ResolveOperand(instruction, 1).FloatLiteral; break; case OpCode.SUB_DOUBLE: ResolveOperand(instruction, 0).DoubleLiteral -= ResolveOperand(instruction, 1).DoubleLiteral; break; case OpCode.SUB_LONG: ResolveOperand(instruction, 0).LongLiteral -= ResolveOperand(instruction, 1).LongLiteral; break; #endregion #region INC case OpCode.INC_BYTE: ResolveOperand(instruction, 0).ByteLiteral++; break; case OpCode.INC_INT: ResolveOperand(instruction, 0).IntegerLiteral++; break; case OpCode.INC_SHORT: ResolveOperand(instruction, 0).ShortLiteral++; break; case OpCode.INC_FLOAT: ResolveOperand(instruction, 0).FloatLiteral++; break; case OpCode.INC_DOUBLE: ResolveOperand(instruction, 0).DoubleLiteral++; break; case OpCode.INC_LONG: ResolveOperand(instruction, 0).LongLiteral++; break; #endregion #region DEC case OpCode.DEC_BYTE: ResolveOperand(instruction, 0).ByteLiteral--; break; case OpCode.DEC_INT: ResolveOperand(instruction, 0).IntegerLiteral--; break; case OpCode.DEC_SHORT: ResolveOperand(instruction, 0).ShortLiteral--; break; case OpCode.DEC_FLOAT: ResolveOperand(instruction, 0).FloatLiteral--; break; case OpCode.DEC_DOUBLE: ResolveOperand(instruction, 0).DoubleLiteral--; break; case OpCode.DEC_LONG: ResolveOperand(instruction, 0).LongLiteral--; break; #endregion #region NEG case OpCode.NEG_BYTE: ResolveOperand(instruction, 0).ByteLiteral = (byte)(-(int)ResolveOperand(instruction, 0).ByteLiteral); break; case OpCode.NEG_INT: ResolveOperand(instruction, 0).IntegerLiteral = -ResolveOperand(instruction, 0).IntegerLiteral; break; case OpCode.NEG_SHORT: ResolveOperand(instruction, 0).ShortLiteral = (short)(-(int)ResolveOperand(instruction, 0).ShortLiteral); break; case OpCode.NEG_FLOAT: ResolveOperand(instruction, 0).FloatLiteral = -ResolveOperand(instruction, 0).FloatLiteral; break; case OpCode.NEG_DOUBLE: ResolveOperand(instruction, 0).DoubleLiteral = -ResolveOperand(instruction, 0).DoubleLiteral; break; case OpCode.NEG_LONG: ResolveOperand(instruction, 0).LongLiteral = -ResolveOperand(instruction, 0).LongLiteral; break; #endregion #region ABS case OpCode.ABS_BYTE: ResolveOperand(instruction, 0).ByteLiteral = (byte)(+(int)ResolveOperand(instruction, 0).ByteLiteral); break; case OpCode.ABS_INT: ResolveOperand(instruction, 0).IntegerLiteral = +ResolveOperand(instruction, 0).IntegerLiteral; break; case OpCode.ABS_SHORT: ResolveOperand(instruction, 0).ShortLiteral = (short)(-(int)ResolveOperand(instruction, 0).ShortLiteral); break; case OpCode.ABS_FLOAT: ResolveOperand(instruction, 0).FloatLiteral = +ResolveOperand(instruction, 0).FloatLiteral; break; case OpCode.ABS_DOUBLE: ResolveOperand(instruction, 0).DoubleLiteral = +ResolveOperand(instruction, 0).DoubleLiteral; break; case OpCode.ABS_LONG: ResolveOperand(instruction, 0).LongLiteral = +ResolveOperand(instruction, 0).LongLiteral; break; #endregion #region Bitwise Operations case OpCode.BIT_OR_INT: ResolveOperand(instruction, 0).IntegerLiteral |= ResolveOperand(instruction, 0).IntegerLiteral; break; case OpCode.BIT_AND_INT: ResolveOperand(instruction, 0).IntegerLiteral &= ResolveOperand(instruction, 0).IntegerLiteral; break; case OpCode.BIT_XOR_INT: ResolveOperand(instruction, 0).IntegerLiteral ^= ResolveOperand(instruction, 0).IntegerLiteral; break; case OpCode.BIT_NOT_INT: ResolveOperand(instruction, 0).IntegerLiteral = ~ResolveOperand(instruction, 0).IntegerLiteral; break; case OpCode.BIT_SHL_INT: ResolveOperand(instruction, 0).IntegerLiteral <<= ResolveOperand(instruction, 0).IntegerLiteral; break; case OpCode.BIT_SHR_INT: ResolveOperand(instruction, 0).IntegerLiteral >>= ResolveOperand(instruction, 0).IntegerLiteral; break; #endregion #region Misc Operations case OpCode.EXP_INT: ResolveOperand(instruction, 0).IntegerLiteral = (int)Math.Pow(ResolveOperand(instruction, 0).IntegerLiteral, ResolveOperand(instruction, 1).IntegerLiteral); break; case OpCode.MOD_INT: if (ResolveOperand(instruction, 0).IntegerLiteral != 0 && ResolveOperand(instruction, 1).IntegerLiteral != 0) ResolveOperand(instruction, 0).IntegerLiteral %= ResolveOperand(instruction, 1).IntegerLiteral; else ResolveOperand(instruction, 0).IntegerLiteral = 0; break; #endregion #region CMP // Flow of control op codes. case OpCode.CMP_NULL: op1 = ResolveOperand(instruction, 0); _registers[(int)Register.Compare].IntegerLiteral = (op1.IsNull() == true || ((op1.DataType.DataType == DataType.Object) && _process.ObjectHeap[op1.ObjectIndex] == null)) ? 1 : 0; break; case OpCode.CMP_Object: RuntimeObject obj1 = ResolveOperand(instruction, 0).ObjectIndex == -1 ? null : _process.ObjectHeap[ResolveOperand(instruction, 0).ObjectIndex]; RuntimeObject obj2 = ResolveOperand(instruction, 1).ObjectIndex == -1 ? null : _process.ObjectHeap[ResolveOperand(instruction, 1).ObjectIndex]; if (obj1 as NativeObject != null && obj2 as NativeObject != null) _registers[(int)Register.Compare].IntegerLiteral = (((NativeObject)obj1).Object == ((NativeObject)obj2).Object) ? 0 : 1; else if (obj1 as NativeObject == null && obj2 as NativeObject == null) _registers[(int)Register.Compare].IntegerLiteral = 0; else _registers[(int)Register.Compare].IntegerLiteral = Math.Sign(ResolveOperand(instruction, 0).ObjectIndex - ResolveOperand(instruction, 1).ObjectIndex); break; case OpCode.CMP_BOOL: _registers[(int)Register.Compare].IntegerLiteral = Math.Sign((ResolveOperand(instruction, 0).BooleanLiteral == true ? 1 : 0) - (ResolveOperand(instruction, 1).BooleanLiteral == true ? 1 : 0)); break; case OpCode.CMP_BYTE: _registers[(int)Register.Compare].IntegerLiteral = Math.Sign(ResolveOperand(instruction, 0).ByteLiteral - ResolveOperand(instruction, 1).ByteLiteral); break; case OpCode.CMP_INT: _registers[(int)Register.Compare].IntegerLiteral = Math.Sign(ResolveOperand(instruction, 0).IntegerLiteral - ResolveOperand(instruction, 1).IntegerLiteral); break; case OpCode.CMP_SHORT: _registers[(int)Register.Compare].IntegerLiteral = Math.Sign(ResolveOperand(instruction, 0).ShortLiteral - ResolveOperand(instruction, 1).ShortLiteral); break; case OpCode.CMP_FLOAT: _registers[(int)Register.Compare].IntegerLiteral = Math.Sign(ResolveOperand(instruction, 0).FloatLiteral - ResolveOperand(instruction, 1).FloatLiteral); break; case OpCode.CMP_STRING: _registers[(int)Register.Compare].IntegerLiteral = Math.Sign(ResolveOperand(instruction, 0).StringLiteral.CompareTo(ResolveOperand(instruction, 1).StringLiteral)); break; case OpCode.CMP_LONG: _registers[(int)Register.Compare].IntegerLiteral = Math.Sign(ResolveOperand(instruction, 0).LongLiteral - ResolveOperand(instruction, 1).LongLiteral); break; case OpCode.CMP_DOUBLE: _registers[(int)Register.Compare].IntegerLiteral = Math.Sign(ResolveOperand(instruction, 0).DoubleLiteral - ResolveOperand(instruction, 1).DoubleLiteral); break; case OpCode.CMP_MEMORY_INDEX: _registers[(int)Register.Compare].IntegerLiteral = Math.Sign(ResolveOperand(instruction, 0).MemoryIndex - ResolveOperand(instruction, 1).MemoryIndex); break; #endregion #region JMP case OpCode.JMP_EQ: if (_registers[(int)Register.Compare].IntegerLiteral == 0) _instructionPointer = ResolveOperand(instruction, 0).InstrIndex; break; case OpCode.JMP_L: if (_registers[(int)Register.Compare].IntegerLiteral == -1) _instructionPointer = ResolveOperand(instruction, 0).InstrIndex; break; case OpCode.JMP_G: if (_registers[(int)Register.Compare].IntegerLiteral == 1) _instructionPointer = ResolveOperand(instruction, 0).InstrIndex; break; case OpCode.JMP_LE: if (_registers[(int)Register.Compare].IntegerLiteral == -1 || _registers[(int)Register.Compare].IntegerLiteral == 0) _instructionPointer = ResolveOperand(instruction, 0).InstrIndex; break; case OpCode.JMP_GE: if (_registers[(int)Register.Compare].IntegerLiteral == 1 || _registers[(int)Register.Compare].IntegerLiteral == 0) _instructionPointer = ResolveOperand(instruction, 0).InstrIndex; break; case OpCode.JMP_NE: if (_registers[(int)Register.Compare].IntegerLiteral != 0) _instructionPointer = ResolveOperand(instruction, 0).InstrIndex; break; case OpCode.JMP: _instructionPointer = ResolveOperand(instruction, 0).InstrIndex; break; #endregion #region IS case OpCode.IS_EQ: stack1 = _runtimeStack.PushEmpty(RuntimeValueType.BooleanLiteral); stack1.BooleanLiteral = (_registers[(int)Register.Compare].IntegerLiteral == 0); break; case OpCode.IS_L: stack1 = _runtimeStack.PushEmpty(RuntimeValueType.BooleanLiteral); stack1.BooleanLiteral = (_registers[(int)Register.Compare].IntegerLiteral == -1); break; case OpCode.IS_G: stack1 = _runtimeStack.PushEmpty(RuntimeValueType.BooleanLiteral); stack1.BooleanLiteral = (_registers[(int)Register.Compare].IntegerLiteral == 1); break; case OpCode.IS_LE: stack1 = _runtimeStack.PushEmpty(RuntimeValueType.BooleanLiteral); stack1.BooleanLiteral = (_registers[(int)Register.Compare].IntegerLiteral == -1 || _registers[(int)Register.Compare].IntegerLiteral == 0); break; case OpCode.IS_GE: stack1 = _runtimeStack.PushEmpty(RuntimeValueType.BooleanLiteral); stack1.BooleanLiteral = (_registers[(int)Register.Compare].IntegerLiteral == 1 || _registers[(int)Register.Compare].IntegerLiteral == 0); break; case OpCode.IS_NE: stack1 = _runtimeStack.PushEmpty(RuntimeValueType.BooleanLiteral); stack1.BooleanLiteral = (_registers[(int)Register.Compare].IntegerLiteral != 0); break; #endregion #region Logical Operations case OpCode.LOGICAL_AND: stack1 = _runtimeStack.PushEmpty(RuntimeValueType.BooleanLiteral); stack1.BooleanLiteral = (ResolveOperand(instruction, 0).BooleanLiteral && ResolveOperand(instruction, 1).BooleanLiteral); break; case OpCode.LOGICAL_OR: stack1 = _runtimeStack.PushEmpty(RuntimeValueType.BooleanLiteral); stack1.BooleanLiteral = (ResolveOperand(instruction, 0).BooleanLiteral || ResolveOperand(instruction, 1).BooleanLiteral); break; case OpCode.LOGICAL_NOT: stack1 = _runtimeStack.PushEmpty(RuntimeValueType.BooleanLiteral); stack1.BooleanLiteral = !ResolveOperand(instruction, 0).BooleanLiteral; break; #endregion #region Stack Frame Manipulation case OpCode.CALL: // Increase instruction following this call so we don't // end up going round is circles when this call returns. _instructionPointer++; CallFunction(ResolveOperand(instruction, 0).Symbol as FunctionSymbol, false); break; case OpCode.RETURN: // Find the function index and throw corruption error if its not there. RuntimeValue functionValue = _runtimeStack.Pop(); if (functionValue.ValueType != RuntimeValueType.StackFrameIndex && functionValue.ValueType != RuntimeValueType.StackBaseMarker) { // Ahh hell, lets try and recover. #if DEBUG Error("Stack has been corrupted."); #else while (functionValue.ValueType != RuntimeValueType.StackFrameIndex && functionValue.ValueType != RuntimeValueType.StackBaseMarker) { if (_runtimeStack.TopIndex == 0) { Error("Stack has been corrupted."); break; } functionValue = _runtimeStack.Pop(); } #endif } // Find the parameter count and local data size. int parameterCount = 0, localDataSize = 0; if (functionValue.Symbol.Type == SymbolType.Function) { parameterCount = ((FunctionSymbol)functionValue.Symbol).ParameterCount; localDataSize = ((FunctionSymbol)functionValue.Symbol).LocalDataSize; } // Find the return value stack entry and throw corruption error if its not there. RuntimeValue returnAddressValue = _runtimeStack[_runtimeStack.TopIndex - (localDataSize + 1)]; if (returnAddressValue.ValueType != RuntimeValueType.ReturnAddress) Error("Stack has been corrupted."); // Reduce the reference count of any local varaibles. DataTypeValue[] parameterTypes = new DataTypeValue[((FunctionSymbol)functionValue.Symbol).ParameterCount]; for (int i = 0; i < ((FunctionSymbol)functionValue.Symbol).ParameterCount; i++) parameterTypes[(((FunctionSymbol)functionValue.Symbol).ParameterCount - 1) - i] = ((VariableSymbol)((FunctionSymbol)functionValue.Symbol).Symbols[i]).DataType; // Remove reference counts for parameters. //for (int i = 0; i < parameterCount; i++) //{ // RuntimeValue parameter = _runtimeStack[(_runtimeStack.TopIndex - (parameterCount + 1)) + i]; // if (parameterTypes[i].DataType == DataType.Object && parameter.ObjectIndex != -1 && _process.ObjectHeap[parameter.ObjectIndex] != null) // { // _process.ObjectHeap[parameter.ObjectIndex].ReferenceCount--; // DIE! // } //} // Remove reference counts for local variables. //foreach (Symbol symbol in functionValue.Symbol.Symbols) //{ // if (!(symbol is VariableSymbol) || ((VariableSymbol)symbol).VariableType != VariableType.Local) continue; // RuntimeValue local = GetRuntimeValueLocal(symbol.Identifier); // if (((VariableSymbol)symbol).DataType.DataType == DataType.Object && local.ObjectIndex != -1 && _process.ObjectHeap[local.ObjectIndex] != null) // _process.ObjectHeap[local.ObjectIndex].ReferenceCount--; // DIE! //} // Pop the functions stack frame. //_runtimeStack.PopFrame(parameterCount + localDataSize + 1); for (int i = 0; i < (parameterCount + localDataSize + 1); i++) { RuntimeValue stack = _runtimeStack.Pop(); if (stack.ObjectIndex != -1) SetObjectValue(stack, -1); else if (stack.MemoryIndex != -1) SetMemoryIndexValue(stack, -1); } // Restore the previous stack. _runtimeStack.FrameIndex = functionValue.InstrIndex; // Jump to the return address. _instructionPointer = returnAddressValue.InstrIndex; //if (_process.Url == "media\\scripts\\LTTP 2.0.fs" && _callStack.Peek().ToString().ToLower() == "onrender") //{ // System.Console.WriteLine("\n\n--- OnRender Statistics ---"); // for (int i = 0; i < opCodeTimes.Length; i++) // System.Console.WriteLine("Time spent on " + ((OpCode)i) + " = " + opCodeTimes[i] + " ms"); //} // Pop this function off the call stack. _callStack.Pop(); // If its a stack base marker make sure we exit the loop at the end. if (functionValue.ValueType == RuntimeValueType.StackBaseMarker) return 2; // Out of things to run? if (_callStack.Count == 0) return 5; break; #endregion #region Execution // Execution related op codes. case OpCode.EXIT: _isRunning = false; _instructionPointer++; return 3; case OpCode.PAUSE: _isPaused = true; _pauseLength = ResolveOperand(instruction, 0).IntegerLiteral; _pauseTimer.Restart(); _instructionPointer++; return 0; case OpCode.GOTO_STATE: _process.ChangeState(ResolveOperand(instruction, 0).Symbol as StateSymbol); break; //return 4; #endregion #region Memory Allocation case OpCode.ALLOCATE_HEAP_NULL: SetMemoryIndexValue(ResolveOperand(instruction, 1), _process.AllocateHeap(ResolveOperand(instruction, 0).IntegerLiteral, RuntimeValueType.Invalid, null)); break; case OpCode.ALLOCATE_HEAP_BOOL: SetMemoryIndexValue(ResolveOperand(instruction, 1), _process.AllocateHeap(ResolveOperand(instruction, 0).IntegerLiteral, RuntimeValueType.BooleanLiteral, null)); break; case OpCode.ALLOCATE_HEAP_BYTE: SetMemoryIndexValue(ResolveOperand(instruction, 1), _process.AllocateHeap(ResolveOperand(instruction, 0).IntegerLiteral, RuntimeValueType.ByteLiteral, null)); break; case OpCode.ALLOCATE_HEAP_INT: SetMemoryIndexValue(ResolveOperand(instruction, 1), _process.AllocateHeap(ResolveOperand(instruction, 0).IntegerLiteral, RuntimeValueType.IntegerLiteral, null)); break; case OpCode.ALLOCATE_HEAP_SHORT: SetMemoryIndexValue(ResolveOperand(instruction, 1), _process.AllocateHeap(ResolveOperand(instruction, 0).IntegerLiteral, RuntimeValueType.ShortLiteral, null)); break; case OpCode.ALLOCATE_HEAP_FLOAT: SetMemoryIndexValue(ResolveOperand(instruction, 1), _process.AllocateHeap(ResolveOperand(instruction, 0).IntegerLiteral, RuntimeValueType.FloatLiteral, null)); break; case OpCode.ALLOCATE_HEAP_DOUBLE: SetMemoryIndexValue(ResolveOperand(instruction, 1), _process.AllocateHeap(ResolveOperand(instruction, 0).IntegerLiteral, RuntimeValueType.DoubleLiteral, null)); break; case OpCode.ALLOCATE_HEAP_LONG: SetMemoryIndexValue(ResolveOperand(instruction, 1), _process.AllocateHeap(ResolveOperand(instruction, 0).IntegerLiteral, RuntimeValueType.LongLiteral, null)); break; case OpCode.ALLOCATE_HEAP_Object: SetMemoryIndexValue(ResolveOperand(instruction, 1), _process.AllocateHeap(ResolveOperand(instruction, 0).IntegerLiteral, RuntimeValueType.Object, null)); break; case OpCode.ALLOCATE_HEAP_STRING: SetMemoryIndexValue(ResolveOperand(instruction, 1), _process.AllocateHeap(ResolveOperand(instruction, 0).IntegerLiteral, RuntimeValueType.StringLiteral, null)); break; case OpCode.DEALLOCATE_HEAP: op1 = ResolveOperand(instruction, 0); _process.MemoryHeap[op1.MemoryIndex - 1].ReferenceCount = 0; break; #endregion #region Debug // Debug op codes. case OpCode.BREAKPOINT: if (_debugger == null) ShowDebugger(); else if (_debugger.NotifyOnBreakPoint == true) _debugger.NotifyOfBreakPoint(); break; // These are for the debugger. We can ignore them :P. case OpCode.ENTER_STATEMENT: if (_debugger != null && _debugger.NotifyOnEnter == true) _debugger.NotifyOfEntry(); break; case OpCode.EXIT_STATEMENT: if (_debugger != null && _debugger.NotifyOnExit == true) _debugger.NotifyOfExit(); break; #endregion #region Thread syncronization case OpCode.LOCK: if (instruction.Locked == true) return 1; // Return and don't increment instruction pointer. else { instruction.Locked = true; _lockInstruction = instruction; } break; case OpCode.UNLOCK: _lockInstruction.Locked = false; break; case OpCode.ENTER_ATOM: _previousInAtom = _inAtom; _inAtom = true; break; case OpCode.LEAVE_ATOM: _inAtom = _previousInAtom; break; #endregion #region Member Modification case OpCode.CALL_METHOD: // Object, Symbol { // Grab the object we are invoked a method off. RuntimeValue objValue = ResolveOperand(instruction, 0); if (objValue.ObjectIndex == -1) Error("Attempt to call method of null object."); RuntimeObject obj = _process.ObjectHeap[objValue.ObjectIndex]; // Grab the function. FunctionSymbol symbol = ResolveOperand(instruction, 1).Symbol as FunctionSymbol; // Clean out the return register and push this function onto the callstack. _callStack.Push(symbol); _registers[(int)Register.Return].Clear(); // Invoke the method. if (obj.InvokeMethod(this, symbol) == false) Error("An error occured while attempting to call a objects method '"+symbol.ToString()+"'. The method may not exist or may not be public."); // Pop of this functions parameters. for (int i = 0; i < symbol.ParameterCount; i++) _runtimeStack.Pop(); // Pop this value off the call stack. _callStack.Pop(); break; } case OpCode.SET_MEMBER: // Object, symbol, value { // Grab the object we are invoked a method off. RuntimeValue objValue = ResolveOperand(instruction, 0); if (objValue.ObjectIndex == -1) Error("Attempt to set member of null object."); RuntimeObject obj = _process.ObjectHeap[objValue.ObjectIndex]; // Grab the variable. VariableSymbol symbol = ResolveOperand(instruction, 1).Symbol as VariableSymbol; // Invoke the method. if (obj.SetMember(this, symbol, ResolveOperand(instruction, 2)) == false) Error("An error occured while attempting to set an objects member. The member may not exist or may not be public."); break; } case OpCode.SET_MEMBER_INDEXED: // Object, symbol, value, index { // Grab the object we are invoked a method off. RuntimeValue objValue = ResolveOperand(instruction, 0); if (objValue.ObjectIndex == -1) Error("Attempt to set member of null object."); RuntimeObject obj = _process.ObjectHeap[objValue.ObjectIndex]; // Grab the variable. VariableSymbol symbol = ResolveOperand(instruction, 1).Symbol as VariableSymbol; // Invoke the method. if (obj.SetMember(this, symbol, ResolveOperand(instruction, 2), ResolveOperand(instruction, 3)) == false) Error("An error occured while attempting to set an objects member. The member may not exist or may not be public."); break; } case OpCode.GET_MEMBER: // Object, symbol { // Grab the object we are invoked a method off. RuntimeValue objValue = ResolveOperand(instruction, 0); if (objValue.ObjectIndex == -1) Error("Attempt to set member of null object."); RuntimeObject obj = _process.ObjectHeap[objValue.ObjectIndex]; // Grab the variable. VariableSymbol symbol = ResolveOperand(instruction, 1).Symbol as VariableSymbol; // Clean out the return register. _registers[(int)Register.Return].Clear(); // Invoke the method. if (obj.SetMember(this, symbol, ResolveOperand(instruction, 2)) == false) Error("An error occured while attempting to get an objects member. The member may not exist or may not be public."); break; } case OpCode.GET_MEMBER_INDEXED: // Object, symbol, index { // Grab the object we are invoked a method off. RuntimeValue objValue = ResolveOperand(instruction, 0); if (objValue.ObjectIndex == -1) Error("Attempt to set member of null object."); RuntimeObject obj = _process.ObjectHeap[objValue.ObjectIndex]; // Grab the variable. VariableSymbol symbol = ResolveOperand(instruction, 1).Symbol as VariableSymbol; // Clean out the return register. _registers[(int)Register.Return].Clear(); // Invoke the method. if (obj.SetMember(this, symbol, ResolveOperand(instruction, 2), ResolveOperand(instruction, 3)) == false) Error("An error occured while attempting to get an objects member. The member may not exist or may not be public."); break; } #endregion // 'ello, 'ello. What goin' on 'ere then. default: Error("Virtual Machine encountered an invalid operation code ("+instruction.OpCode.ToString()+")."); break; } #endregion // Move onto next instruction if this instruction has not progressed the instruction pointer. if (_instructionPointer == originalPointer) _instructionPointer++; //opCodeTimes[(int)instruction.OpCode] += (float)instructionTimer.DurationMillisecond; //if (_process.Url == "media\\scripts\\LTTP 2.0.fs") // System.Console.WriteLine(instruction.OpCode.ToString() + " executed in " + instructionTimer.DurationMillisecond + "ms"); _instructionsExecuted++; #if !DEBUG } catch (Exception e) { Error(e.Message); } #endif } }
/// <summary> /// Parses a top level expression. The top level is responsible for parsing /// any sub expressions and logical operators. /// </summary> /// <returns>The data type this expression evaluates to.</returns> private DataTypeValue ParseExpression() { // Parse the left hand sub expression. DataTypeValue subDataType1 = ParseSubExpression(); // Parse any subsequent sub expressions that are seperated // by logical operators. while (true) { // Check that the next token is logical or relational. Token operatorToken = LookAheadToken(); if (operatorToken.IsLogical == false) break; NextToken(); // Parse the right hand sub expression. DataTypeValue subDataType2 = ParseSubExpression(); // Check the new data type can be cast to the main data type if (CanImplicitlyCast(subDataType1, subDataType2) == true) { // Check the data types are valid with this operator. if (OperatorDataTypeValid(subDataType1, operatorToken.ID) == false) Error(ErrorCode.InvalidDataType, "Operator \"" + operatorToken.Ident + "\" can't be applied to data type \"" + subDataType1.ToString() + "\"", false, 1); // We only want to emit byte code on the second pass. if (_currentPass == 1) { // Pop the result of the second sub expression into arithmetic // register 1. Instruction instruction = CreateInstruction(OpCodeByType(subDataType2, OpCodeType.POP), _currentScope, _currentToken); new Operand(instruction, Register.Arithmetic2); // Pop the result of the first sub expression into arithmetic // register 2. instruction = CreateInstruction(OpCodeByType(subDataType1, OpCodeType.POP), _currentScope, _currentToken); new Operand(instruction, Register.Arithmetic1); #region Logical Byte Code Emiting // Create operation code based on operator. // Logical instructions only take boolean values // so there is no need to cast the values here. switch (operatorToken.ID) { case TokenID.OpLogicalAnd: instruction = CreateInstruction(OpCode.LOGICAL_AND, _currentScope, _currentToken); new Operand(instruction, Register.Arithmetic1); new Operand(instruction, Register.Arithmetic2); break; case TokenID.OpLogicalOr: instruction = CreateInstruction(OpCode.LOGICAL_OR, _currentScope, _currentToken); new Operand(instruction, Register.Arithmetic1); new Operand(instruction, Register.Arithmetic2); break; } #endregion } // Data type is now boolean due to the comparison operators. subDataType1 = new DataTypeValue(DataType.Bool, false, false); } else Error(ErrorCode.InvalidCast, "Can't implicitly cast between \"" + subDataType1.ToString() + "\" and \"" + subDataType2.ToString() + "\"", false, 1); } return subDataType1; }
/// <summary> /// Executes a script or native function. /// </summary> /// <param name="symbol">Symbol of function to call.</param> /// <param name="ignoreThread">If set and this function is a thread spawner a new thread will not be created.</param> private void CallFunction(FunctionSymbol symbol, bool ignoreThread) { // Can't call if debugging :S. if (_debugger != null && _debugger.RunScript == false) return; // Push this function onto the call stack. _callStack.Push(symbol); _registers[(int)Register.Return].Clear(); _callingFunction = symbol; if (symbol.IsImport == true) { // Find and call native function. if (_process.VirtualMachine != null) { DataTypeValue[] parameterTypes = null; NativeFunction function = symbol.NativeFunction; if (function == null) { // Find the functions parameter types. parameterTypes = new DataTypeValue[symbol.ParameterCount]; for (int i = 0; i < symbol.ParameterCount; i++) parameterTypes[(symbol.ParameterCount - 1) - i] = ((VariableSymbol)symbol.Symbols[i]).DataType; // Find native function function = _process.VirtualMachine.FindNativeFunction(symbol.Identifier, parameterTypes); symbol.NativeFunction = function; } if (function != null) { #if !DEBUG try { #endif function.Delegate(this); #if !DEBUG } catch (Exception e) { Error(e.ToString()); } #endif } // Ok so its not a native one, lets check the script exports. else { ScriptExportFunction callFunction = (ScriptExportFunction)ScriptExportFunction.FunctionHashTable[symbol.ToString().ToLower()]; /* foreach (ScriptExportFunction exportFunction in ScriptExportFunction.FunctionList) { if (exportFunction.Symbol.Identifier.ToLower() != symbol.Identifier.ToLower()) continue; if (exportFunction.Symbol.ParameterCount != parameterTypes.Length) continue; bool paramsValid = true; for (int i = 0; i < parameterTypes.Length; i++) if (exportFunction.Symbol.CheckParameterTypeValid(i, parameterTypes[i]) == false) paramsValid = false; if (paramsValid == false) continue; callFunction = exportFunction; break; } */ if (callFunction != null) { InvokeExportFunction(callFunction.Symbol, callFunction.Thread, this); } else { Error("Attempt to call unknown function '" + symbol.ToString() + "'"); } } } // Pop of this functions parameters. for (int i = 0; i < symbol.ParameterCount; i++) { RuntimeValue stack = _runtimeStack.Pop(); if (stack.ObjectIndex != -1) SetObjectValue(stack, -1); else if (stack.MemoryIndex != -1) SetMemoryIndexValue(stack, -1); } // Pop this value off the call stack. _callStack.Pop(); } else { if (symbol.IsThreadSpawner == true && ignoreThread == false) SpawnThread(symbol); else { int oldFrameIndex = _runtimeStack.FrameIndex; // Push the return address which is the current instruction. RuntimeValue returnAddress = _runtimeStack.PushEmpty(RuntimeValueType.ReturnAddress); returnAddress.InstrIndex = _instructionPointer; // Pushs this function stack frame onto stack. _runtimeStack.PushFrame(symbol.LocalDataSize + 1); // Place the function and old stack index into value // and push it onto stack. RuntimeValue stackValue = _runtimeStack.Peek(); stackValue.ValueType = RuntimeValueType.StackFrameIndex; stackValue.Symbol = symbol; stackValue.InstrIndex = oldFrameIndex; // Jump to entry point of this function. _instructionPointer = symbol.EntryPoint; // Force this script into 'running' mode if its been stopped. _isRunning = true; } } }
/// <summary> /// Parses a function declaration. A function is a way of isolating code that you want /// to be able to call multiple times without rewriting it each time. /// Syntax: /// ReturnType Identifier "(" { Identifier ["=" Expression] [ "," ] } ")" Statement /// </summary> private void ParseFunction() { // Make sure we are in a state's scope. if (_currentToken.ID == TokenID.KeywordEvent && _currentScope.Type != SymbolType.State) Error(ErrorCode.InvalidScope, "Events can only be declared within a state block.", false, 0); // Make sure we are in the global scopes as functions can't // be declared anywhere else. if (_currentScope.Type != SymbolType.State && _currentScope.Type != SymbolType.Function && _currentScope.Type != SymbolType.Namespace) Error(ErrorCode.InvalidScope, "Functions can only be declared in a function's, state's or event's scope.", false, 0); // Read in each flag. bool isThread = false; bool isEvent = false; bool isConsole = false; bool isExport = false; bool isImport = false; SymbolAccessModifier modifier = SymbolAccessModifier.Private; bool gotModifier = false; while (_currentToken.IsDataType == false && EndOfTokenStream() != true) { switch (_currentToken.ID) { case TokenID.KeywordThread: if (isThread == true) Error(ErrorCode.DuplicateFlag, "\"" + _currentToken.Ident + "\" flag declared multiple times.", false, 0); isThread = true; break; case TokenID.KeywordEvent: if (isEvent == true) Error(ErrorCode.DuplicateFlag, "\"" + _currentToken.Ident + "\" flag declared multiple times.", false, 0); isEvent = true; break; case TokenID.KeywordConsole: if (isConsole == true) Error(ErrorCode.DuplicateFlag, "\"" + _currentToken.Ident + "\" flag declared multiple times.", false, 0); isConsole = true; break; case TokenID.KeywordImport: if (isImport == true) Error(ErrorCode.DuplicateFlag, "\"" + _currentToken.Ident + "\" flag declared multiple times.", false, 0); isImport = true; break; case TokenID.KeywordExport: if (isExport == true) Error(ErrorCode.DuplicateFlag, "\"" + _currentToken.Ident + "\" flag declared multiple times.", false, 0); isExport = true; break; case TokenID.KeywordPublic: if (gotModifier == true) Error(ErrorCode.DuplicateFlag, "Access modifier has already been declared.", false, 0); modifier = SymbolAccessModifier.Public; gotModifier = true; break; case TokenID.KeywordPrivate: if (gotModifier == true) Error(ErrorCode.DuplicateFlag, "Access modifier has already been declared.", false, 0); modifier = SymbolAccessModifier.Private; gotModifier = true; break; case TokenID.KeywordProtected: if (gotModifier == true) Error(ErrorCode.DuplicateFlag, "Access modifier has already been declared.", false, 0); modifier = SymbolAccessModifier.Protected; gotModifier = true; break; } NextToken(); } if (isEvent == true && (isThread == true || isExport == true || isImport == true || isConsole == true)) Error(ErrorCode.InvalidFlag, "Events can't be declared as native, threaded, exported, imported or console."); // Store the return type for later. DataTypeValue returnDataType = new DataTypeValue(DataTypeFromKeywordToken(_currentToken.ID), false, false); if (returnDataType.DataType == DataType.Invalid) Error(ErrorCode.InvalidDataType, "Functions can't be declared as \"" + _currentToken.Ident + "\".", false, 0); // Check for an array reference if (LookAheadToken().ID == TokenID.CharOpenBracket) { NextToken(); returnDataType.IsArray = true; ExpectToken(TokenID.CharCloseBracket); } // Read in the functions identifier and store it // for layer use. ExpectToken(TokenID.TypeIdentifier); string functionIdentifier = _currentToken.Ident; // Read in the opening parenthesis used to define the paremeter list. ExpectToken(TokenID.CharOpenParenthesis); // Read in each paremeter. ArrayList functionParameterMask = new ArrayList(); int parameterCount = 0; ArrayList paramList = new ArrayList(); if (LookAheadToken().ID != TokenID.CharCloseParenthesis) { // Read in each parameter while (true) { // Check if there is a constant keyword before this variable. bool paramIsConst = false; if (LookAheadToken().ID == TokenID.KeywordConst) { NextToken(); paramIsConst = true; } // Read in the data type keyword and store it. NextToken(); DataTypeValue paramDataType = new DataTypeValue(DataTypeFromKeywordToken(_currentToken.ID), false, false); if (paramDataType.DataType == DataType.Invalid) Error(ErrorCode.InvalidDataType, "Expecting data type keyword.", false, 0); // Read in array declaration. bool paramIsArray = false; if (LookAheadToken().ID == TokenID.CharOpenBracket) { NextToken(); paramIsArray = true; paramDataType.IsArray = true; ExpectToken(TokenID.CharCloseBracket); } // Increase the parameter mask functionParameterMask.Add(paramDataType); parameterCount++; // Read in the parameters identifier. ExpectToken(TokenID.TypeIdentifier); string paramIdentifier = _currentToken.Ident; // Process parameters depending on current pass. if (_currentPass == 0) { VariableSymbol variableSymbol = new VariableSymbol(null); variableSymbol.DataType = paramDataType; variableSymbol.Identifier = paramIdentifier; variableSymbol.VariableType = VariableType.Parameter; variableSymbol.IsArray = paramIsArray; variableSymbol.IsConstant = paramIsConst; paramList.Add(variableSymbol); } // Read in comma if it exists. if (LookAheadToken().ID == TokenID.CharComma) NextToken(); else break; } } // *looks arounds shiftily* ... Ok its hack like but it works. functionParameterMask.Reverse(); // Create a new function symbol. FunctionSymbol functionSymbol; if (_currentPass == 0) { if (_currentScope.FindFunctionByMask(functionIdentifier, functionParameterMask) != null) Error(ErrorCode.DuplicateSymbol, "Function \"" + functionIdentifier + "\" redefinition.", false, 0); functionSymbol = new FunctionSymbol(functionIdentifier, _currentScope); functionSymbol.ReturnType = returnDataType; functionSymbol.ParameterCount = parameterCount; functionSymbol.IsThreadSpawner = isThread; functionSymbol.IsEvent = isEvent; functionSymbol.IsConsole = isConsole; functionSymbol.IsImport = isImport; functionSymbol.IsExport = isExport; functionSymbol.AccessModifier = modifier; if (isConsole == true && functionSymbol.ReturnType.DataType != DataType.Void) Error(ErrorCode.InvalidDataType, "Console variables cannot return a value."); paramList.Reverse(); int parameterIndex = 0; foreach (VariableSymbol variableSymbol in paramList) { if (functionSymbol.FindSymbol(variableSymbol.Identifier, SymbolType.Variable) != null) Error(ErrorCode.DuplicateSymbol, "Variable redefinition \"" + variableSymbol.Identifier + "\""); // If we're a console function we can only accept bool, int, string or float if (isConsole == true && (variableSymbol.DataType.IsArray == true || variableSymbol.DataType.DataType == DataType.Byte || variableSymbol.DataType.DataType == DataType.Double || variableSymbol.DataType.DataType == DataType.Object || variableSymbol.DataType.DataType == DataType.Short)) Error(ErrorCode.InvalidDataType, "Console variables can only accept bool, integer, string or float parameters."); functionSymbol.AddSymbol(variableSymbol); variableSymbol.Scope = functionSymbol; parameterIndex++; } } else { functionSymbol = _currentScope.FindFunctionByMask(functionIdentifier, functionParameterMask) as FunctionSymbol; if (functionSymbol == null) functionSymbol = _globalScope.FindFunctionByMask(functionIdentifier, functionParameterMask) as FunctionSymbol; if (functionSymbol == null) Error(ErrorCode.InvalidFunction, "Attempt to call undeclared function \"" + functionIdentifier + "(" + functionParameterMask + ")\"."); for (int i = 0; i < parameterCount; i++) { VariableSymbol variableSymbol = (VariableSymbol)functionSymbol.Symbols[i]; variableSymbol.StackIndex = -(functionSymbol.LocalDataSize + 2 + (i + 1)); } } _lastMetaDataSymbol = functionSymbol; // Read in the closing parenthesis used to define the end of the paremeter list. ExpectToken(TokenID.CharCloseParenthesis); // Read in this function's statement block. if (functionSymbol.IsImport == false) { Symbol scope = _currentScope; Instruction instruction = null; _currentScope = functionSymbol; ParseStatement(); _currentScope = scope; // Append a mandatory return instruction if we are in pass 2. if (_currentPass == 1) { // Cast the return registered into the return type. if (functionSymbol.ReturnType.DataType != DataType.Void) { instruction = CreateInstruction(OpCodeByType(functionSymbol.ReturnType, OpCodeType.CAST), functionSymbol, _currentToken); new Operand(instruction, Register.Return); } // Return from function. instruction = CreateInstruction(OpCode.RETURN, functionSymbol, _currentToken); } } else ExpectToken(TokenID.CharSemiColon); }
/// <summary> /// Finds a native function that has the same identifier, return type and /// parameter types as those passed. /// </summary> /// <param name="identifier">Identifier of function to find.</param> /// <param name="returnType">Return type of function to find.</param> /// <param name="parameterTypes">Array of parameter types of function to find.</param> /// <returns>Null if no function is found, if one is then it is returned.</returns> public NativeFunction FindNativeFunction(string identifier, DataTypeValue[] parameterMask) { string fullName = identifier + "("; for (int i = 0; i < parameterMask.Length; i++) fullName += parameterMask[i] + (i < parameterMask.Length - 1 ? "," : ""); fullName += ")"; /* foreach (NativeFunction function in _nativeFunctions[(int)identifier[0]]) { if (function.Identifier.ToLower() != identifier.ToLower()) continue; if (function.ParameterTypes.Length != parameterMask.Length) continue; bool paramsValid = true; for (int i = 0; i < parameterMask.Length; i++) if (function.CheckParameterTypeValid(i, parameterMask[i]) == false) paramsValid = false; if (paramsValid == false) continue; return function; } */ return _nativeFunctions[fullName] as NativeFunction; }
/// <summary> /// /// </summary> private void ParseMemberFunctionCall(string identifier, DataTypeValue returnDataType) { // Read the opening parenthesis. ExpectToken(TokenID.CharOpenParenthesis); // Pop the object out. if (_currentPass == 1) { Instruction instruction = CreateInstruction(OpCode.POP_OBJECT, _currentScope, _currentToken); new Operand(instruction, Register.Member); } // Read in each parameter's expression. ArrayList parameterTypeList = new ArrayList(); string parameterMask = ""; while (true) { // Check for parenthesis close if (LookAheadToken().ID != TokenID.CharCloseParenthesis) { // Read in the parameters expression. DataTypeValue expressionType = ParseExpression(); parameterMask += (parameterMask != "" ? "," : "") + expressionType.ToString(); parameterTypeList.Add(expressionType); // Read in comma if we are not at the // end of the parameter list. if (LookAheadToken().ID != TokenID.CharCloseParenthesis) ExpectToken(TokenID.CharComma); } else break; } // *looks arounds shiftily* ... Ok its hack like but it works. parameterTypeList.Reverse(); // Read the closing parenthesis. ExpectToken(TokenID.CharCloseParenthesis); // Get the function symbol if in pass 2. FunctionSymbol functionSymbol = null; if (_currentPass == 1) { functionSymbol = _memberScope.FindFunctionByMask(identifier, parameterTypeList) as FunctionSymbol; if (functionSymbol == null) { // Create the function symbol. functionSymbol = new FunctionSymbol(identifier, _memberScope); functionSymbol.Identifier = identifier; functionSymbol.ReturnType = returnDataType; functionSymbol.IsMember = true; functionSymbol.ParameterCount = parameterTypeList.Count; // Create a symbol for each parameters. foreach (DataTypeValue value in parameterTypeList) { VariableSymbol variableSymbol = new VariableSymbol(functionSymbol); variableSymbol.DataType = value; variableSymbol.Identifier = ""; variableSymbol.VariableType = VariableType.Parameter; variableSymbol.IsArray = value.IsArray; } } Instruction instruction = CreateInstruction(OpCode.CALL_METHOD, _currentScope, _currentToken); new Operand(instruction, Register.Member); new Operand(instruction, functionSymbol); } }
/// <summary> /// Registers a native function that can be imported and used by script processes. /// </summary> public void RegisterNativeFunction(string identifier, FunctionDelegate functionDelegate, DataTypeValue returnType, params DataTypeValue[] parameterTypeList) { string fullName = identifier + "("; for (int i = 0; i < parameterTypeList.Length; i++) fullName += parameterTypeList[i] + (i < parameterTypeList.Length - 1 ? "," : ""); fullName += ")"; _nativeFunctions.Add(fullName, new NativeFunction(identifier, functionDelegate, returnType, parameterTypeList)); }
/// <summary> /// Parses a lower-top level expression. The lower-top level is responsible for parsing /// any leaf expression and relational operators. /// </summary> /// <returns>The data type this expression evaluates to.</returns> private DataTypeValue ParseSubExpression() { // Parse the left hand leaf expression. DataTypeValue leafDataType1 = ParseLeafExpression(); // Parse any subsequent leaf expressions that are seperated // by logical operators. while (true) { // Check that the next token is relational. Token operatorToken = LookAheadToken(); if (operatorToken.IsRelational == false) break; NextToken(); // Parse the right hand lead expression. DataTypeValue leafDataType2 = ParseLeafExpression(); // Check the new data type can be cast to the main data type if (CanImplicitlyCast(leafDataType1, leafDataType2) == true) { // Check the data types are valid with this operator. if (OperatorDataTypeValid(leafDataType1, operatorToken.ID) == false) Error(ErrorCode.InvalidDataType, "Operator \"" + operatorToken.Ident + "\" can't be applied to data type \"" + leafDataType1.ToString() + "\"", false, 1); // We only want to emit byte code on the second pass. if (_currentPass == 1) { // Pop the result of the second sub expression into arithmetic // register 1. Instruction instruction = CreateInstruction(OpCodeByType(leafDataType2, OpCodeType.POP), _currentScope, _currentToken); new Operand(instruction, Register.Arithmetic2); // Pop the result of the first sub expression into arithmetic // register 2. instruction = CreateInstruction(OpCodeByType(leafDataType1, OpCodeType.POP), _currentScope, _currentToken); new Operand(instruction, Register.Arithmetic1); #region Relational Byte Code Emiting // Cast rvalue into lvalues type. if (leafDataType1 != leafDataType2) { instruction = CreateInstruction(OpCodeByType(leafDataType1, OpCodeType.CAST), _currentScope, _currentToken); new Operand(instruction, Register.Arithmetic2); } // Compare the results. instruction = CreateInstruction(OpCodeByType(leafDataType1, OpCodeType.CMP), _currentScope, _currentToken); new Operand(instruction, Register.Arithmetic1); new Operand(instruction, Register.Arithmetic2); // Create equality byte code based on the given operator. switch (operatorToken.ID) { case TokenID.OpEqual: instruction = CreateInstruction(OpCode.IS_EQ, _currentScope, _currentToken); break; case TokenID.OpGreater: instruction = CreateInstruction(OpCode.IS_G, _currentScope, _currentToken); break; case TokenID.OpGreaterEqual: instruction = CreateInstruction(OpCode.IS_GE, _currentScope, _currentToken); break; case TokenID.OpLess: instruction = CreateInstruction(OpCode.IS_L, _currentScope, _currentToken); break; case TokenID.OpLessEqual: instruction = CreateInstruction(OpCode.IS_LE, _currentScope, _currentToken); break; case TokenID.OpNotEqual: instruction = CreateInstruction(OpCode.IS_NE, _currentScope, _currentToken); break; } #endregion } // Data type is now boolean due to the comparison operators. leafDataType1 = new DataTypeValue(DataType.Bool, false, false); } else Error(ErrorCode.InvalidCast, "Can't implicitly cast between \"" + leafDataType1.ToString() + "\" and \"" + leafDataType2.ToString() + "\"", false, 1); } return leafDataType1; }
/// <summary> /// Checks if 2 data types can be used together without the need /// for manual casting. /// </summary> /// <param name="dest">Destination data type.</param> /// <param name="src">Source data type.</param> /// <returns>True if types can be used together.</returns> private bool CanImplicitlyCast(DataTypeValue dest, DataTypeValue src) { if (_currentPass == 0) return true; // Assume everything can cast when in the first pass. if ((dest.IsArray != src.IsArray || dest.IsReference != src.IsReference) && src.DataType != DataType.Null) return false; if (dest == src || dest.DataType == DataType.Null || src.DataType == DataType.Null) return true; switch (dest.DataType) { case DataType.Bool: if (src.DataType == DataType.Bool || src.DataType == DataType.Byte || src.DataType == DataType.Double || src.DataType == DataType.Float || src.DataType == DataType.Int || src.DataType == DataType.Long || src.DataType == DataType.Short || src.DataType == DataType.String) return true; break; case DataType.Byte: if (src.DataType == DataType.Bool || src.DataType == DataType.Byte || src.DataType == DataType.Double || src.DataType == DataType.Float || src.DataType == DataType.Int || src.DataType == DataType.Long || src.DataType == DataType.Short || src.DataType == DataType.String) return true; break; case DataType.Float: if (src.DataType == DataType.Bool || src.DataType == DataType.Byte || src.DataType == DataType.Double || src.DataType == DataType.Float || src.DataType == DataType.Int || src.DataType == DataType.Long || src.DataType == DataType.Short || src.DataType == DataType.String) return true; break; case DataType.Double: if (src.DataType == DataType.Bool || src.DataType == DataType.Byte || src.DataType == DataType.Double || src.DataType == DataType.Float || src.DataType == DataType.Int || src.DataType == DataType.Long || src.DataType == DataType.Short || src.DataType == DataType.String) return true; break; case DataType.Int: if (src.DataType == DataType.Bool || src.DataType == DataType.Byte || src.DataType == DataType.Double || src.DataType == DataType.Float || src.DataType == DataType.Int || src.DataType == DataType.Long || src.DataType == DataType.Short || src.DataType == DataType.String) return true; break; case DataType.Long: if (src.DataType == DataType.Bool || src.DataType == DataType.Byte || src.DataType == DataType.Double || src.DataType == DataType.Float || src.DataType == DataType.Int || src.DataType == DataType.Long || src.DataType == DataType.Short || src.DataType == DataType.String) return true; break; case DataType.Short: if (src.DataType == DataType.Bool || src.DataType == DataType.Byte || src.DataType == DataType.Double || src.DataType == DataType.Float || src.DataType == DataType.Int || src.DataType == DataType.Long || src.DataType == DataType.Short || src.DataType == DataType.String) return true; break; case DataType.String: if (src.DataType == DataType.Bool || src.DataType == DataType.Byte || src.DataType == DataType.Double || src.DataType == DataType.Float || src.DataType == DataType.Int || src.DataType == DataType.Long || src.DataType == DataType.Short || src.DataType == DataType.String) return true; break; // These type's can't be converted to anything but themselfs. case DataType.Object: break; case DataType.Void: break; } return false; }
/// <summary> /// Retrieves a symbol from this symbols scope by checking its identifier /// with the given identifier, its type against the given type and its data /// type against a given data type. /// </summary> /// <param name="ident">Identifier to find symbol by.</param> /// <param name="type">Type of symbol to find symbol by.</param> /// <param name="dataType"></param> /// <returns>A symbol with the same indentifier as the one passed or null if one can't be found.</returns> public Symbol FindVariableSymbol(string ident, DataTypeValue dataType) { foreach (Symbol symbol in _symbolList) { if (symbol.Identifier == null) continue; if (symbol.Identifier.ToLower() == ident.ToLower() && symbol.Type == SymbolType.Variable && ((VariableSymbol)symbol).DataType == dataType) return symbol; } return null; }
/// <summary> /// Chooses the correct byte code based on the data type. /// </summary> /// <param name="type">Type to find operation code from.</param> /// <returns>The correct opcode based on the data type.</returns> private OpCode OpCodeByType(DataTypeValue type, OpCodeType opCodeType) { switch (opCodeType) { case OpCodeType.ALLOCATE_HEAP: switch (type.DataType) { case DataType.Null: return OpCode.ALLOCATE_HEAP_NULL; case DataType.Bool: return OpCode.ALLOCATE_HEAP_BOOL; case DataType.Byte: return OpCode.ALLOCATE_HEAP_BYTE; case DataType.Double: return OpCode.ALLOCATE_HEAP_DOUBLE; case DataType.Object: return OpCode.ALLOCATE_HEAP_Object; case DataType.Float: return OpCode.ALLOCATE_HEAP_FLOAT; case DataType.Int: return OpCode.ALLOCATE_HEAP_INT; case DataType.Long: return OpCode.ALLOCATE_HEAP_LONG; case DataType.Short: return OpCode.ALLOCATE_HEAP_SHORT; case DataType.String: return OpCode.ALLOCATE_HEAP_STRING; } break; case OpCodeType.CAST: switch (type.DataType) { case DataType.Null: return OpCode.CAST_NULL; case DataType.Bool: return OpCode.CAST_BOOL; case DataType.Byte: return OpCode.CAST_BYTE; case DataType.Double: return OpCode.CAST_DOUBLE; case DataType.Object: return OpCode.CAST_Object; case DataType.Float: return OpCode.CAST_FLOAT; case DataType.Int: return OpCode.CAST_INT; case DataType.Long: return OpCode.CAST_LONG; case DataType.Short: return OpCode.CAST_SHORT; case DataType.String: return OpCode.CAST_STRING; } break; case OpCodeType.CMP: // If its an array or reference we need to compare the // memory index not the value. if (type.IsArray == true || type.IsReference == true) { return OpCode.CMP_MEMORY_INDEX; } switch (type.DataType) { case DataType.Null: return OpCode.CMP_NULL; case DataType.Bool: return OpCode.CMP_BOOL; case DataType.Byte: return OpCode.CMP_BYTE; case DataType.Double: return OpCode.CMP_DOUBLE; case DataType.Object: return OpCode.CMP_Object; case DataType.Float: return OpCode.CMP_FLOAT; case DataType.Int: return OpCode.CMP_INT; case DataType.Long: return OpCode.CMP_LONG; case DataType.Short: return OpCode.CMP_SHORT; case DataType.String: return OpCode.CMP_STRING; } break; case OpCodeType.PUSH: // If its an array or reference we need to push the // memory index not the value. if (type.IsArray == true || type.IsReference == true) { return OpCode.PUSH_MEMORY_INDEX; } switch (type.DataType) { case DataType.Null: return OpCode.PUSH_NULL; case DataType.Bool: return OpCode.PUSH_BOOL; case DataType.Byte: return OpCode.PUSH_BYTE; case DataType.Double: return OpCode.PUSH_DOUBLE; case DataType.Object: return OpCode.PUSH_OBJECT; case DataType.Float: return OpCode.PUSH_FLOAT; case DataType.Int: return OpCode.PUSH_INT; case DataType.Long: return OpCode.PUSH_LONG; case DataType.Short: return OpCode.PUSH_SHORT; case DataType.String: return OpCode.PUSH_STRING; } break; case OpCodeType.POP: // If its an array or reference we need to pop the // memory index not the value. if (type.IsArray == true || type.IsReference == true) { return OpCode.POP_MEMORY_INDEX; } switch (type.DataType) { case DataType.Null: return OpCode.POP_NULL; case DataType.Bool: return OpCode.POP_BOOL; case DataType.Byte: return OpCode.POP_BYTE; case DataType.Double: return OpCode.POP_DOUBLE; case DataType.Object: return OpCode.POP_OBJECT; case DataType.Float: return OpCode.POP_FLOAT; case DataType.Int: return OpCode.POP_INT; case DataType.Long: return OpCode.POP_LONG; case DataType.Short: return OpCode.POP_SHORT; case DataType.String: return OpCode.POP_STRING; } break; case OpCodeType.MOV: // If its an array or reference we need to move the // memory index not the value. if (type.IsArray == true || type.IsReference == true) { return OpCode.MOV_MEMORY_INDEX; } switch (type.DataType) { case DataType.Bool: return OpCode.MOV_BOOL; case DataType.Byte: return OpCode.MOV_BYTE; case DataType.Double: return OpCode.MOV_DOUBLE; case DataType.Object: return OpCode.MOV_OBJECT; case DataType.Null: return OpCode.MOV_NULL; case DataType.Float: return OpCode.MOV_FLOAT; case DataType.Int: return OpCode.MOV_INT; case DataType.Long: return OpCode.MOV_LONG; case DataType.Short: return OpCode.MOV_SHORT; case DataType.String: return OpCode.MOV_STRING; } break; case OpCodeType.MUL: switch (type.DataType) { case DataType.Byte: return OpCode.MUL_BYTE; case DataType.Double: return OpCode.MUL_DOUBLE; case DataType.Float: return OpCode.MUL_FLOAT; case DataType.Int: return OpCode.MUL_INT; case DataType.Long: return OpCode.MUL_LONG; case DataType.Short: return OpCode.MUL_SHORT; } break; case OpCodeType.DIV: switch (type.DataType) { case DataType.Byte: return OpCode.DIV_BYTE; case DataType.Double: return OpCode.DIV_DOUBLE; case DataType.Float: return OpCode.DIV_FLOAT; case DataType.Int: return OpCode.DIV_INT; case DataType.Long: return OpCode.DIV_LONG; case DataType.Short: return OpCode.DIV_SHORT; } break; case OpCodeType.ADD: switch (type.DataType) { case DataType.Byte: return OpCode.ADD_BYTE; case DataType.Double: return OpCode.ADD_DOUBLE; case DataType.Float: return OpCode.ADD_FLOAT; case DataType.Int: return OpCode.ADD_INT; case DataType.Long: return OpCode.ADD_LONG; case DataType.Short: return OpCode.ADD_SHORT; case DataType.String: return OpCode.ADD_STRING; } break; case OpCodeType.SUB: switch (type.DataType) { case DataType.Byte: return OpCode.SUB_BYTE; case DataType.Double: return OpCode.SUB_DOUBLE; case DataType.Float: return OpCode.SUB_FLOAT; case DataType.Int: return OpCode.SUB_INT; case DataType.Long: return OpCode.SUB_LONG; case DataType.Short: return OpCode.SUB_SHORT; } break; case OpCodeType.INC: switch (type.DataType) { case DataType.Byte: return OpCode.INC_BYTE; case DataType.Double: return OpCode.INC_DOUBLE; case DataType.Float: return OpCode.INC_FLOAT; case DataType.Int: return OpCode.INC_INT; case DataType.Long: return OpCode.INC_LONG; case DataType.Short: return OpCode.INC_SHORT; } break; case OpCodeType.DEC: switch (type.DataType) { case DataType.Byte: return OpCode.DEC_BYTE; case DataType.Double: return OpCode.DEC_DOUBLE; case DataType.Float: return OpCode.DEC_FLOAT; case DataType.Int: return OpCode.DEC_INT; case DataType.Long: return OpCode.DEC_LONG; case DataType.Short: return OpCode.DEC_SHORT; } break; case OpCodeType.NEG: switch (type.DataType) { case DataType.Byte: return OpCode.NEG_BYTE; case DataType.Double: return OpCode.NEG_DOUBLE; case DataType.Float: return OpCode.NEG_FLOAT; case DataType.Int: return OpCode.NEG_INT; case DataType.Long: return OpCode.NEG_LONG; case DataType.Short: return OpCode.NEG_SHORT; } break; case OpCodeType.ABS: switch (type.DataType) { case DataType.Byte: return OpCode.ABS_BYTE; case DataType.Double: return OpCode.ABS_DOUBLE; case DataType.Float: return OpCode.ABS_FLOAT; case DataType.Int: return OpCode.ABS_INT; case DataType.Long: return OpCode.ABS_LONG; case DataType.Short: return OpCode.ABS_SHORT; } break; } return OpCode.INVALID; }
/// <summary> /// Loads this scene node from a given binary reader. /// </summary> /// <param name="reader">Binary reader to load this scene node from.</param> public override void Load(BinaryReader reader) { // Load all the basic entity details. base.Load(reader); // Load all the scripted specific details. if (reader.ReadBoolean() == true) { string url = reader.ReadString(); // Load the objects script from the memory stream we dumped it into. ScriptProcess process = VirtualMachine.GlobalInstance.LoadScript(url); #region Property Loading int propertyCount = reader.ReadInt16(); for (int i = 0; i < propertyCount; i++) { string identifier = reader.ReadString(); DataTypeValue dataTypeValue = new DataTypeValue((DataType)reader.ReadByte(), false, false); bool isArray = reader.ReadBoolean(); // Look through the script processes global variables to see if this // variable exists. VariableSymbol variableSymbol = null; if (process != null) { foreach (Symbol symbol in process.GlobalScope.Symbols) { if (symbol is VariableSymbol == false || ((VariableSymbol)symbol).Identifier != identifier || ((VariableSymbol)symbol).DataType != dataTypeValue) continue; variableSymbol = symbol as VariableSymbol; break; } } // Quickly find out the meta data of this symbol. string editMethod = ""; if (variableSymbol != null) { foreach (Symbol subSymbol in variableSymbol.Symbols) if (subSymbol is MetaDataSymbol) if (((MetaDataSymbol)subSymbol).Identifier.ToLower() == "editmethod") { editMethod = ((MetaDataSymbol)subSymbol).Value; break; } } // Read in value based on data type. if (isArray == true && reader.ReadBoolean() == true) { int arrayLength = reader.ReadInt32(); int arrayIndex = process == null ? 0 : process[0].AllocateArray(dataTypeValue.DataType, arrayLength); for (int k = 0; k < arrayLength; k++) { switch (dataTypeValue.DataType) { case DataType.Bool: { bool value = reader.ReadBoolean(); if (process != null && variableSymbol != null) process[0].SetArrayElement(arrayIndex, k, value); } break; case DataType.Byte: { byte value = reader.ReadByte(); if (process != null && variableSymbol != null) process[0].SetArrayElement(arrayIndex, k, value); } break; case DataType.Double: { double value = reader.ReadDouble(); if (process != null && variableSymbol != null) process[0].SetArrayElement(arrayIndex, k, value); } break; case DataType.Float: { float value = reader.ReadSingle(); if (process != null && variableSymbol != null) process[0].SetArrayElement(arrayIndex, k, value); } break; case DataType.Int: { int value = reader.ReadInt32(); if (process != null && variableSymbol != null) process[0].SetArrayElement(arrayIndex, k, value); } break; case DataType.Long: { long value = reader.ReadInt64(); if (process != null && variableSymbol != null) process[0].SetArrayElement(arrayIndex, k, value); } break; case DataType.Short: { short value = reader.ReadInt16(); if (process != null && variableSymbol != null) process[0].SetArrayElement(arrayIndex, k, value); } break; case DataType.String: { string value = reader.ReadString(); if (process != null && variableSymbol != null) process[0].SetArrayElement(arrayIndex, i, value); } break; case DataType.Object: { if (reader.ReadBoolean() == true) { int type = reader.ReadByte(); if (type == 0) { string imageUrl = reader.ReadString(); int cellWidth = reader.ReadInt32(); int cellHeight = reader.ReadInt32(); int hSpacing = reader.ReadInt16(); int vSpacing = reader.ReadInt16(); if (process != null && variableSymbol != null) process[0].SetArrayElement(arrayIndex, i, new Fusion.Engine.ScriptingFunctions.ImageScriptObject(GraphicsManager.LoadImage(imageUrl, cellWidth, cellHeight, hSpacing, vSpacing, 0))); } else if (type == 1) { string soundUrl = reader.ReadString(); int freq = reader.ReadInt32(); float innerRadius = reader.ReadSingle(); float outerRadius = reader.ReadSingle(); bool loop = reader.ReadBoolean(); float pan = reader.ReadSingle(); float volume = reader.ReadSingle(); bool streaming = reader.ReadBoolean(); bool positional = reader.ReadBoolean(); Audio.SoundFlags flags = 0; if (streaming == true) flags |= Audio.SoundFlags.Streamed; if (positional == true) flags |= Audio.SoundFlags.Positional; Audio.Sound sound = Audio.AudioManager.LoadSound(soundUrl, flags); sound.Frequency = freq; sound.InnerRadius = innerRadius; sound.OuterRadius = outerRadius; sound.Looping = loop; sound.Pan = pan; sound.Volume = volume; if (process != null && variableSymbol != null) process[0].SetArrayElement(arrayIndex, i, new Fusion.Engine.ScriptingFunctions.SoundScriptObject(sound)); } } } break; } } } else { switch (dataTypeValue.DataType) { case DataType.Bool: { bool value = reader.ReadBoolean(); if (process != null && variableSymbol != null) process[0].SetGlobalVariable(identifier, value); } break; case DataType.Byte: { byte value = reader.ReadByte(); if (process != null && variableSymbol != null) process[0].SetGlobalVariable(identifier, value); } break; case DataType.Double: { double value = reader.ReadDouble(); if (process != null && variableSymbol != null) process[0].SetGlobalVariable(identifier, value); } break; case DataType.Float: { float value = reader.ReadSingle(); if (process != null && variableSymbol != null) process[0].SetGlobalVariable(identifier, value); } break; case DataType.Int: { int value = reader.ReadInt32(); if (process != null && variableSymbol != null) process[0].SetGlobalVariable(identifier, value); } break; case DataType.Long: { long value = reader.ReadInt64(); if (process != null && variableSymbol != null) process[0].SetGlobalVariable(identifier, value); } break; case DataType.Short: { short value = reader.ReadInt16(); if (process != null && variableSymbol != null) process[0].SetGlobalVariable(identifier, value); } break; case DataType.String: { string value = reader.ReadString(); if (process != null && variableSymbol != null) process[0].SetGlobalVariable(identifier, value); } break; case DataType.Object: { if (reader.ReadBoolean() == true) { int type = reader.ReadByte(); if (type == 0) { string imageUrl = reader.ReadString(); int cellWidth = reader.ReadInt32(); int cellHeight = reader.ReadInt32(); int hSpacing = reader.ReadInt16(); int vSpacing = reader.ReadInt16(); if (ResourceManager.ResourceExists(imageUrl) == true && process != null && variableSymbol != null) process[0].SetGlobalVariable(identifier, new Fusion.Engine.ScriptingFunctions.ImageScriptObject(GraphicsManager.LoadImage(imageUrl, cellWidth, cellHeight, hSpacing, vSpacing, 0))); } else if (type == 1) { string soundUrl = reader.ReadString(); int freq = reader.ReadInt32(); float innerRadius = reader.ReadSingle(); float outerRadius = reader.ReadSingle(); bool loop = reader.ReadBoolean(); float pan = reader.ReadSingle(); float volume = reader.ReadSingle(); bool streaming = reader.ReadBoolean(); bool positional = reader.ReadBoolean(); Audio.SoundFlags flags = 0; if (streaming == true) flags |= Audio.SoundFlags.Streamed; if (positional == true) flags |= Audio.SoundFlags.Positional; if (ResourceManager.ResourceExists(soundUrl) == true && process != null && variableSymbol != null) { Audio.Sound sound = Audio.AudioManager.LoadSound(soundUrl, flags); sound.Frequency = freq; sound.InnerRadius = innerRadius; sound.OuterRadius = outerRadius; sound.Looping = loop; sound.Pan = pan; sound.Volume = volume; process[0].SetGlobalVariable(identifier, new Fusion.Engine.ScriptingFunctions.SoundScriptObject(sound)); } } } } break; } } } #endregion // Now set the process! _process.Process = process; if (_process.Process != null) _process.Process.OnStateChange += OnStateChange; if (_process.Process != null && _process.Process.State != null) OnStateChange(_process.Process, _process.Process.State); SyncCollisionEvents(); } }
/// <summary> /// Converts this instance to a textural form. /// </summary> /// <returns>Textural form of this instance.</returns> public override string ToString() { DataTypeValue[] parameterTypes = new DataTypeValue[_parameterCount]; for (int i = 0; i < _parameterCount; i++) parameterTypes[i] = (_symbolList[i] as VariableSymbol).DataType; string fullName = _ident + "("; for (int i = 0; i < parameterTypes.Length; i++) fullName += parameterTypes[i] + (i < parameterTypes.Length - 1 ? "," : ""); fullName += ")"; return fullName; }