Skip to content
Dylan Beattie edited this page Jul 9, 2024 · 1 revision

How do I embed complex logic in a code expression?

You do so by wrapping the body in an extra set of curly braces.

result
    = char:. {{
        switch (@char)
        {
            default:
                return string.Empty;
        }
    }}

How do I include custom methods in the generated parser?

You have two options: partial classes or the @members setting. The @members setting would be used like so:

@members
{
    private bool IsCharAwesome(string c)
    {
        return c == "a";
    }
}

someRule = c:. &{ this.IsCharAwesome(c) }

How do I make an expression return a string when it is already returning something else?

Sequence expressions will return strings, unless they are typed and followed by a code expression. You can change any other expression into a sequence expression by putting an empty string at the beginning.

returnsList   = other*
returnsString = "" other*

How do I emit helpful error message from my parser?

Create a rule that expects EOF (end-of-file), with an alternate case so that it doesn't find an EOF, calls #error{} with information about what it found instead of the EOF. You probably want to wrap the captured text in Regex.Escape so that invisible characters like newlines will be reported as \n instead of just putting a line-break in your error message.

Report a single unexpected character:

EOF = !.
    / unexpected:. 
        #error{ $"Unexpected '{Regex.Escape(unexpected)}' at line {state.Line}, col {state.Column - 1}" }

Match an unexpected keyword or token (everything up to the next space/tab/newline):

EOF = !.
    / unexpected:("" [^ \t\r\n]+) 
        #error{ $"Unexpected '{Regex.Escape(unexpected)}' at line {state.Line}, col {state.Column - 1}" }

How do I emit non-fatal errors and warnings from my parser?

Make use of the stateful parsing.

@using System.Linq
@members
{
    private static void AddError(Cursor state, string message)
    {
        var existing = state["errors"] as IList<MyError>;
        var newList = existing == null ? new List<MyError>() : existing.ToList();
        newList.Add(new MyError
        {
            Line = state.Line,
            Column = state.Column,
            Message = message,
        });
        state["errors"] = newList.AsReadOnly();
    }
}

start<MyResult>
  = stuff:stuff { new MyResult { Stuff = stuff, Errors = state["errors"] } }

stuff
  = foo:("foo" / "bar") #{ AddError(state, "An error occured: " + foo); }

How do I reference an exported rule from another parser?

You can use the -export flag to make a rule visible in a grammar's exported rules collection:

@namespace PegExamples
@classname Library

Expression <YourType> -export = 'OK' { new YourType() }

This can then be used from another parser like so:

@namespace PegExamples
@classname Consumer
@members
{
    private readonly Library library = new Library();
}

delimitedExpression <YourType> = '{' x:importedExpression '}' { x }

importedExpression <YourType> = #parse{ this.library.Exported.Expression(ref state) }