diff --git a/README.rdoc b/README.rdoc index 9038311c..35465f45 100644 --- a/README.rdoc +++ b/README.rdoc @@ -175,14 +175,14 @@ becomes: :text-decoration underline Pretty nice, no? Well, it gets better. -One of the main complaints against CSS is that it doesn't allow constants. +One of the main complaints against CSS is that it doesn't allow variables. What if have a color or a width you re-use all the time? In CSS, you just have to re-type it each time, which is a nightmare when you decide to change it later. Not so for Sass! -You can use the "!" character to set constants. +You can use the "!" character to set variables. Then, if you put "=" after your attribute name, -you can set it to a constant. +you can set it to a variable. For example: !note_bg= #55aaff @@ -205,7 +205,7 @@ becomes: width: 5em; background-color: #55aaff; } -You can even do simple arithmetic operations with constants, +You can even do simple arithmetic operations with variables, adding numbers and even colors together: !main_bg= #46ar12 @@ -227,7 +227,7 @@ becomes: background-color: #79d645; width: 15em; } -Taking the idea of constants a bit further are mixins. +Taking the idea of variables a bit further are mixins. These let you group whole swathes of CSS attributes into a single directive and then include those anywhere you want: diff --git a/TODO b/TODO index 36cef6d9..05fd4c9e 100644 --- a/TODO +++ b/TODO @@ -2,7 +2,7 @@ Testing: Test html2haml and css2sass Features: - Sass should throw generic errors for undefined constants, not syntax errors + Sass should throw generic errors for undefined variables, not syntax errors Sass::Plugin should log errors There should be a way to make Haml tags not insert whitespace "%li, %a"? diff --git a/lib/sass.rb b/lib/sass.rb index e3e46ce5..fc6c3e77 100644 --- a/lib/sass.rb +++ b/lib/sass.rb @@ -481,7 +481,7 @@ $LOAD_PATH << dir unless $LOAD_PATH.include?(dir) # # === Functions # -# SassScript defines some useful functions (see Sass::Constant::Functions) +# SassScript defines some useful functions (see Sass::Script::Functions) # that are called using the normal CSS function syntax: # # p diff --git a/lib/sass/constant.rb b/lib/sass/constant.rb deleted file mode 100644 index efbeb89c..00000000 --- a/lib/sass/constant.rb +++ /dev/null @@ -1,37 +0,0 @@ -require 'strscan' -require 'sass/constant/funcall' -require 'sass/constant/operation' -require 'sass/constant/literal' -require 'sass/constant/parser' - -module Sass - # This module contains various constant-script related functionality. - module Constant - # :stopdoc: - # The character that begins a constant. - CONSTANT_CHAR = ?! - - # The regular expression used to parse constants - MATCH = /^!(\w+)\s*((?:\|\|)?=)\s*(.+)/ - - # The regular expression used to validate constants without matching - VALIDATE = /^!\w+$/ - - def self.resolve(*args) - parse(*args).to_s - end - - def self.parse(value, constants, line) - Parser.parse(value, constants).perform - rescue Sass::SyntaxError => e - if e.message == "Constant arithmetic error" - e.instance_eval do - @message += ": #{value.dump}." - end - end - e.sass_line = line - raise e - end - # :startdoc: - end -end diff --git a/lib/sass/constant/literal.rb b/lib/sass/constant/literal.rb deleted file mode 100644 index f99eb4ad..00000000 --- a/lib/sass/constant/literal.rb +++ /dev/null @@ -1,61 +0,0 @@ -# :stopdoc: -# Let the subclasses see the superclass -module Sass::Constant; class Literal; end; end; -# :startdoc: - -require 'sass/constant/string' -require 'sass/constant/number' -require 'sass/constant/color' -require 'sass/constant/bool' - -class Sass::Constant::Literal # :nodoc: - attr_reader :value - - def initialize(value = nil) - @value = value - end - - def perform - self - end - - def and(other) - to_bool ? other : self - end - - def or(other) - to_bool ? self : other - end - - def eq(other) - Sass::Constant::Bool.new(self.class == other.class && self.value == other.value) - end - - def neq(other) - Sass::Constant::Bool.new(!eq(other).to_bool) - end - - def unary_not - Sass::Constant::Bool.new(!to_bool) - end - - def concat(other) - Sass::Constant::String.new("#{self.to_s} #{other.to_s}") - end - - def comma(other) - Sass::Constant::String.new("#{self.to_s}, #{other.to_s}") - end - - def inspect - value.inspect - end - - def to_bool - true - end - - def to_i - raise SyntaxError.new("#{value.dump} is not an integer.") - end -end diff --git a/lib/sass/constant/string.rb b/lib/sass/constant/string.rb deleted file mode 100644 index b358802c..00000000 --- a/lib/sass/constant/string.rb +++ /dev/null @@ -1,33 +0,0 @@ -require 'sass/constant/literal' - -module Sass::Constant - class String < Literal # :nodoc: - def plus(other) - Sass::Constant::String.new(self.to_s + other.to_s) - end - - def minus(other) - Sass::Constant::String.new("#{self.to_s}-#{other.to_s}") - end - - def unary_minus - Sass::Constant::String.new("-#{self.to_s}") - end - - def div(other) - Sass::Constant::String.new("#{self.to_s}/#{other.to_s}") - end - - def unary_div - Sass::Constant::String.new("/#{self.to_s}") - end - - def funcall(other) - Sass::Constant::String.new("#{self.to_s}(#{other.to_s})") - end - - def to_s - @value - end - end -end diff --git a/lib/sass/engine.rb b/lib/sass/engine.rb index 42cdbb8d..1d1997a4 100644 --- a/lib/sass/engine.rb +++ b/lib/sass/engine.rb @@ -6,7 +6,7 @@ require 'sass/tree/rule_node' require 'sass/tree/comment_node' require 'sass/tree/attr_node' require 'sass/tree/directive_node' -require 'sass/constant' +require 'sass/script' require 'sass/error' require 'haml/shared' @@ -27,7 +27,7 @@ module Sass ATTRIBUTE_CHAR = ?: # The character that designates that - # an attribute should be assigned to the result of constant arithmetic. + # an attribute should be assigned to a SassScript expression. SCRIPT_CHAR = ?= # The character that designates the beginning of a comment, @@ -83,7 +83,7 @@ module Sass :load_paths => ['.'] }.merge! options @template = template - @constants = {"important" => Constant::String.new("!important")} + @environment = {"important" => Script::String.new("!important")} @mixins = {} end @@ -104,8 +104,8 @@ module Sass protected - def constants - @constants + def environment + @environment end def mixins @@ -171,7 +171,7 @@ END @line = line.index node = parse_line(line, root) - # Node is a symbol if it's non-outputting, like a constant assignment, + # Node is a symbol if it's non-outputting, like a variable assignment, # or an array if it's a group of nodes to add return node unless node.is_a? Tree::Node @@ -219,8 +219,8 @@ END def validate_and_append_child(parent, child, line, root) unless root case child - when :constant - raise SyntaxError.new("Constants may only be declared at the root of a document.", line.index) + when :variable + raise SyntaxError.new("Variables may only be declared at the root of a document.", line.index) when :mixin raise SyntaxError.new("Mixins may only be defined at the root of a document.", line.index) when Tree::DirectiveNode @@ -240,8 +240,8 @@ END case line.text[0] when ATTRIBUTE_CHAR parse_attribute(line.text, ATTRIBUTE) - when Constant::CONSTANT_CHAR - parse_constant(line) + when Script::VARIABLE_CHAR + parse_variable(line) when COMMENT_CHAR parse_comment(line.text) when DIRECTIVE_CHAR @@ -281,25 +281,25 @@ END end if eq.strip[0] == SCRIPT_CHAR - value = Sass::Constant.resolve(value, @constants, @line) + value = Script.resolve(value, @environment, @line) end Tree::AttrNode.new(interpolate(name), interpolate(value), @options) end - def parse_constant(line) - name, op, value = line.text.scan(Sass::Constant::MATCH)[0] - raise SyntaxError.new("Illegal nesting: Nothing may be nested beneath constants.", @line + 1) unless line.children.empty? - raise SyntaxError.new("Invalid constant: \"#{line.text}\".", @line) unless name && value + def parse_variable(line) + name, op, value = line.text.scan(Script::MATCH)[0] + raise SyntaxError.new("Illegal nesting: Nothing may be nested beneath variable declarations.", @line + 1) unless line.children.empty? + raise SyntaxError.new("Invalid variable: \"#{line.text}\".", @line) unless name && value - constant = Sass::Constant.parse(value, @constants, @line) + var = Script.parse(value, @environment, @line) if op == '||=' - @constants[name] ||= constant + @environment[name] ||= var else - @constants[name] = constant + @environment[name] = var end - :constant + :variable end def parse_comment(line) @@ -332,7 +332,7 @@ END end def parse_if(line, root, text) - if Sass::Constant.parse(text, @constants, line.index).to_bool + if Script.parse(text, @environment, line.index).to_bool append_children([], line.children, root) else [] @@ -344,7 +344,7 @@ END if var.nil? # scan failed, try to figure out why for error message if text !~ /^[^\s]+/ - expected = "constant name" + expected = "variable name" elsif text !~ /^[^\s]+\s+from\s+.+/ expected = "'from '" else @@ -352,25 +352,25 @@ END end raise SyntaxError.new("Invalid for directive '@for #{text}': expected #{expected}.", @line) end - raise SyntaxError.new("Invalid constant \"#{var}\".", @line) unless var =~ Constant::VALIDATE + raise SyntaxError.new("Invalid variable \"#{var}\".", @line) unless var =~ Script::VALIDATE - from = Sass::Constant.parse(from_expr, @constants, @line).to_i - to = Sass::Constant.parse(to_expr, @constants, @line).to_i + from = Script.parse(from_expr, @environment, @line).to_i + to = Script.parse(to_expr, @environment, @line).to_i range = Range.new(from, to, to_name == 'to') tree = [] - old_constants = @constants.dup + old_env = @environment.dup for i in range - @constants[var[1..-1]] = Constant::Number.new(i) + @environment[var[1..-1]] = Script::Number.new(i) append_children(tree, line.children, root) end - @constants = old_constants + @environment = old_env tree end def parse_while(line, root, text) tree = [] - while Sass::Constant.parse(text, @constants, line.index).to_bool + while Script.parse(text, @environment, line.index).to_bool append_children(tree, line.children, root) end tree @@ -394,15 +394,15 @@ END required_arg_count = 0 args.map! do |arg| raise SyntaxError.new("Mixin arguments can't be empty.", @line) if arg.empty? || arg == "!" - unless arg[0] == Constant::CONSTANT_CHAR + unless arg[0] == Script::VARIABLE_CHAR raise SyntaxError.new("Mixin argument \"#{arg}\" must begin with an exclamation point (!).", @line) end arg, default = arg.split(/\s*=\s*/, 2) required_arg_count += 1 unless default default_arg_found ||= default - raise SyntaxError.new("Invalid constant \"#{arg}\".", @line) unless arg =~ Constant::VALIDATE + raise SyntaxError.new("Invalid variable \"#{arg}\".", @line) unless arg =~ Script::VALIDATE raise SyntaxError.new("Required arguments must not follow optional arguments \"#{arg}\".", @line) if default_arg_found && !default - default = Sass::Constant.parse(default, @constants, @line) if default + default = Script.parse(default, @environment, @line) if default { :name => arg[1..-1], :default_value => default } end mixin = @mixins[name] = Mixin.new(args, line.children) @@ -422,19 +422,19 @@ Mixin #{name} takes #{mixin.args.size} argument#{'s' if mixin.args.size != 1} but #{args.size} #{args.size == 1 ? 'was' : 'were'} passed. END - old_constants = @constants.dup - mixin.args.zip(args).inject(@constants) do |constants, (arg, value)| - constants[arg[:name]] = if value - Sass::Constant.parse(value, old_constants, @line) - else - arg[:default_value] - end - raise SyntaxError.new("Mixin #{name} is missing parameter ##{mixin.args.index(arg)+1} (#{arg[:name]}).") unless constants[arg[:name]] - constants + old_env = @environment.dup + mixin.args.zip(args).inject(@environment) do |env, (arg, value)| + env[arg[:name]] = if value + Script.parse(value, old_env, @line) + else + arg[:default_value] + end + raise SyntaxError.new("Mixin #{name} is missing parameter ##{mixin.args.index(arg)+1} (#{arg[:name]}).") unless env[arg[:name]] + env end tree = append_children([], mixin.tree, root) - @constants = old_constants + @environment = old_env tree end @@ -448,7 +448,7 @@ END if escapes % 2 == 1 str << '#{' else - str << Sass::Constant.resolve(balance(scan, ?{, ?}, 1)[0][0...-1], @constants, @line) + str << Script.resolve(balance(scan, ?{, ?}, 1)[0][0...-1], @environment, @line) end end @@ -495,7 +495,7 @@ END engine = Sass::Engine.new(file.read, new_options) end - engine.constants.merge! @constants + engine.environment.merge! @environment engine.mixins.merge! @mixins begin @@ -505,7 +505,7 @@ END raise err end nodes += root.children - @constants = engine.constants + @environment = engine.environment @mixins = engine.mixins end end diff --git a/lib/sass/script.rb b/lib/sass/script.rb new file mode 100644 index 00000000..3afbb30b --- /dev/null +++ b/lib/sass/script.rb @@ -0,0 +1,37 @@ +require 'strscan' +require 'sass/script/funcall' +require 'sass/script/operation' +require 'sass/script/literal' +require 'sass/script/parser' + +module Sass + # This module contains various SassScript-related functionality. + module Script + # :stopdoc: + # The character that begins a variable. + VARIABLE_CHAR = ?! + + # The regular expression used to parse variables + MATCH = /^!(\w+)\s*((?:\|\|)?=)\s*(.+)/ + + # The regular expression used to validate variables without matching + VALIDATE = /^!\w+$/ + + def self.resolve(*args) + parse(*args).to_s + end + + def self.parse(value, environment, line) + Parser.parse(value, environment).perform + rescue Sass::SyntaxError => e + if e.message == "SassScript error" + e.instance_eval do + @message += ": #{value.dump}." + end + end + e.sass_line = line + raise e + end + # :startdoc: + end +end diff --git a/lib/sass/constant/bool.rb b/lib/sass/script/bool.rb similarity index 69% rename from lib/sass/constant/bool.rb rename to lib/sass/script/bool.rb index e83a7adb..103a1be1 100644 --- a/lib/sass/constant/bool.rb +++ b/lib/sass/script/bool.rb @@ -1,6 +1,6 @@ -require 'sass/constant/literal' +require 'sass/script/literal' -module Sass::Constant +module Sass::Script class Bool < Literal # :nodoc: def to_s @value.to_s diff --git a/lib/sass/constant/color.rb b/lib/sass/script/color.rb similarity index 84% rename from lib/sass/constant/color.rb rename to lib/sass/script/color.rb index 89b2b883..9244624c 100644 --- a/lib/sass/constant/color.rb +++ b/lib/sass/script/color.rb @@ -1,6 +1,6 @@ -require 'sass/constant/literal' +require 'sass/script/literal' -module Sass::Constant +module Sass::Script class Color < Literal # :nodoc: HTML4_COLORS = { @@ -27,15 +27,15 @@ module Sass::Constant end def plus(other) - if other.is_a? Sass::Constant::String - Sass::Constant::String.new(self.to_s + other.to_s) + if other.is_a? Sass::Script::String + Sass::Script::String.new(self.to_s + other.to_s) else piecewise(other, :+) end end def minus(other) - if other.is_a? Sass::Constant::String + if other.is_a? Sass::Script::String raise NoMethodError.new(nil, :minus) else piecewise(other, :-) @@ -43,7 +43,7 @@ module Sass::Constant end def times(other) - if other.is_a? Sass::Constant::String + if other.is_a? Sass::Script::String raise NoMethodError.new(nil, :times) else piecewise(other, :*) @@ -51,7 +51,7 @@ module Sass::Constant end def div(other) - if other.is_a? Sass::Constant::String + if other.is_a? Sass::Script::String raise NoMethodError.new(nil, :div) else piecewise(other, :/) @@ -59,7 +59,7 @@ module Sass::Constant end def mod(other) - if other.is_a? Sass::Constant::String + if other.is_a? Sass::Script::String raise NoMethodError.new(nil, :mod) else piecewise(other, :%) diff --git a/lib/sass/constant/funcall.rb b/lib/sass/script/funcall.rb similarity index 87% rename from lib/sass/constant/funcall.rb rename to lib/sass/script/funcall.rb index 5b03a94b..0a60a730 100644 --- a/lib/sass/constant/funcall.rb +++ b/lib/sass/script/funcall.rb @@ -1,5 +1,5 @@ module Sass - module Constant + module Script class Funcall attr_reader :name, :args @@ -18,7 +18,7 @@ module Sass def perform unless Functions.public_instance_methods.include?(name) && name !~ /^__/ - return Constant::String.new("#{name}(#{args.join(', ')})") + return Script::String.new("#{name}(#{args.join(', ')})") end return Functions.send(name, *args) diff --git a/lib/sass/constant/functions.rb b/lib/sass/script/functions.rb similarity index 79% rename from lib/sass/constant/functions.rb rename to lib/sass/script/functions.rb index de29dee3..4f0f1407 100644 --- a/lib/sass/constant/functions.rb +++ b/lib/sass/script/functions.rb @@ -1,14 +1,14 @@ -module Sass::Constant - # Methods in this module are accessible from the Sass constant context. +module Sass::Script + # Methods in this module are accessible from the Sass script context. # For example, you can write # # color = hsl(120, 100%, 50%) # - # and it will call Sass::Constant::Functions#hsl. + # and it will call Sass::Script::Functions#hsl. # # You can add your own functions to this module, # but there are a few things to keep in mind. - # First of all, the arguments passed are (currently undocumented) Sass::Constant::Literal objects, + # First of all, the arguments passed are (currently undocumented) Sass::Script::Literal objects, # Literal objects are also the expected return values. # # Second, making Ruby functions accessible from Sass introduces the temptation @@ -22,7 +22,7 @@ module Sass::Constant instance_methods.each { |m| undef_method m unless m =~ /^__/ } extend self - # Creates a Sass::Constant::Color object from hue, saturation, and lightness. + # Creates a Sass::Script::Color object from hue, saturation, and lightness. # As per the CSS3 spec (http://www.w3.org/TR/css3-color/#hsl-color), # hue is in degrees, # and saturation and lightness are percentages. @@ -41,11 +41,11 @@ module Sass::Constant end def percentage(value) - value = value.perform if value.is_a?(Sass::Constant::Operation) - unless value.is_a?(Sass::Constant::Number) && value.unitless? + value = value.perform if value.is_a?(Sass::Script::Operation) + unless value.is_a?(Sass::Script::Number) && value.unitless? raise ArgumentError.new("Value is not a unitless number") end - Sass::Constant::Number.new(value.value * 100, '%') + Sass::Script::Number.new(value.value * 100, '%') end private diff --git a/lib/sass/constant/lexer.rb b/lib/sass/script/lexer.rb similarity index 88% rename from lib/sass/constant/lexer.rb rename to lib/sass/script/lexer.rb index 14cd0010..fab1ceb3 100644 --- a/lib/sass/constant/lexer.rb +++ b/lib/sass/script/lexer.rb @@ -1,7 +1,7 @@ require 'strscan' module Sass - module Constant + module Script class Lexer OPERATORS = { '+' => :plus, @@ -39,7 +39,7 @@ module Sass end whitespace - constant || string || number || color || bool || op || ident || + variable || string || number || color || bool || op || ident || (raise SyntaxError.new("Syntax error in '#{@scanner.string}' at '#{@scanner.rest}'.")) end @@ -62,7 +62,7 @@ module Sass @scanner.scan(/\s*/) end - def constant + def variable return unless @scanner.scan(/!(\w+)/) [:const, @scanner[1]] end @@ -74,14 +74,14 @@ module Sass def string return unless @scanner.scan(/"((?:\\.|[^"\\])*)"/) - [:string, Constant::String.new(@scanner[1].gsub(/\\(.)/, '\1'))] + [:string, Script::String.new(@scanner[1].gsub(/\\(.)/, '\1'))] end def number return unless @scanner.scan(/(-)?(?:(\d*\.\d+)|(\d+))([a-zA-Z%]+)?/) value = @scanner[2] ? @scanner[2].to_f : @scanner[3].to_i value = -value if @scanner[1] - [:number, Constant::Number.new(value, Array(@scanner[4]))] + [:number, Script::Number.new(value, Array(@scanner[4]))] end COLOR = /\##{"([0-9a-fA-F]{1,2})" * 3}|(#{Color::HTML4_COLORS.keys.join("|")})/ @@ -93,12 +93,12 @@ module Sass else (1..3).map {|i| @scanner[i]}.map {|num| num.ljust(2, num).to_i(16)} end - [:color, Constant::Color.new(value)] + [:color, Script::Color.new(value)] end def bool return unless s = @scanner.scan(/(true|false)\b/) - [:bool, Constant::Bool.new(s == 'true')] + [:bool, Script::Bool.new(s == 'true')] end def op diff --git a/lib/sass/script/literal.rb b/lib/sass/script/literal.rb new file mode 100644 index 00000000..fbe935a4 --- /dev/null +++ b/lib/sass/script/literal.rb @@ -0,0 +1,56 @@ +class Sass::Script::Literal # :nodoc: + require 'sass/script/string' + require 'sass/script/number' + require 'sass/script/color' + require 'sass/script/bool' + + attr_reader :value + + def initialize(value = nil) + @value = value + end + + def perform + self + end + + def and(other) + to_bool ? other : self + end + + def or(other) + to_bool ? self : other + end + + def eq(other) + Sass::Script::Bool.new(self.class == other.class && self.value == other.value) + end + + def neq(other) + Sass::Script::Bool.new(!eq(other).to_bool) + end + + def unary_not + Sass::Script::Bool.new(!to_bool) + end + + def concat(other) + Sass::Script::String.new("#{self.to_s} #{other.to_s}") + end + + def comma(other) + Sass::Script::String.new("#{self.to_s}, #{other.to_s}") + end + + def inspect + value.inspect + end + + def to_bool + true + end + + def to_i + raise SyntaxError.new("#{value.dump} is not an integer.") + end +end diff --git a/lib/sass/constant/number.rb b/lib/sass/script/number.rb similarity index 97% rename from lib/sass/constant/number.rb rename to lib/sass/script/number.rb index ae87698b..d1a01f03 100644 --- a/lib/sass/constant/number.rb +++ b/lib/sass/script/number.rb @@ -1,6 +1,6 @@ -require 'sass/constant/literal' +require 'sass/script/literal' -module Sass::Constant +module Sass::Script class Number < Literal # :nodoc: attr_reader :numerator_units, :denominator_units @@ -20,7 +20,7 @@ module Sass::Constant elsif other.is_a?(Color) other.plus(self) else - Sass::Constant::String.new(self.to_s + other.to_s) + Sass::Script::String.new(self.to_s + other.to_s) end end @@ -66,7 +66,7 @@ module Sass::Constant end def eq(other) - Sass::Constant::Bool.new(super.to_bool && + Sass::Script::Bool.new(super.to_bool && self.numerator_units.sort == other.numerator_units.sort && self.denominator_units.sort == other.denominator_units.sort) end diff --git a/lib/sass/constant/operation.rb b/lib/sass/script/operation.rb similarity index 78% rename from lib/sass/constant/operation.rb rename to lib/sass/script/operation.rb index bd0c473e..75cbe820 100644 --- a/lib/sass/constant/operation.rb +++ b/lib/sass/script/operation.rb @@ -1,10 +1,10 @@ -require 'sass/constant/string' -require 'sass/constant/number' -require 'sass/constant/color' -require 'sass/constant/functions' -require 'sass/constant/unary_operation' +require 'sass/script/string' +require 'sass/script/number' +require 'sass/script/color' +require 'sass/script/functions' +require 'sass/script/unary_operation' -module Sass::Constant +module Sass::Script class Operation # :nodoc: def initialize(operand1, operand2, operator) @operand1 = operand1 diff --git a/lib/sass/constant/parser.rb b/lib/sass/script/parser.rb similarity index 87% rename from lib/sass/constant/parser.rb rename to lib/sass/script/parser.rb index 66df6247..ee829b21 100644 --- a/lib/sass/constant/parser.rb +++ b/lib/sass/script/parser.rb @@ -1,11 +1,11 @@ -require 'sass/constant/lexer' +require 'sass/script/lexer' module Sass - module Constant + module Script class Parser - def initialize(str, constants = {}) + def initialize(str, environment = {}) @lexer = Lexer.new(str) - @constants = constants + @environment = environment end def parse @@ -67,10 +67,10 @@ RUBY def funcall return paren unless name = try_tok(:ident) # An identifier without arguments is just a string - return Constant::String.new(name.last) unless try_tok(:lparen) + return Script::String.new(name.last) unless try_tok(:lparen) args = arglist || [] assert_tok(:rparen) - Constant::Funcall.new(name.last, args) + Script::Funcall.new(name.last, args) end def arglist @@ -80,17 +80,17 @@ RUBY end def paren - return constant unless try_tok(:lparen) + return variable unless try_tok(:lparen) e = assert_expr(:expr) assert_tok(:rparen) return e end - def constant + def variable return literal unless c = try_tok(:const) - (val = @constants[c.last]) && (return val) + (val = @environment[c.last]) && (return val) - raise SyntaxError.new("Undefined constant: \"!#{c.last}\".") + raise SyntaxError.new("Undefined variable: \"!#{c.last}\".") end def literal diff --git a/lib/sass/script/string.rb b/lib/sass/script/string.rb new file mode 100644 index 00000000..9d643afa --- /dev/null +++ b/lib/sass/script/string.rb @@ -0,0 +1,33 @@ +require 'sass/script/literal' + +module Sass::Script + class String < Literal # :nodoc: + def plus(other) + Sass::Script::String.new(self.to_s + other.to_s) + end + + def minus(other) + Sass::Script::String.new("#{self.to_s}-#{other.to_s}") + end + + def unary_minus + Sass::Script::String.new("-#{self.to_s}") + end + + def div(other) + Sass::Script::String.new("#{self.to_s}/#{other.to_s}") + end + + def unary_div + Sass::Script::String.new("/#{self.to_s}") + end + + def funcall(other) + Sass::Script::String.new("#{self.to_s}(#{other.to_s})") + end + + def to_s + @value + end + end +end diff --git a/lib/sass/constant/unary_operation.rb b/lib/sass/script/unary_operation.rb similarity index 96% rename from lib/sass/constant/unary_operation.rb rename to lib/sass/script/unary_operation.rb index ddb6df9f..7209515e 100644 --- a/lib/sass/constant/unary_operation.rb +++ b/lib/sass/script/unary_operation.rb @@ -1,4 +1,4 @@ -module Sass::Constant +module Sass::Script class UnaryOperation # :nodoc: def initialize(operand, operator) @operand = operand diff --git a/test/sass/engine_test.rb b/test/sass/engine_test.rb index f11bab6d..00e69993 100755 --- a/test/sass/engine_test.rb +++ b/test/sass/engine_test.rb @@ -31,11 +31,11 @@ class SassEngineTest < Test::Unit::TestCase "a\n b : c" => 'Invalid attribute: "b : c".', "a\n b=c: d" => 'Invalid attribute: "b=c: d".', ":a" => 'Attributes aren\'t allowed at the root of a document.', - "!" => 'Invalid constant: "!".', - "!a" => 'Invalid constant: "!a".', - "! a" => 'Invalid constant: "! a".', - "!a b" => 'Invalid constant: "!a b".', - "a\n :b c\n !d = 3" => "Constants may only be declared at the root of a document.", + "!" => 'Invalid variable: "!".', + "!a" => 'Invalid variable: "!a".', + "! a" => 'Invalid variable: "! a".', + "!a b" => 'Invalid variable: "!a b".', + "a\n :b c\n !d = 3" => "Variables may only be declared at the root of a document.", "!a = 1b + 2c" => "Incompatible units: 'c' and 'b'.", "a\n :b= 1b * 2c" => "2b*c isn't a valid CSS value.", "a\n :b= 1b % 2c" => "Cannot modulo by a number with units: 2c.", @@ -46,7 +46,7 @@ class SassEngineTest < Test::Unit::TestCase "a,\n :b c" => ["Rules can\'t end in commas.", 1], "a," => "Rules can\'t end in commas.", "a,\n!b = c" => ["Rules can\'t end in commas.", 1], - "!a = b\n :c d\n" => "Illegal nesting: Nothing may be nested beneath constants.", + "!a = b\n :c d\n" => "Illegal nesting: Nothing may be nested beneath variable declarations.", "@import foo.sass" => "File to import not found or unreadable: foo.sass.", "@import templates/basic\n foo" => "Illegal nesting: Nothing may be nested beneath import directives.", "foo\n @import templates/basic" => "Import directives may only be used at the root of a document.", @@ -68,7 +68,7 @@ class SassEngineTest < Test::Unit::TestCase "=a(b)" => 'Mixin argument "b" must begin with an exclamation point (!).', "=a(,)" => "Mixin arguments can't be empty.", "=a(!)" => "Mixin arguments can't be empty.", - "=a(!foo bar)" => "Invalid constant \"!foo bar\".", + "=a(!foo bar)" => "Invalid variable \"!foo bar\".", "=foo\n bar: baz\n+foo" => ["Attributes aren't allowed at the root of a document.", 2], "a-\#{!b\n c: d" => ["Unbalanced brackets.", 1], "=a(!b = 1, !c)" => "Required arguments must not follow optional arguments \"!c\".", diff --git a/test/sass/functions_test.rb b/test/sass/functions_test.rb index 6183e70c..ab43de96 100644 --- a/test/sass/functions_test.rb +++ b/test/sass/functions_test.rb @@ -1,6 +1,6 @@ require 'test/unit' require File.dirname(__FILE__) + '/../../lib/sass' -require 'sass/constant' +require 'sass/script' class SassFunctionTest < Test::Unit::TestCase def test_hsl @@ -46,6 +46,6 @@ class SassFunctionTest < Test::Unit::TestCase private def assert_rgb_hsl(rgb, hsl) - assert_equal(rgb, Sass::Constant::Functions.hsl(*hsl.map(&Sass::Constant::Parser.method(:parse))).value) + assert_equal(rgb, Sass::Script::Functions.hsl(*hsl.map(&Sass::Script::Parser.method(:parse))).value) end end diff --git a/test/sass/plugin_test.rb b/test/sass/plugin_test.rb index b4323f3c..b0711f00 100644 --- a/test/sass/plugin_test.rb +++ b/test/sass/plugin_test.rb @@ -5,7 +5,7 @@ require 'fileutils' class SassPluginTest < Test::Unit::TestCase @@templates = %w{ - complex constants parent_ref import alt + complex script parent_ref import alt subdir/subdir subdir/nested_subdir/nested_subdir } @@ -52,7 +52,7 @@ class SassPluginTest < Test::Unit::TestCase File.delete(tempfile_loc('bork')) Sass::Plugin.update_stylesheets File.open(tempfile_loc('bork')) do |file| - assert_equal("/*\nSass::SyntaxError: Undefined constant: \"!bork\".\non line 2 of #{template_loc('bork')}\n\n1: bork\n2: :bork= !bork", file.read.split("\n")[0...6].join("\n")) + assert_equal("/*\nSass::SyntaxError: Undefined variable: \"!bork\".\non line 2 of #{template_loc('bork')}\n\n1: bork\n2: :bork= !bork", file.read.split("\n")[0...6].join("\n")) end File.delete(tempfile_loc('bork')) end diff --git a/test/sass/results/constants.css b/test/sass/results/script.css similarity index 100% rename from test/sass/results/constants.css rename to test/sass/results/script.css diff --git a/test/sass/templates/constants.sass b/test/sass/templates/script.sass similarity index 100% rename from test/sass/templates/constants.sass rename to test/sass/templates/script.sass