/// <summary> /// Initializes a new instance of this class and adds itself to /// the given scope's symbol list. /// </summary> /// <param name="scope">Scope that this symbol is in.</param> public EnumerationSymbol(Symbol scope) { _scope = scope; if (scope != null) scope.AddSymbol(this); }
/// <summary> /// Initializes a new instance of this class and adds itself to /// the given scope's symbol list. /// </summary> /// <param name="scope">Scope that this symbol is in.</param> public StateSymbol(Symbol scope) { _scope = scope; if (scope != null) scope.AddSymbol(this); }
/// <summary> /// Initializes a new instance of this class and adds itself to /// the given scope's symbol list. /// </summary> /// <param name="scope">Scope that this symbol is in.</param> /// <param name="ident">Contents of this string.</param> public StringSymbol(Symbol scope, string ident) { _scope = scope; if (scope != null) scope.AddSymbol(this); _ident = ident; }
/// <summary> /// Parses a single statement. A statement encompesses the highest level of parsing, things like /// the while loop, the if statement and the return statement ... ecetera. /// </summary> private void ParseStatement() { // Check is is an empty statement. if (LookAheadToken() != null && LookAheadToken().ID == TokenID.CharSemiColon) { ExpectToken(TokenID.CharSemiColon); return; } // Check if there is an open block next, and if so parse it. if (LookAheadToken().ID == TokenID.CharOpenBrace) { ParseBlock(TokenID.CharOpenBrace, TokenID.CharCloseBrace); return; } // Check if we have a chunk of meta data next. bool foundThisParse = true; if (LookAheadToken().ID == TokenID.CharOpenBracket) { _lastMetaDataSymbol = null; foundThisParse = true; ParseMetaData(); } // Emit a enter statement opcode if in debug. bool isBreakpoint = (LookAheadToken().ID == TokenID.KeywordBreakpoint); if ((_compileFlags & CompileFlags.Debug) != 0 && _currentPass == 1 && isBreakpoint == false) CreateInstruction(OpCode.ENTER_STATEMENT, _currentScope, (Token)_tokenList[_tokenIndex]); // Parse based on starting token. switch (NextToken().ID) { // General statements. case TokenID.KeywordDo: ParseDo(); break; case TokenID.KeywordEnum: ParseEnum(); break; case TokenID.KeywordIf: ParseIf(); break; case TokenID.KeywordLock: ParseLock(); break; case TokenID.KeywordAtom: ParseAtom(); break; case TokenID.KeywordReturn: ParseReturn(); break; case TokenID.KeywordSwitch: ParseSwitch(); break; case TokenID.KeywordState: ParseState(); break; case TokenID.KeywordWhile: ParseWhile(); break; case TokenID.KeywordFor: ParseFor(); break; case TokenID.KeywordGoto: ParseGoto(); break; case TokenID.KeywordBreak: ParseBreak(); break; case TokenID.KeywordContinue: ParseContinue(); break; case TokenID.KeywordGotoState: ParseGotoState(); break; case TokenID.KeywordBreakpoint: ParseBreakPoint(); break; case TokenID.KeywordNamespace: ParseNamespace(); break; case TokenID.KeywordEditor: case TokenID.KeywordEngine: ParseState(); break; /* case TokenID.KeywordStruct: ParseStruct(); break; case TokenID.KeywordClass: ParseClass(); break; */ // Variable / function declarations. case TokenID.KeywordBool: case TokenID.KeywordByte: case TokenID.KeywordDouble: case TokenID.KeywordObject: case TokenID.KeywordFloat: case TokenID.KeywordInt: case TokenID.KeywordLong: case TokenID.KeywordShort: case TokenID.KeywordString: case TokenID.KeywordVoid: case TokenID.KeywordConst: case TokenID.KeywordStatic: case TokenID.KeywordProperty: case TokenID.KeywordImport: case TokenID.KeywordExport: case TokenID.KeywordThread: case TokenID.KeywordPublic: case TokenID.KeywordProtected: case TokenID.KeywordPrivate: case TokenID.KeywordEvent: case TokenID.KeywordConsole: int offset = 0; if (_currentToken.ID == TokenID.KeywordPublic || _currentToken.ID == TokenID.KeywordProtected || _currentToken.ID == TokenID.KeywordPrivate) offset = 1; if (_currentToken.ID == TokenID.KeywordConsole || _currentToken.ID == TokenID.KeywordEvent || _currentToken.ID == TokenID.KeywordExport || _currentToken.ID == TokenID.KeywordImport || _currentToken.ID == TokenID.KeywordThread || LookAheadToken(2 + offset).ID == TokenID.CharOpenParenthesis) ParseFunction(); // If its not parenthesis its a variable declaration. else ParseVariable(); break; // Assignments and function calls. case TokenID.TypeIdentifier: // Check if its a label. if (LookAheadToken().ID == TokenID.CharColon) ParseLabel(); // See if its a function or assignment. else { int tokenIndex = _tokenIndex; Token currentToken = _currentToken; ResolveMemberScope(); // Ignore any array accessors. if (LookAheadToken().ID == TokenID.CharOpenBracket) { NextToken(); int depth = 1; while (depth != 0) { NextToken(); if (_currentToken.ID == TokenID.CharOpenBracket) depth++; else if (_currentToken.ID == TokenID.CharCloseBracket) depth--; } //ExpectToken(TokenID.CharCloseBracket); } // Function if (LookAheadToken().ID == TokenID.CharOpenParenthesis) { string functionName = _currentToken.Ident; _tokenIndex = tokenIndex; _currentToken = currentToken; // Check we are in a valid scope. if (_currentScope.Type != SymbolType.Function || _currentScope == _globalScope) Error(ErrorCode.InvalidScope, "Function calls are only valid inside a function's or event's scope.", false, 0); DataTypeValue functionDataType = ParseFunctionCall(); // Do we have a member call after? if (LookAheadToken().ID == TokenID.OpMemberResolver) { NextToken(); // If its not an object how can we access it? if (functionDataType != new DataTypeValue(DataType.Object, false, false)) Error(ErrorCode.IllegalResolve, "Member accessing can only be preformed on objects.", false, 1); // Parse the cast. ExpectToken(TokenID.CharOpenParenthesis); if (NextToken().IsDataType == false) Error(ErrorCode.InvalidCast, "Attempt to cast member to unknown type.", false, 0); DataTypeValue castDataType = new DataTypeValue(DataTypeFromKeywordToken(_currentToken.ID), false, false); // Is it an array? if (LookAheadToken().ID == TokenID.CharCloseBracket) { NextToken(); ExpectToken(TokenID.CharCloseBracket); castDataType.IsArray = true; } ExpectToken(TokenID.CharCloseParenthesis); ExpectToken(TokenID.TypeIdentifier); if (LookAheadToken().ID == TokenID.CharOpenParenthesis) ParseMemberFunctionCall(_currentToken.Ident, castDataType); else ParseMemberAssignment(_currentToken.Ident, castDataType); } ExpectToken(TokenID.CharSemiColon); } // Member function / assignment? else if (LookAheadToken().ID == TokenID.OpMemberResolver) { ParseMemberAccessor(); ExpectToken(TokenID.CharSemiColon); } // Assignment. else { _tokenIndex = tokenIndex; _currentToken = currentToken; ParseAssignment(); ExpectToken(TokenID.CharSemiColon); } } break; // Its none of the above its invalid. default: Error(ErrorCode.UnexpectedToken, "Encountered unexpected token \"" + _currentToken.ToString() + "\"", false, 0); break; } // Emit a exit statement opcode if in debug. if ((_compileFlags & CompileFlags.Debug) != 0 && _currentPass == 1 && isBreakpoint == false) CreateInstruction(OpCode.EXIT_STATEMENT, _currentScope, _currentToken); // If we read in any meta data associated with this statement then destroy it now. if (_metaDataList.Count > 0 && foundThisParse == true) { Symbol symbolToAttachTo = null; if (_lastMetaDataSymbol != null) symbolToAttachTo = _lastMetaDataSymbol; else symbolToAttachTo = _currentScope; if (_currentPass == 0) { foreach (MetaDataSymbol metaDataSymbol in _metaDataList) { metaDataSymbol.Scope = symbolToAttachTo; symbolToAttachTo.AddSymbol(metaDataSymbol); } } _metaDataList.Clear(); } }
/// <summary> /// Parses a 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> /// Initializes a new instance of this class with the given identifier, /// and add's itself to the given scope's symbol list. /// </summary> /// <param name="ident">Identifier used to identify this symbol.</param> /// <param name="scope">Scope that symbol is in.</param> public FunctionSymbol(string ident, Symbol scope) { _ident = ident; _scope = scope; if (scope != null) scope.AddSymbol(this); }
/// <summary> /// Initializes a new instance of this class and adds itself to /// the given scope's symbol list. /// </summary> /// <param name="scope">Scope that this symbol is in.</param> /// <param name="ident">Name of this meta data.</param> /// <param name="value">Value of this meta data.</param> public MetaDataSymbol(Symbol scope, string ident, string value) { _scope = scope; if (scope != null) scope.AddSymbol(this); _ident = ident; _value = value; }
/// <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> /// 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> /// Adds the given symbol and any sub symbols /// into the compiled symbol list. /// </summary> private void CompileSymbol(Symbol symbol) { _compiledSymbolList.Add(symbol); foreach (Symbol subSymbol in symbol.Symbols) { // Jump target? if (subSymbol.Type == SymbolType.JumpTarget) continue; // Dpn't compile unused import functions. if (subSymbol.Type == SymbolType.Function && ((FunctionSymbol)subSymbol).IsImport == true && ((FunctionSymbol)subSymbol).IsUsed == false) continue; CompileSymbol(subSymbol); } }
/// <summary> /// Creates a new byte code instruction. /// </summary> /// <param name="opcode">Opcode of instruction.</param> /// <param name="scope">Scope that instruction is in.</param> /// <param name="token">Token with line / offset data of instruction.</param> /// <returns>A new byte code instruction.</returns> private Instruction CreateInstruction(OpCode opcode, Symbol scope, Token token) { return new Instruction(opcode, _overrideInstructionScope != null ? _overrideInstructionScope : scope, token); }
/// <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; }
public Operand(Instruction instruction, Symbol symbol) { _opType = OperandType.SymbolIndexTracker; _symbolIndexTracker = symbol; instruction[instruction.OperandCount] = this; }
/// <summary> /// Initializes a new instance of this class and adds itself to /// the given scope's symbol list. /// </summary> /// <param name="scope">Scope that this symbol is in.</param> public NamespaceSymbol(Symbol scope) { _scope = scope; if (scope != null) scope.AddSymbol(this); }
/// <summary> /// Adds a symbol to this symbols scope. /// </summary> /// <param name="symbol">Symbol to add to this symbols scope.</param> public void AddSymbol(Symbol symbol) { symbol.Index = _symbolList.Count; _symbolList.Add(symbol); }
/// <summary> /// Parses a namespace declaration. /// Syntax: /// "{" Block "}" /// </summary> private void ParseNamespace() { // Read in the identifier. ArrayList layers = new ArrayList(); while (true) { ExpectToken(TokenID.TypeIdentifier); layers.Add(_currentToken.Ident); if (LookAheadToken().ID == TokenID.CharPeriod) ExpectToken(TokenID.CharPeriod); else break; } Symbol symbol = null; if (_currentPass == 0) { Symbol lastSymbol = _currentScope; foreach (String layer in layers) { symbol = lastSymbol.FindSymbol(layer); if (symbol == null) { symbol = new NamespaceSymbol(lastSymbol); symbol.Identifier = layer; } lastSymbol = symbol; } symbol = lastSymbol; } else { Symbol lastSymbol = _currentScope; foreach (String layer in layers) lastSymbol = lastSymbol.FindSymbol(layer); symbol = lastSymbol; } // Parse the namespaces block. Symbol scope = _currentScope; _currentScope = symbol; ParseStatement(); _currentScope = scope; // And thats it! Simple huh? }
/// <summary> /// Initializes a new instance of this class and adds itself to /// the given scope's symbol list. /// </summary> /// <param name="scope">Scope that this symbol is in.</param> public VariableSymbol(Symbol scope) { if (scope == null) return; _scope = scope; scope.AddSymbol(this); }
/// <summary> /// Parses a state block. A state block is used to seperate events into specific /// states to better emulate a state based machine. /// Syntax: /// "state" Identifier Statement /// </summary> private void ParseState() { // Make sure we are in a state's scope. if (_currentScope != _globalScope) Error(ErrorCode.InvalidScope, "States can only be declared within the global scope.", false, 0); // See if this is the engine or editor default state. bool isEngineDefault = false; bool isEditorDefault = false; int flagCount = 0; while (_currentToken.ID != TokenID.KeywordState && EndOfTokenStream() != true) { switch (_currentToken.ID) { case TokenID.KeywordEngine: if (_defaultEngineState != null) Error(ErrorCode.DuplicateDefault, "Default engine state declared multiple times.", false, 0); if (isEngineDefault == true) Error(ErrorCode.DuplicateFlag, "\"" + _currentToken.Ident + "\" flag declared multiple times.", false, 0); isEngineDefault = true; break; case TokenID.KeywordEditor: if (_defaultEditorState != null) Error(ErrorCode.DuplicateDefault, "Default editor state declared multiple times.", false, 0); if (isEditorDefault == true) Error(ErrorCode.DuplicateFlag, "\"" + _currentToken.Ident + "\" flag declared multiple times.", false, 0); isEditorDefault = true; break; } flagCount++; NextToken(); } // Read in state keyword if there are previous flags. if (flagCount != 0) CheckToken(TokenID.KeywordState); // Validate the "state(Identifier)" section of the // the event declaration. ExpectToken(TokenID.TypeIdentifier); string stateIdent = _currentToken.Ident; // Create event's symbol. StateSymbol symbol = null; if (_currentPass == 0) { if (_currentScope.FindSymbol(stateIdent, SymbolType.State) != null) Error(ErrorCode.DuplicateSymbol, "State \"" + stateIdent + "\" declared multiple times.", false, 0); symbol = new StateSymbol(_currentScope); symbol.Identifier = stateIdent; symbol.IsEngineDefault = isEngineDefault; symbol.IsEditorDefault = isEditorDefault; if (isEngineDefault == true) _defaultEngineState = symbol; if (isEditorDefault == true) _defaultEditorState = symbol; } else if (_currentPass == 1) { symbol = _currentScope.FindSymbol(stateIdent, SymbolType.State) as StateSymbol; } // Read in this event's contents. _lastMetaDataSymbol = symbol; Symbol scope = _currentScope; _currentScope = symbol; ParseStatement(); _currentScope = scope; }
/// <summary> /// Initializes a new instruction and attachs it to the given scope. /// </summary> /// <param name="opCode">Operation code for this instruction.</param> /// <param name="scope">Scope this instruction should be added to.</param> /// <param name="token">Token to get debug infomation from.</param> public Instruction(OpCode opCode, Symbol scope, Token token) { scope.AddInstruction(this); _opCode = opCode; if (token != null) { _line = token.Line; _offset = token.Offset; _file = token.File; } }
/// <summary> /// Initializes a new jump target instance and adds itself to the given /// scope's symbol list. /// </summary> /// <param name="scope">Scope that symbol is in.</param> public JumpTargetSymbol(Symbol scope) { _scope = scope; if (scope != null) _instrIndex = scope.Instructions.Count; if (scope != null) scope.AddSymbol(this); }