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
94
95
96
97
98
99
100
101
102
# File 'lib/mustache/parser.rb', line 91

def initialize(options = {})
  @options = options
  @option_inline_partials_at_compile_time = options[:inline_partials_at_compile_time]
  if @option_inline_partials_at_compile_time
    @partial_resolver = options[:partial_resolver]
    raise ArgumentError.new "Missing or invalid partial_resolver" unless @partial_resolver.respond_to? :call
  end

  # Initialize default tags
  self.otag ||= '{{'
  self.ctag ||= '}}'
end

Instance Attribute Details

#ctagObject

Returns the value of attribute ctag.



87
88
89
# File 'lib/mustache/parser.rb', line 87

def ctag
  @ctag
end

#otagObject

Returns the value of attribute otag.



87
88
89
# File 'lib/mustache/parser.rb', line 87

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.



119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
# File 'lib/mustache/parser.rb', line 119

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

  unless @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