module Zenithal::ZenithalParserMethod

Constants

ATTRIBUTE_END
ATTRIBUTE_EQUAL
ATTRIBUTE_SEPARATOR
ATTRIBUTE_START
COMMENT_DELIMITER
CONTENT_DELIMITER
CONTENT_END
CONTENT_START
ELEMENT_START
ESCAPE_CHARS
ESCAPE_START
MACRO_START
MARK_CHARS
SPACE_CHARS
SPECIAL_ELEMENT_ENDS
SPECIAL_ELEMENT_STARTS
STRING_END
STRING_START
SYSTEM_INSTRUCTION_NAME
VALID_FIRST_IDENTIFIER_CHARS
VALID_MIDDLE_IDENTIFIER_CHARS

Private Instance Methods

check_version() click to toggle source
# File source/zenml/parser.rb, line 449
def check_version
  string = @source.string
  if match = string.match(/\\zml\?\s*\|.*version\s*=\s*"(\d+\.\d+)".*\|/)
    @version = match[1]
  end
end
create_comment(kind, content, options) click to toggle source
# File source/zenml/parser.rb, line 422
def create_comment(kind, content, options)
  comment = REXML::Comment.new(" " + content.strip + " ")
  return comment
end
create_element(name, marks, attributes, children_list, options) click to toggle source
# File source/zenml/parser.rb, line 343
def create_element(name, marks, attributes, children_list, options)
  nodes = REXML::Nodes[]
  if marks.include?(:trim)
    children_list.each do |children|
      children.trim_indents
    end
  end
  if marks.include?(:instruction)
    unless children_list.size <= 1
      throw_custom("Processing instruction cannot have more than one argument")
    end
    nodes = create_instruction(name, attributes, children_list.first, options)
  else
    unless marks.include?(:multiple) || children_list.size <= 1
      throw_custom("Normal element cannot have more than one argument")
    end
    nodes = create_normal_element(name, attributes, children_list, options)
  end
  return nodes
end
create_escape(place, char, options) click to toggle source
# File source/zenml/parser.rb, line 427
def create_escape(place, char, options)
  unless ESCAPE_CHARS.include?(char)
    throw_custom("Invalid escape")
  end
  return char
end
create_instruction(target, attributes, children, options) click to toggle source
# File source/zenml/parser.rb, line 364
def create_instruction(target, attributes, children, options)
  nodes = REXML::Nodes[]
  if target == SYSTEM_INSTRUCTION_NAME
    @version = attributes["version"] if attributes["version"]
    @special_element_names[:brace] = attributes["brace"] if attributes["brace"]
    @special_element_names[:bracket] = attributes["bracket"] if attributes["bracket"]
    @special_element_names[:slash] = attributes["slash"] if attributes["slash"]
  elsif target == "xml"
    instruction = REXML::XMLDecl.new
    instruction.version = attributes["version"] || REXML::XMLDecl::DEFAULT_VERSION
    instruction.encoding = attributes["encoding"]
    instruction.standalone = attributes["standalone"]
    nodes << instruction
  else
    instruction = REXML::Instruction.new(target)
    actual_contents = []
    attributes.each do |key, value|
      actual_contents << "#{key}=\"#{value}\""
    end
    if children.first && !children.first.empty?
      actual_contents << children.first
    end
    instruction.content = actual_contents.join(" ")
    nodes << instruction
  end
  return nodes
end
create_normal_element(name, attributes, children_list, options) click to toggle source
# File source/zenml/parser.rb, line 392
def create_normal_element(name, attributes, children_list, options)
  nodes = REXML::Nodes[]
  children_list.each do |children|
    element = REXML::Element.new(name)
    attributes.each do |key, value|
      element.add_attribute(key, value)
    end
    children.each do |child|
      element.add(child)
    end
    nodes << element
  end
  return nodes
end
create_special_element(kind, children, options) click to toggle source
# File source/zenml/parser.rb, line 407
def create_special_element(kind, children, options)
  name = @special_element_names[kind]
  if name
    nodes = create_element(name, [], {}, [children], options)
  else
    throw_custom("No name specified for #{kind} elements")
  end
  return nodes
end
create_text(raw_text, options) click to toggle source
# File source/zenml/parser.rb, line 417
def create_text(raw_text, options)
  text = REXML::Text.new(raw_text, true, nil, false)
  return text
end
determine_options(name, marks, attributes, macro, options) click to toggle source
# File source/zenml/parser.rb, line 331
def determine_options(name, marks, attributes, macro, options)
  if marks.include?(:verbal)
    options = options.clone
    options[:verbal] = true
  end
  if macro && @plugins.key?(name)
    options = options.clone
    options[:plugin] = @plugins[name].call(attributes)
  end
  return options
end
parse() click to toggle source
# File source/zenml/parser.rb, line 45
def parse
  if @whole
    parse_document
  else
    parse_nodes({})
  end
end
parse_attribute(first, options) click to toggle source
# File source/zenml/parser.rb, line 155
def parse_attribute(first, options)
  unless first
    parse_char(ATTRIBUTE_SEPARATOR) 
  end
  parse_space
  name = parse_identifier(options)
  parse_space
  value = maybe(->{parse_attribute_value(options)}) || name
  parse_space
  attribute = {name => value}
  return attribute
end
parse_attribute_value(options) click to toggle source
# File source/zenml/parser.rb, line 168
def parse_attribute_value(options)
  parse_char(ATTRIBUTE_EQUAL)
  parse_space
  value = parse_string(options)
  return value
end
parse_attributes(options) click to toggle source
# File source/zenml/parser.rb, line 146
def parse_attributes(options)
  parse_char(ATTRIBUTE_START)
  first_attribute = parse_attribute(true, options)
  rest_attribtues = many(->{parse_attribute(false, options)})
  attributes = first_attribute.merge(*rest_attribtues)
  parse_char(ATTRIBUTE_END)
  return attributes
end
parse_block_comment(options) click to toggle source
# File source/zenml/parser.rb, line 285
def parse_block_comment(options)
  parse_char(CONTENT_START)
  content = parse_block_comment_content(options)
  parse_char(CONTENT_END)
  if @version == "1.0"
    parse_char(COMMENT_DELIMITER)
  end
  comment = create_comment(:block, content, options)
  return comment
end
parse_block_comment_content(options) click to toggle source
# File source/zenml/parser.rb, line 296
def parse_block_comment_content(options)
  chars = many(->{parse_char_out([CONTENT_END])})
  content = chars.join
  return content
end
parse_children(options) click to toggle source
# File source/zenml/parser.rb, line 220
def parse_children(options)
  parse_char(CONTENT_START)
  children = parse_nodes(options)
  parse_char(CONTENT_END)
  return children
end
parse_children_chain(options) click to toggle source
# File source/zenml/parser.rb, line 203
def parse_children_chain(options)
  if @version == "1.0"
    first_children = choose(->{parse_empty_children(options)}, ->{parse_children(options)})
    rest_children_list = many(->{parse_children(options)})
    children_list = [first_children] + rest_children_list
  else
    children_list = many(->{parse_children(options)}, 1..)
  end
  return children_list
end
parse_children_list(options) click to toggle source
# File source/zenml/parser.rb, line 194
def parse_children_list(options)
  if @version == "1.0"
    children_list = parse_children_chain(options)
  else
    children_list = choose(->{parse_empty_children_chain(options)}, ->{parse_children_chain(options)})
  end
  return children_list
end
parse_comment(options) click to toggle source
# File source/zenml/parser.rb, line 265
def parse_comment(options)
  parse_char(COMMENT_DELIMITER)
  comment = choose(->{parse_line_comment(options)}, ->{parse_block_comment(options)})
  return comment
