mirror of
https://github.com/haml/haml.git
synced 2022-11-09 12:33:31 -05:00
Merge branch 'scss' into conversions
Conflicts: lib/sass/script/funcall.rb lib/sass/script/literal.rb lib/sass/script/node.rb lib/sass/script/operation.rb lib/sass/script/unary_operation.rb
This commit is contained in:
commit
de1a763fea
10 changed files with 158 additions and 61 deletions
|
@ -34,12 +34,22 @@ module Sass
|
|||
"#{name}(#{args.map {|a| a.to_sass}.join(', ')})"
|
||||
end
|
||||
|
||||
# Returns the arguments to the function.
|
||||
#
|
||||
# @return [Array<Node>]
|
||||
# @see Node#children
|
||||
def children
|
||||
@args
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
# Evaluates the function call.
|
||||
#
|
||||
# @param environment [Sass::Environment] The environment in which to evaluate the SassScript
|
||||
# @return [Literal] The SassScript object that is the value of the function call
|
||||
# @raise [Sass::SyntaxError] if the function call raises an ArgumentError
|
||||
def perform(environment)
|
||||
def _perform(environment)
|
||||
args = self.args.map {|a| a.perform(environment)}
|
||||
ruby_name = name.gsub('-', '_')
|
||||
unless Haml::Util.has?(:public_instance_method, Functions, ruby_name) && ruby_name !~ /^__/
|
||||
|
@ -53,14 +63,6 @@ module Sass
|
|||
raise e unless e.backtrace.any? {|t| t =~ /:in `(block in )?(#{name}|perform)'$/}
|
||||
raise Sass::SyntaxError.new("#{e.message} for `#{name}'")
|
||||
end
|
||||
|
||||
# Returns the arguments to the function.
|
||||
#
|
||||
# @return [Array<Node>]
|
||||
# @see Node#children
|
||||
def children
|
||||
@args
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -157,6 +157,8 @@ module Sass
|
|||
unless value
|
||||
raise SyntaxError.new("Syntax error in '#{@scanner.string}' at character #{current_position}.")
|
||||
end
|
||||
|
||||
value.last.line = @line if value.last.is_a?(Script::Node)
|
||||
Token.new(value.first, value.last, @line, last_match_position)
|
||||
end
|
||||
|
||||
|
|
|
@ -23,14 +23,6 @@ module Sass::Script
|
|||
@value = value
|
||||
end
|
||||
|
||||
# Evaluates the literal.
|
||||
#
|
||||
# @param environment [Sass::Environment] The environment in which to evaluate the SassScript
|
||||
# @return [Literal] This literal
|
||||
def perform(environment)
|
||||
self
|
||||
end
|
||||
|
||||
# Returns an empty array.
|
||||
#
|
||||
# @return [Array<Node>] empty
|
||||
|
@ -207,5 +199,15 @@ MSG
|
|||
raise Sass::SyntaxError.new("[BUG] All subclasses of Sass::Literal must implement #to_s.")
|
||||
end
|
||||
alias_method :to_sass, :to_s
|
||||
|
||||
protected
|
||||
|
||||
# Evaluates the literal.
|
||||
#
|
||||
# @param environment [Sass::Environment] The environment in which to evaluate the SassScript
|
||||
# @return [Literal] This literal
|
||||
def _perform(environment)
|
||||
self
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -8,6 +8,11 @@ module Sass::Script
|
|||
# @return [{Symbol => Object}]
|
||||
attr_reader :options
|
||||
|
||||
# The line of the document on which this node appeared.
|
||||
#
|
||||
# @return [Fixnum]
|
||||
attr_accessor :line
|
||||
|
||||
# Sets the options hash for this node,
|
||||
# as well as for all child nodes.
|
||||
# See {file:SASS_REFERENCE.md#sass_options the Sass options documentation}.
|
||||
|
@ -20,10 +25,16 @@ module Sass::Script
|
|||
|
||||
# Evaluates the node.
|
||||
#
|
||||
# \{#perform} shouldn't be overridden directly;
|
||||
# instead, override \{#\_perform}.
|
||||
#
|
||||
# @param environment [Sass::Environment] The environment in which to evaluate the SassScript
|
||||
# @return [Literal] The SassScript object that is the value of the SassScript
|
||||
def perform(environment)
|
||||
raise NotImplementedError.new("All subclasses of Sass::Script::Node must override #perform.")
|
||||
_perform(environment)
|
||||
rescue Sass::SyntaxError => e
|
||||
e.modify_backtrace(:line => line)
|
||||
raise e
|
||||
end
|
||||
|
||||
# Returns all child nodes of this node.
|
||||
|
@ -39,5 +50,16 @@ module Sass::Script
|
|||
def to_sass
|
||||
raise NotImplementedError.new("All subclasses of Sass::Script::Node must override #to_sass.")
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
# Evaluates this node.
|
||||
#
|
||||
# @param environment [Sass::Environment] The environment in which to evaluate the SassScript
|
||||
# @return [Literal] The SassScript object that is the value of the SassScript
|
||||
# @see #perform
|
||||
def _perform(environment)
|
||||
raise NotImplementedError.new("All subclasses of Sass::Script::Node must override #_perform.")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -42,12 +42,22 @@ module Sass::Script
|
|||
"#{o1}#{sep}#{o2}"
|
||||
end
|
||||
|
||||
# Returns the operands for this operation.
|
||||
#
|
||||
# @return [Array<Node>]
|
||||
# @see Node#children
|
||||
def children
|
||||
[@operand1, @operand2]
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
# Evaluates the operation.
|
||||
#
|
||||
# @param environment [Sass::Environment] The environment in which to evaluate the SassScript
|
||||
# @return [Literal] The SassScript object that is the value of the operation
|
||||
# @raise [Sass::SyntaxError] if the operation is undefined for the operands
|
||||
def perform(environment)
|
||||
def _perform(environment)
|
||||
literal1 = @operand1.perform(environment)
|
||||
literal2 = @operand2.perform(environment)
|
||||
begin
|
||||
|
@ -60,14 +70,6 @@ module Sass::Script
|
|||
end
|
||||
end
|
||||
|
||||
# Returns the operands for this operation.
|
||||
#
|
||||
# @return [Array<Node>]
|
||||
# @see Node#children
|
||||
def children
|
||||
[@operand1, @operand2]
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def operand_to_sass(pred, op)
|
||||
|
|
|
@ -5,6 +5,13 @@ module Sass
|
|||
# The parser for SassScript.
|
||||
# It parses a string of code into a tree of {Script::Node}s.
|
||||
class Parser
|
||||
# The line number of the parser's current position.
|
||||
#
|
||||
# @return [Fixnum]
|
||||
def line
|
||||
@lexer.line
|
||||
end
|
||||
|
||||
# @param str [String, StringScanner] The source text to parse
|
||||
# @param line [Fixnum] The line on which the SassScript appears.
|
||||
# Used for error reporting
|
||||
|
@ -151,7 +158,9 @@ module Sass
|
|||
def #{name}
|
||||
return unless e = #{sub}
|
||||
while tok = try_tok(#{ops.map {|o| o.inspect}.join(', ')})
|
||||
line = @lexer.line
|
||||
e = Operation.new(e, assert_expr(#{sub.inspect}), tok.type)
|
||||
e.line = line
|
||||
end
|
||||
e
|
||||
end
|
||||
|
@ -162,7 +171,10 @@ RUBY
|
|||
class_eval <<RUBY
|
||||
def unary_#{op}
|
||||
return #{sub} unless try_tok(:#{op})
|
||||
UnaryOperation.new(assert_expr(:unary_#{op}), :#{op})
|
||||
line = @lexer.line
|
||||
op = UnaryOperation.new(assert_expr(:unary_#{op}), :#{op})
|
||||
op.line = line
|
||||
op
|
||||
end
|
||||
RUBY
|
||||
end
|
||||
|
@ -175,7 +187,7 @@ RUBY
|
|||
def concat
|
||||
return unless e = or_expr
|
||||
while sub = or_expr
|
||||
e = Operation.new(e, sub, :concat)
|
||||
e = node(Operation.new(e, sub, :concat))
|
||||
end
|
||||
e
|
||||
end
|
||||
|
@ -199,7 +211,7 @@ RUBY
|
|||
# An identifier without arguments is just a string
|
||||
unless try_tok(:lparen)
|
||||
if color = Color::HTML4_COLORS[name.value]
|
||||
return Color.new(color)
|
||||
return node(Color.new(color))
|
||||
end
|
||||
|
||||
filename = @options[:filename]
|
||||
|
@ -209,11 +221,11 @@ On line #{name.line}, character #{name.offset}#{" of '#{filename}'" if filename}
|
|||
Implicit strings have been deprecated and will be removed in version 3.0.
|
||||
'#{name.value}' was not quoted. Please add double quotes (e.g. "#{name.value}").
|
||||
END
|
||||
Script::String.new(name.value)
|
||||
node(Script::String.new(name.value))
|
||||
else
|
||||
args = arglist || []
|
||||
assert_tok(:rparen)
|
||||
Script::Funcall.new(name.value, args)
|
||||
node(Script::Funcall.new(name.value, args))
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -245,15 +257,18 @@ END
|
|||
|
||||
def variable
|
||||
return string unless c = try_tok(:const)
|
||||
Variable.new(c.value)
|
||||
node(Variable.new(c.value))
|
||||
end
|
||||
|
||||
def string
|
||||
return literal unless first = try_tok(:string)
|
||||
return first.value unless try_tok(:begin_interpolation)
|
||||
line = @lexer.line
|
||||
mid = parse_interpolated
|
||||
last = assert_expr(:string)
|
||||
Operation.new(first.value, Operation.new(mid, last, :plus), :plus)
|
||||
op = Operation.new(first.value, node(Operation.new(mid, last, :plus)), :plus)
|
||||
op.line = line
|
||||
op
|
||||
end
|
||||
|
||||
def literal
|
||||
|
@ -282,6 +297,11 @@ END
|
|||
return if @lexer.done?
|
||||
raise Sass::SyntaxError.new("Unexpected #{@lexer.peek.type} token.")
|
||||
end
|
||||
|
||||
def node(node)
|
||||
node.line = @lexer.line
|
||||
node
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -24,20 +24,6 @@ module Sass::Script
|
|||
op + (op =~ /[a-z]/ ? " " : "") + operand
|
||||
end
|
||||
|
||||
# Evaluates the operation.
|
||||
#
|
||||
# @param environment [Sass::Environment] The environment in which to evaluate the SassScript
|
||||
# @return [Literal] The SassScript object that is the value of the operation
|
||||
# @raise [Sass::SyntaxError] if the operation is undefined for the operand
|
||||
def perform(environment)
|
||||
operator = "unary_#{@operator}"
|
||||
literal = @operand.perform(environment)
|
||||
literal.send(operator)
|
||||
rescue NoMethodError => e
|
||||
raise e unless e.name.to_s == operator.to_s
|
||||
raise Sass::SyntaxError.new("Undefined unary operation: \"#{@operator} #{literal}\".")
|
||||
end
|
||||
|
||||
# Returns the operand of the operation.
|
||||
#
|
||||
# @return [Array<Node>]
|
||||
|
@ -45,5 +31,21 @@ module Sass::Script
|
|||
def children
|
||||
[@operand]
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
# Evaluates the operation.
|
||||
#
|
||||
# @param environment [Sass::Environment] The environment in which to evaluate the SassScript
|
||||
# @return [Literal] The SassScript object that is the value of the operation
|
||||
# @raise [Sass::SyntaxError] if the operation is undefined for the operand
|
||||
def _perform(environment)
|
||||
operator = "unary_#{@operator}"
|
||||
literal = @operand.perform(environment)
|
||||
literal.send(operator)
|
||||
rescue NoMethodError => e
|
||||
raise e unless e.name.to_s == operator.to_s
|
||||
raise Sass::SyntaxError.new("Undefined unary operation: \"#{@operator} #{literal}\".")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -18,16 +18,6 @@ module Sass
|
|||
end
|
||||
alias_method :to_sass, :inspect
|
||||
|
||||
# Evaluates the variable.
|
||||
#
|
||||
# @param environment [Sass::Environment] The environment in which to evaluate the SassScript
|
||||
# @return [Literal] The SassScript object that is the value of the variable
|
||||
# @raise [Sass::SyntaxError] if the variable is undefined
|
||||
def perform(environment)
|
||||
(val = environment.var(name)) && (return val)
|
||||
raise SyntaxError.new("Undefined variable: \"!#{name}\".")
|
||||
end
|
||||
|
||||
# Returns an empty array.
|
||||
#
|
||||
# @return [Array<Node>] empty
|
||||
|
@ -35,6 +25,18 @@ module Sass
|
|||
def children
|
||||
[]
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
# Evaluates the variable.
|
||||
#
|
||||
# @param environment [Sass::Environment] The environment in which to evaluate the SassScript
|
||||
# @return [Literal] The SassScript object that is the value of the variable
|
||||
# @raise [Sass::SyntaxError] if the variable is undefined
|
||||
def _perform(environment)
|
||||
(val = environment.var(name)) && (return val)
|
||||
raise SyntaxError.new("Undefined variable: \"!#{name}\".")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -584,8 +584,11 @@ MESSAGE
|
|||
end
|
||||
|
||||
def sass_script(*args)
|
||||
ScriptParser.new(@scanner, @line,
|
||||
@scanner.pos - (@scanner.string.rindex("\n") || 0)).send(*args)
|
||||
parser = ScriptParser.new(@scanner, @line,
|
||||
@scanner.pos - (@scanner.string.rindex("\n") || 0))
|
||||
result = parser.send(*args)
|
||||
@line = parser.line
|
||||
result
|
||||
end
|
||||
|
||||
EXPR_NAMES = {
|
||||
|
|
|
@ -824,7 +824,7 @@ SCSS
|
|||
assert_equal 2, e.sass_line
|
||||
end
|
||||
|
||||
def test_multiline_script_error
|
||||
def test_multiline_script_syntax_error
|
||||
render <<SCSS
|
||||
foo {
|
||||
bar =
|
||||
|
@ -835,4 +835,44 @@ SCSS
|
|||
assert_equal "Expected expression, was plus token.", e.message
|
||||
assert_equal 3, e.sass_line
|
||||
end
|
||||
|
||||
def test_multiline_script_runtime_error
|
||||
render <<SCSS
|
||||
foo {
|
||||
bar = "baz" +
|
||||
"bar" +
|
||||
!bang }
|
||||
SCSS
|
||||
assert(false, "Expected syntax error")
|
||||
rescue Sass::SyntaxError => e
|
||||
assert_equal "Undefined variable: \"!bang\".", e.message
|
||||
assert_equal 4, e.sass_line
|
||||
end
|
||||
|
||||
def test_post_multiline_script_runtime_error
|
||||
render <<SCSS
|
||||
foo {
|
||||
bar = "baz" +
|
||||
"bar" +
|
||||
"baz";
|
||||
bip = !bop; }
|
||||
SCSS
|
||||
assert(false, "Expected syntax error")
|
||||
rescue Sass::SyntaxError => e
|
||||
assert_equal "Undefined variable: \"!bop\".", e.message
|
||||
assert_equal 5, e.sass_line
|
||||
end
|
||||
|
||||
def test_multiline_property_runtime_error
|
||||
render <<SCSS
|
||||
foo {
|
||||
bar: baz
|
||||
bar
|
||||
\#{!bang} }
|
||||
SCSS
|
||||
assert(false, "Expected syntax error")
|
||||
rescue Sass::SyntaxError => e
|
||||
assert_equal "Undefined variable: \"!bang\".", e.message
|
||||
assert_equal 4, e.sass_line
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue