/// <summary> /// Adds a statement to the unresolved list /// </summary> /// <param name="script"></param> /// <param name="statement"></param> /// <returns></returns> public bool AddUnresolvedStatement(LuatScript script, Statement statement) { LinkedListNode<Statement> node = statement.ListNode[script]; if (node.List == script.UnresolvedStatements) { return false; } if (null != node.List) { if (node.List != script.ResolvedStatements) { throw new Exception("Statement is part of an unknown list"); } node.List.Remove(node); } script.UnresolvedStatements.AddFirst(node); return true; }
/// <summary> /// Matches a <c>ForStatement</c> non-terminal. /// </summary> /// <returns><c>true</c> if the <c>ForStatement</c> was matched successfully; otherwise, <c>false</c>.</returns> /// <remarks> /// The non-terminal can start with: <c>For</c>. /// </remarks> protected virtual bool MatchForStatement(out Statement statement) { statement = null; int startOffset = this.LookAheadToken.StartOffset; if (!this.Match(LuatTokenId.For)) return false; Identifier firstIdent = null; if (!this.MatchIdentifier(out firstIdent)) return false; if (this.TokenIs(this.LookAheadToken, LuatTokenId.Assignment)) { // for a = b, c, d do ... end VariableExpression firstVar = new VariableExpression( firstIdent ); ForStatement forStatement = new ForStatement(); statement = forStatement; forStatement.Iterator = firstVar as VariableExpression; if (!this.Match(LuatTokenId.Assignment)) return false; Expression start = null; if (!this.MatchExpression(out start)) return false; forStatement.Start = start; if (!this.Match(LuatTokenId.Comma)) return false; Expression end = null; if (!this.MatchExpression(out end)) return false; forStatement.End = end; if (this.TokenIs(this.LookAheadToken, LuatTokenId.Comma)) { if (!this.Match(LuatTokenId.Comma)) return false; Expression step = null; if (!this.MatchExpression(out step)) return false; forStatement.Step = step; } if (!this.Match(LuatTokenId.Do)) return false; BlockStatement body; if (!this.MatchBlock(out body)) return false; forStatement.Body = body; if (!this.Match(LuatTokenId.End)) return false; forStatement.StartOffset = startOffset; forStatement.EndOffset = this.Token.EndOffset; } else if (((this.TokenIs(this.LookAheadToken, LuatTokenId.Comma)) || (this.TokenIs(this.LookAheadToken, LuatTokenId.In)))) { // for a,b in c,d do ... end ForInStatement forInStatement = new ForInStatement(); statement = forInStatement; forInStatement.Iterators.Add( firstIdent ); if (this.TokenIs(this.LookAheadToken, LuatTokenId.Comma)) { if (!this.Match(LuatTokenId.Comma)) return false; if (!this.MatchIdentifierList(forInStatement.Iterators)) return false; } if (!this.Match(LuatTokenId.In)) return false; if (!this.MatchExpressionList(forInStatement.Tables)) return false; if (!this.Match(LuatTokenId.Do)) return false; BlockStatement body; if (!this.MatchBlock(out body)) return false; forInStatement.Body = body; if (!this.Match(LuatTokenId.End)) return false; forInStatement.StartOffset = startOffset; forInStatement.EndOffset = this.Token.EndOffset; } else return false; return true; }
/// <summary> /// Matches a <c>LocalDeclaration</c> non-terminal. /// </summary> /// <returns><c>true</c> if the <c>LocalDeclaration</c> was matched successfully; otherwise, <c>false</c>.</returns> /// <remarks> /// The non-terminal can start with: <c>Local</c>. /// </remarks> protected virtual bool MatchLocalDeclaration(out Statement statement) { statement = null; int start = this.LookAheadToken.StartOffset; if (!this.Match(LuatTokenId.Local)) return false; if (!this.MatchLocalSuffix(out statement)) { return this.RaiseError( start, "Expected identifier or function declaration." ); } else { statement.StartOffset = start; statement.EndOffset = this.Token.EndOffset; } return true; }
/// <summary> /// Matches a <c>RepeatStatement</c> non-terminal. /// </summary> /// <returns><c>true</c> if the <c>RepeatStatement</c> was matched successfully; otherwise, <c>false</c>.</returns> /// <remarks> /// The non-terminal can start with: <c>Repeat</c>. /// </remarks> protected virtual bool MatchRepeatStatement(out Statement statement) { statement = null; RepeatStatement repeatStatement = new RepeatStatement(); repeatStatement.StartOffset = this.Token.StartOffset; statement = repeatStatement; if (!this.Match(LuatTokenId.Repeat)) return false; BlockStatement Block; if (!this.MatchBlock(out Block)) return false; repeatStatement.Block = Block; if (!this.Match(LuatTokenId.Until)) return false; Expression conditional; if (!this.MatchExpression(out conditional)) return false; repeatStatement.Conditional = conditional; repeatStatement.EndOffset = this.Token.EndOffset; return true; }
/// <summary> /// Matches a <c>IfStatement</c> non-terminal. /// </summary> /// <returns><c>true</c> if the <c>IfStatement</c> was matched successfully; otherwise, <c>false</c>.</returns> /// <remarks> /// The non-terminal can start with: <c>If</c>. /// </remarks> protected virtual bool MatchIfStatement(out Statement statement) { IfStatement ifStatement = new IfStatement(); statement = ifStatement; Expression conditional; BlockStatement block; ifStatement.StartOffset = this.LookAheadToken.StartOffset; if (!this.Match(LuatTokenId.If)) return false; if (!this.MatchExpression(out conditional)) return false; ifStatement.Conditional = conditional; if (!this.Match(LuatTokenId.Then)) return false; if (!this.MatchBlock(out block)) return false; ifStatement.Block = block; while (this.TokenIs(this.LookAheadToken, LuatTokenId.Elseif)) { if (!this.Match(LuatTokenId.Elseif)) return false; ConditionalBlock conditionalBlock = new ConditionalBlock(); if (!this.MatchExpression(out conditional)) return false; conditionalBlock.Conditional = conditional; if (!this.Match(LuatTokenId.Then)) return false; if (!this.MatchBlock(out block)) return false; conditionalBlock.Block = block; ifStatement.ElseIfs.Add( conditionalBlock ); } if (this.TokenIs(this.LookAheadToken, LuatTokenId.Else)) { if (!this.Match(LuatTokenId.Else)) return false; if (!this.MatchBlock(out block)) return false; ifStatement.Else = block; } if (!this.Match(LuatTokenId.End)) return false; ifStatement.EndOffset = this.Token.EndOffset; return true; }
/// <summary> /// Matches a <c>DoStatement</c> non-terminal. /// </summary> /// <returns><c>true</c> if the <c>DoStatement</c> was matched successfully; otherwise, <c>false</c>.</returns> /// <remarks> /// The non-terminal can start with: <c>Do</c>. /// </remarks> protected virtual bool MatchDoStatement(out Statement statement) { statement = null; DoStatement doStatement = new DoStatement(); doStatement.StartOffset = this.Token.StartOffset; statement = doStatement; if (!this.Match(LuatTokenId.Do)) return false; BlockStatement body; if (!this.MatchBlock(out body)) return false; doStatement.Body = body; if (!this.Match(LuatTokenId.End)) return false; doStatement.EndOffset = this.Token.EndOffset; return true; }
/// <summary> /// Matches a <c>WhileStatement</c> non-terminal. /// </summary> /// <returns><c>true</c> if the <c>WhileStatement</c> was matched successfully; otherwise, <c>false</c>.</returns> /// <remarks> /// The non-terminal can start with: <c>While</c>. /// </remarks> protected virtual bool MatchWhileStatement(out Statement statement) { statement = null; if (!this.Match(LuatTokenId.While)) return false; WhileStatement whileStatement = new WhileStatement(); whileStatement.StartOffset = this.Token.StartOffset; statement = whileStatement; Expression conditional; if (!this.MatchExpression(out conditional)) return false; whileStatement.Conditional = conditional; if (!this.Match(LuatTokenId.Do)) return false; BlockStatement Block; if (!this.MatchBlock(out Block)) return false; whileStatement.Block = Block; if (!this.Match(LuatTokenId.End)) return false; whileStatement.EndOffset = this.Token.EndOffset; return true; }
/// <summary> /// Matches a <c>LastStatementInner</c> non-terminal. /// </summary> /// <returns><c>true</c> if the <c>LastStatementInner</c> was matched successfully; otherwise, <c>false</c>.</returns> /// <remarks> /// The non-terminal can start with: <c>Return</c>, <c>Break</c>. /// </remarks> protected virtual bool MatchLastStatementInner(out Statement statement) { statement = null; if (this.TokenIs(this.LookAheadToken, LuatTokenId.Return)) { if (!this.MatchReturnStatement(out statement)) return false; } else if (this.TokenIs(this.LookAheadToken, LuatTokenId.Break)) { if (!this.MatchBreakStatement(out statement)) return false; } else return false; return true; }
/// <summary> /// Matches a <c>AssignmentOrFunctionCall</c> non-terminal. /// </summary> /// <returns><c>true</c> if the <c>AssignmentOrFunctionCall</c> was matched successfully; otherwise, <c>false</c>.</returns> /// <remarks> /// The non-terminal can start with: <c>OpenParenthesis</c>, <c>OpenCurlyBrace</c>, <c>Identifier</c>, <c>TripleDot</c>, <c>Nil</c>, <c>False</c>, <c>True</c>, <c>Number</c>, <c>Subtraction</c>, <c>Not</c>, <c>Hash</c>, <c>String</c>. /// </remarks> protected virtual bool MatchAssignmentOrFunctionCall(out Statement statement) { Expression expression = null; statement = null; int start = this.LookAheadToken.StartOffset; if (!this.MatchExpressionNoFunction(out expression)) return false; if ( IsFunctionCall( expression ) ) { statement = new ExpressionStatement( expression ); return true; } AssignmentStatement assignment = new AssignmentStatement(); statement = assignment; assignment.Variables.Add( expression ); while (this.TokenIs(this.LookAheadToken, LuatTokenId.Comma)) { if (!this.Match(LuatTokenId.Comma)) return false; if (!this.MatchExpressionNoFunction(out expression)) return false; assignment.Variables.Add( expression ); } if (!this.Match(LuatTokenId.Assignment)) return false; if (!this.MatchExpressionList(assignment.Values)) return false; foreach( Expression variable in assignment.Variables ) { // Mark the LHS expression as being assigned. // This alters the way certain variables are resolved. variable.IsLHSOfAssignment = true; if( false == IsVariable( variable ) ) { // First expression was not a variable or a function call. // Invalid statement string message = "The left-hand side of an assignment must be a variable"; compilationUnit.SyntaxErrors.Add(new SyntaxError( variable.TextRange, message ) ); } } assignment.StartOffset = start; assignment.EndOffset = this.Token.EndOffset; return true; }
/// <summary> /// Matches a <c>StatementInner</c> non-terminal. /// </summary> /// <returns><c>true</c> if the <c>StatementInner</c> was matched successfully; otherwise, <c>false</c>.</returns> /// <remarks> /// The non-terminal can start with: <c>Do</c>, <c>While</c>, <c>Repeat</c>, <c>If</c>, <c>For</c>, <c>Local</c>, <c>Function</c>, <c>OpenParenthesis</c>, <c>OpenCurlyBrace</c>, <c>Identifier</c>, <c>TripleDot</c>, <c>Nil</c>, <c>False</c>, <c>True</c>, <c>Number</c>, <c>Subtraction</c>, <c>Not</c>, <c>Hash</c>, <c>String</c>. /// </remarks> protected virtual bool MatchStatementInner(out Statement statement) { statement = null; if (this.IsInMultiMatchSet(2, this.LookAheadToken)) { if (!this.MatchAssignmentOrFunctionCall(out statement)) return false; } else if (this.TokenIs(this.LookAheadToken, LuatTokenId.Do)) { if (!this.MatchDoStatement(out statement)) return false; } else if (this.TokenIs(this.LookAheadToken, LuatTokenId.While)) { if (!this.MatchWhileStatement(out statement)) return false; } else if (this.TokenIs(this.LookAheadToken, LuatTokenId.Repeat)) { if (!this.MatchRepeatStatement(out statement)) return false; } else if (this.TokenIs(this.LookAheadToken, LuatTokenId.If)) { if (!this.MatchIfStatement(out statement)) return false; } else if (this.TokenIs(this.LookAheadToken, LuatTokenId.For)) { if (!this.MatchForStatement(out statement)) return false; } else if (this.TokenIs(this.LookAheadToken, LuatTokenId.Function)) { if (!this.MatchFunctionDeclaration(out statement)) return false; } else if (this.TokenIs(this.LookAheadToken, LuatTokenId.Local)) { if (!this.MatchLocalDeclaration(out statement)) return false; } else return false; return true; }
/// <summary> /// Matches a <c>BreakStatement</c> non-terminal. /// </summary> /// <returns><c>true</c> if the <c>BreakStatement</c> was matched successfully; otherwise, <c>false</c>.</returns> /// <remarks> /// The non-terminal can start with: <c>Break</c>. /// </remarks> protected virtual bool MatchBreakStatement(out Statement statement) { statement = null; if (!this.Match(LuatTokenId.Break)) return false; statement = new BreakStatement( this.Token.TextRange ); return true; }
/// <summary> /// Matches a <c>ReturnStatement</c> non-terminal. /// </summary> /// <returns><c>true</c> if the <c>ReturnStatement</c> was matched successfully; otherwise, <c>false</c>.</returns> /// <remarks> /// The non-terminal can start with: <c>Return</c>. /// </remarks> protected virtual bool MatchReturnStatement(out Statement statement) { statement = null; int start = this.LookAheadToken.StartOffset; if (!this.Match(LuatTokenId.Return)) return false; ReturnStatement returnStatement = new ReturnStatement(); returnStatement.IsMultiline = this.LexicalParser.LookAheadTokenIsOnDifferentLine; if (this.IsInMultiMatchSet(3, this.LookAheadToken)) { if (!this.MatchExpressionList(returnStatement.Values)) return false; } returnStatement.StartOffset = start; returnStatement.EndOffset = this.Token.EndOffset; if ( returnStatement.Values.Count == 0 ) { returnStatement.IsMultiline = false; } statement = returnStatement; return true; }
/// <summary> /// Matches a <c>FunctionDeclaration</c> non-terminal. /// </summary> /// <returns><c>true</c> if the <c>FunctionDeclaration</c> was matched successfully; otherwise, <c>false</c>.</returns> /// <remarks> /// The non-terminal can start with: <c>Function</c>. /// </remarks> protected virtual bool MatchFunctionDeclaration(out Statement statement) { statement = null; int start = this.LookAheadToken.StartOffset; string description = this.GetDescription(); bool expectsSelf; if (!this.Match(LuatTokenId.Function)) return false; Expression name; if (!this.MatchFunctionName(out name, out expectsSelf)) return false; Function function; if (!this.MatchFunctionBody(out function)) return false; AssignmentStatement assignment = new AssignmentStatement(); assignment.Variables.Add( name ); assignment.Values.Add( function ); // Use the preceding comment as the function description function.Description = (null != description) ? description.Trim( '\n', '\r' ) : null; function.ExpectsSelf = expectsSelf; // Mark the LHS expression as being assigned. // This alters the way certain variables are resolved. name.IsLHSOfAssignment = true; assignment.StartOffset = start; assignment.EndOffset = this.Token.EndOffset; statement = assignment; return true; }
/// <summary> /// Matches a <c>LocalSuffix</c> non-terminal. /// </summary> /// <returns><c>true</c> if the <c>LocalSuffix</c> was matched successfully; otherwise, <c>false</c>.</returns> /// <remarks> /// The non-terminal can start with: <c>Function</c>, <c>Identifier</c>. /// </remarks> protected virtual bool MatchLocalSuffix(out Statement statement) { statement = null; AssignmentStatement assignment = new AssignmentStatement(); assignment.IsLocal = true; Identifier type = null; if (this.TokenIs(this.LookAheadToken, LuatTokenId.Identifier)) { if (!this.MatchVariableList(assignment.Variables)) return false; if (this.TokenIs(this.LookAheadToken, LuatTokenId.Colon)) { if (!this.Match(LuatTokenId.Colon)) return false; if (!this.MatchIdentifier(out type)) return false; } if (this.TokenIs(this.LookAheadToken, LuatTokenId.Assignment)) { if (!this.Match(LuatTokenId.Assignment)) return false; if (!this.MatchExpressionList(assignment.Values)) return false; } } else if (this.TokenIs(this.LookAheadToken, LuatTokenId.Function)) { if (!this.Match(LuatTokenId.Function)) return false; Expression variableNode; if (!this.MatchVariable(out variableNode)) return false; Function function; if (!this.MatchFunctionBody(out function)) return false; VariableExpression variableExpression = variableNode as VariableExpression; assignment.Variables.Add( variableNode ); assignment.Values.Add( function ); } else return false; foreach ( VariableExpression v in assignment.Variables ) { v.IsLHSOfAssignment = true; v.IsLocal = true; v.Type = type == null ? null : type.Text; } statement = assignment; return true; }