TwirlParser

play.twirl.parser.TwirlParser
class TwirlParser(val shouldParseInclusiveDot: Boolean)

TwirlParser is a recursive descent parser for a modified grammar of the Play2 template language as loosely defined here and more rigorously defined by the original template parser, play.templates.ScalaTemplateCompiler.TemplateParser. TwirlParser is meant to be a near drop in replacement for play.templates.ScalaTemplateCompiler.TemplateParser.

The original grammar, as reversed-engineered from play.templates.ScalaTemplateCompiler.TemplateParser, is defined as follows:

 parser : comment? whitespace? ('@' parentheses+)? templateContent
 templateContent : (importExpression | localDef | template | mixed)*
 templateDeclaration : '@' identifier squareBrackets? parentheses*
 localDef : templateDeclaration (' ' | '\t')* '=' (' ' | '\t') scalaBlock
 template : templateDeclaration (' ' | '\t')* '=' (' ' | '\t') '{' templateContent '}'
 mixed : (comment | scalaBlockDisplayed | caseExpression | matchExpression | forExpression | safeExpression | plain | expression) | ('{' mixed* '}')
 scalaBlockDisplayed : scalaBlock
 scalaBlockChained : scalaBlock
 scalaBlock : '@' brackets
 importExpression : '@' 'import ' .* '\r'? '\n'
 caseExpression : whitespace? 'case' .+ '=>' block whitespace?
 forExpression : '@' "for" parentheses block
 matchExpression : '@' (simpleExpr | complexExpr) whitespaceNoBreak 'match' block
 simpleExpr : methodCall expressionPart*
 complexExpr : parentheses
 safeExpression : '@' parentheses
 elseCall : whitespaceNoBreak "else" whitespaceNoBreak?
 chainedMethods : ('.' methodCall)+
 expressionPart : chainedMethods | block | (whitespaceNoBreak scalaBlockChained) | elseCall | parentheses
 expression : '@' methodCall expressionPart*
 methodCall : identifier squareBrackets? parentheses?
 blockArgs : [^'=>' '\n']* '=>'
 block : whitespaceNoBreak '{' blockArgs? mixed* '}'
 brackets : '{' (brackets | [^'}'])* '}'
 comment : '@*' [^'*@']* '*@'
 parentheses : '(' (parentheses | [^')'])* ')'
 squareBrackets : '[' (squareBrackets | [^']'])* ']'
 plain : ('@@' | ([^'@'] [^'{' '}']))+
 whitespaceNoBreak : [' ' '\t']+
 identifier : javaIdentStart javaIdentPart* // see java docs for what these two rules mean

TwirlParser implements a slightly modified version of the above grammar that removes some back-tracking within the 'mixed' non-terminal. It is defined as follows:

 parser : comment? whitespace? ('@' parentheses+)? templateContent
 templateContent : (importExpression | localDef | template | mixed)*
 templateDeclaration : '@' identifier squareBrackets? parentheses*
 localDef : templateDeclaration (' ' | '\t')* '=' (' ' | '\t') scalaBlock
 template : templateDeclaration (' ' | '\t')* '=' (' ' | '\t') '{' templateContent '}'
 mixed : (comment | scalaBlockDisplayed | forExpression | ifExpression | matchExpOrSafeExpOrExpr | caseExpression | plain) | ('{' mixed* '}')
 matchExpOrSafeExpOrExpr : (expression | safeExpression) (whitespaceNoBreak 'match' block)?
 scalaBlockDisplayed : scalaBlock
 scalaBlockChained : scalaBlock
 scalaBlock : '@' brackets
 importExpression : '@' 'import ' .* '\r'? '\n'
 caseExpression : (whitespace? 'case' .+ '=>' block whitespace?) | whitespace
 forExpression : '@' "for" parentheses block
 simpleExpr : methodCall expressionPart*
 complexExpr : parentheses
 safeExpression : '@' parentheses
 ifExpression : '@' "if" parentheses expressionPart (elseIfCall)* elseCall?
 elseCall : whitespaceNoBreak? "else" expressionPart whitespaceNoBreak?
 elseIfCall : whitespaceNoBreak? "else if" parentheses expressionPart whitespaceNoBreak?
 chainedMethods : ('.' methodCall)+
 expressionPart : chainedMethods | block | (whitespaceNoBreak scalaBlockChained) | parentheses
 expression : '@' methodCall expressionPart*
 methodCall : identifier squareBrackets? parentheses?
 blockArgs : [^'=>' '\n']* '=>'
 block : whitespaceNoBreak? '{' blockArgs? mixed* '}'
 brackets : '{' (brackets | [^'}'])* '}'
 comment : '@*' [^'*@']* '*@'
 parentheses : '(' (parentheses | [^')'])* ')'
 squareBrackets : '[' (squareBrackets | [^']'])* ']'
 plain : ('@@' | '@}' | ([^'@'] [^'{' '}']))+
 whitespaceNoBreak : [' ' '\t']+
 identifier : javaIdentStart javaIdentPart* // see java docs for what these two rules mean

