1
0
Fork 0
mirror of https://github.com/haml/haml.git synced 2022-11-09 12:33:31 -05:00

Make Parser a class and remove from Engine

You can now instantiate your own Haml parser separately from the Haml
engine if for some reason you should want to. Eventually this will be
useful for tools that convert Haml to Erb, for example. However for the
moment the primary motivation is simply to guarantee a stricter separation
of concerns between the parser, compiler and engine.
This commit is contained in:
Norman Clarke 2012-05-31 16:23:07 -03:00
parent eda4a55e08
commit 3f3cb8a2e6
4 changed files with 106 additions and 98 deletions

View file

@ -16,7 +16,6 @@ module Haml
# output = haml_engine.render # output = haml_engine.render
# puts output # puts output
class Engine class Engine
include Parser
include Compiler include Compiler
# The options hash. # The options hash.
@ -70,6 +69,23 @@ module Haml
end end
end end
DEFAULT_OPTIONS = {
:suppress_eval => false,
:attr_wrapper => "'",
# Don't forget to update the docs in doc-src/REFERENCE.md
# if you update these
:autoclose => %w[meta img link br hr input area param col base],
:preserve => %w[textarea pre code],
:filename => '(haml)',
:line => 1,
:ugly => false,
:format => :xhtml,
:escape_html => false,
:escape_attrs => true,
:hyphenate_data_attrs => true,
}
# Precompiles the Haml template. # Precompiles the Haml template.
# #
# @param template [String] The Haml template # @param template [String] The Haml template
@ -77,31 +93,15 @@ module Haml
# see {file:REFERENCE.md#options the Haml options documentation} # see {file:REFERENCE.md#options the Haml options documentation}
# @raise [Haml::Error] if there's a Haml syntax error in the template # @raise [Haml::Error] if there's a Haml syntax error in the template
def initialize(template, options = {}) def initialize(template, options = {})
@options = {
:suppress_eval => false,
:attr_wrapper => "'",
# Don't forget to update the docs in doc-src/REFERENCE.md
# if you update these
:autoclose => %w[meta img link br hr input area param col base],
:preserve => %w[textarea pre code],
:filename => '(haml)',
:line => 1,
:ugly => false,
:format => :xhtml,
:escape_html => false,
:escape_attrs => true,
:hyphenate_data_attrs => true,
}
@index = nil # explicitily initialize to avoid warnings @index = nil # explicitily initialize to avoid warnings
@options = DEFAULT_OPTIONS.dup.merge! options.reject {|k, v| v.nil?}
template = check_haml_encoding(template) do |msg, line| @template = check_haml_encoding(template) do |msg, line|
raise Haml::Error.new(msg, line) raise Haml::Error.new(msg, line)
end end
set_up_encoding(options, template) set_up_encoding
@options.merge! options.reject {|k, v| v.nil?}
@index = 0 @index = 0
@options[:format] = :xhtml if @options[:mime_type] == 'text/xml' @options[:format] = :xhtml if @options[:mime_type] == 'text/xml'
@ -110,19 +110,13 @@ module Haml
raise Haml::Error, "Invalid output format #{@options[:format].inspect}" raise Haml::Error, "Invalid output format #{@options[:format].inspect}"
end end
# :eod is a special end-of-document marker @parser = Parser.new(@template, @options)
@template = (template.rstrip).split(/\r\n|\r|\n/) + [:eod, :eod]
@template_index = 0
@to_close_stack = []
@output_tabs = 0
@template_tabs = 0
@flat = false
@newlines = 0
@precompiled = ''
@to_merge = []
@tab_change = 0
compile(parse) @output_tabs = 0
@to_merge = []
@precompiled = ''
compile(@parser.parse)
rescue Haml::Error => e rescue Haml::Error => e
if @index || e.line if @index || e.line
e.backtrace.unshift "#{@options[:filename]}:#{(e.line ? e.line + 1 : @index) + @options[:line] - 1}" e.backtrace.unshift "#{@options[:filename]}:#{(e.line ? e.line + 1 : @index) + @options[:line] - 1}"
@ -307,15 +301,19 @@ module Haml
private private
if RUBY_VERSION < "1.9" if RUBY_VERSION < "1.9"
def set_up_encoding(options, template) def set_up_encoding
options
end end
else else
def set_up_encoding(options, template) def set_up_encoding
@options.tap do |ops| @options.tap do |ops|
ops[:encoding] = Encoding.default_internal || template.encoding ops[:encoding] ||= Encoding.default_internal || @template.encoding
ops[:encoding] = "utf-8" if ops[:encoding].name == "US-ASCII" if ops[:encoding].is_a?(Encoding)
ops[:encoding] = ops[:encoding].name if ops[:encoding].is_a?(Encoding) if ops[:encoding].name == "US-ASCII"
ops[:encoding] = "utf-8"
else
ops[:encoding] = ops[:encoding].name
end
end
end end
end end
end end

View file

@ -78,7 +78,7 @@ module Hpricot
text.split("\n").map do |line| text.split("\n").map do |line|
line.strip! line.strip!
"#{tabulate(tabs)}#{'\\' if Haml::Engine::SPECIAL_CHARACTERS.include?(line[0])}#{line}\n" "#{tabulate(tabs)}#{'\\' if Haml::Parser::SPECIAL_CHARACTERS.include?(line[0])}#{line}\n"
end.join end.join
end end
end end

