javascriptparsingexpressionpegpegjs

how to write a simple peg grammar for a liquid-like templating language?


edit: you can follow the progress here: https://github.com/simple-updates/template

I'm using peg.js and trying to write something that could interpret a template like:

hello {{ "world" }}
{% if a %}
  good {{ a }}
{% else %}
  bad
{% endif %}

I've tried many things but let's say this is my starting point:

Template
  = ws markup ws

ws = " "*

open_interpolation = "{{"
close_interpolation = "}}"
open_tag = "{%"
close_tag = "%}"

char = . // ?

markup =
  (open_tag tag:char* close_tag)
  { return { 'tag': tag.join('') } } /
  (open_interpolation interpolation:char* close_interpolation)
  { return { 'interpolation': interpolation.join('') } } /
  chars:char*
  { return { 'chars': chars.join('') } }

when I try on the string {{ test }} for instance it will just interpret it as chars instead of an interpolation.

any idea of how I could do it?

(obviously it would be more complex with nested "markups")


Solution

  • How about something like this as a start:

    Template
     = Atom*
    
    Atom
     = IfTag
     / Interpolation
     / [^{]
     / !"{%" !"{{" "{"
    
    Interpolation
     = "{{" _ Expression _ "}}"
    
    IfTag
     = If Template ( Else Template )? EndIf
    
    If
     = "{%" _ "if" _ Expression _ "%}"
    
    Else
     = "{%" _ "else" _ "%}"
    
    EndIf
     = "{%" _ "endif" _ "%}"
    
    Expression
     = "\"" [^"]* "\""
     / [a-zA-Z]+
     / [0-9]+
    
    _
     = [ \t\n\r]*
    

    The tricky part here is the !"{%" !"{{" "{" alternative of the Atom production, which reads as follows:

    When no "{%" and "{{" can be seen ahead of the current position, match a single "{"