public static IValueGetter[] ParseItems(
            IEnumerator <Token> tokens,
            Type itemType,
            CompilationContext context)
        {
            List <IValueGetter> items = new List <IValueGetter>();

            tokens.AssertAndSkip(Separator.OpenCurlyBoi);

            if (!tokens.TestWithoutSkipping(Separator.CloseCurlyBoi))
            {
                do
                {
                    Token        temp      = tokens.Current;
                    IValueGetter nextValue = ParseNextGetterExpression(tokens, context);

                    if (!itemType.AssignableFromType(nextValue.GetValueType()))
                    {
                        throw new ScriptParsingException(
                                  source: temp,
                                  message: $"Initializer List Argument must be of appropriate value type: Expected: {itemType.Name}, Found: {nextValue.GetValueType().Name}");
                    }

                    items.Add(nextValue);
                }while (tokens.TestAndConditionallySkip(Separator.Comma));
            }

            tokens.AssertAndSkip(Separator.CloseCurlyBoi);

            return(items.ToArray());
        }
        public static IValueGetter[] ParseArguments(
            IEnumerator <Token> tokens,
            CompilationContext context)
        {
            List <IValueGetter> arguments = new List <IValueGetter>();

            tokens.AssertAndSkip(Separator.OpenParen);

            if (!tokens.TestWithoutSkipping(Separator.CloseParen))
            {
                do
                {
                    Token       temp           = tokens.Current;
                    IExpression nextExpression = ParseNextExpression(tokens, context);
                    if (!(nextExpression is IValueGetter nextValue))
                    {
                        throw new ScriptParsingException(
                                  source: temp,
                                  message: $"Function Arguments must be values: {nextExpression}");
                    }
                    arguments.Add(nextValue);
                }while (tokens.TestAndConditionallySkip(Separator.Comma));
            }

            tokens.AssertAndSkip(Separator.CloseParen);

            return(arguments.ToArray());
        }
        public static IExecutable ParseNextStatement(
            IEnumerator <Token> tokens,
            CompilationContext context)
        {
            Token leadingToken = tokens.Current;

            switch (leadingToken)
            {
            case SeparatorToken sepToken:
                //Valid operations:
                //  Just a semicolon (empty statement)
                //  OpenCurlyBoi (new block)
                switch (sepToken.separator)
                {
                case Separator.Semicolon:
                    //Empty statement.  Kick out.
                    //Skip checking for EOF because it's possible for the last statement to be
                    //  just a semicolon.  This would trigger the EOF exception
                    tokens.CautiousAdvance(checkEOF: false);
                    return(null);

                case Separator.OpenCurlyBoi:
                    tokens.CautiousAdvance();
                    IExecutable statement = new Block(tokens, context);
                    tokens.AssertAndSkip(Separator.CloseCurlyBoi, checkEOF: false);
                    return(statement);

                default:
                    throw new ScriptParsingException(
                              source: sepToken,
                              message: $"Statement cannot begin with a {sepToken.separator}.");
                }


            case KeywordToken kwToken:
                //Valid operations:
                //  Continue, Break
                //  Return (With or Without return value)
                //  Declaration (With or Without assignment, with or without global)
                //  If ( Condition )
                //  While ( Condition )
                //  For ( A; B; C )
                return(ParseKeywordStatement(kwToken, tokens, context));

            case LiteralToken _:
            case IdentifierToken _:
            case OperatorToken _:
                //Valid operations:
                //  Assignment
                //  PostIncrement
                //  PreIncrement
                IExecutable standardExecutable = Expression.ParseNextExecutableExpression(tokens, context);
                tokens.AssertAndSkip(Separator.Semicolon, checkEOF: false);
                return(standardExecutable);

            default:
                throw new Exception($"Unexpected TokenType: {leadingToken}");
            }
        }
        private static void HandleNextSeparator(
            SeparatorToken sepToken,
            ref bool continueCollecting,
            List <ParsingUnit> units,
            IEnumerator <Token> tokens,
            CompilationContext context)
        {
            switch (sepToken.separator)
            {
            case Separator.Semicolon:
            case Separator.Comma:
            case Separator.CloseParen:
            case Separator.CloseIndexer:
            case Separator.Colon:
            case Separator.CloseCurlyBoi:
                continueCollecting = false;
                break;

            case Separator.OpenIndexer:
                tokens.CautiousAdvance();
                IExpression indexerExpression = ParseNextExpression(tokens, context);
                if (!(indexerExpression is IValueGetter indexerValue))
                {
                    throw new ScriptParsingException(
                              source: sepToken,
                              message: $"Index operation requires value type: {indexerExpression}");
                }
                units.Add(new IndexAccessUnit(indexerValue, sepToken));
                tokens.AssertAndSkip(Separator.CloseIndexer);
                break;

            case Separator.OpenParen:
                tokens.CautiousAdvance();
                units.Add(new ParsedValuedUnit(ParseNextExpression(tokens, context), sepToken));
                tokens.AssertAndSkip(Separator.CloseParen);
                break;

            case Separator.OpenCurlyBoi:
                throw new ScriptParsingException(
                          source: sepToken,
                          message: $"CurlyBois cannot exist in an expression.  Are you missing a semicolon?: {sepToken}");

            default:
                throw new Exception($"Unexpected Separator: {sepToken.separator}");
            }
        }
Example #5
0
        private static VariableData[] ParseArgumentsDeclaration(IEnumerator <Token> tokens)
        {
            List <VariableData> arguments = new List <VariableData>();

            tokens.AssertAndSkip(Separator.OpenParen);

            if (!tokens.TestWithoutSkipping(Separator.CloseParen))
            {
                do
                {
                    Type            argumentType = tokens.ReadType();
                    IdentifierToken identToken   = tokens.GetTokenAndAdvance <IdentifierToken>();

                    arguments.Add(new VariableData(identToken, argumentType));
                }while (tokens.TestAndConditionallySkip(Separator.Comma));
            }

            tokens.AssertAndSkip(Separator.CloseParen);

            return(arguments.ToArray());
        }
        public static Type[] ReadTypeArguments(this IEnumerator <Token> tokens)
        {
            tokens.AssertAndSkip(Operator.IsLessThan);

            if (tokens.Current is OperatorToken closeToken &&
                closeToken.operatorType == Operator.IsLessThan)
            {
                throw new ScriptParsingException(
                          source: tokens.Current,
                          message: "Generic types must be specified");
            }

            List <Type> types = new List <Type>();

            do
            {
                types.Add(tokens.ReadType());
            }while (tokens.TestAndConditionallySkip(Separator.Comma));

            tokens.AssertAndSkip(Operator.IsGreaterThan);

            return(types.ToArray());
        }
        private static IExecutable ParseKeywordStatement(
            KeywordToken kwToken,
            IEnumerator <Token> tokens,
            CompilationContext context)
        {
            //Valid operations:
            //  Continue, Break
            //  Return (With or Without return value)
            //  Declaration (With or Without assignment, with or without global)
            //  If ( Condition )
            //  While ( Condition )
            //  For ( A; B; C )

            bool constDeclaration = false;

            switch (kwToken.keyword)
            {
            case Keyword.If:
            {
                tokens.CautiousAdvance();
                tokens.AssertAndSkip(Separator.OpenParen);
                IValueGetter ifTest = Expression.ParseNextGetterExpression(tokens, context);
                tokens.AssertAndSkip(Separator.CloseParen);

                IExecutable trueStatement  = ParseNextStatement(tokens, context);
                IExecutable falseStatement = null;

                if (trueStatement == null)
                {
                    throw new ScriptParsingException(
                              source: kwToken,
                              message: $"No statement returned for If block: {kwToken}");
                }

                //Check the next token for Else or Else IF
                if (tokens.TestAndConditionallySkip(Keyword.Else))
                {
                    falseStatement = ParseNextStatement(tokens, context);
                }
                else if (tokens.TestWithoutSkipping(Keyword.ElseIf))
                {
                    KeywordToken replacementIf = new KeywordToken(
                        source: tokens.Current,
                        keyword: Keyword.If);

                    falseStatement = ParseKeywordStatement(
                        kwToken: replacementIf,
                        tokens: tokens,
                        context: context);
                }

                return(new IfStatement(
                           condition: ifTest,
                           trueBlock: trueStatement,
                           falseBlock: falseStatement,
                           keywordToken: kwToken));
            }

            case Keyword.While:
            {
                tokens.CautiousAdvance();
                //New context is used for loop
                context = context.CreateChildScope(true);

                tokens.AssertAndSkip(Separator.OpenParen);
                IValueGetter conditionTest = Expression.ParseNextGetterExpression(tokens, context);
                tokens.AssertAndSkip(Separator.CloseParen);

                IExecutable loopBody = ParseNextStatement(tokens, context);

                return(new WhileStatement(
                           continueExpression: conditionTest,
                           loopBody: loopBody,
                           keywordToken: kwToken));
            }

            case Keyword.For:
            {
                tokens.CautiousAdvance();
                //New context is used for loop
                context = context.CreateChildScope(true);

                //Open Paren
                tokens.AssertAndSkip(Separator.OpenParen);

                //Initialization
                IExecutable initializationStatement = ParseNextStatement(tokens, context);

                //Semicolon already skipped by statement parsing

                //Continue Expression
                IValueGetter continueExpression = Expression.ParseNextGetterExpression(tokens, context);

                //Semicolon
                tokens.AssertAndSkip(Separator.Semicolon);

                //Increment
                IExecutable incrementStatement = ParseForIncrementer(tokens, context);

                //Close Paren
                tokens.AssertAndSkip(Separator.CloseParen);

                IExecutable loopBody = ParseNextStatement(tokens, context);

                return(new ForStatement(
                           initializationStatement: initializationStatement,
                           continueExpression: continueExpression,
                           incrementStatement: incrementStatement,
                           loopBody: loopBody,
                           keywordToken: kwToken));
            }

            case Keyword.ForEach:
            {
                tokens.CautiousAdvance();

                //New context is used for loop
                context = context.CreateChildScope(true);

                //Open Paren
                tokens.AssertAndSkip(Separator.OpenParen);

                //Item Declaration
                Type            itemType        = tokens.GetTokenAndAdvance <KeywordToken>().keyword.GetValueType();
                IdentifierToken identifierToken = tokens.GetTokenAndAdvance <IdentifierToken>();

                IExecutable declaration = new DeclarationOperation(
                    identifierToken: identifierToken,
                    valueType: itemType,
                    context: context);

                IValue loopVariable = new IdentifierExpression(
                    identifierToken: identifierToken,
                    context: context);

                tokens.AssertAndSkip(Keyword.In);

                //Container
                IValueGetter containerExpression = Expression.ParseNextGetterExpression(tokens, context);

                //Close Paren
                tokens.AssertAndSkip(Separator.CloseParen);

                IExecutable loopBody = ParseNextStatement(tokens, context);

                return(new ForEachStatement(
                           declarationStatement: declaration,
                           loopVariable: loopVariable,
                           containerExpression: containerExpression,
                           loopBody: loopBody,
                           keywordToken: kwToken));
            }

            case Keyword.Continue:
            case Keyword.Break:
            {
                tokens.CautiousAdvance();
                tokens.AssertAndSkip(Separator.Semicolon, false);
                return(new ControlStatement(kwToken, context));
            }

            case Keyword.Return:
            {
                tokens.CautiousAdvance();
                IExecutable returnStatement = new ReturnStatement(
                    keywordToken: kwToken,
                    returnValue: Expression.ParseNextGetterExpression(tokens, context),
                    context: context);
                tokens.AssertAndSkip(Separator.Semicolon, false);
                return(returnStatement);
            }

            case Keyword.Extern:
            case Keyword.Global:
                throw new ScriptParsingException(
                          source: kwToken,
                          message: $"{kwToken.keyword} variable declarations invalid in local context.  Put them outside classes and methods.");


            case Keyword.Const:
                constDeclaration = true;
                tokens.CautiousAdvance();
                goto case Keyword.Bool;

            case Keyword.Bool:
            case Keyword.Double:
            case Keyword.Integer:
            case Keyword.String:
            case Keyword.List:
            case Keyword.Queue:
            case Keyword.Stack:
            case Keyword.DepletableBag:
            case Keyword.DepletableList:
            case Keyword.RingBuffer:
            case Keyword.Dictionary:
            case Keyword.HashSet:
            case Keyword.Random:
            case Keyword.DataFile:
            {
                Type valueType = tokens.ReadType();

                IdentifierToken identToken = tokens.GetTokenAndAdvance <IdentifierToken>();

                if (tokens.TestAndConditionallySkip(Separator.Semicolon, false))
                {
                    if (constDeclaration)
                    {
                        throw new ScriptParsingException(
                                  source: kwToken,
                                  message: $"const variable declared without a value.  What is the point?");
                    }

                    return(new DeclarationOperation(
                               identifierToken: identToken,
                               valueType: valueType,
                               context: context));
                }
                else if (tokens.TestAndConditionallySkip(Operator.Assignment))
                {
                    IValueGetter initializerExpression = Expression.ParseNextGetterExpression(tokens, context);

                    tokens.AssertAndSkip(Separator.Semicolon, false);

                    return(DeclarationAssignmentOperation.CreateDelcaration(
                               identifierToken: identToken,
                               valueType: valueType,
                               initializer: initializerExpression,
                               isConstant: constDeclaration,
                               context: context));
                }

                throw new ScriptParsingException(
                          source: identToken,
                          message: $"Invalid variable declaration: {kwToken} {identToken} {tokens.Current}");
            }

            case Keyword.System:
            case Keyword.User:
            case Keyword.Debug:
            case Keyword.Math:
            {
                IExecutable identifierStatement =
                    Expression.ParseNextExecutableExpression(tokens, context);
                tokens.AssertAndSkip(Separator.Semicolon, false);
                return(identifierStatement);
            }

            case Keyword.ElseIf:
            case Keyword.Else:
                throw new ScriptParsingException(
                          source: kwToken,
                          message: $"Unpaired {kwToken.keyword} token: {kwToken}");

            default:
                throw new ScriptParsingException(
                          source: kwToken,
                          message: $"A Statement cannot begin with this keyword: {kwToken}");
            }
        }
        private static void HandleNextKeyword(
            KeywordToken keywordToken,
            List <ParsingUnit> units,
            IEnumerator <Token> tokens,
            CompilationContext context)
        {
            switch (keywordToken.keyword)
            {
            case Keyword.New:
            {
                tokens.CautiousAdvance();
                Type newObjectType = tokens.ReadType();

                IValueGetter[] args = ParseArguments(tokens, context);

                if (tokens.TestWithoutSkipping(Separator.OpenCurlyBoi))
                {
                    Type itemType = newObjectType.GetInitializerItemType();
                    if (itemType == null)
                    {
                        throw new ScriptParsingException(
                                  source: keywordToken,
                                  message: $"Initializer Lists only function on collections. " +
                                  $"Did you enter the wrong type, or possibly omit a semicolon at the end of the expression?");
                    }

                    //Initializer Syntax
                    IValueGetter[] items = ParseItems(tokens, itemType, context);

                    units.Add(new ParsedValuedUnit(
                                  value: new ConstructInitializedCollectionExpression(
                                      objectType: newObjectType,
                                      args: args,
                                      items: items,
                                      source: keywordToken),
                                  firstToken: keywordToken));
                }
                else
                {
                    units.Add(new ParsedValuedUnit(
                                  value: new ConstructObjectExpression(
                                      objectType: newObjectType,
                                      args: args),
                                  firstToken: keywordToken));
                }
            }
            break;

            case Keyword.System:
            case Keyword.User:
            case Keyword.Math:
            case Keyword.Debug:
            {
                tokens.CautiousAdvance();
                tokens.AssertAndSkip(Operator.MemberAccess);
                IdentifierToken identifierToken = tokens.GetTokenAndAdvance <IdentifierToken>();
                if (tokens.TestWithoutSkipping(Operator.IsLessThan))
                {
                    //Generic Method
                    Type[] internalTypes = tokens.ReadTypeArguments();

                    units.Add(new ParsedValuedUnit(
                                  value: MemberManagement.HandleStaticGenericMethodExpression(
                                      keywordToken: keywordToken,
                                      args: ParseArguments(tokens, context),
                                      identifier: identifierToken.identifier,
                                      genericTypes: internalTypes),
                                  firstToken: keywordToken));
                }
                else if (tokens.TestWithoutSkipping(Separator.OpenParen))
                {
                    //Method
                    units.Add(new ParsedValuedUnit(
                                  value: MemberManagement.HandleStaticMethodExpression(
                                      keywordToken: keywordToken,
                                      args: ParseArguments(tokens, context),
                                      identifier: identifierToken.identifier),
                                  firstToken: keywordToken));
                }
                else
                {
                    //Member
                    units.Add(new ParsedValuedUnit(
                                  value: MemberManagement.HandleStaticMemberExpression(
                                      keywordToken: keywordToken,
                                      identifier: identifierToken.identifier),
                                  firstToken: keywordToken));
                }
            }
            break;

            default:
                units.Add(new TokenUnit(tokens.Current));
                tokens.CautiousAdvance();
                break;
            }
        }
        private static void HandleNextOperator(
            OperatorToken opToken,
            List <ParsingUnit> units,
            IEnumerator <Token> tokens,
            CompilationContext context)
        {
            switch (opToken.operatorType)
            {
            case Operator.Assignment:
            case Operator.PlusEquals:
            case Operator.MinusEquals:
            case Operator.TimesEquals:
            case Operator.DivideEquals:
            case Operator.PowerEquals:
            case Operator.ModuloEquals:
            case Operator.AndEquals:
            case Operator.OrEquals:
            case Operator.Plus:
            case Operator.Minus:
            case Operator.Times:
            case Operator.Divide:
            case Operator.Power:
            case Operator.Modulo:
            case Operator.CastDouble:
            case Operator.CastInteger:
            case Operator.Increment:
            case Operator.Decrement:
            case Operator.Not:
            case Operator.IsEqualTo:
            case Operator.IsNotEqualTo:
            case Operator.IsGreaterThan:
            case Operator.IsGreaterThanOrEqualTo:
            case Operator.IsLessThan:
            case Operator.IsLessThanOrEqualTo:
            case Operator.And:
            case Operator.Or:
            case Operator.Negate:
                //Add and continue
                units.Add(new TokenUnit(opToken));
                tokens.CautiousAdvance();
                break;

            case Operator.Ternary:
                //Add the ternary Operator
                units.Add(new TokenUnit(opToken));
                tokens.CautiousAdvance();

                //Add the first expected expression
                Token firstToken = tokens.Current;
                units.Add(new ParsedValuedUnit(
                              value: ParseNextExpression(tokens, context),
                              firstToken: firstToken));

                //Colon
                tokens.AssertAndSkip(Separator.Colon);

                //Add the second expected expression
                firstToken = tokens.Current;
                units.Add(new ParsedValuedUnit(
                              value: ParseNextExpression(tokens, context),
                              firstToken: firstToken));
                break;

            case Operator.MemberAccess:
                tokens.CautiousAdvance();
                IdentifierToken nameToken = tokens.GetTokenAndAdvance <IdentifierToken>();
                if (tokens.TestWithoutSkipping(Separator.OpenParen))
                {
                    //Method
                    units.Add(new MemberAccessUnit(
                                  identifier: nameToken.identifier,
                                  args: ParseArguments(tokens, context),
                                  firstToken: opToken));
                }
                else
                {
                    //Value
                    units.Add(new MemberAccessUnit(
                                  identifier: nameToken.identifier,
                                  args: null,
                                  firstToken: opToken));
                }
                break;


            case Operator.AmbiguousMinus:
            default:
                throw new ArgumentException($"Unsupported Operator: {opToken.operatorType}");
            }
        }
