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
# puts output
class Engine
include Parser
include Compiler
# The options hash.
@ -70,6 +69,23 @@ module Haml
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.
#
# @param template [String] The Haml template
@ -77,31 +93,15 @@ module Haml
# see {file:REFERENCE.md#options the Haml options documentation}
# @raise [Haml::Error] if there's a Haml syntax error in the template
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
@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)
end
set_up_encoding(options, template)
set_up_encoding
@options.merge! options.reject {|k, v| v.nil?}
@index = 0
@options[:format] = :xhtml if @options[:mime_type] == 'text/xml'
@ -110,19 +110,13 @@ module Haml
raise Haml::Error, "Invalid output format #{@options[:format].inspect}"
end
# :eod is a special end-of-document marker
@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
@parser = Parser.new(@template, @options)
compile(parse)
@output_tabs = 0
@to_merge = []
@precompiled = ''
compile(@parser.parse)
rescue Haml::Error => e
if @index || e.line
e.backtrace.unshift "#{@options[:filename]}:#{(e.line ? e.line + 1 : @index) + @options[:line] - 1}"
@ -307,15 +301,19 @@ module Haml
private
if RUBY_VERSION < "1.9"
def set_up_encoding(options, template)
options
def set_up_encoding
end
else
def set_up_encoding(options, template)
def set_up_encoding
@options.tap do |ops|
ops[:encoding] = Encoding.default_internal || template.encoding
ops[:encoding] = "utf-8" if ops[:encoding].name == "US-ASCII"
ops[:encoding] = ops[:encoding].name if ops[:encoding].is_a?(Encoding)
ops[:encoding] ||= Encoding.default_internal || @template.encoding
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

View file

@ -78,7 +78,7 @@ module Hpricot
text.split("\n").map do |line|
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
end

View file

@ -1,7 +1,7 @@
require 'strscan'
module Haml
module Parser
class Parser
include Haml::Util
# Designates an XHTML/XML element.
@ -75,6 +75,54 @@ module Haml
# The Regex that matches a literal string or symbol value
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
@ -124,44 +172,6 @@ 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.
def process_indent(line)
return unless line.tabs <= @template_tabs && @template_tabs > 0
@ -675,26 +685,6 @@ END
!((text[-3..-2] =~ /\W\?/) || text[-3..-2] == "?\\")
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)
res = Haml::Util.balance(*args)
return res if res

View file

@ -319,6 +319,26 @@ METHOD
"#{indentation.length} #{noun}#{'s' unless singular}#{was}"
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
# Parses a magic comment at the beginning of a Haml file.