The parser for SassScript. It parses a string of code into a tree of {Script::Node}s.
It would be possible to have unified # and # methods, but detecting the method/token difference turns out to be quite expensive.
@param str [String, StringScanner] The source text to parse @param line [Fixnum] The line on which the SassScript appears.
Used for error reporting
@param offset [Fixnum] The number of characters in on which the SassScript appears.
Used for error reporting
@param options [{Symbol => Object}] An options hash;
see {file:SASS_REFERENCE.md#sass_options the Sass options documentation}
# File lib/sass/script/parser.rb, line 22 22: def initialize(str, line, offset, options = {}) 23: @options = options 24: @lexer = lexer_class.new(str, line, offset, options) 25: end
Parses a SassScript expression.
@overload parse(str, line, offset, filename = nil) @return [Script::Node] The root node of the parse tree @see Parser#initialize @see Parser#parse
# File lib/sass/script/parser.rb, line 119 119: def self.parse(*args) 120: new(*args).parse 121: end
Returns an integer representing the precedence of the given operator. A lower integer indicates a looser binding.
@private
# File lib/sass/script/parser.rb, line 137 137: def precedence_of(op) 138: PRECEDENCE.each_with_index do |e, i| 139: return i if Array(e).include?(op) 140: end 141: raise "[BUG] Unknown operator #{op}" 142: end
Defines a simple left-associative production. name is the name of the production, sub is the name of the production beneath it, and ops is a list of operators for this precedence level
# File lib/sass/script/parser.rb, line 150 150: def production(name, sub, *ops) 151: class_eval 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 152: end
# File lib/sass/script/parser.rb, line 166 166: def unary(op, sub) 167: class_eval 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 168: end
The line number of the parser’s current position.
@return [Fixnum]
# File lib/sass/script/parser.rb, line 11 11: def line 12: @lexer.line 13: end
Parses a SassScript expression.
@return [Script::Node] The root node of the parse tree @raise [Sass::SyntaxError] if the expression isn’t valid SassScript
# File lib/sass/script/parser.rb, line 48 48: def parse 49: expr = assert_expr :expr 50: assert_done 51: expr.options = @options 52: expr 53: rescue Sass::SyntaxError => e 54: e.modify_backtrace :line => @lexer.line, :filename => @options[:filename] 55: raise e 56: end
Parses a SassScript expression within an interpolated segment (`#{}`). This means that it stops when it comes across an unmatched `}`, which signals the end of an interpolated segment, it returns rather than throwing an error.
@return [Script::Node] The root node of the parse tree @raise [Sass::SyntaxError] if the expression isn’t valid SassScript
# File lib/sass/script/parser.rb, line 34 34: def parse_interpolated 35: expr = assert_expr :expr 36: assert_tok :end_interpolation 37: expr.options = @options 38: expr 39: rescue Sass::SyntaxError => e 40: e.modify_backtrace :line => @lexer.line, :filename => @options[:filename] 41: raise e 42: end
Parses the argument list for a mixin definition.
@return [Array
# File lib/sass/script/parser.rb, line 99 99: def parse_mixin_definition_arglist 100: args = defn_arglist!(false) 101: assert_done 102: 103: args.each do |k, v| 104: k.options = @options 105: v.options = @options if v 106: end 107: args 108: rescue Sass::SyntaxError => e 109: e.modify_backtrace :line => @lexer.line, :filename => @options[:filename] 110: raise e 111: end
Parses the argument list for a mixin include.
@return [Array
# File lib/sass/script/parser.rb, line 79 79: def parse_mixin_include_arglist 80: args = [] 81: 82: if try_tok(:lparen) 83: args = arglist || args 84: assert_tok(:rparen) 85: end 86: assert_done 87: 88: args.each {|a| a.options = @options} 89: args 90: rescue Sass::SyntaxError => e 91: e.modify_backtrace :line => @lexer.line, :filename => @options[:filename] 92: raise e 93: end
Parses a SassScript expression, ending it when it encounters one of the given identifier tokens.
@param [#] A set of strings that delimit the expression. @return [Script::Node] The root node of the parse tree @raise [Sass::SyntaxError] if the expression isn’t valid SassScript
# File lib/sass/script/parser.rb, line 64 64: def parse_until(tokens) 65: @stop_at = tokens 66: expr = assert_expr :expr 67: assert_done 68: expr.options = @options 69: expr 70: rescue Sass::SyntaxError => e 71: e.modify_backtrace :line => @lexer.line, :filename => @options[:filename] 72: raise e 73: end
# File lib/sass/script/parser.rb, line 297 297: def arglist 298: return unless e = interpolation 299: return [e] unless try_tok(:comma) 300: [e, *assert_expr(:arglist)] 301: end
# File lib/sass/script/parser.rb, line 381 381: def assert_done 382: return if @lexer.done? 383: @lexer.expected!(EXPR_NAMES[:default]) 384: end
# File lib/sass/script/parser.rb, line 366 366: def assert_expr(name) 367: (e = send(name)) && (return e) 368: @lexer.expected!(EXPR_NAMES[name] || EXPR_NAMES[:default]) 369: end
# File lib/sass/script/parser.rb, line 371 371: def assert_tok(*names) 372: (t = try_tok(*names)) && (return t) 373: @lexer.expected!(names.map {|tok| Lexer::TOKEN_NAMES[tok] || tok}.join(" or ")) 374: end
# File lib/sass/script/parser.rb, line 224 224: def concat 225: return unless e = or_expr 226: while sub = or_expr 227: e = node(Operation.new(e, sub, :concat)) 228: end 229: e 230: end
# File lib/sass/script/parser.rb, line 262 262: def defn_arglist!(must_have_default) 263: return [] unless try_tok(:lparen) 264: return [] if try_tok(:rparen) 265: res = [] 266: loop do 267: line = @lexer.line 268: offset = @lexer.offset + 1 269: c = assert_tok(:const) 270: var = Script::Variable.new(c.value) 271: if tok = (try_tok(:colon) || try_tok(:single_eq)) 272: val = assert_expr(:concat) 273: 274: if tok.type == :single_eq 275: val.context = :equals 276: val.options = @options 277: Script.equals_warning("mixin argument defaults", "$#{c.value}", 278: val.to_sass, false, line, offset, @options[:filename]) 279: end 280: must_have_default = true 281: elsif must_have_default 282: raise SyntaxError.new("Required argument #{var.inspect} must come before any optional arguments.") 283: end 284: res << [var, val] 285: break unless try_tok(:comma) 286: end 287: assert_tok(:rparen) 288: res 289: end
# File lib/sass/script/parser.rb, line 291 291: def fn_arglist 292: return unless e = equals 293: return [e] unless try_tok(:comma) 294: [e, *assert_expr(:fn_arglist)] 295: end
# File lib/sass/script/parser.rb, line 255 255: def funcall 256: return raw unless tok = try_tok(:funcall) 257: args = fn_arglist || [] 258: assert_tok(:rparen) 259: node(Script::Funcall.new(tok.value, args)) 260: end
# File lib/sass/script/parser.rb, line 244 244: def ident 245: return funcall unless @lexer.peek && @lexer.peek.type == :ident 246: return if @stop_at && @stop_at.include?(@lexer.peek.value) 247: 248: name = @lexer.next 249: if color = Color::HTML4_COLORS[name.value] 250: return node(Color.new(color)) 251: end 252: node(Script::String.new(name.value, :identifier)) 253: end
# File lib/sass/script/parser.rb, line 211 211: def interpolation(first = concat) 212: e = first 213: while interp = try_tok(:begin_interpolation) 214: wb = @lexer.whitespace?(interp) 215: line = @lexer.line 216: mid = parse_interpolated 217: wa = @lexer.whitespace? 218: e = Script::Interpolation.new(e, mid, concat, wb, wa) 219: e.line = line 220: end 221: e 222: end
@private
# File lib/sass/script/parser.rb, line 183 183: def lexer_class; Lexer; end
# File lib/sass/script/parser.rb, line 352 352: def literal 353: (t = try_tok(:color, :bool)) && (return t.value) 354: end
# File lib/sass/script/parser.rb, line 386 386: def node(node) 387: node.line = @lexer.line 388: node 389: end
# File lib/sass/script/parser.rb, line 345 345: def number 346: return literal unless tok = try_tok(:number) 347: num = tok.value 348: num.original = num.to_s unless @in_parens 349: num 350: end
# File lib/sass/script/parser.rb, line 318 318: def paren 319: return variable unless try_tok(:lparen) 320: was_in_parens = @in_parens 321: @in_parens = true 322: e = assert_expr(:expr) 323: assert_tok(:rparen) 324: return e 325: ensure 326: @in_parens = was_in_parens 327: end
# File lib/sass/script/parser.rb, line 303 303: def raw 304: return special_fun unless tok = try_tok(:raw) 305: node(Script::String.new(tok.value)) 306: end
# File lib/sass/script/parser.rb, line 308 308: def special_fun 309: return paren unless tok = try_tok(:special_fun) 310: first = node(Script::String.new(tok.value.first)) 311: Haml::Util.enum_slice(tok.value[1..1], 2).inject(first) do |l, (i, r)| 312: Script::Interpolation.new( 313: l, i, r && node(Script::String.new(r)), 314: false, false) 315: end 316: end
# File lib/sass/script/parser.rb, line 334 334: def string 335: return number unless first = try_tok(:string) 336: return first.value unless try_tok(:begin_interpolation) 337: line = @lexer.line 338: mid = parse_interpolated 339: last = assert_expr(:string) 340: interp = StringInterpolation.new(first.value, mid, last) 341: interp.line = line 342: interp 343: end
# File lib/sass/script/parser.rb, line 188 188: def try_op_before_interp(op, prev = nil) 189: return unless @lexer.peek && @lexer.peek.type == :begin_interpolation 190: wb = @lexer.whitespace?(op) 191: str = Script::String.new(Lexer::OPERATORS_REVERSE[op.type]) 192: str.line = @lexer.line 193: interp = Script::Interpolation.new(prev, str, nil, wb, !:wa, :originally_text) 194: interp.line = @lexer.line 195: interpolation(interp) 196: end
# File lib/sass/script/parser.rb, line 198 198: def try_ops_after_interp(ops, name) 199: return unless @lexer.after_interpolation? 200: return unless op = try_tok(*ops) 201: interp = try_op_before_interp(op) and return interp 202: 203: wa = @lexer.whitespace? 204: str = Script::String.new(Lexer::OPERATORS_REVERSE[op.type]) 205: str.line = @lexer.line 206: interp = Script::Interpolation.new(nil, str, assert_expr(name), !:wb, wa, :originally_text) 207: interp.line = @lexer.line 208: return interp 209: end
Disabled; run with --debug to generate this.
Generated with the Darkfish Rdoc Generator 1.1.6.