Handlebars - the next generation
Language specification

Path Expressions

PathExpressions
PathExpression ::
Id PathExpressionTailopt
PathExpressionTail ::
. PathExpression
/ PathExpression
Id ::
IdCharacter Idopt
IdCharacter ::
SourceCharacter but not one of NonIdCharacter or Whitespace
NonIdCharacter :: one of
! " # % & ' ( ) * + , . / ; < = > @ [ \ ] ^ ` { | } ~

A path expression is a list of ids separated by dots. It is resolved by recursively retrieving the property matching the id of the current input object.

06-path-expression/path-expression-dots.hb-spec.json
A path expressions with parts seperated by dots
Template:
{{person.name}}
Input / Output
Input
{ "person": { "name": "Max" } }
Output
Max
AST
{
  "type": "Program",
  "body": [
    {
      "type": "MustacheStatement",
      "escaped": true,
      "params": [],
      "path": {
        "type": "PathExpression",
        "original": "person.name",
        "data": false,
        "depth": 0,
        "parts": ["person", "name"],
        "loc": {
          "start": { "line": 1, "column": 2 },
          "end": { "line": 1, "column": 13 }
        }
      },
      "strip": { "open": false, "close": false },
      "loc": {
        "start": { "line": 1, "column": 0 },
        "end": { "line": 1, "column": 15 }
      }
    }
  ],
  "strip": {},
  "loc": {
    "start": { "line": 1, "column": 0 },
    "end": { "line": 1, "column": 15 }
  }
}

If the id matches no property of the object, an empty string is returned. (TODO: Or does it return “undefined” or “null”, which is converted to an empty string by the mustache-statements?)

06-path-expression/path-expression-resolves-to-nothing.hb-spec.json
A path expressions that is resolved to nothing yields an empty string
Template:
{{person.name}}
Input / Output
Input
{ "person": "Max" }
Output
AST
{
  "type": "Program",
  "body": [
    {
      "type": "MustacheStatement",
      "escaped": true,
      "params": [],
      "path": {
        "type": "PathExpression",
        "original": "person.name",
        "data": false,
        "depth": 0,
        "parts": ["person", "name"],
        "loc": {
          "start": { "line": 1, "column": 2 },
          "end": { "line": 1, "column": 13 }
        }
      },
      "strip": { "open": false, "close": false },
      "loc": {
        "start": { "line": 1, "column": 0 },
        "end": { "line": 1, "column": 15 }
      }
    }
  ],
  "strip": {},
  "loc": {
    "start": { "line": 1, "column": 0 },
    "end": { "line": 1, "column": 15 }
  }
}

If a prefix of the path expressions that resolves to nothing, the result is also an empty string.

06-path-expression/path-expression-too-long.hb-spec.json
A path expression retrieving values from null 'null' returns an empty string
Template:
{{person.name}}{{{person.name}}}
Input / Output
Input
{}
Output
AST
{
  "type": "Program",
  "body": [
    {
      "type": "MustacheStatement",
      "escaped": true,
      "params": [],
      "path": {
        "type": "PathExpression",
        "original": "person.name",
        "data": false,
        "depth": 0,
        "parts": ["person", "name"],
        "loc": {
          "start": { "line": 1, "column": 2 },
          "end": { "line": 1, "column": 13 }
        }
      },
      "strip": { "open": false, "close": false },
      "loc": {
        "start": { "line": 1, "column": 0 },
        "end": { "line": 1, "column": 15 }
      }
    },
    {
      "type": "MustacheStatement",
      "escaped": false,
      "params": [],
      "path": {
        "type": "PathExpression",
        "original": "person.name",
        "data": false,
        "depth": 0,
        "parts": ["person", "name"],
        "loc": {
          "start": { "line": 1, "column": 18 },
          "end": { "line": 1, "column": 29 }
        }
      },
      "strip": { "open": false, "close": false },
      "loc": {
        "start": { "line": 1, "column": 15 },
        "end": { "line": 1, "column": 32 }
      }
    }
  ],
  "strip": {},
  "loc": {
    "start": { "line": 1, "column": 0 },
    "end": { "line": 1, "column": 32 }
  }
}

A path expression may contain ids separated by slashes

