class Rbexy::Parser

Attributes

position[RW]
tokens[R]

Public Class Methods

new(tokens) click to toggle source
# File lib/rbexy/parser.rb, line 8
def initialize(tokens)
  @tokens = tokens
  @position = 0
end

Public Instance Methods

parse() click to toggle source
# File lib/rbexy/parser.rb, line 13
def parse
  validate_tokens!
  Nodes::Template.new(parse_tokens)
end
parse_attr() click to toggle source
# File lib/rbexy/parser.rb, line 102
def parse_attr
  name = take!(:ATTR_NAME)[1]
  value = nil

  if take(:OPEN_ATTR_VALUE)
    value = parse_text || parse_expression
    raise ParseError, "Missing attribute value" unless value
    take(:CLOSE_ATTR_VALUE)
  else
    value = default_empty_attr_value
  end

  Nodes::XmlAttr.new(name, value)
end
parse_attrs() click to toggle source
# File lib/rbexy/parser.rb, line 75
def parse_attrs
  return [] unless take(:OPEN_ATTRS)

  attrs = []

  eventually!(:CLOSE_ATTRS)
  until take(:CLOSE_ATTRS)
    attrs << (parse_splat_attr || parse_silent_newline || parse_attr)
  end

  attrs
end
parse_children() click to toggle source
# File lib/rbexy/parser.rb, line 117
def parse_children
  children = []

  eventually!(:OPEN_TAG_END)
  until take(:OPEN_TAG_END)
    children << parse_token
  end

  take(:TAG_NAME)
  take!(:CLOSE_TAG_END)

  children
end
parse_expression() click to toggle source
# File lib/rbexy/parser.rb, line 37
def parse_expression
  return unless take(:OPEN_EXPRESSION)

  statements = []

  eventually!(:CLOSE_EXPRESSION)
  until take(:CLOSE_EXPRESSION)
    statements << (parse_expression_body || parse_tag)
  end

  Nodes::ExpressionGroup.new(statements)
end
parse_expression!() click to toggle source
# File lib/rbexy/parser.rb, line 50
def parse_expression!
  peek!(:OPEN_EXPRESSION)
  parse_expression
end
parse_expression_body() click to toggle source
# File lib/rbexy/parser.rb, line 55
def parse_expression_body
  return unless token = take(:EXPRESSION_BODY)
  Nodes::Expression.new(token[1])
end
parse_silent_newline() click to toggle source
# File lib/rbexy/parser.rb, line 97
def parse_silent_newline
  return unless take(:SILENT_NEWLINE)
  Nodes::SilentNewline.new
end
parse_splat_attr() click to toggle source
# File lib/rbexy/parser.rb, line 88
def parse_splat_attr
  return unless take(:OPEN_ATTR_SPLAT)

  expression = parse_expression!
  take!(:CLOSE_ATTR_SPLAT)

  expression
end
parse_tag() click to toggle source
# File lib/rbexy/parser.rb, line 60
def parse_tag
  return unless take(:OPEN_TAG_DEF)

  name = take!(:TAG_NAME)
  members = []
  members.concat(take_all(:SILENT_NEWLINE).map { Nodes::SilentNewline.new })
  members.concat(parse_attrs)

  take!(:CLOSE_TAG_DEF)

  children = parse_children

  Nodes::XmlNode.new(name[1], members, children)
end
parse_text() click to toggle source
# File lib/rbexy/parser.rb, line 32
def parse_text
  return unless token = take(:TEXT)
  Nodes::Text.new(token[1])
end
parse_token() click to toggle source
# File lib/rbexy/parser.rb, line 28
def parse_token
  parse_text || parse_silent_newline || parse_expression || parse_tag || parse_declaration
end
parse_tokens() click to toggle source
# File lib/rbexy/parser.rb, line 18
def parse_tokens
  results = []

  while result = parse_token
    results << result
  end

  results
end

Private Instance Methods

default_empty_attr_value() click to toggle source
# File lib/rbexy/parser.rb, line 172
def default_empty_attr_value
  Nodes::Text.new("")
end
error_window() click to toggle source
# File lib/rbexy/parser.rb, line 176
def error_window
  window_start = position - 2
  window_start = 0 if window_start < 0
  window_end = position + 2
  window_end = tokens.length-1 if window_end >= tokens.length

  tokens[window_start..window_end].map.with_index do |token, i|
    prefix = window_start + i == position ? "=>" : "  "
    "#{prefix} #{token}"
  end.join("\n")
end
eventually!(token_name) click to toggle source
# File lib/rbexy/parser.rb, line 167
def eventually!(token_name)
  tokens[position..-1].first { |t| t[0] == token_name } ||
    raise(ParseError, "Expected to find a #{token_name} but never did")
end
parse_declaration() click to toggle source
# File lib/rbexy/parser.rb, line 133
def parse_declaration
  return unless token = take(:DECLARATION)
  Nodes::Declaration.new(token[1])
end
peek(token_name) click to toggle source
# File lib/rbexy/parser.rb, line 157
def peek(token_name)
  if (token = tokens[position]) && token[0] == token_name
    token
  end
end
peek!(token_name) click to toggle source
# File lib/rbexy/parser.rb, line 163
def peek!(token_name)
  peek(token_name) || unexpected_token!(token_name)
end
take(token_name) click to toggle source
# File lib/rbexy/parser.rb, line 138
def take(token_name)
  if token = peek(token_name)
    self.position += 1
    token
  end
end
take!(token_name) click to toggle source
# File lib/rbexy/parser.rb, line 153
def take!(token_name)
  take(token_name) || unexpected_token!(token_name)
end
take_all(token_name) click to toggle source
# File lib/rbexy/parser.rb, line 145
def take_all(token_name)
  result = []
  while token = take(token_name)
    result << token
  end
  result
end
unexpected_token!(expected_token) click to toggle source
# File lib/rbexy/parser.rb, line 188
def unexpected_token!(expected_token)
  raise(ParseError, "Unexpected token #{tokens[position][0]}, expecting #{expected_token}\n#{error_window}")
end
validate_all_tags_close!() click to toggle source
# File lib/rbexy/parser.rb, line 196
def validate_all_tags_close!
  open_count = tokens.count { |t| t[0] == :OPEN_TAG_DEF }
  close_count = tokens.count { |t| t[0] == :OPEN_TAG_END }
  if open_count != close_count
    raise(ParseError, "#{open_count - close_count} tags fail to close. All tags must close, either <NAME></NAME> or self-closing <NAME />")
  end
end
validate_tokens!() click to toggle source
# File lib/rbexy/parser.rb, line 192
def validate_tokens!
  validate_all_tags_close!
end