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 DirectiveNode ExtendNode VariableNode WarnNode RootNode WhileNode RuleNode MixinDefNode 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/literal.rb lib/sass/script/node.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 Files Callbacks 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/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_85_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], ]
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

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

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 150
150:         def production(name, sub, *ops)
151:           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"
152:         end

[Source]

     # File lib/sass/script/parser.rb, line 167
167:         def unary(op, sub)
168:           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"
169:         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 299
299:       def arglist
300:         return unless e = interpolation
301:         return [e] unless try_tok(:comma)
302:         [e, *assert_expr(:arglist)]
303:       end

[Source]

     # File lib/sass/script/parser.rb, line 383
383:       def assert_done
384:         return if @lexer.done?
385:         @lexer.expected!(EXPR_NAMES[:default])
386:       end

[Source]

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

[Source]

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

[Source]

     # File lib/sass/script/parser.rb, line 226
226:       def concat
227:         return unless e = or_expr
228:         while sub = or_expr
229:           e = node(Operation.new(e, sub, :concat))
230:         end
231:         e
232:       end

[Source]

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

[Source]

     # File lib/sass/script/parser.rb, line 293
293:       def fn_arglist
294:         return unless e = equals
295:         return [e] unless try_tok(:comma)
296:         [e, *assert_expr(:fn_arglist)]
297:       end

[Source]

     # File lib/sass/script/parser.rb, line 257
257:       def funcall
258:         return raw unless tok = try_tok(:funcall)
259:         args = fn_arglist || []
260:         assert_tok(:rparen)
261:         node(Script::Funcall.new(tok.value, args))
262:       end

[Source]

     # File lib/sass/script/parser.rb, line 246
246:       def ident
247:         return funcall unless @lexer.peek && @lexer.peek.type == :ident
248:         return if @stop_at && @stop_at.include?(@lexer.peek.value)
249: 
250:         name = @lexer.next
251:         if color = Color::HTML4_COLORS[name.value]
252:           return node(Color.new(color))
253:         end
254:         node(Script::String.new(name.value, :identifier))
255:       end

[Source]

     # File lib/sass/script/parser.rb, line 213
213:       def interpolation(first = concat)
214:         e = first
215:         while interp = try_tok(:begin_interpolation)
216:           wb = @lexer.whitespace?(interp)
217:           line = @lexer.line
218:           mid = parse_interpolated
219:           wa = @lexer.whitespace?
220:           e = Script::Interpolation.new(e, mid, concat, wb, wa)
221:           e.line = line
222:         end
223:         e
224:       end

@private

[Source]

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

[Source]

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

[Source]

     # File lib/sass/script/parser.rb, line 388
388:       def node(node)
389:         node.line = @lexer.line
390:         node
391:       end

[Source]

     # File lib/sass/script/parser.rb, line 347
347:       def number
348:         return literal unless tok = try_tok(:number)
349:         num = tok.value
350:         num.original = num.to_s unless @in_parens
351:         num
352:       end

[Source]

     # File lib/sass/script/parser.rb, line 320
320:       def paren
321:         return variable unless try_tok(:lparen)
322:         was_in_parens = @in_parens
323:         @in_parens = true
324:         e = assert_expr(:expr)
325:         assert_tok(:rparen)
326:         return e
327:       ensure
328:         @in_parens = was_in_parens
329:       end

[Source]

     # File lib/sass/script/parser.rb, line 305
305:       def raw
306:         return special_fun unless tok = try_tok(:raw)
307:         node(Script::String.new(tok.value))
308:       end

[Source]

     # File lib/sass/script/parser.rb, line 310
310:       def special_fun
311:         return paren unless tok = try_tok(:special_fun)
312:         first = node(Script::String.new(tok.value.first))
313:         Haml::Util.enum_slice(tok.value[1..-1], 2).inject(first) do |l, (i, r)|
314:           Script::Interpolation.new(
315:             l, i, r && node(Script::String.new(r)),
316:             false, false)
317:         end
318:       end

[Source]

     # File lib/sass/script/parser.rb, line 336
336:       def string
337:         return number unless first = try_tok(:string)
338:         return first.value unless try_tok(:begin_interpolation)
339:         line = @lexer.line
340:         mid = parse_interpolated
341:         last = assert_expr(:string)
342:         interp = StringInterpolation.new(first.value, mid, last)
343:         interp.line = line
344:         interp
345:       end

[Source]

     # File lib/sass/script/parser.rb, line 190
190:       def try_op_before_interp(op, prev = nil)
191:         return unless @lexer.peek && @lexer.peek.type == :begin_interpolation
192:         wb = @lexer.whitespace?(op)
193:         str = Script::String.new(Lexer::OPERATORS_REVERSE[op.type])
194:         str.line = @lexer.line
195:         interp = Script::Interpolation.new(prev, str, nil, wb, !:wa, :originally_text)
196:         interp.line = @lexer.line
197:         interpolation(interp)
198:       end

[Source]

     # File lib/sass/script/parser.rb, line 200
200:       def try_ops_after_interp(ops, name)
201:         return unless @lexer.after_interpolation?
202:         return unless op = try_tok(*ops)
203:         interp = try_op_before_interp(op) and return interp
204: 
205:         wa = @lexer.whitespace?
206:         str = Script::String.new(Lexer::OPERATORS_REVERSE[op.type])
207:         str.line = @lexer.line
208:         interp = Script::Interpolation.new(nil, str, assert_expr(name), !:wb, wa, :originally_text)
209:         interp.line = @lexer.line
210:         return interp
211:       end

[Source]

     # File lib/sass/script/parser.rb, line 378
378:       def try_tok(*names)
379:         peeked =  @lexer.peek
380:         peeked && names.include?(peeked.type) && @lexer.next
381:       end

[Source]

     # File lib/sass/script/parser.rb, line 331
331:       def variable
332:         return string unless c = try_tok(:const)
333:         node(Variable.new(*c.value))
334:       end

[Validate]