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}"); } }
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}"); } }
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}."); } }