class Zenithal::Parser

Constants

ERROR_TAG

Attributes

source[R]

Public Class Methods

new(source) click to toggle source
# File source/zenml/parser_utility.rb, line 10
def initialize(source)
  case source
  when Zenithal::StringReader
    @source = source
  when File
    @source = Zenithal::StringReader.new(source.read)
  else
    @source = Zenithal::StringReader.new(source.to_s)
  end
end

Public Instance Methods

run() click to toggle source
# File source/zenml/parser_utility.rb, line 32
def run
  value = nil
  message = catch(ERROR_TAG) do
    value = parse
  end
  unless value
    raise Zenithal::ZenithalParseError.new(message)
  end
  return value
end
update(source) click to toggle source
# File source/zenml/parser_utility.rb, line 21
def update(source)
  case source
  when Zenithal::StringReader
    @source = source
  when File
    @source = Zenithal::StringReader.new(source.read)
  else
    @source = Zenithal::StringReader.new(source.to_s)
  end
end

Private Instance Methods

catch_custom(&block) click to toggle source

Catch a parse error. Do not use the standard exception mechanism during parsing.

# File source/zenml/parser_utility.rb, line 204
def catch_custom(&block)
  catch(ERROR_TAG, &block)
end
choose(*parsers) click to toggle source

First this method executes the first specified parser. If it fails without consuming any input, then this method tries the next specified parser and repeats this procedure.

# File source/zenml/parser_utility.rb, line 147
def choose(*parsers)
  value, message = nil, ""
  parsers.each do |parser|
    mark = @source.mark
    message = catch_custom do
      value = parser.call
    end
    if value
      break
    elsif mark != @source.mark
      break
    end
  end
  unless value
    throw_custom(message, true)
  end
  return value
end
error_message(message) click to toggle source
# File source/zenml/parser_utility.rb, line 217
def error_message(message)
  return "[line #{@source.lineno}, column #{@source.columnno}] #{message}"
end
many(parser, range = 0..) click to toggle source
# File source/zenml/parser_utility.rb, line 166
def many(parser, range = 0..)
  values, message, count = [], "", 0
  lower_limit, upper_limit = range.begin, range.end
  if upper_limit && range.exclude_end?
    upper_limit -= 1
  end
  loop do
    mark = @source.mark
    value = nil
    message = catch_custom do
      value = parser.call
    end
    if value
      values << value
      count += 1
      if upper_limit && count >= upper_limit
        break
      end
    else
      if mark != @source.mark
        throw_custom(message, true)
      end
      break
    end
  end
  unless count >= lower_limit
    throw_custom(message, true)
  end
  return values
end
maybe(parser) click to toggle source
# File source/zenml/parser_utility.rb, line 197
def maybe(parser)
  value = many(parser, 0..1).first
  return value
end
parse() click to toggle source

Parses a whole data. This method is intended to be overridden in subclasses.

# File source/zenml/parser_utility.rb, line 47
def parse
  throw_custom("Not implemented")
  return nil
end
parse_char(query = nil) click to toggle source

Parses a single character which matches the specified query. If the next character does not match the query or the end of file is reached, then an error occurs and no input is consumed. Otherwise, a string which consists of the matched single chracter is returned.

# File source/zenml/parser_utility.rb, line 55
def parse_char(query = nil)
  char = @source.peek
  if char
    predicate, message = false, nil
    case query
    when String
      predicate = query == char
      message = "Expected '#{query}' but got '#{char}'"
    when Regexp
      predicate = query =~ char
      message = "Expected /#{query}/ but got '#{char}'"
    when Integer
      predicate = query == char.ord
      message = "Expected '##{query}' but got '#{char}'"
    when Range
      predicate = query.cover?(char.ord)
      symbol = (query.exclude_end?) ? "..." : ".."
      message = "Expected '##{query.begin}'#{symbol}'##{query.end}' but got '#{char}'"
    when NilClass
      predicate = true
      message = ""
    end
    unless predicate
      throw_custom(message)
    end
  else
    throw_custom("Unexpected end of file")
  end
  char = @source.read
  return char
end
parse_char_any(queries) click to toggle source

Parses a single character which matches any of the specified queries.

# File source/zenml/parser_utility.rb, line 88
def parse_char_any(queries)
  parsers = []
  queries.each do |query|
    parsers << ->{parse_char(query)}
  end
  char = choose(*parsers)
  return char
end
parse_char_out(chars) click to toggle source

Parses a single character other than the specified characters. If the next character coincides with any of the elements of the arguments, then an error occurs and no input is consumed. Otherwise, a string which consists of the next single chracter is returned.

# File source/zenml/parser_utility.rb, line 100
def parse_char_out(chars)
  char = @source.peek
  if char
    if chars.any?{|s| s == char}
      chars_string = chars.map{|s| "'#{s}'"}.join(", ")
      throw_custom("Expected other than #{chars_string} but got '#{char}'")
    end
  else
    throw_custom("Unexpected end of file")
  end
  char = @source.read
  return char
end
parse_eof() click to toggle source
# File source/zenml/parser_utility.rb, line 114
def parse_eof
  char = @source.peek
  if char
    throw_custom("Document ends before reaching end of file")
  end
  char = @source.read
  return true
end
parse_none() click to toggle source

Parses nothing; thus an error always occur.

# File source/zenml/parser_utility.rb, line 124
def parse_none
  throw_custom("This cannot happen")
  return nil
end
throw_custom(message, raw = false) click to toggle source

Raises a parse error. Do not use the standard exception mechanism during parsing, and always use this method to avoid creating an unnecessary stacktrace.

# File source/zenml/parser_utility.rb, line 210
def throw_custom(message, raw = false)
  unless raw
    message = error_message(message)
  end
  throw(ERROR_TAG, message)
end
try(parser) click to toggle source

Simply executes the specified parser, but additionally performs backtracking on error. If an error occurs in executing the parser, this method rewinds the state of the input to that before executing, and then raises an error. Otherwise, a result obtained by the parser is returned.

# File source/zenml/parser_utility.rb, line 132
def try(parser)
  mark = @source.mark
  value = nil
  message = catch_custom do
    value = parser.call
  end
  unless value
    @source.reset(mark)
    throw_custom(message, true)
  end
  return value
end