06-path-expression/path-expression-slashes.hb-spec.json
A path expressions with parts seperated by dots
Template:
{{person/name}}
Input / Output
Input
{ "person": { "name": "Max" } }
Output
Max
AST
{
  "type": "Program",
  "body": [
    {
      "type": "MustacheStatement",
      "escaped": true,
      "params": [],
      "path": {
        "type": "PathExpression",
        "original": "person/name",
        "data": false,
        "depth": 0,
        "parts": ["person", "name"],
        "loc": {
          "start": { "line": 1, "column": 2 },
          "end": { "line": 1, "column": 13 }
        }
      },
      "strip": { "open": false, "close": false },
      "loc": {
        "start": { "line": 1, "column": 0 },
        "end": { "line": 1, "column": 15 }
      }
    }
  ],
  "strip": {},
  "loc": {
    "start": { "line": 1, "column": 0 },
    "end": { "line": 1, "column": 15 }
  }
}

Dots and slashes may be mixed.

06-path-expression/path-expression-dots-and-slashes.hb-spec.json
A path expressions with parts seperated by dots
Template:
{{who.person/name}}
Input / Output
Input
{ "who": { "person": { "name": "Max" } } }
Output
Max
AST
{
  "type": "Program",
  "body": [
    {
      "type": "MustacheStatement",
      "escaped": true,
      "params": [],
      "path": {
        "type": "PathExpression",
        "original": "who.person/name",
        "data": false,
        "depth": 0,
        "parts": ["who", "person", "name"],
        "loc": {
          "start": { "line": 1, "column": 2 },
          "end": { "line": 1, "column": 17 }
        }
      },
      "strip": { "open": false, "close": false },
      "loc": {
        "start": { "line": 1, "column": 0 },
        "end": { "line": 1, "column": 19 }
      }
    }
  ],
  "strip": {},
  "loc": {
    "start": { "line": 1, "column": 0 },
    "end": { "line": 1, "column": 19 }
  }
}

Literal segments

Identifiers may contain any unicode character except for the following.

Testcases for invalid ids

It may be wise to allow multiple templates in such a test-case. Otherwise we need one file per illegal character.

Ids with special charactares must be wrapped in [ and ]. It may than not include a closing ].

06-path-expression/id-in-square-brackets.hb-spec.json
An id with special characters can be wrapped in square brackets
Template:
{{['].[ ].[0]}}
Warning: The original Handlebars implementation has a deviating AST (which the author of this document considers to be wrong):
Input / Output
Input
{ "'": { " ": ["success"] } }
Output
success
AST
{
  "type": "Program",
  "body": [
    {
      "type": "MustacheStatement",
      "escaped": true,
      "params": [],
      "path": {
        "type": "PathExpression",
        "original": "['].[ ].[0]",
        "data": false,
        "depth": 0,
        "parts": ["'", " ", "0"],
        "loc": {
          "start": { "line": 1, "column": 2 },
          "end": { "line": 1, "column": 13 }
        }
      },
      "strip": { "open": false, "close": false },
      "loc": {
        "start": { "line": 1, "column": 0 },
        "end": { "line": 1, "column": 15 }
      }
    }
  ],
  "strip": {},
  "loc": {
    "start": { "line": 1, "column": 0 },
    "end": { "line": 1, "column": 15 }
  }
}
Original AST
{
  "type": "Program",
  "body": [
    {
      "type": "MustacheStatement",
      "escaped": true,
      "params": [],
      "path": {
        "type": "PathExpression",
        "original": "'. .0",
        "data": false,
        "depth": 0,
        "parts": ["'", " ", "0"],
        "loc": {
          "start": { "line": 1, "column": 2 },
          "end": { "line": 1, "column": 13 }
        }
      },
      "strip": { "open": false, "close": false },
      "loc": {
        "start": { "line": 1, "column": 0 },
        "end": { "line": 1, "column": 15 }
      }
    }
  ],
  "strip": {},
  "loc": {
    "start": { "line": 1, "column": 0 },
    "end": { "line": 1, "column": 15 }
  }
}

TODO: Quotes to allow invalid chars

The original Handlebars parser also allows Literal expressions to be the

In https://github.com/handlebars-lang/docs/pull/119 it was pointed out that the following also works

{{"id containing spaces"}}
{{"id containing spaces"}}

but the following does not

{{ 'id containing spaces'.moreProp}}
{{ "id containing spaces".moreProps}}

This is because the {{ 'id containing spaces' }} interpreted by the parser as LiteralString, where quotes are allowed, but {{ 'id containing spaces'.moreProp}} is a contains a PathExpressions, where quotes aren’t allowed.

This could be considered a bug in the parser.