Ejemplo n.º 1
0
 /// <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;
     }
 }
Ejemplo n.º 2
0
        /// <summary>
        ///		Parses a for loop, which is similar to the while statement but keeps the initialization,
        ///		incrementation and comparison in a more intuative format.
        ///		Syntax:
        ///			"For" "(" Initialization ";" Expression ";" Expression ")" Statement
        /// 
        ///		Output byte code example:
        ///         [initialization]
        ///     start:
        ///         [expression]
        /// 		pop a1
        ///			cmp a1, 0
        ///			jmp_eq exit
        ///         [statement block]
        ///         [increment]
        ///         jmp start
        ///     
        /// </summary>
        private void ParseFor()
        {
            // Check we are in a valid scope.
            if (_currentScope.Type != SymbolType.Function || _currentScope == _globalScope)
                Error(ErrorCode.InvalidScope, "For statements are only valid inside a function's or event's scope.", false, 0);

            // Declare some important variables.
            JumpTargetSymbol startJumpTarget = null;
            JumpTargetSymbol finishJumpTarget = null;
            JumpTargetSymbol incrementJumpTarget = null;
            Instruction instruction = null;

            // Create jump targets if we are in pass 2.
            if (_currentPass == 1)
            {
                startJumpTarget = new JumpTargetSymbol(_currentScope);
                incrementJumpTarget = new JumpTargetSymbol(_currentScope);
                finishJumpTarget = new JumpTargetSymbol(_currentScope);
            }

            // Read in the variable declaration.
            ExpectToken(TokenID.CharOpenParenthesis);
            NextToken();
            if (_currentToken.ID == TokenID.KeywordBool || _currentToken.ID == TokenID.KeywordByte ||
                _currentToken.ID == TokenID.KeywordDouble || _currentToken.ID == TokenID.KeywordObject ||
                _currentToken.ID == TokenID.KeywordFloat || _currentToken.ID == TokenID.KeywordInt ||
                _currentToken.ID == TokenID.KeywordLong || _currentToken.ID == TokenID.KeywordShort ||
                _currentToken.ID == TokenID.KeywordString || _currentToken.ID == TokenID.KeywordVoid ||
                _currentToken.ID == TokenID.KeywordConst || _currentToken.ID == TokenID.KeywordStatic)
                ParseVariable();

            // Its not a variable declaration so read it in as a assignment.
            else
                ParseAssignment();

            // Bind the starting target.
            if (_currentPass == 1)
                startJumpTarget.Bind();

            // Parse the expression block.
            DataTypeValue expressionType = ParseExpression();
            ExpectToken(TokenID.CharSemiColon);

            // Create the expression evaluation instruction's.
            if (_currentPass == 1)
            {
                // Pop the result of the expression into arith register 1.
                instruction = CreateInstruction(OpCodeByType(expressionType, OpCodeType.POP), _currentScope, _currentToken);
                new Operand(instruction, Register.Arithmetic1);

                // Cast it to boolean if its not already boolean.
                if (expressionType != new DataTypeValue(DataType.Bool, false, false))
                {
                    instruction = CreateInstruction(OpCode.CAST_BOOL, _currentScope, _currentToken);
                    new Operand(instruction, Register.Arithmetic1);
                }

                // Compare the result of the expression to false.
                instruction = CreateInstruction(OpCode.CMP_BOOL, _currentScope, _currentToken);
                new Operand(instruction, Register.Arithmetic1);
                new Operand(instruction, false);

                // If its equal jump out of this loop.
                instruction = CreateInstruction(OpCode.JMP_EQ, _currentScope, _currentToken);
                new Operand(instruction, finishJumpTarget);
            }

            // Skip the increment and parse the block.
            int incrementTokenIndex = _tokenIndex;
            Token incrementToken = _currentToken;
            while (EndOfTokenStream() == false && LookAheadToken().ID != TokenID.CharCloseParenthesis)
                NextToken();
            NextToken(); // Read in closing brace.

            // Push a loop tracker into the tracker stack.
            if (_currentPass == 1)
                _loopTrackerStack.Push(new LoopTracker(incrementJumpTarget, finishJumpTarget));

            // Parse the loop's body.
            ParseStatement();

            // Pop the loop tracker of the tracker state.
            if (_currentPass == 1)
                _loopTrackerStack.Pop();

            // Note down the current token index / token.
            int finishTokenIndex = _tokenIndex;
            Token finishToken = _currentToken;

            // Go back to the increment block and parse it.
            _tokenIndex = incrementTokenIndex;
            _currentToken = incrementToken;
            NextToken();

            // Bind the increment jump target.
            if (_currentPass == 1)
                incrementJumpTarget.Bind();

            // Parse the increment.
            ParseAssignment();

            // Go back to the end.
            _tokenIndex = finishTokenIndex;
            _currentToken = finishToken;

            // Jump to the start block.
            if (_currentPass == 1)
            {
                instruction = CreateInstruction(OpCode.JMP, _currentScope, _currentToken);
                new Operand(instruction, startJumpTarget);
            }

            // Bind the finishing target.
            if (_currentPass == 1)
                finishJumpTarget.Bind();
        }
Ejemplo n.º 3
0
        /// <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();
            }
        }
Ejemplo n.º 4
0
 /// <summary>
 ///		Returns the next token in the script and advances the stream, this method
 ///		will keep on lexing the stream if an invalid token is returned, the purpose of
 ///		this is to gracefully handle errors.
 /// </summary>
 /// <returns>Next token in script.</returns>
 private Token NextToken()
 {
     if (EndOfTokenStream() == true) return null;
     _currentToken = (Token)_tokenList[_tokenIndex];
     _tokenIndex++;
     return _currentToken;
 }
