Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up

A thin C# wrapper for FParsec.

License

NotificationsYou must be signed in to change notification settings

bert2/FParsec.CSharp

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

buildtestscoverageCodeFactorlast commitnuget packagenuget downloads

FParsec.CSharp is a C# wrapper for the F# packageFParsec. FParsec is a parser combinator library with which you can implement parsers declaratively.

Why FParsec.CSharp?

While using FParsec from C# is entirely possible in theory, it is very awkward in practice. Most of FParsec's elegance is lost in translation due to C#'s inferior type inference and its lack of custom operators.

FParsec.CSharp tries to alleviate that by wrapping FParsec's operators as extension functions.

FParsec.CSharp does not try to hide any types fromFParsec orFSharp.Core--the wrapper is thin and also avoids name collisions. That way you can always fallback to FParsec anytime you need some functionality not implemented by FParsec.CSharp.

Based on the current implementation it should be easy to extend the wrapper yourself if needed. Pull requests are always welcome!

Note that the documentation assumes prior knowledge on FParsec or other parser combinator libraries.

Getting started

Import the combinators, pre-defined parsers, and helper functions:

usingFParsec.CSharp;// extension functions (combinators & helpers)usingstaticFParsec.CSharp.PrimitivesCS;// combinator functionsusingstaticFParsec.CSharp.CharParsersCS;// pre-defined parsers

Now you can write some parsers:

varparser=AnyChar.And(Digit);varreply=parser.ParseString("a1");Debug.Assert(reply.Result==('a','1'));

Executing parsers

An FParsec parser is a function that takes aCharStream<T> and returns aReply<T>. In C# such parsers are represented by the typeFSharpFunction<CharStream<TUserState>, Reply<TResult>> and can be executed with the methodReply<TResult>> Invoke(CharStream<TUserState>).

FParsec.CSharp comes with extensions to make things easier for you.

Running the parser function and getting theReply

You have already seen one way to execute a parser in the previous section:ParseString().

ParseString(string) is just a wrapper forFSharpFunc<CharStream<Unit>, Reply<T>>.Invoke(). It constructs theCharStream for you from the given string.

ParseFile(string) will do the same for a given file path.

If you need maximum control thenParse(CharStream<Unit>) is your best option. You can configure theCharStream yourself and inspect all the details of theReply after parsing.

Getting nicer error messages withParserResult

UsingInvoke(),Parse(), or any of its variants is generally not recommended. FParsec provides a better way to execute parsers that generates nicely formatted error messages:Run().

The extensionParserResult<T, Unit> Run(string) does the same asReply<T> ParseString(string), but also builds an error message from theReply's errors and the parser postion:

varresult=Many1(Digit).AndR(Upper).Run("123a");// Don't be shocked: in the next section we will learn how to improve this.varmsg=((ParserResult<char,Unit>.Failure)result).Item1;Console.WriteLine(msg);

The above will print the following detailed parsing failure message:

Error in Ln: 1 Col: 4123a   ^Expecting: decimal digit or uppercase letter

Run(string) is actually just a short form forRunOnString(string).

Additionally, there are three moreRun...() functions:RunOnString(string, int, int) (parse a substring),RunOnStream() (parse aStream), andRunOnFile() (parse... well you get the idea).

Handling parser results

FParsec'sParserResult is an F# discriminated union, which are awkward to work with in C#. FParsec.CSharp comes with extensions methods that hide those ugly details.

Getting the result or an exception

The easiest way to get the parser result is to useGetResult():

varone=Digit.Run("1").GetResult();Debug.Assert(one=='1');

GetResult() will throw anInvalidOperationException in case the parser failed. The exception will contain the detailed parsing failure message.

Getting the result with custom error handling

If you need more graceful error handling you can useGetResult<T>(Func<string, T>) which delegates error handling to the caller and provides the failure message to it:

// provide fallback valuevard1=Digit.Run("a").GetResult(_=>default);Debug.Assert(d1=='\0');// throw your own exceptionvard2=Digit.Run("a").GetResult(msg=>thrownewException($"whoops...{msg}"));// do anything as long as you return a fallback value or throwvard3=Digit.Run("a").GetResult(_=>{Console.WriteLine("oof");returnGetRandomChar();});

Additionally, the extensionsGetResultOrError<T>(Func<ParserError, T>) andGetResultOrFailure<T>(Func<ParserResult<T, Unit>.Failure, T>) let you inspect theParserError orFailure objects during error handling.

Safe unwrapping

Alternatively, you can can safely unwrap aParserResult<T, Unit> into a tuple(T? result, string? message) usingUnwrapResult() (which will never throw):

var(res,msg)=Digit.Run("a").UnwrapResult();Console.WriteLine(msg??$"Parser succeeded:{res}");

In case parsing succeeded the left side of the tuple will hold the parser's return value and the right side will benull.

In case parsing failed the left side of the tuple will hold the return value type'sdefault value and the right side will hold the detailed parser error message.

Hence the safest way to check if the parser result tuple indicates failure is to check whether the right side isnull.

With C# 8.0 you can do that quite nicely using aswitch expression and recursive patterns:

varresponse=Digit.Run("a").UnwrapResult()switch{(varr,null)=>$"Parser succeeded:{r}",(_,varm)=>$"Parser failed:{m}"};

UnwrapWithError() andUnwrapWithFailure() work the same way, but return theParserError orParserResult<TResult, Unit>.Failure instance in the right side of the tuple.

Deconstructing parser results (C# 8.0)

FParsec.CSharp extends the types involved with parser results with deconstructors so you can make use of C# 8.0's recursive patterns insideswitch statements/expressions:

varresponse=Digit.Run("1")switch{ParserResult<char,Unit>.Success(varc,_,_)=>$"Parsed '{c}'.",ParserResult<char,Unit>.Failure(_,(_,(_,_,varcol,_)),_)=>$"Some error at column{col}."};
varresponse=Digit.ParseString("a")switch{(ReplyStatus.Ok,varc,_)=>$"Parsed '{c}'.",(ReplyStatus.Error,_,(ErrorMessage.Expectederr,_))=>$"Expected a{err.Label}.",    _=>"oof."};

You will need to import some of FParsec's namespaces for this to work:

using FParsec; // contains `ReplyStatus` and `ErrorMessage`using static FParsec.CharParsers; // contains `ParserResult`

Working with user state

FParsec.CSharp, like FParsec, supports parsing with user state. This is reflected by the type parameterU in the signatures:

publicFSharpFunc<CharStream<U>,Reply<(T1,T2)>>And<U,T1,T2>(thisFSharpFunc<CharStream<U>,Reply<T1>>p1,FSharpFunc<CharStream<U>,Reply<T2>>p2);

If a combinator/parser supports user state then it will always haveU as the first type parameter.

For the combinators fromFParsec.CSharp.PrimitivesCS this will be transparent most of the time, because C# is able to infer the user state type of the combinator from the user state type of the parser argument(s).

Unfortunately C# is not able to infer the user state type retrospectively from later bindings and hence forces you to explicitly specify the user state type on parsers that have no parser parameters. In the case of the predefined parsers fromFParsec.CSharp.CharParsersCS (which usually don't take other parsers as arguments) this restriction would be cause for much annoyance.

That's why all parsers/combinators that have no parser parameters have two variants: one assuming a user state type ofUnit and another one expecting the explicit type argumentU. The names of the latter ones are always suffixed with the letter "U":

var parserWithoutUserState = Digit.And(Letter);var parserWithUserState = DigitU<int>().And(LetterU<int>());

Below are example test cases to demonstrate working with user state:

[Fact]publicvoidSimpleSet(){switch(SetUserState(12).RunOnString("",0)){caseParserResult<Unit,int>.Success(_,12,_):break;default:thrownewException();}}[Fact]publicvoidCountParsedLetters(){varcountedLetter=LetterU<int>().And(UpdateUserState<int>(cnt=>cnt+1));SkipMany(countedLetter).And(GetUserState<int>()).RunOnString("abcd",0).GetResult().ShouldBe(4);}[Fact]publicvoidCheckNestingLevel(){FSharpFunc<CharStream<int>,Reply<Unit>>expr=null;varparens=Between('(',Rec(()=>expr),')');varempty=ReturnU<int,Unit>(null);expr=Choice(parens.AndR(UpdateUserState<int>(depth=>depth+1)),empty);expr.AndR(UserStateSatisfies<int>(depth=>depth<3)).RunOnString("((()))",0).IsFailure.ShouldBeTrue();}

Using FParsec.CSharp and FParsec together

Working with FParsec parsers directly

In case you need one of FParsec's more specialized parsers you can easily import their namespace:

usingstaticFParsec.CharParsers;

In the example below we are usingFParsec.CharParsers.many1Chars2(). As you can see it integrates seemlessly with FParsec.CSharp:

varfirst=Letter.Or(CharP('_'));varrest=Letter.Or(CharP('_')).Or(Digit);varidentifier=many1Chars2(first,rest);varp=identifier.And(Skip('=')).And(Int);varr=p.ParseString("my_1st_var=13");System.Diagnostics.Debug.Assert(r.Result==("my_1st_var",13));

Passing lambdas to FParsec

Some of FParsec's parsers take anonymous functions. But since they expect curriedFSharpFuncs they won't accept C# lambdas. FParsec.CSharp comes with a little helper to createFSharpFuncs fromFunc objects:

// convert lambda with factory methodvarfsfunc1=FSharpFunc.From<char,bool>(c=>c=='x'||c=='y');// convert Func object with extension methodFunc<char,bool>func= c=>c=='1'||c=='2';varfsfunc2=func.ToFSharpFunc();varp=manySatisfy<Unit>(fsfunc1).And(manySatisfy<Unit>(fsfunc2));varr=p.ParseString("xyxyyy212221212");

Examples

You can find lots of examples in thetest project. Below are some parser definitions from there.

Simple JSON

FSharpFunc<CharStream<Unit>,Reply<JToken>>jvalue=null;varjnull=StringCI("null",(JToken)null).Lbl("null");varjnum=Float.Map(i=>(JToken)i).Lbl("number");varjbool=StringCI("true").Or(StringCI("false")).Map(b=>(JToken)bool.Parse(b)).Lbl("bool");varquotedString=Between('"',ManyChars(NoneOf("\"")),'"');varjstring=quotedString.Map(s=>(JToken)s).Lbl("string");vararrItems=Many(Rec(()=>jvalue),sep:CharP(',').And(WS));varjarray=Between(CharP('[').And(WS),arrItems,CharP(']')).Map(elems=>(JToken)newJArray(elems)).Lbl("array");varjidentifier=quotedString.Lbl("identifier");varjprop=jidentifier.And(WS).And(Skip(':')).And(WS).And(Rec(()=>jvalue)).Map((name,value)=>newJProperty(name,value));varobjProps=Many(jprop,sep:CharP(',').And(WS));varjobject=Between(CharP('{').And(WS),objProps,CharP('}')).Map(props=>(JToken)newJObject(props)).Lbl("object");jvalue=Choice(jnum,jbool,jnull,jstring,jarray,jobject).And(WS);varsimpleJsonParser=WS.And(jobject).And(WS).And(EOF).Map(o=>(JObject)o);

Simple XML

varnameStart=Choice(Letter,CharP('_'));varnameChar=Choice(Letter,Digit,AnyOf("-_."));varname=Many1Chars(nameStart,nameChar).And(WS);varquotedString=Between('"',ManyChars(NoneOf("\"")),'"');varattribute=name.And(Skip('=')).And(WS).And(quotedString).And(WS).Lbl_("attribute").Map((attrName,attrVal)=>newXAttribute(attrName,attrVal));varattributes=Many(attribute);FSharpFunc<CharStream<Unit>,Reply<XElement>>element=null;varelementStart=Skip('<').AndTry(name.Lbl("tag name")).And(attributes);FSharpFunc<CharStream<Unit>,Reply<string>>closingTag(stringtagName)=>Between("</",StringP(tagName).And(WS),">").Lbl_($"closing tag '</{tagName}>'");FSharpFunc<CharStream<Unit>,Reply<object>>textContent(stringleadingWS)=>NotEmpty(ManyChars(NoneOf("<")).Map(text=>leadingWS+text).Map(x=>(object)x).Lbl_("text content"));varchildElement=Rec(()=>element).Map(x=>(object)x).Lbl_("child element");objectEmptyContentToEmptyString(FSharpList<object>xs)=>xs.IsEmpty?(object)"":xs;varelementContent=Many(WS.WithSkipped().AndTry(ws=>Choice(textContent(ws),childElement))).Map(EmptyContentToEmptyString);FSharpFunc<CharStream<Unit>,Reply<XElement>>elementEnd(stringelName,FSharpList<XAttribute>elAttrs)=>Choice(Skip("/>").Return((object)null),Skip(">").And(elementContent).And(WS).AndL(closingTag(elName))).Map(elContent=>newXElement(elName,elContent,elAttrs));element=elementStart.And(elementEnd);varsimpleXmlParser=WS.And(element).And(WS).And(EOF);

Glob patterns

varglobParser=Many(Choice(Skip('?').Map(NFA.MakeAnyChar),Skip('*').Map(NFA.MakeAnyChar).Map(NFA.MakeZeroOrMore),Between('[',AnyChar.And(Skip('-')).And(AnyChar),']').Map(NFA.MakeCharRange),Skip('\\').And(AnyOf(@"?*[]\")).Map(NFA.MakeChar),AnyChar.Map(NFA.MakeChar))).And(EOF).Map(NFA.Concat).Map(proto=>proto(newFinal()));

This example contructs a non-deterministic finite automaton (NFA) during parsing and can be used for matching:

[Fact]publicvoidCanParseAndMatchGlobPattern()=>globParser.ParseString("The * syntax is easy?").Result.Matches("The glob syntax is easy!").ShouldBe(true);

Arithmetic expressions

FParsec.CSharp comes with a builder to constructFParsec.OperatorPrecedenceParsers:

varbasicExprParser=newOPPBuilder<Unit,int,Unit>().WithOperators(ops=>ops.AddInfix("+",1,(x,y)=>x+y).AddInfix("*",2,(x,y)=>x*y)).WithTerms(Natural).Build().ExpressionParser;varrecursiveExprParser=newOPPBuilder<Unit,int,Unit>().WithOperators(ops=>ops.AddInfix("+",1,(x,y)=>x+y).AddInfix("*",2,(x,y)=>x*y)).WithTerms(term=>Choice(Natural,Between('(',term,')'))).Build().ExpressionParser;

It also supports implicit operators:

varexprParser=WS.And(newOPPBuilder<Unit,int,Unit>().WithOperators(ops=>ops.AddInfix("+",10,WS,(x,y)=>x+y).AddInfix("-",10,WS,(x,y)=>x-y).AddInfix("*",20,WS,(x,y)=>x*y).AddInfix("/",20,WS,(x,y)=>x/y).AddPrefix("-",20, x=>-x).AddInfix("^",30,Associativity.Right,WS,(x,y)=>(int)Math.Pow(x,y)).AddPostfix("!",40,Factorial)).WithImplicitOperator(20,(x,y)=>x*y).WithTerms(term=>Choice(Natural.And(WS),Between(CharP('(').And(WS),term,CharP(')').And(WS)))).Build().ExpressionParser);

Simple regular expressions

Armed with theOPPBuilder and the NFA implementation used for the glob parser above we can even build a simple regex parser & matcher:

varsimpleRegexParser=Many(newOPPBuilder<Unit,NFA.ProtoState,Unit>().WithImplicitOperator(2,NFA.Connect).WithOperators(ops=>ops.AddPostfix("*",3,NFA.MakeZeroOrMore).AddPostfix("+",3,NFA.MakeOneOrMore).AddPostfix("?",3,NFA.MakeZeroOrOne).AddInfix("|",1,NFA.MakeAlternation)).WithTerms(matchExpr=>{vargroup=Between('(',Many(matchExpr),')');varwildcard=Skip('.');varcharMatch=NoneOf("*+?|()");returnChoice(group.Map(NFA.Concat),wildcard.Map(NFA.MakeAnyChar),charMatch.Map(NFA.MakeChar));}).Build().ExpressionParser).And(EOF).Map(NFA.Concat).Map(proto=>proto(newFinal()));
[Fact]publicvoidCanParseAndMatchRegex()=>simpleRegexParser.ParseString("The( simple)? .+ syntax is .*more tricky( and (complex|difficult|involved))+.").Result.Matches("The simple regex syntax is only a little bit more tricky and complex and involved!").ShouldBe(true);

Simple script language

This example implements a simple functional script language. It only knows one type (int) and is super inefficient, but it has lots of functional fu (e.g. lazy evaluation, partial application, lambdas, higher order functions, and function composition).

varnumber=Natural.Lbl("number");staticStringParsernotReserved(stringid)=>id=="let"||id=="in"||id=="match"?Zero<string>():Return(id);varidentifier1=Choice(Letter,CharP('_'));varidentifierRest=Choice(Letter,CharP('_'),CharP('\''),Digit);varidentifier=Purify(Many1Chars(identifier1,identifierRest)).AndTry(notReserved).Lbl("identifier");varparameters=Many(identifier,sep:WS1,canEndWithSep:true).Lbl("parameter list");ScriptParser?expression=null;varletBinding=Skip("let").AndR(WS1).And(identifier).And(WS).And(parameters).And(Skip('=')).And(WS).And(Rec(()=>expression).Lbl("'let' definition expression")).And(Skip("in")).And(WS1).And(Rec(()=>expression).Lbl("'let' body expression")).Map(Flat).Lbl("'let' binding");varlambda=Skip('\\').And(parameters).And(Skip("->")).And(WS).And(Rec(()=>expression).Lbl("lambda body")).Lbl("lambda");vardefaultCase=Skip('_').AndRTry(NotFollowedBy(identifierRest)).AndR(WS).Return(ScriptB.AlwaysMatches);varcaseValueExpr=Rec(()=>expression).Map(ScriptB.Matches);varcaseExpr=Skip('|').AndR(WS).And(defaultCase.Or(caseValueExpr).Lbl("case value expression")).And(Skip("=>")).And(WS).And(Rec(()=>expression).Lbl("case result expression")).Lbl("match case");varmatchExpr=Skip("match").AndR(WS1).And(Rec(()=>expression).Lbl("match value expression")).And(Many1(caseExpr)).Lbl("'match' expression");expression=newOPPBuilder<Unit,Script,Unit>().WithOperators(ops=>ops.AddInfix("+",10,WS,ScriptB.Lift2((x,y)=>x+y)).AddInfix("-",10,WS,ScriptB.Lift2((x,y)=>x-y)).AddInfix("*",20,WS,ScriptB.Lift2((x,y)=>x*y)).AddInfix("/",20,WS,ScriptB.Lift2((x,y)=>x/y)).AddInfix("%",20,WS,ScriptB.Lift2((x,y)=>x%y)).AddPrefix("-",20,ScriptB.Lift(x=>-x)).AddInfix(".",30,Associativity.Right,WS,ScriptB.Compose)).WithImplicitOperator(50,ScriptB.Apply).WithTerms(term=>Choice(letBinding.Map(ScriptB.BindVar),matchExpr.Map(ScriptB.Match),Between(CharP('(').And(WS),term,CharP(')').And(WS)),number.And(WS).Map(ScriptB.Return),identifier.And(WS).Map(ScriptB.Resolve),lambda.Map(ScriptB.Lambda)).Lbl("expression")).Build().ExpressionParser;varscriptParser=WS.And(expression).And(EOF);

This parser builds a function that can be invoked (with an empty arguments list and an empty "runtime environment") to execute the script:

[Fact]publicvoidFibonacciNumber()=>scriptParser.Run(@"        let fib n = match n            | 0 => 0            | 1 => 1            | _ => fib (n-1) + fib (n-2)        in fib 7").GetResult().Invoke(FSharpList<Script>.Empty,newDictionary<string,Script>()).ShouldBe(13);

Hints

Debugging

When you need to debug into your parser chain, use theDebug() combinator on any of your chain's parsers.

It takes twoActions:

  • Action<CharStream<Unit>> before: is run before the parser is applied,
  • Action<CharStream<Unit>, Reply<T>> after: is run after the parser was applied.

For instance, you can use emptyActions and place break points inside them:

varp=Digit.Debug(cs=>{},(cs,r)=>{}).And(Letter.Debug(cs=>{},(cs,r)=>{})).Debug(cs=>{},(cs,r)=>{});

Reducing namespace noise

The signatures of FParsec.CSharp's combinators can look pretty daunting. For instance, the signature of the combinatorMany() might appear like this in InteliSense or when hovering over its name:

Microsoft.FSharp.Core.FSharpFunc<FParsec.CharStream<Unit>,FParsec.Reply<Microsoft.FSharp.Collections.FSharpList<T>>>PrimitivesCS.Many<U,T>(Microsoft.FSharp.Core.FSharpFunc<FParsec.CharStream<Unit>,FParsec.Reply<T>>p)

Remember that Visual Studio hides namespaces in the UI that have ausing in the current file. So if you add the followingusings (even though not all of them are actually needed)...

usingFParsec;usingMicrosoft.FSharp.Collections;usingMicrosoft.FSharp.Core;

...then the Visual Studio UI will simplify the above signature to:

FSharpFunc<CharStream<Unit>,Reply<FSharpList<T>>>PrimitivesCS.Many<U,T>(FSharpFunc<CharStream<Unit>,Reply<T>>p)

Still a mouthful, but a little more readable nonetheless.

Aliasing awkward types

You can use type aliases to further simplify signatures in the Visual Studio UI and your code:

usingChars=FParsec.CharStream<Microsoft.FSharp.Core.Unit>;

Unfortunately C# does not support type aliases with open generics. Hence if you want to simplify the type of a parser you will have to do it for each of the possibleReply<T>s you are using:

usingStringParser=Microsoft.FSharp.Core.FSharpFunc<FParsec.CharStream<Microsoft.FSharp.Core.Unit>,FParsec.Reply<string>>;usingJsonParser=Microsoft.FSharp.Core.FSharpFunc<FParsec.CharStream<Microsoft.FSharp.Core.Unit>,FParsec.Reply<JObject>>;// ...

If you place your importusingsoutside, and your aliasusingsinside your namespace declaration then this will simplify your alias definitions:

usingSystem.Xml.Linq;usingFParsec;usingMicrosoft.FSharp.Core;namespaceTests{usingXElParser=FSharpFunc<CharStream<Unit>,Reply<XElement>>;// ...

Combining all suggestions yourusings could look like this for minimal noise:

#pragma warning disableIDE0065// Misplaced using directiveusingFParsec;usingFParsec.CSharp;usingMicrosoft.FSharp.Collections;usingMicrosoft.FSharp.Core;usingstaticFParsec.CSharp.PrimitivesCS;usingstaticFParsec.CSharp.CharParsersCS;namespaceMyParser{usingChars=CharStream<Unit>;usingCharParser=FSharpFunc<CharStream<Unit>,Reply<char>>;usingStringParser=FSharpFunc<CharStream<Unit>,Reply<string>>;// ...

Where is the FParsec functionx?

FParsec.CSharp does not mirror all of FParsec's functions exactly. A few are not wrapped and some are just named differently.

Below is a table that maps FParsec's parser functions, combinators, and helper functions to their FParsec.CSharp equivalent.

The typeFSharpFunc<CharStream<U>, Reply<T>> is shortened toP<T> for brewity.

Keep in mind that many predefined parsers and some of the combinators have a variant that supports parsing with user state. Those variants always have aU suffix in their name and are not listed in this table.

FParsecFParsec.CSharp
preturnP<T> Return(T)
pzeroP<T> Zero<T>()
(>>=)P<TR> P<T1>.And(Func<T1, P<TR>>),
P<TR> P<Unit>.And(Func<P<TR>>) if left side returnsUnit,
P<TR> P<(T1,T2)>.And(Func<T1, T2, P<TR>>) deconstructs left tuple result,
P<TR> P<(T1,T2,T3)>.And(Func<T1, T2, T3, P<TR>>) deconstructs left 3-tuple result
(>>%)P<T2> P<T1>.Return(T2)
(>>.)P<T2> P<T1>.AndR(P<T2>) skips left explicitly,
P<T> P<Unit>.And(P<T>) skips left implicitly when it returnsUnit
(.>>)P<T1> P<T1>.AndL(P<T2>) skips right explicitly,
P<T> P<T>.And(P<Unit>) skips right implicitly when it returnsUnit
(.>>.)P<(T1,T2)> P<T1>.And(P<T2>) if neither side returnsUnit,
P<(Unit,Unit)> P<Unit>.And_(P<Unit>) if any side returnsUnit
(|>>)P<TR> P<T1>.Map(Func<T1, TR>),
P<TR> P<Unit>.Map(Func<TR>) if left side returnsUnit,
P<TR> P<(T1,T2)>.Map(Func<T1, T2, TR>) deconstructs left tuple result,
P<TR> P<(T1,T2,T3)>.Map(Func<T1, T2, T3, TR>) deconstructs left 3-tuple result
betweenP<T2> Between(P<T1>, P<T2>, P<T3>) (different argument order)
pipe2P<TR> Pipe(P<T1>, P<T2>, Func<T1, T2, TR>)
pipe3P<TR> Pipe(P<T1>, P<T2>, P<T3>, Func<T1, T2, T3, TR>)
pipe4P<TR> Pipe(P<T1>, P<T2>, P<T3>, P<T4>, Func<T1, T2, T3, T4, TR>)
pipe5P<TR> Pipe(P<T1>, P<T2>, P<T3>, P<T4>, P<T5>, Func<T1, T2, T3, T4, T5, TR>)
(<|>)P<T> P<T>.Or(P<T>)
choiceP<T> Choice(params P<T>[])
choiceLP<T> Choice(string, params P<T>[])
(<|>%)P<T> P<T>.Or(T)
optP<FSharpOption<T>> Opt_(P<T>),
P<T> Opt(P<T>) unwraps theFSharpOption<T>,
P<T> Opt(P<T>, T) unwraps theFSharpOption<T> with default value
optionalP<Unit> Optional(P<T>)
attemptP<T> Try(P<T>)
(>>=?)P<T2> P<T1>.AndTry(Func<T1, P<T2>>)
(>>?)P<T2> P<T1>.AndRTry(P<T2>) skips left explicitly,
P<T> P<Unit>.AndTry(P<T>) skips left implicitly when it returnsUnit
(.>>?)P<T1> P<T1>.AndLTry(P<T2>) skips right explicitly,
P<T> P<T>.AndTry(P<Unit>) skips right implicitly when it returnsUnit
(.>>.?)P<(T1,T2)> P<T1>.AndTry(P<T2>) if neither side returnsUnit,
P<(Unit,Unit)> P<Unit>.AndTry_(P<Unit>) if any side returnsUnit
notEmptyP<T> NotEmpty(P<T>)
followedByP<Unit> FollowedBy(P<T>)
followedByLP<Unit> FollowedBy(P<T>, string)
notFollowedByP<Unit> NotFollowedBy(P<T>)
notFollowedByLP<Unit> NotFollowedBy(P<T>, string)
lookAheadP<T> LookAhead(P<T>)
(<?>)P<T> P<T>.Label(string)
(<??>)P<T> P<T>.Label_(string)
failP<T> Fail(string)
failFatallyP<T> FailFatally(string)
tuple2P<(T1,T2)> Tuple(P<T1>, P<T2>)
tuple3P<(T1,T2,T3)> Tuple(P<T1>, P<T2>, P<T3>)
tuple4P<(T1,T2,T3,T4)> Tuple(P<T1>, P<T2>, P<T3>, P<T4>)
tuple5P<(T1,T2,T3,T4,T5)> Tuple(P<T1>, P<T2>, P<T3>, P<T4>, P<T5>)
parrayP<T[]> Array(int, P<T>)
skipArrayP<Unit> SkipArray(int, P<T>)
manyP<FSharpList<T>> Many(P<T>)
skipManyP<Unit> SkipMany(P<T>)
many1P<FSharpList<T>> Many1(P<T>)
skipMany1P<Unit> SkipMany1(P<T>)
sepByP<FSharpList<T>> Many(P<T>, P<TSep>)
skipSepByP<Unit> SkipMany(P<T>, P<TSep>)
sepBy1P<FSharpList<T>> Many1(P<T>, P<TSep>)
skipSepBy1P<Unit> SkipMany1(P<T>, P<TSep>)
sepEndByP<FSharpList<T>> Many(P<T>, P<TSep>, canEndWithSep: true)
skipSepEndByP<Unit> SkipMany(P<T>, P<TSep>, canEndWithSep: true)
sepEndBy1P<FSharpList<T>> Many1(P<T>, P<TSep>, canEndWithSep: true)
skipSepEndBy1P<Unit> SkipMany1(P<T>, P<TSep>, canEndWithSep: true)
manyTillP<FSharpList<T>> ManyTill(P<T>, P<TEnd>)
skipManyTillP<Unit> SkipManyTill(P<T>, P<TEnd>)
many1TillP<FSharpList<T>> Many1Till(P<T>, P<TEnd>)
skipMany1TillP<Unit> SkipMany1Till(P<T>, P<TEnd>)
chainl1P<T> ChainL(P<T>, P<Func<T, T, T>>)
chainlP<T> ChainL(P<T>, P<Func<T, T, T>>, T)
chainr1P<T> ChainR(P<T>, P<Func<T, T, T>>)
chainrP<T> ChainR(P<T>, P<Func<T, T, T>>, T)
runParserOnStringParserResult<T> P<T>.RunOnString(string, string)
runParserOnSubstringParserResult<T> P<T>.RunOnString(string, int, int, string)
runParserOnStreamParserResult<T> P<T>.RunOnStream(Stream, Encoding, string)
runParserOnFileParserResult<T> P<T>.RunOnFile(string, Encoding)
runParserResult<T> P<T>.Run(string)
getPositionP<Position> PositionP
getUserStateP<U> GetUserState<U>()
setUserStateP<Unit> SetUserState<U>(U)
updateUserStateP<Unit> UpdateUserState<U>(Func<U, U>)
userStateSatisfiesP<Unit> UserStateSatisfies<U>(Func<U, bool>)
pcharP<char> CharP(char)
skipCharP<Unit> Skip(char)
charReturnP<T> CharP(char, T)
anyCharP<char> AnyChar
skipAnyCharP<Unit> SkipAnyChar
satisfyP<char> CharP(Func<char, bool>)
skipSatisfyP<Unit> Skip(Func<char, bool>)
satisfyLP<char> CharP(Func<char, bool>, string)
skipSatisfyLP<Unit> Skip(Func<char, bool>, string)
anyOfP<char> AnyOf(IEnumerable<char>)
skipAnyOfP<Unit> SkipAnyOf(IEnumerable<char>)
noneOfP<char> NoneOf(IEnumerable<char>)
skipNoneOfP<Unit> SkipNoneOf(IEnumerable<char>)
asciiUpperP<char> AsciiUpper
asciiLowerP<char> AsciiLower
asciiLetterP<char> AsciiLetter
upperP<char> Upper
lowerP<char> Lower
letterP<char> Letter
digitP<char> Digit
hexP<char> Hex
octalP<char> Octal
isAnyOfnot implemented
isNoneOfnot implemented
isAsciiUpperbool IsAsciiUpper(char)
isAsciiLowerbool IsAsciiLower(char)
isAsciiLetterbool IsAsciiLetter(char)
isUpperbool IsUpper(char)
isLowerbool IsLower(char)
isLetterbool IsLetter(char)
isDigitbool IsDigit(char)
isHexbool IsHex(char)
isOctalbool IsOctal(char)
tabP<char> Tab
newlineP<char> Newline
skipNewlineP<Unit> SkipNewline
newlineReturnP<T> NewlineReturn(T)
unicodeNewlineP<Unit> UnicodeNewline
skipUnicodeNewlineP<Unit> SkipUnicodeNewline
unicodeNewlineReturnP<T> UnicodeNewlineReturn(T x)
spacesP<Unit> Spaces
spaces1P<Unit> Spaces1
unicodeSpacesP<Unit> UnicodeSpaces
unicodeSpaces1P<Unit> UnicodeSpaces1
eofP<Unit> EOF
pstringP<string> StringP(string)
skipStringP<Unit> Skip(string)
stringReturnP<T> StringP(string, T)
pstringCIP<string> StringCI(string)
skipStringCIP<Unit> SkipCI(string)
stringCIReturnP<T> StringP(string, T)
anyStringP<string> AnyString(int)
skipAnyStringP<Unit> SkipAnyString(int)
restOfLineP<string> RestOfLine(bool)
skipRestOfLineP<Unit> SkipRestOfLine(bool)
charsTillStringReply<string> CharsTillString(string, int, bool)
skipCharsTillStringReply<Unit> SkipCharsTillString(string, int, bool)
charsTillStringCIReply<string> CharsTillStringCI(string, int, bool)
skipCharsTillStringCIReply<Unit> SkipCharsTillStringCI(string, int, bool)
manySatisfyP<string> ManyChars(Func<char, bool>)
manySatisfy2P<string> ManyChars(Func<char, bool>, Func<char, bool>)
skipManySatisfyP<Unit> SkipManyChars(Func<char, bool>)
skipManySatisfy2P<Unit> SkipManyChars(Func<char, bool>, Func<char, bool>)
many1SatisfyP<string> Many1Chars(Func<char, bool>)
many1Satisfy2P<string> Many1Chars(Func<char, bool>, Func<char, bool>)
skipMany1SatisfyP<Unit> SkipMany1Chars(Func<char, bool>)
skipMany1Satisfy2P<Unit> SkipMany1Chars(Func<char, bool>, Func<char, bool>)
many1SatisfyLP<string> Many1Chars(Func<char, bool>, string)
many1Satisfy2LP<string> Many1Chars(Func<char, bool>, Func<char, bool>, string)
skipMany1SatisfyLP<Unit> SkipMany1Chars(Func<char, bool>, string)
skipMany1Satisfy2LP<Unit> SkipMany1Chars(Func<char, bool>, Func<char, bool>, string)
manyMinMaxSatisfyP<string> ManyChars(Func<char, bool>, int, int)
manyMinMaxSatisfy2P<string> ManyChars(Func<char, bool>, Func<char, bool>, int, int)
skipManyMinMaxSatisfyP<Unit> SkipManyChars(Func<char, bool>, int, int)
skipManyMinMaxSatisfy2P<Unit> SkipManyChars(Func<char, bool>, Func<char, bool>, int, int)
manyMinMaxSatisfyLP<string> ManyChars(Func<char, bool>, int, int, string)
manyMinMaxSatisfy2LP<string> ManyChars(Func<char, bool>, Func<char, bool>, int, int, string)
skipManyMinMaxSatisfyLP<Unit> SkipManyChars(Func<char, bool>, int, int, string)
skipManyMinMaxSatisfy2LP<Unit> SkipManyChars(Func<char, bool>, Func<char, bool>, int, int, string)
regexReply<string> Regex(string)
regexLReply<string> Regex(string, string)
identifiernot implemented
manyCharsP<string> ManyChars(P<char>)
manyChars2P<string> ManyChars(P<char>, P<char>)
many1CharsP<string> Many1Chars(P<char>)
many1Chars2P<string> Many1Chars(P<char>, P<char>)
manyCharsTillP<string> ManyCharsTill(P<char>, P<TEnd>)
manyCharsTill2P<string> ManyCharsTill(P<char>, P<char>, P<TEnd>)
manyCharsTillApplyP<T> ManyCharsTill(P<char>, P<TEnd>, Func<string, TEnd, T>)
manyCharsTillApply2P<T> ManyCharsTill(P<char>, P<char>, P<TEnd>, Func<string, TEnd, T>)
many1CharsTillP<string> Many1CharsTill(P<char>, P<TEnd>)
many1CharsTill2P<string> Many1CharsTill(P<char>, P<char>, P<TEnd>)
many1CharsTillApplyP<T> Many1CharsTill(P<char>, P<TEnd>, Func<string, TEnd, T>)
many1CharsTillApply2P<T> Many1CharsTill(P<char>, P<char>, P<TEnd>, Func<string, TEnd, T>)
manyStringsP<string> ManyStrings(P<string>)
manyStrings2not implemented
many1StringsP<string> Many1Strings(P<string>)
many1Strings2not implemented
stringsSepByManyStrings(P<string>, P<String>)
stringsSepBy1Many1Strings(P<string>, P<String>)
skippedP<string> P<Unit>.WithSkipped()
withSkippedStringP<T2> P<T1>.WithSkipped(Func<string, T1, T2>),
P<(string,T)> P<T>.WithSkipped()
numberLiteralP<NumberLiteral> NumberLiteral(NumberLiteralOptions, string)
numberLiteralEReply<NumberLiteral> NumberLiteralE(NumberLiteralOptions, ErrorMessageList, CharStream<Unit>)
pfloatP<double> Float
pint64P<long> Long
pint32P<int> Int
pint16P<short> Short
pint8P<sbyte> Byte
puint64P<ulong> ULong
puint32P<uint> UInt
puint16P<ushort> UShort
puint8P<byte> UByte
notFollowedByEofP<Unit> NotFollowedByEOF
followedByNewlineP<Unit> FollowedByNewline
notFollowedByNewlineP<Unit> NotFollowedByNewline
followedByStringP<Unit> FollowedBy(string)
followedByStringCIP<Unit> FollowedByCI(foo)
notFollowedByStringP<Unit> NotFollowedBy(string)
notFollowedByStringCIP<Unit> NotFollowedByCI(string)
nextCharSatisfiesP<Unit> NextCharSatisfies(Func<char, bool>)
nextCharSatisfiesNotP<Unit> NextCharSatisfiesNot(Func<char, bool>)
next2CharsSatisfyP<Unit> Next2CharsSatisfy(Func<char, char, bool>)
next2CharsSatisfyNotP<Unit> Next2CharsSatisfyNot(Func<char, char, bool>)
previousCharSatisfiesP<Unit> PreviousCharSatisfies(Func<char, bool>)
previousCharSatisfiesNotP<Unit> PreviousCharSatisfiesNot(Func<char, bool>)
foldCasestring FoldCase(string)
normalizeNewlinesstring NormalizeNewlines(string)
floatToHexStringstring DoubleToHexString(double)
floatOfHexStringdouble DoubleOfHexString(string)
float32ToHexStringstring FloatToHexString(double)
float32OfHexStringdouble FloatOfHexString(string)

Credits

This library is based on the following works:


[8]ページ先頭

©2009-2025 Movatter.jp