class HamdownCore::Parser
Constants
- BLOCK_KEYWORD_REGEX
- COMMENT_PREFIX
- CONDITIONAL_COMMENT_REGEX
- DIV_CLASS_PREFIX
- DIV_ID_PREFIX
- DOCTYPE_PREFIX
- ELEMENT_PREFIX
- ESCAPE_PREFIX
- FILTER_PREFIX
- HTML
- MARKDOWN
- MID_BLOCK_KEYWORDS
- SILENT_SCRIPT_PREFIX
- START_BLOCK_KEYWORDS
- START_BLOCK_KEYWORD_REGEX
Try to parse assignments to block starters as best as possible
Public Class Methods
new(options = {})
click to toggle source
# File lib/hamdown_core/parser.rb, line 14 def initialize(options = {}) @filename = options[:filename] end
Public Instance Methods
call(template_str)
click to toggle source
# File lib/hamdown_core/parser.rb, line 18 def call(template_str) @ast = Ast::Root.new @stack = [] @line_parser = LineParser.new(@filename, template_str) @indent_tracker = IndentTracker.new(on_enter: method(:indent_enter), on_leave: method(:indent_leave)) @filter_parser = FilterParser.new(@indent_tracker) while @line_parser.has_next? in_filter = !@ast.is_a?(Ast::HamlComment) && @filter_parser.enabled? line = @line_parser.next_line(in_filter: in_filter) if in_filter ast = @filter_parser.append(line) if ast @ast << ast end end unless @filter_parser.enabled? line_count = line.count("\n") line.delete!("\n") parse_line(line) line_count.times do @ast << create_node(Ast::Empty) end end end ast = @filter_parser.finish if ast @ast << ast end @indent_tracker.finish @ast rescue Error => e if @filename && e.lineno e.backtrace.unshift "#{@filename}:#{e.lineno}" end raise e end
Private Instance Methods
block_keyword(text)
click to toggle source
# File lib/hamdown_core/parser.rb, line 307 def block_keyword(text) m = text.match(BLOCK_KEYWORD_REGEX) if m m[1] || m[2] end end
create_node(klass) { |node| ... }
click to toggle source
# File lib/hamdown_core/parser.rb, line 318 def create_node(klass, &block) klass.new.tap do |node| node.filename = @line_parser.filename node.lineno = @line_parser.lineno if block yield(node) end end end
indent_enter(_, _text)
click to toggle source
# File lib/hamdown_core/parser.rb, line 265 def indent_enter(_, _text) empty_lines = [] while @ast.children.last.is_a?(Ast::Empty) empty_lines << @ast.children.pop end @stack.push(@ast) @ast = @ast.children.last case @ast when Ast::Text syntax_error!('nesting within plain text is illegal') when Ast::Doctype syntax_error!('nesting within a header command is illegal') when nil syntax_error!('Indenting at the beginning of the document is illegal') end @ast.children = empty_lines if @ast.is_a?(Ast::Element) && @ast.self_closing syntax_error!('Illegal nesting: nesting within a self-closing tag is illegal') end if @ast.is_a?(Ast::HtmlComment) && !@ast.comment.empty? syntax_error!('Illegal nesting: nesting within a html comment that already has content is illegal.') end if @ast.is_a?(Ast::HamlComment) @indent_tracker.enter_comment! else @indent_tracker.check_indent_level!(@line_parser.lineno) end nil end
indent_leave(_indent_level, _text)
click to toggle source
# File lib/hamdown_core/parser.rb, line 295 def indent_leave(_indent_level, _text) parent_ast = @stack.pop @ast = parent_ast nil end
parse_comment(text)
click to toggle source
# File lib/hamdown_core/parser.rb, line 160 def parse_comment(text) text = text[1, text.size - 1].strip comment = create_node(Ast::HtmlComment) comment.comment = text if text[0] == '[' comment.conditional, rest = parse_conditional_comment(text) text.replace(rest) end @ast << comment end
parse_conditional_comment(text)
click to toggle source
# File lib/hamdown_core/parser.rb, line 173 def parse_conditional_comment(text) s = StringScanner.new(text[1..-1]) depth = Utils.balance(s, '[', ']') if depth == 0 [s.pre_match, s.rest.lstrip] else syntax_error!('Unmatched brackets in conditional comment') end end
parse_doctype(text)
click to toggle source
# File lib/hamdown_core/parser.rb, line 156 def parse_doctype(text) @ast << create_node(Ast::Doctype) { |d| d.doctype = text[3..-1].strip } end
parse_element(text)
click to toggle source
# File lib/hamdown_core/parser.rb, line 233 def parse_element(text) @ast << ElementParser.new(@line_parser).parse(text) end
parse_filter(text)
click to toggle source
# File lib/hamdown_core/parser.rb, line 257 def parse_filter(text) filter_name = text[/\A#{FILTER_PREFIX}(\w+)\z/, 1] unless filter_name syntax_error!("Invalid filter name: #{text}") end @filter_parser.start(filter_name, @line_parser.filename, @line_parser.lineno) end
parse_html_list(text, type)
click to toggle source
# File lib/hamdown_core/parser.rb, line 195 def parse_html_list(text, type) if type == :ol @ast << create_node(Ast::HtmlOlList) { |t| t.text = text } elsif type == :ul @ast << create_node(Ast::HtmlUlList) { |t| t.text = text } else raise 'undefined html list type' end end
parse_html_list_end(text)
click to toggle source
# File lib/hamdown_core/parser.rb, line 209 def parse_html_list_end(text) @ast << create_node(Ast::HtmlListEnd) { |t| t.text = text } end
parse_html_list_item(text)
click to toggle source
# File lib/hamdown_core/parser.rb, line 205 def parse_html_list_item(text) @ast << create_node(Ast::HtmlListItem) { |t| t.text = text } end
parse_line(line)
click to toggle source
# File lib/hamdown_core/parser.rb, line 86 def parse_line(line) text, indent = @indent_tracker.process(line, @line_parser.lineno) if text.empty? @ast << create_node(Ast::Empty) return end if @ast.is_a?(Ast::HamlComment) @ast << create_node(Ast::Text) { |t| t.text = text } return end case text when MARKDOWN['headers'] parse_md_header(text) when MARKDOWN['list_item'] parse_md_list(text) when MARKDOWN['quotes'] parse_md_quote(text) when MARKDOWN['image'] parse_md_image(text) when MARKDOWN['image_title'] parse_md_image(text, true) when MARKDOWN['link'] parse_md_link(text) when MARKDOWN['link_title'] parse_md_link(text, true) when HTML['list_ol_root'] parse_html_list(text, :ol) when HTML['list_ul_root'] parse_html_list(text, :ul) when HTML['list_end'] parse_html_list_end(text) when HTML['list_item'] parse_html_list_item(text) else std_parse_line(text, indent) end end
parse_md_header(text)
click to toggle source
# File lib/hamdown_core/parser.rb, line 187 def parse_md_header(text) @ast << create_node(Ast::MdHeader) { |t| t.text = text } end
parse_md_image(text, title = false)
click to toggle source
# File lib/hamdown_core/parser.rb, line 217 def parse_md_image(text, title = false) if title == true @ast << create_node(Ast::MdImageTitle) { |t| t.text = text } else @ast << create_node(Ast::MdImage) { |t| t.text = text } end end
parse_md_link(text, title = false)
click to toggle source
# File lib/hamdown_core/parser.rb, line 225 def parse_md_link(text, title = false) if title == true @ast << create_node(Ast::MdLinkTitle) { |t| t.text = text } else @ast << create_node(Ast::MdLink) { |t| t.text = text } end end
parse_md_list(text)
click to toggle source
# File lib/hamdown_core/parser.rb, line 191 def parse_md_list(text) @ast << create_node(Ast::MdList) { |t| t.text = text } end
parse_md_quote(text)
click to toggle source
# File lib/hamdown_core/parser.rb, line 213 def parse_md_quote(text) @ast << create_node(Ast::MdQuote) { |t| t.text = text } end
parse_plain(text)
click to toggle source
# File lib/hamdown_core/parser.rb, line 183 def parse_plain(text) @ast << create_node(Ast::Text) { |t| t.text = text } end
parse_script(text)
click to toggle source
# File lib/hamdown_core/parser.rb, line 237 def parse_script(text) node = ScriptParser.new(@line_parser).parse(text) if node.is_a?(Ast::Script) node.keyword = block_keyword(node.script) end @ast << node end
parse_silent_script(text)
click to toggle source
# File lib/hamdown_core/parser.rb, line 245 def parse_silent_script(text) if text.start_with?('-#') @ast << create_node(Ast::HamlComment) return end node = create_node(Ast::SilentScript) script = text[/\A- *(.*)\z/, 1] node.script = [script, *RubyMultiline.read(@line_parser, script)].join("\n") node.keyword = block_keyword(node.script) @ast << node end
std_parse_line(text, indent)
click to toggle source
# File lib/hamdown_core/parser.rb, line 127 def std_parse_line(text, indent) case text[0] when ESCAPE_PREFIX parse_plain(text[1..-1]) when ELEMENT_PREFIX parse_element(text) when DOCTYPE_PREFIX if text.start_with?('!!!') parse_doctype(text) else parse_script(text) end when COMMENT_PREFIX parse_comment(text) when SILENT_SCRIPT_PREFIX parse_silent_script(text) when DIV_ID_PREFIX, DIV_CLASS_PREFIX if text.start_with?('#{') parse_script(text) else parse_line("#{indent}%div#{text}") end when FILTER_PREFIX parse_filter(text) else parse_script(text) end end
syntax_error!(message)
click to toggle source
# File lib/hamdown_core/parser.rb, line 314 def syntax_error!(message) raise Error.new(message, @line_parser.lineno) end