Skip to content

Instantly share code, notes, and snippets.

@masak
Last active August 29, 2015 14:05
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save masak/d59749975a1d4e200903 to your computer and use it in GitHub Desktop.
Save masak/d59749975a1d4e200903 to your computer and use it in GitHub Desktop.
Deriving a parser from Str methods

Given the following two classes:

class Config::Line {
    has Str $.key;
    has Int $.value;

    method Str { "$.key: $.value;" }
}

class Config::File {
    has Config::Line @.config-lines;

    method Str { join "\n", @.config-lines }
}

It seems to me we do have enough information present to create the following grammar and actions:

grammar Config::File::Syntax {
    token TOP { <config-line>* % \n }
    token config-line { <key> ': ' <value> ';' }
    token key { <-[:]>* }
    token value { '-'? \d+ }
}

class Config::File::Actions {
    method TOP($/) { make Config::File.new(:config-lines($<config-line>».ast)) }
    method config-line($/) { make Config::Line.new(:key($<key>.ast), :value($<value>.ast)) }
    method key($/) { make ~$/ }
    method value($/) { make +$/ }
}

Yes, we're using everything from the two classes to derive that. (Names and types of attributes, relationship between the classes, some knowledge of how to parse built-in types Str and Int, and especially the contents of the two classes' Str methods.)

Yes, there are probably limits to this approach. Specifically, we can't expect to handle anything that might occur in a user-authored Str method — we run into undecidability fairly quickly. But it seems to me most Str methods are not that fancy, and keep within a fairly narrow, easily analyzed band in the state space of all possible code. For rare cases where the transformation falls short, it can just give up and confess its limitations to the user.

And yes, there is an element of arbitrary decisions in the mapping. The grammar parses only config lines, not empty lines or lines with comments, or comments at the end of config lines. The <-[:]> limitation in key is at the same time very sensible, possible to derive from context, and a little bit presumptuous. A number of decisions (at least one config line? at least one character in key? no negative values?) could probably be governed from where clauses in the classes — again with the caveat about undecidability.

With all that out of the way — could we do this? Would it be useful?

(Before you ask, I do remember jnthn's Grammar::Generative. It does something else, though: given a grammar and an AST, it returns a text that can parse to that AST. What I'm proposing is deriving a grammar from a model, such that the resulting grammar can parse what the model can stringify.)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment