class HtmlConditionalComment::Parser

Parse tokens into a tree of nodes

Pseudo grammar

template = { html | statement } statement = “<!” , [ “–” ] , “if” , expression , “]” , [ “–” ] , “>” , template , “<!” , [ “–” ] , “endif” , “]” , [ “–” ] , “>” expression = term [ “|” , term ] term = factor [ “&” , factor ] factor = subexpression | “!” , factor | “(” , expression , “)” subexpression = [ operator ] browser | boolean operator = “gt” | “gte” | “lt” | “lte” boolean = “true” | “false” browser = feature [ version_vector ]

Constants

CLOSE
OPEN

Public Class Methods

new(tokens) click to toggle source
# File lib/html-conditional-comment/parser.rb, line 28
def initialize(tokens)
  @symbol = nil
  @tokens = tokens
  @max_pos = tokens.size() - 1
  @pos = -1
end

Public Instance Methods

parse() click to toggle source
# File lib/html-conditional-comment/parser.rb, line 35
def parse()
  self.next()

  nodes = template()

  #Tokens left, syntax error
  error() if @pos < @max_pos

  nodes
end

Protected Instance Methods

accept(symbol) click to toggle source

Accept the symbol and move on or not

# File lib/html-conditional-comment/parser.rb, line 206
def accept(symbol)
  if(symbol == @symbol)
    self.next()
    return true
  else
    return false
  end
end
boolean() click to toggle source

True or false

# File lib/html-conditional-comment/parser.rb, line 65
def boolean()
  if accept(:boolean_true)
    Nodes::True.instance()
  elsif accept(:boolean_false)
    Nodes::False.instance()
  else
    error()
  end
end
browser() click to toggle source

Browser is combination of feature and optional version

# File lib/html-conditional-comment/parser.rb, line 49
def browser()
  node = Nodes::Browser.new()
  node.feature = @value
  expect(:feature)

  if current(:version_vector)
    node.version_vector = VersionVector.new(@value)
    accept(:version_vector)
  else
    node.version_vector = VersionVector.new(nil)
  end

  node
end
condition() click to toggle source
# File lib/html-conditional-comment/parser.rb, line 153
def condition()
  node = Nodes::Condition.new()

  expect(:open)
  expect(:if)
  node.left = expression()

  #TODO Goofy confirmation of non-closing HTML comment
  if current(:close)
    error() if @value =~ OPEN
  end
  expect(:close)

  unless current(:open) && peek(:endif)
    node.right = template()
  end

  #TODO More goofyness
  if current(:open)
    error() if @value =~ CLOSE
  end
  expect(:open)

  expect(:endif)
  expect(:close)

  node
end
current(symbol) click to toggle source
# File lib/html-conditional-comment/parser.rb, line 220
def current(symbol)
  @symbol == symbol
end
error() click to toggle source
# File lib/html-conditional-comment/parser.rb, line 237
def error()
  raise HtmlConditionalComment::ParseError.new("Syntax error", @value)
end
expect(symbol) click to toggle source

Expect a current symbol or raise

# File lib/html-conditional-comment/parser.rb, line 216
def expect(symbol)
  raise HtmlConditionalComment::ParseError.new("Expected #{symbol}, received #{@symbol}", @value) unless accept(symbol)
end
expression() click to toggle source

Or

# File lib/html-conditional-comment/parser.rb, line 141
def expression()
  node = term()
  while accept(:operator_or)
    branch_node = Nodes::Or.new()
    branch_node.left = node
    branch_node.right = term()
    node = branch_node
  end

  node
end
factor() click to toggle source

Negated self or paranthesised expression

# File lib/html-conditional-comment/parser.rb, line 111
def factor()
  node = nil

  if accept(:operator_not)
    node = Nodes::Not.new()
    node.child = factor()
  elsif accept(:paren_open)
    node = expression()
    expect(:paren_close)
  else
    node = subexpression()
  end

  node
end
html() click to toggle source
# File lib/html-conditional-comment/parser.rb, line 182
def html()
  node = Nodes::Html.new()
  node.content = @value
  expect(:html)
  node
end
next() click to toggle source
# File lib/html-conditional-comment/parser.rb, line 228
def next()
  @pos += 1
  #raise HtmlConditionalComment::ParserError.new('EOF') if @pos >= @max_pos
  token = @tokens[@pos] || []
  @symbol = token[0]
  @value = token[1]
  token
end
operator() click to toggle source

Comparison operators

# File lib/html-conditional-comment/parser.rb, line 76
def operator()
  if accept(:operator_less_than)
    Nodes::LessThan.new()
  elsif accept(:operator_less_than_equal)
    Nodes::LessThanEqual.new()
  elsif accept(:operator_greater_than)
    Nodes::GreaterThan.new()
  elsif accept(:operator_greater_than_equal)
    Nodes::GreaterThanEqual.new()
  else
    error()
  end
end
peek(symbol) click to toggle source
# File lib/html-conditional-comment/parser.rb, line 224
def peek(symbol)
  @tokens[@pos+1][0] == symbol
end
subexpression() click to toggle source

Either a comparison with the browser, boolean, or simply just the browser

# File lib/html-conditional-comment/parser.rb, line 91
def subexpression()
  node = nil

  if current(:operator_less_than) || current(:operator_less_than_equal) ||
    current(:operator_greater_than) || current(:operator_greater_than_equal)

    node = operator()
    node.child = browser()
  elsif current(:boolean_true) || current(:boolean_false)
    node = boolean()
  else
    #No comparison operator is assuming equals
    node = Nodes::Equal.new()
    node.child = browser()
  end

  node
end
template() click to toggle source
# File lib/html-conditional-comment/parser.rb, line 189
def template()
  nodes = Nodes::Nodes.new()

  while current(:html) || (current(:open) && peek(:if))
    nodes << if current(:html)
      html()
    elsif current(:open) && peek(:if)
      condition()
    end
  end

  nodes
end
term() click to toggle source

And

# File lib/html-conditional-comment/parser.rb, line 128
def term()
  node = factor()
  while accept(:operator_and)
    branch_node = Nodes::And.new()
    branch_node.left = node
    branch_node.right = factor()
    node = branch_node
  end

  node
end