public void ChoiceTest() { Combinator.Choice <Char, Char>(Enumerable.Empty <Parser <Char, Char> >()) .Run("inputString".AsStream()) .Case( failure: (restStream, _) => { /* OK */ }, success: (restStream, _) => Assert.Fail()); Combinator.Choice <Char, Char>(new[] { Parser.Fail <Char, Char>("Failure"), Parser.Fail <Char, Char>("Failure") }) .Run("inputString".AsStream()) .Case( failure: (restStream, _) => { /* OK */ }, success: (restStream, _) => Assert.Fail()); Combinator.Choice <Char, Char>(new[] { Chars.Satisfy('i'), Parser.Fail <Char, Char>("Failure") }) .Run("inputString".AsStream()) .Case( failure: (restStream, _) => Assert.Fail(), success: (restStream, value) => { Assert.AreEqual('i', value); Assert.True(restStream.Current.HasValue); Assert.AreEqual('n', restStream.Current.Value.Item0); Assert.AreEqual(1, restStream.Current.Value.Item1.Line); Assert.AreEqual(2, restStream.Current.Value.Item1.Column); }); Combinator.Choice <Char, Char>(new[] { Parser.Fail <Char, Char>("Failure"), Chars.Satisfy('i') }) .Run("inputString".AsStream()) .Case( failure: (restStream, _) => Assert.Fail(), success: (restStream, value) => { Assert.AreEqual('i', value); Assert.True(restStream.Current.HasValue); Assert.AreEqual('n', restStream.Current.Value.Item0); Assert.AreEqual(1, restStream.Current.Value.Item1.Line); Assert.AreEqual(2, restStream.Current.Value.Item1.Column); }); Combinator.Choice <Char, Char>(new[] { Parser.Return <Char, Char>('a'), Parser.Return <Char, Char>('b'), }) .Run("inputString".AsStream()) .Case( failure: (restStream, _) => Assert.Fail(), success: (restStream, value) => { Assert.AreEqual('a', value); }); }
private static void InitializeV04Parser() { var space = Chars.OneOf('\t', ' ').Ignore(); var spaces = space.Many0().Ignore(); var spacesOrNewlines = Chars.OneOf('\t', ' ', '\r', '\n').Many0().Ignore(); var newline = Combinator.Choice( Chars.Sequence("\r\n"), Chars.Sequence("\r"), Chars.Sequence("\n") ).Ignore(); var newlineOrEof = newline.Or(Chars.EndOfInput()).Ignore(); var notNewlineChar = Chars.NoneOf('\r', '\n'); var comment = notNewlineChar.Many0() .Between(Chars.Satisfy('#').Ignore(), newlineOrEof) .Select(c => new Comment(c)); var newlineOrComment = newlineOrEof.Select(_ => (Comment)null).Or(comment); var escaped = Chars.Satisfy('\\').Bindr(Combinator.Choice( Chars.Satisfy('b').Select(_ => "\b"), Chars.Satisfy('t').Select(_ => "\t"), Chars.Satisfy('n').Select(_ => "\n"), Chars.Satisfy('f').Select(_ => "\f"), Chars.Satisfy('r').Select(_ => "\r"), Chars.Sequence("\""), //Chars.Sequence("/"), Chars.Sequence("\\"), Chars.Satisfy('u').Bindr(Chars.Hex().Repeat(4)) .Or(Chars.Satisfy('U').Bindr(Chars.Hex().Repeat(8))) .Select(c => char.ConvertFromUtf32(Convert.ToInt32(string.Concat(c), 16))) )); var newlineEscape = Combinator.Choice(Chars.Sequence("\\\r\n"), Chars.Sequence("\\\r"), Chars.Sequence("\\\n")) .Bindr(spacesOrNewlines).Select(_ => ""); var basicString = Chars.NoneOf('\r', '\n', '"', '\\').Select(c => c.ToString()) .Or(escaped).Many0().Between(Chars.Satisfy('"').Ignore(), Chars.Satisfy('"').Ignore()) .Select(c => new TomlValue(TomlItemType.BasicString, c.Unfold())); var threeQuotes = Chars.Sequence("\"\"\"").Ignore(); var multilineBasicStringChar = Combinator.Choice( Chars.NoneOf('\\', '"').Select(c => c.ToString()), escaped, newlineEscape ); var multilineBasicString = Combinator.Choice( multilineBasicStringChar, Combinator.Sequence(Chars.Sequence('"'), multilineBasicStringChar).Select(Unfold), Combinator.Sequence(Chars.Sequence("\"\""), multilineBasicStringChar).Select(Unfold) ).Many0().Between(threeQuotes, threeQuotes) .Select(c => new TomlValue(TomlItemType.MultilineBasicString, c.Unfold().RemoveFirstNewLine())); var literalString = Chars.NoneOf('\r', '\n', '\'').Many0() .Between(Chars.Satisfy('\'').Ignore(), Chars.Satisfy('\'').Ignore()) .Select(c => new TomlValue(TomlItemType.LiteralString, string.Concat(c))); var threeLiteralQuotes = Chars.Sequence("'''").Ignore(); var multilineLiteralStringChar = Chars.NoneOf('\''); var multilineLiteralString = Combinator.Choice( multilineLiteralStringChar.Select(c => c.ToString()), Combinator.Sequence(Chars.Satisfy('\''), multilineLiteralStringChar), Chars.Sequence("''").Bindr(multilineLiteralStringChar).Select(c => string.Concat("''", c)) ).Many0().Between(threeLiteralQuotes, threeLiteralQuotes) .Select(c => new TomlValue(TomlItemType.MultilineLiteralString, c.Unfold().RemoveFirstNewLine())); var sign = Chars.OneOf('+', '-').Optional().Select(o => o.Case(() => "", c => c.ToString())); var digit = Chars.Satisfy(c => c >= '0' && c <= '9'); var digits = digit.Many1(); var digitsWithUnderscores = digits.SepBy1(Chars.Satisfy('_').Ignore()).Select(Unfold); var signedDigitsWithUnderscores = sign.Append(digitsWithUnderscores); var integer = signedDigitsWithUnderscores.Select(x => new TomlValue(TomlItemType.Integer, string.Concat(x))); var floatv = Combinator.Choice( signedDigitsWithUnderscores.Append(Chars.Satisfy('.')).Append(digitsWithUnderscores), signedDigitsWithUnderscores.Append(Chars.Sequence(".").Append(digitsWithUnderscores).Optional()) .Append(Chars.OneOf('e', 'E')).Append(signedDigitsWithUnderscores) ).Select(x => new TomlValue(TomlItemType.Float, string.Concat(x))); var boolv = Chars.Sequence("true").Select(_ => true) .Or(Chars.Sequence("false").Select(_ => false)) .Select(b => new TomlValue(TomlItemType.Boolean, b)); var hyphen = Chars.Satisfy('-'); var colon = Chars.Satisfy(':'); var twoDigits = digit.Repeat(2); var datetime = digit.Repeat(4) // year .Append(hyphen).Append(twoDigits) // month .Append(hyphen).Append(twoDigits) // day .Append( Chars.Sequence("T").Append(twoDigits) // hour .Append(colon).Append(twoDigits) // minute .Append(colon).Append(twoDigits) // second .Append( Chars.Sequence(".").Append(digits).Optional() ) .Append( Chars.Sequence("Z") .Or( Chars.Sequence("+").Or(Chars.Sequence("-")) .Append(twoDigits) .Append(Chars.Satisfy(':')) .Append(twoDigits) ) .Optional() ) .Optional() ) .Select(x => new TomlValue(TomlItemType.Datetime, XmlConvert.ToDateTimeOffset(string.Concat(x)))); Parser <char, TomlValue> arrayRef = null; var array = Delayed.Return(() => arrayRef); Parser <char, TomlValue> inlineTableRef = null; var inlineTable = Delayed.Return(() => inlineTableRef); var key = Chars.Satisfy(c => (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_' || c == '-') .Many1().Or(basicString.Select(x => (string)x.Value)); var value = Combinator.Choice( multilineBasicString, basicString, multilineLiteralString, literalString, // 順番大事 datetime, floatv, integer, boolv, Combinator.Lazy(array), Combinator.Lazy(inlineTable) ); inlineTableRef = key.Between(spaces, spaces) .Pipe( Chars.Satisfy('=').Bindr(value.Between(spaces, spaces)), (k, v) => new KeyValue(k, v, null) ) .SepEndBy0(Chars.Satisfy(',').Ignore()) .Between(Chars.Satisfy('{').Ignore(), spaces.SeqIgnore(Chars.Satisfy('}'))) .Select(x => new TomlValue(TomlItemType.InlineTable, x)); var comments = comment.Between(spacesOrNewlines, spacesOrNewlines).Many0(); var comma = Chars.Satisfy(',').Between(spacesOrNewlines, spacesOrNewlines).Ignore(); Func <Parser <char, TomlValue>, Parser <char, TomlValue> > createArrayParser = p => Chars.Satisfy('[').Bindr( from i in (from b in comments from v in p.Between(spacesOrNewlines, spacesOrNewlines) from a in comments select new ArrayItem(v, b, a) ).SepEndBy0(comma) from c in comments.Bindl(Chars.Satisfy(']')) select new TomlValue(TomlItemType.Array, i.Concat(new[] { new ArrayItem(null, null, c) })) ); arrayRef = Combinator.Choice( createArrayParser(Combinator.Choice( multilineBasicString, basicString, multilineLiteralString, literalString)), // 順番大事 createArrayParser(datetime), createArrayParser(floatv), createArrayParser(integer), createArrayParser(boolv), createArrayParser(Combinator.Lazy(array)), createArrayParser(Combinator.Lazy(inlineTable)) ); var tableName = key.Between(spaces, spaces).SepBy1(Chars.Satisfy('.').Ignore()); var startTable = tableName.Between( spacesOrNewlines.SeqIgnore(Chars.Satisfy('[')), Chars.Satisfy(']').SeqIgnore(spaces) ); var startTableLine = from t in startTable from c in newlineOrComment select new TableInfo(t, c, false); var startArrayOfTable = tableName.Between( spacesOrNewlines.SeqIgnore(Chars.Sequence("[[")), Chars.Sequence("]]").SeqIgnore(spaces) ); var startArrayOfTableLine = from t in startArrayOfTable from c in newlineOrComment select new TableInfo(t, c, true); var keyValue = from k in key.Between(spacesOrNewlines, spaces) from v in Chars.Satisfy('=').Bindr(value.Between(spaces, spaces)) from c in newlineOrComment select(TableNode) new KeyValue(k, v, c); var nodes = Combinator.Choice(keyValue, spacesOrNewlines.Bindr(comment)).Many0(); var table = from t in Combinator.Choice(startTableLine, startArrayOfTableLine) from c in nodes select new Table(t, c); v04Parser = from r in nodes from t in table.Many0().Bindl(spacesOrNewlines) select new ParseResult(r, t); }
public static Parser <TToken, TToken> OneOf <TToken>(IEnumerable <TToken> candidates) where TToken : IEquatable <TToken> { return(Combinator.Choice(candidates.Select(Prims.Satisfy <TToken>))); }
public static Parser <Char, Char> OneOf(IEnumerable <Char> candidates) { return(Combinator.Choice(candidates.Select(Chars.Char))); }
private static Parser <TToken, Unit> DiscardBefore <TToken, T>(Parser <TToken, T> p) => Combinator.Choice(p.Lookahead().Ignore(), from junk in Prims.Any <TToken>().Ignore() from pAhead in DiscardBefore(p) select junk);
internal StandardGrammar() { this._isReadOnly = false; var newline = Combinator.Choice( Chars.Sequence("\r\n"), Chars.OneOf('\r', '\n', '\x85', '\u2028', '\u2029') .Select(EnumerableEx.Return) ).Select(_ => Environment.NewLine); var punctuation = Chars.OneOf('"', '\'', '(', ')', ',', '.', ':', ';', '[', ']', '`', '{', '}'); Parser <Char, YacqExpression> expressionRef = null; var expression = new Lazy <Parser <Char, YacqExpression> >( () => stream => expressionRef(stream) ); this.Add("yacq", "expression", g => expression.Value); // Comments { this.Add("comment", "eol", g => Prims.Pipe( ';'.Satisfy(), newline.Not().Right(Chars.Any()).Many(), newline.Ignore().Or(Chars.Eof()), (p, r, s) => (YacqExpression)YacqExpression.Ignore() )); Parser <Char, YacqExpression> blockCommentRef = null; Parser <Char, YacqExpression> blockCommentRestRef = null; var blockComment = new Lazy <Parser <Char, YacqExpression> >( () => stream => blockCommentRef(stream) ); var blockCommentRest = new Lazy <Parser <Char, YacqExpression> >( () => stream => blockCommentRestRef(stream) ); var blockCommentPrefix = Chars.Sequence("#|"); var blockCommentSuffix = Chars.Sequence("|#"); blockCommentRef = blockCommentPrefix .Right(blockCommentRest.Value.Many()) .Left(blockCommentSuffix) .Select(_ => (YacqExpression)YacqExpression.Ignore()); blockCommentRestRef = blockCommentPrefix.Not() .Right(blockCommentSuffix.Not()) .Right(Chars.Any()) .Select(_ => (YacqExpression)YacqExpression.Ignore()) .Or(blockComment.Value); this.Add("comment", "block", g => blockComment.Value); this.Add("comment", "expression", g => Prims.Pipe( Chars.Sequence("#;"), g["yacq", "expression"], (p, r) => (YacqExpression)YacqExpression.Ignore() )); this.Add("yacq", "comment", g => Combinator.Choice(g["comment"])); } this.Add("yacq", "ignore", g => Combinator.Choice( this.Get["yacq", "comment"].Ignore(), Chars.Space().Ignore(), newline.Ignore() ).Many().Select(_ => (YacqExpression)YacqExpression.Ignore())); // Texts this.Add("term", "text", g => SetPosition( Chars.OneOf('\'', '\"', '`') .SelectMany(q => q.Satisfy() .Not() .Right('\\'.Satisfy() .Right(q.Satisfy()) .Or(Chars.Any()) ) .Many() .Left(q.Satisfy()) .Select(cs => cs.StartWith(q).EndWith(q)) ) .Select(cs => (YacqExpression)YacqExpression.Text(new String(cs.ToArray()))) )); // Numbers { var numberPrefix = Chars.OneOf('+', '-'); var numberSuffix = Combinator.Choice( Chars.Sequence("ul"), Chars.Sequence("UL"), Chars.OneOf('D', 'F', 'L', 'M', 'U', 'd', 'f', 'l', 'm', 'u') .Select(EnumerableEx.Return) ); var digit = '_'.Satisfy().Many().Right(Chars.Digit()); var hexPrefix = Chars.Sequence("0x"); var hex = '_'.Satisfy().Many().Right(Chars.Hex()); var octPrefix = Chars.Sequence("0o"); var oct = '_'.Satisfy().Many().Right(Chars.Oct()); var binPrefix = Chars.Sequence("0b"); var bin = '_'.Satisfy().Many().Right(Chars.OneOf('0', '1')); var fraction = Prims.Pipe( '.'.Satisfy(), digit.Many(1), (d, ds) => ds.StartWith(d) ); var exponent = Prims.Pipe( Chars.OneOf('E', 'e'), Chars.OneOf('+', '-').Maybe(), digit.Many(1), (e, s, ds) => ds .If(_ => s.Exists(), _ => _.StartWith(s.Perform())) .StartWith(e) ); this.Add("term", "number", g => Combinator.Choice( SetPosition(Prims.Pipe( binPrefix, bin.Many(1), numberSuffix.Maybe(), (p, n, s) => (YacqExpression)YacqExpression.Number( new String(p.Concat(n).If( _ => s.Exists(), cs => cs.Concat(s.Perform()) ).ToArray()) ) )), SetPosition(Prims.Pipe( octPrefix, oct.Many(1), numberSuffix.Maybe(), (p, n, s) => (YacqExpression)YacqExpression.Number( new String(p.Concat(n).If( _ => s.Exists(), cs => cs.Concat(s.Perform()) ).ToArray()) ) )), SetPosition(Prims.Pipe( hexPrefix, hex.Many(1), numberSuffix.Maybe(), (p, n, s) => (YacqExpression)YacqExpression.Number( new String(p.Concat(n).If( _ => s.Exists(), cs => cs.Concat(s.Perform()) ).ToArray()) ) )), SetPosition( numberPrefix.Maybe().SelectMany(p => digit.Many(1).SelectMany(i => fraction.Maybe().SelectMany(f => exponent.Maybe().SelectMany(e => numberSuffix.Maybe().Select(s => (YacqExpression)YacqExpression.Number(new String(EnumerableEx.Concat( i.If(_ => p.Exists(), _ => _.StartWith(p.Perform())), f.Otherwise(Enumerable.Empty <Char>), e.Otherwise(Enumerable.Empty <Char>), s.Otherwise(Enumerable.Empty <Char>) ).ToArray())) ) ) ) ) ) ) )); } // Lists this.Add("term", "list", g => SetPosition( g["yacq", "expression"] .Between(g["yacq", "ignore"], g["yacq", "ignore"]) .Many() .Between('('.Satisfy(), ')'.Satisfy()) .Select(es => (YacqExpression)YacqExpression.List(es)) )); // Vectors this.Add("term", "vector", g => SetPosition( g["yacq", "expression"] .Between(g["yacq", "ignore"], g["yacq", "ignore"]) .Many() .Between('['.Satisfy(), ']'.Satisfy()) .Select(es => (YacqExpression)YacqExpression.Vector(es)) )); // Lambda Lists this.Add("term", "lambdaList", g => SetPosition( g["yacq", "expression"] .Between(g["yacq", "ignore"], g["yacq", "ignore"]) .Many() .Between('{'.Satisfy(), '}'.Satisfy()) .Select(es => (YacqExpression)YacqExpression.LambdaList(es)) )); // Quotes this.Add("term", "quote", g => SetPosition( Prims.Pipe( Chars.Sequence("#'"), g["yacq", "expression"], (p, e) => (YacqExpression)YacqExpression.List(YacqExpression.Identifier("quote"), e) ) )); // Quasiquotes this.Add("term", "quasiquote", g => SetPosition( Prims.Pipe( Chars.Sequence("#`"), g["yacq", "expression"], (p, e) => (YacqExpression)YacqExpression.List(YacqExpression.Identifier("quasiquote"), e) ) )); // Unquote-Splicings this.Add("term", "unquoteSplicing", g => SetPosition( Prims.Pipe( Chars.Sequence("#,@"), g["yacq", "expression"], (p, e) => (YacqExpression)YacqExpression.List(YacqExpression.Identifier("unquote-splicing"), e) ) )); // Unquotes this.Add("term", "unquote", g => SetPosition( Prims.Pipe( Chars.Sequence("#,"), g["yacq", "expression"], (p, e) => (YacqExpression)YacqExpression.List(YacqExpression.Identifier("unquote"), e) ) )); // Identifiers this.Add("term", "identifier", g => Combinator.Choice( SetPosition('.'.Satisfy() .Many(1) .Select(cs => (YacqExpression)YacqExpression.Identifier(new String(cs.ToArray()))) ), SetPosition(':'.Satisfy() .Many(1) .Select(cs => (YacqExpression)YacqExpression.Identifier(new String(cs.ToArray()))) ), SetPosition(Chars.Digit() .Not() .Right(Chars.Space() .Or(punctuation) .Not() .Right(Chars.Any()) .Many(1) ) .Select(cs => (YacqExpression)YacqExpression.Identifier(new String(cs.ToArray()))) ) )); // Terms this.Add("yacq", "term", g => Combinator.Choice(g["term"]) .Between(g["yacq", "ignore"], g["yacq", "ignore"]) ); // Infix Dots this.Add("infix", "dot", g => Prims.Pipe( g["yacq", "term"], '.'.Satisfy() .Right(g["yacq", "term"]) .Many(), (h, t) => t.Aggregate(h, (l, r) => (YacqExpression)YacqExpression.List(YacqExpression.Identifier("."), l, r) ) )); // Infix Colons this.Add("infix", "colon", g => Prims.Pipe( g["infix", "dot"], ':'.Satisfy() .Right(g["infix", "dot"]) .Many(), (h, t) => t.Aggregate(h, (l, r) => (YacqExpression)YacqExpression.List(YacqExpression.Identifier(":"), l, r) ) )); expressionRef = this.Get["infix"].Last(); this.Set.Default = g => g["yacq", "expression"]; this._isReadOnly = true; }