Class: Mustache::Parser

Inherits:
Object
  • Object
show all
Defined in:
lib/mustache/parser.rb

Overview

The Parser is responsible for taking a string template and converting it into an array of tokens and, really, expressions. It raises SyntaxError if there is anything it doesn't understand and knows which sigil corresponds to which tag type.

For example, given this template:

Hi {{thing}}!

Run through the Parser we'll get these tokens:

[:multi,
  [:static, "Hi "],
  [:mustache, :etag, "thing"],
  [:static, "!\n"]]

You can see the array of tokens for any template with the mustache(1) command line tool:

$ mustache --tokens test.mustache
[:multi, [:static, "Hi "], [:mustache, :etag, "thing"], [:static, "!\n"]]

Defined Under Namespace

Classes: SyntaxError

Constant Summary collapse

VALID_TYPES =

The sigil types which are valid after an opening `{{`

[ '#', '^', '/', '=', '!', '<', '>', '&', '{' ].map(&:freeze)
SKIP_WHITESPACE =

After these types of tags, all whitespace until the end of the line will be skipped if they are the first (and only) non-whitespace content on the line.

[ '#', '^', '/', '<', '>', '=', '!' ].map(&:freeze)
ALLOWED_CONTENT =

The content allowed in a tag name.

/(\w|[?!\/.-])*/
ANY_CONTENT =

These types of tags allow any content, the rest only allow ALLOWED_CONTENT.

[ '!', '=' ].map(&:freeze)

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(options = {}) ⇒ Parser

Accepts an options hash which does nothing but may be used in the future.


91
92
93
# File 'lib/mustache/parser.rb', line 91

def initialize(options = {})
  @options = {}
end

Instance Attribute Details

#ctagObject

The closing tag delimiter. This too may be changed at runtime.


101
102
103
# File 'lib/mustache/parser.rb', line 101

def ctag
  @ctag ||= '}}'
end

#otagObject

The opening tag delimiter. This may be changed at runtime.


96
97
98
# File 'lib/mustache/parser.rb', line 96

def otag
  @otag ||= '{{'
end

Class Method Details

.add_type(*types, &block) ⇒ Object

Add a supported sigil type (with optional aliases) to the Parser.

Requires a block, which will be sent the following parameters:

  • content - The raw content of the tag

  • fetch- A mustache context fetch expression for the content

  • padding - Indentation whitespace from the currently-parsed line

  • pre_match_position - Location of the scanner before a match was made

The provided block will be evaluated against the current instance of Parser, and may append to the Parser's @result as needed.


65
66
67
68
69
70
71
72
73
# File 'lib/mustache/parser.rb', line 65

def self.add_type(*types, &block)
  types = types.map(&:to_s)
  type, *aliases = types
  method_name = "scan_tag_#{type}".to_sym
  define_method(method_name, &block)
  aliases.each { |a| alias_method "scan_tag_#{a}", method_name }
  types.each { |t| VALID_TYPES << t unless VALID_TYPES.include?(t) }
  @valid_types = nil
end

.valid_typesObject


50
51
52
# File 'lib/mustache/parser.rb', line 50

def self.valid_types
  @valid_types ||= Regexp.new(VALID_TYPES.map { |t| Regexp.escape(t) }.join('|') )
end

Instance Method Details

#compile(template) ⇒ Object

Given a string template, returns an array of tokens.


106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
# File 'lib/mustache/parser.rb', line 106

def compile(template)
  @encoding = nil

  if template.respond_to?(:encoding)
    @encoding = template.encoding
    template = template.dup.force_encoding("BINARY")
  end

  # Keeps information about opened sections.
  @sections = []
  @result = [:multi]
  @scanner = StringScanner.new(template)

  # Scan until the end of the template.
  until @scanner.eos?
    scan_tags || scan_text
  end

  if !@sections.empty?
    # We have parsed the whole file, but there's still opened sections.
    type, pos, _ = @sections.pop
    error "Unclosed section #{type.inspect}", pos
  end

  @result
end