end
parse_document() click to toggle source
# File source/zenml/parser.rb, line 53
def parse_document
  document = REXML::Document.new
  check_version
  children = parse_nodes({})
  if @exact
    parse_eof 
  end
  children.each do |child|
    document.add(child)
  end
  return document
end
parse_element(options) click to toggle source
# File source/zenml/parser.rb, line 95
def parse_element(options)
  name, marks, attributes, macro = parse_tag(options)
  next_options = determine_options(name, marks, attributes, macro, options)
  children_list = parse_children_list(next_options)
  if name == SYSTEM_INSTRUCTION_NAME
    parse_space
  end
  if macro
    element = process_macro(name, marks, attributes, children_list, options)
  else
    element = create_element(name, marks, attributes, children_list, options)
  end
  return element
end
parse_empty_children(options) click to toggle source
# File source/zenml/parser.rb, line 227
def parse_empty_children(options)
  if @version == "1.0"
    parse_char(CONTENT_END)
    children = REXML::Nodes[]
  else
    parse_char(CONTENT_DELIMITER)
    children = REXML::Nodes[]
  end
  return children
end
parse_empty_children_chain(options) click to toggle source
# File source/zenml/parser.rb, line 214
def parse_empty_children_chain(options)
  children = parse_empty_children(options)
  children_list = [children]
  return children_list
end
parse_escape(place, options) click to toggle source
# File source/zenml/parser.rb, line 302
def parse_escape(place, options)
  parse_char(ESCAPE_START)
  char = parse_char
  escape = create_escape(place, char, options)
  return escape
end
parse_first_identifier_char(options) click to toggle source
# File source/zenml/parser.rb, line 316
def parse_first_identifier_char(options)
  char = parse_char_any(VALID_FIRST_IDENTIFIER_CHARS)
  return char
end
parse_identifier(options) click to toggle source
# File source/zenml/parser.rb, line 309
def parse_identifier(options)
  first_char = parse_first_identifier_char(options)
  rest_chars = many(->{parse_middle_identifier_char(options)})
  identifier = first_char + rest_chars.join
  return identifier
end
parse_line_comment(options) click to toggle source
# File source/zenml/parser.rb, line 271
def parse_line_comment(options)
  parse_char(COMMENT_DELIMITER)
  content = parse_line_comment_content(options)
  parse_char("\n")
  comment = create_comment(:line, content, options)
  return comment
end
parse_line_comment_content(options) click to toggle source
# File source/zenml/parser.rb, line 279
def parse_line_comment_content(options)
  chars = many(->{parse_char_out(["\n"])})
  content = chars.join
  return content
end
parse_mark(options) click to toggle source
# File source/zenml/parser.rb, line 138
def parse_mark(options)
  parsers = MARK_CHARS.map do |mark, query|
    next ->{parse_char(query).yield_self{|_| mark}}
  end
  mark = choose(*parsers)
  return mark
end
parse_marks(options) click to toggle source
# File source/zenml/parser.rb, line 133
def parse_marks(options)
  marks = many(->{parse_mark(options)})
  return marks
end
parse_middle_identifier_char(options) click to toggle source
# File source/zenml/parser.rb, line 321
def parse_middle_identifier_char(options)
  char = parse_char_any(VALID_MIDDLE_IDENTIFIER_CHARS)
  return char
end
parse_nodes(options) click to toggle source
# File source/zenml/parser.rb, line 66
def parse_nodes(options)
  nodes = nil
  if options[:plugin]
    nodes = options[:plugin].send(:parse)
  elsif options[:verbal]
    raw_nodes = many(->{parse_text(options)})
    nodes = raw_nodes.inject(REXML::Nodes[], :<<)
  else
    parsers = []
    parsers << ->{parse_element(options)}
    if options[:in_slash]
      next_options = options.clone
      next_options.delete(:in_slash)
    else
      next_options = options
    end
    @special_element_names.each do |kind, name|
      unless kind == :slash && options[:in_slash]
        parsers << ->{parse_special_element(kind, next_options)}
      end
    end
    parsers << ->{parse_comment(options)}
    parsers << ->{parse_text(options)}
    raw_nodes = many(->{choose(*parsers)})
    nodes = raw_nodes.inject(REXML::Nodes[], :<<)
  end
  return nodes