Ejemplo n.º 5
0
        /// <summary>
        ///		Parses the lowest level of an expression. The lowest level is respolsible
        ///		for parsing things such as literal values and function calls, it also
        ///		deals with unary, prefix and postfix operators.
        /// </summary>
        /// <returns>The data type this expression evaluates to.</returns>
        private DataTypeValue ParseFactorExpression()
        {
            // Declare a variable to store the data type in.
            DataTypeValue factorDataType = new DataTypeValue(DataType.Invalid, false, false);
            StringSymbol stringSymbol = null;

            #region Unary op
            // Check if there is a unary operator pending.
            bool unaryOpPending = false;
            Token unaryOpToken = LookAheadToken();
            if (unaryOpToken.ID == TokenID.OpAdd || unaryOpToken.ID == TokenID.OpSub ||
                unaryOpToken.ID == TokenID.OpLogicalNot || unaryOpToken.ID == TokenID.OpBitwiseNot ||
                unaryOpToken.ID == TokenID.OpIncrement || unaryOpToken.ID == TokenID.OpDecrement)
            {
                unaryOpPending = true;
                NextToken();
            }
            #endregion

            #region Cast Pending
            // Check if there is a explicit cast pending.
            bool castPending = false;
            DataTypeValue castType = new DataTypeValue(DataType.Invalid, false, false);
            if (LookAheadToken().ID == TokenID.CharOpenParenthesis)
            {
                castType.DataType = DataTypeFromKeywordToken(LookAheadToken(2).ID);
                if (castType.DataType != DataType.Invalid)
                {
                    NextToken(); // Read in open parenthesis.
                    NextToken(); // Read in cast keyword.
                    // Check for array.
                    if (LookAheadToken().ID == TokenID.CharOpenBracket)
                    {
                        NextToken();
                        castType.IsArray = true;
                        ExpectToken(TokenID.CharCloseBracket);
                    }
                    ExpectToken(TokenID.CharCloseParenthesis); // Read in closing parenthesis.
                    castPending = true;
                }
            }
            #endregion

            // Determine what factor type we are looking at
            // and act accordingly.
            NextToken();
            switch (_currentToken.ID)
            {
                #region Indexer
                case TokenID.KeywordIndexer:

                    // Check we are inside an indexer loop.
                    if (_insideIndexerLoop == false)
                        Error(ErrorCode.InvalidIndexer, "The Indexer keyword can only be used inside a valid indexable loop.");

                    // Set the factor data type as an integer (an indexer is always an integer).
                    factorDataType = new DataTypeValue(DataType.Int, false, false);

                    // Generate byte code to push indexer onto the stack.
                    if (_currentPass == 1)
                    {
                        VariableSymbol indexerVariable = _currentScope.FindSymbol("$" + _indexerLoopIndex, SymbolType.Variable) as VariableSymbol;
                        Instruction instruction = CreateInstruction(OpCodeByType(factorDataType, OpCodeType.PUSH), _currentScope, _currentToken);
                        Operand.DirectStackOperand(instruction, indexerVariable.StackIndex);
                    }
                    break;

                #endregion
                #region New
                case TokenID.KeywordNew:

                    // Read in identifier specifying what we wish to create a new
                    // instance of.
                    NextToken();

                    // Check if we are detailing with a data type.
                    DataTypeValue dataType = new DataTypeValue(DataTypeFromKeywordToken(_currentToken.ID), true, false);
                    factorDataType = dataType;
                    if (dataType.DataType != DataType.Invalid)
                    {

                        // Check this is an array.
                        if (LookAheadToken().ID == TokenID.CharOpenBracket)
                        {
                            // Read in opening bracket.
                            NextToken();

                            // Parse the size expression.
                            DataTypeValue indexDataType = ParseExpression();

                            // Make sure we can convert it to an integer index.
                            if (CanImplicitlyCast(new DataTypeValue(DataType.Int, false, false), indexDataType))
                            {
                                // If we are in pass 2 create the byte code to allocate a
                                // new array on the heap and associate it with this variable.
                                if (_currentPass == 1)
                                {
                                    // Pop the index value into arith register 2.
                                    Instruction instruction = CreateInstruction(OpCodeByType(indexDataType, OpCodeType.POP), _currentScope, _currentToken);
                                    new Operand(instruction, Register.Arithmetic2);

                                    // Cast?

                                    // Allocate enough space on the heap for the array.
                                    instruction = CreateInstruction(OpCodeByType(dataType, OpCodeType.ALLOCATE_HEAP), _currentScope, _currentToken);
                                    new Operand(instruction, Register.Arithmetic2);
                                    new Operand(instruction, Register.Reserved4);

                                    // Push the memory index onto the stack.
                                    instruction = CreateInstruction(OpCodeByType(dataType, OpCodeType.PUSH), _currentScope, _currentToken);
                                    new Operand(instruction, Register.Reserved4);
                                }
                            }
                            else
                                Error(ErrorCode.InvalidCast, "Can't implicitly cast between \"Int\" and \"" + indexDataType.ToString() + "\"", false, 0);

                            // Read in closing bracket.
                            ExpectToken(TokenID.CharCloseBracket);

                            if (LookAheadToken().ID == TokenID.CharOpenBrace)
                            {
                                ExpectToken(TokenID.CharOpenBrace);

                                int index = 0;
                                while (true)
                                {
                                    // Parse the expression
                                    DataTypeValue valueType = ParseExpression();
                                    if (CanImplicitlyCast(new DataTypeValue(dataType.DataType, false, false), valueType) == true)
                                    {
                                        // Its valid so insert.
                                        if (_currentPass == 1)
                                        {
                                            Instruction instruction = null;

                                            // Pop the value into arith 2.
                                            instruction = CreateInstruction(OpCodeByType(valueType, OpCodeType.POP), _currentScope, _currentToken);
                                            new Operand(instruction, Register.Arithmetic2);

                                            // Pop the memory index off the stack.
                                            instruction = CreateInstruction(OpCodeByType(dataType, OpCodeType.POP), _currentScope, _currentToken);
                                            new Operand(instruction, Register.Reserved4);

                                            // Do we need to cast first?
                                            if (dataType.DataType != valueType.DataType)
                                            {
                                                // Cast value.
                                                instruction = CreateInstruction(OpCodeByType(new DataTypeValue(dataType.DataType, false, false), OpCodeType.CAST), _currentScope, _currentToken);
                                                new Operand(instruction, Register.Arithmetic2);
                                            }

                                            // Move index into arith 3.
                                            instruction = CreateInstruction(OpCode.MOV_INT, _currentScope, _currentToken);
                                            new Operand(instruction, Register.Arithmetic3);
                                            new Operand(instruction, index);

                                            // Insert!
                                            instruction = CreateInstruction(OpCodeByType(new DataTypeValue(dataType.DataType, false, false), OpCodeType.MOV), _currentScope, _currentToken);
                                            Operand.IndirectMemoryIndexedOperand(instruction, Register.Reserved4, Register.Arithmetic3);
                                            new Operand(instruction, Register.Arithmetic2);

                                            // Push the memory index back onto the stack.
                                            instruction = CreateInstruction(OpCodeByType(dataType, OpCodeType.PUSH), _currentScope, _currentToken);
                                            new Operand(instruction, Register.Reserved4);
                                        }
                                    }
                                    else
                                        Error(ErrorCode.InvalidDataType, "Unable to implicitly cast from type \"" + DataTypeFromTypeToken(_currentToken.ID).ToString() + "\" to type \"" + dataType.DataType.ToString() + "\".", false, 1);

                                    // Read in comma or break out of loop if there isn't one.
                                    if (LookAheadToken().ID == TokenID.CharComma)
                                    {
                                        NextToken();
                                        index++;
                                    }
                                    else
                                        break;
                                }

                                ExpectToken(TokenID.CharCloseBrace);
                            }
                        }
                        else
                            Error(ErrorCode.InvalidFactor, "Primitive data types must currently be created as arrays.", false,0);
                    }
                    else
                        Error(ErrorCode.InvalidFactor, "New keyword can currently only be used to create new primitive data arrays.", false,0);

                    break;
                #endregion
                #region Boolean
                case TokenID.TypeBoolean:
                    factorDataType = new DataTypeValue(DataType.Bool, false, false);
                    if (_currentPass == 1)
                    {
                        Instruction instruction = CreateInstruction(OpCodeByType(factorDataType, OpCodeType.PUSH), _currentScope, _currentToken);
                        new Operand(instruction, bool.Parse(_currentToken.Ident));
                    }
                    break;
                #endregion
                #region Byte
                case TokenID.TypeByte:
                    factorDataType = new DataTypeValue(DataType.Byte, false, false);
                    if (_currentPass == 1)
                    {
                        Instruction instruction = CreateInstruction(OpCodeByType(factorDataType, OpCodeType.PUSH), _currentScope, _currentToken);
                        new Operand(instruction, byte.Parse(_currentToken.Ident));
                    }
                    break;
                #endregion
                #region Double
                case TokenID.TypeDouble:
                    factorDataType = new DataTypeValue(DataType.Double, false, false);
                    if (_currentPass == 1)
                    {
                        Instruction instruction = CreateInstruction(OpCodeByType(factorDataType, OpCodeType.PUSH), _currentScope, _currentToken);
                        new Operand(instruction, double.Parse(_currentToken.Ident));
                    }
                    break;
                #endregion
                #region Float
                case TokenID.TypeFloat:
                    factorDataType = new DataTypeValue(DataType.Float, false, false);
                    if (_currentPass == 1)
                    {
                        Instruction instruction = CreateInstruction(OpCodeByType(factorDataType, OpCodeType.PUSH), _currentScope, _currentToken);
                        new Operand(instruction, float.Parse(_currentToken.Ident));
                    }
                    break;
                #endregion
                #region Integer
                case TokenID.TypeInteger:
                    factorDataType = new DataTypeValue(DataType.Int, false, false);
                    if (_currentPass == 1)
                    {
                        Instruction instruction = CreateInstruction(OpCodeByType(factorDataType, OpCodeType.PUSH), _currentScope, _currentToken);
                        if (_currentToken.Ident.Length > 2 && _currentToken.Ident.Substring(0, 2) == "0x")
                            new Operand(instruction, Convert.ToInt32(_currentToken.Ident, 16));
                        else
                            new Operand(instruction, int.Parse(_currentToken.Ident));
                    }
                    break;
                #endregion
                #region Long
                case TokenID.TypeLong:
                    factorDataType = new DataTypeValue(DataType.Long, false, false);
                    if (_currentPass == 1)
                    {
                        Instruction instruction = CreateInstruction(OpCodeByType(factorDataType, OpCodeType.PUSH), _currentScope, _currentToken);
                        new Operand(instruction, Convert.ToInt64(_currentToken.Ident));
                    }
                    break;
                #endregion
                #region Short
                case TokenID.TypeShort:
                    factorDataType = new DataTypeValue(DataType.Short, false, false);
                    if (_currentPass == 1)
                    {
                        Instruction instruction = CreateInstruction(OpCodeByType(factorDataType, OpCodeType.PUSH), _currentScope, _currentToken);
                        new Operand(instruction, short.Parse(_currentToken.Ident));
                    }
                    break;
                #endregion
                #region Null
                case TokenID.TypeNull:
                    factorDataType = new DataTypeValue(DataType.Null, false, false);
                    if (_currentPass == 1)
                    {
                        Instruction instruction = CreateInstruction(OpCodeByType(factorDataType, OpCodeType.PUSH), _currentScope, _currentToken);
                    }
                    break;
                #endregion
                #region String
                case TokenID.TypeString:
                    // Always add strings to the global scope, this stops them
                    // being duplicated in multiple scopes.
                    factorDataType = new DataTypeValue(DataType.String, false, false);

                    // Check for a pre-existing string with the same value.
                    foreach (Symbol subSymbol in _globalScope.Symbols)
                    {
                        if (subSymbol.Identifier == null || subSymbol.Type != SymbolType.String) continue;
                        if (subSymbol.Identifier == _currentToken.Ident)
                        {
                            stringSymbol = subSymbol as StringSymbol;
                            break;
                        }
                    }

                    if (stringSymbol == null) stringSymbol = new StringSymbol(_globalScope, _currentToken.Ident);
                    if (_currentPass == 1)
                    {
                        Instruction instruction = CreateInstruction(OpCodeByType(factorDataType, OpCodeType.PUSH), _currentScope, _currentToken);
                        new Operand(instruction, stringSymbol);
                    }
                    break;
                #endregion
                #region Identifier
                case TokenID.TypeIdentifier:
                    VariableSymbol symbol = null;
                    string symbolIdent = _currentToken.Ident;
                    int tokenIndex = _tokenIndex;
                    Token currentToken = _currentToken;
                    Symbol resolvedScope = ResolveMemberScope();
                    symbol = resolvedScope == null ? null : resolvedScope.FindSymbol(_currentToken.Ident, SymbolType.Variable) as VariableSymbol;
                    if (symbol != null)
                    {
                        // Tag the variable as used so we don't end up throwing up a warning.
                        symbol.IsUsed = true;

                        // Set the factor data type.
                        factorDataType = new DataTypeValue(symbol.DataType.DataType, symbol.DataType.IsArray, symbol.DataType.IsReference);

                        if (symbol.IsConstant == true && symbol.ConstToken != null)
                        {
                            switch (symbol.DataType.DataType)
                            {
                                case DataType.Bool:
                                    factorDataType = new DataTypeValue(DataType.Bool, false, false);
                                    if (_currentPass == 1)
                                    {
                                        Instruction instruction = CreateInstruction(OpCodeByType(factorDataType, OpCodeType.PUSH), _currentScope, _currentToken);
                                        new Operand(instruction, bool.Parse(symbol.ConstToken.Ident));
                                    }
                                    break;
                                case DataType.Byte:
                                    factorDataType = new DataTypeValue(DataType.Byte, false, false);
                                    if (_currentPass == 1)
                                    {
                                        Instruction instruction = CreateInstruction(OpCodeByType(factorDataType, OpCodeType.PUSH), _currentScope, _currentToken);
                                        new Operand(instruction, byte.Parse(symbol.ConstToken.Ident));
                                    }
                                    break;
                                case DataType.Double:
                                    factorDataType = new DataTypeValue(DataType.Double, false, false);
                                    if (_currentPass == 1)
                                    {
                                        Instruction instruction = CreateInstruction(OpCodeByType(factorDataType, OpCodeType.PUSH), _currentScope, _currentToken);
                                        new Operand(instruction, double.Parse(symbol.ConstToken.Ident));
                                    }
                                    break;
                                case DataType.Float:
                                    factorDataType = new DataTypeValue(DataType.Float, false, false);
                                    if (_currentPass == 1)
                                    {
                                        Instruction instruction = CreateInstruction(OpCodeByType(factorDataType, OpCodeType.PUSH), _currentScope, _currentToken);
                                        new Operand(instruction, float.Parse(symbol.ConstToken.Ident));
                                    }
                                    break;
                                case DataType.Int:
                                    factorDataType = new DataTypeValue(DataType.Int, false, false);
                                    if (_currentPass == 1)
                                    {
                                        Instruction instruction = CreateInstruction(OpCodeByType(factorDataType, OpCodeType.PUSH), _currentScope, _currentToken);
                                        if (symbol.ConstToken.Ident.Length > 2 && symbol.ConstToken.Ident.Substring(0, 2) == "0x")
                                            new Operand(instruction, Convert.ToInt32(symbol.ConstToken.Ident, 16));
                                        else
                                            new Operand(instruction, int.Parse(symbol.ConstToken.Ident));
                                    }
                                    break;
                                case DataType.Long:
                                    factorDataType = new DataTypeValue(DataType.Long, false, false);
                                    if (_currentPass == 1)
                                    {
                                        Instruction instruction = CreateInstruction(OpCodeByType(factorDataType, OpCodeType.PUSH), _currentScope, _currentToken);
                                        new Operand(instruction, Convert.ToInt64(symbol.ConstToken.Ident));
                                    }
                                    break;
                                case DataType.Short:
                                    factorDataType = new DataTypeValue(DataType.Short, false, false);
                                    if (_currentPass == 1)
                                    {
                                        Instruction instruction = CreateInstruction(OpCodeByType(factorDataType, OpCodeType.PUSH), _currentScope, _currentToken);
                                        new Operand(instruction, short.Parse(symbol.ConstToken.Ident));
                                    }
                                    break;
                                case DataType.String:
                                    factorDataType = new DataTypeValue(DataType.String, false, false);
                                    stringSymbol = _globalScope.FindSymbol(symbol.ConstToken.Ident, SymbolType.String) as StringSymbol;
                                    if (stringSymbol == null) stringSymbol = new StringSymbol(_globalScope, symbol.ConstToken.Ident);
                                    if (_currentPass == 1)
                                    {
                                        Instruction instruction = CreateInstruction(OpCodeByType(factorDataType, OpCodeType.PUSH), _currentScope, _currentToken);
                                        new Operand(instruction, stringSymbol);
                                    }
                                    break;
                            }
                        }
                        else
                        {
                            // Check if we are parsing an array.
                            if (LookAheadToken().ID == TokenID.CharOpenBracket)
                            {
                                // As the array is indexed we can remove array from the factor data type.
                                factorDataType.IsArray = false;

                                // Read in open bracket.
                                NextToken();

                                // Make sure it really is an array.
                                if (symbol.IsArray == false) Error(ErrorCode.InvalidArrayIndex, "Non-array variables can not be indexed.", false, 0);

                                // Check that the next token is not a closing brace otherwise
                                // its an invalid index.
                                if (LookAheadToken().ID == TokenID.CharCloseBracket)
                                    Error(ErrorCode.InvalidArrayIndex, "Array's must be indexed.", false, 0);

                                // Parse the index expression.
                                DataTypeValue indexDataType = ParseExpression();
                                ExpectToken(TokenID.CharCloseBracket);

                                // Check the index data type can be cast to an integer index.
                                if (CanImplicitlyCast(new DataTypeValue(DataType.Int, false, false), indexDataType) == true)
                                {
                                    // Emit the value retrieval's byte code.
                                    if (_currentPass == 1)
                                    {
                                        // Pop the index value into register 1.
                                        Instruction instruction = CreateInstruction(OpCodeByType(indexDataType, OpCodeType.POP), _currentScope, _currentToken);
                                        new Operand(instruction, Register.Reserved1);

                                        // Cast index value to integer
                                        if (indexDataType.DataType != DataType.Int)
                                        {
                                            instruction = CreateInstruction(OpCode.CAST_INT, _currentScope, _currentToken);
                                            new Operand(instruction, Register.Reserved1);
                                        }

                                        // Move the array's memory slot into reserved2 register.
                                        instruction = CreateInstruction(OpCode.MOV_MEMORY_INDEX, _currentScope, _currentToken);
                                        new Operand(instruction, Register.Reserved2);
                                        if (symbol.VariableType == VariableType.Constant || symbol.VariableType == VariableType.Global)
                                            Operand.DirectMemoryOperand(instruction, symbol.MemoryIndex);
                                        else
                                            Operand.DirectStackOperand(instruction, symbol.StackIndex);

                                        // Push the array's data onto the stack indexed
                                        // by the register we just poped the index into.
                                        instruction = CreateInstruction(OpCodeByType(factorDataType, OpCodeType.PUSH), _currentScope, _currentToken);
                                        Operand.IndirectMemoryIndexedOperand(instruction, Register.Reserved2, Register.Reserved1);

                                    }

                                    #region ++ / -- Parsing
                                    // Check if there is a increment (++) or decrement (--) operator following this value..
                                    if (LookAheadToken().ID == TokenID.OpIncrement)
                                    {
                                        // Read in increment token.
                                        NextToken();

                                        // Check the data types are valid with this operator.
                                        if (OperatorDataTypeValid(symbol.DataType, _currentToken.ID) == false)
                                            Error(ErrorCode.InvalidDataType, "Operator \"" + _currentToken.Ident + "\" can't be applied to data type \"" + symbol.DataType.ToString() + "\"", false, 0);

                                        // Make sure the symbol is not constant.
                                        if (symbol.IsConstant == true)
                                            Error(ErrorCode.IllegalAssignment, "Can't modify constant value.", false, 0);

                                        // Increment the variable.
                                        if (_currentPass == 1)
                                        {
                                            // Take the assumtion that the values are still in the register from the previous code.
                                            Instruction instruction = CreateInstruction(OpCodeByType(symbol.DataType, OpCodeType.INC), _currentScope, _currentToken);
                                            Operand.IndirectMemoryIndexedOperand(instruction, Register.Reserved2, Register.Reserved1);
                                        }
                                    }
                                    else if (LookAheadToken().ID == TokenID.OpDecrement)
                                    {
                                        // Read in increment token.
                                        NextToken();

                                        // Check the data types are valid with this operator.
                                        if (OperatorDataTypeValid(symbol.DataType, _currentToken.ID) == false)
                                            Error(ErrorCode.InvalidDataType, "Operator \"" + _currentToken.Ident + "\" can't be applied to data type \"" + symbol.DataType.ToString() + "\"", false, 0);

                                        // Make sure the symbol is not constant.
                                        if (symbol.IsConstant == true)
                                            Error(ErrorCode.IllegalAssignment, "Can't modify constant value.", false, 0);

                                        // Decrement the variable.
                                        if (_currentPass == 1)
                                        {
                                            // Take the assumtion that the values are still in the register from the previous code.
                                            Instruction instruction = CreateInstruction(OpCodeByType(symbol.DataType, OpCodeType.DEC), _currentScope, _currentToken);
                                            Operand.IndirectMemoryIndexedOperand(instruction, Register.Reserved2, Register.Reserved1);
                                        }
                                    }
                                    #endregion

                                }
                                else
                                    Error(ErrorCode.InvalidCast, "Can't implicitly cast between \"Int\" and \"" + indexDataType.ToString() + "\"", false, 0);

                            }

                            // Its not an array so parse it as a reqular variable.
                            else
                            {
                                // Push the variable's value onto stack.
                                if (_currentPass == 1)
                                {
                                    Instruction instruction = CreateInstruction(OpCodeByType(symbol.DataType, OpCodeType.PUSH), _currentScope, _currentToken);
                                    if (symbol.VariableType == VariableType.Global || symbol.VariableType == VariableType.Constant)
                                        Operand.DirectMemoryOperand(instruction, symbol.MemoryIndex);
                                    else
                                        Operand.DirectStackOperand(instruction, symbol.StackIndex);
                                }

                                #region ++ / -- Parsing
                                // Check if there is a increment (++) or decrement (--) operator following this value..
                                if (LookAheadToken().ID == TokenID.OpIncrement)
                                {
                                    // Read in increment token.
                                    NextToken();

                                    // Check the data types are valid with this operator.
                                    if (OperatorDataTypeValid(symbol.DataType, _currentToken.ID) == false)
                                        Error(ErrorCode.InvalidDataType, "Operator \"" + _currentToken.Ident + "\" can't be applied to data type \"" + symbol.DataType.ToString() + "\"", false, 0);

                                    // Make sure the symbol is not constant.
                                    if (symbol.IsConstant == true)
                                        Error(ErrorCode.IllegalAssignment, "Can't modify constant value.", false, 0);

                                    // Increment the variable.
                                    if (_currentPass == 1)
                                    {
                                        Instruction instruction = CreateInstruction(OpCodeByType(symbol.DataType, OpCodeType.INC), _currentScope, _currentToken);
                                        if (symbol.VariableType == VariableType.Global || symbol.VariableType == VariableType.Constant)
                                            Operand.DirectMemoryOperand(instruction, symbol.MemoryIndex);
                                        else
                                            Operand.DirectStackOperand(instruction, symbol.StackIndex);
                                    }
                                }
                                else if (LookAheadToken().ID == TokenID.OpDecrement)
                                {
                                    // Read in increment token.
                                    NextToken();

                                    // Check the data types are valid with this operator.
                                    if (OperatorDataTypeValid(symbol.DataType, _currentToken.ID) == false)
                                        Error(ErrorCode.InvalidDataType, "Operator \"" + _currentToken.Ident + "\" can't be applied to data type \"" + symbol.DataType.ToString() + "\"", false, 0);

                                    // Make sure the symbol is not constant.
                                    if (symbol.IsConstant == true)
                                        Error(ErrorCode.IllegalAssignment, "Can't modify constant value.", false, 0);

                                    // Decrement the variable.
                                    if (_currentPass == 1)
                                    {
                                        Instruction instruction = CreateInstruction(OpCodeByType(symbol.DataType, OpCodeType.DEC), _currentScope, _currentToken);
                                        if (symbol.VariableType == VariableType.Global || symbol.VariableType == VariableType.Constant)
                                            Operand.DirectMemoryOperand(instruction, symbol.MemoryIndex);
                                        else
                                            Operand.DirectStackOperand(instruction, symbol.StackIndex);
                                    }
                                }
                                #endregion

                            }
                        }
                    }

                    // It look for opening parenthesis which would make it a function call.
                    else if (EndOfTokenStream() == false && LookAheadToken().ID == TokenID.CharOpenParenthesis)
                    {
                        #region Function calling

                        _tokenIndex = tokenIndex;
                        _currentToken = currentToken;

                        // Parse the function call and remember the return type.
                        DataTypeValue functionReturnType = ParseFunctionCall();

                        // Push the return value of the function onto the stack.
                        if (_currentPass == 1)
                        {
                            Instruction instruction = CreateInstruction(OpCodeByType(functionReturnType, OpCodeType.PUSH), _currentScope, _currentToken);
                            new Operand(instruction, Register.Return);
                        }

                        // Set the factors data type.
                        factorDataType = functionReturnType;

                        #endregion
                    }

                    // WTF! Is this?
                    else
                    {
                        Error(ErrorCode.UndeclaredVariable, "Encountered attempt to access an undeclared symbol \"" + symbolIdent + "\".", false, 1);
                    }
                    break;
                #endregion
                #region Sub expression
                case TokenID.CharOpenParenthesis:
                    factorDataType = ParseExpression();
                    ExpectToken(TokenID.CharCloseParenthesis);
                    break;
                #endregion
                default:
                    Error(ErrorCode.InvalidFactor, "Invalid factor.", false, 0);
                    break;
            }

            // Create instructions for unary operator if in second pass.
            if (unaryOpPending == true && _currentPass == 1)
            {
                // Check the data types are valid with this operator.
                if (OperatorDataTypeValid(factorDataType, unaryOpToken.ID) == false)
                    Error(ErrorCode.InvalidDataType, "Operator \"" + unaryOpToken.Ident + "\" can't be apply to data type \"" + factorDataType.ToString() + "\"");

                // Pop the value of this value out of the stack
                // and preform action on it.
                Instruction instruction = CreateInstruction(OpCodeByType(factorDataType, OpCodeType.POP), _currentScope, _currentToken);
                new Operand(instruction, Register.Arithmetic1);

                // Create instruction based on operation code.
                switch (unaryOpToken.ID)
                {
                    case TokenID.OpAdd: instruction = CreateInstruction(OpCodeByType(factorDataType, OpCodeType.ABS), _currentScope, _currentToken); break;
                    case TokenID.OpSub: instruction = CreateInstruction(OpCodeByType(factorDataType, OpCodeType.NEG), _currentScope, _currentToken); break;
                    case TokenID.OpLogicalNot: instruction = CreateInstruction(OpCode.LOGICAL_NOT, _currentScope, _currentToken); break;
                    case TokenID.OpBitwiseNot: instruction = CreateInstruction(OpCode.BIT_NOT_INT, _currentScope, _currentToken); break;
                    case TokenID.OpIncrement: instruction = CreateInstruction(OpCodeByType(factorDataType, OpCodeType.INC), _currentScope, _currentToken); break;
                    case TokenID.OpDecrement: instruction = CreateInstruction(OpCodeByType(factorDataType, OpCodeType.DEC), _currentScope, _currentToken); break;
                }
                new Operand(instruction, Register.Arithmetic1);

                // Push the value back onto the stack (as long as its not the logical not, which does that itself).
                if (unaryOpToken.ID != TokenID.OpLogicalNot)
                {
                    instruction = CreateInstruction(OpCodeByType(factorDataType, OpCodeType.PUSH), _currentScope, _currentToken);
                    new Operand(instruction, Register.Arithmetic1);
                }
            }

            // If a cast is pending then convert the result to the given type.
            if (castPending == true && factorDataType != castType)
            {
                if (_currentPass == 1)
                {
                    // Pop the result out of the stack.
                    Instruction instruction = CreateInstruction(OpCodeByType(factorDataType, OpCodeType.POP), _currentScope, _currentToken);
                    new Operand(instruction, Register.Arithmetic1);

                    // Cast it to the given type.
                    instruction = CreateInstruction(OpCodeByType(castType, OpCodeType.CAST), _currentScope, _currentToken);
                    new Operand(instruction, Register.Arithmetic1);

                    // Push the value back onto the stack.
                    instruction = CreateInstruction(OpCodeByType(castType, OpCodeType.PUSH), _currentScope, _currentToken);
                    new Operand(instruction, Register.Arithmetic1);
                }
                factorDataType = castType;
            }

            return factorDataType;
        }
