diff --git a/GParse/Parsing/IPrattParser.cs b/GParse/Parsing/IPrattParser.cs index 2d6337e..5e3e544 100644 --- a/GParse/Parsing/IPrattParser.cs +++ b/GParse/Parsing/IPrattParser.cs @@ -16,12 +16,22 @@ public interface IPrattParser ITokenReader TokenReader { get; } /// - /// Attempts to parse an expression + /// Attempts to parse an expression with a minimum precedence of + /// . /// - /// + /// + /// The minimum precedence is used to enforce the precedence of operators as well as + /// associativity. + /// + /// The + /// uses the parameter to implement associativity by passing in + /// the associativity of the operator subtracted by one so that the operator itself is in the set + /// of possible parselets. + /// + /// /// /// - Boolean TryParseExpression ( Int32 precedence, out ExpressionNodeT expression ); + Boolean TryParseExpression ( Int32 minPrecedence, out ExpressionNodeT expression ); /// /// Attempts to parse an expression diff --git a/GParse/Parsing/Parselets/SingleTokenInfixOperatorParselet.cs b/GParse/Parsing/Parselets/SingleTokenInfixOperatorParselet.cs index d817873..77607dd 100644 --- a/GParse/Parsing/Parselets/SingleTokenInfixOperatorParselet.cs +++ b/GParse/Parsing/Parselets/SingleTokenInfixOperatorParselet.cs @@ -55,8 +55,21 @@ public Boolean TryParse ( IPrattParser parser, Expr { parsedExpression = default; Token op = parser.TokenReader.Consume ( ); - return parser.TryParseExpression ( this.isRightAssociative ? this.Precedence - 1 : this.Precedence, out ExpressionNodeT nextExpr ) - && this.factory ( expression, op, nextExpr, out parsedExpression ); + + // We decrease the precedence by one on right-associative operators because the minimum + // precedence passed to TryParseExpression is exclusive (meaning that the precedence of the + // infix parselets must be higher than the one we pass it. + // TODO: Check if this cannot create bugs with other operators that have the same precedence. + Int32 minPrecedence; + if ( this.isRightAssociative ) + minPrecedence = this.Precedence - 1; + else + minPrecedence = this.Precedence; + + if ( parser.TryParseExpression ( minPrecedence, out ExpressionNodeT nextExpr ) ) + return this.factory ( expression, op, nextExpr, out parsedExpression ); + else + return false; } } } diff --git a/GParse/Parsing/Parselets/SingleTokenPrefixOperatorParselet.cs b/GParse/Parsing/Parselets/SingleTokenPrefixOperatorParselet.cs index cc08448..4dacd62 100644 --- a/GParse/Parsing/Parselets/SingleTokenPrefixOperatorParselet.cs +++ b/GParse/Parsing/Parselets/SingleTokenPrefixOperatorParselet.cs @@ -49,8 +49,10 @@ public Boolean TryParse ( IPrattParser parser, IPro { parsedExpression = default; Token prefix = parser.TokenReader.Consume ( ); - return parser.TryParseExpression ( this.precedence, out ExpressionNodeT expression ) - && this.factory ( prefix, expression, out parsedExpression ); + if ( parser.TryParseExpression ( this.precedence, out ExpressionNodeT expression ) ) + return this.factory ( prefix, expression, out parsedExpression ); + else + return false; } } } diff --git a/GParse/Parsing/PrattParser.cs b/GParse/Parsing/PrattParser.cs index 38233fa..15f6525 100644 --- a/GParse/Parsing/PrattParser.cs +++ b/GParse/Parsing/PrattParser.cs @@ -12,23 +12,21 @@ namespace GParse.Parsing public class PrattParser : IPrattParser { /// - /// The registered + /// The this holds the tree of to be used while parsing expressions. /// protected readonly PrattParserModuleTree> prefixModuleTree; /// - /// The registered + /// This holds the tree of to be used while parsing expressions. /// protected readonly PrattParserModuleTree> infixModuleTree; /// - /// The emitter provided to the constructor + /// This is the reporter to which the parser should send Diagnostics to. /// protected readonly IProgress diagnosticReporter; - /// - /// The parser's token reader - /// + /// public ITokenReader TokenReader { get; } /// @@ -50,13 +48,8 @@ protected internal PrattParser ( ITokenReader tokenReader, PrattPars #region ParseExpression - /// /// - /// - /// - /// - /// - public virtual Boolean TryParseExpression ( Int32 precedence, out ExpressionNodeT expression ) + public virtual Boolean TryParseExpression ( Int32 minPrecedence, out ExpressionNodeT expression ) { expression = default; var foundExpression = false; @@ -81,7 +74,7 @@ public virtual Boolean TryParseExpression ( Int32 precedence, out ExpressionNode foreach ( IInfixParselet module in this.infixModuleTree.GetSortedCandidates ( this.TokenReader ) ) { SourceLocation start = this.TokenReader.Location; - if ( precedence < module.Precedence + if ( minPrecedence < module.Precedence && module.TryParse ( this, expression, this.diagnosticReporter, out ExpressionNodeT tmpExpr ) ) { couldParse = true; @@ -97,11 +90,7 @@ public virtual Boolean TryParseExpression ( Int32 precedence, out ExpressionNode return true; } - /// /// - /// - /// - /// public virtual Boolean TryParseExpression ( out ExpressionNodeT expression ) => this.TryParseExpression ( 0, out expression );