Class Sass::Script::Parser
In: lib/sass/script/parser.rb
Parent: Object
Haml::Util Engine Color SyntaxError UnitConversionError StandardError AbstractSequence CommaSequence Sequence SimpleSequence Simple Parent Universal Class SelectorPseudoClass Id Pseudo Attribute Interpolation Element Node Operation Literal UnaryOperation StringInterpolation Funcall Interpolation Variable Lexer CssLexer Number Bool String Parser Parser CssParser EvaluationContext StaticParser SassParser CssParser Node DebugNode IfNode CommentNode ForNode PropNode MixinNode CharsetNode DirectiveNode VariableNode WarnNode ExtendNode RootNode WhileNode MixinDefNode RuleNode Enumerable ImportNode Merb::BootLoader MerbBootLoader Repl CSS Environment Rack StalenessChecker lib/sass/repl.rb lib/sass/css.rb lib/sass/environment.rb lib/sass/error.rb lib/sass/engine.rb lib/sass/selector/simple_sequence.rb lib/sass/selector/abstract_sequence.rb lib/sass/selector/sequence.rb lib/sass/selector/comma_sequence.rb lib/sass/selector/simple.rb lib/sass/selector.rb Selector lib/sass/script/css_parser.rb lib/sass/script/lexer.rb lib/sass/script/color.rb lib/sass/script/string.rb lib/sass/script/unary_operation.rb lib/sass/script/variable.rb lib/sass/script/funcall.rb lib/sass/script/string_interpolation.rb lib/sass/script/operation.rb lib/sass/script/bool.rb lib/sass/script/parser.rb lib/sass/script/node.rb lib/sass/script/literal.rb lib/sass/script/interpolation.rb lib/sass/script/css_lexer.rb lib/sass/script/number.rb lib/sass/script/functions.rb Functions Script lib/sass/scss/sass_parser.rb lib/sass/scss/static_parser.rb lib/sass/scss/parser.rb lib/sass/scss/css_parser.rb ScriptLexer ScriptParser RX SCSS Callbacks Files lib/sass/tree/while_node.rb lib/sass/tree/if_node.rb lib/sass/tree/mixin_def_node.rb lib/sass/tree/debug_node.rb lib/sass/tree/root_node.rb lib/sass/tree/for_node.rb lib/sass/tree/import_node.rb lib/sass/tree/prop_node.rb lib/sass/tree/node.rb lib/sass/tree/comment_node.rb lib/sass/tree/extend_node.rb lib/sass/tree/charset_node.rb lib/sass/tree/mixin_node.rb lib/sass/tree/warn_node.rb lib/sass/tree/directive_node.rb lib/sass/tree/rule_node.rb lib/sass/tree/variable_node.rb Tree lib/sass/plugin/rack.rb lib/sass/plugin/staleness_checker.rb lib/sass/plugin/merb.rb Plugin Sass dot/m_86_0.png

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

Methods

Constants

PRECEDENCE = [ :comma, :single_eq, :concat, :or, :and, [:eq, :neq], [:gt, :gte, :lt, :lte], [:plus, :minus], [:times, :div, :mod], ]
ASSOCIATIVE = [:comma, :concat, :plus, :times]
EXPR_NAMES = { :string => "string", :default => "expression (e.g. 1px, bold)", :arglist => "mixin argument", :fn_arglist => "function argument", }   It would be possible to have unified assert and try methods, but detecting the method/token difference turns out to be quite expensive.

Public Class methods

Returns whether or not the given operation is associative.

@private

[Source]

     # File lib/sass/script/parser.rb, line 149
149:         def associative?(op)
150:           ASSOCIATIVE.include?(op)
151:         end

@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}

[Source]

    # 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

[Source]

     # 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

[Source]

     # File lib/sass/script/parser.rb, line 139
139:         def precedence_of(op)
140:           PRECEDENCE.each_with_index do |e, i|
141:             return i if Array(e).include?(op)
142:           end
143:           raise "[BUG] Unknown operator #{op}"
144:         end

Private Class methods

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

[Source]

     # File lib/sass/script/parser.rb, line 159
159:         def production(name, sub, *ops)
160:           class_eval "            def \#{name}\n              interp = try_ops_after_interp(\#{ops.inspect}, \#{name.inspect}) and return interp\n              return unless e = \#{sub}\n              while tok = try_tok(\#{ops.map {|o| o.inspect}.join(', ')})\n                interp = try_op_before_interp(tok, e) and return interp\n                line = @lexer.line\n                e = Operation.new(e, assert_expr(\#{sub.inspect}), tok.type)\n                e.line = line\n              end\n              e\n            end\n"
161:         end

[Source]

     # File lib/sass/script/parser.rb, line 176
176:         def unary(op, sub)
177:           class_eval "            def unary_\#{op}\n              return \#{sub} unless tok = try_tok(:\#{op})\n              interp = try_op_before_interp(tok) and return interp\n              line = @lexer.line \n              op = UnaryOperation.new(assert_expr(:unary_\#{op}), :\#{op})\n              op.line = line\n              op\n            end\n"
178:         end

Public Instance methods

The line number of the parser‘s current position.

@return [Fixnum]

[Source]

    # 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

[Source]

    # 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

[Source]

    # 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<Script::Node>] The root nodes of the arguments. @raise [Sass::SyntaxError] if the argument list isn‘t valid SassScript

[Source]

     # 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<Script::Node>] The root nodes of the arguments. @raise [Sass::SyntaxError] if the argument list isn‘t valid SassScript

[Source]

    # 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 [include?(String)] 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

[Source]

    # 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

Private Instance methods

[Source]

     # File lib/sass/script/parser.rb, line 308
308:       def arglist
309:         return unless e = interpolation
310:         return [e] unless try_tok(:comma)
311:         [e, *assert_expr(:arglist)]
312:       end

[Source]

     # File lib/sass/script/parser.rb, line 392
392:       def assert_done
393:         return if @lexer.done?
394:         @lexer.expected!(EXPR_NAMES[:default])
395:       end

[Source]

     # File lib/sass/script/parser.rb, line 377
377:       def assert_expr(name)
378:         (e = send(name)) && (return e)
379:         @lexer.expected!(EXPR_NAMES[name] || EXPR_NAMES[:default])
380:       end

[Source]

     # File lib/sass/script/parser.rb, line 382
382:       def assert_tok(*names)
383:         (t = try_tok(*names)) && (return t)
384:         @lexer.expected!(names.map {|tok| Lexer::TOKEN_NAMES[tok] || tok}.join(" or "))
385:       end

[Source]

     # File lib/sass/script/parser.rb, line 235
235:       def concat
236:         return unless e = or_expr
237:         while sub = or_expr
238:           e = node(Operation.new(e, sub, :concat))
239:         end
240:         e
241:       end

[Source]

     # File lib/sass/script/parser.rb, line 273
273:       def defn_arglist!(must_have_default)
274:         return [] unless try_tok(:lparen)
275:         return [] if try_tok(:rparen)
276:         res = []
277:         loop do
278:           line = @lexer.line
279:           offset = @lexer.offset + 1
280:           c = assert_tok(:const)
281:           var = Script::Variable.new(c.value)
282:           if tok = (try_tok(:colon) || try_tok(:single_eq))
283:             val = assert_expr(:concat)
284: 
285:             if tok.type == :single_eq
286:               val.context = :equals
287:               val.options = @options
288:               Script.equals_warning("mixin argument defaults", "$#{c.value}",
289:                 val.to_sass, false, line, offset, @options[:filename])
290:             end
291:             must_have_default = true
292:           elsif must_have_default
293:             raise SyntaxError.new("Required argument #{var.inspect} must come before any optional arguments.")
294:           end
295:           res << [var, val]
296:           break unless try_tok(:comma)
297:         end
298:         assert_tok(:rparen)
299:         res
300:       end

[Source]

     # File lib/sass/script/parser.rb, line 302
302:       def fn_arglist
303:         return unless e = equals
304:         return [e] unless try_tok(:comma)
305:         [e, *assert_expr(:fn_arglist)]
306:       end

[Source]

     # File lib/sass/script/parser.rb, line 266
266:       def funcall
267:         return raw unless tok = try_tok(:funcall)
268:         args = fn_arglist || []
269:         assert_tok(:rparen)
270:         node(Script::Funcall.new(tok.value, args))
271:       end

[Source]

     # File lib/sass/script/parser.rb, line 255