TwirlParser can detect several type of parse errors and provides line information. In all cases, the parser will continue parsing the best it can after encountering an error. The following errors are what can be detected:

  • EOF found when more input was expected.
  • Unmatched curly braces
  • Missing blocks after case and match statements
  • Invalid ("alone") '@' symbols.

Attributes

Graph
Supertypes
class Object
trait Matchable
class Any

Members list

Type members

Classlikes

case class Error(template: Template, input: Input, errors: List[PosString]) extends ParseResult

Attributes

Supertypes
trait Serializable
trait Product
trait Equals
class ParseResult
class Object
trait Matchable
class Any
Show all
case class Input()

Attributes

Supertypes
trait Serializable
trait Product
trait Equals
class Object
trait Matchable
class Any
Show all
sealed abstract class ParseResult

Attributes

Supertypes
class Object
trait Matchable
class Any
Known subtypes
class Error
class Success
case class Success(template: Template, input: Input) extends ParseResult

Attributes

Supertypes
trait Serializable
trait Product
trait Equals
class ParseResult
class Object
trait Matchable
class Any
Show all

Value members

Concrete methods

def accept(str: String): Unit

Try to match str and advance str.length characters.

Try to match str and advance str.length characters.

Reports an error if the input does not match str or if str.length goes past the EOF.

Attributes

def any(length: Int): String

Consume/Advance length characters, and return the consumed characters. Returns "" if at EOF.

Consume/Advance length characters, and return the consumed characters. Returns "" if at EOF.

Attributes

def anyUntil(stop: String, inclusive: Boolean): String

Consume characters until input matches stop

Consume characters until input matches stop

Value parameters

inclusive
  • should stop be included in the consumed characters?

Attributes

Returns

the consumed characters

def anyUntil(f: Char => Boolean, inclusive: Boolean): String

Consume characters until f returns false on the peek of input.

Consume characters until f returns false on the peek of input.

Value parameters

inclusive
  • should the stopped character be included in the consumed characters?

Attributes

Returns

the consumed characters

def block(blockArgsAllowed: Boolean): Block
def brackets(): String
def check(f: Char => Boolean): Boolean

Does f applied to the current peek return true or false? If true, advance one character.

Does f applied to the current peek return true or false? If true, advance one character.

Will not advance if at EOF.

Attributes

Returns

true if advancing, false otherwise.

def check(str: String): Boolean

Does the current input match str? If so, advance str.length.

Does the current input match str? If so, advance str.length.

Will not advance if str.length surpasses EOF

Attributes

Returns

true if advancing, false otherwise.

def comment(): Comment

Parse a comment.

Parse a comment.

Attributes

def elseCall(): Seq[ScalaExpPart]
def elseIfCall(): Seq[ScalaExpPart]
def error(message: String, offset: Int): Unit
def expressionPart(blockArgsAllowed: Boolean): ScalaExpPart
def extraImports(): Seq[Simple]
def identifier(): String

Parses comments and/or whitespace, ignoring both until the last comment is reached, and returning that (if found)

Parses comments and/or whitespace, ignoring both until the last comment is reached, and returning that (if found)

Attributes

def localDef(): Def
def methodCall(): String
def mixed(): ListBuffer[TemplateTree]
def parentheses(): String
def parse(source: String): ParseResult
def plain(): Plain
def position[T <: Positional](positional: T, offset: Int): T

Set the source position of a Positional

Set the source position of a Positional

Attributes

def recursiveTag(prefix: String, suffix: String, allowStringLiterals: Boolean): String

Recursively match pairs of prefixes and suffixes and return the consumed characters

Recursively match pairs of prefixes and suffixes and return the consumed characters

Terminates at EOF.

Attributes

def several[T, BufferType <: Buffer[T]](parser: () => T, provided: BufferType)(implicit manifest: ClassTag[BufferType]): BufferType

Match zero or more parser

Match zero or more parser

Attributes

def squareBrackets(): String
def stringLiteral(quote: String, escape: String): String

Match a string literal, allowing for escaped quotes. Terminates at EOF.

Match a string literal, allowing for escaped quotes. Terminates at EOF.

Attributes

def templateContent(): (Seq[Simple], Seq[Def], Seq[Template], Seq[TemplateTree])
def whitespace(): String
def whitespaceNoBreak(): String

Concrete fields