/// <summary> /// Imperative implementation of chain, which in a non-stack overflow utopia /// would look similar to this: /// /// from x in ps[index] /// from y in chaini(ps, index + 1) /// select x.Cons(y); /// /// </summary> public static Parser <Seq <T> > chaini <T>(Seq <Parser <T> > ps) => ps.IsEmpty ? unexpected <Seq <T> >("chain parser with 0 items") : inp => { if (ps.Count == 1) { return(ps.Head.Map(x => x.Cons())(inp)); } var current = inp; List <T> results = new List <T>(); ParserError error = null; ParserResult <T> last = null; int count = ps.Count; foreach (var p in ps) { count--; var t = p(current); if (last == null) { // cerr if (t.Tag == ResultTag.Consumed && t.Reply.Tag == ReplyTag.Error) { return(ConsumedError <Seq <T> >(t.Reply.Error)); } // eerr else if (t.Tag == ResultTag.Empty && t.Reply.Tag == ReplyTag.Error) { return(EmptyError <Seq <T> >(t.Reply.Error)); } // c*k else if (t.Tag == ResultTag.Consumed && t.Reply.Tag == ReplyTag.OK) { results.Add(t.Reply.Result); last = t; error = t.Reply.Error; current = t.Reply.State; } // eok else //if (t.Tag == ResultTag.Empty && t.Reply.Tag == ReplyTag.OK) { results.Add(t.Reply.Result); last = t; error = t.Reply.Error; } } else { if (last.Tag == ResultTag.Consumed) { // c*k, cerr if (t.Tag == ResultTag.Consumed && t.Reply.Tag == ReplyTag.Error) { return(ConsumedError <Seq <T> >(t.Reply.Error)); } // c*k, eerr else if (t.Tag == ResultTag.Empty && t.Reply.Tag == ReplyTag.Error) { return(ConsumedError <Seq <T> >(mergeError(error, t.Reply.Error))); } // c*k, c*k else if (t.Tag == ResultTag.Consumed && t.Reply.Tag == ReplyTag.OK) { if (count == 0) { results.Add(t.Reply.Result); return(ConsumedOK(Seq(results), t.Reply.State, t.Reply.Error)); } else { results.Add(t.Reply.Result); last = t; error = t.Reply.Error; current = t.Reply.State; } } // c*k, eok else //if (t.Tag == ResultTag.Empty && t.Reply.Tag == ReplyTag.OK) { if (count == 0) { // c*k, eok -> c*k (not a typo, this should be -> c*k) results.Add(t.Reply.Result); return(ConsumedOK(Seq(results), t.Reply.State, mergeError(error, t.Reply.Error))); } else { results.Add(t.Reply.Result); last = t; error = mergeError(error, t.Reply.Error); } } } else if (last.Tag == ResultTag.Empty) { // eok, cerr if (t.Tag == ResultTag.Consumed && t.Reply.Tag == ReplyTag.Error) { return(ConsumedError <Seq <T> >(t.Reply.Error)); } // eok, eerr else if (t.Tag == ResultTag.Empty && t.Reply.Tag == ReplyTag.Error) { return(EmptyError <Seq <T> >(mergeError(error, t.Reply.Error))); } // eok, c*k else if (t.Tag == ResultTag.Consumed && t.Reply.Tag == ReplyTag.OK) { if (count == 0) { results.Add(t.Reply.Result); return(ConsumedOK(Seq(results), t.Reply.State, t.Reply.Error)); } else { results.Add(t.Reply.Result); last = t; error = t.Reply.Error; current = t.Reply.State; } } // eok, eok else //if (t.Tag == ResultTag.Empty && t.Reply.Tag == ReplyTag.OK) { if (count == 0) { results.Add(t.Reply.Result); return(EmptyOK(Seq(results), t.Reply.State, mergeError(error, t.Reply.Error))); } else { results.Add(t.Reply.Result); last = t; error = mergeError(error, t.Reply.Error); } } } } } return(ConsumedOK(Seq(results), current, error)); };
/// <summary> /// Imperative implementation of chain, which in a non-stack overflow utopia /// would look similar to this: /// /// from x in ps[index] /// from y in chaini(ps, index + 1) /// select x.Cons(y); /// /// </summary> public static Parser <I, IEnumerable <O> > chaini <I, O>(Parser <I, O>[] ps) => ps.Length == 0 ? unexpected <I, IEnumerable <O> >("chain parser with 0 items") : inp => { if (ps.Length == 1) { return(ps[0].Map(x => new[] { x }.AsEnumerable())(inp)); } var current = inp; var results = new List <O>(); ParserError error = null; ParserResult <I, O> last = null; int count = ps.Length; foreach (var p in ps) { count--; var t = p(current); if (last == null) { // cerr if (t.Tag == ResultTag.Consumed && t.Reply.Tag == ReplyTag.Error) { return(ConsumedError <I, IEnumerable <O> >(t.Reply.Error)); } // eerr else if (t.Tag == ResultTag.Empty && t.Reply.Tag == ReplyTag.Error) { return(EmptyError <I, IEnumerable <O> >(t.Reply.Error)); } // c*k else if (t.Tag == ResultTag.Consumed && t.Reply.Tag == ReplyTag.OK) { results.Add(t.Reply.Result); last = t; error = t.Reply.Error; current = t.Reply.State; } // eok else //if (t.Tag == ResultTag.Empty && t.Reply.Tag == ReplyTag.OK) { results.Add(t.Reply.Result); last = t; error = t.Reply.Error; } } else { if (last.Tag == ResultTag.Consumed) { // c*k, cerr if (t.Tag == ResultTag.Consumed && t.Reply.Tag == ReplyTag.Error) { return(ConsumedError <I, IEnumerable <O> >(t.Reply.Error)); } // c*k, eerr else if (t.Tag == ResultTag.Empty && t.Reply.Tag == ReplyTag.Error) { return(ConsumedError <I, IEnumerable <O> >(mergeError(error, t.Reply.Error))); } // c*k, c*k else if (t.Tag == ResultTag.Consumed && t.Reply.Tag == ReplyTag.OK) { if (count == 0) { results.Add(t.Reply.Result); return(ConsumedOK <I, IEnumerable <O> >(results, t.Reply.State, t.Reply.Error)); } else { results.Add(t.Reply.Result); last = t; error = t.Reply.Error; current = t.Reply.State; } } // c*k, eok else //if (t.Tag == ResultTag.Empty && t.Reply.Tag == ReplyTag.OK) { if (count == 0) { // c*k, eok -> c*k (not a typo, this should be -> c*k) results.Add(t.Reply.Result); return(ConsumedOK <I, IEnumerable <O> >(results, t.Reply.State, mergeError(error, t.Reply.Error))); } else { results.Add(t.Reply.Result); last = t; error = mergeError(error, t.Reply.Error); } } } else if (last.Tag == ResultTag.Empty) { // eok, cerr if (t.Tag == ResultTag.Consumed && t.Reply.Tag == ReplyTag.Error) { return(ConsumedError <I, IEnumerable <O> >(t.Reply.Error)); } // eok, eerr else if (t.Tag == ResultTag.Empty && t.Reply.Tag == ReplyTag.Error) { return(EmptyError <I, IEnumerable <O> >(mergeError(error, t.Reply.Error))); } // eok, c*k else if (t.Tag == ResultTag.Consumed && t.Reply.Tag == ReplyTag.OK) { if (count == 0) { results.Add(t.Reply.Result); return(ConsumedOK <I, IEnumerable <O> >(results, t.Reply.State, t.Reply.Error)); } else { results.Add(t.Reply.Result); last = t; error = t.Reply.Error; current = t.Reply.State; } } // eok, eok else //if (t.Tag == ResultTag.Empty && t.Reply.Tag == ReplyTag.OK) { if (count == 0) { results.Add(t.Reply.Result); return(EmptyOK <I, IEnumerable <O> >(results, t.Reply.State, mergeError(error, t.Reply.Error))); } else { results.Add(t.Reply.Result); last = t; error = mergeError(error, t.Reply.Error); } } } } } return(ConsumedOK <I, IEnumerable <O> >(results, current, error)); };