Class | Sass::Script::Parser |
In: |
lib/sass/script/parser.rb
|
Parent: | Object |
The parser for SassScript. It parses a string of code into a tree of {Script::Node}s.
@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 filename [String] The name of the file in which the SassScript appears.
Used for error reporting
# File lib/sass/script/parser.rb, line 15 15: def initialize(str, line, offset, filename = nil) 16: @filename = filename 17: @lexer = Lexer.new(str, line, offset, filename) 18: 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 81 81: def self.parse(*args) 82: new(*args).parse 83: 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 92 92: def production(name, sub, *ops) 93: class_eval " def \#{name}\n return unless e = \#{sub}\n while tok = try_tok(\#{ops.map {|o| o.inspect}.join(', ')})\n e = Operation.new(e, assert_expr(\#{sub.inspect}), tok.type)\n end\n e\n end\n" 94: end
# File lib/sass/script/parser.rb, line 105 105: def unary(op, sub) 106: class_eval " def unary_\#{op}\n return \#{sub} unless try_tok(:\#{op})\n UnaryOperation.new(assert_expr(:unary_\#{op}), :\#{op})\n end\n" 107: 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 37 37: def parse 38: expr = assert_expr :expr 39: assert_done 40: expr 41: 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 27 27: def parse_interpolated 28: expr = assert_expr :expr 29: assert_tok :end_interpolation 30: expr 31: 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
# File lib/sass/script/parser.rb, line 63 63: def parse_mixin_definition_arglist 64: args = [] 65: 66: if try_tok(:lparen) 67: args = defn_arglist(false) || args 68: assert_tok(:rparen) 69: end 70: assert_done 71: 72: args 73: 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
# File lib/sass/script/parser.rb, line 47 47: def parse_mixin_include_arglist 48: args = [] 49: 50: if try_tok(:lparen) 51: args = arglist || args 52: assert_tok(:rparen) 53: end 54: assert_done 55: 56: args 57: end
# File lib/sass/script/parser.rb, line 171 171: def arglist 172: return unless e = concat 173: return [e] unless try_tok(:comma) 174: [e, *arglist] 175: end
# File lib/sass/script/parser.rb, line 219 219: def assert_done 220: return if @lexer.done? 221: raise Sass::SyntaxError.new("Unexpected #{@lexer.peek.type} token.") 222: end
It would be possible to have unified assert and try methods, but detecting the method/token difference turns out to be quite expensive.
# File lib/sass/script/parser.rb, line 204 204: def assert_expr(name) 205: (e = send(name)) && (return e) 206: raise Sass::SyntaxError.new("Expected expression, was #{@lexer.done? ? 'end of text' : "#{@lexer.peek.type} token"}.") 207: end
# File lib/sass/script/parser.rb, line 209 209: def assert_tok(*names) 210: (t = try_tok(*names)) && (return t) 211: raise Sass::SyntaxError.new("Expected #{names.join(' or ')} token, was #{@lexer.done? ? 'end of text' : "#{@lexer.peek.type} token"}.") 212: end
# File lib/sass/script/parser.rb, line 120 120: def concat 121: return unless e = or_expr 122: while sub = or_expr 123: e = Operation.new(e, sub, :concat) 124: end 125: e 126: end
# File lib/sass/script/parser.rb, line 158 158: def defn_arglist(must_have_default) 159: return unless c = try_tok(:const) 160: var = Script::Variable.new(c.value) 161: if try_tok(:single_eq) 162: val = assert_expr(:concat) 163: elsif must_have_default 164: raise SyntaxError.new("Required argument #{var.inspect} must come before any optional arguments.", @line) 165: end 166: 167: return [[var, val]] unless try_tok(:comma) 168: [[var, val], *defn_arglist(val)] 169: end
# File lib/sass/script/parser.rb, line 139 139: def funcall 140: return paren unless name = try_tok(:ident) 141: # An identifier without arguments is just a string 142: unless try_tok(:lparen) 143: warn("DEPRECATION WARNING:\nOn line \#{name.line}, character \#{name.offset}\#{\" of '\#{@filename}'\" if @filename}\nImplicit strings have been deprecated and will be removed in version 2.4.\n'\#{name.value}' was not quoted. Please add double quotes (e.g. \"\#{name.value}\").\n") 144: Script::String.new(name.value) 145: else 146: args = arglist || [] 147: assert_tok(:rparen) 148: Script::Funcall.new(name.value, args) 149: end 150: end
# File lib/sass/script/parser.rb, line 197 197: def literal 198: (t = try_tok(:number, :color, :bool)) && (return t.value) 199: end
# File lib/sass/script/parser.rb, line 177 177: def paren 178: return variable unless try_tok(:lparen) 179: e = assert_expr(:expr) 180: assert_tok(:rparen) 181: return e 182: end
# File lib/sass/script/parser.rb, line 189 189: def string 190: return literal unless first = try_tok(:string) 191: return first.value unless try_tok(:begin_interpolation) 192: mid = parse_interpolated 193: last = assert_expr(:string) 194: Operation.new(first.value, Operation.new(mid, last, :plus), :plus) 195: end
# File lib/sass/script/parser.rb, line 214 214: def try_tok(*names) 215: peeked = @lexer.peek 216: peeked && names.include?(peeked.type) && @lexer.next 217: end