Treetop PEG for Puppet resources
Earlier this year at Puppet Camp EU, Randall Hansen ran an open space session on improving the Puppet user experience.
Lots of sharp edges were identified, but one issue that I raised was the annoying need for trailing commas to break up parameters in resource declarations.
I chatted about this briefly with Luke and for a laugh I decided to write a Treetop Parsing Expression Grammar (PEG) for Puppet resources that supported newlines as the parameter delimeter:
# puppet.treetop
grammar Puppet
rule resource
whitespace
type
whitespace
open
whitespace
name
whitespace
parameters
whitespace
close
whitespace
{
def resource_type
type.text_value
end
def resource_name
name.word.text_value
end
}
end
rule type
word
end
rule open
"{"
end
rule close
"}"
end
rule name
quotes word quotes ":"
{
def name
word
end
}
end
rule word
[a-zA-Z]+
end
rule quotes
"'" / '"'
end
rule parameters
newline* (whitespace parameter comma_or_newline*)*
end
rule parameter
whitespace
word
whitespace
arrow
whitespace
word
whitespace
end
rule arrow
"=>"
end
rule comma_or_newline
comma / newline
end
rule comma
","
end
rule newline
"\n"
end
rule whitespace
"\s"* / "\n"+
end
end
It's throwaway code, but as far as I'm aware it's relatively idiomatic Treetop.
It came in handy earlier this week when explaining PEGs to a new recruit into the R&D team at work.
Said recruit suggested that I publish it, as there aren't too many examples of Treetop PEGs floating around.
To run the PEG over an example snippet:
#!/usr/bin/env ruby
require 'rubygems'
require 'bundler/setup'
require 'polyglot'
require 'treetop'
Treetop.load "puppet"
snippet = <<-SNIPPET
package { "foobar":
ensure => present, another => bar, spoons => doom
foo => bar
}
SNIPPET
parser = PuppetParser.new
if @root = parser.parse(snippet.strip)
puts 'success'
p @root.resource_type
p @root.resource_name
else
puts 'failure'
puts parser.failure_reason
puts parser.failure_column
puts parser.failure_line
puts parser.failure_index
end
Gemfile for running it and all the above code is in a Gist.