View file

@ -1,7 +1,7 @@
require 'strscan' require 'strscan'
module Haml module Haml
module Parser class Parser
include Haml::Util include Haml::Util
# Designates an XHTML/XML element. # Designates an XHTML/XML element.
@ -75,6 +75,54 @@ module Haml
# The Regex that matches a literal string or symbol value # The Regex that matches a literal string or symbol value
LITERAL_VALUE_REGEX = /:(\w*)|(["'])((?![\\#]|\2).|\\.)*\2/ LITERAL_VALUE_REGEX = /:(\w*)|(["'])((?![\\#]|\2).|\\.)*\2/
def initialize(template, options)
# :eod is a special end-of-document marker
@template = (template.rstrip).split(/\r\n|\r|\n/) + [:eod, :eod]
@options = options
@flat = false
@template_index = 0
@template_tabs = 0
end
def parse
@root = @parent = ParseNode.new(:root)
@haml_comment = false
@indentation = nil
@line = next_line
raise SyntaxError.new("Indenting at the beginning of the document is illegal.", @line.index) if @line.tabs != 0
while next_line
process_indent(@line) unless @line.text.empty?
if flat?
text = @line.full.dup
text = "" unless text.gsub!(/^#{@flat_spaces}/, '')
@filter_buffer << "#{text}\n"
@line = @next_line
next
end
@tab_up = nil
process_line(@line.text, @line.index) unless @line.text.empty? || @haml_comment
if @parent.type != :haml_comment && (block_opened? || @tab_up)
@template_tabs += 1
@parent = @parent.children.last
end
if !@haml_comment && !flat? && @next_line.tabs - @line.tabs > 1
raise SyntaxError.new("The line was indented #{@next_line.tabs - @line.tabs} levels deeper than the previous line.", @next_line.index)
end
@line = @next_line
end
# Close all the open tags
close until @parent.type == :root
@root
end
private private
# @private # @private
@ -124,44 +172,6 @@ END
end end
end end
def parse
@root = @parent = ParseNode.new(:root)
@haml_comment = false
@indentation = nil
@line = next_line
raise SyntaxError.new("Indenting at the beginning of the document is illegal.", @line.index) if @line.tabs != 0
while next_line
process_indent(@line) unless @line.text.empty?
if flat?
text = @line.full.dup
text = "" unless text.gsub!(/^#{@flat_spaces}/, '')
@filter_buffer << "#{text}\n"
@line = @next_line
next
end
@tab_up = nil
process_line(@line.text, @line.index) unless @line.text.empty? || @haml_comment
if @parent.type != :haml_comment && (block_opened? || @tab_up)
@template_tabs += 1
@parent = @parent.children.last
end
if !@haml_comment && !flat? && @next_line.tabs - @line.tabs > 1
raise SyntaxError.new("The line was indented #{@next_line.tabs - @line.tabs} levels deeper than the previous line.", @next_line.index)
end
@line = @next_line
end
# Close all the open tags
close until @parent.type == :root
@root
end
# Processes and deals with lowering indentation. # Processes and deals with lowering indentation.
def process_indent(line) def process_indent(line)
return unless line.tabs <= @template_tabs && @template_tabs > 0 return unless line.tabs <= @template_tabs && @template_tabs > 0
@ -675,26 +685,6 @@ END
!((text[-3..-2] =~ /\W\?/) || text[-3..-2] == "?\\") !((text[-3..-2] =~ /\W\?/) || text[-3..-2] == "?\\")
end end
def contains_interpolation?(str)
str.include?('#{')
end
def unescape_interpolation(str, escape_html = nil)
res = ''
rest = Haml::Util.handle_interpolation str.dump do |scan|
escapes = (scan[2].size - 1) / 2
res << scan.matched[0...-3 - escapes]
if escapes % 2 == 1
res << '#{'
else
content = eval('"' + balance(scan, ?{, ?}, 1)[0][0...-1] + '"')
content = "Haml::Helpers.html_escape((#{content}))" if escape_html
res << '#{' + content + "}"# Use eval to get rid of string escapes
end
end
res + rest
end
def balance(*args) def balance(*args)
res = Haml::Util.balance(*args) res = Haml::Util.balance(*args)
return res if res return res if res

View file

@ -319,6 +319,26 @@ METHOD
"#{indentation.length} #{noun}#{'s' unless singular}#{was}" "#{indentation.length} #{noun}#{'s' unless singular}#{was}"
end end
def contains_interpolation?(str)
str.include?('#{')
end
def unescape_interpolation(str, escape_html = nil)
res = ''
rest = Haml::Util.handle_interpolation str.dump do |scan|
escapes = (scan[2].size - 1) / 2
res << scan.matched[0...-3 - escapes]
if escapes % 2 == 1
res << '#{'
else
content = eval('"' + balance(scan, ?{, ?}, 1)[0][0...-1] + '"')
content = "Haml::Helpers.html_escape((#{content}))" if escape_html
res << '#{' + content + "}"# Use eval to get rid of string escapes
end
end
res + rest
end
private private
# Parses a magic comment at the beginning of a Haml file. # Parses a magic comment at the beginning of a Haml file.