} // or null public override string GetExpected() { var s = ""; if (Min > 0) { s += $"{ItemProd.GetExpected()} "; } s += $"[ {SeparatorProd?.GetExpected() ?? ""} {ItemProd.GetExpected()} ]*"; return(s); }
public override MatchResult?MatchStep(MatchStack stack, MatchFrame frame, TokenQueue q) { if (frame.ListState == ListTermState.Start) { frame.ListState = ListTermState.MatchItem; stack.Push(ItemProd); return(null); // -> MatchItem } else if (frame.ListState == ListTermState.MatchSeparator) { var result = frame.SubResult; if (result.IsMatch) { // we have a separator. now try to match the item following it. frame.ListState = ListTermState.MatchItem; stack.Push(ItemProd); return(null); // -> MatchItem } else if (result.ErrorMessage == null) { // we didn't find a separator. this list is done. back up to the beginning of where // the not-separator started and we're done. q.Jump(frame.ListSeparatorStartLoc); if (frame.ListCount < Min) { return(MatchResult.Error( $"At least {Min} list item{(Min == 1 ? " is" : "s are")} required, but only " + $"{frame.ListCount} {(frame.ListCount == 1 ? "was" : "were")} provided. " + $"Expected list item: {ItemProd.GetExpected()}")); } else { return(MatchResult.Matched); } } else { return(result); // error } } else if (frame.ListState == ListTermState.MatchItem) { var result = frame.SubResult; if (result.IsMatch) { // we have an item. is there another? frame.ListCount++; if (SeparatorProd == null) { // there is no separator, so match the next item frame.ListState = ListTermState.MatchItem; frame.ListSeparatorStartLoc = q.GetLocation(); stack.Push(ItemProd); return(null); // -> MatchItem } else { // match separator + item frame.ListState = ListTermState.MatchSeparator; frame.ListSeparatorStartLoc = q.GetLocation(); stack.Push(SeparatorProd); return(null); // -> MatchSeparator } } else if (result.ErrorMessage == null) { if (frame.ListCount == 0) { // the first item might be missing because the list can potentially be optional. return(Min == 0 ? MatchResult.Matched : MatchResult.NoMatch); } else if (SeparatorProd == null) { // there's no separator, so eventually we'll end up here when the list ends. q.Jump(frame.ListSeparatorStartLoc); if (frame.ListCount < Min) { return(MatchResult.Error( $"At least {Min} list item{(Min == 1 ? " is" : "s are")} required, but only " + $"{frame.ListCount} {(frame.ListCount == 1 ? "was" : "were")} provided. " + $"Expected list item: {ItemProd.GetExpected()}")); } else { return(MatchResult.Matched); } } else { // subsequent items must be present because, in the MatchItem state, we've already consumed a // separator so there must be an item following it. return(MatchResult.Error($"Expected list item: {ItemProd.GetExpected()}")); } } else { return(result); // error } } else { throw new Exception($"Unrecognized state: {frame.ListState}"); } }