A file I was trying to parse allows C# style multi-line comments:
/*
* A Comment
*/
If I was writing a PEG style grammar for this I might write something
like:
comment: '/*' (!'*/' .)* '*/'
Basically, consume /*, optionally consume any number of characters
except for the pattern */, and finally consume */.
Is there currently an easy or elegant solution to this in Sprache or
will I need to write an extension method that keeps track of the input
position, compares the parsed input with the exception pattern, and
upon matching the exception pattern rolls back the input to the stored
position (when we first started to see a match) and returns success?
Thanks
Sean
static readonly Parser<string> CommentParser =
from cs in Parse.Char('/').Then(v => Parse.Char('*'))
from c in Parse.Any.XUntil(Parse.Char('*').Then(v =>
Parse.Char('/')))
from ce in Parse.Char('*').Then(v => Parse.Char('/'))
select new string(c.ToArray());
Currently Until and XUntil always return Sucess (like Many) though my
gut says that if the end of file is reached without ever matching the
stop condition then they should probably return Failure.
Anyways... if you find any of the below useful then you are more than
welcome to include it in future releases of Sprache.
public static readonly Parser<char> Any = Char(c => true, "any"); //
Because sometimes you just don't care.
public static Parser<IEnumerable<T>> Until<T>(this Parser<T> parser, T
stop)
{
Enforce.ArgumentNotNull(parser, "parser");
return i =>
{
var remainder = i;
var result = new List<T>();
var r = parser(i);
while (r is Success<T>)
{
var s = r as Success<T>;
if (remainder == s.Remainder)
break;
result.Add(s.Result);
remainder = s.Remainder;
if (s.Result.Equals(stop))
break;
r = parser(remainder);
}
return new Success<IEnumerable<T>>(result, remainder);
};
}
public static Parser<IEnumerable<T>> XUntil<T>(this Parser<T> parser,
T stop)
{
Enforce.ArgumentNotNull(parser, "parser");
return i =>
{
var remainder = i;
var result = new List<T>();
var r = parser(i);
while (r is Success<T>)
{
var s = r as Success<T>;
if (remainder == s.Remainder)
break;
if (s.Result.Equals(stop))
break;
result.Add(s.Result);
remainder = s.Remainder;
r = parser(remainder);
}
return new Success<IEnumerable<T>>(result, remainder);
};
}
public static Parser<IEnumerable<T>> XUntil<T, U>(this Parser<T>
parser, Parser<U> stop)
{
Enforce.ArgumentNotNull(parser, "parser");
Enforce.ArgumentNotNull(stop, "stop");
return i =>
{
var remainder = i;
var result = new List<T>();
var r = parser(i);
while (r is Success<T>)
{
var s = r as Success<T>;
if (remainder == s.Remainder)
break;
if (stop(remainder) is Success<U>)
break;
result.Add(s.Result);
remainder = s.Remainder;
r = parser(remainder);
}
return new Success<IEnumerable<T>>(result, remainder);
};
}
On Feb 10, 5:07 pm, Nicholas Blumhardt <nicholas.blumha...@gmail.com>
wrote:
> Hi Sean,
>
> Sprache doesn't have a built-in for this, but one would be welcome.
>
> Something along these lines _might_ work:
>
> Parse.CharExcept('*").Or(Parse.Char('*').Then(c =>
> Parse.CharExcept('/'))).Many()
>
> Good luck!
>
> Nick
>
public static Parser<IEnumerable<T>> XUntil<T, U>(this Parser<T>
parser, Parser<U> stop)
{
Enforce.ArgumentNotNull(parser, "parser");
Enforce.ArgumentNotNull(stop, "stop");
return i =>
{
var remainder = i;
var result = new List<T>();
if (stop(remainder) is Success<U>)
return new Success<IEnumerable<T>>(result, remainder);
var r = parser(i);
while (r is Success<T>)
{
var s = r as Success<T>;
if (remainder == s.Remainder)
return new Failure<IEnumerable<T>>(s.Remainder, () =>
"", () => new[] { "" });
result.Add(s.Result);
remainder = s.Remainder;
if (stop(remainder) is Success<U>)
return new Success<IEnumerable<T>>(result, remainder);
r = parser(remainder);
}
var f = r as Failure<T>;
return new Failure<IEnumerable<T>>(f.FailedInput, () =>
f.Message, () => f.Expectations);
};
|
public static Parser<IEnumerable<T>> Until<T, U>(this Parser<T> parser, Parser<U> until) |
|
{ |
|
return parser.Except(until).Many().Then(r => until.Return(r)); |
| } |