public override MatchResult?MatchStep(MatchStack stack, MatchFrame frame, TokenQueue q) { if (frame.OrState == OrTermState.Start) { // try to match the first sub-production stack.Push(Prods[0]); frame.OrState = OrTermState.Match; frame.OrProdIndex = 0; frame.OrStartLoc = q.GetLocation(); return(null); } else if (frame.OrState == OrTermState.Match) { // we have finished matching one of the productions. if it matched, then we're done. if not, move on // to the next production. var result = frame.SubResult; if (result.IsMatch) { return(MatchResult.Matched); } else if (result.ErrorMessage == null) { // no match. rewind to the beginning and retry with the next one. q.Jump(frame.OrStartLoc); frame.OrProdIndex++; if (frame.OrProdIndex >= Prods.Length) { // we have exhausted all of the possibilities and none of them matched. return(MatchResult.NoMatch); } stack.Push(Prods[frame.OrProdIndex]); return(null); } else { // started to match but mismatched past the point of no return. return(result); } } else { throw new Exception($"Unrecognized state: {frame.OrState}"); } }
public override MatchResult?MatchStep(MatchStack stack, MatchFrame frame, TokenQueue q) { if (frame.OptionalState == OptionalTermState.Start) { // try to match the sub-production stack.Push(Prod); frame.OptionalState = OptionalTermState.Match; frame.OptionalStartLoc = q.GetLocation(); return(null); } else if (frame.OptionalState == OptionalTermState.Match) { // done matching the sub-production var result = frame.SubResult; if (result.IsMatch) { // the optional term is indeed present. return(MatchResult.Matched); } else if (result.ErrorMessage == null) { // it didn't match but wasn't an error. this is fine, but we do have to walk the cursor back to // where it started since we effectively "matched" zero tokens. q.Jump(frame.OptionalStartLoc); return(MatchResult.Matched); } else { // it started to match but then mismatched past the point of no return. that's an error. return(result); } } else { throw new Exception($"Unrecognized state: {frame.OptionalState}"); } }
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}"); } }