Ejemplo n.º 6
0
 /// <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);
 }
Ejemplo n.º 7
0
        /// <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>
        ///		Lexically analyses and returns the next token in the script.
        /// </summary>
        /// <returns>Next token in script.</returns>
        private Token LexToken()
        {
            _currentToken = null;
            switch (_currentChar)
            {
                case "=":
                    switch (NextCharacter())
                    {
                        case "=": NextCharacter(); _currentToken = new Token(TokenID.OpEqual, "==", _lexerLine, _lexerOffset, _file); break;
                        default: _currentToken = new Token(TokenID.OpAssign, "=", _lexerLine, _lexerOffset, _file); break;
                    }
                    break;

                case "^":
                    switch (NextCharacter())
                    {
                        case "=": NextCharacter(); _currentToken = new Token(TokenID.OpAssignBitwiseXOr, "^=", _lexerLine, _lexerOffset, _file); break;
                        default: _currentToken = new Token(TokenID.OpBitwiseXOr, "^", _lexerLine, _lexerOffset, _file); break;
                    }
                    break;

                case "~":
                    switch (NextCharacter())
                    {
                        case "=": NextCharacter(); _currentToken = new Token(TokenID.OpAssignBitwiseNot, "~=", _lexerLine, _lexerOffset, _file); break;
                        default: _currentToken = new Token(TokenID.OpBitwiseNot, "~", _lexerLine, _lexerOffset, _file); break;
                    }
                    break;

                case "|":
                    switch (NextCharacter())
                    {
                        case "=": NextCharacter(); _currentToken = new Token(TokenID.OpAssignBitwiseOr, "|=", _lexerLine, _lexerOffset, _file); break;
                        case "|": NextCharacter(); _currentToken = new Token(TokenID.OpLogicalOr, "||", _lexerLine, _lexerOffset, _file); break;
                        default: _currentToken = new Token(TokenID.OpBitwiseOr, "|", _lexerLine, _lexerOffset, _file); break;
                    }
                    break;

                case "&":
                    switch (NextCharacter())
                    {
                        case "=": NextCharacter(); _currentToken = new Token(TokenID.OpAssignBitwiseAnd, "&=", _lexerLine, _lexerOffset, _file); break;
                        case "&": NextCharacter(); _currentToken = new Token(TokenID.OpLogicalAnd, "&&", _lexerLine, _lexerOffset, _file); break;
                        default: _currentToken = new Token(TokenID.OpBitwiseAnd, "&", _lexerLine, _lexerOffset, _file); break;
                    }
                    break;

                case "%":
                    switch (NextCharacter())
                    {
                        case "=": NextCharacter(); _currentToken = new Token(TokenID.OpAssignModulus, "%=", _lexerLine, _lexerOffset, _file); break;
                        default: _currentToken = new Token(TokenID.OpModulus, "%", _lexerLine, _lexerOffset, _file); break;
                    }
                    break;

                case "-":
                    switch (NextCharacter())
                    {
                        case ">": NextCharacter(); _currentToken = new Token(TokenID.OpMemberResolver, "->", _lexerLine, _lexerOffset, _file); break;
                        case "=": NextCharacter(); _currentToken = new Token(TokenID.OpAssignSub, "-=", _lexerLine, _lexerOffset, _file); break;
                        case "-": NextCharacter(); _currentToken = new Token(TokenID.OpDecrement, "--", _lexerLine, _lexerOffset, _file); break;
                        default: _currentToken = new Token(TokenID.OpSub, "-", _lexerLine, _lexerOffset, _file); break;
                    }
                    break;

                case "+":
                    switch (NextCharacter())
                    {
                        case "=": NextCharacter(); _currentToken = new Token(TokenID.OpAssignAdd, "+=", _lexerLine, _lexerOffset, _file); break;
                        case "+": NextCharacter(); _currentToken = new Token(TokenID.OpIncrement, "++", _lexerLine, _lexerOffset, _file); break;
                        default: _currentToken = new Token(TokenID.OpAdd, "+", _lexerLine, _lexerOffset, _file); break;
                    }
                    break;

                case "/":
                    switch (NextCharacter())
                    {
                        case "*": //Parse through multiple layers of block comments.
                            #region Block Comment Lexing

                            int commentDepth = 1;
                            while (commentDepth > 0)
                            {
                                switch (NextCharacter())
                                {
                                    case "*":
                                        if (NextCharacter() == "/")
                                        {
                                            commentDepth--;
                                            NextCharacter();
                                        }
                                        break;
                                    case "/":
                                        if (NextCharacter() == "*")
                                        {
                                            commentDepth++;
                                            NextCharacter();
                                        }
                                        break;
                                }
                            }

                            #endregion
                            break;
                        case "/":
                            while (_currentChar != "\n" && !EndOfTokenStream())
                                NextCharacter();
                            break;
                        case "=": NextCharacter(); _currentToken = new Token(TokenID.OpAssignDivide, "/=", _lexerLine, _lexerOffset, _file); break;
                        default: _currentToken = new Token(TokenID.OpDivide, "/", _lexerLine, _lexerOffset, _file); break;
                    }
                    break;

                case "*":
                    switch (NextCharacter())
                    {
                        case "=": NextCharacter(); _currentToken = new Token(TokenID.OpAssignMultiply, "*=", _lexerLine, _lexerOffset, _file); break;
                        default: _currentToken = new Token(TokenID.OpMultiply, "*", _lexerLine, _lexerOffset, _file); break;
                    }
                    break;

                case "<":
                    switch (NextCharacter())
                    {
                        case "=": NextCharacter(); _currentToken = new Token(TokenID.OpLessEqual, "<=", _lexerLine, _lexerOffset, _file); break;
                        case "<":
                            switch (NextCharacter())
                            {
                                case "=": NextCharacter(); _currentToken = new Token(TokenID.OpAssignBitwiseSHL, "<<=", _lexerLine, _lexerOffset, _file); break;
                                default: _currentToken = new Token(TokenID.OpBitwiseSHL, "<<", _lexerLine, _lexerOffset, _file); break;
                            }
                            break;
                        default: _currentToken = new Token(TokenID.OpLess, "<", _lexerLine, _lexerOffset, _file); break;
                    }
                    break;

                case ">":
                    switch (NextCharacter())
                    {
                        case "=": NextCharacter(); _currentToken = new Token(TokenID.OpGreaterEqual, ">=", _lexerLine, _lexerOffset, _file); break;
                        case ">":
                            switch (NextCharacter())
                            {
                                case "=": NextCharacter(); _currentToken = new Token(TokenID.OpAssignBitwiseSHR, ">>=", _lexerLine, _lexerOffset, _file); break;
                                default: _currentToken = new Token(TokenID.OpBitwiseSHR, ">>", _lexerLine, _lexerOffset, _file); break;
                            }
                            break;
                        default: _currentToken = new Token(TokenID.OpGreater, ">", _lexerLine, _lexerOffset, _file); break;
                    }
                    break;

                case "!":
                    switch (NextCharacter())
                    {
                        case "=": NextCharacter(); _currentToken = new Token(TokenID.OpNotEqual, "!=", _lexerLine, _lexerOffset, _file); break;
                        default: _currentToken = new Token(TokenID.OpLogicalNot, "!", _lexerLine, _lexerOffset, _file); break;
                    }
                    break;

                case "{": NextCharacter(); _currentToken = new Token(TokenID.CharOpenBrace, "{", _lexerLine, _lexerOffset, _file); break;
                case "}": NextCharacter(); _currentToken = new Token(TokenID.CharCloseBrace, "}", _lexerLine, _lexerOffset, _file); break;
                case "[": NextCharacter(); _currentToken = new Token(TokenID.CharOpenBracket, "[", _lexerLine, _lexerOffset, _file); break;
                case "]": NextCharacter(); _currentToken = new Token(TokenID.CharCloseBracket, "]", _lexerLine, _lexerOffset, _file); break;
                case "(": NextCharacter(); _currentToken = new Token(TokenID.CharOpenParenthesis, "(", _lexerLine, _lexerOffset, _file); break;
                case ")": NextCharacter(); _currentToken = new Token(TokenID.CharCloseParenthesis, ")", _lexerLine, _lexerOffset, _file); break;
                case ",": NextCharacter(); _currentToken = new Token(TokenID.CharComma, ",", _lexerLine, _lexerOffset, _file); break;
                case ":": NextCharacter(); _currentToken = new Token(TokenID.CharColon, ":", _lexerLine, _lexerOffset, _file); break;
                case ";": NextCharacter(); _currentToken = new Token(TokenID.CharSemiColon, ";", _lexerLine, _lexerOffset, _file); break;
                case ".": NextCharacter(); _currentToken = new Token(TokenID.CharPeriod, ".", _lexerLine, _lexerOffset, _file); break;
                case "#": NextCharacter(); _currentToken = new Token(TokenID.CharDirective, "#", _lexerLine, _lexerOffset, _file); break;

                case " ":
                case "\t":
                case "\n":
                case "\r":
                    // We can safely ignore any whitespace characters.
                    NextCharacter();
                    break;

                case "'":
                case "\"": // String literals
                    #region String Literal Lexing

                    string stringLiteral = "";
                    string stringStartChar = _currentChar;
                    while (true)
                    {
                        if (NextCharacter() == stringStartChar) break;
                        if (EndOfTokenStream() == true || _currentChar == "\n")
                        {
                            Error(ErrorCode.UnfinishedStringLiteral, "Unfinished string literal");
                            break;
                        }

                        // Check for escape sequence
                        if (_currentChar == "\\")
                        {
                            NextCharacter();
                            switch (_currentChar)
                            {
                                case "'":
                                case "\"": stringLiteral += stringStartChar; break;
                                case "a": stringLiteral += "\a"; break;
                                case "b": stringLiteral += "\b"; break;
                                case "f": stringLiteral += "\f"; break;
                                case "n": stringLiteral += "\n"; break;
                                case "r": stringLiteral += "\r"; break;
                                case "t": stringLiteral += "\t"; break;
                                case "v": stringLiteral += "\v"; break;
                                case "0": stringLiteral += "\0"; break;
                                case "\\": stringLiteral += "\\"; break;
                                default:
                                    Error(ErrorCode.InvalidEscapeSequence, "Found invalid escape sequence '\\" + _currentChar + "' in string literal.");
                                    break;
                            }
                        }
                        else
                            stringLiteral += _currentChar;
                    }

                    NextCharacter();
                    _currentToken = new Token(TokenID.TypeString, stringLiteral, _lexerLine, _lexerOffset, _file);

                    #endregion
                    break;

                default:
                    if ((_currentChar[0] >= 'A' && _currentChar[0] <= 'Z') ||
                        (_currentChar[0] >= 'a' && _currentChar[0] <= 'z') ||
                         _currentChar[0] == '_') // Check if its a identifier.
                    {
                        #region Identiier Lexing

                        TokenID identType = TokenID.TypeIdentifier;
                        string identLiteral = "";
                        int index = 0;

                        while (true)
                        {
                            identLiteral += _currentChar;
                            if (_currentChar[0] >= '0' && _currentChar[0] <= '9' && index == 0)
                            {
                                Error(ErrorCode.MalformedIdentifier, "Found malformed identifier.");
                                break;
                            }

                            // Go to next character.
                            NextCharacter();
                            if (EndOfTokenStream() || StringMethods.IsStringIdentifier(_currentChar) == false) break;
                            index++;
                        }

                        // Check if its a keyword or not.
                        switch (identLiteral.ToLower())
                        {
                            case "state":	identType = TokenID.KeywordState;	break;
                            case "event":	identType = TokenID.KeywordEvent;	break;
                            case "default": identType = TokenID.KeywordDefault; break;
                            case "switch":	identType = TokenID.KeywordSwitch;	break;
                            case "case":	identType = TokenID.KeywordCase;	break;
                            case "if":		identType = TokenID.KeywordIf;		break;
                            case "else":	identType = TokenID.KeywordElse;	break;
                            case "enum":	identType = TokenID.KeywordEnum;	break;
                            case "const":	identType = TokenID.KeywordConst;	break;
                            case "ref":		identType = TokenID.KeywordRef;		break;
                            case "for":		identType = TokenID.KeywordFor;		break;
                            case "return":	identType = TokenID.KeywordReturn;	break;
                            case "while":	identType = TokenID.KeywordWhile;	break;
                            case "do":		identType = TokenID.KeywordDo;		break;
                            case "new":		identType = TokenID.KeywordNew;		break;
                            case "delete":	identType = TokenID.KeywordDelete;	break;
                            case "lock":	identType = TokenID.KeywordLock;	break;
                            case "atom":	identType = TokenID.KeywordAtom;	break;
                            case "object":	identType = TokenID.KeywordObject;	break;
                            case "string":	identType = TokenID.KeywordString;	break;
                            case "bool":	identType = TokenID.KeywordBool;	break;
                            case "byte":	identType = TokenID.KeywordByte;	break;
                            case "int":		identType = TokenID.KeywordInt;		break;
                            case "short":	identType = TokenID.KeywordShort;	break;
                            case "float":	identType = TokenID.KeywordFloat;	break;
                            case "double":	identType = TokenID.KeywordDouble;	break;
                            case "long":	identType = TokenID.KeywordLong;	break;
                            case "void":	identType = TokenID.KeywordVoid;	break;
                            case "thread":	identType = TokenID.KeywordThread;	break;
                            case "goto":	identType = TokenID.KeywordGoto;	break;
                            case "break":   identType = TokenID.KeywordBreak;	break;
                            case "continue":identType = TokenID.KeywordContinue;break;
                            case "true":	identType = TokenID.TypeBoolean;	break;
                            case "false":	identType = TokenID.TypeBoolean;	break;
                            case "null":	identType = TokenID.TypeNull;		identLiteral = "0"; break;
                            case "indexer": identType = TokenID.KeywordIndexer; break;
                            case "gotostate": identType = TokenID.KeywordGotoState; break;
                            case "static": identType = TokenID.KeywordStatic; break;
                            case "breakpoint": identType = TokenID.KeywordBreakpoint; break;
                            case "property": identType = TokenID.KeywordProperty; break;
                            case "private": identType = TokenID.KeywordPrivate; break;
                            case "public": identType = TokenID.KeywordPublic; break;
                            case "protected": identType = TokenID.KeywordProtected; break;
                            case "struct": identType = TokenID.KeywordStruct; break;
                            case "class": identType = TokenID.KeywordClass; break;
                            case "engine": identType = TokenID.KeywordEngine; break;
                            case "editor": identType = TokenID.KeywordEditor; break;
                            case "namespace": identType = TokenID.KeywordNamespace; break;
                            case "console": identType = TokenID.KeywordConsole; break;
                            case "import": identType = TokenID.KeywordImport; break;
                            case "export": identType = TokenID.KeywordExport; break;
                        }

                        _currentToken = new Token(identType, identLiteral, _lexerLine, _lexerOffset, _file);

                        #endregion
                    }
                    else if (StringMethods.IsStringNumeric(_currentChar)) // Check if we are dealing with a number.
                    {
                        #region Numeric Lexing

                        TokenID numberType = TokenID.TypeInteger;
                        string numberLiteral = "";
                        bool foundRadix = false, isHex = false, foundTypeSpecifier = false;
                        int index = 0;

                        while (true)
                        {
                            numberLiteral += _currentChar;
                            if (_currentChar == "x")
                                if (index == 1 && numberLiteral[0] == '0' && isHex == false)
                                    isHex = true;
                                else
                                {
                                    Error(ErrorCode.MalformedHex, "Found malformed hexidecimal literal.");
                                    break;
                                }

                            if ((_currentChar[0] >= 'A' && _currentChar[0] <= 'F') ||
                                (_currentChar[0] >= 'a' && _currentChar[0] <= 'f'))
                            {
                                if (isHex == false && foundTypeSpecifier == true)
                                {
                                    Error(ErrorCode.MalformedHex, "Found malformed floating point literal.");
                                    break;
                                }
                            }

                            if (_currentChar == ".")
                                if (foundRadix == false)
                                {
                                    foundRadix = true;
                                    numberType = TokenID.TypeFloat;
                                }
                                else
                                {
                                    Error(ErrorCode.MalformedFloat, "Found malformed floating point literal.");
                                    break;
                                }

                            if (_currentChar == "-")
                                if (!(index == 0))
                                {
                                    Error(ErrorCode.MalformedInteger, "Found malformed integer literal.");
                                    break;
                                }

                            if (_currentChar == "f")
                            {
                                foundTypeSpecifier = true;
                                numberType = TokenID.TypeFloat;
                            }

                            if (_currentChar == "d")
                            {
                                foundTypeSpecifier = true;
                                numberType = TokenID.TypeDouble;
                            }

                            if (_currentChar == "l")
                            {
                                if (foundRadix == true) Error(ErrorCode.MalformedDataTypeSpecifier, "Long type specifier can't be applied to float-point values.");
                                foundTypeSpecifier = true;
                                numberType = TokenID.TypeLong;
                            }

                            if (_currentChar == "s")
                            {
                                if (foundRadix == true) Error(ErrorCode.MalformedDataTypeSpecifier, "Short type specifier can't be applied to float-point values.");
                                foundTypeSpecifier = true;
                                numberType = TokenID.TypeShort;
                            }

                            if (_currentChar == "b")
                            {
                                if (foundRadix == true) Error(ErrorCode.MalformedDataTypeSpecifier, "Byte type specifier can't be applied to float-point values.");
                                foundTypeSpecifier = true;
                                numberType = TokenID.TypeByte;
                            }

                            // Go to next character.
                            NextCharacter();
                            if (EndOfTokenStream() || StringMethods.IsStringNumeric(_currentChar) == false) break;

                            // Make sure that the type specifier (as in 0.00f) is on the end.
                            if (foundTypeSpecifier == true)
                            {
                                Error(ErrorCode.MalformedDataTypeSpecifier, "Found malformed data type specifier.");
                                break;
                            }
                            index++;
                        }

                        // If there is a type specifier attached to the end of this number
                        // then remove it.
                        if (foundTypeSpecifier == true) numberLiteral = numberLiteral.Substring(0, numberLiteral.Length - 1);

                        // Create the numeric token.
                        _currentToken = new Token(numberType, numberLiteral, _lexerLine, _lexerOffset, _file);

                        #endregion
                    }
                    else
                    {
                        Error(ErrorCode.InvalidCharacter, "Found invalid character '" + _currentChar + "' while analysing tokens.");
                        NextCharacter();
                        break;
                    }
                    break;

            }

            return _currentToken;
        }
