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
|
# 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,14 +69,8 @@ module Haml
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Precompiles the Haml template.
|
|
||||||
#
|
DEFAULT_OPTIONS = {
|
||||||
# @param template [String] The Haml template
|
|
||||||
# @param options [{Symbol => Object}] An options hash;
|
|
||||||
# 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,
|
:suppress_eval => false,
|
||||||
:attr_wrapper => "'",
|
:attr_wrapper => "'",
|
||||||
# Don't forget to update the docs in doc-src/REFERENCE.md
|
# Don't forget to update the docs in doc-src/REFERENCE.md
|
||||||
|
@ -93,15 +86,22 @@ module Haml
|
||||||
:hyphenate_data_attrs => true,
|
:hyphenate_data_attrs => true,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Precompiles the Haml template.
|
||||||
|
#
|
||||||
|
# @param template [String] The Haml template
|
||||||
|
# @param options [{Symbol => Object}] An options hash;
|
||||||
|
# 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 = {})
|
||||||
@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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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.
|
||||||
|
|
Loading…
Reference in a new issue