class Sass::Script::Parser

It parses a string of code into a tree of {Script::Node}s.
The parser for SassScript.

def self.parse(*args)

Other tags:
    See: Parser#parse -
    See: Parser#initialize -

Returns:
  • (Script::Node) - The root node of the parse tree

Overloads:
  • parse(str, line, offset, filename = nil)
def self.parse(*args)
  new(*args).parse
end

def arglist

def arglist
  return unless e = interpolation
  return [e] unless try_tok(:comma)
  [e, *assert_expr(:arglist)]
end

def assert_done

def assert_done
  return if @lexer.done?
  @lexer.expected!(EXPR_NAMES[:default])
end

def assert_expr(name)

def assert_expr(name)
  (e = send(name)) && (return e)
  @lexer.expected!(EXPR_NAMES[name] || EXPR_NAMES[:default])
end

def assert_tok(*names)

def assert_tok(*names)
  (t = try_tok(*names)) && (return t)
  @lexer.expected!(names.map {|tok| Lexer::TOKEN_NAMES[tok] || tok}.join(" or "))
end

def concat

def concat
  return unless e = or_expr
  while sub = or_expr
    e = node(Operation.new(e, sub, :concat))
  end
  e
end

def defn_arglist!(must_have_default)

def defn_arglist!(must_have_default)
  return [] unless try_tok(:lparen)
  return [] if try_tok(:rparen)
  res = []
  loop do
    line = @lexer.line
    offset = @lexer.offset + 1
    c = assert_tok(:const)
    var = Script::Variable.new(c.value)
    if tok = (try_tok(:colon) || try_tok(:single_eq))
      val = assert_expr(:concat)
      if tok.type == :single_eq
        val.context = :equals
        val.options = @options
        Script.equals_warning("mixin argument defaults", "$#{c.value}",
          val.to_sass, false, line, offset, @options[:filename])
      end
      must_have_default = true
    elsif must_have_default
      raise SyntaxError.new("Required argument #{var.inspect} must come before any optional arguments.")
    end
    res << [var, val]
    break unless try_tok(:comma)
  end
  assert_tok(:rparen)
  res
end

def fn_arglist

def fn_arglist
  return unless e = equals
  return [e] unless try_tok(:comma)
  [e, *assert_expr(:fn_arglist)]
end

def funcall

def funcall
  return raw unless tok = try_tok(:funcall)
  args = fn_arglist || []
  assert_tok(:rparen)
  node(Script::Funcall.new(tok.value, args))
end

def ident

def ident
  return funcall unless @lexer.peek && @lexer.peek.type == :ident
  return if @stop_at && @stop_at.include?(@lexer.peek.value)
  name = @lexer.next
  if color = Color::HTML4_COLORS[name.value]
    return node(Color.new(color))
  end
  node(Script::String.new(name.value, :identifier))
end

def initialize(str, line, offset, options = {})

Parameters:
  • options ({Symbol => Object}) -- An options hash;
  • offset (Fixnum) -- The number of characters in on which the SassScript appears.
  • line (Fixnum) -- The line on which the SassScript appears.
  • str (String, StringScanner) -- The source text to parse
def initialize(str, line, offset, options = {})
  @options = options
  @lexer = lexer_class.new(str, line, offset, options)
end

def interpolation(first = concat)

def interpolation(first = concat)
  e = first
  while interp = try_tok(:begin_interpolation)
    wb = @lexer.whitespace?(interp)
    line = @lexer.line
    mid = parse_interpolated
    wa = @lexer.whitespace?
    e = Script::Interpolation.new(e, mid, concat, wb, wa)
    e.line = line
  end
  e
end

def lexer_class; Lexer; end

Other tags:
    Private: -
def lexer_class; Lexer; end

def line

Returns:
  • (Fixnum) -
def line
  @lexer.line
end

def literal

def literal
  (t = try_tok(:color, :bool)) && (return t.value)
end

def node(node)

def node(node)
  node.line = @lexer.line
  node
end

def number

def number
  return literal unless tok = try_tok(:number)
  num = tok.value
  num.original = num.to_s unless @in_parens
  num
end

def paren

def paren
  return variable unless try_tok(:lparen)
  was_in_parens = @in_parens
  @in_parens = true
  e = assert_expr(:expr)
  assert_tok(:rparen)
  return e
ensure
  @in_parens = was_in_parens
end

def parse

Raises:
  • (Sass::SyntaxError) - if the expression isn't valid SassScript

Returns:
  • (Script::Node) - The root node of the parse tree
def parse
  expr = assert_expr :expr
  assert_done
  expr.options = @options
  expr
rescue Sass::SyntaxError => e
  e.modify_backtrace :line => @lexer.line, :filename => @options[:filename]
  raise e
end

def parse_interpolated

Raises:
  • (Sass::SyntaxError) - if the expression isn't valid SassScript

Returns:
  • (Script::Node) - The root node of the parse tree
def parse_interpolated
  expr = assert_expr :expr
  assert_tok :end_interpolation
  expr.options = @options
  expr
