feat: Grammar Parser (#3)
Reviewed-on: https://git.bupt-hpc.cn/jackfiled/CanonSharp/pulls/3 Co-authored-by: jackfiled <xcrenchangjun@outlook.com> Co-committed-by: jackfiled <xcrenchangjun@outlook.com>
This commit is contained in:
parent
3ed8bf5d36
commit
cf19f8197e
|
@ -1,49 +0,0 @@
|
|||
namespace CanonSharp.Combinator.Abstractions;
|
||||
|
||||
/// <summary>
|
||||
/// 失败解析结果基类
|
||||
/// </summary>
|
||||
/// <typeparam name="TToken">输入流类型</typeparam>
|
||||
/// <typeparam name="T">解析结果类型</typeparam>
|
||||
public abstract class FailedResult<TToken, T> : ParseResult<TToken, T>
|
||||
{
|
||||
public override T Value => throw Exception;
|
||||
|
||||
/// <summary>
|
||||
/// 当前读取到的状态
|
||||
/// </summary>
|
||||
public abstract IReadState<TToken> State { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 解析失败的消息
|
||||
/// </summary>
|
||||
public abstract string Message { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 解析失败的异常
|
||||
/// </summary>
|
||||
public virtual ParseException Exception => new(ToString());
|
||||
|
||||
/// <summary>
|
||||
/// 转换该失败结果的类型
|
||||
/// </summary>
|
||||
/// <typeparam name="TNext">转换之后的结果类型</typeparam>
|
||||
/// <returns>转换之后的失败解析类型</returns>
|
||||
public abstract FailedResult<TToken, TNext> Convert<TNext>();
|
||||
|
||||
internal override ParseResult<TToken, TResult> Next<TNext, TResult>(Func<T, Parser<TToken, TNext>> nextParser,
|
||||
Func<ParseResult<TToken, TNext>, ParseResult<TToken, TResult>> continuation)
|
||||
=> continuation(Convert<TNext>());
|
||||
|
||||
public override ParseResult<TToken, TResult> Map<TResult>(Func<T, TResult> map)
|
||||
=> Convert<TResult>();
|
||||
|
||||
public override TResult CaseOf<TResult>(Func<SuccessfulResult<TToken, T>, TResult> successfulHandler,
|
||||
Func<FailedResult<TToken, T>, TResult> failedHandler)
|
||||
=> failedHandler(this);
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"Parse Failed: {Message}.";
|
||||
}
|
||||
}
|
45
CanonSharp.Combinator/Abstractions/IFailedResult.cs
Normal file
45
CanonSharp.Combinator/Abstractions/IFailedResult.cs
Normal file
|
@ -0,0 +1,45 @@
|
|||
namespace CanonSharp.Combinator.Abstractions;
|
||||
|
||||
/// <summary>
|
||||
/// 失败解析结果基类
|
||||
/// </summary>
|
||||
/// <typeparam name="TToken">输入流类型</typeparam>
|
||||
/// <typeparam name="T">解析结果类型</typeparam>
|
||||
public interface IFailedResult<TToken, out T> : IParseResult<TToken, T>
|
||||
{
|
||||
T IParseResult<TToken, T>.Value => throw Exception;
|
||||
|
||||
/// <summary>
|
||||
/// 当前读取到的状态
|
||||
/// </summary>
|
||||
public IReadState<TToken> State { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 解析失败的消息
|
||||
/// </summary>
|
||||
public string Message { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 解析失败的异常
|
||||
/// </summary>
|
||||
public ParseException Exception => new(Message);
|
||||
|
||||
/// <summary>
|
||||
/// 转换该失败结果的类型
|
||||
/// </summary>
|
||||
/// <typeparam name="TNext">转换之后的结果类型</typeparam>
|
||||
/// <returns>转换之后的失败解析类型</returns>
|
||||
public IFailedResult<TToken, TNext> Convert<TNext>();
|
||||
|
||||
IParseResult<TToken, TResult> IParseResult<TToken, T>.Next<TNext, TResult>(
|
||||
Func<T, IParser<TToken, TNext>> nextParser,
|
||||
Func<IParseResult<TToken, TNext>, IParseResult<TToken, TResult>> continuation)
|
||||
=> continuation(Convert<TNext>());
|
||||
|
||||
IParseResult<TToken, TResult> IParseResult<TToken, T>.Map<TResult>(Func<T, TResult> map)
|
||||
=> Convert<TResult>();
|
||||
|
||||
TResult IParseResult<TToken, T>.CaseOf<TResult>(Func<ISuccessfulResult<TToken, T>, TResult> successfulHandler,
|
||||
Func<IFailedResult<TToken, T>, TResult> failedHandler)
|
||||
=> failedHandler(this);
|
||||
}
|
|
@ -5,17 +5,12 @@ namespace CanonSharp.Combinator.Abstractions;
|
|||
/// </summary>
|
||||
/// <typeparam name="TToken">输入流类型</typeparam>
|
||||
/// <typeparam name="T">实际结果类型</typeparam>
|
||||
public abstract class ParseResult<TToken, T>
|
||||
public interface IParseResult<TToken, out T>
|
||||
{
|
||||
/// <summary>
|
||||
/// 实际结果对象
|
||||
/// </summary>
|
||||
public abstract T Value { get; }
|
||||
|
||||
protected ParseResult()
|
||||
{
|
||||
|
||||
}
|
||||
public T Value { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 在当前结果上应用下一个解析器
|
||||
|
@ -25,8 +20,8 @@ public abstract class ParseResult<TToken, T>
|
|||
/// <typeparam name="TNext">下一个解析器函数返回的解析结果类型</typeparam>
|
||||
/// <typeparam name="TResult">最终的解析结果类型</typeparam>
|
||||
/// <returns></returns>
|
||||
internal abstract ParseResult<TToken, TResult> Next<TNext, TResult>(Func<T, Parser<TToken, TNext>> nextParser,
|
||||
Func<ParseResult<TToken, TNext>, ParseResult<TToken, TResult>> continuation);
|
||||
internal IParseResult<TToken, TResult> Next<TNext, TResult>(Func<T, IParser<TToken, TNext>> nextParser,
|
||||
Func<IParseResult<TToken, TNext>, IParseResult<TToken, TResult>> continuation);
|
||||
|
||||
/// <summary>
|
||||
/// 映射结果
|
||||
|
@ -34,7 +29,7 @@ public abstract class ParseResult<TToken, T>
|
|||
/// <param name="map">映射结果的函数</param>
|
||||
/// <typeparam name="TResult">映射结果函数返回解析结果的类型</typeparam>
|
||||
/// <returns>最终的解析结果</returns>
|
||||
public abstract ParseResult<TToken, TResult> Map<TResult>(Func<T, TResult> map);
|
||||
public IParseResult<TToken, TResult> Map<TResult>(Func<T, TResult> map);
|
||||
|
||||
/// <summary>
|
||||
/// 在成功或者失败解析结果上应用不同的后继函数
|
||||
|
@ -43,6 +38,6 @@ public abstract class ParseResult<TToken, T>
|
|||
/// <param name="failedHandler">在失败解析结构上应用的函数</param>
|
||||
/// <typeparam name="TResult">最后返回解析结果的类型</typeparam>
|
||||
/// <returns>最后的解析结果</returns>
|
||||
public abstract TResult CaseOf<TResult>(Func<SuccessfulResult<TToken, T>, TResult> successfulHandler,
|
||||
Func<FailedResult<TToken, T>, TResult> failedHandler);
|
||||
public TResult CaseOf<TResult>(Func<ISuccessfulResult<TToken, T>, TResult> successfulHandler,
|
||||
Func<IFailedResult<TToken, T>, TResult> failedHandler);
|
||||
}
|
|
@ -7,7 +7,7 @@ namespace CanonSharp.Combinator.Abstractions;
|
|||
/// </summary>
|
||||
/// <typeparam name="TToken">输入流类型</typeparam>
|
||||
/// <typeparam name="T">解析结果类型</typeparam>
|
||||
public abstract class Parser<TToken, T>
|
||||
public interface IParser<TToken, out T>
|
||||
{
|
||||
/// <summary>
|
||||
/// 解析器运行函数
|
||||
|
@ -17,16 +17,16 @@ public abstract class Parser<TToken, T>
|
|||
/// <typeparam name="TState">输入流状态类型</typeparam>
|
||||
/// <typeparam name="TResult">后继函数运行之后的解析结果类型</typeparam>
|
||||
/// <returns></returns>
|
||||
internal abstract ParseResult<TToken, TResult> Run<TState, TResult>(TState state,
|
||||
Func<ParseResult<TToken, T>, ParseResult<TToken, TResult>> continuation)
|
||||
internal IParseResult<TToken, TResult> Run<TState, TResult>(TState state,
|
||||
Func<IParseResult<TToken, T>, IParseResult<TToken, TResult>> continuation)
|
||||
where TState : IReadState<TToken, TState>;
|
||||
|
||||
public ParseResult<TToken, T> Parse<TState>(TState state) where TState : IReadState<TToken, TState>
|
||||
public IParseResult<TToken, T> Parse<TState>(TState state) where TState : IReadState<TToken, TState>
|
||||
{
|
||||
return Run(state);
|
||||
}
|
||||
|
||||
private ParseResult<TToken, T> Run<TState>(TState state) where TState : IReadState<TToken, TState>
|
||||
private IParseResult<TToken, T> Run<TState>(TState state) where TState : IReadState<TToken, TState>
|
||||
{
|
||||
try
|
||||
{
|
||||
|
@ -38,6 +38,6 @@ public abstract class Parser<TToken, T>
|
|||
}
|
||||
}
|
||||
|
||||
public static Parser<TToken, T> operator |(Parser<TToken, T> a, Parser<TToken, T> b)
|
||||
public static IParser<TToken, T> operator |(IParser<TToken, T> a, IParser<TToken, T> b)
|
||||
=> a.Alternative(b);
|
||||
}
|
29
CanonSharp.Combinator/Abstractions/ISuccessfulResult.cs
Normal file
29
CanonSharp.Combinator/Abstractions/ISuccessfulResult.cs
Normal file
|
@ -0,0 +1,29 @@
|
|||
namespace CanonSharp.Combinator.Abstractions;
|
||||
|
||||
/// <summary>
|
||||
/// 成功解析结果基类
|
||||
/// </summary>
|
||||
/// <typeparam name="TToken">输入流类型</typeparam>
|
||||
/// <typeparam name="T">实际的解析结果类型</typeparam>
|
||||
public interface ISuccessfulResult<TToken,out T> : IParseResult<TToken, T>
|
||||
{
|
||||
/// <summary>
|
||||
/// 运行下一个解析器
|
||||
/// </summary>
|
||||
/// <param name="parser">下一个解析器</param>
|
||||
/// <param name="continuation">处理解析结果的后继函数</param>
|
||||
/// <typeparam name="TNext">下一个解析器返回的结果类型</typeparam>
|
||||
/// <typeparam name="TResult">最终的结果类型</typeparam>
|
||||
/// <returns>最终的结果</returns>
|
||||
protected IParseResult<TToken, TResult> RunNext<TNext, TResult>(IParser<TToken, TNext> parser,
|
||||
Func<IParseResult<TToken, TNext>, IParseResult<TToken, TResult>> continuation);
|
||||
|
||||
IParseResult<TToken, TResult> IParseResult<TToken, T>.Next<TNext, TResult>(
|
||||
Func<T, IParser<TToken, TNext>> nextParser,
|
||||
Func<IParseResult<TToken, TNext>, IParseResult<TToken, TResult>> continuation)
|
||||
=> RunNext(nextParser(Value), continuation);
|
||||
|
||||
TResult IParseResult<TToken, T>.CaseOf<TResult>(Func<ISuccessfulResult<TToken, T>, TResult> successfulHandler,
|
||||
Func<IFailedResult<TToken, T>, TResult> failedHandler)
|
||||
=> successfulHandler(this);
|
||||
}
|
|
@ -1,33 +0,0 @@
|
|||
namespace CanonSharp.Combinator.Abstractions;
|
||||
|
||||
/// <summary>
|
||||
/// 成功解析结果基类
|
||||
/// </summary>
|
||||
/// <param name="value">实际的解析结果</param>
|
||||
/// <typeparam name="TToken">输入流类型</typeparam>
|
||||
/// <typeparam name="T">实际的解析结果类型</typeparam>
|
||||
public abstract class SuccessfulResult<TToken, T>(T value) : ParseResult<TToken, T>
|
||||
{
|
||||
public override T Value => value;
|
||||
|
||||
/// <summary>
|
||||
/// 运行下一个解析器
|
||||
/// </summary>
|
||||
/// <param name="parser">下一个解析器</param>
|
||||
/// <param name="continuation">处理解析结果的后继函数</param>
|
||||
/// <typeparam name="TNext">下一个解析器返回的结果类型</typeparam>
|
||||
/// <typeparam name="TResult">最终的结果类型</typeparam>
|
||||
/// <returns>最终的结果</returns>
|
||||
protected abstract ParseResult<TToken, TResult> RunNext<TNext, TResult>(Parser<TToken, TNext> parser,
|
||||
Func<ParseResult<TToken, TNext>, ParseResult<TToken, TResult>> continuation);
|
||||
|
||||
internal override ParseResult<TToken, TResult> Next<TNext, TResult>(Func<T, Parser<TToken, TNext>> nextParser,
|
||||
Func<ParseResult<TToken, TNext>, ParseResult<TToken, TResult>> continuation)
|
||||
=> RunNext(nextParser(Value), continuation);
|
||||
|
||||
public override TResult CaseOf<TResult>(Func<SuccessfulResult<TToken, T>, TResult> successfulHandler,
|
||||
Func<FailedResult<TToken, T>, TResult> failedHandler)
|
||||
=> successfulHandler(this);
|
||||
|
||||
public override string ToString() => Value?.ToString() ?? string.Empty;
|
||||
}
|
|
@ -19,7 +19,7 @@ public static class ParserExtensions
|
|||
/// <typeparam name="T"></typeparam>
|
||||
/// <returns></returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Parser<TToken, T> Alternative<TToken, T>(this Parser<TToken, T> first, Parser<TToken, T> second)
|
||||
public static IParser<TToken, T> Alternative<TToken, T>(this IParser<TToken, T> first, IParser<TToken, T> second)
|
||||
=> new AlternativeParser<TToken, T>(first, second);
|
||||
|
||||
/// <summary>
|
||||
|
@ -32,8 +32,8 @@ public static class ParserExtensions
|
|||
/// <typeparam name="T"></typeparam>
|
||||
/// <returns></returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Parser<TToken, T> Alternative<TToken, T>(this Parser<TToken, T> parser,
|
||||
Func<FailedResult<TToken, T>, Parser<TToken, T>> resume)
|
||||
public static IParser<TToken, T> Alternative<TToken, T>(this IParser<TToken, T> parser,
|
||||
Func<IFailedResult<TToken, T>, IParser<TToken, T>> resume)
|
||||
=> new ResumeParser<TToken, T>(parser, resume);
|
||||
|
||||
/// <summary>
|
||||
|
@ -46,8 +46,8 @@ public static class ParserExtensions
|
|||
/// <typeparam name="TResult"></typeparam>
|
||||
/// <returns></returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Parser<TToken, TResult> Bind<TToken, T, TResult>(this Parser<TToken, T> parser,
|
||||
Func<T, Parser<TToken, TResult>> next)
|
||||
public static IParser<TToken, TResult> Bind<TToken, T, TResult>(this IParser<TToken, T> parser,
|
||||
Func<T, IParser<TToken, TResult>> next)
|
||||
=> new BindParser<TToken, T, TResult>(parser, next);
|
||||
|
||||
/// <summary>
|
||||
|
@ -60,7 +60,7 @@ public static class ParserExtensions
|
|||
/// <typeparam name="TResult"></typeparam>
|
||||
/// <returns></returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Parser<TToken, TResult> Map<TToken, T, TResult>(this Parser<TToken, T> parser, Func<T, TResult> map)
|
||||
public static IParser<TToken, TResult> Map<TToken, T, TResult>(this IParser<TToken, T> parser, Func<T, TResult> map)
|
||||
=> new MapParser<TToken, T, TResult>(parser, map);
|
||||
|
||||
/// <summary>
|
||||
|
@ -73,7 +73,7 @@ public static class ParserExtensions
|
|||
/// <typeparam name="TResult"></typeparam>
|
||||
/// <returns></returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Parser<TToken, TResult> Map<TToken, T, TResult>(this Parser<TToken, T> parser, TResult result)
|
||||
public static IParser<TToken, TResult> Map<TToken, T, TResult>(this IParser<TToken, T> parser, TResult result)
|
||||
=> parser.Map(_ => result);
|
||||
|
||||
/// <summary>
|
||||
|
@ -87,9 +87,9 @@ public static class ParserExtensions
|
|||
/// <typeparam name="TResult"></typeparam>
|
||||
/// <returns></returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Parser<TToken, TResult> Next<TToken, T, TResult>(this Parser<TToken, T> parser,
|
||||
Func<T, Parser<TToken, TResult>> next,
|
||||
Func<FailedResult<TToken, T>, Parser<TToken, TResult>> failedNext)
|
||||
public static IParser<TToken, TResult> Next<TToken, T, TResult>(this IParser<TToken, T> parser,
|
||||
Func<T, IParser<TToken, TResult>> next,
|
||||
Func<IFailedResult<TToken, T>, IParser<TToken, TResult>> failedNext)
|
||||
=> new NextParser<TToken, T, TResult>(parser, next, failedNext);
|
||||
|
||||
/// <summary>
|
||||
|
@ -103,8 +103,8 @@ public static class ParserExtensions
|
|||
/// <typeparam name="TResult"></typeparam>
|
||||
/// <returns></returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Parser<TToken, TResult> Next<TToken, T, TResult>(this Parser<TToken, T> parser,
|
||||
Func<T, Parser<TToken, TResult>> next, Func<FailedResult<TToken, T>, TResult> failedHandler)
|
||||
public static IParser<TToken, TResult> Next<TToken, T, TResult>(this IParser<TToken, T> parser,
|
||||
Func<T, IParser<TToken, TResult>> next, Func<IFailedResult<TToken, T>, TResult> failedHandler)
|
||||
=> parser.Next(next, failedResult => Pure<TToken, TResult>(failedHandler(failedResult)));
|
||||
|
||||
/// <summary>
|
||||
|
@ -118,8 +118,8 @@ public static class ParserExtensions
|
|||
/// <typeparam name="TResult"></typeparam>
|
||||
/// <returns></returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Parser<TToken, TResult> Next<TToken, T, TResult>(this Parser<TToken, T> parser,
|
||||
Func<T, Parser<TToken, TResult>> next, TResult failedResult)
|
||||
public static IParser<TToken, TResult> Next<TToken, T, TResult>(this IParser<TToken, T> parser,
|
||||
Func<T, IParser<TToken, TResult>> next, TResult failedResult)
|
||||
=> parser.Next(next, _ => Pure<TToken, TResult>(failedResult));
|
||||
|
||||
/// <summary>
|
||||
|
@ -133,8 +133,8 @@ public static class ParserExtensions
|
|||
/// <typeparam name="TResult"></typeparam>
|
||||
/// <returns></returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Parser<TToken, TResult> Next<TToken, T, TResult>(this Parser<TToken, T> parser,
|
||||
Func<T, TResult> nextResult, Func<FailedResult<TToken, T>, Parser<TToken, TResult>> failedResume)
|
||||
public static IParser<TToken, TResult> Next<TToken, T, TResult>(this IParser<TToken, T> parser,
|
||||
Func<T, TResult> nextResult, Func<IFailedResult<TToken, T>, IParser<TToken, TResult>> failedResume)
|
||||
=> parser.Next(x => Pure<TToken, TResult>(nextResult(x)), failedResume);
|
||||
|
||||
/// <summary>
|
||||
|
@ -148,8 +148,8 @@ public static class ParserExtensions
|
|||
/// <typeparam name="TResult"></typeparam>
|
||||
/// <returns></returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Parser<TToken, TResult> Next<TToken, T, TResult>(this Parser<TToken, T> parser,
|
||||
Func<T, TResult> nextResult, Func<FailedResult<TToken, T>, TResult> failedResult)
|
||||
public static IParser<TToken, TResult> Next<TToken, T, TResult>(this IParser<TToken, T> parser,
|
||||
Func<T, TResult> nextResult, Func<IFailedResult<TToken, T>, TResult> failedResult)
|
||||
=> new SuccessfulMapParser<TToken, T, TResult>(parser, nextResult, failedResult);
|
||||
|
||||
/// <summary>
|
||||
|
@ -163,7 +163,7 @@ public static class ParserExtensions
|
|||
/// <typeparam name="TResult"></typeparam>
|
||||
/// <returns></returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Parser<TToken, TResult> Next<TToken, T, TResult>(this Parser<TToken, T> parser,
|
||||
public static IParser<TToken, TResult> Next<TToken, T, TResult>(this IParser<TToken, T> parser,
|
||||
Func<T, TResult> successfulHandler, TResult failedResult)
|
||||
=> parser.Next(successfulHandler, _ => failedResult);
|
||||
|
||||
|
@ -181,8 +181,8 @@ public static class ParserExtensions
|
|||
/// <typeparam name="T"></typeparam>
|
||||
/// <returns></returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Parser<TToken, T> Do<TToken, T>(this Parser<TToken, T> parser, Action<T> successfulAction,
|
||||
Action<FailedResult<TToken, T>> failedAction)
|
||||
public static IParser<TToken, T> Do<TToken, T>(this IParser<TToken, T> parser, Action<T> successfulAction,
|
||||
Action<IFailedResult<TToken, T>> failedAction)
|
||||
=> new DoParser<TToken, T>(parser, successfulAction, failedAction);
|
||||
|
||||
/// <summary>
|
||||
|
@ -194,7 +194,7 @@ public static class ParserExtensions
|
|||
/// <typeparam name="T"></typeparam>
|
||||
/// <returns></returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Parser<TToken, T> Do<TToken, T>(this Parser<TToken, T> parser, Action<T> successfulAction)
|
||||
public static IParser<TToken, T> Do<TToken, T>(this IParser<TToken, T> parser, Action<T> successfulAction)
|
||||
=> parser.Do(successfulAction, _ => { });
|
||||
|
||||
/// <summary>
|
||||
|
@ -206,7 +206,7 @@ public static class ParserExtensions
|
|||
/// <typeparam name="T"></typeparam>
|
||||
/// <returns></returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Parser<TToken, T> LookAhead<TToken, T>(this Parser<TToken, T> parser)
|
||||
public static IParser<TToken, T> LookAhead<TToken, T>(this IParser<TToken, T> parser)
|
||||
=> new LookAheadParser<TToken, T>(parser);
|
||||
|
||||
/// <summary>
|
||||
|
@ -219,7 +219,7 @@ public static class ParserExtensions
|
|||
/// <typeparam name="T"></typeparam>
|
||||
/// <returns></returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Parser<TToken, T> Not<TToken, TIgnore, T>(this Parser<TToken, TIgnore> parser, T result)
|
||||
public static IParser<TToken, T> Not<TToken, TIgnore, T>(this IParser<TToken, TIgnore> parser, T result)
|
||||
=> new ReverseParser<TToken, TIgnore, T>(parser, result);
|
||||
|
||||
/// <summary>
|
||||
|
@ -231,21 +231,21 @@ public static class ParserExtensions
|
|||
/// <typeparam name="TIgnore"></typeparam>
|
||||
/// <returns></returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Parser<TToken, Unit> Not<TToken, TIgnore>(this Parser<TToken, TIgnore> parser)
|
||||
public static IParser<TToken, Unit> Not<TToken, TIgnore>(this IParser<TToken, TIgnore> parser)
|
||||
=> parser.Not(Unit.Instance);
|
||||
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Parser<TToken, T> Try<TToken, T>(this Parser<TToken, T> parser,
|
||||
Func<FailedResult<TToken, T>, T> resume)
|
||||
public static IParser<TToken, T> Try<TToken, T>(this IParser<TToken, T> parser,
|
||||
Func<IFailedResult<TToken, T>, T> resume)
|
||||
=> new TryParser<TToken, T>(parser, resume);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Parser<TToken, T> Try<TToken, T>(this Parser<TToken, T> parser, T result)
|
||||
public static IParser<TToken, T> Try<TToken, T>(this IParser<TToken, T> parser, T result)
|
||||
=> parser.Try(_ => result);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Parser<TToken, bool> Try<TToken, T>(this Parser<TToken, T> parser)
|
||||
public static IParser<TToken, bool> Try<TToken, T>(this IParser<TToken, T> parser)
|
||||
=> parser.Next(_ => true, false).Try(false);
|
||||
|
||||
#endregion
|
||||
|
@ -262,8 +262,8 @@ public static class ParserExtensions
|
|||
/// <typeparam name="TRight"></typeparam>
|
||||
/// <returns></returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Parser<TToken, TLeft> Left<TToken, TLeft, TRight>(this Parser<TToken, TLeft> left,
|
||||
Parser<TToken, TRight> right)
|
||||
public static IParser<TToken, TLeft> Left<TToken, TLeft, TRight>(this IParser<TToken, TLeft> left,
|
||||
IParser<TToken, TRight> right)
|
||||
=> left.Bind(right.Map);
|
||||
|
||||
/// <summary>
|
||||
|
@ -276,12 +276,12 @@ public static class ParserExtensions
|
|||
/// <typeparam name="TRight"></typeparam>
|
||||
/// <returns></returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Parser<TToken, TRight> Right<TToken, TLeft, TRight>(this Parser<TToken, TLeft> left,
|
||||
Parser<TToken, TRight> right)
|
||||
public static IParser<TToken, TRight> Right<TToken, TLeft, TRight>(this IParser<TToken, TLeft> left,
|
||||
IParser<TToken, TRight> right)
|
||||
=> left.Bind(_ => right);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static Parser<TToken, IEnumerable<T>> ManyRecursively<TToken, T>(this Parser<TToken, T> parser,
|
||||
private static IParser<TToken, IEnumerable<T>> ManyRecursively<TToken, T>(this IParser<TToken, T> parser,
|
||||
IEnumerable<T> result)
|
||||
=> parser.Next(x => parser.ManyRecursively(result.Append(x)), result);
|
||||
|
||||
|
@ -293,7 +293,7 @@ public static class ParserExtensions
|
|||
/// <typeparam name="T"></typeparam>
|
||||
/// <returns></returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Parser<TToken, IEnumerable<T>> Many<TToken, T>(this Parser<TToken, T> parser)
|
||||
public static IParser<TToken, IEnumerable<T>> Many<TToken, T>(this IParser<TToken, T> parser)
|
||||
=> parser.ManyRecursively([]);
|
||||
|
||||
/// <summary>
|
||||
|
@ -304,7 +304,7 @@ public static class ParserExtensions
|
|||
/// <typeparam name="T"></typeparam>
|
||||
/// <returns></returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Parser<TToken, IEnumerable<T>> Many1<TToken, T>(this Parser<TToken, T> parser)
|
||||
public static IParser<TToken, IEnumerable<T>> Many1<TToken, T>(this IParser<TToken, T> parser)
|
||||
=> parser.Bind(x => parser.ManyRecursively([x]));
|
||||
|
||||
/// <summary>
|
||||
|
@ -317,7 +317,7 @@ public static class ParserExtensions
|
|||
/// <typeparam name="TIgnore"></typeparam>
|
||||
/// <returns></returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Parser<TToken, Unit> SkipMany<TToken, TIgnore>(this Parser<TToken, TIgnore> parser)
|
||||
public static IParser<TToken, Unit> SkipMany<TToken, TIgnore>(this IParser<TToken, TIgnore> parser)
|
||||
=> Fix<TToken, Unit>(self => parser.Next(_ => self, Unit.Instance));
|
||||
|
||||
/// <summary>
|
||||
|
@ -330,11 +330,11 @@ public static class ParserExtensions
|
|||
/// <typeparam name="TIgnore"></typeparam>
|
||||
/// <returns></returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Parser<TToken, Unit> SkipMany1<TToken, TIgnore>(this Parser<TToken, TIgnore> parser)
|
||||
public static IParser<TToken, Unit> SkipMany1<TToken, TIgnore>(this IParser<TToken, TIgnore> parser)
|
||||
=> parser.Right(parser.SkipMany());
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static Parser<TToken, T> ChainRecursively<TToken, T>(Func<T, Parser<TToken, T>> chain, T value)
|
||||
private static IParser<TToken, T> ChainRecursively<TToken, T>(Func<T, IParser<TToken, T>> chain, T value)
|
||||
=> chain(value).Next(x => ChainRecursively(chain, x), value);
|
||||
|
||||
/// <summary>
|
||||
|
@ -347,12 +347,12 @@ public static class ParserExtensions
|
|||
/// <typeparam name="T"></typeparam>
|
||||
/// <returns></returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Parser<TToken, T> Chain<TToken, T>(this Parser<TToken, T> parser, Func<T, Parser<TToken, T>> chain)
|
||||
public static IParser<TToken, T> Chain<TToken, T>(this IParser<TToken, T> parser, Func<T, IParser<TToken, T>> chain)
|
||||
=> parser.Bind(x => ChainRecursively(chain, x));
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static Parser<TToken, IEnumerable<T>> ManyTillRecursively<TToken, T, TIgnore>(this Parser<TToken, T> parser,
|
||||
Parser<TToken, TIgnore> terminator, IEnumerable<T> result)
|
||||
private static IParser<TToken, IEnumerable<T>> ManyTillRecursively<TToken, T, TIgnore>(this IParser<TToken, T> parser,
|
||||
IParser<TToken, TIgnore> terminator, IEnumerable<T> result)
|
||||
=> terminator.Next(_ => Pure<TToken, IEnumerable<T>>(result),
|
||||
_ => parser.Bind(x => parser.ManyTillRecursively(terminator, result.Append(x))));
|
||||
|
||||
|
@ -367,8 +367,8 @@ public static class ParserExtensions
|
|||
/// <typeparam name="TIgnore"></typeparam>
|
||||
/// <returns></returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Parser<TToken, IEnumerable<T>> ManyTill<TToken, T, TIgnore>(this Parser<TToken, T> parser,
|
||||
Parser<TToken, TIgnore> terminator)
|
||||
public static IParser<TToken, IEnumerable<T>> ManyTill<TToken, T, TIgnore>(this IParser<TToken, T> parser,
|
||||
IParser<TToken, TIgnore> terminator)
|
||||
=> parser.ManyTillRecursively(terminator, []);
|
||||
|
||||
/// <summary>
|
||||
|
@ -382,8 +382,8 @@ public static class ParserExtensions
|
|||
/// <typeparam name="TIgnore"></typeparam>
|
||||
/// <returns></returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Parser<TToken, IEnumerable<T>> Many1Till<TToken, T, TIgnore>(this Parser<TToken, T> parser,
|
||||
Parser<TToken, TIgnore> terminator)
|
||||
public static IParser<TToken, IEnumerable<T>> Many1Till<TToken, T, TIgnore>(this IParser<TToken, T> parser,
|
||||
IParser<TToken, TIgnore> terminator)
|
||||
=> parser.Bind(x => parser.ManyTillRecursively(terminator, [x]));
|
||||
|
||||
/// <summary>
|
||||
|
@ -397,8 +397,8 @@ public static class ParserExtensions
|
|||
/// <typeparam name="T"></typeparam>
|
||||
/// <returns></returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Parser<TToken, T> SkipTill<TToken, TIgnore, T>(this Parser<TToken, TIgnore> parser,
|
||||
Parser<TToken, T> terminator)
|
||||
public static IParser<TToken, T> SkipTill<TToken, TIgnore, T>(this IParser<TToken, TIgnore> parser,
|
||||
IParser<TToken, T> terminator)
|
||||
=> Fix<TToken, T>(self => terminator | parser.Right(self));
|
||||
|
||||
/// <summary>
|
||||
|
@ -412,8 +412,8 @@ public static class ParserExtensions
|
|||
/// <typeparam name="T"></typeparam>
|
||||
/// <returns></returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Parser<TToken, T> Skip1Till<TToken, TIgnore, T>(this Parser<TToken, TIgnore> parser,
|
||||
Parser<TToken, T> terminator)
|
||||
public static IParser<TToken, T> Skip1Till<TToken, TIgnore, T>(this IParser<TToken, TIgnore> parser,
|
||||
IParser<TToken, T> terminator)
|
||||
=> parser.Right(parser.SkipTill(terminator));
|
||||
|
||||
/// <summary>
|
||||
|
@ -424,7 +424,7 @@ public static class ParserExtensions
|
|||
/// <typeparam name="T"></typeparam>
|
||||
/// <returns></returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Parser<TToken, T> Match<TToken, T>(this Parser<TToken, T> parser)
|
||||
public static IParser<TToken, T> Match<TToken, T>(this IParser<TToken, T> parser)
|
||||
=> SkipTill(Any<TToken>(), parser);
|
||||
|
||||
/// <summary>
|
||||
|
@ -440,8 +440,8 @@ public static class ParserExtensions
|
|||
/// <typeparam name="TRight"></typeparam>
|
||||
/// <returns></returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Parser<TToken, IEnumerable<T>> Quote<TToken, T, TLeft, TRight>(this Parser<TToken, T> parser,
|
||||
Parser<TToken, TLeft> left, Parser<TToken, TRight> right)
|
||||
public static IParser<TToken, IEnumerable<T>> Quote<TToken, T, TLeft, TRight>(this IParser<TToken, T> parser,
|
||||
IParser<TToken, TLeft> left, IParser<TToken, TRight> right)
|
||||
=> left.Right(parser.ManyTill(right));
|
||||
|
||||
/// <summary>
|
||||
|
@ -454,8 +454,8 @@ public static class ParserExtensions
|
|||
/// <typeparam name="TQuote"></typeparam>
|
||||
/// <returns></returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Parser<TToken, IEnumerable<T>> Quote<TToken, T, TQuote>(this Parser<TToken, T> parser,
|
||||
Parser<TToken, TQuote> quotedParser)
|
||||
public static IParser<TToken, IEnumerable<T>> Quote<TToken, T, TQuote>(this IParser<TToken, T> parser,
|
||||
IParser<TToken, TQuote> quotedParser)
|
||||
=> parser.Quote(quotedParser, quotedParser);
|
||||
|
||||
/// <summary>
|
||||
|
@ -470,8 +470,8 @@ public static class ParserExtensions
|
|||
/// <typeparam name="TSeparator"></typeparam>
|
||||
/// <returns></returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Parser<TToken, IEnumerable<T>> SeparatedBy1<TToken, T, TSeparator>(this Parser<TToken, T> parser,
|
||||
Parser<TToken, TSeparator> separator)
|
||||
public static IParser<TToken, IEnumerable<T>> SeparatedBy1<TToken, T, TSeparator>(this IParser<TToken, T> parser,
|
||||
IParser<TToken, TSeparator> separator)
|
||||
=> parser.Bind(x => separator.Right(parser).ManyRecursively([x]));
|
||||
|
||||
/// <summary>
|
||||
|
@ -486,8 +486,8 @@ public static class ParserExtensions
|
|||
/// <typeparam name="TSeparator"></typeparam>
|
||||
/// <returns></returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Parser<TToken, IEnumerable<T>> SeparatedBy<TToken, T, TSeparator>(this Parser<TToken, T> parser,
|
||||
Parser<TToken, TSeparator> separator)
|
||||
public static IParser<TToken, IEnumerable<T>> SeparatedBy<TToken, T, TSeparator>(this IParser<TToken, T> parser,
|
||||
IParser<TToken, TSeparator> separator)
|
||||
=> parser.SeparatedBy1(separator).Try([]);
|
||||
|
||||
/// <summary>
|
||||
|
@ -502,8 +502,8 @@ public static class ParserExtensions
|
|||
/// <typeparam name="TSeparator"></typeparam>
|
||||
/// <returns></returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Parser<TToken, IEnumerable<T>> EndBy<TToken, T, TSeparator>(this Parser<TToken, T> parser,
|
||||
Parser<TToken, TSeparator> separator)
|
||||
public static IParser<TToken, IEnumerable<T>> EndBy<TToken, T, TSeparator>(this IParser<TToken, T> parser,
|
||||
IParser<TToken, TSeparator> separator)
|
||||
=> parser.Many().Left(separator);
|
||||
|
||||
/// <summary>
|
||||
|
@ -518,8 +518,8 @@ public static class ParserExtensions
|
|||
/// <typeparam name="TSeparator"></typeparam>
|
||||
/// <returns></returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Parser<TToken, IEnumerable<T>> EndBy1<TToken, T, TSeparator>(this Parser<TToken, T> parser,
|
||||
Parser<TToken, TSeparator> separator)
|
||||
public static IParser<TToken, IEnumerable<T>> EndBy1<TToken, T, TSeparator>(this IParser<TToken, T> parser,
|
||||
IParser<TToken, TSeparator> separator)
|
||||
=> parser.Many1().Left(separator);
|
||||
|
||||
/// <summary>
|
||||
|
@ -534,8 +534,8 @@ public static class ParserExtensions
|
|||
/// <typeparam name="TSeparator"></typeparam>
|
||||
/// <returns></returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Parser<TToken, IEnumerable<T>> SeparatedOrEndBy1<TToken, T, TSeparator>(this Parser<TToken, T> parser,
|
||||
Parser<TToken, TSeparator> separator)
|
||||
public static IParser<TToken, IEnumerable<T>> SeparatedOrEndBy1<TToken, T, TSeparator>(this IParser<TToken, T> parser,
|
||||
IParser<TToken, TSeparator> separator)
|
||||
=> parser.SeparatedBy1(separator).Left(separator.Try());
|
||||
|
||||
/// <summary>
|
||||
|
@ -550,8 +550,8 @@ public static class ParserExtensions
|
|||
/// <typeparam name="TSeparator"></typeparam>
|
||||
/// <returns></returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Parser<TToken, IEnumerable<T>> SeparatedOrEndBy<TToken, T, TSeparator>(this Parser<TToken, T> parser,
|
||||
Parser<TToken, TSeparator> separator)
|
||||
public static IParser<TToken, IEnumerable<T>> SeparatedOrEndBy<TToken, T, TSeparator>(this IParser<TToken, T> parser,
|
||||
IParser<TToken, TSeparator> separator)
|
||||
=> parser.SeparatedOrEndBy1(separator).Try([]);
|
||||
|
||||
#endregion
|
||||
|
@ -559,13 +559,13 @@ public static class ParserExtensions
|
|||
#region LINQ
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Parser<TToken, TResult> Select<TToken, T, TResult>(this Parser<TToken, T> parser,
|
||||
public static IParser<TToken, TResult> Select<TToken, T, TResult>(this IParser<TToken, T> parser,
|
||||
Func<T, TResult> selector)
|
||||
=> parser.Map(selector);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Parser<TToken, TResult> SelectMany<TToken, T, TIntermediate, TResult>(this Parser<TToken, T> parser,
|
||||
Func<T, Parser<TToken, TIntermediate>> selector, Func<T, TIntermediate, TResult> projector)
|
||||
public static IParser<TToken, TResult> SelectMany<TToken, T, TIntermediate, TResult>(this IParser<TToken, T> parser,
|
||||
Func<T, IParser<TToken, TIntermediate>> selector, Func<T, TIntermediate, TResult> projector)
|
||||
=> parser.Bind(x => selector(x).Map(y => projector(x, y)));
|
||||
|
||||
#endregion
|
||||
|
|
|
@ -19,7 +19,7 @@ public static class ParseResultBuilder
|
|||
/// <typeparam name="T">解析成功的对象类型</typeparam>
|
||||
/// <returns></returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static ParseResult<TToken, T> Succeed<TToken, TState, T>(T value, TState state)
|
||||
public static IParseResult<TToken, T> Succeed<TToken, TState, T>(T value, TState state)
|
||||
where TState : IReadState<TToken, TState>
|
||||
=> new InternalSuccessfulResult<TToken, TState, T>(value, state);
|
||||
|
||||
|
@ -32,7 +32,7 @@ public static class ParseResultBuilder
|
|||
/// <typeparam name="T">解析成功的对象类型</typeparam>
|
||||
/// <returns></returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static ParseResult<TToken, T> Fail<TToken, TState, T>(TState state)
|
||||
public static IParseResult<TToken, T> Fail<TToken, TState, T>(TState state)
|
||||
where TState : IReadState<TToken, TState>
|
||||
=> new FailedResultWithError<TToken, TState, T>(state);
|
||||
|
||||
|
@ -47,7 +47,7 @@ public static class ParseResultBuilder
|
|||
/// <typeparam name="T">解析成功的对象类型</typeparam>
|
||||
/// <returns></returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static ParseResult<TToken, T> Fail<TToken, TState, T>(string message, TState state)
|
||||
public static IParseResult<TToken, T> Fail<TToken, TState, T>(string message, TState state)
|
||||
where TState : IReadState<TToken, TState>
|
||||
=> new FailedResultWithMessage<TToken, TState, T>(message, state);
|
||||
|
||||
|
@ -61,7 +61,7 @@ public static class ParseResultBuilder
|
|||
/// <typeparam name="T">解析成功的对象类型</typeparam>
|
||||
/// <returns></returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static ParseResult<TToken, T> Fail<TToken, TState, T>(Exception exception, TState state)
|
||||
public static IParseResult<TToken, T> Fail<TToken, TState, T>(Exception exception, TState state)
|
||||
where TState : IReadState<TToken, TState>
|
||||
=> new FailedResultWithException<TToken, TState, T>(exception, state);
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@ public static class ParserBuilder
|
|||
/// <typeparam name="T"></typeparam>
|
||||
/// <returns></returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Parser<TToken, T> Pure<TToken, T>(T value)
|
||||
public static IParser<TToken, T> Pure<TToken, T>(T value)
|
||||
=> new PureParser<TToken, T>(value);
|
||||
|
||||
/// <summary>
|
||||
|
@ -31,7 +31,7 @@ public static class ParserBuilder
|
|||
/// <typeparam name="T"></typeparam>
|
||||
/// <returns></returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Parser<TToken, T> Pure<TToken, T>(Func<IReadState<TToken>, T> valueFunc)
|
||||
public static IParser<TToken, T> Pure<TToken, T>(Func<IReadState<TToken>, T> valueFunc)
|
||||
=> new DelayedPureParser<TToken, T>(valueFunc);
|
||||
|
||||
/// <summary>
|
||||
|
@ -40,7 +40,7 @@ public static class ParserBuilder
|
|||
/// <typeparam name="TToken"></typeparam>
|
||||
/// <returns></returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Parser<TToken, Unit> Null<TToken>() => Pure<TToken, Unit>(Unit.Instance);
|
||||
public static IParser<TToken, Unit> Null<TToken>() => Pure<TToken, Unit>(Unit.Instance);
|
||||
|
||||
/// <summary>
|
||||
/// 失败的解析器
|
||||
|
@ -49,7 +49,7 @@ public static class ParserBuilder
|
|||
/// <typeparam name="T"></typeparam>
|
||||
/// <returns></returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Parser<TToken, T> Fail<TToken, T>() => new FailedParser<TToken, T>();
|
||||
public static IParser<TToken, T> Fail<TToken, T>() => new FailedParser<TToken, T>();
|
||||
|
||||
/// <summary>
|
||||
/// 失败的解析器
|
||||
|
@ -59,7 +59,7 @@ public static class ParserBuilder
|
|||
/// <typeparam name="T"></typeparam>
|
||||
/// <returns></returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Parser<TToken, T> Fail<TToken, T>(string message) => new FailedParserWithMessage<TToken, T>(message);
|
||||
public static IParser<TToken, T> Fail<TToken, T>(string message) => new FailedParserWithMessage<TToken, T>(message);
|
||||
|
||||
/// <summary>
|
||||
/// 失败的解析器
|
||||
|
@ -69,7 +69,7 @@ public static class ParserBuilder
|
|||
/// <typeparam name="T"></typeparam>
|
||||
/// <returns></returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Parser<TToken, T> Fail<TToken, T>(Func<IReadState<TToken>, string> messageFunc) =>
|
||||
public static IParser<TToken, T> Fail<TToken, T>(Func<IReadState<TToken>, string> messageFunc) =>
|
||||
new FailedParserWithDelayedMessage<TToken, T>(messageFunc);
|
||||
|
||||
/// <summary>
|
||||
|
@ -80,7 +80,7 @@ public static class ParserBuilder
|
|||
/// <typeparam name="T"></typeparam>
|
||||
/// <returns></returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Parser<TToken, T> Fail<TToken, T>(Exception exception) =>
|
||||
public static IParser<TToken, T> Fail<TToken, T>(Exception exception) =>
|
||||
new FailedParserWithException<TToken, T>(exception);
|
||||
|
||||
/// <summary>
|
||||
|
@ -90,7 +90,7 @@ public static class ParserBuilder
|
|||
/// <typeparam name="TToken"></typeparam>
|
||||
/// <returns></returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Parser<TToken, TToken> Satisfy<TToken>(Func<TToken, bool> predicate)
|
||||
public static IParser<TToken, TToken> Satisfy<TToken>(Func<TToken, bool> predicate)
|
||||
=> new SatisfyParser<TToken>(predicate);
|
||||
|
||||
/// <summary>
|
||||
|
@ -99,7 +99,7 @@ public static class ParserBuilder
|
|||
/// <typeparam name="TToken"></typeparam>
|
||||
/// <returns></returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Parser<TToken, TToken> Any<TToken>() => Satisfy<TToken>(_ => true);
|
||||
public static IParser<TToken, TToken> Any<TToken>() => Satisfy<TToken>(_ => true);
|
||||
|
||||
/// <summary>
|
||||
/// 识别指定输入元素的解析器
|
||||
|
@ -108,7 +108,7 @@ public static class ParserBuilder
|
|||
/// <typeparam name="TToken"></typeparam>
|
||||
/// <returns></returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Parser<TToken, TToken> Token<TToken>(TToken token)
|
||||
public static IParser<TToken, TToken> Token<TToken>(TToken token)
|
||||
=> Satisfy<TToken>(t => EqualityComparer<TToken>.Default.Equals(t, token));
|
||||
|
||||
/// <summary>
|
||||
|
@ -118,7 +118,7 @@ public static class ParserBuilder
|
|||
/// <typeparam name="TToken"></typeparam>
|
||||
/// <returns></returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Parser<TToken, Unit> Skip<TToken>(int count) => new SkipParser<TToken>(count);
|
||||
public static IParser<TToken, Unit> Skip<TToken>(int count) => new SkipParser<TToken>(count);
|
||||
|
||||
/// <summary>
|
||||
/// 识别指定数量输入元素的解析器
|
||||
|
@ -127,7 +127,7 @@ public static class ParserBuilder
|
|||
/// <typeparam name="TToken"></typeparam>
|
||||
/// <returns></returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Parser<TToken, IEnumerable<TToken>> Take<TToken>(int count) => new TakeParser<TToken>(count);
|
||||
public static IParser<TToken, IEnumerable<TToken>> Take<TToken>(int count) => new TakeParser<TToken>(count);
|
||||
|
||||
#endregion
|
||||
|
||||
|
@ -141,7 +141,7 @@ public static class ParserBuilder
|
|||
/// <typeparam name="T"></typeparam>
|
||||
/// <returns></returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Parser<TToken, T> Fix<TToken, T>(Func<Parser<TToken, T>, Parser<TToken, T>> parserFix)
|
||||
public static IParser<TToken, T> Fix<TToken, T>(Func<IParser<TToken, T>, IParser<TToken, T>> parserFix)
|
||||
=> new FixParser<TToken, T>(parserFix);
|
||||
|
||||
#endregion
|
||||
|
@ -156,8 +156,8 @@ public static class ParserBuilder
|
|||
/// <typeparam name="T"></typeparam>
|
||||
/// <returns></returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Parser<TToken, T> Choice<TToken, T>(IEnumerable<Parser<TToken, T>> parsers)
|
||||
=> parsers.Reverse().Aggregate((next, parser) => parser.Alternative(next));
|
||||
public static IParser<TToken, T> Choice<TToken, T>(IEnumerable<IParser<TToken, T>> parsers)
|
||||
=> parsers.Aggregate((result, parser) => result.Alternative(parser));
|
||||
|
||||
/// <summary>
|
||||
/// 按照给定的解析器组依次尝试
|
||||
|
@ -167,7 +167,7 @@ public static class ParserBuilder
|
|||
/// <typeparam name="T"></typeparam>
|
||||
/// <returns></returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Parser<TToken, T> Choice<TToken, T>(params Parser<TToken, T>[] parsers)
|
||||
public static IParser<TToken, T> Choice<TToken, T>(params IParser<TToken, T>[] parsers)
|
||||
=> Choice(parsers.AsEnumerable());
|
||||
|
||||
/// <summary>
|
||||
|
@ -178,7 +178,7 @@ public static class ParserBuilder
|
|||
/// <typeparam name="T"></typeparam>
|
||||
/// <returns></returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Parser<TToken, IEnumerable<T>> Sequence<TToken, T>(IEnumerable<Parser<TToken, T>> parsers)
|
||||
public static IParser<TToken, IEnumerable<T>> Sequence<TToken, T>(IEnumerable<IParser<TToken, T>> parsers)
|
||||
=> parsers.Reverse().Aggregate(Pure<TToken, IEnumerable<T>>([]),
|
||||
(next, parser) => parser.Bind(
|
||||
x => next.Map(result => result.Prepend(x))));
|
||||
|
@ -191,7 +191,7 @@ public static class ParserBuilder
|
|||
/// <typeparam name="T"></typeparam>
|
||||
/// <returns></returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Parser<TToken, IEnumerable<T>> Sequence<TToken, T>(params Parser<TToken, T>[] parsers)
|
||||
public static IParser<TToken, IEnumerable<T>> Sequence<TToken, T>(params IParser<TToken, T>[] parsers)
|
||||
=> Sequence(parsers.AsEnumerable());
|
||||
|
||||
/// <summary>
|
||||
|
@ -203,7 +203,7 @@ public static class ParserBuilder
|
|||
/// <typeparam name="TIgnore"></typeparam>
|
||||
/// <returns></returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Parser<TToken, IEnumerable<TToken>> TakeTill<TToken, TIgnore>(Parser<TToken, TIgnore> terminator)
|
||||
public static IParser<TToken, IEnumerable<TToken>> TakeTill<TToken, TIgnore>(IParser<TToken, TIgnore> terminator)
|
||||
=> Any<TToken>().ManyTill(terminator);
|
||||
|
||||
/// <summary>
|
||||
|
@ -215,9 +215,8 @@ public static class ParserBuilder
|
|||
/// <typeparam name="TIgnore"></typeparam>
|
||||
/// <returns></returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Parser<TToken, IEnumerable<TToken>> Take1Till<TToken, TIgnore>(Parser<TToken, TIgnore> termintor)
|
||||
public static IParser<TToken, IEnumerable<TToken>> Take1Till<TToken, TIgnore>(IParser<TToken, TIgnore> termintor)
|
||||
=> Any<TToken>().Many1Till(termintor);
|
||||
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
|
|
@ -10,11 +10,12 @@ namespace CanonSharp.Combinator.Parsers.Bases;
|
|||
/// <param name="second">第二个解析器</param>
|
||||
/// <typeparam name="TToken">输入流类型</typeparam>
|
||||
/// <typeparam name="T">解析器结果类型</typeparam>
|
||||
internal sealed class AlternativeParser<TToken, T>(Parser<TToken, T> first, Parser<TToken, T> second)
|
||||
: Parser<TToken, T>
|
||||
internal sealed class AlternativeParser<TToken, T>(IParser<TToken, T> first, IParser<TToken, T> second)
|
||||
: IParser<TToken, T>
|
||||
{
|
||||
internal override ParseResult<TToken, TResult> Run<TState, TResult>(TState state,
|
||||
Func<ParseResult<TToken, T>, ParseResult<TToken, TResult>> continuation)
|
||||
public IParseResult<TToken, TResult> Run<TState, TResult>(TState state,
|
||||
Func<IParseResult<TToken, T>, IParseResult<TToken, TResult>> continuation)
|
||||
where TState : IReadState<TToken, TState>
|
||||
{
|
||||
return first.Run(state, result => result.CaseOf(continuation, _ => second.Run(state, continuation)));
|
||||
}
|
||||
|
|
|
@ -11,10 +11,11 @@ namespace CanonSharp.Combinator.Parsers.Bases;
|
|||
/// <typeparam name="TIntermediate">上游解析器结果类型</typeparam>
|
||||
/// <typeparam name="T">下游解析器结果类型</typeparam>
|
||||
internal sealed class BindParser<TToken, TIntermediate, T>(
|
||||
Parser<TToken, TIntermediate> parser,
|
||||
Func<TIntermediate, Parser<TToken, T>> next) : Parser<TToken, T>
|
||||
IParser<TToken, TIntermediate> parser,
|
||||
Func<TIntermediate, IParser<TToken, T>> next) : IParser<TToken, T>
|
||||
{
|
||||
internal override ParseResult<TToken, TResult> Run<TState, TResult>(TState state,
|
||||
Func<ParseResult<TToken, T>, ParseResult<TToken, TResult>> continuation)
|
||||
public IParseResult<TToken, TResult> Run<TState, TResult>(TState state,
|
||||
Func<IParseResult<TToken, T>, IParseResult<TToken, TResult>> continuation)
|
||||
where TState : IReadState<TToken, TState>
|
||||
=> parser.Run(state, result => result.Next(next, continuation));
|
||||
}
|
||||
|
|
|
@ -9,25 +9,17 @@ namespace CanonSharp.Combinator.Parsers.Bases;
|
|||
/// </summary>
|
||||
/// <typeparam name="TToken"></typeparam>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
internal sealed class FixParser<TToken, T> : Parser<TToken, T>
|
||||
internal sealed class FixParser<TToken, T> : IParser<TToken, T>
|
||||
{
|
||||
private readonly Parser<TToken, T> _parser;
|
||||
private readonly IParser<TToken, T> _parser;
|
||||
|
||||
public FixParser(Func<Parser<TToken, T>, Parser<TToken, T>> func)
|
||||
public FixParser(Func<IParser<TToken, T>, IParser<TToken, T>> func)
|
||||
{
|
||||
_parser = func(this);
|
||||
}
|
||||
|
||||
internal override ParseResult<TToken, TResult> Run<TState, TResult>(TState state,
|
||||
Func<ParseResult<TToken, T>, ParseResult<TToken, TResult>> continuation)
|
||||
public IParseResult<TToken, TResult> Run<TState, TResult>(TState state,
|
||||
Func<IParseResult<TToken, T>, IParseResult<TToken, TResult>> continuation)
|
||||
where TState : IReadState<TToken, TState>
|
||||
=> _parser.Run(state, continuation);
|
||||
}
|
||||
|
||||
internal sealed class FixParser<TToken, TParameter, T>(
|
||||
Func<Func<TParameter, Parser<TToken, T>>, TParameter, Parser<TToken, T>> func,
|
||||
TParameter parameter) : Parser<TToken, T>
|
||||
{
|
||||
internal override ParseResult<TToken, TResult> Run<TState, TResult>(TState state,
|
||||
Func<ParseResult<TToken, T>, ParseResult<TToken, TResult>> continuation)
|
||||
=> func(p => new FixParser<TToken, TParameter, T>(func, p), parameter).Run(state, continuation);
|
||||
}
|
||||
|
|
|
@ -12,10 +12,11 @@ namespace CanonSharp.Combinator.Parsers.Bases;
|
|||
/// <typeparam name="TIntermediate"></typeparam>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
internal sealed class MapParser<TToken, TIntermediate, T>(
|
||||
Parser<TToken, TIntermediate> parser,
|
||||
Func<TIntermediate, T> func) : Parser<TToken, T>
|
||||
IParser<TToken, TIntermediate> parser,
|
||||
Func<TIntermediate, T> func) : IParser<TToken, T>
|
||||
{
|
||||
internal override ParseResult<TToken, TResult> Run<TState, TResult>(TState state,
|
||||
Func<ParseResult<TToken, T>, ParseResult<TToken, TResult>> continuation)
|
||||
public IParseResult<TToken, TResult> Run<TState, TResult>(TState state,
|
||||
Func<IParseResult<TToken, T>, IParseResult<TToken, TResult>> continuation)
|
||||
where TState : IReadState<TToken, TState>
|
||||
=> parser.Run(state, result => continuation(result.Map(func)));
|
||||
}
|
||||
|
|
|
@ -12,12 +12,13 @@ namespace CanonSharp.Combinator.Parsers.Bases;
|
|||
/// <typeparam name="TIntermediate">上游解析器结果类型</typeparam>
|
||||
/// <typeparam name="T">最终解析结果类型</typeparam>
|
||||
internal sealed class NextParser<TToken, TIntermediate, T>(
|
||||
Parser<TToken, TIntermediate> parser,
|
||||
Func<TIntermediate, Parser<TToken, T>> successfulParser,
|
||||
Func<FailedResult<TToken, TIntermediate>, Parser<TToken, T>> failedParser) : Parser<TToken, T>
|
||||
IParser<TToken, TIntermediate> parser,
|
||||
Func<TIntermediate, IParser<TToken, T>> successfulParser,
|
||||
Func<IFailedResult<TToken, TIntermediate>, IParser<TToken, T>> failedParser) : IParser<TToken, T>
|
||||
{
|
||||
internal override ParseResult<TToken, TResult> Run<TState, TResult>(TState state,
|
||||
Func<ParseResult<TToken, T>, ParseResult<TToken, TResult>> continuation)
|
||||
public IParseResult<TToken, TResult> Run<TState, TResult>(TState state,
|
||||
Func<IParseResult<TToken, T>, IParseResult<TToken, TResult>> continuation)
|
||||
where TState : IReadState<TToken, TState>
|
||||
{
|
||||
return parser.Run(state, result => result.CaseOf(
|
||||
successfulResult => successfulResult.Next(successfulParser, continuation),
|
||||
|
|
|
@ -11,11 +11,12 @@ namespace CanonSharp.Combinator.Parsers.Bases;
|
|||
/// <typeparam name="TToken">输入令牌类型</typeparam>
|
||||
/// <typeparam name="T">解析结果类型</typeparam>
|
||||
internal sealed class ResumeParser<TToken, T>(
|
||||
Parser<TToken, T> parser,
|
||||
Func<FailedResult<TToken, T>, Parser<TToken, T>> failedHandler) : Parser<TToken, T>
|
||||
IParser<TToken, T> parser,
|
||||
Func<IFailedResult<TToken, T>, IParser<TToken, T>> failedHandler) : IParser<TToken, T>
|
||||
{
|
||||
internal override ParseResult<TToken, TResult> Run<TState, TResult>(TState state,
|
||||
Func<ParseResult<TToken, T>, ParseResult<TToken, TResult>> continuation)
|
||||
public IParseResult<TToken, TResult> Run<TState, TResult>(TState state,
|
||||
Func<IParseResult<TToken, T>, IParseResult<TToken, TResult>> continuation)
|
||||
where TState : IReadState<TToken, TState>
|
||||
{
|
||||
return parser.Run(state,
|
||||
result => result.CaseOf(continuation,
|
||||
|
|
|
@ -9,18 +9,20 @@ namespace CanonSharp.Combinator.Parsers;
|
|||
/// <typeparam name="TToken">输入流类型</typeparam>
|
||||
/// <typeparam name="TIntermediate">需要修改结果的解析器</typeparam>
|
||||
/// <typeparam name="T">最终返回的解析结果</typeparam>
|
||||
public abstract class ModifiedParser<TToken, TIntermediate, T>(Parser<TToken, TIntermediate> parser) : Parser<TToken, T>
|
||||
public abstract class ModifiedParser<TToken, TIntermediate, T>(IParser<TToken, TIntermediate> parser)
|
||||
: IParser<TToken, T>
|
||||
{
|
||||
protected abstract ParseResult<TToken, T> Fail<TState>(TState state,
|
||||
FailedResult<TToken, TIntermediate> failedResult)
|
||||
protected abstract IParseResult<TToken, T> Fail<TState>(TState state,
|
||||
IFailedResult<TToken, TIntermediate> failedResult)
|
||||
where TState : IReadState<TToken, TState>;
|
||||
|
||||
protected abstract ParseResult<TToken, T> Succeed<TState>(TState state,
|
||||
SuccessfulResult<TToken, TIntermediate> successfulResult)
|
||||
protected abstract IParseResult<TToken, T> Succeed<TState>(TState state,
|
||||
ISuccessfulResult<TToken, TIntermediate> successfulResult)
|
||||
where TState : IReadState<TToken, TState>;
|
||||
|
||||
internal override ParseResult<TToken, TResult> Run<TState, TResult>(TState state,
|
||||
Func<ParseResult<TToken, T>, ParseResult<TToken, TResult>> continuation)
|
||||
public IParseResult<TToken, TResult> Run<TState, TResult>(TState state,
|
||||
Func<IParseResult<TToken, T>, IParseResult<TToken, TResult>> continuation)
|
||||
where TState : IReadState<TToken, TState>
|
||||
=> parser.Run(state, result => result.CaseOf(
|
||||
success => continuation(Succeed(state, success)),
|
||||
failure => continuation(Fail(state, failure))));
|
||||
|
|
|
@ -11,18 +11,18 @@ namespace CanonSharp.Combinator.Parsers.Modifiers;
|
|||
/// <typeparam name="TToken">输入流类型</typeparam>
|
||||
/// <typeparam name="T">解析结果类型</typeparam>
|
||||
internal sealed class DoParser<TToken, T>(
|
||||
Parser<TToken, T> parser,
|
||||
IParser<TToken, T> parser,
|
||||
Action<T> succeed,
|
||||
Action<FailedResult<TToken, T>> fail) : ModifiedParser<TToken, T, T>(parser)
|
||||
Action<IFailedResult<TToken, T>> fail) : ModifiedParser<TToken, T, T>(parser)
|
||||
{
|
||||
protected override ParseResult<TToken, T> Succeed<TState>(TState state,
|
||||
SuccessfulResult<TToken, T> successfulResult)
|
||||
protected override IParseResult<TToken, T> Succeed<TState>(TState state,
|
||||
ISuccessfulResult<TToken, T> successfulResult)
|
||||
{
|
||||
succeed(successfulResult.Value);
|
||||
return successfulResult;
|
||||
}
|
||||
|
||||
protected override ParseResult<TToken, T> Fail<TState>(TState state, FailedResult<TToken, T> failedResult)
|
||||
protected override IParseResult<TToken, T> Fail<TState>(TState state, IFailedResult<TToken, T> failedResult)
|
||||
{
|
||||
fail(failedResult);
|
||||
return failedResult;
|
||||
|
|
|
@ -10,12 +10,12 @@ namespace CanonSharp.Combinator.Parsers.Modifiers;
|
|||
/// <param name="parser">需要向前看的解析器</param>
|
||||
/// <typeparam name="TToken">输入流令牌</typeparam>
|
||||
/// <typeparam name="T">返回的解析结果类型</typeparam>
|
||||
internal sealed class LookAheadParser<TToken, T>(Parser<TToken, T> parser) : ModifiedParser<TToken, T, T>(parser)
|
||||
internal sealed class LookAheadParser<TToken, T>(IParser<TToken, T> parser) : ModifiedParser<TToken, T, T>(parser)
|
||||
{
|
||||
protected override ParseResult<TToken, T> Succeed<TState>(TState state,
|
||||
SuccessfulResult<TToken, T> successfulResult)
|
||||
protected override IParseResult<TToken, T> Succeed<TState>(TState state,
|
||||
ISuccessfulResult<TToken, T> successfulResult)
|
||||
=> ParseResultBuilder.Succeed<TToken, TState, T>(successfulResult.Value, state);
|
||||
|
||||
protected override ParseResult<TToken, T> Fail<TState>(TState state, FailedResult<TToken, T> failedResult)
|
||||
protected override IParseResult<TToken, T> Fail<TState>(TState state, IFailedResult<TToken, T> failedResult)
|
||||
=> ParseResultBuilder.Fail<TToken, TState, T>($"Failed when looking ahead: {failedResult}", state);
|
||||
}
|
||||
|
|
|
@ -12,15 +12,15 @@ namespace CanonSharp.Combinator.Parsers.Modifiers;
|
|||
/// <typeparam name="TToken">输入流的类型</typeparam>
|
||||
/// <typeparam name="TIntermediate">上游解析器结果类型</typeparam>
|
||||
/// <typeparam name="T">最终的返回结果</typeparam>
|
||||
internal sealed class ReverseParser<TToken, TIntermediate, T>(Parser<TToken, TIntermediate> parser, T result)
|
||||
internal sealed class ReverseParser<TToken, TIntermediate, T>(IParser<TToken, TIntermediate> parser, T result)
|
||||
: ModifiedParser<TToken, TIntermediate, T>(parser)
|
||||
{
|
||||
protected override ParseResult<TToken, T> Succeed<TState>(TState state,
|
||||
SuccessfulResult<TToken, TIntermediate> successfulResult)
|
||||
protected override IParseResult<TToken, T> Succeed<TState>(TState state,
|
||||
ISuccessfulResult<TToken, TIntermediate> successfulResult)
|
||||
=> ParseResultBuilder.Fail<TToken, TState, T>($"Unexpected successful result: {successfulResult.Value}",
|
||||
state);
|
||||
|
||||
protected override ParseResult<TToken, T> Fail<TState>(TState state,
|
||||
FailedResult<TToken, TIntermediate> failedResult)
|
||||
protected override IParseResult<TToken, T> Fail<TState>(TState state,
|
||||
IFailedResult<TToken, TIntermediate> failedResult)
|
||||
=> ParseResultBuilder.Succeed<TToken, TState, T>(result, state);
|
||||
}
|
||||
|
|
|
@ -12,15 +12,15 @@ namespace CanonSharp.Combinator.Parsers.Modifiers;
|
|||
/// <typeparam name="TIntermediate">上游解析器解析结果类型</typeparam>
|
||||
/// <typeparam name="T">最终的解析结果类型</typeparam>
|
||||
internal sealed class SuccessfulMapParser<TToken, TIntermediate, T>(
|
||||
Parser<TToken, TIntermediate> parser,
|
||||
IParser<TToken, TIntermediate> parser,
|
||||
Func<TIntermediate, T> successfulHandler,
|
||||
Func<FailedResult<TToken, TIntermediate>, T> failedHandler) : ModifiedParser<TToken, TIntermediate, T>(parser)
|
||||
Func<IFailedResult<TToken, TIntermediate>, T> failedHandler) : ModifiedParser<TToken, TIntermediate, T>(parser)
|
||||
{
|
||||
protected override ParseResult<TToken, T> Succeed<TState>(TState state,
|
||||
SuccessfulResult<TToken, TIntermediate> successfulResult)
|
||||
protected override IParseResult<TToken, T> Succeed<TState>(TState state,
|
||||
ISuccessfulResult<TToken, TIntermediate> successfulResult)
|
||||
=> successfulResult.Map(successfulHandler);
|
||||
|
||||
protected override ParseResult<TToken, T> Fail<TState>(TState state,
|
||||
FailedResult<TToken, TIntermediate> failedResult)
|
||||
protected override IParseResult<TToken, T> Fail<TState>(TState state,
|
||||
IFailedResult<TToken, TIntermediate> failedResult)
|
||||
=> ParseResultBuilder.Succeed<TToken, TState, T>(failedHandler(failedResult), state);
|
||||
}
|
||||
|
|
|
@ -11,13 +11,13 @@ namespace CanonSharp.Combinator.Parsers.Modifiers;
|
|||
/// <param name="resume">处理失败结果的恢复函数</param>
|
||||
/// <typeparam name="TToken">输入流令牌</typeparam>
|
||||
/// <typeparam name="T">解析器返回结果类型</typeparam>
|
||||
internal sealed class TryParser<TToken, T>(Parser<TToken, T> parser, Func<FailedResult<TToken, T>, T> resume)
|
||||
internal sealed class TryParser<TToken, T>(IParser<TToken, T> parser, Func<IFailedResult<TToken, T>, T> resume)
|
||||
: ModifiedParser<TToken, T, T>(parser)
|
||||
{
|
||||
protected override ParseResult<TToken, T> Succeed<TState>(TState state,
|
||||
SuccessfulResult<TToken, T> successfulResult)
|
||||
protected override IParseResult<TToken, T> Succeed<TState>(TState state,
|
||||
ISuccessfulResult<TToken, T> successfulResult)
|
||||
=> successfulResult;
|
||||
|
||||
protected override ParseResult<TToken, T> Fail<TState>(TState state, FailedResult<TToken, T> failedResult)
|
||||
protected override IParseResult<TToken, T> Fail<TState>(TState state, IFailedResult<TToken, T> failedResult)
|
||||
=> ParseResultBuilder.Succeed<TToken, TState, T>(resume(failedResult), state);
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ namespace CanonSharp.Combinator.Parsers;
|
|||
/// </summary>
|
||||
/// <typeparam name="TToken">输入流类型</typeparam>
|
||||
/// <typeparam name="T">解析结果的类型</typeparam>
|
||||
public abstract class PrimitiveParser<TToken, T> : Parser<TToken, T>
|
||||
public abstract class PrimitiveParser<TToken, T> : IParser<TToken, T>
|
||||
{
|
||||
/// <summary>
|
||||
/// 运行解析器 返回解析结果
|
||||
|
@ -16,10 +16,11 @@ public abstract class PrimitiveParser<TToken, T> : Parser<TToken, T>
|
|||
/// <param name="state">当前输入流的状态</param>
|
||||
/// <typeparam name="TState">输入流状态的类型</typeparam>
|
||||
/// <returns>解析结果</returns>
|
||||
protected abstract ParseResult<TToken, T> Run<TState>(TState state)
|
||||
protected abstract IParseResult<TToken, T> Run<TState>(TState state)
|
||||
where TState : IReadState<TToken, TState>;
|
||||
|
||||
internal sealed override ParseResult<TToken, TResult> Run<TState, TResult>(TState state,
|
||||
Func<ParseResult<TToken, T>, ParseResult<TToken, TResult>> continuation)
|
||||
public IParseResult<TToken, TResult> Run<TState, TResult>(TState state,
|
||||
Func<IParseResult<TToken, T>, IParseResult<TToken, TResult>> continuation)
|
||||
where TState : IReadState<TToken, TState>
|
||||
=> continuation(Run(state));
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ namespace CanonSharp.Combinator.Parsers.Primitives;
|
|||
/// <typeparam name="T">解析结果的类型</typeparam>
|
||||
internal sealed class FailedParser<TToken, T> : PrimitiveParser<TToken, T>
|
||||
{
|
||||
protected override ParseResult<TToken, T> Run<TState>(TState state)
|
||||
protected override IParseResult<TToken, T> Run<TState>(TState state)
|
||||
=> ParseResultBuilder.Fail<TToken, TState, T>(state);
|
||||
}
|
||||
|
||||
|
@ -21,7 +21,7 @@ internal sealed class FailedParser<TToken, T> : PrimitiveParser<TToken, T>
|
|||
/// <typeparam name="T">解析结果的类型</typeparam>
|
||||
internal sealed class FailedParserWithMessage<TToken, T>(string message) : PrimitiveParser<TToken, T>
|
||||
{
|
||||
protected override ParseResult<TToken, T> Run<TState>(TState state)
|
||||
protected override IParseResult<TToken, T> Run<TState>(TState state)
|
||||
=> ParseResultBuilder.Fail<TToken, TState, T>(message, state);
|
||||
}
|
||||
|
||||
|
@ -34,7 +34,7 @@ internal sealed class FailedParserWithMessage<TToken, T>(string message) : Primi
|
|||
internal sealed class FailedParserWithDelayedMessage<TToken, T>(Func<IReadState<TToken>, string> messageFunc)
|
||||
: PrimitiveParser<TToken, T>
|
||||
{
|
||||
protected override ParseResult<TToken, T> Run<TState>(TState state)
|
||||
protected override IParseResult<TToken, T> Run<TState>(TState state)
|
||||
=> ParseResultBuilder.Fail<TToken, TState, T>(messageFunc(state), state);
|
||||
}
|
||||
|
||||
|
@ -46,6 +46,6 @@ internal sealed class FailedParserWithDelayedMessage<TToken, T>(Func<IReadState<
|
|||
/// <typeparam name="T">解析结果的类型</typeparam>
|
||||
internal sealed class FailedParserWithException<TToken, T>(Exception e) : PrimitiveParser<TToken, T>
|
||||
{
|
||||
protected override ParseResult<TToken, T> Run<TState>(TState state)
|
||||
protected override IParseResult<TToken, T> Run<TState>(TState state)
|
||||
=> ParseResultBuilder.Fail<TToken, TState, T>(e, state);
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ namespace CanonSharp.Combinator.Parsers.Primitives;
|
|||
/// <typeparam name="T">解析成功返回值的类型</typeparam>
|
||||
internal sealed class PureParser<TToken, T>(T value) : PrimitiveParser<TToken, T>
|
||||
{
|
||||
protected override ParseResult<TToken, T> Run<TState>(TState state)
|
||||
protected override IParseResult<TToken, T> Run<TState>(TState state)
|
||||
=> ParseResultBuilder.Succeed<TToken, TState, T>(value, state);
|
||||
}
|
||||
|
||||
|
@ -22,6 +22,6 @@ internal sealed class PureParser<TToken, T>(T value) : PrimitiveParser<TToken, T
|
|||
/// <typeparam name="T">解析成功返回值的类型</typeparam>
|
||||
internal sealed class DelayedPureParser<TToken, T>(Func<IReadState<TToken>, T> valueFunc) : PrimitiveParser<TToken, T>
|
||||
{
|
||||
protected override ParseResult<TToken, T> Run<TState>(TState state)
|
||||
protected override IParseResult<TToken, T> Run<TState>(TState state)
|
||||
=> ParseResultBuilder.Succeed<TToken, TState, T>(valueFunc(state), state);
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ namespace CanonSharp.Combinator.Parsers.Primitives;
|
|||
/// <typeparam name="TToken">输入流类型</typeparam>
|
||||
internal sealed class SatisfyParser<TToken>(Func<TToken, bool> predicate) : PrimitiveParser<TToken, TToken>
|
||||
{
|
||||
protected override ParseResult<TToken, TToken> Run<TState>(TState state)
|
||||
protected override IParseResult<TToken, TToken> Run<TState>(TState state)
|
||||
{
|
||||
return state.HasValue && predicate(state.Current)
|
||||
? ParseResultBuilder.Succeed<TToken, TState, TToken>(state.Current, state.Next)
|
||||
|
|
|
@ -10,7 +10,7 @@ namespace CanonSharp.Combinator.Parsers.Primitives;
|
|||
/// <typeparam name="TToken">输入流类型</typeparam>
|
||||
internal sealed class SkipParser<TToken>(int count) : PrimitiveParser<TToken, Unit>
|
||||
{
|
||||
protected override ParseResult<TToken, Unit> Run<TState>(TState state)
|
||||
protected override IParseResult<TToken, Unit> Run<TState>(TState state)
|
||||
{
|
||||
List<TState> result = state.AsEnumerable<TToken, TState>().Take(count).ToList();
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ namespace CanonSharp.Combinator.Parsers.Primitives;
|
|||
/// <typeparam name="TToken">输入流类型</typeparam>
|
||||
internal sealed class TakeParser<TToken>(int count) : PrimitiveParser<TToken, IEnumerable<TToken>>
|
||||
{
|
||||
protected override ParseResult<TToken, IEnumerable<TToken>> Run<TState>(TState state)
|
||||
protected override IParseResult<TToken, IEnumerable<TToken>> Run<TState>(TState state)
|
||||
{
|
||||
List<TState> result = state.AsEnumerable<TToken, TState>().Take(count).ToList();
|
||||
|
||||
|
|
|
@ -8,15 +8,17 @@ namespace CanonSharp.Combinator.Results;
|
|||
/// <typeparam name="TToken">输入流的类型</typeparam>
|
||||
/// <typeparam name="TState">输入流的读取类型</typeparam>
|
||||
/// <typeparam name="T">实际的结果类型</typeparam>
|
||||
internal sealed class FailedResultWithError<TToken, TState, T>(TState state) : FailedResult<TToken, T>
|
||||
internal sealed class FailedResultWithError<TToken, TState, T>(TState state) : IFailedResult<TToken, T>
|
||||
where TState : IReadState<TToken, TState>
|
||||
{
|
||||
public override IReadState<TToken> State => state;
|
||||
public IReadState<TToken> State => state;
|
||||
|
||||
public override string Message => $"Unexpected state: {state}.";
|
||||
public string Message => $"Unexpected state: {state}.";
|
||||
|
||||
public override FailedResult<TToken, TNext> Convert<TNext>()
|
||||
public IFailedResult<TToken, TNext> Convert<TNext>()
|
||||
{
|
||||
return new FailedResultWithError<TToken, TState, TNext>(state);
|
||||
}
|
||||
|
||||
public override string ToString() => $"Parse failed: {Message}.";
|
||||
}
|
||||
|
|
|
@ -10,15 +10,18 @@ namespace CanonSharp.Combinator.Results;
|
|||
/// <typeparam name="TToken">输入流类型</typeparam>
|
||||
/// <typeparam name="TState">当前输入流状态的类型</typeparam>
|
||||
/// <typeparam name="T">解析结果的类型</typeparam>
|
||||
public class FailedResultWithException<TToken, TState, T>(Exception exception, TState state) : FailedResult<TToken, T>
|
||||
internal sealed class FailedResultWithException<TToken, TState, T>(Exception exception, TState state)
|
||||
: IFailedResult<TToken, T>
|
||||
where TState : IReadState<TToken, TState>
|
||||
{
|
||||
public override IReadState<TToken> State => state;
|
||||
public IReadState<TToken> State => state;
|
||||
|
||||
public override ParseException Exception => new(ToString(), exception);
|
||||
public ParseException Exception => new(Message, exception);
|
||||
|
||||
public override string Message => $"Exception occured: {exception}.";
|
||||
public string Message => $"Exception occured: {exception}.";
|
||||
|
||||
public override FailedResult<TToken, TNext> Convert<TNext>()
|
||||
public IFailedResult<TToken, TNext> Convert<TNext>()
|
||||
=> new FailedResultWithException<TToken, TState, TNext>(exception, state);
|
||||
|
||||
public override string ToString() => $"Parse failed: {Message}.";
|
||||
}
|
||||
|
|
|
@ -10,15 +10,18 @@ namespace CanonSharp.Combinator.Results;
|
|||
/// <typeparam name="TToken">输入流的类型</typeparam>
|
||||
/// <typeparam name="TState">读取状态类型</typeparam>
|
||||
/// <typeparam name="T">解析结果的类型</typeparam>
|
||||
internal sealed class FailedResultWithMessage<TToken, TState, T>(string message, TState state) : FailedResult<TToken, T>
|
||||
internal sealed class FailedResultWithMessage<TToken, TState, T>(string message, TState state)
|
||||
: IFailedResult<TToken, T>
|
||||
where TState : IReadState<TToken, TState>
|
||||
{
|
||||
public override IReadState<TToken> State => state;
|
||||
public IReadState<TToken> State => state;
|
||||
|
||||
public override string Message => message;
|
||||
public string Message => message;
|
||||
|
||||
public override FailedResult<TToken, TNext> Convert<TNext>()
|
||||
public IFailedResult<TToken, TNext> Convert<TNext>()
|
||||
{
|
||||
return new FailedResultWithMessage<TToken, TState, TNext>(message, state);
|
||||
}
|
||||
|
||||
public override string ToString() => $"Parse failed: {Message}.";
|
||||
}
|
||||
|
|
|
@ -11,13 +11,17 @@ namespace CanonSharp.Combinator.Results;
|
|||
/// <typeparam name="TState">输入流的状态类型</typeparam>
|
||||
/// <typeparam name="T">解析结果的类型</typeparam>
|
||||
internal sealed class InternalSuccessfulResult<TToken, TState, T>(T result, TState state)
|
||||
: SuccessfulResult<TToken, T>(result)
|
||||
: ISuccessfulResult<TToken, T>
|
||||
where TState : IReadState<TToken, TState>
|
||||
{
|
||||
protected override ParseResult<TToken, TResult> RunNext<TNext, TResult>(Parser<TToken, TNext> parser,
|
||||
Func<ParseResult<TToken, TNext>, ParseResult<TToken, TResult>> continuation)
|
||||
public T Value => result;
|
||||
|
||||
public IParseResult<TToken, TResult> RunNext<TNext, TResult>(IParser<TToken, TNext> parser,
|
||||
Func<IParseResult<TToken, TNext>, IParseResult<TToken, TResult>> continuation)
|
||||
=> parser.Run(state, continuation);
|
||||
|
||||
public override ParseResult<TToken, TResult> Map<TResult>(Func<T, TResult> map)
|
||||
public IParseResult<TToken, TResult> Map<TResult>(Func<T, TResult> map)
|
||||
=> new InternalSuccessfulResult<TToken, TState, TResult>(map(Value), state);
|
||||
|
||||
public override string ToString() => Value?.ToString() ?? string.Empty;
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ namespace CanonSharp.Combinator.Text;
|
|||
/// <param name="comparison">字符串比较模式</param>
|
||||
public class StringParser(string except, StringComparison comparison) : PrimitiveParser<char, string>
|
||||
{
|
||||
protected override ParseResult<char, string> Run<TState>(TState state)
|
||||
protected override IParseResult<char, string> Run<TState>(TState state)
|
||||
{
|
||||
TState[] states = state.AsEnumerable<char, TState>().Take(except.Length).ToArray();
|
||||
string actual = new(states.Select(x => x.Current).ToArray());
|
||||
|
|
|
@ -13,7 +13,7 @@ public static class TextParserBuilder
|
|||
/// <param name="token">识别的单个字符</param>
|
||||
/// <returns></returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Parser<char, char> Char(char token) => Satisfy<char>(x => x == token);
|
||||
public static IParser<char, char> Char(char token) => Satisfy<char>(x => x == token);
|
||||
|
||||
/// <summary>
|
||||
/// 忽略大小写识别单个字符
|
||||
|
@ -21,7 +21,7 @@ public static class TextParserBuilder
|
|||
/// <param name="token"></param>
|
||||
/// <returns></returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Parser<char, char> CharIgnoreCase(char token) =>
|
||||
public static IParser<char, char> CharIgnoreCase(char token) =>
|
||||
Satisfy<char>(x => char.ToUpperInvariant(x) == char.ToUpperInvariant(token));
|
||||
|
||||
/// <summary>
|
||||
|
@ -30,7 +30,7 @@ public static class TextParserBuilder
|
|||
/// <param name="candidate"></param>
|
||||
/// <returns></returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Parser<char, char> OneOf(string candidate) => Satisfy<char>(candidate.Contains);
|
||||
public static IParser<char, char> OneOf(string candidate) => Satisfy<char>(candidate.Contains);
|
||||
|
||||
/// <summary>
|
||||
/// 忽略大小写识别字符串中的一个字符
|
||||
|
@ -38,7 +38,7 @@ public static class TextParserBuilder
|
|||
/// <param name="candidate"></param>
|
||||
/// <returns></returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Parser<char, char> OneOfIgnoreCase(string candidate) =>
|
||||
public static IParser<char, char> OneOfIgnoreCase(string candidate) =>
|
||||
Satisfy<char>(x => candidate.Contains(x, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
/// <summary>
|
||||
|
@ -48,7 +48,7 @@ public static class TextParserBuilder
|
|||
/// <param name="comparison">字符串比较方法</param>
|
||||
/// <returns></returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Parser<char, string> String(string except, StringComparison comparison) =>
|
||||
public static IParser<char, string> String(string except, StringComparison comparison) =>
|
||||
new StringParser(except, comparison);
|
||||
|
||||
/// <summary>
|
||||
|
@ -57,7 +57,7 @@ public static class TextParserBuilder
|
|||
/// <param name="except"></param>
|
||||
/// <returns></returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Parser<char, string> String(string except) => String(except, StringComparison.Ordinal);
|
||||
public static IParser<char, string> String(string except) => String(except, StringComparison.Ordinal);
|
||||
|
||||
/// <summary>
|
||||
/// 忽略大小写识别一个字符串
|
||||
|
@ -65,7 +65,7 @@ public static class TextParserBuilder
|
|||
/// <param name="except"></param>
|
||||
/// <returns></returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Parser<char, string> StringIgnoreCase(string except) =>
|
||||
public static IParser<char, string> StringIgnoreCase(string except) =>
|
||||
String(except, StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
/// <summary>
|
||||
|
@ -75,28 +75,28 @@ public static class TextParserBuilder
|
|||
/// <param name="end">包括的终止字符</param>
|
||||
/// <returns></returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Parser<char, char> Range(char start, char end) => Satisfy<char>(x => x >= start && x <= end);
|
||||
public static IParser<char, char> Range(char start, char end) => Satisfy<char>(x => x >= start && x <= end);
|
||||
|
||||
/// <summary>
|
||||
/// 识别Unicode字符类别的解析器
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Parser<char, char> Letter() => Satisfy<char>(char.IsLetter);
|
||||
public static IParser<char, char> Letter() => Satisfy<char>(char.IsLetter);
|
||||
|
||||
/// <summary>
|
||||
/// 识别Unicode数字类别的解析器
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Parser<char, char> Digit() => Satisfy<char>(char.IsDigit);
|
||||
public static IParser<char, char> Digit() => Satisfy<char>(char.IsDigit);
|
||||
|
||||
/// <summary>
|
||||
/// 识别ASCII字符类别的解析器
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Parser<char, char> AsciiLetter() =>
|
||||
public static IParser<char, char> AsciiLetter() =>
|
||||
Satisfy<char>(x => x is >= 'a' and <= 'z' or >= 'A' and <= 'Z');
|
||||
|
||||
/// <summary>
|
||||
|
@ -104,20 +104,20 @@ public static class TextParserBuilder
|
|||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Parser<char, char> AsciiDigit() => Satisfy<char>(x => x is >= '0' and <= '9');
|
||||
public static IParser<char, char> AsciiDigit() => Satisfy<char>(x => x is >= '0' and <= '9');
|
||||
|
||||
/// <summary>
|
||||
/// 识别Unicode空白类型的字符
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Parser<char, char> Space() => Satisfy<char>(char.IsWhiteSpace);
|
||||
public static IParser<char, char> Space() => Satisfy<char>(char.IsWhiteSpace);
|
||||
|
||||
/// <summary>
|
||||
/// 识别所有的换行符
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Parser<char, string> LineBreak() =>
|
||||
public static IParser<char, string> LineBreak() =>
|
||||
OneOf("\u000D\u000A\u0085\u2028\u2029\n").Map(x => x.ToString()) | String("\r\n");
|
||||
}
|
||||
|
|
|
@ -6,9 +6,9 @@ namespace CanonSharp.Combinator.Text;
|
|||
|
||||
public static class TextParserExtensions
|
||||
{
|
||||
public static Parser<char, T> SkipSpaces<T>(this Parser<char, T> parser)
|
||||
public static IParser<char, T> SkipSpaces<T>(this IParser<char, T> parser)
|
||||
=> Space().SkipTill(parser);
|
||||
|
||||
public static Parser<char, T> SkipSpaceAndLineBreak<T>(this Parser<char, T> parser)
|
||||
public static IParser<char, T> SkipSpaceAndLineBreak<T>(this IParser<char, T> parser)
|
||||
=> (Space().Map(x => x.ToString()) | LineBreak()).SkipTill(parser);
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<RootNamespace>CanonSharp.Common</RootNamespace>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
392
CanonSharp.Pascal/Parser/GrammarParser.cs
Normal file
392
CanonSharp.Pascal/Parser/GrammarParser.cs
Normal file
|
@ -0,0 +1,392 @@
|
|||
using CanonSharp.Combinator.Abstractions;
|
||||
using CanonSharp.Combinator.Extensions;
|
||||
using static CanonSharp.Combinator.ParserBuilder;
|
||||
using CanonSharp.Pascal.Scanner;
|
||||
using CanonSharp.Pascal.SyntaxTree;
|
||||
|
||||
namespace CanonSharp.Pascal.Parser;
|
||||
|
||||
public sealed class GrammarParser : GrammarParserBuilder
|
||||
{
|
||||
public static IParser<LexicalToken, SyntaxNodeBase> FactorParser()
|
||||
{
|
||||
// factor -> - factor | + factor
|
||||
IParser<LexicalToken, SyntaxNodeBase> minusParser =
|
||||
from _ in Operator("-")
|
||||
from node in FactorParser()
|
||||
select new UnaryOperatorNode(UnaryOperatorType.Minus, node);
|
||||
|
||||
IParser<LexicalToken, SyntaxNodeBase> plusParser =
|
||||
from _ in Operator("+")
|
||||
from node in FactorParser()
|
||||
select new UnaryOperatorNode(UnaryOperatorType.Plus, node);
|
||||
|
||||
IParser<LexicalToken, SyntaxNodeBase> notParser =
|
||||
from _ in Keyword("not")
|
||||
from node in FactorParser()
|
||||
select new UnaryOperatorNode(UnaryOperatorType.Not, node);
|
||||
|
||||
IParser<LexicalToken, SyntaxNodeBase> parenthesisParser =
|
||||
from _1 in Delimiter("(")
|
||||
from node in ExpressionParser()
|
||||
from _2 in Delimiter(")")
|
||||
select node;
|
||||
|
||||
IParser<LexicalToken, SyntaxNodeBase> procedureCallParser =
|
||||
from identifier in IdentifierParser()
|
||||
from _ in Delimiter("(")
|
||||
from expressions in ExpressionParser().SeparatedBy(Delimiter(","))
|
||||
from _1 in Delimiter(")")
|
||||
select new ProcedureCallNode(identifier, expressions);
|
||||
|
||||
return Choice(
|
||||
TrueParser(),
|
||||
FalseParser(),
|
||||
NumberParser(),
|
||||
minusParser,
|
||||
plusParser,
|
||||
notParser,
|
||||
procedureCallParser,
|
||||
VariableParser(),
|
||||
parenthesisParser
|
||||
);
|
||||
}
|
||||
|
||||
private static IParser<LexicalToken, SyntaxNodeBase> TermRecursively(SyntaxNodeBase left)
|
||||
{
|
||||
// MultiplyOperator -> * | / | div | mod | and
|
||||
IParser<LexicalToken, BinaryOperatorType> multiplyOperator = Choice(
|
||||
from _ in Operator("*")
|
||||
select BinaryOperatorType.Multiply,
|
||||
from _ in Operator("/")
|
||||
select BinaryOperatorType.Divide,
|
||||
from _ in Keyword("div")
|
||||
select BinaryOperatorType.IntegerDivide,
|
||||
from _ in Keyword("mod")
|
||||
select BinaryOperatorType.Mod,
|
||||
from _ in Keyword("and")
|
||||
select BinaryOperatorType.And);
|
||||
|
||||
return multiplyOperator.Next(op =>
|
||||
{
|
||||
return FactorParser().Map(right => new BinaryOperatorNode(op, left, right))
|
||||
.Bind(TermRecursively);
|
||||
}, left);
|
||||
}
|
||||
|
||||
public static IParser<LexicalToken, SyntaxNodeBase> TermParser()
|
||||
{
|
||||
// Term -> Factor | Term MultiplyOperator Factor
|
||||
// 消除左递归为
|
||||
// Term -> Factor Term'
|
||||
// Term' -> MultiplyOperator Factor Term' | ε
|
||||
return FactorParser().Bind(TermRecursively);
|
||||
}
|
||||
|
||||
private static IParser<LexicalToken, SyntaxNodeBase> SimpleExpressionRecursively(SyntaxNodeBase left)
|
||||
{
|
||||
// AddOperator -> + | - | or
|
||||
IParser<LexicalToken, BinaryOperatorType> addOperator = Choice(
|
||||
from _ in Operator("+")
|
||||
select BinaryOperatorType.Add,
|
||||
from _ in Operator("-")
|
||||
select BinaryOperatorType.Subtract,
|
||||
from _ in Keyword("or")
|
||||
select BinaryOperatorType.Or);
|
||||
|
||||
return addOperator.Next(op =>
|
||||
{
|
||||
return TermParser().Map(right => new BinaryOperatorNode(op, left, right))
|
||||
.Bind(SimpleExpressionRecursively);
|
||||
}, left);
|
||||
}
|
||||
|
||||
public static IParser<LexicalToken, SyntaxNodeBase> SimpleExpressionParser()
|
||||
{
|
||||
// SimpleExpression -> Term | SimpleExpression AddOperator Term
|
||||
// 消除左递归为
|
||||
// SimpleExpression -> Term SimpleExpression'
|
||||
// SimpleExpression' -> AddOperator Term SimpleExpression' | ε
|
||||
return TermParser().Bind(SimpleExpressionRecursively);
|
||||
}
|
||||
|
||||
public static IParser<LexicalToken, SyntaxNodeBase> ExpressionParser()
|
||||
{
|
||||
// RelationOperator -> = | <> | < | <= | > | >=
|
||||
IParser<LexicalToken, BinaryOperatorType> relationOperator = Choice(
|
||||
from _ in Operator("=")
|
||||
select BinaryOperatorType.Equal,
|
||||
from _ in Operator("<>")
|
||||
select BinaryOperatorType.NotEqual,
|
||||
from _ in Operator("<")
|
||||
select BinaryOperatorType.Less,
|
||||
from _ in Operator("<=")
|
||||
select BinaryOperatorType.LessEqual,
|
||||
from _ in Operator(">")
|
||||
select BinaryOperatorType.Greater,
|
||||
from _ in Operator(">=")
|
||||
select BinaryOperatorType.GreaterEqual);
|
||||
|
||||
// Expression -> SimpleExpression | SimpleExpression RelationOperator SimpleExpression
|
||||
return Choice(
|
||||
from left in SimpleExpressionParser()
|
||||
from op in relationOperator
|
||||
from right in SimpleExpressionParser()
|
||||
select new BinaryOperatorNode(op, left, right),
|
||||
SimpleExpressionParser()
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ExpressionList Parser
|
||||
/// ExpressionList -> Expression | ExpressionList , Expression
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static IParser<LexicalToken, IEnumerable<SyntaxNodeBase>> ExpressionsParser()
|
||||
=> ExpressionParser().SeparatedBy1(Delimiter(","));
|
||||
|
||||
public static IParser<LexicalToken, VariableNode> VariableParser()
|
||||
{
|
||||
return Choice(
|
||||
from token in IdentifierParser()
|
||||
from _ in Delimiter("[")
|
||||
from expressions in ExpressionsParser()
|
||||
from _1 in Delimiter("]")
|
||||
select new VariableNode(token, expressions),
|
||||
from token in IdentifierParser()
|
||||
select new VariableNode(token)
|
||||
);
|
||||
}
|
||||
|
||||
public static IParser<LexicalToken, IfNode> IfParser()
|
||||
{
|
||||
IParser<LexicalToken, IfNode> commonPart = from _ in Keyword("if")
|
||||
from condition in ExpressionParser()
|
||||
from _1 in Keyword("then")
|
||||
from statement in StatementParser()
|
||||
select new IfNode(condition, statement);
|
||||
|
||||
return Choice(
|
||||
from common in commonPart
|
||||
from _ in Keyword("else")
|
||||
from elseStatement in StatementParser()
|
||||
select new IfNode(common.Condition, common.Statement, elseStatement),
|
||||
commonPart
|
||||
);
|
||||
}
|
||||
|
||||
public static IParser<LexicalToken, ForNode> ForParser()
|
||||
{
|
||||
return from _ in Keyword("for")
|
||||
from identifier in IdentifierParser()
|
||||
from _1 in Operator(":=")
|
||||
from left in ExpressionParser()
|
||||
from _2 in Keyword("to")
|
||||
from right in ExpressionParser()
|
||||
from _3 in Keyword("do")
|
||||
from statement in StatementParser()
|
||||
select new ForNode(identifier, left, right, statement);
|
||||
}
|
||||
|
||||
public static IParser<LexicalToken, WhileNode> WhileParser()
|
||||
{
|
||||
return from _ in Keyword("while")
|
||||
from condition in ExpressionParser()
|
||||
from _1 in Keyword("do")
|
||||
from statement in StatementParser()
|
||||
select new WhileNode(condition, statement);
|
||||
}
|
||||
|
||||
public static IParser<LexicalToken, ProcedureCallNode> ProcedureCallParser()
|
||||
{
|
||||
return Choice(
|
||||
from identifier in IdentifierParser()
|
||||
from _ in Delimiter("(")
|
||||
from expressions in ExpressionParser().SeparatedBy(Delimiter(","))
|
||||
from _1 in Delimiter(")")
|
||||
select new ProcedureCallNode(identifier, expressions),
|
||||
from identifier in IdentifierParser()
|
||||
select new ProcedureCallNode(identifier, [])
|
||||
);
|
||||
}
|
||||
|
||||
public static IParser<LexicalToken, SyntaxNodeBase> StatementParser()
|
||||
{
|
||||
return Choice<LexicalToken, SyntaxNodeBase>(
|
||||
from variable in VariableParser()
|
||||
from _ in Operator(":=")
|
||||
from expression in ExpressionParser()
|
||||
select new AssignNode(variable, expression),
|
||||
ProcedureCallParser(),
|
||||
IfParser(),
|
||||
ForParser(),
|
||||
WhileParser(),
|
||||
CompoundStatementParser()
|
||||
);
|
||||
}
|
||||
|
||||
public static IParser<LexicalToken, BlockNode> CompoundStatementParser()
|
||||
{
|
||||
return from _1 in Keyword("begin")
|
||||
from statements in StatementParser().SeparatedOrEndBy(Delimiter(";"))
|
||||
from _2 in Keyword("end")
|
||||
select new BlockNode(statements);
|
||||
}
|
||||
|
||||
public static IParser<LexicalToken, SyntaxNodeBase> ConstValueParser()
|
||||
{
|
||||
return Choice(
|
||||
from _ in Operator("-")
|
||||
from num in NumberParser()
|
||||
select new UnaryOperatorNode(UnaryOperatorType.Minus, num),
|
||||
from _ in Operator("+")
|
||||
from num in NumberParser()
|
||||
select new UnaryOperatorNode(UnaryOperatorType.Plus, num),
|
||||
NumberParser(),
|
||||
CharParser(),
|
||||
TrueParser(),
|
||||
FalseParser()
|
||||
);
|
||||
}
|
||||
|
||||
public static IParser<LexicalToken, SyntaxNodeBase> ConstDeclarationParser()
|
||||
{
|
||||
return from identifier in Satisfy<LexicalToken>(token =>
|
||||
token.TokenType == LexicalTokenType.Identifier)
|
||||
from _ in Operator("=")
|
||||
from node in ConstValueParser()
|
||||
select new ConstantNode(identifier, node);
|
||||
}
|
||||
|
||||
public static IParser<LexicalToken, BlockNode> ConstDeclarationsParser()
|
||||
{
|
||||
return (from _ in Keyword("const")
|
||||
from tokens in ConstDeclarationParser().SeparatedOrEndBy1(Delimiter(";"))
|
||||
select new BlockNode(tokens)).Try(new BlockNode([]));
|
||||
}
|
||||
|
||||
public static IParser<LexicalToken, TypeNode> ArrayTypeParser()
|
||||
{
|
||||
IParser<LexicalToken, IEnumerable<ArrayRange>> arrayRangeParser = (
|
||||
from left in IntegerParser()
|
||||
from _ in Delimiter("..")
|
||||
from right in IntegerParser()
|
||||
select new ArrayRange(left.Value, right.Value)).SeparatedBy1(Delimiter(","));
|
||||
|
||||
return from _ in Keyword("array")
|
||||
from _1 in Delimiter("[")
|
||||
from ranges in arrayRangeParser
|
||||
from _2 in Delimiter("]")
|
||||
from _3 in Keyword("of")
|
||||
from typeToken in BasicTypeParser()
|
||||
select new TypeNode(typeToken, ranges);
|
||||
}
|
||||
|
||||
public static IParser<LexicalToken, SyntaxNodeBase> TypeParser()
|
||||
{
|
||||
return Choice(ArrayTypeParser(),
|
||||
from token in BasicTypeParser()
|
||||
select new TypeNode(token));
|
||||
}
|
||||
|
||||
public static IParser<LexicalToken, VariableDeclarationNode> VariableDeclarationParser()
|
||||
{
|
||||
return from tokens in Satisfy<LexicalToken>(
|
||||
token => token.TokenType == LexicalTokenType.Identifier).SeparatedBy1(Delimiter(","))
|
||||
from _1 in Delimiter(":")
|
||||
from type in TypeParser()
|
||||
select new VariableDeclarationNode(tokens, type.Convert<TypeNode>());
|
||||
}
|
||||
|
||||
public static IParser<LexicalToken, BlockNode> VariableDeclarationsParser()
|
||||
{
|
||||
return (from _ in Keyword("var")
|
||||
from nodes in VariableDeclarationParser().SeparatedOrEndBy1(Delimiter(";"))
|
||||
select new BlockNode(nodes)).Try(new BlockNode([]));
|
||||
}
|
||||
|
||||
public static IParser<LexicalToken, IEnumerable<Parameter>> ParameterParser()
|
||||
{
|
||||
return Choice(
|
||||
from _ in Keyword("var")
|
||||
from tokens in IdentifierParser().SeparatedBy1(Delimiter(","))
|
||||
from _1 in Delimiter(":")
|
||||
from typeToken in TypeParser()
|
||||
select tokens.Select(x => new Parameter(true, x, typeToken)),
|
||||
from tokens in IdentifierParser().SeparatedBy1(Delimiter(","))
|
||||
from _ in Delimiter(":")
|
||||
from typeToken in TypeParser()
|
||||
select tokens.Select(x => new Parameter(false, x, typeToken))
|
||||
);
|
||||
}
|
||||
|
||||
public static IParser<LexicalToken, IEnumerable<Parameter>> FormalParameterParser()
|
||||
{
|
||||
return (from _ in Delimiter("(")
|
||||
from parameters in ParameterParser().SeparatedBy(Delimiter(";"))
|
||||
from _1 in Delimiter(")")
|
||||
select parameters.Aggregate(new List<Parameter>(), (result, array) =>
|
||||
{
|
||||
result.AddRange(array);
|
||||
return result;
|
||||
})).Try([]);
|
||||
}
|
||||
|
||||
public static IParser<LexicalToken, SubprogramHead> SubprogramHeadParser()
|
||||
{
|
||||
return Choice(
|
||||
from _ in Keyword("procedure")
|
||||
from identifier in IdentifierParser()
|
||||
from parameters in FormalParameterParser()
|
||||
select new SubprogramHead(identifier, parameters),
|
||||
from _ in Keyword("function")
|
||||
from identifier in IdentifierParser()
|
||||
from parameters in FormalParameterParser()
|
||||
from _1 in Delimiter(":")
|
||||
from typeToken in TypeParser()
|
||||
select new SubprogramHead(identifier, parameters, typeToken)
|
||||
);
|
||||
}
|
||||
|
||||
public static IParser<LexicalToken, SubprogramBody> SubprogramBodyParser()
|
||||
{
|
||||
return from constant in ConstDeclarationsParser()
|
||||
from variables in VariableDeclarationsParser()
|
||||
from block in CompoundStatementParser()
|
||||
select new SubprogramBody(constant, variables, block);
|
||||
}
|
||||
|
||||
public static IParser<LexicalToken, Subprogram> SubprogramParser()
|
||||
{
|
||||
return from head in SubprogramHeadParser()
|
||||
from _ in Delimiter(";")
|
||||
from body in SubprogramBodyParser()
|
||||
select new Subprogram(head, body);
|
||||
}
|
||||
|
||||
public static IParser<LexicalToken, ProgramBody> ProgramBodyParser()
|
||||
{
|
||||
return from constant in ConstDeclarationsParser()
|
||||
from variables in VariableDeclarationsParser()
|
||||
from subprograms in SubprogramParser().SeparatedOrEndBy(Delimiter(";"))
|
||||
.Map(x => new BlockNode(x))
|
||||
from block in CompoundStatementParser()
|
||||
select new ProgramBody(constant, variables, subprograms, block);
|
||||
}
|
||||
|
||||
public static IParser<LexicalToken, ProgramHead> ProgramHeadParser()
|
||||
{
|
||||
return from _ in Keyword("program")
|
||||
from token in Satisfy<LexicalToken>(token => token.TokenType == LexicalTokenType.Identifier)
|
||||
select new ProgramHead(token);
|
||||
}
|
||||
|
||||
public static IParser<LexicalToken, Program> ProgramParser()
|
||||
{
|
||||
return from head in ProgramHeadParser()
|
||||
from _1 in Delimiter(";")
|
||||
from body in ProgramBodyParser()
|
||||
from _2 in Delimiter(".")
|
||||
select new Program(head, body);
|
||||
}
|
||||
}
|
70
CanonSharp.Pascal/Parser/GrammarParserBase.cs
Normal file
70
CanonSharp.Pascal/Parser/GrammarParserBase.cs
Normal file
|
@ -0,0 +1,70 @@
|
|||
using CanonSharp.Combinator.Abstractions;
|
||||
using CanonSharp.Combinator.Extensions;
|
||||
using CanonSharp.Pascal.Scanner;
|
||||
using CanonSharp.Pascal.SyntaxTree;
|
||||
using static CanonSharp.Combinator.ParserBuilder;
|
||||
|
||||
namespace CanonSharp.Pascal.Parser;
|
||||
|
||||
public abstract class GrammarParserBuilder
|
||||
{
|
||||
protected static IParser<LexicalToken, LexicalToken> Keyword(string value)
|
||||
=> Satisfy<LexicalToken>(token =>
|
||||
token.TokenType == LexicalTokenType.Keyword &&
|
||||
token.LiteralValue.Equals(value, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
protected static IParser<LexicalToken, LexicalToken> Operator(string value)
|
||||
=> Satisfy<LexicalToken>(token => token.TokenType == LexicalTokenType.Operator && token.LiteralValue == value);
|
||||
|
||||
protected static IParser<LexicalToken, LexicalToken> Delimiter(string value)
|
||||
=> Satisfy<LexicalToken>(token => token.TokenType == LexicalTokenType.Delimiter && token.LiteralValue == value);
|
||||
|
||||
protected static IParser<LexicalToken, BooleanValueNode> TrueParser()
|
||||
{
|
||||
return from _ in Keyword("true")
|
||||
select new BooleanValueNode(true);
|
||||
}
|
||||
|
||||
protected static IParser<LexicalToken, BooleanValueNode> FalseParser()
|
||||
{
|
||||
return from _ in Keyword("false")
|
||||
select new BooleanValueNode(false);
|
||||
}
|
||||
|
||||
protected static IParser<LexicalToken, IntegerValueNode> IntegerParser()
|
||||
{
|
||||
return from token in Satisfy<LexicalToken>(token => token.TokenType == LexicalTokenType.ConstInteger)
|
||||
select new IntegerValueNode(int.Parse(token.LiteralValue));
|
||||
}
|
||||
|
||||
protected static IParser<LexicalToken, SyntaxNodeBase> NumberParser()
|
||||
{
|
||||
return Choice<LexicalToken, SyntaxNodeBase>(
|
||||
from token in Satisfy<LexicalToken>(x => x.TokenType == LexicalTokenType.ConstInteger)
|
||||
select new IntegerValueNode(int.Parse(token.LiteralValue)),
|
||||
from token in Satisfy<LexicalToken>(x => x.TokenType == LexicalTokenType.ConstFloat)
|
||||
select new FloatValueNode(double.Parse(token.LiteralValue))
|
||||
);
|
||||
}
|
||||
|
||||
protected static IParser<LexicalToken, CharValueNode> CharParser()
|
||||
{
|
||||
return Satisfy<LexicalToken>(token => token.TokenType == LexicalTokenType.Character)
|
||||
.Map(x => new CharValueNode(char.Parse(x.LiteralValue)));
|
||||
}
|
||||
|
||||
protected static IParser<LexicalToken, LexicalToken> BasicTypeParser()
|
||||
{
|
||||
return Choice(
|
||||
Keyword("integer"),
|
||||
Keyword("real"),
|
||||
Keyword("boolean"),
|
||||
Keyword("char")
|
||||
);
|
||||
}
|
||||
|
||||
protected static IParser<LexicalToken, LexicalToken> IdentifierParser()
|
||||
{
|
||||
return Satisfy<LexicalToken>(token => token.TokenType == LexicalTokenType.Identifier);
|
||||
}
|
||||
}
|
53
CanonSharp.Pascal/Parser/LexicalTokenReadState.cs
Normal file
53
CanonSharp.Pascal/Parser/LexicalTokenReadState.cs
Normal file
|
@ -0,0 +1,53 @@
|
|||
using CanonSharp.Combinator.Abstractions;
|
||||
using CanonSharp.Pascal.Scanner;
|
||||
|
||||
namespace CanonSharp.Pascal.Parser;
|
||||
|
||||
public sealed class LexicalTokenReadState : IReadState<LexicalToken, LexicalTokenReadState>
|
||||
{
|
||||
private readonly List<LexicalToken> _tokens;
|
||||
private readonly int _pos;
|
||||
|
||||
public LexicalToken Current => _tokens[_pos];
|
||||
|
||||
public bool HasValue => _pos < _tokens.Count;
|
||||
|
||||
public LexicalTokenReadState Next => new(_tokens, _pos + 1);
|
||||
|
||||
private LexicalTokenReadState(List<LexicalToken> tokens, int pos)
|
||||
{
|
||||
_tokens = tokens;
|
||||
_pos = pos;
|
||||
}
|
||||
|
||||
public LexicalTokenReadState(IEnumerable<LexicalToken> tokens)
|
||||
{
|
||||
_tokens = tokens.ToList();
|
||||
_pos = 0;
|
||||
}
|
||||
|
||||
public bool Equals(LexicalTokenReadState? other)
|
||||
{
|
||||
if (other is null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_tokens.Count != other._tokens.Count)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach ((LexicalToken first, LexicalToken second) in _tokens.Zip(other._tokens))
|
||||
{
|
||||
if (!first.Equals(second))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return _pos == other._pos;
|
||||
}
|
||||
|
||||
public override string ToString() => HasValue ? Current.ToString() : "End of input stream.";
|
||||
}
|
|
@ -4,11 +4,11 @@ using CanonSharp.Combinator.Extensions;
|
|||
using static CanonSharp.Combinator.Text.TextParserBuilder;
|
||||
using static CanonSharp.Combinator.ParserBuilder;
|
||||
|
||||
namespace CanonSharp.Common.Scanner;
|
||||
namespace CanonSharp.Pascal.Scanner;
|
||||
|
||||
public sealed class LexicalScanner
|
||||
{
|
||||
private readonly Parser<char, IEnumerable<LexicalToken>> _parser = PascalParser();
|
||||
private readonly IParser<char, IEnumerable<LexicalToken>> _parser = PascalParser();
|
||||
|
||||
public IEnumerable<LexicalToken> Tokenize<TState>(TState state)
|
||||
where TState : IReadState<char, TState>
|
||||
|
@ -16,7 +16,7 @@ public sealed class LexicalScanner
|
|||
return _parser.Parse(state).Value;
|
||||
}
|
||||
|
||||
public static Parser<char, LexicalToken> KeywordParser()
|
||||
public static IParser<char, LexicalToken> KeywordParser()
|
||||
{
|
||||
return from value in Choice(StringIgnoreCase("program"),
|
||||
StringIgnoreCase("const"),
|
||||
|
@ -37,7 +37,7 @@ public sealed class LexicalScanner
|
|||
StringIgnoreCase("real"),
|
||||
StringIgnoreCase("boolean"),
|
||||
StringIgnoreCase("char"),
|
||||
StringIgnoreCase("divide"),
|
||||
StringIgnoreCase("div"),
|
||||
StringIgnoreCase("not"),
|
||||
StringIgnoreCase("mod"),
|
||||
StringIgnoreCase("and"),
|
||||
|
@ -49,16 +49,16 @@ public sealed class LexicalScanner
|
|||
select new LexicalToken(LexicalTokenType.Keyword, value);
|
||||
}
|
||||
|
||||
public static Parser<char, LexicalToken> DelimiterParser()
|
||||
public static IParser<char, LexicalToken> DelimiterParser()
|
||||
{
|
||||
Parser<char, LexicalToken> semicolonParser = from token in Char(':')
|
||||
IParser<char, LexicalToken> semicolonParser = from token in Char(':')
|
||||
from _ in Char('=').LookAhead().Not()
|
||||
select new LexicalToken(LexicalTokenType.Delimiter, token.ToString());
|
||||
Parser<char, LexicalToken> periodParser = from token in Char('.')
|
||||
IParser<char, LexicalToken> periodParser = from token in Char('.')
|
||||
from _ in Char('.').LookAhead().Not()
|
||||
select new LexicalToken(LexicalTokenType.Delimiter, ".");
|
||||
|
||||
Parser<char, LexicalToken> singleCharTokenParser = from token in Choice(
|
||||
IParser<char, LexicalToken> singleCharTokenParser = from token in Choice(
|
||||
String(","),
|
||||
String(";"),
|
||||
String("("),
|
||||
|
@ -71,17 +71,17 @@ public sealed class LexicalScanner
|
|||
return singleCharTokenParser | semicolonParser | periodParser;
|
||||
}
|
||||
|
||||
public static Parser<char, LexicalToken> OperatorParser()
|
||||
public static IParser<char, LexicalToken> OperatorParser()
|
||||
{
|
||||
Parser<char, LexicalToken> lessParser = from token in Char('<')
|
||||
IParser<char, LexicalToken> lessParser = from token in Char('<')
|
||||
from _ in Char('=').LookAhead().Not()
|
||||
select new LexicalToken(LexicalTokenType.Operator, "<");
|
||||
|
||||
Parser<char, LexicalToken> greaterParser = from token in Char('>')
|
||||
IParser<char, LexicalToken> greaterParser = from token in Char('>')
|
||||
from _ in Char('=').LookAhead().Not()
|
||||
select new LexicalToken(LexicalTokenType.Operator, ">");
|
||||
|
||||
Parser<char, LexicalToken> otherParsers = from token in Choice(
|
||||
IParser<char, LexicalToken> otherParsers = from token in Choice(
|
||||
String("="),
|
||||
String("!="),
|
||||
String("<="),
|
||||
|
@ -96,14 +96,19 @@ public sealed class LexicalScanner
|
|||
return otherParsers | lessParser | greaterParser;
|
||||
}
|
||||
|
||||
public static Parser<char, LexicalToken> ConstIntegerParser()
|
||||
public static IParser<char, LexicalToken> ConstIntegerParser()
|
||||
{
|
||||
return from nums in AsciiDigit().Many1()
|
||||
return Choice(
|
||||
from nums in AsciiDigit().Many1()
|
||||
from _ in String("..").LookAhead()
|
||||
select new LexicalToken(LexicalTokenType.ConstInteger, new string(nums.ToArray())),
|
||||
from nums in AsciiDigit().Many1()
|
||||
from _ in Char('.').LookAhead().Not()
|
||||
select new LexicalToken(LexicalTokenType.ConstInteger, new string(nums.ToArray()));
|
||||
select new LexicalToken(LexicalTokenType.ConstInteger, new string(nums.ToArray()))
|
||||
);
|
||||
}
|
||||
|
||||
public static Parser<char, LexicalToken> ConstFloatParser()
|
||||
public static IParser<char, LexicalToken> ConstFloatParser()
|
||||
{
|
||||
return from integer in AsciiDigit().Many1()
|
||||
from _ in Char('.')
|
||||
|
@ -112,24 +117,24 @@ public sealed class LexicalScanner
|
|||
new string(integer.ToArray()) + '.' + new string(fraction.ToArray()));
|
||||
}
|
||||
|
||||
public static Parser<char, LexicalToken> IdentifierParser()
|
||||
public static IParser<char, LexicalToken> IdentifierParser()
|
||||
{
|
||||
return from first in AsciiLetter() | Char('_')
|
||||
from second in (AsciiLetter() | AsciiDigit() | Char('_')).Many()
|
||||
select new LexicalToken(LexicalTokenType.Identifier, first + new string(second.ToArray()));
|
||||
}
|
||||
|
||||
public static Parser<char, Unit> CommentParser()
|
||||
public static IParser<char, Unit> CommentParser()
|
||||
{
|
||||
return Any<char>().Quote(Char('{'), Char('}')).Map(_ => Unit.Instance);
|
||||
}
|
||||
|
||||
public static Parser<char, Unit> JunkParser()
|
||||
public static IParser<char, Unit> JunkParser()
|
||||
{
|
||||
return Space().Map(_ => Unit.Instance) | LineBreak().Map(_ => Unit.Instance) | CommentParser();
|
||||
}
|
||||
|
||||
public static Parser<char, LexicalToken> CharParser()
|
||||
public static IParser<char, LexicalToken> CharParser()
|
||||
{
|
||||
return from str in Any<char>().Quote(Char('\'')).Map(x => new string(x.ToArray()))
|
||||
select str.Length <= 1
|
||||
|
@ -137,13 +142,13 @@ public sealed class LexicalScanner
|
|||
: new LexicalToken(LexicalTokenType.String, str);
|
||||
}
|
||||
|
||||
public static Parser<char, IEnumerable<LexicalToken>> PascalParser()
|
||||
public static IParser<char, IEnumerable<LexicalToken>> PascalParser()
|
||||
{
|
||||
return JunkParser().SkipTill(Choice(KeywordParser(),
|
||||
DelimiterParser(),
|
||||
OperatorParser(),
|
||||
ConstIntegerParser(),
|
||||
ConstFloatParser(),
|
||||
ConstIntegerParser(),
|
||||
CharParser(),
|
||||
IdentifierParser())).Many();
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
namespace CanonSharp.Common.Scanner;
|
||||
namespace CanonSharp.Pascal.Scanner;
|
||||
|
||||
public enum LexicalTokenType
|
||||
{
|
|
@ -1,6 +1,6 @@
|
|||
using CanonSharp.Combinator.Abstractions;
|
||||
|
||||
namespace CanonSharp.Common.Scanner;
|
||||
namespace CanonSharp.Pascal.Scanner;
|
||||
|
||||
/// <summary>
|
||||
/// 字符串输入流状态
|
10
CanonSharp.Pascal/SyntaxTree/AssignNode.cs
Normal file
10
CanonSharp.Pascal/SyntaxTree/AssignNode.cs
Normal file
|
@ -0,0 +1,10 @@
|
|||
namespace CanonSharp.Pascal.SyntaxTree;
|
||||
|
||||
public sealed class AssignNode(VariableNode variable, SyntaxNodeBase expression) : SyntaxNodeBase
|
||||
{
|
||||
public override SyntaxNodeType NodeType => SyntaxNodeType.Assign;
|
||||
|
||||
public VariableNode Variable => variable;
|
||||
|
||||
public SyntaxNodeBase Expression => expression;
|
||||
}
|
33
CanonSharp.Pascal/SyntaxTree/BinaryOperatorNode.cs
Normal file
33
CanonSharp.Pascal/SyntaxTree/BinaryOperatorNode.cs
Normal file
|
@ -0,0 +1,33 @@
|
|||
namespace CanonSharp.Pascal.SyntaxTree;
|
||||
|
||||
public enum BinaryOperatorType
|
||||
{
|
||||
Add,
|
||||
Subtract,
|
||||
Multiply,
|
||||
|
||||
// Pascal特有的整数除法关键词div
|
||||
IntegerDivide,
|
||||
Divide,
|
||||
Mod,
|
||||
And,
|
||||
Or,
|
||||
Equal,
|
||||
NotEqual,
|
||||
Greater,
|
||||
GreaterEqual,
|
||||
Less,
|
||||
LessEqual
|
||||
}
|
||||
|
||||
public sealed class BinaryOperatorNode(BinaryOperatorType operatorType, SyntaxNodeBase left, SyntaxNodeBase right)
|
||||
: SyntaxNodeBase
|
||||
{
|
||||
public override SyntaxNodeType NodeType => SyntaxNodeType.BinaryOperator;
|
||||
|
||||
public BinaryOperatorType OperatorType => operatorType;
|
||||
|
||||
public SyntaxNodeBase Left => left;
|
||||
|
||||
public SyntaxNodeBase Right => right;
|
||||
}
|
8
CanonSharp.Pascal/SyntaxTree/BlockNode.cs
Normal file
8
CanonSharp.Pascal/SyntaxTree/BlockNode.cs
Normal file
|
@ -0,0 +1,8 @@
|
|||
namespace CanonSharp.Pascal.SyntaxTree;
|
||||
|
||||
public sealed class BlockNode(IEnumerable<SyntaxNodeBase> statements) : SyntaxNodeBase
|
||||
{
|
||||
public override SyntaxNodeType NodeType => SyntaxNodeType.Block;
|
||||
|
||||
public IList<SyntaxNodeBase> Statements => statements.ToList();
|
||||
}
|
8
CanonSharp.Pascal/SyntaxTree/BooleanValueNode.cs
Normal file
8
CanonSharp.Pascal/SyntaxTree/BooleanValueNode.cs
Normal file
|
@ -0,0 +1,8 @@
|
|||
namespace CanonSharp.Pascal.SyntaxTree;
|
||||
|
||||
public sealed class BooleanValueNode(bool value) : SyntaxNodeBase
|
||||
{
|
||||
public override SyntaxNodeType NodeType => SyntaxNodeType.BooleanValue;
|
||||
|
||||
public bool Value => value;
|
||||
}
|
8
CanonSharp.Pascal/SyntaxTree/CharValueNode.cs
Normal file
8
CanonSharp.Pascal/SyntaxTree/CharValueNode.cs
Normal file
|
@ -0,0 +1,8 @@
|
|||
namespace CanonSharp.Pascal.SyntaxTree;
|
||||
|
||||
public sealed class CharValueNode(char value) : SyntaxNodeBase
|
||||
{
|
||||
public override SyntaxNodeType NodeType => SyntaxNodeType.CharValue;
|
||||
|
||||
public char Value => value;
|
||||
}
|
12
CanonSharp.Pascal/SyntaxTree/ConstantNode.cs
Normal file
12
CanonSharp.Pascal/SyntaxTree/ConstantNode.cs
Normal file
|
@ -0,0 +1,12 @@
|
|||
using CanonSharp.Pascal.Scanner;
|
||||
|
||||
namespace CanonSharp.Pascal.SyntaxTree;
|
||||
|
||||
public sealed class ConstantNode(LexicalToken identifier, SyntaxNodeBase value) : SyntaxNodeBase
|
||||
{
|
||||
public override SyntaxNodeType NodeType => SyntaxNodeType.Constant;
|
||||
|
||||
public LexicalToken Identifier = identifier;
|
||||
|
||||
public SyntaxNodeBase Value = value;
|
||||
}
|
8
CanonSharp.Pascal/SyntaxTree/FloatValueNode.cs
Normal file
8
CanonSharp.Pascal/SyntaxTree/FloatValueNode.cs
Normal file
|
@ -0,0 +1,8 @@
|
|||
namespace CanonSharp.Pascal.SyntaxTree;
|
||||
|
||||
public sealed class FloatValueNode(double value) : SyntaxNodeBase
|
||||
{
|
||||
public override SyntaxNodeType NodeType => SyntaxNodeType.FloatValue;
|
||||
|
||||
public double Value => value;
|
||||
}
|
20
CanonSharp.Pascal/SyntaxTree/ForNode.cs
Normal file
20
CanonSharp.Pascal/SyntaxTree/ForNode.cs
Normal file
|
@ -0,0 +1,20 @@
|
|||
using CanonSharp.Pascal.Scanner;
|
||||
|
||||
namespace CanonSharp.Pascal.SyntaxTree;
|
||||
|
||||
public sealed class ForNode(
|
||||
LexicalToken identifier,
|
||||
SyntaxNodeBase leftCondition,
|
||||
SyntaxNodeBase rightCondition,
|
||||
SyntaxNodeBase statement) : SyntaxNodeBase
|
||||
{
|
||||
public override SyntaxNodeType NodeType => SyntaxNodeType.For;
|
||||
|
||||
public LexicalToken Identifier => identifier;
|
||||
|
||||
public SyntaxNodeBase LeftCondition => leftCondition;
|
||||
|
||||
public SyntaxNodeBase RightCondition => rightCondition;
|
||||
|
||||
public SyntaxNodeBase Statement => statement;
|
||||
}
|
26
CanonSharp.Pascal/SyntaxTree/IfNode.cs
Normal file
26
CanonSharp.Pascal/SyntaxTree/IfNode.cs
Normal file
|
@ -0,0 +1,26 @@
|
|||
namespace CanonSharp.Pascal.SyntaxTree;
|
||||
|
||||
public sealed class IfNode : SyntaxNodeBase
|
||||
{
|
||||
public override SyntaxNodeType NodeType => SyntaxNodeType.If;
|
||||
|
||||
public SyntaxNodeBase Condition { get; }
|
||||
|
||||
public SyntaxNodeBase Statement { get; }
|
||||
|
||||
public SyntaxNodeBase? ElseStatement { get; }
|
||||
|
||||
public IfNode(SyntaxNodeBase condition, SyntaxNodeBase statement)
|
||||
{
|
||||
Condition = condition;
|
||||
Statement = statement;
|
||||
ElseStatement = null;
|
||||
}
|
||||
|
||||
public IfNode(SyntaxNodeBase condition, SyntaxNodeBase statement, SyntaxNodeBase elseStatement)
|
||||
{
|
||||
Condition = condition;
|
||||
Statement = statement;
|
||||
ElseStatement = elseStatement;
|
||||
}
|
||||
}
|
8
CanonSharp.Pascal/SyntaxTree/IntegerValueNode.cs
Normal file
8
CanonSharp.Pascal/SyntaxTree/IntegerValueNode.cs
Normal file
|
@ -0,0 +1,8 @@
|
|||
namespace CanonSharp.Pascal.SyntaxTree;
|
||||
|
||||
public sealed class IntegerValueNode(int value) : SyntaxNodeBase
|
||||
{
|
||||
public override SyntaxNodeType NodeType => SyntaxNodeType.IntegerValue;
|
||||
|
||||
public int Value => value;
|
||||
}
|
14
CanonSharp.Pascal/SyntaxTree/Parameter.cs
Normal file
14
CanonSharp.Pascal/SyntaxTree/Parameter.cs
Normal file
|
@ -0,0 +1,14 @@
|
|||
using CanonSharp.Pascal.Scanner;
|
||||
|
||||
namespace CanonSharp.Pascal.SyntaxTree;
|
||||
|
||||
public sealed class Parameter(bool isReference, LexicalToken identifier, SyntaxNodeBase typeNode) : SyntaxNodeBase
|
||||
{
|
||||
public override SyntaxNodeType NodeType => SyntaxNodeType.Parameter;
|
||||
|
||||
public bool IsReference => isReference;
|
||||
|
||||
public LexicalToken Identifier => identifier;
|
||||
|
||||
public SyntaxNodeBase TypeNode => typeNode;
|
||||
}
|
18
CanonSharp.Pascal/SyntaxTree/ProcedureCallNode.cs
Normal file
18
CanonSharp.Pascal/SyntaxTree/ProcedureCallNode.cs
Normal file
|
@ -0,0 +1,18 @@
|
|||
using CanonSharp.Pascal.Scanner;
|
||||
|
||||
namespace CanonSharp.Pascal.SyntaxTree;
|
||||
|
||||
public sealed class ProcedureCallNode : SyntaxNodeBase
|
||||
{
|
||||
public ProcedureCallNode(LexicalToken identifier, IEnumerable<SyntaxNodeBase> parameters)
|
||||
{
|
||||
Identifier = identifier;
|
||||
Parameters.AddRange(parameters);
|
||||
}
|
||||
|
||||
public override SyntaxNodeType NodeType => SyntaxNodeType.ProcedureCall;
|
||||
|
||||
public LexicalToken Identifier { get; }
|
||||
|
||||
public List<SyntaxNodeBase> Parameters { get; } = [];
|
||||
}
|
10
CanonSharp.Pascal/SyntaxTree/Program.cs
Normal file
10
CanonSharp.Pascal/SyntaxTree/Program.cs
Normal file
|
@ -0,0 +1,10 @@
|
|||
namespace CanonSharp.Pascal.SyntaxTree;
|
||||
|
||||
public sealed class Program(ProgramHead head, ProgramBody body) : SyntaxNodeBase
|
||||
{
|
||||
public override SyntaxNodeType NodeType => SyntaxNodeType.Program;
|
||||
|
||||
public ProgramHead Head => head;
|
||||
|
||||
public ProgramBody Body => body;
|
||||
}
|
17
CanonSharp.Pascal/SyntaxTree/ProgramBody.cs
Normal file
17
CanonSharp.Pascal/SyntaxTree/ProgramBody.cs
Normal file
|
@ -0,0 +1,17 @@
|
|||
namespace CanonSharp.Pascal.SyntaxTree;
|
||||
|
||||
public sealed class ProgramBody(BlockNode constantDeclarations, BlockNode variableDeclarations,
|
||||
BlockNode subprograms,
|
||||
BlockNode mainBlock)
|
||||
: SyntaxNodeBase
|
||||
{
|
||||
public override SyntaxNodeType NodeType => SyntaxNodeType.ProgramBody;
|
||||
|
||||
public BlockNode ConstantDeclarations => constantDeclarations;
|
||||
|
||||
public BlockNode VariableDeclarations => variableDeclarations;
|
||||
|
||||
public BlockNode Subprograms => subprograms;
|
||||
|
||||
public BlockNode MainBlock => mainBlock;
|
||||
}
|
10
CanonSharp.Pascal/SyntaxTree/ProgramHead.cs
Normal file
10
CanonSharp.Pascal/SyntaxTree/ProgramHead.cs
Normal file
|
@ -0,0 +1,10 @@
|
|||
using CanonSharp.Pascal.Scanner;
|
||||
|
||||
namespace CanonSharp.Pascal.SyntaxTree;
|
||||
|
||||
public sealed class ProgramHead(LexicalToken programName) : SyntaxNodeBase
|
||||
{
|
||||
public override SyntaxNodeType NodeType => SyntaxNodeType.ProgramHead;
|
||||
|
||||
public LexicalToken ProgramName => programName;
|
||||
}
|
10
CanonSharp.Pascal/SyntaxTree/Subprogram.cs
Normal file
10
CanonSharp.Pascal/SyntaxTree/Subprogram.cs
Normal file
|
@ -0,0 +1,10 @@
|
|||
namespace CanonSharp.Pascal.SyntaxTree;
|
||||
|
||||
public sealed class Subprogram(SubprogramHead subprogramHead, SubprogramBody subprogramBody) : SyntaxNodeBase
|
||||
{
|
||||
public override SyntaxNodeType NodeType => SyntaxNodeType.SubProgram;
|
||||
|
||||
public SubprogramHead Head => subprogramHead;
|
||||
|
||||
public SubprogramBody Body => subprogramBody;
|
||||
}
|
12
CanonSharp.Pascal/SyntaxTree/SubprogramBody.cs
Normal file
12
CanonSharp.Pascal/SyntaxTree/SubprogramBody.cs
Normal file
|
@ -0,0 +1,12 @@
|
|||
namespace CanonSharp.Pascal.SyntaxTree;
|
||||
|
||||
public sealed class SubprogramBody(BlockNode constDeclarations, BlockNode variableDeclarations, BlockNode mainBlock) : SyntaxNodeBase
|
||||
{
|
||||
public override SyntaxNodeType NodeType => SyntaxNodeType.SubprogramBody;
|
||||
|
||||
public BlockNode ConstDeclarations => constDeclarations;
|
||||
|
||||
public BlockNode VariableDeclarations => variableDeclarations;
|
||||
|
||||
public BlockNode MainBlock => mainBlock;
|
||||
}
|
28
CanonSharp.Pascal/SyntaxTree/SubprogramHead.cs
Normal file
28
CanonSharp.Pascal/SyntaxTree/SubprogramHead.cs
Normal file
|
@ -0,0 +1,28 @@
|
|||
using CanonSharp.Pascal.Scanner;
|
||||
|
||||
namespace CanonSharp.Pascal.SyntaxTree;
|
||||
|
||||
public sealed class SubprogramHead : SyntaxNodeBase
|
||||
{
|
||||
public override SyntaxNodeType NodeType => SyntaxNodeType.SubprogramHead;
|
||||
|
||||
public LexicalToken Identifier { get; }
|
||||
|
||||
public List<Parameter> Parameters { get; } = [];
|
||||
|
||||
public SyntaxNodeBase? TypeToken { get; }
|
||||
|
||||
public SubprogramHead(LexicalToken identifier, IEnumerable<Parameter> parameters)
|
||||
{
|
||||
Identifier = identifier;
|
||||
Parameters.AddRange(parameters);
|
||||
TypeToken = null;
|
||||
}
|
||||
|
||||
public SubprogramHead(LexicalToken identifier, IEnumerable<Parameter> parameters, SyntaxNodeBase typeToken) : this(
|
||||
identifier, parameters)
|
||||
{
|
||||
TypeToken = typeToken;
|
||||
}
|
||||
|
||||
}
|
43
CanonSharp.Pascal/SyntaxTree/SyntaxNodeBase.cs
Normal file
43
CanonSharp.Pascal/SyntaxTree/SyntaxNodeBase.cs
Normal file
|
@ -0,0 +1,43 @@
|
|||
namespace CanonSharp.Pascal.SyntaxTree;
|
||||
|
||||
public enum SyntaxNodeType
|
||||
{
|
||||
BinaryOperator,
|
||||
UnaryOperator,
|
||||
IntegerValue,
|
||||
FloatValue,
|
||||
BooleanValue,
|
||||
CharValue,
|
||||
Variable,
|
||||
Type,
|
||||
Assign,
|
||||
Block,
|
||||
Constant,
|
||||
VariableDeclaration,
|
||||
If,
|
||||
While,
|
||||
For,
|
||||
ProcedureCall,
|
||||
Parameter,
|
||||
SubprogramHead,
|
||||
SubprogramBody,
|
||||
SubProgram,
|
||||
ProgramBody,
|
||||
ProgramHead,
|
||||
Program
|
||||
}
|
||||
|
||||
public abstract class SyntaxNodeBase
|
||||
{
|
||||
public abstract SyntaxNodeType NodeType { get; }
|
||||
|
||||
public T Convert<T>() where T : SyntaxNodeBase
|
||||
{
|
||||
if (this is not T result)
|
||||
{
|
||||
throw new InvalidCastException($"Can't convert {NodeType} to target node.");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
24
CanonSharp.Pascal/SyntaxTree/TypeNode.cs
Normal file
24
CanonSharp.Pascal/SyntaxTree/TypeNode.cs
Normal file
|
@ -0,0 +1,24 @@
|
|||
using CanonSharp.Pascal.Scanner;
|
||||
|
||||
namespace CanonSharp.Pascal.SyntaxTree;
|
||||
|
||||
public record struct ArrayRange(int Left, int Right);
|
||||
|
||||
public sealed class TypeNode : SyntaxNodeBase
|
||||
{
|
||||
public override SyntaxNodeType NodeType => SyntaxNodeType.Type;
|
||||
|
||||
public LexicalToken TypeToken { get; }
|
||||
|
||||
public List<ArrayRange> ArrayRanges { get; } = [];
|
||||
|
||||
public TypeNode(LexicalToken typeToken)
|
||||
{
|
||||
TypeToken = typeToken;
|
||||
}
|
||||
|
||||
public TypeNode(LexicalToken typeToken, IEnumerable<ArrayRange> arrayRanges) : this(typeToken)
|
||||
{
|
||||
ArrayRanges.AddRange(arrayRanges);
|
||||
}
|
||||
}
|
17
CanonSharp.Pascal/SyntaxTree/UnaryOperatorNode.cs
Normal file
17
CanonSharp.Pascal/SyntaxTree/UnaryOperatorNode.cs
Normal file
|
@ -0,0 +1,17 @@
|
|||
namespace CanonSharp.Pascal.SyntaxTree;
|
||||
|
||||
public enum UnaryOperatorType
|
||||
{
|
||||
Plus,
|
||||
Minus,
|
||||
Not
|
||||
}
|
||||
|
||||
public sealed class UnaryOperatorNode(UnaryOperatorType operatorType, SyntaxNodeBase node) : SyntaxNodeBase
|
||||
{
|
||||
public override SyntaxNodeType NodeType => SyntaxNodeType.UnaryOperator;
|
||||
|
||||
public UnaryOperatorType OperatorType => operatorType;
|
||||
|
||||
public SyntaxNodeBase Node => node;
|
||||
}
|
12
CanonSharp.Pascal/SyntaxTree/VariableDeclarationNode.cs
Normal file
12
CanonSharp.Pascal/SyntaxTree/VariableDeclarationNode.cs
Normal file
|
@ -0,0 +1,12 @@
|
|||
using CanonSharp.Pascal.Scanner;
|
||||
|
||||
namespace CanonSharp.Pascal.SyntaxTree;
|
||||
|
||||
public sealed class VariableDeclarationNode(IEnumerable<LexicalToken> identifiers, TypeNode typeNode) : SyntaxNodeBase
|
||||
{
|
||||
public override SyntaxNodeType NodeType => SyntaxNodeType.VariableDeclaration;
|
||||
|
||||
public IList<LexicalToken> Identifiers => identifiers.ToList();
|
||||
|
||||
public TypeNode TypeNode => typeNode;
|
||||
}
|
23
CanonSharp.Pascal/SyntaxTree/VariableNode.cs
Normal file
23
CanonSharp.Pascal/SyntaxTree/VariableNode.cs
Normal file
|
@ -0,0 +1,23 @@
|
|||
using CanonSharp.Pascal.Scanner;
|
||||
|
||||
namespace CanonSharp.Pascal.SyntaxTree;
|
||||
|
||||
public sealed class VariableNode : SyntaxNodeBase
|
||||
{
|
||||
public override SyntaxNodeType NodeType => SyntaxNodeType.Variable;
|
||||
|
||||
public LexicalToken Identifier { get; }
|
||||
|
||||
public List<SyntaxNodeBase> Indexers { get; } = [];
|
||||
|
||||
public VariableNode(LexicalToken identifier)
|
||||
{
|
||||
Identifier = identifier;
|
||||
}
|
||||
|
||||
public VariableNode(LexicalToken identifier, IEnumerable<SyntaxNodeBase> expressions)
|
||||
{
|
||||
Identifier = identifier;
|
||||
Indexers.AddRange(expressions);
|
||||
}
|
||||
}
|
10
CanonSharp.Pascal/SyntaxTree/WhileNode.cs
Normal file
10
CanonSharp.Pascal/SyntaxTree/WhileNode.cs
Normal file
|
@ -0,0 +1,10 @@
|
|||
namespace CanonSharp.Pascal.SyntaxTree;
|
||||
|
||||
public sealed class WhileNode(SyntaxNodeBase condition, SyntaxNodeBase statement) : SyntaxNodeBase
|
||||
{
|
||||
public override SyntaxNodeType NodeType => SyntaxNodeType.While;
|
||||
|
||||
public SyntaxNodeBase Condition => condition;
|
||||
|
||||
public SyntaxNodeBase Statement => statement;
|
||||
}
|
|
@ -23,7 +23,7 @@
|
|||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\CanonSharp.Common\CanonSharp.Common.csproj" />
|
||||
<ProjectReference Include="..\CanonSharp.Pascal\CanonSharp.Pascal.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
|
@ -11,7 +11,7 @@ public class BasicParsersTests : ParserTestsBase
|
|||
[Fact]
|
||||
public void AlternativeTest()
|
||||
{
|
||||
Parser<char, char> parser = Token('a') | Token('b');
|
||||
IParser<char, char> parser = Token('a') | Token('b');
|
||||
|
||||
ValidateSuccessfulResult(parser, 'a', "abc");
|
||||
ValidateSuccessfulResult(parser, 'b', "bcd");
|
||||
|
@ -26,7 +26,7 @@ public class BasicParsersTests : ParserTestsBase
|
|||
[Fact]
|
||||
public void BindTest()
|
||||
{
|
||||
Parser<char, char> parser = Token('a').Bind(_ => Token('b')).Bind(_ => Token('c'));
|
||||
IParser<char, char> parser = Token('a').Bind(_ => Token('b')).Bind(_ => Token('c'));
|
||||
ValidateSuccessfulResult(parser, 'c', "abc");
|
||||
ValidateFailedResult(parser, "acd");
|
||||
|
||||
|
@ -36,7 +36,7 @@ public class BasicParsersTests : ParserTestsBase
|
|||
[Fact]
|
||||
public void MapTest()
|
||||
{
|
||||
Parser<char, string> parser = Token('a').Map(c => $"{c}");
|
||||
IParser<char, string> parser = Token('a').Map(c => $"{c}");
|
||||
ValidateSuccessfulResult(parser, "a", "abc");
|
||||
|
||||
parser = Token('a').Map("test");
|
||||
|
@ -46,7 +46,7 @@ public class BasicParsersTests : ParserTestsBase
|
|||
[Fact]
|
||||
public void NextTest()
|
||||
{
|
||||
Parser<char, char> parser = Token('a').Next(_ => Token('a'), _ => Token('b'));
|
||||
IParser<char, char> parser = Token('a').Next(_ => Token('a'), _ => Token('b'));
|
||||
ValidateSuccessfulResult(parser, 'a', "aaa");
|
||||
ValidateSuccessfulResult(parser, 'b', "bbb");
|
||||
|
||||
|
@ -61,7 +61,7 @@ public class BasicParsersTests : ParserTestsBase
|
|||
[Fact]
|
||||
public void NextTest2()
|
||||
{
|
||||
Parser<char, string> parser = Token('a').Next(_ => "123", _ => Pure<char, string>("456"));
|
||||
IParser<char, string> parser = Token('a').Next(_ => "123", _ => Pure<char, string>("456"));
|
||||
ValidateSuccessfulResult(parser, "123", "aaa");
|
||||
ValidateSuccessfulResult(parser, "456", "bbb");
|
||||
|
||||
|
@ -77,7 +77,7 @@ public class BasicParsersTests : ParserTestsBase
|
|||
[Fact]
|
||||
public void FixTest()
|
||||
{
|
||||
Parser<char, char> parser = Fix<char, Unit>(self => Token('a').Next(_ => self, Unit.Instance))
|
||||
IParser<char, char> parser = Fix<char, Unit>(self => Token('a').Next(_ => self, Unit.Instance))
|
||||
.Bind(_ => Token('b'));
|
||||
ValidateSuccessfulResult(parser, 'b', "aaaaab");
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ public class CombinatorParserTests : ParserTestsBase
|
|||
[Fact]
|
||||
public void ChoiceTest()
|
||||
{
|
||||
Parser<char, char> parser = Choice(Token('a'), Token('b'), Token('c'));
|
||||
IParser<char, char> parser = Choice(Token('a'), Token('b'), Token('c'));
|
||||
ValidateSuccessfulResult(parser, 'a', "abc");
|
||||
ValidateSuccessfulResult(parser, 'b', "bcd");
|
||||
ValidateSuccessfulResult(parser, 'c', "cde");
|
||||
|
@ -25,17 +25,18 @@ public class CombinatorParserTests : ParserTestsBase
|
|||
[Fact]
|
||||
public void SequenceTest()
|
||||
{
|
||||
Parser<char, IEnumerable<char>> parser = Sequence(Token('a'), Token('b'), Token('c'));
|
||||
IParser<char, IEnumerable<char>> parser = Sequence(Token('a'), Token('b'), Token('c'));
|
||||
ValidateSuccessfulResult(parser, ['a', 'b', 'c'], "abc");
|
||||
|
||||
parser = Sequence([Token('a'), Token('b'), Token('c')]);
|
||||
IEnumerable<IParser<char, char>> parsers = [Token('a'), Token('b'), Token('c')];
|
||||
parser = Sequence(parsers);
|
||||
ValidateSuccessfulResult(parser, ['a', 'b', 'c'], "abc");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void LeftRightTest()
|
||||
{
|
||||
Parser<char, char> parser = Token('a').Left(Token('b'));
|
||||
IParser<char, char> parser = Token('a').Left(Token('b'));
|
||||
ValidateSuccessfulResult(parser, 'a', "ab");
|
||||
|
||||
parser = Token('a').Right(Token('b'));
|
||||
|
@ -45,7 +46,7 @@ public class CombinatorParserTests : ParserTestsBase
|
|||
[Fact]
|
||||
public void ManyTest()
|
||||
{
|
||||
Parser<char, IEnumerable<char>> parser = Token('a').Many();
|
||||
IParser<char, IEnumerable<char>> parser = Token('a').Many();
|
||||
ValidateSuccessfulResult(parser, [], "bbb");
|
||||
ValidateSuccessfulResult(parser, ['a', 'a', 'a'], "aaa");
|
||||
|
||||
|
@ -57,7 +58,7 @@ public class CombinatorParserTests : ParserTestsBase
|
|||
[Fact]
|
||||
public void SkipManyTest()
|
||||
{
|
||||
Parser<char, char> parser = Token('a').SkipMany().Right(Token('b'));
|
||||
IParser<char, char> parser = Token('a').SkipMany().Right(Token('b'));
|
||||
ValidateSuccessfulResult(parser, 'b', "aaaab");
|
||||
ValidateSuccessfulResult(parser, 'b', "bbbb");
|
||||
|
||||
|
@ -71,7 +72,7 @@ public class CombinatorParserTests : ParserTestsBase
|
|||
{
|
||||
// 等效于Many1
|
||||
// 但是不返回中间结果
|
||||
Parser<char, char> parser = Token('a').Chain(Token);
|
||||
IParser<char, char> parser = Token('a').Chain(Token);
|
||||
ValidateSuccessfulResult(parser, 'a', "aa");
|
||||
ValidateFailedResult(parser, "bb");
|
||||
|
||||
|
@ -83,7 +84,7 @@ public class CombinatorParserTests : ParserTestsBase
|
|||
[Fact]
|
||||
public void ManyTillTest()
|
||||
{
|
||||
Parser<char, IEnumerable<char>> parser = Token('a').ManyTill(Token('b').LookAhead());
|
||||
IParser<char, IEnumerable<char>> parser = Token('a').ManyTill(Token('b').LookAhead());
|
||||
ValidateSuccessfulResult(parser, ['a', 'a', 'a'], "aaab");
|
||||
ValidateSuccessfulResult(parser, [], "b");
|
||||
|
||||
|
@ -95,19 +96,33 @@ public class CombinatorParserTests : ParserTestsBase
|
|||
[Fact]
|
||||
public void SkipTillTest()
|
||||
{
|
||||
Parser<char, char> parser = Token('a').SkipTill(Token('b'));
|
||||
IParser<char, char> parser = Token('a').SkipTill(Token('b'));
|
||||
ValidateSuccessfulResult(parser, 'b', "aaab");
|
||||
ValidateSuccessfulResult(parser, 'b', "b");
|
||||
|
||||
parser = Token('a').Skip1Till(Token('b'));
|
||||
ValidateSuccessfulResult(parser, 'b', "aaab");
|
||||
ValidateFailedResult(parser, "b");
|
||||
|
||||
parser = Token('a').SkipTill(Choice(
|
||||
Token('b'), Token('c')));
|
||||
|
||||
ValidateSuccessfulResult(parser, 'b', "aaab");
|
||||
ValidateSuccessfulResult(parser, 'c', "ac");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SkipTillManyTest()
|
||||
{
|
||||
IParser<char, IEnumerable<char>> parser = Token('a').SkipTill(Token('b')).Many();
|
||||
|
||||
ValidateSuccessfulResult(parser, ['b', 'b'], "aaabaab");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TakeTillTest()
|
||||
{
|
||||
Parser<char, IEnumerable<char>> parser = TakeTill(Token('b').LookAhead());
|
||||
IParser<char, IEnumerable<char>> parser = TakeTill(Token('b').LookAhead());
|
||||
ValidateSuccessfulResult(parser, ['a', 'a'], "aab");
|
||||
ValidateSuccessfulResult(parser, [], "b");
|
||||
|
||||
|
@ -119,7 +134,7 @@ public class CombinatorParserTests : ParserTestsBase
|
|||
[Fact]
|
||||
public void MatchTest()
|
||||
{
|
||||
Parser<char, char> parser = Token('b').Match();
|
||||
IParser<char, char> parser = Token('b').Match();
|
||||
ValidateSuccessfulResult(parser, 'b', "asdfasdfasdfasdfb");
|
||||
ValidateSuccessfulResult(parser, 'b', "b");
|
||||
}
|
||||
|
@ -127,7 +142,7 @@ public class CombinatorParserTests : ParserTestsBase
|
|||
[Fact]
|
||||
public void QuoteTest()
|
||||
{
|
||||
Parser<char, IEnumerable<char>> parser = Any<char>().Quote(Token('['), Token(']'));
|
||||
IParser<char, IEnumerable<char>> parser = Any<char>().Quote(Token('['), Token(']'));
|
||||
ValidateSuccessfulResult(parser, ['1', '2', '3'], "[123]");
|
||||
|
||||
parser = Any<char>().Quote(Token('\''));
|
||||
|
@ -137,7 +152,7 @@ public class CombinatorParserTests : ParserTestsBase
|
|||
[Fact]
|
||||
public void SeparatedByTest()
|
||||
{
|
||||
Parser<char, IEnumerable<char>> parser = Token('a').SeparatedBy(Token(','));
|
||||
IParser<char, IEnumerable<char>> parser = Token('a').SeparatedBy(Token(','));
|
||||
ValidateSuccessfulResult(parser, ['a', 'a', 'a'], "a,a,a");
|
||||
ValidateSuccessfulResult(parser, ['a'], "a");
|
||||
ValidateSuccessfulResult(parser, [], "");
|
||||
|
@ -151,7 +166,7 @@ public class CombinatorParserTests : ParserTestsBase
|
|||
[Fact]
|
||||
public void EndByTest()
|
||||
{
|
||||
Parser<char, IEnumerable<char>> parser = Satisfy<char>(char.IsLetter).EndBy(Token('.'));
|
||||
IParser<char, IEnumerable<char>> parser = Satisfy<char>(char.IsLetter).EndBy(Token('.'));
|
||||
ValidateSuccessfulResult(parser, ['a', 'b', 'c'], "abc.");
|
||||
ValidateSuccessfulResult(parser, [], ".");
|
||||
|
||||
|
@ -163,7 +178,7 @@ public class CombinatorParserTests : ParserTestsBase
|
|||
[Fact]
|
||||
public void SeparatedOrEndByTest()
|
||||
{
|
||||
Parser<char, IEnumerable<char>> parser = Satisfy<char>(char.IsLetter).SeparatedOrEndBy1(Token(','));
|
||||
IParser<char, IEnumerable<char>> parser = Satisfy<char>(char.IsLetter).SeparatedOrEndBy1(Token(','));
|
||||
ValidateSuccessfulResult(parser, ['a', 'b', 'c'], "a,b,c,");
|
||||
ValidateSuccessfulResult(parser, ['a', 'b', 'c'], "a,b,c");
|
||||
ValidateFailedResult(parser, "");
|
||||
|
|
|
@ -10,7 +10,7 @@ public class LinqTests : ParserTestsBase
|
|||
[Fact]
|
||||
public void SelectTest1()
|
||||
{
|
||||
Parser<char, string> parser = from token in Char('a')
|
||||
IParser<char, string> parser = from token in Char('a')
|
||||
select token.ToString();
|
||||
ValidateSuccessfulResult(parser, "a", "a");
|
||||
ValidateFailedResult(parser, "b");
|
||||
|
@ -19,7 +19,7 @@ public class LinqTests : ParserTestsBase
|
|||
[Fact]
|
||||
public void SelectManyTest1()
|
||||
{
|
||||
Parser<char, int> parser = from _1 in Char('a')
|
||||
IParser<char, int> parser = from _1 in Char('a')
|
||||
from _2 in Char('b')
|
||||
from _3 in Char('c')
|
||||
select 123;
|
||||
|
|
|
@ -12,7 +12,7 @@ public class ModifierParserTests : ParserTestsBase
|
|||
[Fact]
|
||||
public void DoTest()
|
||||
{
|
||||
Parser<char, char> parser = Token('a').Do(x => Assert.Equal('a', x)).Do(x => Assert.Equal('a', x),
|
||||
IParser<char, char> parser = Token('a').Do(x => Assert.Equal('a', x)).Do(x => Assert.Equal('a', x),
|
||||
failedResult => Assert.ThrowsAny<ParseException>(() => failedResult.Value));
|
||||
|
||||
ValidateSuccessfulResult(parser, 'a', "abc");
|
||||
|
@ -22,7 +22,7 @@ public class ModifierParserTests : ParserTestsBase
|
|||
[Fact]
|
||||
public void LookAheadTest()
|
||||
{
|
||||
Parser<char, char> parser = Token('a').LookAhead().Next(_ => Token('a'), _ => Token('b'));
|
||||
IParser<char, char> parser = Token('a').LookAhead().Next(_ => Token('a'), _ => Token('b'));
|
||||
ValidateSuccessfulResult(parser, 'a', "abc");
|
||||
ValidateSuccessfulResult(parser, 'b', "bcd");
|
||||
}
|
||||
|
@ -30,7 +30,7 @@ public class ModifierParserTests : ParserTestsBase
|
|||
[Fact]
|
||||
public void NotTest()
|
||||
{
|
||||
Parser<char, char> parser = Token('a').Not('b');
|
||||
IParser<char, char> parser = Token('a').Not('b');
|
||||
ValidateSuccessfulResult(parser, 'b', "bcd");
|
||||
|
||||
parser = Token('a').Not().Bind(_ => Token('b'));
|
||||
|
@ -40,7 +40,7 @@ public class ModifierParserTests : ParserTestsBase
|
|||
[Fact]
|
||||
public void TryTest()
|
||||
{
|
||||
Parser<char, string> parser = String("abc").Try("cde");
|
||||
IParser<char, string> parser = String("abc").Try("cde");
|
||||
ValidateSuccessfulResult(parser, "abc", "abc");
|
||||
ValidateSuccessfulResult(parser, "cde", "cde");
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ public class PrimitiveParserTests : ParserTestsBase
|
|||
[Fact]
|
||||
public void PureTest()
|
||||
{
|
||||
Parser<char, char> parser = Pure<char, char>('a');
|
||||
IParser<char, char> parser = Pure<char, char>('a');
|
||||
ValidateSuccessfulResult(parser, 'a', "abc");
|
||||
|
||||
parser = Pure<char, char>(_ => 'a');
|
||||
|
@ -21,18 +21,18 @@ public class PrimitiveParserTests : ParserTestsBase
|
|||
[Fact]
|
||||
public void NullTest()
|
||||
{
|
||||
Parser<char, Unit> parser = Null<char>();
|
||||
IParser<char, Unit> parser = Null<char>();
|
||||
ValidateSuccessfulResult(parser, Unit.Instance, "abc");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FailTest()
|
||||
{
|
||||
Parser<char, char> parser = Fail<char, char>();
|
||||
IParser<char, char> parser = Fail<char, char>();
|
||||
ValidateFailedResult(parser, "abc");
|
||||
|
||||
parser = Fail<char, char>("Failed message");
|
||||
FailedResult<char, char> result = ValidateFailedResult(parser, "abc");
|
||||
IFailedResult<char, char> result = ValidateFailedResult(parser, "abc");
|
||||
Assert.Equal("Failed message", result.Message);
|
||||
|
||||
parser = Fail<char, char>(x => $"{x}");
|
||||
|
@ -47,29 +47,36 @@ public class PrimitiveParserTests : ParserTestsBase
|
|||
[Fact]
|
||||
public void SatisfyTest()
|
||||
{
|
||||
Parser<char, char> parser = Satisfy<char>(char.IsLetter);
|
||||
IParser<char, char> parser = Satisfy<char>(char.IsLetter);
|
||||
ValidateSuccessfulResult(parser, 'a', "abc");
|
||||
ValidateFailedResult(parser, "123");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SatisfyFailedTest()
|
||||
{
|
||||
IParser<char, char> parser = Satisfy<char>(char.IsLetter);
|
||||
ValidateFailedResult(parser, "");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AnyTest()
|
||||
{
|
||||
Parser<char, char> parser = Any<char>();
|
||||
IParser<char, char> parser = Any<char>();
|
||||
ValidateSuccessfulResult(parser, '1', "123");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TokenTest()
|
||||
{
|
||||
Parser<char, char> parser = Token('a');
|
||||
IParser<char, char> parser = Token('a');
|
||||
ValidateSuccessfulResult(parser, 'a', "abc");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TakeTest()
|
||||
{
|
||||
Parser<char, IEnumerable<char>> parser = Take<char>(5);
|
||||
IParser<char, IEnumerable<char>> parser = Take<char>(5);
|
||||
ValidateSuccessfulResult(parser, ['h', 'e', 'l', 'l', 'o'], "hello");
|
||||
ValidateFailedResult(parser, "abc");
|
||||
}
|
||||
|
@ -77,7 +84,7 @@ public class PrimitiveParserTests : ParserTestsBase
|
|||
[Fact]
|
||||
public void SkipTest()
|
||||
{
|
||||
Parser<char, char> parser = Skip<char>(5).Bind(_ => Token(','));
|
||||
IParser<char, char> parser = Skip<char>(5).Bind(_ => Token(','));
|
||||
ValidateSuccessfulResult(parser, ',', "hello,world.");
|
||||
ValidateFailedResult(parser, "abc");
|
||||
}
|
||||
|
|
62
CanonSharp.Tests/ParserTests/ConstDeclarationTests.cs
Normal file
62
CanonSharp.Tests/ParserTests/ConstDeclarationTests.cs
Normal file
|
@ -0,0 +1,62 @@
|
|||
using CanonSharp.Pascal.Parser;
|
||||
using CanonSharp.Pascal.SyntaxTree;
|
||||
using CanonSharp.Tests.Utils;
|
||||
|
||||
namespace CanonSharp.Tests.ParserTests;
|
||||
|
||||
public class ConstDeclarationTests : GrammarParserTestBase
|
||||
{
|
||||
[Fact]
|
||||
public void BooleanConstValueTest()
|
||||
{
|
||||
BooleanValueNode node = RunParser<BooleanValueNode>(GrammarParser.ConstValueParser(), "true");
|
||||
Assert.True(node.Value);
|
||||
|
||||
node = RunParser<BooleanValueNode>(GrammarParser.ConstValueParser(), "false");
|
||||
Assert.False(node.Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CharConstValueTest()
|
||||
{
|
||||
CharValueNode node = RunParser<CharValueNode>(GrammarParser.ConstValueParser(), "'a'");
|
||||
Assert.Equal('a', node.Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NumberConstValueTest()
|
||||
{
|
||||
IntegerValueNode integerValueNode = RunParser<IntegerValueNode>(GrammarParser.ConstValueParser(), "250");
|
||||
Assert.Equal(250, integerValueNode.Value);
|
||||
|
||||
FloatValueNode floatValueNode = RunParser<FloatValueNode>(GrammarParser.ConstValueParser(), "100.5");
|
||||
Assert.Equal(100.5, floatValueNode.Value);
|
||||
|
||||
UnaryOperatorNode node = RunParser<UnaryOperatorNode>(GrammarParser.ConstValueParser(), "+250");
|
||||
Assert.Equal(UnaryOperatorType.Plus, node.OperatorType);
|
||||
node = RunParser<UnaryOperatorNode>(GrammarParser.ConstValueParser(), "-100.5");
|
||||
Assert.Equal(UnaryOperatorType.Minus, node.OperatorType);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ConstDeclarationTest()
|
||||
{
|
||||
ConstantNode node = RunParser<ConstantNode>(GrammarParser.ConstDeclarationParser(), "a = true");
|
||||
|
||||
Assert.Equal("a", node.Identifier.LiteralValue);
|
||||
Assert.True(node.Value.Convert<BooleanValueNode>().Value);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("", 0)]
|
||||
[InlineData("const INFINITE = 100;", 1)]
|
||||
[InlineData("const a = true; b = false", 2)]
|
||||
[InlineData("const code1 = 100.4;code2 = 'a'", 2)]
|
||||
public void ConstDeclarationsCountTest(string input, int count)
|
||||
{
|
||||
BlockNode blockNode = RunParser<BlockNode>(GrammarParser.ConstDeclarationsParser(), input);
|
||||
List<ConstantNode> constantNodes = blockNode.Statements.Select(node => node.Convert<ConstantNode>()).ToList();
|
||||
|
||||
Assert.Equal(count, constantNodes.Count);
|
||||
}
|
||||
}
|
297
CanonSharp.Tests/ParserTests/ExpressionParserTests.cs
Normal file
297
CanonSharp.Tests/ParserTests/ExpressionParserTests.cs
Normal file
|
@ -0,0 +1,297 @@
|
|||
using CanonSharp.Pascal.Parser;
|
||||
using CanonSharp.Pascal.SyntaxTree;
|
||||
using CanonSharp.Tests.Utils;
|
||||
|
||||
namespace CanonSharp.Tests.ParserTests;
|
||||
|
||||
public sealed class ExpressionParserTests : GrammarParserTestBase
|
||||
{
|
||||
[Fact]
|
||||
public void BoolTest()
|
||||
{
|
||||
BooleanValueNode node = RunParser<BooleanValueNode>(GrammarParser.FactorParser(), "false");
|
||||
Assert.False(node.Value);
|
||||
|
||||
node = RunParser<BooleanValueNode>(GrammarParser.FactorParser(), "true");
|
||||
Assert.True(node.Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NumberTest()
|
||||
{
|
||||
IntegerValueNode integerValueNode = RunParser<IntegerValueNode>(GrammarParser.FactorParser(), "123456");
|
||||
Assert.Equal(123456, integerValueNode.Value);
|
||||
|
||||
FloatValueNode floatValueNode = RunParser<FloatValueNode>(GrammarParser.FactorParser(), "123.456");
|
||||
Assert.Equal(123.456, floatValueNode.Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void UnaryOperatorTest()
|
||||
{
|
||||
UnaryOperatorNode node = RunParser<UnaryOperatorNode>(GrammarParser.FactorParser(), "- 123");
|
||||
Assert.Equal(UnaryOperatorType.Minus, node.OperatorType);
|
||||
Assert.Equal(123, node.Node.Convert<IntegerValueNode>().Value);
|
||||
|
||||
node = RunParser<UnaryOperatorNode>(GrammarParser.FactorParser(), "+ 100.5");
|
||||
Assert.Equal(UnaryOperatorType.Plus, node.OperatorType);
|
||||
Assert.Equal(100.5, node.Node.Convert<FloatValueNode>().Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IdentifierTest()
|
||||
{
|
||||
VariableNode node = RunParser<VariableNode>(GrammarParser.VariableParser(), "temp");
|
||||
Assert.Equal("temp", node.Identifier.LiteralValue);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ArrayIdentifierTest()
|
||||
{
|
||||
VariableNode node = RunParser<VariableNode>(GrammarParser.VariableParser(), "a[0]");
|
||||
Assert.Equal("a", node.Identifier.LiteralValue);
|
||||
Assert.Single(node.Indexers);
|
||||
Assert.Equal(0, node.Indexers.First().Convert<IntegerValueNode>().Value);
|
||||
|
||||
node = RunParser<VariableNode>(GrammarParser.VariableParser(), "a[i + 1]");
|
||||
Assert.Equal("a", node.Identifier.LiteralValue);
|
||||
Assert.Single(node.Indexers);
|
||||
BinaryOperatorNode binaryOperatorNode = node.Indexers.First().Convert<BinaryOperatorNode>();
|
||||
Assert.Equal(BinaryOperatorType.Add, binaryOperatorNode.OperatorType);
|
||||
Assert.Equal("i", binaryOperatorNode.Left.Convert<VariableNode>().Identifier.LiteralValue);
|
||||
Assert.Equal(1, binaryOperatorNode.Right.Convert<IntegerValueNode>().Value);
|
||||
|
||||
node = RunParser<VariableNode>(GrammarParser.VariableParser(), "a[b[i + 1] - 10, c]");
|
||||
Assert.Equal("a", node.Identifier.LiteralValue);
|
||||
Assert.Equal(2, node.Indexers.Count);
|
||||
|
||||
VariableNode secondNode = node.Indexers[1].Convert<VariableNode>();
|
||||
Assert.Equal("c", secondNode.Identifier.LiteralValue);
|
||||
Assert.Empty(secondNode.Indexers);
|
||||
|
||||
BinaryOperatorNode firstNode = node.Indexers[0].Convert<BinaryOperatorNode>();
|
||||
Assert.Equal(BinaryOperatorType.Subtract, firstNode.OperatorType);
|
||||
Assert.Equal(10, firstNode.Right.Convert<IntegerValueNode>().Value);
|
||||
|
||||
VariableNode variableNode = firstNode.Left.Convert<VariableNode>();
|
||||
Assert.Equal("b", variableNode.Identifier.LiteralValue);
|
||||
Assert.Single(variableNode.Indexers);
|
||||
binaryOperatorNode = variableNode.Indexers[0].Convert<BinaryOperatorNode>();
|
||||
Assert.Equal(BinaryOperatorType.Add, binaryOperatorNode.OperatorType);
|
||||
Assert.Equal("i", binaryOperatorNode.Left.Convert<VariableNode>().Identifier.LiteralValue);
|
||||
Assert.Equal(1, binaryOperatorNode.Right.Convert<IntegerValueNode>().Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ProcedureCallTest()
|
||||
{
|
||||
ProcedureCallNode node = RunParser<ProcedureCallNode>(GrammarParser.FactorParser(), "test(1)");
|
||||
Assert.Equal("test", node.Identifier.LiteralValue);
|
||||
Assert.Single(node.Parameters);
|
||||
Assert.Equal(1, node.Parameters[0].Convert<IntegerValueNode>().Value);
|
||||
|
||||
node = RunParser<ProcedureCallNode>(GrammarParser.FactorParser(), "test()");
|
||||
Assert.Equal("test", node.Identifier.LiteralValue);
|
||||
Assert.Empty(node.Parameters);
|
||||
|
||||
VariableNode variableNode = RunParser<VariableNode>(GrammarParser.FactorParser(), "test");
|
||||
Assert.Equal("test", variableNode.Identifier.LiteralValue);
|
||||
Assert.Empty(variableNode.Indexers);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SingleTermTest()
|
||||
{
|
||||
VariableNode node = RunParser<VariableNode>(GrammarParser.TermParser(), "temp");
|
||||
Assert.Equal("temp", node.Identifier.LiteralValue);
|
||||
|
||||
UnaryOperatorNode unaryOperatorNode = RunParser<UnaryOperatorNode>(GrammarParser.TermParser(), "- 123");
|
||||
Assert.Equal(UnaryOperatorType.Minus, unaryOperatorNode.OperatorType);
|
||||
Assert.Equal(123, unaryOperatorNode.Node.Convert<IntegerValueNode>().Value);
|
||||
|
||||
unaryOperatorNode = RunParser<UnaryOperatorNode>(GrammarParser.TermParser(), "+ 100.5");
|
||||
Assert.Equal(UnaryOperatorType.Plus, unaryOperatorNode.OperatorType);
|
||||
Assert.Equal(100.5, unaryOperatorNode.Node.Convert<FloatValueNode>().Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MultiplyTermTest1()
|
||||
{
|
||||
BinaryOperatorNode node = RunParser<BinaryOperatorNode>(GrammarParser.TermParser(), "10 / 2");
|
||||
|
||||
Assert.Equal(BinaryOperatorType.Divide, node.OperatorType);
|
||||
Assert.Equal(10, node.Left.Convert<IntegerValueNode>().Value);
|
||||
Assert.Equal(2, node.Right.Convert<IntegerValueNode>().Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MultiplyTermTest2()
|
||||
{
|
||||
BinaryOperatorNode node = RunParser<BinaryOperatorNode>(GrammarParser.TermParser(), "10 div 2");
|
||||
|
||||
Assert.Equal(BinaryOperatorType.IntegerDivide, node.OperatorType);
|
||||
Assert.Equal(10, node.Left.Convert<IntegerValueNode>().Value);
|
||||
Assert.Equal(2, node.Right.Convert<IntegerValueNode>().Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MultiplyTermTest3()
|
||||
{
|
||||
BinaryOperatorNode node = RunParser<BinaryOperatorNode>(GrammarParser.TermParser(), "temp * 2 div 3");
|
||||
|
||||
Assert.Equal(BinaryOperatorType.IntegerDivide, node.OperatorType);
|
||||
Assert.Equal(3, node.Right.Convert<IntegerValueNode>().Value);
|
||||
BinaryOperatorNode leftNode = node.Left.Convert<BinaryOperatorNode>();
|
||||
Assert.Equal(BinaryOperatorType.Multiply, leftNode.OperatorType);
|
||||
Assert.Equal("temp", leftNode.Left.Convert<VariableNode>().Identifier.LiteralValue);
|
||||
Assert.Equal(2, leftNode.Right.Convert<IntegerValueNode>().Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SimpleExpressionTest1()
|
||||
{
|
||||
VariableNode node = RunParser<VariableNode>(GrammarParser.SimpleExpressionParser(), "temp");
|
||||
Assert.Equal("temp", node.Identifier.LiteralValue);
|
||||
|
||||
UnaryOperatorNode unaryOperatorNode =
|
||||
RunParser<UnaryOperatorNode>(GrammarParser.SimpleExpressionParser(), "- 123");
|
||||
Assert.Equal(UnaryOperatorType.Minus, unaryOperatorNode.OperatorType);
|
||||
Assert.Equal(123, unaryOperatorNode.Node.Convert<IntegerValueNode>().Value);
|
||||
|
||||
unaryOperatorNode = RunParser<UnaryOperatorNode>(GrammarParser.SimpleExpressionParser(), "+ 100.5");
|
||||
Assert.Equal(UnaryOperatorType.Plus, unaryOperatorNode.OperatorType);
|
||||
Assert.Equal(100.5, unaryOperatorNode.Node.Convert<FloatValueNode>().Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SimpleExpressionTest2()
|
||||
{
|
||||
BinaryOperatorNode node = RunParser<BinaryOperatorNode>(GrammarParser.SimpleExpressionParser(), "1 + 1");
|
||||
|
||||
Assert.Equal(BinaryOperatorType.Add, node.OperatorType);
|
||||
Assert.Equal(1, node.Left.Convert<IntegerValueNode>().Value);
|
||||
Assert.Equal(1, node.Right.Convert<IntegerValueNode>().Value);
|
||||
|
||||
node = RunParser<BinaryOperatorNode>(GrammarParser.SimpleExpressionParser(), "1 + 1 - 2");
|
||||
|
||||
Assert.Equal(BinaryOperatorType.Subtract, node.OperatorType);
|
||||
Assert.Equal(2, node.Right.Convert<IntegerValueNode>().Value);
|
||||
|
||||
BinaryOperatorNode leftNode = node.Left.Convert<BinaryOperatorNode>();
|
||||
Assert.Equal(BinaryOperatorType.Add, leftNode.OperatorType);
|
||||
Assert.Equal(1, leftNode.Left.Convert<IntegerValueNode>().Value);
|
||||
Assert.Equal(1, leftNode.Right.Convert<IntegerValueNode>().Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SimpleExpressionTest3()
|
||||
{
|
||||
BinaryOperatorNode node = RunParser<BinaryOperatorNode>(GrammarParser.SimpleExpressionParser(), "1 - 2 * 5");
|
||||
|
||||
Assert.Equal(BinaryOperatorType.Subtract, node.OperatorType);
|
||||
Assert.Equal(1, node.Left.Convert<IntegerValueNode>().Value);
|
||||
|
||||
BinaryOperatorNode rightNode = node.Right.Convert<BinaryOperatorNode>();
|
||||
Assert.Equal(BinaryOperatorType.Multiply, rightNode.OperatorType);
|
||||
Assert.Equal(2, rightNode.Left.Convert<IntegerValueNode>().Value);
|
||||
Assert.Equal(5, rightNode.Right.Convert<IntegerValueNode>().Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SimpleExpressionTest4()
|
||||
{
|
||||
BinaryOperatorNode node = RunParser<BinaryOperatorNode>(GrammarParser.SimpleExpressionParser(), "1 + +1");
|
||||
|
||||
Assert.Equal(BinaryOperatorType.Add, node.OperatorType);
|
||||
Assert.Equal(1, node.Left.Convert<IntegerValueNode>().Value);
|
||||
UnaryOperatorNode unaryOperatorNode = node.Right.Convert<UnaryOperatorNode>();
|
||||
Assert.Equal(UnaryOperatorType.Plus, unaryOperatorNode.OperatorType);
|
||||
Assert.Equal(1, unaryOperatorNode.Node.Convert<IntegerValueNode>().Value);
|
||||
|
||||
node = RunParser<BinaryOperatorNode>(GrammarParser.SimpleExpressionParser(), "1 - -1");
|
||||
|
||||
Assert.Equal(BinaryOperatorType.Subtract, node.OperatorType);
|
||||
Assert.Equal(1, node.Left.Convert<IntegerValueNode>().Value);
|
||||
unaryOperatorNode = node.Right.Convert<UnaryOperatorNode>();
|
||||
Assert.Equal(UnaryOperatorType.Minus, unaryOperatorNode.OperatorType);
|
||||
Assert.Equal(1, unaryOperatorNode.Node.Convert<IntegerValueNode>().Value);
|
||||
|
||||
node = RunParser<BinaryOperatorNode>(GrammarParser.SimpleExpressionParser(), "+ 1 - - 1");
|
||||
|
||||
Assert.Equal(BinaryOperatorType.Subtract, node.OperatorType);
|
||||
unaryOperatorNode = node.Left.Convert<UnaryOperatorNode>();
|
||||
Assert.Equal(UnaryOperatorType.Plus, unaryOperatorNode.OperatorType);
|
||||
Assert.Equal(1, unaryOperatorNode.Node.Convert<IntegerValueNode>().Value);
|
||||
unaryOperatorNode = node.Right.Convert<UnaryOperatorNode>();
|
||||
Assert.Equal(UnaryOperatorType.Minus, unaryOperatorNode.OperatorType);
|
||||
Assert.Equal(1, unaryOperatorNode.Node.Convert<IntegerValueNode>().Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SimpleExpressionTest5()
|
||||
{
|
||||
BinaryOperatorNode node = RunParser<BinaryOperatorNode>(GrammarParser.SimpleExpressionParser(),
|
||||
"true and temp or temp and false");
|
||||
|
||||
Assert.Equal(BinaryOperatorType.Or, node.OperatorType);
|
||||
|
||||
BinaryOperatorNode left = node.Left.Convert<BinaryOperatorNode>();
|
||||
Assert.Equal(BinaryOperatorType.And, left.OperatorType);
|
||||
BinaryOperatorNode right = node.Right.Convert<BinaryOperatorNode>();
|
||||
Assert.Equal(BinaryOperatorType.And, right.OperatorType);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ExpressionTest1()
|
||||
{
|
||||
BinaryOperatorNode node = RunParser<BinaryOperatorNode>(GrammarParser.ExpressionParser(),
|
||||
"true and temp or temp and false");
|
||||
|
||||
Assert.Equal(BinaryOperatorType.Or, node.OperatorType);
|
||||
|
||||
BinaryOperatorNode left = node.Left.Convert<BinaryOperatorNode>();
|
||||
Assert.Equal(BinaryOperatorType.And, left.OperatorType);
|
||||
BinaryOperatorNode right = node.Right.Convert<BinaryOperatorNode>();
|
||||
Assert.Equal(BinaryOperatorType.And, right.OperatorType);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ExpressionTest2()
|
||||
{
|
||||
BinaryOperatorNode node = RunParser<BinaryOperatorNode>(GrammarParser.ExpressionParser(), "2 >= 1");
|
||||
Assert.Equal(BinaryOperatorType.GreaterEqual, node.OperatorType);
|
||||
Assert.Equal(2, node.Left.Convert<IntegerValueNode>().Value);
|
||||
Assert.Equal(1, node.Right.Convert<IntegerValueNode>().Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ExpressionTest3()
|
||||
{
|
||||
BinaryOperatorNode node = RunParser<BinaryOperatorNode>(GrammarParser.ExpressionParser(), "(1 + 1) * 2");
|
||||
|
||||
Assert.Equal(BinaryOperatorType.Multiply, node.OperatorType);
|
||||
Assert.Equal(2, node.Right.Convert<IntegerValueNode>().Value);
|
||||
|
||||
node = node.Left.Convert<BinaryOperatorNode>();
|
||||
Assert.Equal(BinaryOperatorType.Add, node.OperatorType);
|
||||
Assert.Equal(1, node.Left.Convert<IntegerValueNode>().Value);
|
||||
Assert.Equal(1, node.Right.Convert<IntegerValueNode>().Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ExpressionsTest1()
|
||||
{
|
||||
List<BinaryOperatorNode> nodes =
|
||||
RunParser<BinaryOperatorNode>(GrammarParser.ExpressionsParser(), "1 + 1, 2 * 3");
|
||||
|
||||
Assert.Equal(BinaryOperatorType.Add, nodes[0].OperatorType);
|
||||
Assert.Equal(BinaryOperatorType.Multiply, nodes[1].OperatorType);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void VariableTest1()
|
||||
{
|
||||
VariableNode node = RunParser<VariableNode>(GrammarParser.VariableParser(), "temp");
|
||||
Assert.Equal("temp", node.Identifier.LiteralValue);
|
||||
}
|
||||
}
|
229
CanonSharp.Tests/ParserTests/ProgramParserTests.cs
Normal file
229
CanonSharp.Tests/ParserTests/ProgramParserTests.cs
Normal file
|
@ -0,0 +1,229 @@
|
|||
using CanonSharp.Pascal.SyntaxTree;
|
||||
using CanonSharp.Tests.Utils;
|
||||
|
||||
namespace CanonSharp.Tests.ParserTests;
|
||||
|
||||
public class ProgramParserTests : GrammarParserTestBase
|
||||
{
|
||||
[Fact]
|
||||
public void ProgramParseTest1()
|
||||
{
|
||||
Program program = ProgramParse("""
|
||||
program main;
|
||||
begin
|
||||
end.
|
||||
""");
|
||||
|
||||
Assert.Equal("main", program.Head.ProgramName.LiteralValue);
|
||||
Assert.Empty(program.Body.MainBlock.Statements);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ProgramParseTest2()
|
||||
{
|
||||
Program program = ProgramParse("""
|
||||
program main;
|
||||
begin
|
||||
temp := 1 + 1;
|
||||
end.
|
||||
""");
|
||||
|
||||
Assert.Equal("main", program.Head.ProgramName.LiteralValue);
|
||||
|
||||
AssignNode assignNode = program.Body.MainBlock.Statements[0].Convert<AssignNode>();
|
||||
Assert.Equal("temp", assignNode.Variable.Identifier.LiteralValue);
|
||||
|
||||
BinaryOperatorNode binaryOperatorNode = assignNode.Expression.Convert<BinaryOperatorNode>();
|
||||
Assert.Equal(BinaryOperatorType.Add, binaryOperatorNode.OperatorType);
|
||||
Assert.Equal(1, binaryOperatorNode.Left.Convert<IntegerValueNode>().Value);
|
||||
Assert.Equal(1, binaryOperatorNode.Right.Convert<IntegerValueNode>().Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ProgramParseTest3()
|
||||
{
|
||||
Program program = ProgramParse("""
|
||||
program main;
|
||||
const a = 1; b = true;
|
||||
begin
|
||||
temp := 1 + 1;
|
||||
end.
|
||||
""");
|
||||
|
||||
Assert.Equal("main", program.Head.ProgramName.LiteralValue);
|
||||
|
||||
List<ConstantNode> constantNodes = program.Body.ConstantDeclarations.Statements
|
||||
.Select(x => x.Convert<ConstantNode>()).ToList();
|
||||
Assert.Equal(2, constantNodes.Count);
|
||||
Assert.Equal("a", constantNodes[0].Identifier.LiteralValue);
|
||||
Assert.Equal("b", constantNodes[1].Identifier.LiteralValue);
|
||||
|
||||
AssignNode assignNode = program.Body.MainBlock.Statements[0].Convert<AssignNode>();
|
||||
Assert.Equal("temp", assignNode.Variable.Identifier.LiteralValue);
|
||||
|
||||
BinaryOperatorNode binaryOperatorNode = assignNode.Expression.Convert<BinaryOperatorNode>();
|
||||
Assert.Equal(BinaryOperatorType.Add, binaryOperatorNode.OperatorType);
|
||||
Assert.Equal(1, binaryOperatorNode.Left.Convert<IntegerValueNode>().Value);
|
||||
Assert.Equal(1, binaryOperatorNode.Right.Convert<IntegerValueNode>().Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ProgramParseTest4()
|
||||
{
|
||||
Program program = ProgramParse("""
|
||||
program main;
|
||||
var a : integer;
|
||||
begin
|
||||
a := 1 + 1;
|
||||
end.
|
||||
""");
|
||||
|
||||
IList<VariableDeclarationNode> variableDeclarationNodes = program.Body.VariableDeclarations.Statements
|
||||
.Select(x => x.Convert<VariableDeclarationNode>()).ToList();
|
||||
Assert.Single(variableDeclarationNodes);
|
||||
Assert.Single(variableDeclarationNodes.First().Identifiers);
|
||||
Assert.Equal("a", variableDeclarationNodes.First().Identifiers.First().LiteralValue);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("""
|
||||
program DoNothing;
|
||||
begin
|
||||
end.
|
||||
""")]
|
||||
[InlineData("""
|
||||
program Add;
|
||||
var a : Integer;
|
||||
begin
|
||||
a := 1 + 1
|
||||
end.
|
||||
""")]
|
||||
[InlineData("""
|
||||
program varTest;
|
||||
var a : integer;
|
||||
begin
|
||||
a := 9 div 1;
|
||||
end.
|
||||
""")]
|
||||
[InlineData("""
|
||||
program main;
|
||||
var a : integer;
|
||||
begin
|
||||
a := 1 + +1;
|
||||
a := 1 - -1;
|
||||
a := 1 + -1;
|
||||
a := 1 - +1;
|
||||
end.
|
||||
""")]
|
||||
[InlineData("""
|
||||
program main;
|
||||
const a = true; b = false;
|
||||
var c, d : boolean;
|
||||
begin
|
||||
c := true;
|
||||
d := false;
|
||||
end.
|
||||
""")]
|
||||
[InlineData("""
|
||||
program arrayTest;
|
||||
var a : array [0..10] of integer;
|
||||
begin
|
||||
a[0] := 1;
|
||||
end.
|
||||
""")]
|
||||
[InlineData("""
|
||||
program arrayTest;
|
||||
var a : array [10..100, 0..10] of integer;
|
||||
begin
|
||||
a[10,0] := 1;
|
||||
end.
|
||||
""")]
|
||||
[InlineData("""
|
||||
program main;
|
||||
begin
|
||||
begin
|
||||
end
|
||||
end.
|
||||
""")]
|
||||
[InlineData("""
|
||||
program main;
|
||||
var i : integer;
|
||||
begin
|
||||
while i < 10 do
|
||||
i := i + 1;
|
||||
end.
|
||||
""")]
|
||||
[InlineData("""
|
||||
program main;
|
||||
procedure test;
|
||||
begin
|
||||
end;
|
||||
begin
|
||||
end.
|
||||
""")]
|
||||
[InlineData("""
|
||||
program main;
|
||||
function test(a, b : integer) : integer;
|
||||
begin
|
||||
test := 1;
|
||||
end;
|
||||
begin
|
||||
end.
|
||||
""")]
|
||||
[InlineData("""
|
||||
program main;
|
||||
var i : integer;
|
||||
begin
|
||||
for i := 1 to 100 do
|
||||
begin
|
||||
doSomething(i);
|
||||
end;
|
||||
end.
|
||||
""")]
|
||||
[InlineData("""
|
||||
program main;
|
||||
begin
|
||||
if 1 = 2 then
|
||||
begin
|
||||
test1;
|
||||
test2 := a;
|
||||
end
|
||||
else
|
||||
begin
|
||||
doSomething;
|
||||
end;
|
||||
end.
|
||||
""")]
|
||||
[InlineData("""
|
||||
program main;
|
||||
var a : integer;
|
||||
function test : integer;
|
||||
begin
|
||||
end;
|
||||
begin
|
||||
test;
|
||||
a := test;
|
||||
test();
|
||||
a := test();
|
||||
end.
|
||||
""")]
|
||||
[InlineData("""
|
||||
program main;
|
||||
procedure test();
|
||||
begin
|
||||
end;
|
||||
begin
|
||||
test();
|
||||
end.
|
||||
""")]
|
||||
[InlineData("""
|
||||
program main;
|
||||
begin
|
||||
test
|
||||
end.
|
||||
""")]
|
||||
public void ProgramParseTest(string input)
|
||||
{
|
||||
ProgramParse(input);
|
||||
}
|
||||
}
|
190
CanonSharp.Tests/ParserTests/StatementParserTests.cs
Normal file
190
CanonSharp.Tests/ParserTests/StatementParserTests.cs
Normal file
|
@ -0,0 +1,190 @@
|
|||
using CanonSharp.Pascal.Parser;
|
||||
using CanonSharp.Pascal.SyntaxTree;
|
||||
using CanonSharp.Tests.Utils;
|
||||
|
||||
namespace CanonSharp.Tests.ParserTests;
|
||||
|
||||
public class StatementParserTests : GrammarParserTestBase
|
||||
{
|
||||
[Fact]
|
||||
public void StatementTest1()
|
||||
{
|
||||
AssignNode node = RunParser<AssignNode>(GrammarParser.StatementParser(), "temp := 1");
|
||||
|
||||
Assert.Equal("temp", node.Variable.Identifier.LiteralValue);
|
||||
Assert.Equal(1, node.Expression.Convert<IntegerValueNode>().Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IfTest1()
|
||||
{
|
||||
IfNode node = RunParser<IfNode>(GrammarParser.IfParser(), """
|
||||
if i = 1 then
|
||||
a := a + 1
|
||||
else
|
||||
a := a + 2
|
||||
""");
|
||||
Assert.NotNull(node.ElseStatement);
|
||||
|
||||
BinaryOperatorNode condition = node.Condition.Convert<BinaryOperatorNode>();
|
||||
Assert.Equal(BinaryOperatorType.Equal, condition.OperatorType);
|
||||
|
||||
AssignNode statement = node.Statement.Convert<AssignNode>();
|
||||
Assert.Equal("a", statement.Variable.Identifier.LiteralValue);
|
||||
|
||||
AssignNode elseStatement = node.Statement.Convert<AssignNode>();
|
||||
Assert.Equal("a", elseStatement.Variable.Identifier.LiteralValue);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IfTest2()
|
||||
{
|
||||
IfNode node = RunParser<IfNode>(GrammarParser.IfParser(), """
|
||||
if i = 1 then
|
||||
if i = 2 then
|
||||
a := a + 1
|
||||
else
|
||||
a := a + 2
|
||||
""");
|
||||
|
||||
Assert.Null(node.ElseStatement);
|
||||
|
||||
IfNode subIfNode = node.Statement.Convert<IfNode>();
|
||||
Assert.NotNull(subIfNode.ElseStatement);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ForTest1()
|
||||
{
|
||||
ForNode node = RunParser<ForNode>(GrammarParser.ForParser(), """
|
||||
for i := 1 to 10 do
|
||||
a := a + i
|
||||
""");
|
||||
Assert.Equal("i", node.Identifier.LiteralValue);
|
||||
Assert.Equal(1, node.LeftCondition.Convert<IntegerValueNode>().Value);
|
||||
Assert.Equal(10, node.RightCondition.Convert<IntegerValueNode>().Value);
|
||||
|
||||
AssignNode assignNode = node.Statement.Convert<AssignNode>();
|
||||
Assert.Equal("a", assignNode.Variable.Identifier.LiteralValue);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WhileTest1()
|
||||
{
|
||||
WhileNode node = RunParser<WhileNode>(GrammarParser.WhileParser(), """
|
||||
while c >= 1 do
|
||||
a := a + 1;
|
||||
""");
|
||||
|
||||
BinaryOperatorNode binaryOperatorNode = node.Condition.Convert<BinaryOperatorNode>();
|
||||
Assert.Equal(BinaryOperatorType.GreaterEqual, binaryOperatorNode.OperatorType);
|
||||
|
||||
AssignNode assignNode = node.Statement.Convert<AssignNode>();
|
||||
Assert.Equal("a", assignNode.Variable.Identifier.LiteralValue);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ProcedureCallTest1()
|
||||
{
|
||||
ProcedureCallNode node = RunParser<ProcedureCallNode>(GrammarParser.ProcedureCallParser(), "test()");
|
||||
Assert.Equal("test", node.Identifier.LiteralValue);
|
||||
Assert.Empty(node.Parameters);
|
||||
|
||||
node = RunParser<ProcedureCallNode>(GrammarParser.ProcedureCallParser(), "test");
|
||||
Assert.Equal("test", node.Identifier.LiteralValue);
|
||||
Assert.Empty(node.Parameters);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ProcedureCallTest2()
|
||||
{
|
||||
ProcedureCallNode node = RunParser<ProcedureCallNode>(GrammarParser.ProcedureCallParser(),
|
||||
"test(1 + 1, true)");
|
||||
|
||||
Assert.Equal(2, node.Parameters.Count);
|
||||
|
||||
BinaryOperatorNode firstParameter = node.Parameters[0].Convert<BinaryOperatorNode>();
|
||||
Assert.Equal(BinaryOperatorType.Add, firstParameter.OperatorType);
|
||||
|
||||
BooleanValueNode secondParameter = node.Parameters[1].Convert<BooleanValueNode>();
|
||||
Assert.True(secondParameter.Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ProcedureCallTest3()
|
||||
{
|
||||
ProcedureCallNode node =
|
||||
RunParser<ProcedureCallNode>(GrammarParser.ProcedureCallParser(), "test(1 * + 1, test2[0])");
|
||||
|
||||
Assert.Equal(2, node.Parameters.Count);
|
||||
|
||||
VariableNode secondParameter = node.Parameters[1].Convert<VariableNode>();
|
||||
Assert.Equal("test2", secondParameter.Identifier.LiteralValue);
|
||||
Assert.Single(secondParameter.Indexers);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("""
|
||||
begin
|
||||
temp := 1 + 1;
|
||||
flag := true and false;
|
||||
end
|
||||
""")]
|
||||
[InlineData("""
|
||||
begin
|
||||
temp := 1 + 1;
|
||||
flag := true and false
|
||||
end
|
||||
""")]
|
||||
public void CompoundStatementTest1(string input)
|
||||
{
|
||||
BlockNode node = RunParser<BlockNode>(GrammarParser.CompoundStatementParser(), input);
|
||||
|
||||
Assert.Equal(2, node.Statements.Count);
|
||||
AssignNode assignNode = node.Statements[0].Convert<AssignNode>();
|
||||
Assert.Equal("temp", assignNode.Variable.Identifier.LiteralValue);
|
||||
|
||||
assignNode = node.Statements[1].Convert<AssignNode>();
|
||||
Assert.Equal("flag", assignNode.Variable.Identifier.LiteralValue);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CompoundStatementTest2()
|
||||
{
|
||||
BlockNode node = RunParser<BlockNode>(GrammarParser.CompoundStatementParser(), "begin end");
|
||||
Assert.Empty(node.Statements);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ProgramHeadTest1()
|
||||
{
|
||||
ProgramHead head = RunParser<ProgramHead>(GrammarParser.ProgramHeadParser(), "program main");
|
||||
|
||||
Assert.Equal("main", head.ProgramName.LiteralValue);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("""
|
||||
begin
|
||||
temp := 1 + 1;
|
||||
flag := true and false;
|
||||
end
|
||||
""")]
|
||||
[InlineData("""
|
||||
begin
|
||||
temp := 1 + 1;
|
||||
flag := true and false
|
||||
end
|
||||
""")]
|
||||
public void ProgramBodyTest1(string input)
|
||||
{
|
||||
BlockNode node = RunParser<ProgramBody>(GrammarParser.ProgramBodyParser(), input).MainBlock;
|
||||
|
||||
Assert.Equal(2, node.Statements.Count);
|
||||
AssignNode assignNode = node.Statements[0].Convert<AssignNode>();
|
||||
Assert.Equal("temp", assignNode.Variable.Identifier.LiteralValue);
|
||||
|
||||
assignNode = node.Statements[1].Convert<AssignNode>();
|
||||
Assert.Equal("flag", assignNode.Variable.Identifier.LiteralValue);
|
||||
}
|
||||
}
|
83
CanonSharp.Tests/ParserTests/SubprogramParserTests.cs
Normal file
83
CanonSharp.Tests/ParserTests/SubprogramParserTests.cs
Normal file
|
@ -0,0 +1,83 @@
|
|||
using CanonSharp.Pascal.Parser;
|
||||
using CanonSharp.Pascal.SyntaxTree;
|
||||
using CanonSharp.Tests.Utils;
|
||||
|
||||
namespace CanonSharp.Tests.ParserTests;
|
||||
|
||||
public class SubprogramParserTests : GrammarParserTestBase
|
||||
{
|
||||
[Fact]
|
||||
public void ParameterTest1()
|
||||
{
|
||||
List<Parameter> parameters = RunParser<Parameter>(GrammarParser.ParameterParser(), "a ,b : integer");
|
||||
|
||||
Assert.Equal(2, parameters.Count);
|
||||
Assert.All(parameters, p =>
|
||||
{
|
||||
Assert.False(p.IsReference);
|
||||
Assert.Equal("integer", p.TypeNode.Convert<TypeNode>().TypeToken.LiteralValue);
|
||||
});
|
||||
|
||||
parameters = RunParser<Parameter>(GrammarParser.ParameterParser(), "var a, b,c: real");
|
||||
Assert.Equal(3, parameters.Count);
|
||||
Assert.All(parameters, p =>
|
||||
{
|
||||
Assert.True(p.IsReference);
|
||||
Assert.Equal("real", p.TypeNode.Convert<TypeNode>().TypeToken.LiteralValue);
|
||||
});
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("(var a, b : integer; c, d : real)", 4)]
|
||||
[InlineData("(a : boolean; var c, d, e : char)", 4)]
|
||||
[InlineData("(a : integer)", 1)]
|
||||
[InlineData("(var b ,c : real)", 2)]
|
||||
public void FormalParameterTest(string input, int count)
|
||||
{
|
||||
List<Parameter> parameters = RunParser<Parameter>(GrammarParser.FormalParameterParser(), input);
|
||||
Assert.Equal(count, parameters.Count);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("procedure test", 0)]
|
||||
[InlineData("procedure test()", 0)]
|
||||
[InlineData("procedure test(a : integer; var b : real)", 2)]
|
||||
[InlineData("procedure test(a,b, c : real; var e, f : boolean)", 5)]
|
||||
[InlineData("function test : integer", 0)]
|
||||
[InlineData("function test() : real", 0)]
|
||||
[InlineData("function test(a : integer; var b : real) : boolean", 2)]
|
||||
[InlineData("function test(a,b, c : real; var e, f : boolean) : char", 5)]
|
||||
public void SubprogramHeadTest(string input, int count)
|
||||
{
|
||||
SubprogramHead head = RunParser<SubprogramHead>(GrammarParser.SubprogramHeadParser(), input);
|
||||
Assert.Equal(count, head.Parameters.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SubprogramHeadTest1()
|
||||
{
|
||||
SubprogramHead head =
|
||||
RunParser<SubprogramHead>(GrammarParser.SubprogramHeadParser(), "function test(a : integer) : real");
|
||||
|
||||
Assert.Equal("test", head.Identifier.LiteralValue);
|
||||
Assert.Single(head.Parameters);
|
||||
Assert.NotNull(head.TypeToken);
|
||||
Assert.Equal("real", head.TypeToken.Convert<TypeNode>().TypeToken.LiteralValue);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SubprogramBodyTest1()
|
||||
{
|
||||
SubprogramBody body = RunParser<SubprogramBody>(GrammarParser.SubprogramBodyParser(), """
|
||||
const a = 3;
|
||||
var c, d : integer; e, f :real;
|
||||
begin
|
||||
c := a + d;
|
||||
end
|
||||
""");
|
||||
|
||||
Assert.Single(body.ConstDeclarations.Statements);
|
||||
Assert.Equal(2, body.VariableDeclarations.Statements.Count);
|
||||
Assert.Equal(1, body.MainBlock.Statements.Count);
|
||||
}
|
||||
}
|
82
CanonSharp.Tests/ParserTests/VariableDeclarationTests.cs
Normal file
82
CanonSharp.Tests/ParserTests/VariableDeclarationTests.cs
Normal file
|
@ -0,0 +1,82 @@
|
|||
using CanonSharp.Pascal.Parser;
|
||||
using CanonSharp.Pascal.SyntaxTree;
|
||||
using CanonSharp.Tests.Utils;
|
||||
|
||||
namespace CanonSharp.Tests.ParserTests;
|
||||
|
||||
public class VariableDeclarationTests : GrammarParserTestBase
|
||||
{
|
||||
[Theory]
|
||||
[InlineData("boolean", "boolean")]
|
||||
[InlineData("integer", "integer")]
|
||||
[InlineData("char", "char")]
|
||||
[InlineData("real", "real")]
|
||||
public void BasicTypeParseTest(string input, string value)
|
||||
{
|
||||
TypeNode node = RunParser<TypeNode>(GrammarParser.TypeParser(), input);
|
||||
Assert.Equal(value, node.TypeToken.LiteralValue);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ArrayTypeParseTest()
|
||||
{
|
||||
TypeNode node = RunParser<TypeNode>(GrammarParser.ArrayTypeParser(), "array [0..9] of integer");
|
||||
|
||||
Assert.Equal("integer", node.TypeToken.LiteralValue);
|
||||
Assert.Single(node.ArrayRanges);
|
||||
Assert.Equal(new ArrayRange(0, 9), node.ArrayRanges.First());
|
||||
|
||||
node = RunParser<TypeNode>(GrammarParser.ArrayTypeParser(), "array [0..10,0..10] of char");
|
||||
Assert.Equal("char", node.TypeToken.LiteralValue);
|
||||
Assert.Equal(2, node.ArrayRanges.Count);
|
||||
Assert.Equal(new ArrayRange(0, 10), node.ArrayRanges[0]);
|
||||
Assert.Equal(new ArrayRange(0, 10), node.ArrayRanges[1]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void VariableDeclarationTest()
|
||||
{
|
||||
VariableDeclarationNode node = RunParser<VariableDeclarationNode>(GrammarParser.VariableDeclarationParser(),
|
||||
"a : integer");
|
||||
|
||||
Assert.Contains(node.Identifiers, token => token.LiteralValue == "a");
|
||||
Assert.Equal("integer", node.TypeNode.TypeToken.LiteralValue);
|
||||
|
||||
node = RunParser<VariableDeclarationNode>(GrammarParser.VariableDeclarationParser(),
|
||||
"a, b, c : boolean");
|
||||
|
||||
Assert.Contains(node.Identifiers, token => token.LiteralValue == "a");
|
||||
Assert.Contains(node.Identifiers, token => token.LiteralValue == "b");
|
||||
Assert.Contains(node.Identifiers, token => token.LiteralValue == "c");
|
||||
Assert.Equal("boolean", node.TypeNode.TypeToken.LiteralValue);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ArrayVariableDeclarationTest()
|
||||
{
|
||||
VariableDeclarationNode node = RunParser<VariableDeclarationNode>(GrammarParser.VariableDeclarationParser(),
|
||||
"test_array: array [0..9,10..20] of real");
|
||||
|
||||
Assert.Single(node.Identifiers);
|
||||
Assert.Contains(node.Identifiers, token => token.LiteralValue == "test_array");
|
||||
Assert.Equal("real", node.TypeNode.TypeToken.LiteralValue);
|
||||
|
||||
Assert.Equal(2, node.TypeNode.ArrayRanges.Count);
|
||||
Assert.Equal(new ArrayRange(0, 9), node.TypeNode.ArrayRanges[0]);
|
||||
Assert.Equal(new ArrayRange(10, 20), node.TypeNode.ArrayRanges[1]);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("var a : integer;", 1)]
|
||||
[InlineData("var a : integer; b : boolean;", 2)]
|
||||
[InlineData("var a : integer; b,c : boolean;" +
|
||||
"d : char", 3)]
|
||||
[InlineData("var a,d,e : integer; b : boolean; c: real;", 3)]
|
||||
[InlineData("var a : array [0..5] of integer", 1)]
|
||||
[InlineData("var a,b,c: array [0..9,10..20] of integer; c,d : boolean", 2)]
|
||||
public void VariableDeclarationsTest(string input, int count)
|
||||
{
|
||||
BlockNode node = RunParser<BlockNode>(GrammarParser.VariableDeclarationsParser(), input);
|
||||
Assert.Equal(count, node.Statements.Count);
|
||||
}
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
using CanonSharp.Combinator.Extensions;
|
||||
using CanonSharp.Common.Scanner;
|
||||
using CanonSharp.Pascal.Scanner;
|
||||
|
||||
namespace CanonSharp.Tests.LexicalAnalyzerTests;
|
||||
namespace CanonSharp.Tests.ReaderTests;
|
||||
|
||||
public class StringReadStateTests
|
||||
{
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
using CanonSharp.Common.Scanner;
|
||||
using CanonSharp.Pascal.Scanner;
|
||||
using CanonSharp.Tests.Utils;
|
||||
|
||||
namespace CanonSharp.Tests.ScannerTest;
|
||||
namespace CanonSharp.Tests.ScannerTests;
|
||||
|
||||
public class LexicalParserTests : LexicalTestBase
|
||||
{
|
||||
|
@ -131,6 +131,21 @@ public class LexicalParserTests : LexicalTestBase
|
|||
]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void LexicalParserTest4()
|
||||
{
|
||||
ValidateLexicalTokens(LexicalScanner.PascalParser(), "array [0..9] of integer", [
|
||||
(LexicalTokenType.Keyword, "array"),
|
||||
(LexicalTokenType.Delimiter, "["),
|
||||
(LexicalTokenType.ConstInteger, "0"),
|
||||
(LexicalTokenType.Delimiter, ".."),
|
||||
(LexicalTokenType.ConstInteger, "9"),
|
||||
(LexicalTokenType.Delimiter, "]"),
|
||||
(LexicalTokenType.Keyword, "of"),
|
||||
(LexicalTokenType.Keyword, "integer")
|
||||
]);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("""
|
||||
program exFunction;
|
|
@ -1,10 +1,10 @@
|
|||
using CanonSharp.Combinator;
|
||||
using CanonSharp.Combinator.Abstractions;
|
||||
using CanonSharp.Combinator.Extensions;
|
||||
using CanonSharp.Common.Scanner;
|
||||
using CanonSharp.Pascal.Scanner;
|
||||
using CanonSharp.Tests.Utils;
|
||||
|
||||
namespace CanonSharp.Tests.ScannerTest;
|
||||
namespace CanonSharp.Tests.ScannerTests;
|
||||
|
||||
public class LexicalTokenParserTest : LexicalTestBase
|
||||
{
|
||||
|
@ -27,9 +27,14 @@ public class LexicalTokenParserTest : LexicalTestBase
|
|||
[InlineData("true")]
|
||||
[InlineData("false")]
|
||||
[InlineData("while")]
|
||||
[InlineData("div")]
|
||||
[InlineData("not")]
|
||||
[InlineData("mod")]
|
||||
[InlineData("and")]
|
||||
[InlineData("or")]
|
||||
public void KeywordParserTest(string literalValue)
|
||||
{
|
||||
Parser<char, LexicalToken> keyword = LexicalScanner.KeywordParser();
|
||||
IParser<char, LexicalToken> keyword = LexicalScanner.KeywordParser();
|
||||
ValidateSuccessfulParser(keyword, LexicalTokenType.Keyword, literalValue, literalValue);
|
||||
}
|
||||
|
||||
|
@ -39,7 +44,7 @@ public class LexicalTokenParserTest : LexicalTestBase
|
|||
[InlineData("todo")]
|
||||
public void FailedKeywordParserTest(string input)
|
||||
{
|
||||
Parser<char, LexicalToken> keyword = LexicalScanner.KeywordParser();
|
||||
IParser<char, LexicalToken> keyword = LexicalScanner.KeywordParser();
|
||||
ValidateFailedParser(keyword, input);
|
||||
}
|
||||
|
||||
|
@ -55,7 +60,7 @@ public class LexicalTokenParserTest : LexicalTestBase
|
|||
[InlineData("..")]
|
||||
public void DelimiterParserTest(string literalValue)
|
||||
{
|
||||
Parser<char, LexicalToken> delimiter = LexicalScanner.DelimiterParser();
|
||||
IParser<char, LexicalToken> delimiter = LexicalScanner.DelimiterParser();
|
||||
ValidateSuccessfulParser(delimiter, LexicalTokenType.Delimiter, literalValue, literalValue);
|
||||
}
|
||||
|
||||
|
@ -63,7 +68,7 @@ public class LexicalTokenParserTest : LexicalTestBase
|
|||
[InlineData(":=")]
|
||||
public void FailedDelimiterParserTest(string input)
|
||||
{
|
||||
Parser<char, LexicalToken> delimiter = LexicalScanner.DelimiterParser();
|
||||
IParser<char, LexicalToken> delimiter = LexicalScanner.DelimiterParser();
|
||||
ValidateFailedParser(delimiter, input);
|
||||
}
|
||||
|
||||
|
@ -81,7 +86,7 @@ public class LexicalTokenParserTest : LexicalTestBase
|
|||
[InlineData(":=")]
|
||||
public void OperatorParserTest(string literalValue)
|
||||
{
|
||||
Parser<char, LexicalToken> operatorParser = LexicalScanner.OperatorParser();
|
||||
IParser<char, LexicalToken> operatorParser = LexicalScanner.OperatorParser();
|
||||
ValidateSuccessfulParser(operatorParser, LexicalTokenType.Operator, literalValue, literalValue);
|
||||
}
|
||||
|
||||
|
@ -94,7 +99,7 @@ public class LexicalTokenParserTest : LexicalTestBase
|
|||
[InlineData("andand")]
|
||||
public void IdentifierParserTest(string literalValue)
|
||||
{
|
||||
Parser<char, LexicalToken> identifier = LexicalScanner.IdentifierParser();
|
||||
IParser<char, LexicalToken> identifier = LexicalScanner.IdentifierParser();
|
||||
|
||||
ValidateSuccessfulParser(identifier, LexicalTokenType.Identifier, literalValue, literalValue);
|
||||
}
|
||||
|
@ -102,10 +107,11 @@ public class LexicalTokenParserTest : LexicalTestBase
|
|||
[Theory]
|
||||
[InlineData(123, "123")]
|
||||
[InlineData(0, "0")]
|
||||
[InlineData(10, "10..20")]
|
||||
public void ConstIntegerTest(int value, string input)
|
||||
{
|
||||
StringReadState state = new(input);
|
||||
ParseResult<char, LexicalToken> result = LexicalScanner.ConstIntegerParser().Parse(state);
|
||||
IParseResult<char, LexicalToken> result = LexicalScanner.ConstIntegerParser().Parse(state);
|
||||
|
||||
Assert.Equal(LexicalTokenType.ConstInteger, result.Value.TokenType);
|
||||
Assert.Equal(value, int.Parse(result.Value.LiteralValue));
|
||||
|
@ -117,7 +123,7 @@ public class LexicalTokenParserTest : LexicalTestBase
|
|||
public void ConstFloatTest(double value, string input)
|
||||
{
|
||||
StringReadState state = new(input);
|
||||
ParseResult<char, LexicalToken> result = LexicalScanner.ConstFloatParser().Parse(state);
|
||||
IParseResult<char, LexicalToken> result = LexicalScanner.ConstFloatParser().Parse(state);
|
||||
|
||||
Assert.Equal(LexicalTokenType.ConstFloat, result.Value.TokenType);
|
||||
Assert.Equal(value, double.Parse(result.Value.LiteralValue));
|
||||
|
@ -129,7 +135,7 @@ public class LexicalTokenParserTest : LexicalTestBase
|
|||
public void CharTest(char value, string input)
|
||||
{
|
||||
StringReadState state = new(input);
|
||||
ParseResult<char, LexicalToken> result = LexicalScanner.CharParser().Parse(state);
|
||||
IParseResult<char, LexicalToken> result = LexicalScanner.CharParser().Parse(state);
|
||||
|
||||
Assert.Equal(LexicalTokenType.Character, result.Value.TokenType);
|
||||
Assert.Equal(value, char.Parse(result.Value.LiteralValue));
|
||||
|
@ -140,7 +146,7 @@ public class LexicalTokenParserTest : LexicalTestBase
|
|||
public void StringTest(string value, string input)
|
||||
{
|
||||
StringReadState state = new(input);
|
||||
ParseResult<char, LexicalToken> result = LexicalScanner.CharParser().Parse(state);
|
||||
IParseResult<char, LexicalToken> result = LexicalScanner.CharParser().Parse(state);
|
||||
|
||||
Assert.Equal(LexicalTokenType.String, result.Value.TokenType);
|
||||
Assert.Equal(value, result.Value.LiteralValue);
|
||||
|
@ -152,7 +158,7 @@ public class LexicalTokenParserTest : LexicalTestBase
|
|||
public void CommentTest(string input)
|
||||
{
|
||||
StringReadState state = new(input);
|
||||
ParseResult<char, Unit> result = LexicalScanner.CommentParser().Parse(state);
|
||||
IParseResult<char, Unit> result = LexicalScanner.CommentParser().Parse(state);
|
||||
|
||||
Assert.Equal(Unit.Instance, result.Value);
|
||||
}
|
||||
|
@ -168,8 +174,8 @@ public class LexicalTokenParserTest : LexicalTestBase
|
|||
public void JunkTest(string input)
|
||||
{
|
||||
StringReadState state = new(input);
|
||||
Parser<char, LexicalToken> parser = LexicalScanner.JunkParser().SkipTill(LexicalScanner.KeywordParser());
|
||||
ParseResult<char, LexicalToken> result = parser.Parse(state);
|
||||
IParser<char, LexicalToken> parser = LexicalScanner.JunkParser().SkipTill(LexicalScanner.KeywordParser());
|
||||
IParseResult<char, LexicalToken> result = parser.Parse(state);
|
||||
|
||||
Assert.Equal(LexicalTokenType.Keyword, result.Value.TokenType);
|
||||
Assert.Equal("program", result.Value.LiteralValue);
|
|
@ -1,7 +1,7 @@
|
|||
using CanonSharp.Combinator.Abstractions;
|
||||
using CanonSharp.Combinator.Extensions;
|
||||
using CanonSharp.Combinator.Text;
|
||||
using CanonSharp.Common.Scanner;
|
||||
using CanonSharp.Pascal.Scanner;
|
||||
using CanonSharp.Tests.Utils;
|
||||
using static CanonSharp.Combinator.Text.TextParserBuilder;
|
||||
|
||||
|
@ -24,7 +24,7 @@ public class TextParserTests : ParserTestsBase
|
|||
[InlineData('d', "d")]
|
||||
public void OneOfTest(char except, string input)
|
||||
{
|
||||
Parser<char, char> parser = OneOf("abcd");
|
||||
IParser<char, char> parser = OneOf("abcd");
|
||||
ValidateSuccessfulResult(parser, except, input);
|
||||
}
|
||||
|
||||
|
@ -39,7 +39,7 @@ public class TextParserTests : ParserTestsBase
|
|||
[InlineData('D', "D")]
|
||||
public void OneOfIgnoreCaseTest(char except, string input)
|
||||
{
|
||||
Parser<char, char> parser = OneOfIgnoreCase("abcd");
|
||||
IParser<char, char> parser = OneOfIgnoreCase("abcd");
|
||||
ValidateSuccessfulResult(parser, except, input);
|
||||
}
|
||||
|
||||
|
@ -50,7 +50,7 @@ public class TextParserTests : ParserTestsBase
|
|||
[InlineData("Hello,World.")]
|
||||
public void StringIgnoreCaseTest(string literalValue)
|
||||
{
|
||||
Parser<char, string> parser = StringIgnoreCase("hello,world.");
|
||||
IParser<char, string> parser = StringIgnoreCase("hello,world.");
|
||||
ValidateSuccessfulResult(parser, literalValue, literalValue);
|
||||
}
|
||||
|
||||
|
@ -60,7 +60,7 @@ public class TextParserTests : ParserTestsBase
|
|||
[InlineData('9')]
|
||||
public void RangeTest(char except)
|
||||
{
|
||||
Parser<char, char> parser = Range('0', '9');
|
||||
IParser<char, char> parser = Range('0', '9');
|
||||
ValidateSuccessfulResult(parser, except, except.ToString());
|
||||
|
||||
ValidateFailedResult(parser, "abc");
|
||||
|
@ -161,8 +161,8 @@ public class TextParserTests : ParserTestsBase
|
|||
test
|
||||
""");
|
||||
|
||||
Parser<char, IEnumerable<string>> parser = StringIgnoreCase("test").SkipSpaceAndLineBreak().Many();
|
||||
ParseResult<char, IEnumerable<string>> result = parser.Parse(state);
|
||||
IParser<char, IEnumerable<string>> parser = StringIgnoreCase("test").SkipSpaceAndLineBreak().Many();
|
||||
IParseResult<char, IEnumerable<string>> result = parser.Parse(state);
|
||||
|
||||
Assert.All(result.Value, x => Assert.Equal("test", x.ToLower()));
|
||||
}
|
||||
|
|
31
CanonSharp.Tests/Utils/GrammarParserTestBase.cs
Normal file
31
CanonSharp.Tests/Utils/GrammarParserTestBase.cs
Normal file
|
@ -0,0 +1,31 @@
|
|||
using CanonSharp.Combinator.Abstractions;
|
||||
using CanonSharp.Pascal.Parser;
|
||||
using CanonSharp.Pascal.Scanner;
|
||||
using CanonSharp.Pascal.SyntaxTree;
|
||||
|
||||
namespace CanonSharp.Tests.Utils;
|
||||
|
||||
public abstract class GrammarParserTestBase
|
||||
{
|
||||
protected static T RunParser<T>(IParser<LexicalToken, SyntaxNodeBase> parser, string input) where T : SyntaxNodeBase
|
||||
{
|
||||
LexicalScanner scanner = new();
|
||||
LexicalTokenReadState state = new(scanner.Tokenize(new StringReadState(input)));
|
||||
IParseResult<LexicalToken, SyntaxNodeBase> parseResult = parser.Parse(state);
|
||||
|
||||
return parseResult.Value.Convert<T>();
|
||||
}
|
||||
|
||||
protected static List<T> RunParser<T>(IParser<LexicalToken, IEnumerable<SyntaxNodeBase>> parser,
|
||||
string input) where T : SyntaxNodeBase
|
||||
{
|
||||
LexicalScanner scanner = new();
|
||||
LexicalTokenReadState state = new(scanner.Tokenize(new StringReadState(input)));
|
||||
IParseResult<LexicalToken, IEnumerable<SyntaxNodeBase>> parseResult = parser.Parse(state);
|
||||
|
||||
return parseResult.Value.Select(node => node.Convert<T>()).ToList();
|
||||
}
|
||||
|
||||
protected static Program ProgramParse(string input)
|
||||
=> RunParser<Program>(GrammarParser.ProgramParser(), input);
|
||||
}
|
|
@ -1,36 +1,38 @@
|
|||
using CanonSharp.Combinator;
|
||||
using CanonSharp.Combinator.Abstractions;
|
||||
using CanonSharp.Common.Scanner;
|
||||
using CanonSharp.Pascal.Scanner;
|
||||
|
||||
namespace CanonSharp.Tests.Utils;
|
||||
|
||||
public abstract class LexicalTestBase
|
||||
{
|
||||
protected static void ValidateSuccessfulParser(Parser<char, LexicalToken> parser, LexicalTokenType exceptedType,
|
||||
protected static void ValidateSuccessfulParser(IParser<char, LexicalToken> parser, LexicalTokenType exceptedType,
|
||||
string literalValue, string input)
|
||||
{
|
||||
StringReadState state = new(input);
|
||||
ParseResult<char, LexicalToken> result = parser.Parse(state);
|
||||
IParseResult<char, LexicalToken> result = parser.Parse(state);
|
||||
|
||||
Assert.Equal(exceptedType, result.Value.TokenType);
|
||||
Assert.Equal(literalValue, result.Value.LiteralValue);
|
||||
}
|
||||
|
||||
protected static void ValidateFailedParser(Parser<char, LexicalToken> parser, string input)
|
||||
protected static void ValidateFailedParser(IParser<char, LexicalToken> parser, string input)
|
||||
{
|
||||
StringReadState state = new(input);
|
||||
ParseResult<char, LexicalToken> result = parser.Parse(state);
|
||||
IParseResult<char, LexicalToken> result = parser.Parse(state);
|
||||
Assert.ThrowsAny<ParseException>(() => result.Value);
|
||||
}
|
||||
|
||||
protected static void ValidateLexicalTokens(Parser<char, IEnumerable<LexicalToken>> parser, string input,
|
||||
IEnumerable<(LexicalTokenType, string)> exceptedResult)
|
||||
protected static void ValidateLexicalTokens(IParser<char, IEnumerable<LexicalToken>> parser, string input,
|
||||
List<(LexicalTokenType, string)> exceptedResult)
|
||||
{
|
||||
StringReadState state = new(input);
|
||||
ParseResult<char, IEnumerable<LexicalToken>> result = parser.Parse(state);
|
||||
IParseResult<char, IEnumerable<LexicalToken>> result = parser.Parse(state);
|
||||
List<LexicalToken> actualResult = result.Value.ToList();
|
||||
|
||||
Assert.Equal(exceptedResult.Count, actualResult.Count);
|
||||
foreach (((LexicalTokenType exceptedType, string exceptedValue), LexicalToken token) in exceptedResult.Zip(
|
||||
result.Value))
|
||||
actualResult))
|
||||
{
|
||||
Assert.Equal(exceptedType, token.TokenType);
|
||||
Assert.Equal(exceptedValue, token.LiteralValue);
|
||||
|
|
|
@ -1,26 +1,26 @@
|
|||
using CanonSharp.Combinator;
|
||||
using CanonSharp.Combinator.Abstractions;
|
||||
using CanonSharp.Common.Scanner;
|
||||
using CanonSharp.Pascal.Scanner;
|
||||
|
||||
namespace CanonSharp.Tests.Utils;
|
||||
|
||||
public abstract class ParserTestsBase
|
||||
{
|
||||
protected static void ValidateSuccessfulResult<T>(Parser<char, T> parser, T value, string source)
|
||||
protected static void ValidateSuccessfulResult<T>(IParser<char, T> parser, T value, string source)
|
||||
{
|
||||
StringReadState state = new(source);
|
||||
|
||||
ParseResult<char, T> result = parser.Parse(state);
|
||||
IParseResult<char, T> result = parser.Parse(state);
|
||||
Assert.Equal(value, result.Value);
|
||||
}
|
||||
|
||||
protected static void ValidateSuccessfulResult<T>(
|
||||
Parser<char, IEnumerable<T>> parser,
|
||||
IParser<char, IEnumerable<T>> parser,
|
||||
IEnumerable<T> values, string source)
|
||||
{
|
||||
StringReadState state = new(source);
|
||||
|
||||
ParseResult<char, IEnumerable<T>> result = parser.Parse(state);
|
||||
IParseResult<char, IEnumerable<T>> result = parser.Parse(state);
|
||||
|
||||
foreach ((T actual, T except) in result.Value.Zip(values))
|
||||
{
|
||||
|
@ -28,16 +28,16 @@ public abstract class ParserTestsBase
|
|||
}
|
||||
}
|
||||
|
||||
protected static FailedResult<char, T> ValidateFailedResult<T>(Parser<char, T> parser, string source)
|
||||
protected static IFailedResult<char, T> ValidateFailedResult<T>(IParser<char, T> parser, string source)
|
||||
{
|
||||
StringReadState state = new(source);
|
||||
|
||||
ParseResult<char, T> result = parser.Parse(state);
|
||||
IParseResult<char, T> result = parser.Parse(state);
|
||||
Assert.ThrowsAny<ParseException>(() =>
|
||||
{
|
||||
_ = result.Value;
|
||||
});
|
||||
|
||||
return (FailedResult<char, T>)result;
|
||||
return (IFailedResult<char, T>)result;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ VisualStudioVersion = 17.0.31903.59
|
|||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CanonSharp.Tests", "CanonSharp.Tests\CanonSharp.Tests.csproj", "{5A28EF92-805F-44E9-8E13-EA9A5BB623BB}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CanonSharp.Common", "CanonSharp.Common\CanonSharp.Common.csproj", "{288943FE-E3E6-49E2-8C6F-9B5B9242266C}"
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CanonSharp.Pascal", "CanonSharp.Pascal\CanonSharp.Pascal.csproj", "{288943FE-E3E6-49E2-8C6F-9B5B9242266C}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".gitea", ".gitea", "{B97A35A0-4616-4B3F-8F73-A66827BC98BA}"
|
||||
EndProject
|
||||
|
|
Loading…
Reference in New Issue
Block a user