| 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