It sounds like you are trying to get a list of parsers tested (but not required) at the end of the match..
Eto.Parse doesn't keep track of that, so there's no way to get those directly.. unless you change your grammar a little to include an optional parser at the end for the ones you're interested in - then the error list (should) include the ones at the end of the match, even though they will never match..
ErrorIndex is the last index of the right most parser that caused the error (with AddError as true). If you're not getting the position you're expecting, it's probably because your grammar doesn't match those characters.. It'll give you the starting position of the parser that failed, not the ending position.
I care about the named parsers. I can go through the tree and stick an optional something on the end of each. How will that not mess with my grammar? I don't understand "they will never match". Do we have a "void" parser?
It gives the starting position and my Errors list has 15 items in it. How do I know which Error was the "right-most", aka, the best match? Are the Errors ordered?
using Eto.Parse;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace TestParser
{
class Program
{
class CarGrammar : Grammar
{
private static readonly Parser WS = +Terminals.SingleLineWhiteSpace;
public CarGrammar():base("procedure")
{
var writables = ((Parser)"[brake]" | "[throttle]").Named("telemetry");
var readables = (writables | "[velocity]").Named("telemetry");
var number = new Eto.Parse.Parsers.NumberParser{AllowDecimal = true, AllowExponent = true, AllowSign = false};
// (it sure would be nice to specify a range on that number)
var writer = ((Parser)"set").Named("action") & WS & writables & WS & "at" & WS & number.Named("value") & ~WS & ~(Parser)"%";
var op = ((Parser)"<"|">"|"=").Named("operator");
var unit = ((Parser)"m/s"|"kph"|"mph").Named("unit");
var reader = ((Parser)"until").Named("action") & WS & readables & WS & op & ~WS & ~unit; var row = (~(writer | reader) & Terminals.Eol).Named("row");
Inner = +row;
}
} static void Main(string[] args)
{
var parser = new CarGrammar(); var match = parser.Match("set [throttle] at 15 %\r\n");
Console.WriteLine("Running match on 'set [throttle] at 15 %nl'");
Console.WriteLine("Expected success at {0} but it was {1}", true, match.Success);
Console.WriteLine("Expected {0} errors but had {1} errors", 2, match.Errors.Count());
Console.WriteLine("Expected error index at {0} but it was {1}", 23, match.ErrorIndex);
Console.WriteLine("Expected possibilities of {0} but had {1}", "set, until", FindPossibilities(match));
Console.WriteLine("Reported error: {0}", match.ErrorMessage);
Console.WriteLine(); match = parser.Match("");
Console.WriteLine("Running match on ''");
Console.WriteLine("Expected success at {0} but it was {1}", false, match.Success);
Console.WriteLine("Expected {0} errors but had {1} errors", 2, match.Errors.Count());
Console.WriteLine("Expected error index at {0} but it was {1}", 0, match.ErrorIndex);
Console.WriteLine("Expected possibilities of {0} but had {1}", "set, until", FindPossibilities(match));
Console.WriteLine("Reported error: {0}", match.ErrorMessage);
Console.WriteLine(); match = parser.Match("set ");
Console.WriteLine("Running match on 'set '");
Console.WriteLine("Expected success at {0} but it was {1}", false, match.Success);
Console.WriteLine("Expected {0} errors but had {1} errors", 2, match.Errors.Count());
Console.WriteLine("Expected error index at {0} but it was {1}", 4, match.ErrorIndex);
Console.WriteLine("Expected possibilities of '{0}' but had '{1}'", "[brake], [throttle]", FindPossibilities(match));
Console.WriteLine("Reported error: {0}", match.ErrorMessage);
Console.WriteLine(); match = parser.Match("set [throttle] ");
Console.WriteLine("Running match on 'set [throttle] '");
Console.WriteLine("Expected success at {0} but it was {1}", false, match.Success);
Console.WriteLine("Expected {0} errors but had {1} errors", 1, match.Errors.Count());
Console.WriteLine("Expected error index at {0} but it was {1}", 14, match.ErrorIndex);
Console.WriteLine("Expected possibilities of '{0}' but had '{1}'", "at", FindPossibilities(match));
Console.WriteLine(); if (Debugger.IsAttached)
Console.ReadKey();
} private static string FindPossibilities(GrammarMatch match)
{
var literals = new List<string>();
foreach (var child in match.Errors)
{
literals.AddRange(FindPossibilities(child));
}
return string.Join(", ", literals.Distinct().OrderBy(l => l));
} private static List<string> FindPossibilities(Parser match)
{
var literals = new List<string>(); if (match is Eto.Parse.Parsers.LiteralTerminal)
literals.Add(((Eto.Parse.Parsers.LiteralTerminal)match).Value); foreach (var child in match.Children())
{
literals.AddRange(FindPossibilities(child));
}
return literals;
}
}
}
There is also another problem with the way you are trying to get the list of expected matches.. For example it'd report all of the elements of a sequence as expected which in fact it was only the first element of the sequence that you want back.
There is one bug with Eto.Parse though, where it reports errors for all parents even if the ErrorIndex does not match the position of it. This will be fixed next version (which would make the parsers with errors match exactly what you're expecting).See attached sample on how to get the expected parsers a little more accurately.
private class TestGrammar : Grammar{ private static readonly Parser WS = +Terminals.SingleLineWhiteSpace; public TestGrammar(): base("procedure") { var eol = (Terminals.Eol | Terminals.End).Named("eol"); var hash = ((Parser)"#"); hash.AddError = true; var comment = ~(~WS & (hash & (-Terminals.AnyChar ^ eol)));
var set = (Parser)"set"; set.Name = "operator"; var at = (Parser)"at"; at.Name = "at"; var val = new NumberParser(); val.Name = "value"; var perc = (Parser) "%"; perc.Name = "percent"; var row = new AlternativeParser(); for (int i = 3; i > 0; i--) // curious to know how to do this with i counting up; can I specify a lookahead amount on my group strings? { // should really use a repeating parser instead, then update your FindPossibilities to // deal with them and extract the possibilities from the repeat. var strings = new string[10]; for (int j = 0; j < strings.Length; j++) { strings[j] = new string((char)(j + 0x41), i); }
// cleaner way var group = new AlternativeParser(strings.Select(r => (Parser)r)); group.Name = "action"; var inner = set & WS & group & WS & at & WS & val & ~(~WS & perc) & comment & ~WS & eol;
// much cleaner.. don't need each row to repeat themselves, we repeat the entire alternate below row.Add(inner); } // don't check the row unless there's something there by gobbling up the eol's Inner = +(eol | row); }}