255:       def ident
256:         return funcall unless @lexer.peek && @lexer.peek.type == :ident
257:         return if @stop_at && @stop_at.include?(@lexer.peek.value)
258: 
259:         name = @lexer.next
260:         if color = Color::HTML4_COLORS[name.value.downcase]
261:           return node(Color.new(color))
262:         end
263:         node(Script::String.new(name.value, :identifier))
264:       end

[Source]

     # File lib/sass/script/parser.rb, line 222
222:       def interpolation(first = concat)
223:         e = first
224:         while interp = try_tok(:begin_interpolation)
225:           wb = @lexer.whitespace?(interp)
226:           line = @lexer.line
227:           mid = parse_interpolated
228:           wa = @lexer.whitespace?
229:           e = Script::Interpolation.new(e, mid, concat, wb, wa)
230:           e.line = line
231:         end
232:         e
233:       end

@private

[Source]

     # File lib/sass/script/parser.rb, line 194
194:       def lexer_class; Lexer; end

[Source]

     # File lib/sass/script/parser.rb, line 363
363:       def literal
364:         (t = try_tok(:color, :bool)) && (return t.value)
365:       end

[Source]

     # File lib/sass/script/parser.rb, line 397
397:       def node(node)
398:         node.line = @lexer.line
399:         node
400:       end

[Source]

     # File lib/sass/script/parser.rb, line 356
356:       def number
357:         return literal unless tok = try_tok(:number)
358:         num = tok.value
359:         num.original = num.to_s unless @in_parens
360:         num
361:       end

[Source]

     # File lib/sass/script/parser.rb, line 329
329:       def paren
330:         return variable unless try_tok(:lparen)
331:         was_in_parens = @in_parens
332:         @in_parens = true
333:         e = assert_expr(:expr)
334:         assert_tok(:rparen)
335:         return e
336:       ensure
337:         @in_parens = was_in_parens
338:       end

[Source]

     # File lib/sass/script/parser.rb, line 314
314:       def raw
315:         return special_fun unless tok = try_tok(:raw)
316:         node(Script::String.new(tok.value))
317:       end

[Source]

     # File lib/sass/script/parser.rb, line 319
319:       def special_fun
320:         return paren unless tok = try_tok(:special_fun)
321:         first = node(Script::String.new(tok.value.first))
322:         Haml::Util.enum_slice(tok.value[1..-1], 2).inject(first) do |l, (i, r)|
323:           Script::Interpolation.new(
324:             l, i, r && node(Script::String.new(r)),
325:             false, false)
326:         end
327:       end

[Source]

     # File lib/sass/script/parser.rb, line 345
345:       def string
346:         return number unless first = try_tok(:string)
347:         return first.value unless try_tok(:begin_interpolation)
348:         line = @lexer.line
349:         mid = parse_interpolated
350:         last = assert_expr(:string)
351:         interp = StringInterpolation.new(first.value, mid, last)
352:         interp.line = line
353:         interp
354:       end

[Source]

     # File lib/sass/script/parser.rb, line 199
199:       def try_op_before_interp(op, prev = nil)
200:         return unless @lexer.peek && @lexer.peek.type == :begin_interpolation
201:         wb = @lexer.whitespace?(op)
202:         str = Script::String.new(Lexer::OPERATORS_REVERSE[op.type])
203:         str.line = @lexer.line
204:         interp = Script::Interpolation.new(prev, str, nil, wb, !:wa, :originally_text)
205:         interp.line = @lexer.line
206:         interpolation(interp)
207:       end

[Source]

     # File lib/sass/script/parser.rb, line 209
209:       def try_ops_after_interp(ops, name)
210:         return unless @lexer.after_interpolation?
211:         return unless op = try_tok(*ops)
212:         interp = try_op_before_interp(op) and return interp
213: 
214:         wa = @lexer.whitespace?
215:         str = Script::String.new(Lexer::OPERATORS_REVERSE[op.type])
216:         str.line = @lexer.line
217:         interp = Script::Interpolation.new(nil, str, assert_expr(name), !:wb, wa, :originally_text)
218:         interp.line = @lexer.line
219:         return interp
220:       end

[Source]

     # File lib/sass/script/parser.rb, line 387
387:       def try_tok(*names)
388:         peeked =  @lexer.peek
389:         peeked && names.include?(peeked.type) && @lexer.next
390:       end

[Source]

     # File lib/sass/script/parser.rb, line 340
340:       def variable
341:         return string unless c = try_tok(:const)
342:         node(Variable.new(*c.value))
343:       end

[Validate]