Ejemplo n.º 9
0
 /// <summary>
 ///		Looks up the give token in the define list and changes
 ///		its ident if a define is found.
 /// </summary>
 /// <param name="token">Token to lookup.</param>
 /// <returns>The original token but changed to the given define.</returns>
 private Token LookupDefineToken(Token token)
 {
     foreach (Define def in _defineList)
         if (def.Ident.ToLower() == token.Ident.ToLower())
         {
             token.Ident = def.Value;
             token.ID    = def.ValueID;
             break;
         }
     return token;
 }
Ejemplo n.º 10
0
        /// <summary>
        ///		Processes the given token list and acts upon any pre processor directives (eg. #if, #define, ...).
        /// </summary>
        /// <param name="tokenList">Text of script to analyse.</param>
        /// <param name="flags">Bitmask of flags explaining how to process tokens.</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>
        /// <returns>Number of error that occured.</returns>
        public int Process(ArrayList tokenList, CompileFlags flags, Define[] defineList, object[] includePaths)
        {
            _tokenList = tokenList;
            _newTokenList.Clear();
            _errorList.Clear();
            _defineList.Clear();
            _includedFiles.Clear();
            _includePaths = includePaths;
            _currentToken = null;
            _tokenIndex = 0;
            _compileFlags = flags;

            // Create a new define for every definition passed.
            if (defineList != null)
            {
                foreach (Define def in defineList)
                    _defineList.Add(def);
            }
            try
            {
                while (!EndOfTokenStream())
                {
                    NextToken();
                    ProcessToken();
                }
            }
            catch (CompileBreakException)
            {
                // Ignore this exception, it is used to jump out of the compiling loop.
            }

            return _errorList.Count;
        }