Rename Sass::Constant to Sass::Script, and refer to constants as variables.
This commit is contained in:
parent
b59dffc442
commit
03d2ab6f3b
10
README.rdoc
10
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:
|
||||
|
||||
|
|
2
TODO
2
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"?
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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 <expr>'"
|
||||
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
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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, :%)
|
|
@ -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)
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -1,4 +1,4 @@
|
|||
module Sass::Constant
|
||||
module Sass::Script
|
||||
class UnaryOperation # :nodoc:
|
||||
def initialize(operand, operator)
|
||||
@operand = operand
|
|
@ -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\".",
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue