/// <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> /// Loads the byte code from a given binary reader. /// </summary> /// <param name="reader">BinaryReader to load byte code from.</param> private void LoadByteCode(BinaryReader reader) { // Check the header is correct if (reader.ReadByte() == 'C' && reader.ReadByte() == 'R' && reader.ReadByte() == 'X') { // Read in header variables. _compileFlags = (CompileFlags)reader.ReadInt32(); _internalVariableIndex = reader.ReadInt32(); _memorySize = reader.ReadInt32(); int globalScopeIndex = reader.ReadInt32(); int memberScopeIndex = reader.ReadInt32(); _defaultEngineStateIndex = reader.ReadInt32(); _defaultEditorStateIndex = reader.ReadInt32(); // Create a new memory heap of the correct size if (_memorySize > _memoryHeap.Length) _memoryHeap = new RuntimeValue[_memorySize]; for (int i = 0; i < _memorySize; i++) _memoryHeap[i] = new RuntimeValue(RuntimeValueType.Invalid); // Set the 'special' globals to their appropriate values. _memoryHeap[0].ValueType = RuntimeValueType.Object; _memoryHeap[0].ObjectIndex = 0; int defineCount = reader.ReadInt32(); int symbolCount = reader.ReadInt32(); int instructionCount = reader.ReadInt32(); int debugFileCount = 0; if ((_compileFlags & CompileFlags.Debug) != 0) debugFileCount = reader.ReadInt32(); // Read in debug file list. string[] debugFiles = new string[debugFileCount]; if ((_compileFlags & CompileFlags.Debug) != 0) { for (int i = 0; i < debugFileCount; i++) debugFiles[i] = reader.ReadString(); } // Read in the define list. for (int i = 0; i < defineCount; i++) { string ident = reader.ReadString(); TokenID valueID = (TokenID)reader.ReadInt32(); string value = reader.ReadString(); Define define = new Define(ident, value, valueID); _defineList.Add(define); } // Read in symbol list. _symbolList = new Symbol[symbolCount]; for (int i = 0; i < symbolCount; i++) { // Read in general details about symbol. SymbolType type = (SymbolType)reader.ReadByte(); string identifier = reader.ReadString(); int scopeIndex = reader.ReadInt32(); Symbol scope = null; Symbol symbol = null; if (scopeIndex != -1) scope = (Symbol)_symbolList[scopeIndex]; // Read in specialized details about symbol. switch (type) { case SymbolType.JumpTarget: continue; // Ignore jump targets. case SymbolType.Namespace: NamespaceSymbol namespaceSymbol = new NamespaceSymbol(scope); symbol = namespaceSymbol; break; case SymbolType.Enumeration: EnumerationSymbol enumSymbol = new EnumerationSymbol(scope); symbol = enumSymbol; break; case SymbolType.String: StringSymbol stringSymbol = new StringSymbol(scope, identifier); symbol = stringSymbol; break; case SymbolType.Function: FunctionSymbol functionSymbol = new FunctionSymbol(identifier, scope); symbol = functionSymbol; functionSymbol.EntryPoint = reader.ReadInt32(); functionSymbol.LocalDataSize = reader.ReadInt16(); functionSymbol.IsEvent = reader.ReadBoolean(); functionSymbol.IsConsole = reader.ReadBoolean(); functionSymbol.IsExport = reader.ReadBoolean(); functionSymbol.IsImport = reader.ReadBoolean(); functionSymbol.IsThreadSpawner = reader.ReadBoolean(); functionSymbol.IsMember = reader.ReadBoolean(); functionSymbol.ParameterCount = reader.ReadByte(); bool isArray = reader.ReadBoolean(); bool isReference = reader.ReadBoolean(); functionSymbol.ReturnType = new DataTypeValue((DataType)reader.ReadByte(), isArray, isReference); functionSymbol.AccessModifier = (SymbolAccessModifier)reader.ReadByte(); break; case SymbolType.State: StateSymbol stateSymbol = new StateSymbol(scope); symbol = stateSymbol; stateSymbol.IsEngineDefault = reader.ReadBoolean(); stateSymbol.IsEditorDefault = reader.ReadBoolean(); break; case SymbolType.Variable: VariableSymbol variableSymbol = new VariableSymbol(scope); symbol = variableSymbol; variableSymbol.DataType = new DataTypeValue((DataType)reader.ReadByte(), false, false); variableSymbol.DataType.IsReference = reader.ReadBoolean(); variableSymbol.IsArray = reader.ReadBoolean(); variableSymbol.DataType.IsArray = variableSymbol.IsArray; variableSymbol.IsConstant = reader.ReadBoolean(); variableSymbol.MemoryIndex = reader.ReadInt32(); variableSymbol.StackIndex = reader.ReadInt32(); variableSymbol.VariableType = (VariableType)reader.ReadByte(); variableSymbol.IsProperty = reader.ReadBoolean(); variableSymbol.AccessModifier = (SymbolAccessModifier)reader.ReadByte(); variableSymbol.ConstToken = new Token(TokenID.TypeIdentifier, reader.ReadString(), 0, 0, ""); break; case SymbolType.MetaData: MetaDataSymbol metaDataSymbol = new MetaDataSymbol(scope, identifier, ""); symbol = metaDataSymbol; metaDataSymbol.Value = reader.ReadString(); break; } symbol.Identifier = identifier; symbol.Index = i; _symbolList[i] = symbol; } // Retrieve global scope. _globalScope = _symbolList[globalScopeIndex] as FunctionSymbol; _memberScope = _symbolList[memberScopeIndex] as FunctionSymbol; //_currentState = _symbolList[_defaultEngineStateIndex] as StateSymbol; // Force this to be declared in the engine / editor. // Read in instruction list. _instructionList = new RuntimeInstruction[instructionCount]; for (int i = 0; i < instructionCount; i++) { // Read in instruction details and create a new instruction. OpCode opCode = (OpCode)reader.ReadByte(); int operandCount = reader.ReadByte(); RuntimeInstruction instruction = new RuntimeInstruction(opCode); _instructionList[i] = instruction; if ((_compileFlags & CompileFlags.Debug) != 0) { int fileIndex = reader.ReadSByte(); if (fileIndex != -1) instruction.File = debugFiles[fileIndex]; instruction.Offset = reader.ReadInt16(); instruction.Line = reader.ReadInt16(); } // Read in each operand attached to this instruction for (int k = 0; k < operandCount; k++) { // Read in general details about this operand and create // a new runtime value instance. RuntimeValueType opType = (RuntimeValueType)reader.ReadInt32(); RuntimeValue operand = new RuntimeValue(opType); instruction.Operands[instruction.OperandCount] = operand; instruction.OperandCount++; // Read in specialized info about this operand. switch(opType) { case RuntimeValueType.BooleanLiteral: operand.BooleanLiteral = reader.ReadBoolean(); break; case RuntimeValueType.ByteLiteral: operand.ByteLiteral = reader.ReadByte(); break; case RuntimeValueType.DirectMemory: operand.MemoryIndex = reader.ReadInt32(); break; case RuntimeValueType.DirectMemoryIndexed: operand.MemoryIndex = reader.ReadInt32(); operand.OffsetRegister = (Register)reader.ReadByte(); break; case RuntimeValueType.DirectStack: operand.StackIndex = reader.ReadInt32(); break; case RuntimeValueType.DirectStackIndexed: operand.StackIndex = reader.ReadInt32(); operand.OffsetRegister = (Register)reader.ReadByte(); break; case RuntimeValueType.DoubleLiteral: operand.DoubleLiteral = reader.ReadDouble(); break; case RuntimeValueType.FloatLiteral: operand.FloatLiteral = reader.ReadSingle(); break; case RuntimeValueType.IndirectMemory: operand.Register = (Register)reader.ReadByte(); break; case RuntimeValueType.IndirectMemoryIndexed: operand.Register = (Register)reader.ReadByte(); operand.OffsetRegister = (Register)reader.ReadByte(); break; case RuntimeValueType.IndirectStack: operand.Register = (Register)reader.ReadByte(); break; case RuntimeValueType.IndirectStackIndexed: operand.Register = (Register)reader.ReadByte(); operand.OffsetRegister = (Register)reader.ReadByte(); break; case RuntimeValueType.InstrIndex: operand.InstrIndex = reader.ReadInt32(); break; case RuntimeValueType.IntegerLiteral: operand.IntegerLiteral = reader.ReadInt32(); break; case RuntimeValueType.LongLiteral: operand.LongLiteral = reader.ReadInt64(); break; case RuntimeValueType.Register: operand.Register = (Register)reader.ReadByte(); break; case RuntimeValueType.ShortLiteral: operand.ShortLiteral = reader.ReadInt16(); break; case RuntimeValueType.SymbolIndex: operand.SymbolIndex = reader.ReadInt32(); operand.Symbol = (Symbol)_symbolList[operand.SymbolIndex]; if (operand.Symbol is StringSymbol) { operand.StringLiteral = operand.Symbol.Identifier; operand.ValueType = RuntimeValueType.StringLiteral; } break; } } } // Fill the member-function hash table. foreach (Symbol symbol in GlobalScope.Symbols) if (symbol != null && symbol.Type == SymbolType.Function && ((FunctionSymbol)symbol).AccessModifier == SymbolAccessModifier.Public) _memberFunctionHashTable.Add(symbol.ToString().ToLower(), symbol); } else throw new Exception("Unable to load script byte code, header is invalid."); }