Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added new overloads to LeftAssociative, RightAssociative and Unary #128

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
136 changes: 136 additions & 0 deletions src/Parlot/Fluent/Parsers.LeftAssociative.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
using System;
using System.Linq;

namespace Parlot.Fluent
{
public static partial class Parsers
{
/// <summary>
/// Builds a parser that creates a left-associative structure.
/// c.f. https://en.wikipedia.org/wiki/Operator_associativity
/// </summary>
/// <typeparam name="T">The type of the returned parser.</typeparam>
/// <typeparam name="TInput">The type of the symbol parsers.</typeparam>
/// <param name="parser">The higher-priority parser the symbols are separating.</param>
/// <param name="list">The list of operators that can be parsed and their associated result factory methods.</param>
/// <returns></returns>
/// <example>
/// // additive => multiplicative(("-" | "+") multiplicative) * ;
/// var additive = multiplicative.LeftAssociative(
/// (plus, static (a, b) => new Addition(a, b)),
/// (minus, static (a, b) => new Subtraction(a, b))
/// );
/// </example>
public static Parser<T> LeftAssociative<T, TInput>(this Parser<T> parser, params (Parser<TInput> op, Func<T, T, T> factory)[] list)
{
var choices = list.Select(l => new Then<TInput, Func<T, T, T>>(l.op, l.factory));

return parser.And(ZeroOrMany(new OneOf<Func<T, T, T>>(choices.ToArray()).And(parser)))
.Then(static x =>
{
// multiplicative
var result = x.Item1;

// (("-" | "+") multiplicative ) *
foreach (var op in x.Item2)
{
result = op.Item1(result, op.Item2);
}

return result;
});
}

/// <summary>
/// Builds a parser that creates a left-associative structure.
/// c.f. https://en.wikipedia.org/wiki/Operator_associativity
/// </summary>
/// <typeparam name="T">The type of the returned parser.</typeparam>
/// <typeparam name="TInput">The type of the symbol parsers.</typeparam>
/// <param name="parser">The higher-priority parser the symbols are separating.</param>
/// <param name="list">The list of operators that can be parsed and their associated result factory methods.</param>
/// <returns></returns>
/// <example>
/// // additive => multiplicative(("-" | "+") multiplicative) * ;
/// var additive = multiplicative.LeftAssociative(
/// (plus, static (a, b) => new Addition(a, b)),
/// (minus, static (a, b) => new Subtraction(a, b))
/// );
/// </example>
public static Parser<T> LeftAssociative<T, TInput>(this Parser<T> parser, params (Parser<TInput> op, Func<ParseContext, T, T, T> factory)[] list)
{
var choices = list.Select(l => new Then<TInput, Func<ParseContext, T, T, T>>(l.op, l.factory));

return parser.And(ZeroOrMany(new OneOf<Func<ParseContext, T, T, T>>(choices.ToArray()).And(parser)))
.Then(static (context, x) =>
{
// multiplicative
var result = x.Item1;

// (("-" | "+") multiplicative ) *
foreach (var op in x.Item2)
{
result = op.Item1(context, result, op.Item2);
}

return result;
});
}

/// <summary>
/// Builds a parser that creates a left-associative structure.
/// c.f. https://en.wikipedia.org/wiki/Operator_associativity
/// </summary>
/// <typeparam name="T">The type of the returned parser.</typeparam>
/// <typeparam name="TInput">The type of the symbol parsers.</typeparam>
/// <param name="parser">The higher-priority parser the symbols are separating.</param>
/// <param name="ops">The array of operators that can be parsed.</param>
/// <param name="factory">The factory method to create the result of the operation.</param>
/// <returns>A parser that creates a left-associative structure.</returns>
public static Parser<T> LeftAssociative<T, TInput>(this Parser<T> parser, Parser<TInput>[] ops, Func<TInput, T, T, T> factory)
{
var choices = ops.Select(op => new Then<TInput, Func<T, T, T>>(op, operation => (a, b) => factory(operation, a, b)));

return parser.And(ZeroOrMany(new OneOf<Func<T, T, T>>(choices.ToArray()).And(parser)))
.Then(static x =>
{
var result = x.Item1;

foreach (var op in x.Item2)
{
result = op.Item1(result, op.Item2);
}

return result;
});
}

/// <summary>
/// Builds a parser that creates a left-associative structure.
/// c.f. https://en.wikipedia.org/wiki/Operator_associativity
/// </summary>
/// <typeparam name="T">The type of the returned parser.</typeparam>
/// <typeparam name="TInput">The type of the symbol parsers.</typeparam>
/// <param name="parser">The higher-priority parser the symbols are separating.</param>
/// <param name="ops">The array of operators that can be parsed.</param>
/// <param name="factory">The factory method to create the result of the operation.</param>
/// <returns>A parser that creates a left-associative structure.</returns>
public static Parser<T> LeftAssociative<T, TInput>(this Parser<T> parser, Parser<TInput>[] ops, Func<ParseContext, TInput, T, T, T> factory)
{
var choices = ops.Select(op => new Then<TInput, Func<ParseContext, T, T, T>>(op, operation => (context, a, b) => factory(context, operation, a, b)));

return parser.And(ZeroOrMany(new OneOf<Func<ParseContext, T, T, T>>(choices.ToArray()).And(parser)))
.Then(static (context, x) =>
{
var result = x.Item1;

foreach (var op in x.Item2)
{
result = op.Item1(context, result, op.Item2);
}

return result;
});
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ namespace Parlot.Fluent
public static partial class Parsers
{
/// <summary>
/// Builds a parser that creates a left-associative structure.
/// Builds a parser that creates a right-associative structure.
/// c.f. https://en.wikipedia.org/wiki/Operator_associativity
/// </summary>
/// <typeparam name="T">The type of the returned parser.</typeparam>
Expand All @@ -15,34 +15,47 @@ public static partial class Parsers
/// <param name="list">The list of operators that can be parsed and their associated result factory methods.</param>
/// <returns></returns>
/// <example>
/// // additive => multiplicative(("-" | "+") multiplicative) * ;
/// var additive = multiplicative.LeftAssociative(
/// (plus, static (a, b) => new Addition(a, b)),
/// (minus, static (a, b) => new Subtraction(a, b))
/// // exponentiation => primary( ("^") primary) * ;
/// var exponentiation = primary.RightAssociative(
/// (equal, static (a, b) => new Exponent(a, b))
/// );
/// </example>
public static Parser<T> LeftAssociative<T, TInput>(this Parser<T> parser, params (Parser<TInput> op, Func<T, T, T> factory)[] list)
public static Parser<T> RightAssociative<T, TInput>(this Parser<T> parser,
params (Parser<TInput> op, Func<T, T, T> factory)[] list)
{
var choices = list.Select(l => new Then<TInput, Func<T, T, T>>(l.op, l.factory));

return parser.And(ZeroOrMany(new OneOf<Func<T, T, T>>(choices.ToArray()).And(parser)))
.Then(static x =>
{
// multiplicative
var result = x.Item1;
// a; (=, b); (^, c) -> = (a, ^(b, c))

var operations = x.Item2;

T result;

if (operations.Count > 0)
{
result = operations[operations.Count - 1].Item2;

for (var i = operations.Count - 1; i > 0; i--)
{
result = operations[i].Item1(operations[i - 1].Item2, result);
}

// (("-" | "+") multiplicative ) *
foreach (var op in x.Item2)
result = operations[0].Item1(x.Item1, result);
}
else
{
result = op.Item1(result, op.Item2);
result = x.Item1;
}

return result;
});
}

/// <summary>
/// Builds a parser that creates a left-associative structure.
/// Builds a parser that creates a right-associative structure.
/// c.f. https://en.wikipedia.org/wiki/Operator_associativity
/// </summary>
/// <typeparam name="T">The type of the returned parser.</typeparam>
Expand All @@ -51,26 +64,39 @@ public static Parser<T> LeftAssociative<T, TInput>(this Parser<T> parser, params
/// <param name="list">The list of operators that can be parsed and their associated result factory methods.</param>
/// <returns></returns>
/// <example>
/// // additive => multiplicative(("-" | "+") multiplicative) * ;
/// var additive = multiplicative.LeftAssociative(
/// (plus, static (a, b) => new Addition(a, b)),
/// (minus, static (a, b) => new Subtraction(a, b))
/// // exponentiation => primary( ("^") primary) * ;
/// var exponentiation = primary.RightAssociative(
/// (equal, static (a, b) => new Exponent(a, b))
/// );
/// </example>
public static Parser<T> LeftAssociative<T, TInput>(this Parser<T> parser, params (Parser<TInput> op, Func<ParseContext, T, T, T> factory)[] list)
public static Parser<T> RightAssociative<T, TInput>(this Parser<T> parser,
params (Parser<TInput> op, Func<ParseContext, T, T, T> factory)[] list)
{
var choices = list.Select(l => new Then<TInput, Func<ParseContext, T, T, T>>(l.op, l.factory));

return parser.And(ZeroOrMany(new OneOf<Func<ParseContext, T, T, T>>(choices.ToArray()).And(parser)))
.Then(static (context, x) =>
{
// multiplicative
var result = x.Item1;
// a; (=, b); (^, c) -> = (a, ^(b, c))

var operations = x.Item2;

// (("-" | "+") multiplicative ) *
foreach (var op in x.Item2)
T result;

if (operations.Count > 0)
{
result = op.Item1(context, result, op.Item2);
result = operations[operations.Count - 1].Item2;

for (var i = operations.Count - 1; i > 0; i--)
{
result = operations[i].Item1(context, operations[i - 1].Item2, result);
}

result = operations[0].Item1(context, x.Item1, result);
}
else
{
result = x.Item1;
}

return result;
Expand All @@ -84,26 +110,20 @@ public static Parser<T> LeftAssociative<T, TInput>(this Parser<T> parser, params
/// <typeparam name="T">The type of the returned parser.</typeparam>
/// <typeparam name="TInput">The type of the symbol parsers.</typeparam>
/// <param name="parser">The higher-priority parser the symbols are separating.</param>
/// <param name="list">The list of operators that can be parsed and their associated result factory methods.</param>
/// <returns></returns>
/// <example>
/// // exponentiation => primary( ("^") primary) * ;
/// var exponentiation = primary.RightAssociative(
/// (equal, static (a, b) => new Exponent(a, b))
/// );
/// </example>
public static Parser<T> RightAssociative<T, TInput>(this Parser<T> parser, params (Parser<TInput> op, Func<T, T, T> factory)[] list)
/// <param name="ops">The list of operator parsers.</param>
/// <param name="factory">The factory method for creating results from operators and operands.</param>
/// <returns>The parser for a right-associative structure.</returns>
public static Parser<T> RightAssociative<T, TInput>(this Parser<T> parser, Parser<TInput>[] ops,
Func<TInput, T, T, T> factory)
{
var choices = list.Select(l => new Then<TInput, Func<T, T, T>>(l.op, l.factory));
var choices = ops.Select(op =>
new Then<TInput, Func<T, T, T>>(op, operation => (a, b) => factory(operation, a, b)));

return parser.And(ZeroOrMany(new OneOf<Func<T, T, T>>(choices.ToArray()).And(parser)))
.Then(static x =>
{
// a; (=, b); (^, c) -> = (a, ^(b, c))

var operations = x.Item2;

T result = default;
T result;

if (operations.Count > 0)
{
Expand Down Expand Up @@ -132,26 +152,21 @@ public static Parser<T> RightAssociative<T, TInput>(this Parser<T> parser, param
/// <typeparam name="T">The type of the returned parser.</typeparam>
/// <typeparam name="TInput">The type of the symbol parsers.</typeparam>
/// <param name="parser">The higher-priority parser the symbols are separating.</param>
/// <param name="list">The list of operators that can be parsed and their associated result factory methods.</param>
/// <returns></returns>
/// <example>
/// // exponentiation => primary( ("^") primary) * ;
/// var exponentiation = primary.RightAssociative(
/// (equal, static (a, b) => new Exponent(a, b))
/// );
/// </example>
public static Parser<T> RightAssociative<T, TInput>(this Parser<T> parser, params (Parser<TInput> op, Func<ParseContext, T, T, T> factory)[] list)
/// <param name="ops">The list of operator parsers.</param>
/// <param name="factory">The factory method for creating results from operators and operands.</param>
/// <returns>The parser for a right-associative structure.</returns>
public static Parser<T> RightAssociative<T, TInput>(this Parser<T> parser, Parser<TInput>[] ops,
Func<ParseContext, TInput, T, T, T> factory)
{
var choices = list.Select(l => new Then<TInput, Func<ParseContext, T, T, T>>(l.op, l.factory));
var choices = ops.Select(op =>
new Then<TInput, Func<ParseContext, T, T, T>>(op,
operation => (context, a, b) => factory(context, operation, a, b)));

return parser.And(ZeroOrMany(new OneOf<Func<ParseContext, T, T, T>>(choices.ToArray()).And(parser)))
.Then(static (context, x) =>
{
// a; (=, b); (^, c) -> = (a, ^(b, c))

var operations = x.Item2;

T result = default;
T result;

if (operations.Count > 0)
{
Expand All @@ -172,39 +187,5 @@ public static Parser<T> RightAssociative<T, TInput>(this Parser<T> parser, param
return result;
});
}

/// <summary>
/// Builds a parser that creates a unary operation.
/// </summary>
/// <typeparam name="T">The type of the returned parser.</typeparam>
/// <typeparam name="TInput">The type of the symbol parsers.</typeparam>
/// <param name="parser">The higher-priority parser the symbols are separating.</param>
/// <param name="list">The list of operators that can be parsed and their associated result factory methods.</param>
/// <returns></returns>
public static Parser<T> Unary<T, TInput>(this Parser<T> parser, params (Parser<TInput> op, Func<T, T> factory)[] list)
{
return Recursive<T>((u) =>
{
var choices = list.Select(l => new Then<T, T>(l.op.SkipAnd(u), l.factory));
return new OneOf<T>(choices.ToArray()).Or(parser);
});
}

/// <summary>
/// Builds a parser that creates a unary operation.
/// </summary>
/// <typeparam name="T">The type of the returned parser.</typeparam>
/// <typeparam name="TInput">The type of the symbol parsers.</typeparam>
/// <param name="parser">The higher-priority parser the symbols are separating.</param>
/// <param name="list">The list of operators that can be parsed and their associated result factory methods.</param>
/// <returns></returns>
public static Parser<T> Unary<T, TInput>(this Parser<T> parser, params (Parser<TInput> op, Func<ParseContext, T, T> factory)[] list)
{
return Recursive<T>((u) =>
{
var choices = list.Select(l => new Then<T, T>(l.op.SkipAnd(u), l.factory));
return new OneOf<T>(choices.ToArray()).Or(parser);
});
}
}
}
Loading