Example #10
0
        public void ParseNextGlobal(
            IEnumerator <Token> scriptTokens,
            ScriptCompilationContext context)
        {
            switch (scriptTokens.Current)
            {
            case KeywordToken kwToken:
                //Valid operations:
                //  Global declaration
                //  Extern declaration
                //  Member declaration
                //  Class declaration
                switch (kwToken.keyword)
                {
                case Keyword.Global:
                case Keyword.Extern:
                    //Parse Global Declaration
                {
                    scriptTokens.CautiousAdvance();

                    Type valueType = scriptTokens.ReadType();

                    IdentifierToken identToken            = scriptTokens.GetTokenAndAdvance <IdentifierToken>();
                    IValueGetter    initializerExpression = null;

                    if (scriptTokens.TestAndConditionallySkip(Operator.Assignment))
                    {
                        initializerExpression = Expression.ParseNextGetterExpression(scriptTokens, context);
                    }

                    scriptTokens.AssertAndSkip(Separator.Semicolon, false);

                    scriptDeclarations.Add(
                        new GlobalDeclaration(
                            identifierToken: identToken,
                            valueType: valueType,
                            isExtern: kwToken.keyword == Keyword.Extern,
                            initializer: initializerExpression,
                            context: context));
                }
                    return;

                case Keyword.Const:
                {
                    scriptTokens.CautiousAdvance();

                    Type valueType = scriptTokens.ReadType();

                    IdentifierToken identToken = scriptTokens.GetTokenAndAdvance <IdentifierToken>();
                    scriptTokens.AssertAndSkip(Operator.Assignment);
                    IValueGetter initializerExpression = Expression.ParseNextGetterExpression(scriptTokens, context);

                    scriptTokens.AssertAndSkip(Separator.Semicolon, false);

                    if (!(initializerExpression is LiteralToken litToken))
                    {
                        throw new ScriptParsingException(
                                  source: kwToken,
                                  message: $"The value of Const declarations must be constant");
                    }

                    object value = litToken.GetAs <object>();

                    if (!valueType.IsAssignableFrom(litToken.GetValueType()))
                    {
                        value = Convert.ChangeType(value, valueType);
                    }

                    context.DeclareConstant(
                        identifierToken: identToken,
                        type: valueType,
                        value: value);
                }
                    return;

                case Keyword.Void:
                case Keyword.Bool:
                case Keyword.Double:
                case Keyword.Integer:
                case Keyword.String:
                case Keyword.List:
                case Keyword.Queue:
                case Keyword.Stack:
                case Keyword.DepletableBag:
                case Keyword.DepletableList:
                case Keyword.RingBuffer:
                case Keyword.Dictionary:
                case Keyword.HashSet:
                case Keyword.Random:
                case Keyword.DataFile:
                    //Parse Function or Member Declaration
                {
                    Type            valueType  = scriptTokens.ReadType();
                    IdentifierToken identToken = scriptTokens.GetTokenAndAdvance <IdentifierToken>();

                    if (scriptTokens.TestWithoutSkipping(Separator.OpenParen))
                    {
                        VariableData[] arguments = ParseArgumentsDeclaration(scriptTokens);

                        if (scriptFunctions.ContainsKey(identToken.identifier))
                        {
                            throw new ScriptParsingException(
                                      source: identToken,
                                      message: $"Two declarations of function {identToken.identifier} found.");
                        }

                        scriptFunctions.Add(identToken.identifier,
                                            new ScriptFunction(
                                                functionTokens: scriptTokens,
                                                functionSignature: new FunctionSignature(
                                                    identifierToken: identToken,
                                                    returnType: valueType,
                                                    arguments: arguments),
                                                context: context));
                    }
                    else
                    {
                        //Member declaration
                        if (kwToken.keyword == Keyword.Void)
                        {
                            throw new ScriptParsingException(
                                      source: kwToken,
                                      message: $"Cannot declare a member of type Void");
                        }

                        IValueGetter initializerExpression = null;
                        if (scriptTokens.TestAndConditionallySkip(Operator.Assignment))
                        {
                            initializerExpression = Expression.ParseNextGetterExpression(scriptTokens, context);
                        }

                        scriptTokens.AssertAndSkip(Separator.Semicolon, false);

                        scriptDeclarations.Add(
                            new MemberDeclaration(
                                identifierToken: identToken,
                                valueType: valueType,
                                initializer: initializerExpression,
                                context: context));
                    }
                }
                    return;

                default:
                    throw new ScriptParsingException(
                              source: kwToken,
                              message: $"Token not valid for global context: {kwToken}.");
                }

            default:
                throw new ScriptParsingException(
                          source: scriptTokens.Current,
                          message: $"Token not valid for global context: {scriptTokens.Current}.");
            }
        }