/// <summary> /// Returns a character <see cref="Node"/> /// </summary> /// <remarks> /// char => . /// </remarks> /// <param name="iterator">Pattern iterator giving access to the character stream</param> /// <returns></returns> private static Node Char(ref PatternIterator iterator) { var data = iterator.Peek(); if (!(char.IsLetterOrDigit(data) || data == '\0')) { throw new InvalidOperationException($"Parse error: expected alphanumeric, got {iterator.Peek()} at {iterator.Position}"); } return(new CharacterNode(iterator.Pop())); }
/// <summary> /// Returns an expression <see cref="Node"/> /// </summary> /// <remarks> /// expr = concat '|' expr | concat /// </remarks> /// <param name="iterator">Pattern iterator giving access to the character stream</param> /// <returns></returns> private static Node Expression(ref PatternIterator iterator) { var left = Concat(ref iterator); switch (iterator.Peek()) { case '|': iterator.Pop(); return(new AlternativeNode(left, Expression(ref iterator))); default: return(left); } }
/// <summary> /// Returns a sequential <see cref="Node"/> /// </summary> /// <remarks> /// concat => rep concat | rep /// </remarks> /// <param name="iterator">Pattern iterator giving access to the character stream</param> /// <returns></returns> private static Node Concat(ref PatternIterator iterator) { var left = Rep(ref iterator); switch (iterator.Peek()) { case '.': iterator.Pop(); return(new ConcatenationNode(left, Concat(ref iterator))); default: return(left); } }
/// <summary> /// Returns an atomic <see cref="Node"/> /// </summary> /// <remarks> /// atom => char | '(' expr ')' /// </remarks> /// <param name="iterator">Pattern iterator giving access to the character stream</param> /// <returns></returns> private static Node Atom(ref PatternIterator iterator) { switch (iterator.Peek()) { case '(': iterator.Pop(); var expression = Expression(ref iterator); var next = iterator.Pop(); if (next != ')') { throw new ParseException(iterator.Data, iterator.Position, $"Expected ')' but received '{next}'"); } return(expression); default: return(Char(ref iterator)); } }
/// <summary> /// Returns a repeating <see cref="Note"/> /// </summary> /// <remarks> /// rep => atom '*' | atom '?' | atom '+' | atom /// </remarks> /// <param name="iterator">Pattern iterator giving access to the character stream</param> /// <returns></returns> private static Node Rep(ref PatternIterator iterator) { var node = Atom(ref iterator); switch (iterator.Peek()) { case '*': iterator.Pop(); return(new OptionalRepeatingNode(node)); case '?': iterator.Pop(); return(new OptionalNode(node)); case '+': iterator.Pop(); return(new RequiredRepeatingNode(node)); default: return(node); } }