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:
parent
eda4a55e08
commit
3f3cb8a2e6
4 changed files with 106 additions and 98 deletions
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
Loading…
Reference in a new issue