lib/sass/script/parser.rb in sass-3.3.0.alpha.256 vs lib/sass/script/parser.rb in sass-3.3.0.alpha.353

- old
+ new

@@ -20,11 +20,11 @@ end # @param str [String, StringScanner] The source text to parse # @param line [Fixnum] The line on which the SassScript appears. # Used for error reporting and sourcemap building - # @param offset [Fixnum] The character (not byte) offset in the line on which the SassScript appears. + # @param offset [Fixnum] The character (not byte) offset where the script starts in the line. # Used for error reporting and sourcemap building # @param options [{Symbol => Object}] An options hash; # see {file:SASS_REFERENCE.md#sass_options the Sass options documentation} def initialize(str, line, offset, options = {}) @options = options @@ -79,11 +79,14 @@ raise e end # Parses the argument list for a mixin include. # - # @return [(Array<Script::Tree::Node>, {String => Script::Tree::Node}, Script::Tree::Node, Script::Tree::Node)] + # @return [(Array<Script::Tree::Node>, + # {String => Script::Tree::Node}, + # Script::Tree::Node, + # Script::Tree::Node)] # The root nodes of the positional arguments, keyword arguments, and # splat argument(s). Keyword arguments are in a hash from names to values. # @raise [Sass::SyntaxError] if the argument list isn't valid SassScript def parse_mixin_include_arglist args, keywords = [], {} @@ -211,15 +214,17 @@ # sub is the name of the production beneath it, # and ops is a list of operators for this precedence level def production(name, sub, *ops) class_eval <<RUBY, __FILE__, __LINE__ + 1 def #{name} - interp = try_ops_after_interp(#{ops.inspect}, #{name.inspect}) and return interp + interp = try_ops_after_interp(#{ops.inspect}, #{name.inspect}) + return interp if interp return unless e = #{sub} while tok = try_tok(#{ops.map {|o| o.inspect}.join(', ')}) if interp = try_op_before_interp(tok, e) - return interp unless other_interp = try_ops_after_interp(#{ops.inspect}, #{name.inspect}, interp) + other_interp = try_ops_after_interp(#{ops.inspect}, #{name.inspect}, interp) + return interp unless other_interp return other_interp end start_pos = source_position e = node(Tree::Operation.new(e, assert_expr(#{sub.inspect}), tok.type), start_pos) @@ -231,11 +236,12 @@ def unary(op, sub) class_eval <<RUBY, __FILE__, __LINE__ + 1 def unary_#{op} return #{sub} unless tok = try_tok(:#{op}) - interp = try_op_before_interp(tok) and return interp + interp = try_op_before_interp(tok) + return interp if interp start_pos = source_position node(Tree::UnaryOperation.new(assert_expr(:unary_#{op}), :#{op}), start_pos) end RUBY end @@ -245,48 +251,51 @@ def source_position Sass::Source::Position.new(line, offset) end - def range(start_pos, end_pos=source_position) + def range(start_pos, end_pos = source_position) Sass::Source::Range.new(start_pos, end_pos, @options[:filename], @options[:importer]) end # @private def lexer_class; Lexer; end def map start_pos = source_position - return unless e = interpolation + e = interpolation + return unless e return list e, start_pos unless @lexer.peek && @lexer.peek.type == :colon key, value = map_pair(e) map = node(Sass::Script::Tree::MapLiteral.new([[key, value]]), start_pos) - while tok = try_tok(:comma) + while try_tok(:comma) key, value = assert_expr(:map_pair) map.pairs << [key, value] end map end - def map_pair(key=nil) + def map_pair(key = nil) return unless key ||= interpolation assert_tok :colon return key, assert_expr(:interpolation) end def expr start_pos = source_position - return unless e = interpolation + e = interpolation + return unless e list e, start_pos end def list(first, start_pos) list = node(Sass::Script::Tree::ListLiteral.new([first], :comma), start_pos) - while tok = try_tok(:comma) - if interp = try_op_before_interp(tok, list) - return interp unless other_interp = try_ops_after_interp([:comma], :expr, interp) + while (tok = try_tok(:comma)) + if (interp = try_op_before_interp(tok, list)) + other_interp = try_ops_after_interp([:comma], :expr, interp) + return interp unless other_interp return other_interp end list.elements << assert_expr(:interpolation) end list.elements.size == 1 ? list.elements.first : list @@ -295,36 +304,39 @@ production :equals, :interpolation, :single_eq def try_op_before_interp(op, prev = nil) return unless @lexer.peek && @lexer.peek.type == :begin_interpolation wb = @lexer.whitespace?(op) - str = literal_node(Script::Value::String.new(Lexer::OPERATORS_REVERSE[op.type]), op.source_range) + str = literal_node(Script::Value::String.new(Lexer::OPERATORS_REVERSE[op.type]), + op.source_range) interp = node( Script::Tree::Interpolation.new(prev, str, nil, wb, !:wa, :originally_text), (prev || str).source_range.start_pos) interpolation(interp) end def try_ops_after_interp(ops, name, prev = nil) return unless @lexer.after_interpolation? - return unless op = try_tok(*ops) - interp = try_op_before_interp(op, prev) and return interp + op = try_tok(*ops) + return unless op + interp = try_op_before_interp(op, prev) + return interp if interp wa = @lexer.whitespace? - str = literal_node(Script::Value::String.new(Lexer::OPERATORS_REVERSE[op.type]), op.source_range) + str = literal_node(Script::Value::String.new(Lexer::OPERATORS_REVERSE[op.type]), + op.source_range) str.line = @lexer.line interp = node( Script::Tree::Interpolation.new(prev, str, assert_expr(name), !:wb, wa, :originally_text), (prev || str).source_range.start_pos) - return interp + interp end def interpolation(first = space) e = first - while interp = try_tok(:begin_interpolation) + while (interp = try_tok(:begin_interpolation)) wb = @lexer.whitespace?(interp) - line = @lexer.line mid = parse_interpolated wa = @lexer.whitespace? e = node( Script::Tree::Interpolation.new(e, mid, space, wb, wa), (e || mid).source_range.start_pos) @@ -332,16 +344,21 @@ e end def space start_pos = source_position - return unless e = or_expr + e = or_expr + return unless e arr = [e] - while e = or_expr + while (e = or_expr) arr << e end - arr.size == 1 ? arr.first : node(Sass::Script::Tree::ListLiteral.new(arr, :space), start_pos) + if arr.size == 1 + arr.first + else + node(Sass::Script::Tree::ListLiteral.new(arr, :space), start_pos) + end end production :or_expr, :and_expr, :or production :and_expr, :eq_or_neq, :and production :eq_or_neq, :relational, :eq, :neq @@ -357,18 +374,19 @@ 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 = Sass::Script::Value::Color::COLOR_NAMES[name.value.downcase] + if (color = Sass::Script::Value::Color::COLOR_NAMES[name.value.downcase]) return literal_node(Sass::Script::Value::Color.new(color), name.source_range) end literal_node(Script::Value::String.new(name.value, :identifier), name.source_range) end def funcall - return raw unless tok = try_tok(:funcall) + tok = try_tok(:funcall) + return raw unless tok args, keywords, splat, kwarg_splat = fn_arglist assert_tok(:rparen) node(Script::Tree::Funcall.new(tok.value, args, keywords, splat, kwarg_splat), tok.source_range.start_pos, source_position) end @@ -389,11 +407,12 @@ var = node(Script::Tree::Variable.new(c.value), c.source_range) if try_tok(:colon) val = assert_expr(:space) must_have_default = true elsif must_have_default - raise SyntaxError.new("Required argument #{var.inspect} must come before any optional arguments.") + raise SyntaxError.new( + "Required argument #{var.inspect} must come before any optional arguments.") elsif try_tok(:splat) splat = var break end res << [var, val] @@ -412,12 +431,13 @@ end def arglist(subexpr, description) args = [] keywords = Sass::Util::NormalizedMap.new + e = send(subexpr) - return [args, keywords] unless e = send(subexpr) + return [args, keywords] unless e loop do if @lexer.peek && @lexer.peek.type == :colon name = e @lexer.expected!("comma") unless name.is_a?(Tree::Variable) @@ -428,11 +448,11 @@ raise SyntaxError.new("Keyword argument \"#{name.to_sass}\" passed more than once") end keywords[name.name] = value else - if !keywords.empty? + unless keywords.empty? raise SyntaxError.new("Positional arguments must come before keyword arguments.") end if try_tok(:splat) splat = e @@ -448,17 +468,19 @@ e = assert_expr(subexpr, description) end end def raw - return special_fun unless tok = try_tok(:raw) + tok = try_tok(:raw) + return special_fun unless tok literal_node(Script::Value::String.new(tok.value), tok.source_range) end def special_fun start_pos = source_position - return paren unless tok = try_tok(:special_fun) + tok = try_tok(:special_fun) + return paren unless tok first = literal_node(Script::Value::String.new(tok.value.first), start_pos, start_pos.after(tok.value.first)) Sass::Util.enum_slice(tok.value[1..-1], 2).inject(first) do |l, (i, r)| end_pos = i.source_range.end_pos end_pos = end_pos.after(r) if r @@ -485,37 +507,42 @@ @in_parens = was_in_parens end def variable start_pos = source_position - return string unless c = try_tok(:const) + c = try_tok(:const) + return string unless c node(Tree::Variable.new(*c.value), start_pos) end def string - return number unless first = try_tok(:string) + first = try_tok(:string) + return number unless first str = literal_node(first.value, first.source_range) return str unless try_tok(:begin_interpolation) mid = parse_interpolated last = assert_expr(:string) node(Tree::StringInterpolation.new(str, mid, last), first.source_range.start_pos) end def number - return selector unless tok = try_tok(:number) + tok = try_tok(:number) + return selector unless tok num = tok.value num.original = num.to_s unless @in_parens literal_node(num, tok.source_range.start_pos) end def selector - return literal unless tok = try_tok(:selector) + tok = try_tok(:selector) + return literal unless tok node(tok.value, tok.source_range.start_pos) end def literal - (t = try_tok(:color, :bool, :null)) && (return literal_node(t.value, t.source_range)) + t = try_tok(:color, :bool, :null) + return literal_node(t.value, t.source_range) if t end # It would be possible to have unified #assert and #try methods, # but detecting the method/token difference turns out to be quite expensive. @@ -526,15 +553,17 @@ :fn_arglist => "function argument", :splat => "...", } def assert_expr(name, expected = nil) - (e = send(name)) && (return e) + e = send(name) + return e if e @lexer.expected!(expected || EXPR_NAMES[name] || EXPR_NAMES[:default]) end def assert_tok(*names) - (t = try_tok(*names)) && (return t) + t = try_tok(*names) + return t if t @lexer.expected!(names.map {|tok| Lexer::TOKEN_NAMES[tok] || tok}.join(" or ")) end def try_tok(*names) peeked = @lexer.peek