require 'hamlet/forked_slim_parser' # @api private module Hamlet class Parser < ForkedSlim::Parser if RUBY_VERSION > '1.9' CLASS_ID_REGEX = /\A\s*(#|\.)([\w\u00c0-\uFFFF][\w:\u00c0-\uFFFF-]*)/ else CLASS_ID_REGEX = /\A\s*(#|\.)(\w[\w:-]*)/ end # Compile string to Temple expression # # @param [String] str Slim code # @return [Array] Temple expression representing the code]] def call(str) # Set string encoding if option is set if options[:encoding] && str.respond_to?(:encoding) old = str.encoding str = str.dup if str.frozen? str.force_encoding(options[:encoding]) # Fall back to old encoding if new encoding is invalid str.force_encoding(old_enc) unless str.valid_encoding? end result = [:multi] reset(str.split($/), [result]) while @lines.first && @lines.first =~ /\A\s*\Z/ @stacks.last << [:newline] next_line end if @lines.first and @lines.first =~ /\A]*)/i if !$'.empty? and $'[0] !~ /\s*#/ fail("did not expect content after doctype") end @stacks.last << [:html, :doctype, $1] next_line end parse_line while next_line reset result end private def parse_line if @line =~ /\A\s*\Z/ @stacks.last << [:newline] return end indent = get_indent(@line) # Remove the indentation @line.lstrip! indent +=1 if @line[0] == '>' # If there's more stacks than indents, it means that the previous # line is expecting this line to be indented. expecting_indentation = @stacks.size > @indents.size if indent > @indents.last # This line was actually indented, so we'll have to check if it was # supposed to be indented or not. unless expecting_indentation syntax_error!('Unexpected indentation') end @indents << indent else # This line was *not* indented more than the line before, # so we'll just forget about the stack that the previous line pushed. @stacks.pop if expecting_indentation # This line was deindented. # Now we're have to go through the all the indents and figure out # how many levels we've deindented. while indent < @indents.last @indents.pop @stacks.pop end # This line's indentation happens lie "between" two other line's # indentation: # # hello # world # this # <- This should not be possible! syntax_error!('Malformed indentation') if indent != @indents.last end parse_line_indicators end def parse_line_indicators if @needs_space and not @line[0] == '>' @stacks.last << [:slim, :interpolate, "\n" ] @stacks.last << [:newline] end @needs_space = false case @line[0] when '-' # code block. block = [:multi] @line.slice!(0) @stacks.last << [:slim, :control, parse_broken_line, block] @stacks << block when '=' # output block. @line =~ /\A=(=?)('?)/ @line = $' block = [:multi] @stacks.last << [:slim, :output, $1.empty?, parse_broken_line, block] @stacks.last << [:static, ' '] unless $2.empty? @stacks << block when '<' case @line when /\A<(\w+):\s*\Z/ # Embedded template. It is treated as block. block = [:multi] @stacks.last << [:newline] << [:slim, :embedded, $1, block] @stacks << block parse_text_block(nil, :from_embedded) return # Don't append newline, this has already been done before when /\A<([#\.]|\w[:\w-]*)/ # HTML tag. parse_tag($1) when /\A