/// <summary> /// Parses a variable declaration. A variable is a place in memory that is used /// to store a value used by other statements. /// Syntax: /// DataType { Identifier [ "[" Expression "]" ] [ "=" Expression ] [ "," ] } ";" /// </summary> private void ParseVariable() { // Make sure we are in a valid scope. if (_currentScope.Type != SymbolType.Function && _currentScope.Type != SymbolType.State && _currentScope.Type != SymbolType.Namespace) Error(ErrorCode.InvalidScope, "Variables can't be declared in this scope.", false, 0); // Check if we are in a states scope. bool inState = (_currentScope.Type == SymbolType.State); bool inNamespace = (_currentScope.Type == SymbolType.Namespace); bool inEnumeration = (_currentScope.Type == SymbolType.Enumeration); // Read in each flag. bool isStatic = false; bool isConst = false; bool isProperty = false; SymbolAccessModifier modifier = SymbolAccessModifier.Private; bool gotModifier = false; while (EndOfTokenStream() != true) { switch (_currentToken.ID) { case TokenID.KeywordStatic: if (isStatic == true) Error(ErrorCode.DuplicateFlag, "\"" + _currentToken.Ident + "\" flag declared multiple times.", false, 0); isStatic = true; break; case TokenID.KeywordConst: if (isConst == true) Error(ErrorCode.DuplicateFlag, "\"" + _currentToken.Ident + "\" flag declared multiple times.", false, 0); isConst = true; break; case TokenID.KeywordProperty: if (isProperty == true) Error(ErrorCode.DuplicateFlag, "\"" + _currentToken.Ident + "\" flag declared multiple times.", false, 0); isProperty = 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; default: goto outOfLoop; } NextToken(); } outOfLoop: // Get data type by the current tokens id. DataType dataType = DataTypeFromKeywordToken(_currentToken.ID); if (dataType == DataType.Void || dataType == DataType.Invalid) Error(ErrorCode.InvalidDataType, "Variable can't be declared as \"" + _currentToken.Ident + "\".", false, 0); // Check if its an array. bool isArray = false; if (LookAheadToken().ID == TokenID.CharOpenBracket) { ExpectToken(TokenID.CharOpenBracket); isArray = true; ExpectToken(TokenID.CharCloseBracket); } // Parse all variables seperated by a comma. while (true) { // Read in variable identifier and store it for later use. ExpectToken(TokenID.TypeIdentifier); string variableIdent = _currentToken.Ident; // Create a new variable symbol. VariableSymbol variableSymbol = null; if (_currentPass == 0) { if (_currentScope.FindSymbol(variableIdent, SymbolType.Variable) != null) Error(ErrorCode.DuplicateSymbol, "Variable \"" + variableIdent + "\" declared multiple times.", false, 0); variableSymbol = new VariableSymbol(_currentScope); variableSymbol.Identifier = variableIdent; variableSymbol.IsArray = isArray; variableSymbol.IsConstant = isConst; variableSymbol.IsProperty = isProperty; variableSymbol.AccessModifier = modifier; variableSymbol.DataType = new DataTypeValue(dataType, isArray, false); variableSymbol.VariableType = isConst == true ? VariableType.Constant : (_currentScope == _globalScope || inState == true || isStatic == true || inEnumeration == true || inNamespace == true ? VariableType.Global : VariableType.Local); if (variableSymbol.VariableType == VariableType.Constant || variableSymbol.VariableType == VariableType.Global) { variableSymbol.MemoryIndex = _memorySize; _memorySize++; } else { if (_currentScope.Type == SymbolType.Function) { variableSymbol.StackIndex = -(((FunctionSymbol)_currentScope).LocalDataSize + 2); ((FunctionSymbol)_currentScope).LocalDataSize++; } } } else if (_currentPass == 1) { variableSymbol = _currentScope.FindSymbol(variableIdent, SymbolType.Variable) as VariableSymbol; if (variableSymbol == null) variableSymbol = _globalScope.FindSymbol(variableIdent, SymbolType.Variable) as VariableSymbol; } _lastMetaDataSymbol = variableSymbol; // Check if there is an assignment. if (LookAheadToken().ID == TokenID.OpAssign) { NextToken(); // If we are a constant then see if we are being assigned a literal value. Token LAT = LookAheadToken(); if (isConst == true && (LAT.ID == TokenID.TypeBoolean || LAT.ID == TokenID.TypeByte || LAT.ID == TokenID.TypeDouble || LAT.ID == TokenID.TypeFloat || LAT.ID == TokenID.TypeInteger || LAT.ID == TokenID.TypeLong || LAT.ID == TokenID.TypeShort || LAT.ID == TokenID.TypeString) && LookAheadToken(2).ID == TokenID.CharSemiColon) { NextToken(); if (CanImplicitlyCast(variableSymbol.DataType, new DataTypeValue(DataTypeFromTypeToken(_currentToken.ID), false, false)) == true) variableSymbol.ConstToken = _currentToken; else Error(ErrorCode.InvalidDataType, "Unable to implicitly cast from type \"" + DataTypeFromTypeToken(_currentToken.ID).ToString() + "\" to type \"" + variableSymbol.DataType.ToString() + "\".", false, 1); } else { // This will temporarily change the state if the variable is static. Symbol previousScope = null; if (isStatic == true || inState == true || inNamespace == true || inEnumeration == true) { previousScope = _overrideInstructionScope; _overrideInstructionScope = _globalScope; } // Parse the assignment expression. DataTypeValue expressionType = ParseExpression(); if (CanImplicitlyCast(variableSymbol.DataType, expressionType) == true) { if (_currentPass == 1) { // Pop the value out of the stack and assign it to this variable. Instruction instruction = CreateInstruction(OpCodeByType(expressionType, OpCodeType.POP), _currentScope, _currentToken); new Operand(instruction, Register.Arithmetic1); // Cast arith register 1 into the variables type. if (variableSymbol.DataType != expressionType) { instruction = CreateInstruction(OpCodeByType(variableSymbol.DataType, OpCodeType.CAST), _currentScope, _currentToken); new Operand(instruction, Register.Arithmetic1); } // Move the resulting value (stored in arithmatic register 3) // into the variable. instruction = CreateInstruction(OpCodeByType(variableSymbol.DataType, OpCodeType.MOV), _currentScope, _currentToken); Operand op1 = null; if (variableSymbol.VariableType == VariableType.Global || variableSymbol.VariableType == VariableType.Constant) op1 = Operand.DirectMemoryOperand(instruction, variableSymbol.MemoryIndex); else op1 = Operand.DirectStackOperand(instruction, variableSymbol.StackIndex); new Operand(instruction, Register.Arithmetic1); } } else { Error(ErrorCode.InvalidDataType, "Unable to implicitly cast from type \"" + expressionType.ToString() + "\" to type \"" + variableSymbol.DataType.ToString() + "\".", false, 1); } // If we have changed the scope then change back. if (isStatic == true || inState == true || inNamespace == true || inEnumeration == true) { _overrideInstructionScope = previousScope; } } } else { if (isConst == true) Error(ErrorCode.IllegalAssignment, "Constant must be assigned a value.", false, 0); } // Read in comma or break out of loop if there isn't one. if (LookAheadToken().ID == TokenID.CharComma) NextToken(); else break; } // Read in semi-colon at end of declarations. ExpectToken(TokenID.CharSemiColon); }
/// <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> /// 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 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> /// 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> /// Parses a do looping statment. The do loop comes in 2 flavours do and do/while; The do /// loop will loop through the statements body for x amount of times, where x is the result /// of the expression. The Do/While loop works just like the While loop but gets evaluated /// at the end rather than the start. /// Syntax: /// "Do" ["(" Expression ")"] Statement [ "While" "(" Expression ")" ] /// </summary> private void ParseDo() { // Check we are in a valid scope. if (_currentScope.Type != SymbolType.Function || _currentScope == _globalScope) Error(ErrorCode.InvalidScope, "Do statments are only valid inside a function's or event's scope.", false, 0); bool whileLoop = true; VariableSymbol doTrackerVariable = null; Instruction instruction = null; // Bind the starting jump target. JumpTargetSymbol startJumpTarget = null, endJumpTarget = null; if (_currentPass == 1) { startJumpTarget = new JumpTargetSymbol(_currentScope); endJumpTarget = new JumpTargetSymbol(_currentScope); } // Check if there is an expression next, if there is its a do loop. if (LookAheadToken().ID == TokenID.CharOpenParenthesis) { // Create an internal variable to count number of loops. if (_currentPass == 0) { doTrackerVariable = new VariableSymbol(_currentScope); doTrackerVariable.Identifier = "$" + _internalVariableIndex++; doTrackerVariable.IsUsed = true; doTrackerVariable.DataType = new DataTypeValue(DataType.Int, false, false); doTrackerVariable.VariableType = VariableType.Local; if (_currentScope.Type == SymbolType.Function) { doTrackerVariable.StackIndex = -(((FunctionSymbol)_currentScope).LocalDataSize + 2); ((FunctionSymbol)_currentScope).LocalDataSize++; } } else { doTrackerVariable = _currentScope.FindSymbol("$" + _internalVariableIndex++, SymbolType.Variable) as VariableSymbol; // Reset the tracker variable to 0. instruction = CreateInstruction(OpCode.MOV_INT, _currentScope, _currentToken); Operand.DirectStackOperand(instruction, doTrackerVariable.StackIndex); new Operand(instruction, (long)0); } // Bind the jump target to here. if (_currentPass == 1) startJumpTarget.Bind(); // Read in do expression. whileLoop = false; NextToken(); DataTypeValue expressionDataType = ParseExpression(); ExpectToken(TokenID.CharCloseParenthesis); // Make sure we can cast between the expression type and boolean. if (!CanImplicitlyCast(new DataTypeValue(DataType.Int, false, false), expressionDataType)) Error(ErrorCode.InvalidCast, "Can't implicitly cast between \"Int\" and \"" + expressionDataType.ToString() + "\"", false, 0); // If we are in pass 2 emit byte code to check value. if (_currentPass == 1) { // Pop the amount of times to loop into the reserved register 1. instruction = CreateInstruction(OpCodeByType(expressionDataType, OpCodeType.POP), _currentScope, _currentToken); new Operand(instruction, Register.Reserved1); // If the value is not a long then cast it into one. if (expressionDataType != new DataTypeValue(DataType.Int, false, false)) { instruction = CreateInstruction(OpCode.CAST_INT, _currentScope, _currentToken); new Operand(instruction, Register.Reserved1); } // Compare the loop tracker to the number of times to loop. instruction = CreateInstruction(OpCode.CMP_INT, _currentScope, _currentToken); Operand.DirectStackOperand(instruction, doTrackerVariable.StackIndex); new Operand(instruction, Register.Reserved1); // If its above or equal to the number of times we want to loop for, then exit. instruction = CreateInstruction(OpCode.JMP_GE, _currentScope, _currentToken); new Operand(instruction, endJumpTarget); // Increment the loop tracker variable. instruction = CreateInstruction(OpCode.INC_INT, _currentScope, _currentToken); Operand.DirectStackOperand(instruction, doTrackerVariable.StackIndex); } } else { // Bind the jump target to here. if (_currentPass == 1) startJumpTarget.Bind(); } // Push a loop tracker onto the loop stack. if (_currentPass == 1) _loopTrackerStack.Push(new LoopTracker(startJumpTarget, endJumpTarget)); // Parse the body of this statement. bool indexerLoop = _insideIndexerLoop; int indexerIndex = _indexerLoopIndex; if (whileLoop == false) { _insideIndexerLoop = true; _indexerLoopIndex = _internalVariableIndex - 1; } ParseStatement(); _insideIndexerLoop = indexerLoop; _indexerLoopIndex = indexerIndex; // Pop the loop tracker of the loop stack if (_currentPass == 1) _loopTrackerStack.Pop(); // If its a while loop then read in the while expression and output evaluation code. if (whileLoop == true) { // Parse the expression. ExpectToken(TokenID.KeywordWhile); ExpectToken(TokenID.CharOpenParenthesis); DataTypeValue expressionDataType = ParseExpression(); ExpectToken(TokenID.CharCloseParenthesis); if (_currentPass == 1) { // Pop the result of the expression into arith register 1. instruction = CreateInstruction(OpCodeByType(expressionDataType, OpCodeType.POP), _currentScope, _currentToken); new Operand(instruction, Register.Arithmetic1); // Cast it to boolean if its not already boolean if (expressionDataType != new DataTypeValue(DataType.Bool, false, false)) { instruction = CreateInstruction(OpCode.CAST_BOOL, _currentScope, _currentToken); new Operand(instruction, Register.Arithmetic1); } // Compare the expression result to 0. instruction = CreateInstruction(OpCode.CMP_BOOL, _currentScope, _currentToken); new Operand(instruction, Register.Arithmetic1); new Operand(instruction, false); // Jump to finish jump target if comparison is equal to false. instruction = CreateInstruction(OpCode.JMP_EQ, _currentScope, _currentToken); new Operand(instruction, endJumpTarget); } } // Create an instruction to jump back to the start of the loop. if (_currentPass == 1) { instruction = CreateInstruction(OpCode.JMP, _currentScope, _currentToken); new Operand(instruction, startJumpTarget); // Bind the end jump target to here. endJumpTarget.Bind(); } }
/// <summary> /// Parses a enumeration declaration. An enumeration is a way of creating a list /// of sequential integer constants without having to create and assign them. /// Syntax: /// "enum" "{" { Identifier [ "=" Expression ] [ "," ] } "}" /// </summary> private void ParseEnum() { // Make sure we are in a valid scope. if (_currentScope != _globalScope && _currentScope.Type != SymbolType.Namespace) Error(ErrorCode.InvalidScope, "Enumerations are only valid in global scope.", false, 0); // Read in enumeration identifier. ExpectToken(TokenID.TypeIdentifier); // Create enumeration symbol. EnumerationSymbol mainEnumSymbol = null; if (_currentPass == 0) { if (_currentScope.FindSymbol(_currentToken.Ident) != null) Error(ErrorCode.DuplicateSymbol, "Encountered multiple declarations of the "+_currentToken.Ident+" symbol"); mainEnumSymbol = new EnumerationSymbol(_currentScope); mainEnumSymbol.Identifier = _currentToken.Ident; } else mainEnumSymbol = _currentScope.FindSymbol(_currentToken.Ident) as EnumerationSymbol; // Read in opening brace. ExpectToken(TokenID.CharOpenBrace); // Read in each constant and its expression. int indexValue = 1; while (true) { // Read in constant identifier. ExpectToken(TokenID.TypeIdentifier); string enumIdentifier = _currentToken.Ident; // Create constant variable to store value into. VariableSymbol enumSymbol = null; if (_currentPass == 0) { if (mainEnumSymbol.FindSymbol(_currentToken.Ident) != null) Error(ErrorCode.DuplicateSymbol, "Encountered multiple declarations of the " + _currentToken.Ident + " symbol"); enumSymbol = new VariableSymbol(mainEnumSymbol); enumSymbol.Identifier = enumIdentifier; enumSymbol.DataType = new DataTypeValue(DataType.Int, false, false); enumSymbol.IsConstant = true; } else enumSymbol = mainEnumSymbol.FindSymbol(enumIdentifier) as VariableSymbol; _lastMetaDataSymbol = enumSymbol; // Read in expression if its there. Instruction instruction; if (LookAheadToken().ID == TokenID.OpAssign) { NextToken(); Token LAT = LookAheadToken(); Token LALAT = LookAheadToken(2); if ((LAT.ID == TokenID.TypeBoolean || LAT.ID == TokenID.TypeByte || LAT.ID == TokenID.TypeDouble || LAT.ID == TokenID.TypeFloat || LAT.ID == TokenID.TypeInteger || LAT.ID == TokenID.TypeLong || LAT.ID == TokenID.TypeShort || LAT.ID == TokenID.TypeString) && (LALAT.ID == TokenID.CharComma || LALAT.ID == TokenID.CharCloseBrace)) { NextToken(); if (CanImplicitlyCast(enumSymbol.DataType, new DataTypeValue(DataTypeFromTypeToken(_currentToken.ID), false, false)) == true) enumSymbol.ConstToken = _currentToken; else Error(ErrorCode.InvalidDataType, "Unable to implicitly cast from type \"" + DataTypeFromTypeToken(_currentToken.ID).ToString() + "\" to type \"" + enumSymbol.DataType.ToString() + "\".", false, 1); } else { Symbol oldScope = _currentScope; _currentScope = _globalScope; // Parse the assignment expression. DataTypeValue assignmentType = ParseExpression(); if (CanImplicitlyCast(new DataTypeValue(DataType.Int, false, false), assignmentType) == true) { // We need to allocate some memory space. if (_currentPass == 0) { enumSymbol.MemoryIndex = _memorySize; _memorySize++; } if (_currentPass == 1) { // Pop the expression value into arith register 1. instruction = CreateInstruction(OpCodeByType(enumSymbol.DataType, OpCodeType.POP), _currentScope, _currentToken); new Operand(instruction, Register.Arithmetic1); // Move the value stored in arith register 1 into enums memory space. instruction = CreateInstruction(OpCodeByType(enumSymbol.DataType, OpCodeType.MOV), _currentScope, _currentToken); Operand.DirectMemoryOperand(instruction, enumSymbol.MemoryIndex); new Operand(instruction, Register.Arithmetic1); } } else Error(ErrorCode.InvalidDataType, "Enumeration const of type \"" + enumSymbol.DataType.ToString() + "\" can't store type \"" + assignmentType.ToString() + "\".", false, 0); _currentScope = oldScope; } } else enumSymbol.ConstToken = new Token(TokenID.TypeInteger, indexValue.ToString(), 0, 0, ""); // Update index number in a *2 fashion so it can // be used as a bitmask. indexValue *= 2; // Read in comma if it exists. if (LookAheadToken().ID == TokenID.CharComma) NextToken(); else break; } // Read in closing brace. ExpectToken(TokenID.CharCloseBrace); }
/// <summary> /// Invoked when a script wants to set the value of an indexed member of this object. /// </summary> /// <param name="thread">Script thread that invoked this function.</param> /// <param name="variable">Symbol describing the member that it wants set.</param> /// <param name="value">Value it wants the member to be set to.</param> /// <param name="index">Index of member to it wants set.</param> /// <returns>True if successfull or false if not.</returns> public virtual bool SetMember(ScriptThread thread, VariableSymbol variable, RuntimeValue value, RuntimeValue indexer) { return false; }
/// <summary> /// Compiles a script from a string into byte code. /// </summary> /// <param name="data">Data of script to compile.</param> /// <param name="flags">Bitmask of flags defining how the script should be compiled.</param> /// <param name="defineList">A list of defines to use while preprocessing script.</param> /// <param name="includePaths">A list of directory paths to use when looking for include files.</param> /// <param name="fileUrl">Contains the url of the file this data comes from.</param> /// <returns>The number of errors or warning this script has generated duren compilation.</returns> public int CompileString(string data, CompileFlags flags, Define[] defineList, object[] includePaths, string fileUrl) { #if !DEBUG try { #endif // Reset all variables used to store compilation details. _errorList.Clear(); _compiledSymbolList.Clear(); _compiledInstructionList.Clear(); _compiledDebugFileList.Clear(); _compiledDefineList.Clear(); _loopTrackerStack.Clear(); _metaDataList.Clear(); _currentToken = null; _currentPass = 0; _currentScope = null; _globalScope = new FunctionSymbol("$global", null); // Initialize global scope here. _memberScope = new FunctionSymbol("$member", null); // Initialize member scope here. _compileFlags = flags; _tokenList = null; _tokenIndex = 0; _memorySize = 1; // Reserve space for 'special' globals like 'this'. _internalVariableIndex = 0; _defaultEngineState = _defaultEditorState = null; _errorsOccured = false; _overrideInstructionScope = null; // Create the 'this' variable. VariableSymbol thisSymbol = new VariableSymbol(_globalScope); thisSymbol.DataType = new DataTypeValue(DataType.Object, false, false); thisSymbol.Identifier = "this"; thisSymbol.IsConstant = true; thisSymbol.IsUsed = true; thisSymbol.MemoryIndex = 0; thisSymbol.VariableType = VariableType.Constant; // Create a lexer and convert script into a list // of tokens. DebugLogger.WriteLog("Preforming lexical analysis on script."); Lexer lexer = new Lexer(); if (lexer.Analyse(data, _compileFlags, fileUrl) > 0) { foreach (CompileError error in lexer.ErrorList) _errorList.Add(error); } _tokenList = lexer.TokenList; // Add the script directory into the include path array. string includePath = Path.GetDirectoryName(fileUrl); string[] newIncludePaths = new string[includePaths.Length + 1]; includePaths.CopyTo(newIncludePaths, 0); newIncludePaths[newIncludePaths.Length - 1] = includePath; includePaths = newIncludePaths; // Create a pre-processor and process the token list DebugLogger.WriteLog("Preforming preprocessing on script."); PreProcessor preProcessor = new PreProcessor(); if (preProcessor.Process(_tokenList, _compileFlags, defineList, includePaths) > 0) { foreach (CompileError error in preProcessor.ErrorList) _errorList.Add(error); } _compiledDefineList = preProcessor.DefineList; _tokenList = preProcessor.TokenList; try { // Pass 0: Collect infomation. // Pass 1: Generate byte code. DebugLogger.WriteLog("Compiling script in 2 passes."); // Go over the source code in 2 passes. for (int pass = 0; pass < 2; pass++) { _currentPass = pass; _currentToken = null; _tokenIndex = 0; _currentScope = _globalScope; _internalVariableIndex = 0; // This needs to be reset so statements using // an internal variables can find them on the second pass. while (!EndOfTokenStream()) { try { ParseStatement(); } catch (CompilePanicModeException) { DebugLogger.WriteLog("Panic mode initialized in script."); // Don't do anything here, just allow the error to be // forgoten and carry on as normal. } // If there are any errors quit compilation now. if (_errorsOccured == true) throw new CompileBreakException(); } } // Yell at user if no default state has been declared. if (_defaultEngineState == null && (_compileFlags & CompileFlags.Library) == 0) Error(ErrorCode.MissingDefaultState, "Default engine state is missing."); } catch (CompileBreakException) { DebugLogger.WriteLog("Script compilation broken via CompileBreakException."); } // Check what errors occured. int fatalErrorCount = 0; int errorCount = 0; int warningCount = 0; int messageCount = 0; foreach (CompileError error in _errorList) { switch (error.AlertLevel) { case ErrorAlertLevel.Error: errorCount++; break; case ErrorAlertLevel.FatalError: fatalErrorCount++; break; case ErrorAlertLevel.Message: messageCount++; break; case ErrorAlertLevel.Warning: warningCount++; break; } DebugLogger.WriteLog(error.ToString(), LogAlertLevel.Warning); } DebugLogger.WriteLog("Script compiled with "+fatalErrorCount+" fatal errors, "+errorCount+" errors, "+warningCount+" warnings and "+messageCount+" messages."); // If there are any errors quit compilation now. if (_errorsOccured == true) return _errorList.Count; // Append an exit symbol onto the global scopes instruction list. CreateInstruction(OpCode.EXIT, _globalScope, _currentToken); DebugLogger.WriteLog("Optimizing and tweeking symbols and instructions."); // Compile symbol list. CompileSymbol(_globalScope); CompileSymbol(_memberScope); // Go through instruction list and replace placeholders with their values. int symbolIndex = 0; foreach (Symbol symbol in _compiledSymbolList) { symbol.Index = symbolIndex; symbolIndex++; // Update the entry point if this is a function or event. switch (symbol.Type) { case SymbolType.Function: ((FunctionSymbol)symbol).EntryPoint = _compiledInstructionList.Count; break; case SymbolType.Variable: bool check = true; if (symbol.Scope.Type == SymbolType.Function) if (((FunctionSymbol)symbol.Scope).IsImport == true) check = false; // Should we remove it rather than warning? if (((VariableSymbol)symbol).IsUsed == false && ((VariableSymbol)symbol).IsConstant == false && check == true) Warning(ErrorCode.UnusedVariable, "Variable \"" + symbol.Identifier + "\" is declared but never used."); break; } foreach (Instruction instruction in symbol.Instructions) { // Create a new debug file entry if this instruction // was generated in a previously unknown file. if (instruction.File != null && instruction.File != "") { bool found = false; foreach (string file in _compiledDebugFileList) if (file == instruction.File) found = true; if (found == false) _compiledDebugFileList.Add(instruction.File); } // Go through each operand attach to this instruction // and check for any trackers. foreach (Operand operand in instruction.Operands) { if (operand == null) continue; // Update operand based on type. switch (operand.OpType) { case OperandType.JumpTarget: operand.OpType = OperandType.InstrIndex; switch (symbol.Type) { case SymbolType.Function: operand.InstrIndex = ((FunctionSymbol)symbol).EntryPoint + operand.JumpTarget.InstrIndex; break; } break; case OperandType.SymbolIndexTracker: operand.OpType = OperandType.SymbolIndex; operand.SymbolIndex = _compiledSymbolList.IndexOf(operand.SymbolIndexTracker); break; } } _compiledInstructionList.Add(instruction); } } // Optimize this symbols instructions. (Currently somewhat error prone) //int instructionCount = _compiledInstructionList.Count; //ScriptOptimizer optimizer = new ScriptOptimizer(); //optimizer.Optimize(_compiledInstructionList, _compiledSymbolList); //_compiledInstructionList = optimizer.OptimizedInstructions; //_compiledSymbolList = optimizer.OptimizedSymbols; // int index = 0; //foreach (Instruction instr in _compiledInstructionList) //{ // System.Console.WriteLine("\t"+instr.Decompile()); // index++; //} #if !DEBUG } catch (Exception) { _errorList.Add(new CompileError(ErrorCode.InternalError, "An internal compiler error occured.", ErrorAlertLevel.FatalError, 0, 0, "")); } #endif return _errorList.Count; }
/// <summary> /// Invoked when a script wants to get the value of a member of this object. /// </summary> /// <param name="thread">Script thread that invoked this function.</param> /// <param name="variable">Symbol describing the member that it wants get.</param> /// <returns>True if successfull or false if not.</returns> public virtual bool GetMember(ScriptThread thread, VariableSymbol variable) { return false; }
/// <summary> /// Resolves a local variable to a runtime value. /// </summary> /// <param name="variableSymbol">Symbol of variable to resolve.</param> /// <returns>Resolved local's runtime value.</returns> private RuntimeValue ResolveLocal(VariableSymbol variableSymbol) { RuntimeValue variableValue = null; if (variableSymbol.VariableType == VariableType.Global) variableValue = _process.MemoryHeap[variableSymbol.MemoryIndex]; else if (variableSymbol.VariableType == VariableType.Local) variableValue = _runtimeStack[variableSymbol.StackIndex]; else if (variableSymbol.VariableType == VariableType.Parameter) variableValue = _runtimeStack[variableSymbol.StackIndex];// ResolveParameter(_callingFunction.Symbols.Count - (_callingFunction.Symbols.IndexOf(variableSymbol) - 1)); return variableValue; }
/// <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."); }
/// <summary> /// Invoked when a script wants to get the value of an indexed member of this object. /// </summary> /// <param name="thread">Script thread that invoked this function.</param> /// <param name="variable">Symbol describing the member that it wants get.</param> /// <param name="index">Index of member it want to get.</param> /// <returns>True if successfull or false if not.</returns> public override bool SetMember(ScriptThread thread, VariableSymbol variable, RuntimeValue value, RuntimeValue indexer) { if (_nativeObject is ScriptedEntityNode) { // Check the script process is valid. ScriptProcess process = ((ScriptedEntityNode)_nativeObject).ScriptProcess; if (process == null) return false; // See if we can get the symbol they are after. VariableSymbol processSymbol = process.GetSymbol(variable.Identifier, SymbolType.Variable) as VariableSymbol; if (processSymbol == null) return false; // Grab the variable runtime value. (This can probably be optimized). RuntimeValue runtimeValue = process[0].GetRuntimeValueGlobal(processSymbol.Identifier); // Check its public. if (processSymbol.AccessModifier != SymbolAccessModifier.Public) return false; // Check data types are correct. if (variable.DataType.DataType != processSymbol.DataType.DataType || processSymbol.DataType.IsArray != true) return false; // Grab the arrays variable. RuntimeValue indexValue = process[0].GetRuntimeValueArrayElement(runtimeValue.MemoryIndex, indexer.IntegerLiteral); // Copy value. RuntimeValue copiedValue = process[0].CopyValueFromThread(value, process[0], thread); // Set value (this can be optimized). process[0].SetGlobalVariable(processSymbol.Identifier, copiedValue); } else if (_nativeObject is EntityNode) { } else if (_nativeObject is EmitterNode) { } else if (_nativeObject is TilemapSegmentNode) { } else if ((_nativeObject as SceneNode) != null) { } return false; }
/// <summary> /// Invoked when a script wants to set the value of a member of this object. /// </summary> /// <param name="thread">Script thread that invoked this function.</param> /// <param name="variable">Symbol describing the member that it wants set.</param> /// <param name="value">Value it wants the member to be set to.</param> /// <returns>True if successfull or false if not.</returns> public override bool GetMember(ScriptThread thread, VariableSymbol variable) { if (_nativeObject is ScriptedEntityNode) { // Check the script process is valid. ScriptProcess process = ((ScriptedEntityNode)_nativeObject).ScriptProcess; if (process == null) return false; // See if we can get the symbol they are after. VariableSymbol processSymbol = process.GetSymbol(variable.Identifier, SymbolType.Variable) as VariableSymbol; if (processSymbol == null) return false; // Grab the variable runtime value. (This can probably be optimized). RuntimeValue runtimeValue= process[0].GetRuntimeValueGlobal(processSymbol.Identifier); // Check its public. if (processSymbol.AccessModifier != SymbolAccessModifier.Public) return false; // Check data types are correct. if (variable.DataType != processSymbol.DataType) return false; // Copy value into calling thread. if (variable.DataType.IsArray == true) { thread.Registers[(int)Register.Return].MemoryIndex = (short)thread.CopyValueArrayFromThread(runtimeValue, thread, process[0]); } else thread.Registers[(int)Register.Return] = thread.CopyValueFromThread(runtimeValue, thread, process[0]); // Set the return register to our newly copied value. // Return. return true; } else if (_nativeObject is EntityNode) { } else if (_nativeObject is EmitterNode) { } else if (_nativeObject is TilemapSegmentNode) { } else if ((_nativeObject as SceneNode) != null) { } return false; }