end
parse_space() click to toggle source
# File source/zenml/parser.rb, line 326
def parse_space
  space = many(->{parse_char_any(SPACE_CHARS)})
  return space
end
parse_special_element(kind, options) click to toggle source
# File source/zenml/parser.rb, line 110
def parse_special_element(kind, options)
  parse_char(SPECIAL_ELEMENT_STARTS[kind])
  if kind == :slash
    next_options = options.clone
    next_options[:in_slash] = true
  else
    next_options = options
  end
  children = parse_nodes(next_options)
  parse_char(SPECIAL_ELEMENT_ENDS[kind])
  element = create_special_element(kind, children, options)
  return element
end
parse_string(options) click to toggle source
# File source/zenml/parser.rb, line 175
def parse_string(options)
  parse_char(STRING_START)
  strings = many(->{parse_string_plain_or_escape(options)})
  parse_char(STRING_END)
  string = strings.join
  return string
end
parse_string_plain(options) click to toggle source
# File source/zenml/parser.rb, line 183
def parse_string_plain(options)
  chars = many(->{parse_char_out([STRING_END, ESCAPE_START])}, 1..)
  string = chars.join
  return string
end
parse_string_plain_or_escape(options) click to toggle source
# File source/zenml/parser.rb, line 189
def parse_string_plain_or_escape(options)
  string = choose(->{parse_escape(:string, options)}, ->{parse_string_plain(options)})
  return string
end
parse_tag(options) click to toggle source
# File source/zenml/parser.rb, line 124
def parse_tag(options)
  start_char = parse_char_any([ELEMENT_START, MACRO_START])
  name = parse_identifier(options)
  marks = parse_marks(options)
  attributes = maybe(->{parse_attributes(options)}) || {}
  macro = start_char == MACRO_START
  return name, marks, attributes, macro
end
parse_text(options) click to toggle source
# File source/zenml/parser.rb, line 238
def parse_text(options)
  raw_texts = many(->{parse_text_plain_or_escape(options)}, 1..)
  text = create_text(raw_texts.join, options)
  return text
end
parse_text_plain(options) click to toggle source
# File source/zenml/parser.rb, line 244
def parse_text_plain(options)
  out_chars = [ESCAPE_START, CONTENT_END]
  unless options[:verbal]
    out_chars.push(ELEMENT_START, MACRO_START, CONTENT_START, COMMENT_DELIMITER)
    if @version == "1.1"
      out_chars.push(CONTENT_DELIMITER)
    end
    @special_element_names.each do |kind, name|
      out_chars.push(SPECIAL_ELEMENT_STARTS[kind], SPECIAL_ELEMENT_ENDS[kind])
    end
  end
  chars = many(->{parse_char_out(out_chars)}, 1..)
  string = chars.join
  return string
end
parse_text_plain_or_escape(options) click to toggle source
# File source/zenml/parser.rb, line 260
def parse_text_plain_or_escape(options)
  string = choose(->{parse_escape(:text, options)}, ->{parse_text_plain(options)})
  return string
end
process_macro(name, marks, attributes, children_list, options) click to toggle source
# File source/zenml/parser.rb, line 434
def process_macro(name, marks, attributes, children_list, options)
  elements = REXML::Nodes[]
  if @macros.key?(name)
    raw_elements = @macros[name].call(attributes, children_list)
    raw_elements.each do |raw_element|
      elements << raw_element
    end
  elsif @plugins.key?(name)
    elements = children_list.first
  else
    throw_custom("No such macro '#{name}'")
  end
  return elements
end