News for hackers. Stuff that matters.
Re:What the heck is a parser-combinator? by louthy on Wednesday June 21, 2017 @07:55AM (#14602881)
Wanted to leave a comment on the site, but it seems comments are broken, so I'll stick this here just in case the author sees it:

You've linked to my csharp-monad library for C# parser combinators. This has been superseded by my language-ext project: https://github.com/louthy/language-ext/

It is a much more advanced and efficient port of the Haskell Parsec library. Would you mind linking to that instead?

Your Sprache examples would look like this in language-ext:

    public static readonly Parser<JCLCommand> JCLText =
        from open    in ch('$') 
        from ws1     in spaces
        from command in asString(many(noneOf(' ')))
        from ws2     in spaces
        from content in asString(many(noneOf('"')))
        select new JCLCommand(command, content);
     
    public static readonly Parser<JCLCommand> GlobalText =
        from variablename in asString(many(noneOf('=')))
        from ws2          in ch('=')
        from openbrack    in ch('(')
        from filepath     in asString(many(noneOf(')')))
        from closebrack   in ch(')')
        select new JCLCommand(variablename, filepath);
But the power of parser combinators are their reusable nature. So I'd break that down to a set of tools:

    static Parser<A> token<A>(Parser<A> p) =>
        from x in p
        from _ in either(spaces, eof)
        select x;

    static Parser<string> symbol(string x) =>
        token(str(x));

    static Parser<string> identifier =
        token(asString(many1(alphaNum)));

    static Parser<A> quotes<A>(Parser<A> p) =>
        between(symbol("\""), symbol("\""), p);

    static Parser<A> parens<A>(Parser<A> p) =>
        between(symbol("("), symbol(")"), p);

    static Parser<string> quoteText =
        token(quotes(asString(many(satisfy(x => x != '"')))));

    static Parser<string> parensText =
        token(parens(asString(many(satisfy(x => x != ')')))));
Then your final parsers would look like this:

    static readonly Parser<JCLCommand> JCLText =
        from open    in symbol("$")
        from command in identifier
        from content in quoteText
        select new JCLCommand(command, content);

    static readonly Parser<JCLCommand> GlobalText =
        from variablename in identifier
        from ws2          in symbol("=")
        from filepath     in parensText
        select new JCLCommand(variablename, filepath);

    static readonly Parser<Seq<JCLCommand>> Commands =
        from _        in spaces
        from commands in many1(either(JCLText, GlobalText))
        select commands;

Which is much easier to understand I think. It's not exactly the same as it defines what an identifier is, but it's much more tolerant of rogue spaces because of the token parser. This is definitely the most compelling aspect of parser combinators for me, the way they compose so elegantly.

There are no comments on this comment yet.