rescue Sass::SyntaxError => e
  e.modify_backtrace :line => @lexer.line, :filename => @options[:filename]
  raise e
end

def parse_mixin_definition_arglist

Raises:
  • (Sass::SyntaxError) - if the argument list isn't valid SassScript

Returns:
  • (Array) - The root nodes of the arguments.
def parse_mixin_definition_arglist
  args = defn_arglist!(false)
  assert_done
  args.each do |k, v|
    k.options = @options
    v.options = @options if v
  end
  args
rescue Sass::SyntaxError => e
  e.modify_backtrace :line => @lexer.line, :filename => @options[:filename]
  raise e
end

def parse_mixin_include_arglist

Raises:
  • (Sass::SyntaxError) - if the argument list isn't valid SassScript

Returns:
  • (Array) - The root nodes of the arguments.
def parse_mixin_include_arglist
  args = []
  if try_tok(:lparen)
    args = arglist || args
    assert_tok(:rparen)
  end
  assert_done
  args.each {|a| a.options = @options}
  args
rescue Sass::SyntaxError => e
  e.modify_backtrace :line => @lexer.line, :filename => @options[:filename]
  raise e
end

def parse_until(tokens)

Raises:
  • (Sass::SyntaxError) - if the expression isn't valid SassScript

Returns:
  • (Script::Node) - The root node of the parse tree

Parameters:
  • A (#include?(String)) -- set of strings that delimit the expression.
def parse_until(tokens)
  @stop_at = tokens
  expr = assert_expr :expr
  assert_done
  expr.options = @options
  expr
rescue Sass::SyntaxError => e
  e.modify_backtrace :line => @lexer.line, :filename => @options[:filename]
  raise e
end

def precedence_of(op)

Other tags:
    Private: -
def precedence_of(op)
  PRECEDENCE.each_with_index do |e, i|
    return i if Array(e).include?(op)
  end
  raise "[BUG] Unknown operator #{op}"
end

def production(name, sub, *ops)

and ops is a list of operators for this precedence level
sub is the name of the production beneath it,
name is the name of the production,
Defines a simple left-associative production.
def production(name, sub, *ops)
  class_eval <<RUBY
    def #{name}
      interp = try_ops_after_interp(#{ops.inspect}, #{name.inspect}) and return interp
      return unless e = #{sub}
      while tok = try_tok(#{ops.map {|o| o.inspect}.join(', ')})
        interp = try_op_before_interp(tok, e) and return interp
        line = @lexer.line
        e = Operation.new(e, assert_expr(#{sub.inspect}), tok.type)
        e.line = line
      end
      e
    end
end

def raw

def raw
  return special_fun unless tok = try_tok(:raw)
  node(Script::String.new(tok.value))
end

def special_fun

def special_fun
  return paren unless tok = try_tok(:special_fun)
  first = node(Script::String.new(tok.value.first))
  Haml::Util.enum_slice(tok.value[1..-1], 2).inject(first) do |l, (i, r)|
    Script::Interpolation.new(
      l, i, r && node(Script::String.new(r)),
      false, false)
  end
end

def string

def string
  return number unless first = try_tok(:string)
  return first.value unless try_tok(:begin_interpolation)
  line = @lexer.line
  mid = parse_interpolated
  last = assert_expr(:string)
  interp = StringInterpolation.new(first.value, mid, last)
  interp.line = line
  interp
end

def try_op_before_interp(op, prev = nil)

def try_op_before_interp(op, prev = nil)
  return unless @lexer.peek && @lexer.peek.type == :begin_interpolation
  wb = @lexer.whitespace?(op)
  str = Script::String.new(Lexer::OPERATORS_REVERSE[op.type])
  str.line = @lexer.line
  interp = Script::Interpolation.new(prev, str, nil, wb, !:wa, :originally_text)
  interp.line = @lexer.line
  interpolation(interp)
end

def try_ops_after_interp(ops, name)

def try_ops_after_interp(ops, name)
  return unless @lexer.after_interpolation?
  return unless op = try_tok(*ops)
  interp = try_op_before_interp(op) and return interp
  wa = @lexer.whitespace?
  str = Script::String.new(Lexer::OPERATORS_REVERSE[op.type])
  str.line = @lexer.line
  interp = Script::Interpolation.new(nil, str, assert_expr(name), !:wb, wa, :originally_text)
  interp.line = @lexer.line
  return interp
end

def try_tok(*names)

def try_tok(*names)
  peeked =  @lexer.peek
  peeked && names.include?(peeked.type) && @lexer.next
end

def unary(op, sub)

def unary(op, sub)
  class_eval <<RUBY
    def unary_#{op}
      return #{sub} unless tok = try_tok(:#{op})
      interp = try_op_before_interp(tok) and return interp
      line = @lexer.line 
      op = UnaryOperation.new(assert_expr(:unary_#{op}), :#{op})
      op.line = line
      op
    end
end

def variable

def variable
  return string unless c = try_tok(:const)
  node(Variable.new(*c.value))
end