summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEevee (Alex Munroe) <eevee.git@veekun.com>2013-08-15 15:45:36 -0700
committerEevee (Alex Munroe) <eevee.git@veekun.com>2013-08-15 15:45:36 -0700
commit0f12919835eabdfef8a7b3b981de057bb8bc5957 (patch)
tree1ee713ade7a015a0a684a81b14f17d9521c4de44
parent85cae01f116d0177c8f74d17ac1945b845b902c0 (diff)
downloadpyscss-0f12919835eabdfef8a7b3b981de057bb8bc5957.tar.gz
Experimental first crack at maps support.
-rw-r--r--scss/expression.py89
-rw-r--r--scss/functions/core.py44
-rw-r--r--scss/src/grammar/grammar.g25
-rw-r--r--scss/src/grammar/grammar.py70
-rw-r--r--scss/tests/files/general/007-maps-example.css3
-rw-r--r--scss/tests/files/general/007-maps-example.scss24
-rw-r--r--scss/tests/functions/test_core.py8
-rw-r--r--scss/types.py32
8 files changed, 248 insertions, 47 deletions
diff --git a/scss/expression.py b/scss/expression.py
index 09d470d..802d35a 100644
--- a/scss/expression.py
+++ b/scss/expression.py
@@ -10,7 +10,7 @@ import six
import scss.config as config
from scss.cssdefs import COLOR_NAMES, is_builtin_css_function, _expr_glob_re, _interpolate_re, _variable_re
from scss.rule import Namespace
-from scss.types import BooleanValue, ColorValue, ListValue, Null, NumberValue, ParserValue, String, Undefined
+from scss.types import BooleanValue, ColorValue, ListValue, Map, Null, NumberValue, ParserValue, String, Undefined
from scss.util import dequote, normalize_var
################################################################################
@@ -335,6 +335,23 @@ class ListLiteral(Expression):
return ListValue(items, separator="," if self.comma else "")
+class MapLiteral(Expression):
+ def __init__(self, pairs):
+ self.pairs = pairs
+
+ def evaluate(self, calculator, divide=False):
+ # TODO unclear here whether the keys should be bare tokens or Literals;
+ # depends how the syntax works!
+ scss_pairs = []
+ for key, value in self.pairs:
+ scss_pairs.append((
+ String(key, quotes=None),
+ value.evaluate(calculator),
+ ))
+
+ return Map(scss_pairs)
+
+
class ArgspecLiteral(Expression):
"""Contains pairs of argument names and values, as parsed from a function
definition or function call.
@@ -424,8 +441,8 @@ class SassExpressionScanner(Scanner):
patterns = None
_patterns = [
('":"', ':'),
+ ('","', ','),
('[ \r\t\n]+', '[ \r\t\n]+'),
- ('COMMA', ','),
('LPAR', '\\(|\\['),
('RPAR', '\\)|\\]'),
('END', '$'),
@@ -449,8 +466,10 @@ class SassExpressionScanner(Scanner):
('UNITS', '(?<!\\s)(?:[a-zA-Z]+|%)(?![-\\w])'),
('NUM', '(?:\\d+(?:\\.\\d*)?|\\.\\d+)'),
('COLOR', '#(?:[a-fA-F0-9]{6}|[a-fA-F0-9]{3})(?![a-fA-F0-9])'),
+ ('KWVAR', '\\$[-a-zA-Z0-9_]+(?=\\s*:)'),
('VAR', '\\$[-a-zA-Z0-9_]+'),
('FNCT', '[-a-zA-Z_][-a-zA-Z0-9_]*(?=\\()'),
+ ('KWID', '[-a-zA-Z_][-a-zA-Z0-9_]*(?=\\s*:)'),
('ID', '[-a-zA-Z_][-a-zA-Z0-9_]*'),
('BANG_IMPORTANT', '!important'),
]
@@ -579,9 +598,15 @@ class SassExpression(Parser):
_token_ = self._peek(self.u_expr_chks)
if _token_ == 'LPAR':
LPAR = self._scan('LPAR')
- expr_lst = self.expr_lst()
+ _token_ = self._peek(self.atom_rsts)
+ if _token_ == 'KWID':
+ expr_map = self.expr_map()
+ v = expr_map
+ else: # in self.not_expr_rsts
+ expr_lst = self.expr_lst()
+ v = Parentheses(expr_lst)
RPAR = self._scan('RPAR')
- return Parentheses(expr_lst)
+ return v
elif _token_ == 'ID':
ID = self._scan('ID')
return Literal(parse_bareword(ID))
@@ -592,14 +617,14 @@ class SassExpression(Parser):
FNCT = self._scan('FNCT')
v = ArgspecLiteral([])
LPAR = self._scan('LPAR')
- if self._peek(self.atom_rsts) != 'RPAR':
+ if self._peek(self.atom_rsts_) != 'RPAR':
argspec = self.argspec()
v = argspec
RPAR = self._scan('RPAR')
return CallOp(FNCT, v)
elif _token_ == 'NUM':
NUM = self._scan('NUM')
- if self._peek(self.atom_rsts_) == 'UNITS':
+ if self._peek(self.atom_rsts__) == 'UNITS':
UNITS = self._scan('UNITS')
return Literal(NumberValue(float(NUM), unit=UNITS.lower()))
return Literal(NumberValue(float(NUM)))
@@ -616,11 +641,31 @@ class SassExpression(Parser):
VAR = self._scan('VAR')
return Variable(VAR)
+ def expr_map(self):
+ map_items = self.map_items()
+ return MapLiteral(map_items)
+
+ def map_items(self):
+ map_item = self.map_item()
+ pairs = [map_item]
+ if self._peek(self.map_items_rsts) == '","':
+ self._scan('","')
+ if self._peek(self.map_items_rsts_) == 'KWID':
+ map_items = self.map_items()
+ pairs.extend(map_items)
+ return pairs
+
+ def map_item(self):
+ KWID = self._scan('KWID')
+ self._scan('":"')
+ expr_slst = self.expr_slst()
+ return (KWID, expr_slst)
+
def argspec(self):
argspec_item = self.argspec_item()
v = [argspec_item]
- while self._peek(self.argspec_rsts) == 'COMMA':
- COMMA = self._scan('COMMA')
+ while self._peek(self.map_items_rsts) == '","':
+ self._scan('","')
argspec_item = self.argspec_item()
v.append(argspec_item)
return ArgspecLiteral(v)
@@ -639,8 +684,8 @@ class SassExpression(Parser):
def expr_lst(self):
expr_slst = self.expr_slst()
v = [expr_slst]
- while self._peek(self.expr_lst_rsts) == 'COMMA':
- COMMA = self._scan('COMMA')
+ while self._peek(self.expr_lst_rsts) == '","':
+ self._scan('","')
expr_slst = self.expr_slst()
v.append(expr_slst)
return ListLiteral(v) if len(v) > 1 else v[0]
@@ -654,23 +699,25 @@ class SassExpression(Parser):
return ListLiteral(v, comma=False) if len(v) > 1 else v[0]
m_expr_chks = set(['MUL', 'DIV'])
- comparison_rsts = set(['LPAR', 'QSTR', 'RPAR', 'BANG_IMPORTANT', 'LE', 'COLOR', 'NE', 'LT', 'NUM', 'COMMA', 'GT', 'END', 'SIGN', 'ADD', 'FNCT', 'STR', 'VAR', 'EQ', 'ID', 'AND', 'GE', 'NOT', 'OR'])
- atom_rsts = set(['LPAR', 'BANG_IMPORTANT', 'COLOR', 'QSTR', 'SIGN', 'NOT', 'ADD', 'NUM', 'FNCT', 'STR', 'VAR', 'RPAR', 'ID'])
+ map_items_rsts_ = set(['KWID', 'RPAR'])
+ comparison_rsts = set(['LPAR', 'QSTR', 'RPAR', 'BANG_IMPORTANT', 'LE', 'COLOR', 'NE', 'LT', 'NUM', 'GT', 'END', 'SIGN', 'ADD', 'FNCT', 'STR', 'VAR', 'EQ', 'ID', 'AND', 'GE', 'NOT', 'OR', '","'])
+ atom_rsts = set(['KWID', 'LPAR', 'COLOR', 'QSTR', 'SIGN', 'VAR', 'ADD', 'NUM', 'FNCT', 'STR', 'NOT', 'BANG_IMPORTANT', 'ID'])
+ atom_rsts__ = set(['LPAR', 'SUB', 'QSTR', 'RPAR', 'VAR', 'MUL', 'DIV', 'BANG_IMPORTANT', 'LE', 'COLOR', 'NE', 'LT', 'NUM', 'GT', 'END', 'SIGN', 'GE', 'FNCT', 'STR', 'UNITS', 'EQ', 'ID', 'AND', 'ADD', 'NOT', 'OR', '","'])
u_expr_chks = set(['LPAR', 'COLOR', 'QSTR', 'NUM', 'FNCT', 'STR', 'VAR', 'BANG_IMPORTANT', 'ID'])
- m_expr_rsts = set(['LPAR', 'SUB', 'QSTR', 'RPAR', 'MUL', 'DIV', 'BANG_IMPORTANT', 'LE', 'COLOR', 'NE', 'LT', 'NUM', 'COMMA', 'GT', 'END', 'SIGN', 'GE', 'FNCT', 'STR', 'VAR', 'EQ', 'ID', 'AND', 'ADD', 'NOT', 'OR'])
- argspec_item_rsts_ = set(['LPAR', 'COLOR', 'QSTR', 'SIGN', 'VAR', 'ADD', 'NUM', '":"', 'STR', 'NOT', 'BANG_IMPORTANT', 'ID', 'FNCT'])
- expr_lst_rsts = set(['END', 'COMMA', 'RPAR'])
- argspec_rsts = set(['COMMA', 'RPAR'])
- and_expr_rsts = set(['AND', 'LPAR', 'BANG_IMPORTANT', 'END', 'COLOR', 'QSTR', 'SIGN', 'VAR', 'ADD', 'NUM', 'COMMA', 'FNCT', 'STR', 'NOT', 'ID', 'RPAR', 'OR'])
+ m_expr_rsts = set(['LPAR', 'SUB', 'QSTR', 'RPAR', 'MUL', 'DIV', 'BANG_IMPORTANT', 'LE', 'COLOR', 'NE', 'LT', 'NUM', 'GT', 'END', 'SIGN', 'GE', 'FNCT', 'STR', 'VAR', 'EQ', 'ID', 'AND', 'ADD', 'NOT', 'OR', '","'])
+ map_items_rsts = set(['RPAR', '","'])
+ expr_lst_rsts = set(['RPAR', 'END', '","'])
+ and_expr_rsts = set(['AND', 'LPAR', 'END', 'COLOR', 'QSTR', 'SIGN', 'VAR', 'ADD', 'NUM', 'RPAR', 'FNCT', 'STR', 'NOT', 'ID', 'BANG_IMPORTANT', 'OR', '","'])
u_expr_rsts = set(['LPAR', 'COLOR', 'QSTR', 'SIGN', 'ADD', 'NUM', 'FNCT', 'STR', 'VAR', 'BANG_IMPORTANT', 'ID'])
- expr_rsts = set(['LPAR', 'BANG_IMPORTANT', 'END', 'COLOR', 'QSTR', 'RPAR', 'VAR', 'ADD', 'NUM', 'COMMA', 'FNCT', 'STR', 'NOT', 'ID', 'SIGN', 'OR'])
+ expr_rsts = set(['LPAR', 'END', 'COLOR', 'QSTR', 'SIGN', 'VAR', 'ADD', 'NUM', 'RPAR', 'FNCT', 'STR', 'NOT', 'ID', 'BANG_IMPORTANT', 'OR', '","'])
not_expr_rsts = set(['LPAR', 'COLOR', 'QSTR', 'SIGN', 'VAR', 'ADD', 'NUM', 'FNCT', 'STR', 'NOT', 'BANG_IMPORTANT', 'ID'])
argspec_item_rsts = set(['LPAR', 'COLOR', 'QSTR', 'SIGN', 'NOT', 'ADD', 'NUM', 'FNCT', 'STR', 'VAR', 'BANG_IMPORTANT', 'ID'])
- atom_rsts_ = set(['LPAR', 'SUB', 'QSTR', 'RPAR', 'VAR', 'MUL', 'DIV', 'BANG_IMPORTANT', 'LE', 'COLOR', 'NE', 'LT', 'NUM', 'COMMA', 'GT', 'END', 'SIGN', 'GE', 'FNCT', 'STR', 'UNITS', 'EQ', 'ID', 'AND', 'ADD', 'NOT', 'OR'])
+ atom_rsts_ = set(['LPAR', 'BANG_IMPORTANT', 'COLOR', 'QSTR', 'SIGN', 'NOT', 'ADD', 'NUM', 'FNCT', 'STR', 'VAR', 'RPAR', 'ID'])
comparison_chks = set(['GT', 'GE', 'NE', 'LT', 'LE', 'EQ'])
a_expr_chks = set(['ADD', 'SUB'])
- a_expr_rsts = set(['LPAR', 'SUB', 'QSTR', 'RPAR', 'BANG_IMPORTANT', 'LE', 'COLOR', 'NE', 'LT', 'NUM', 'COMMA', 'GT', 'END', 'SIGN', 'GE', 'FNCT', 'STR', 'VAR', 'EQ', 'ID', 'AND', 'ADD', 'NOT', 'OR'])
- expr_slst_rsts = set(['LPAR', 'BANG_IMPORTANT', 'END', 'COLOR', 'QSTR', 'RPAR', 'VAR', 'ADD', 'NUM', 'COMMA', 'FNCT', 'STR', 'NOT', 'SIGN', 'ID'])
+ a_expr_rsts = set(['LPAR', 'SUB', 'QSTR', 'RPAR', 'BANG_IMPORTANT', 'LE', 'COLOR', 'NE', 'LT', 'NUM', 'GT', 'END', 'SIGN', 'GE', 'FNCT', 'STR', 'VAR', 'EQ', 'ID', 'AND', 'ADD', 'NOT', 'OR', '","'])
+ argspec_item_rsts_ = set(['LPAR', 'COLOR', 'QSTR', 'SIGN', 'VAR', 'ADD', 'NUM', '":"', 'STR', 'NOT', 'BANG_IMPORTANT', 'ID', 'FNCT'])
+ expr_slst_rsts = set(['LPAR', 'END', 'COLOR', 'QSTR', 'SIGN', 'VAR', 'ADD', 'NUM', 'RPAR', 'FNCT', 'STR', 'NOT', 'BANG_IMPORTANT', 'ID', '","'])
### Grammar ends.
diff --git a/scss/functions/core.py b/scss/functions/core.py
index 8e1c1ae..96c87a5 100644
--- a/scss/functions/core.py
+++ b/scss/functions/core.py
@@ -435,6 +435,7 @@ CORE_LIBRARY.add(NumberValue.wrap_python_function(math.floor), 'floor', 1)
# ------------------------------------------------------------------------------
# List functions
+
def __parse_separator(separator, default_from=None):
if separator is None:
return None
@@ -536,6 +537,49 @@ def index(lst, val):
# ------------------------------------------------------------------------------
+# Map functions
+
+@register('map-get', 2)
+def map_get(map, key):
+ print(repr(map.index.keys()))
+ return map.index[key]
+
+
+@register('map-merge', 2)
+def map_merge(*maps):
+ pairs = []
+ index = {}
+ for map in maps:
+ for key, value in map.pairs:
+ if key in index:
+ continue
+
+ pairs.append((key, value))
+ index[key] = value
+
+ return Map(pairs)
+
+
+@register('map-keys', 1)
+def map_keys(map):
+ return List(
+ [k for (k, v) in map.pairs],
+ comma=True)
+
+
+@register('map-values', 1)
+def map_values(map):
+ return List(
+ [v for (k, v) in map.pairs],
+ comma=True)
+
+
+@register('map-has-key', 2)
+def map_values(map, key):
+ return BooleanValue(key in map.index)
+
+
+# ------------------------------------------------------------------------------
# Meta functions
@register('type-of', 1)
diff --git a/scss/src/grammar/grammar.g b/scss/src/grammar/grammar.g
index 00c55f3..4eac9ef 100644
--- a/scss/src/grammar/grammar.g
+++ b/scss/src/grammar/grammar.g
@@ -5,7 +5,6 @@
%%
parser SassExpression:
ignore: "[ \r\t\n]+"
- token COMMA: ","
token LPAR: "\\(|\\["
token RPAR: "\\)|\\]"
token END: "$"
@@ -29,8 +28,10 @@ parser SassExpression:
token UNITS: "(?<!\s)(?:[a-zA-Z]+|%)(?![-\w])"
token NUM: "(?:\d+(?:\.\d*)?|\.\d+)"
token COLOR: "#(?:[a-fA-F0-9]{6}|[a-fA-F0-9]{3})(?![a-fA-F0-9])"
+ token KWVAR: "\$[-a-zA-Z0-9_]+(?=\s*:)"
token VAR: "\$[-a-zA-Z0-9_]+"
token FNCT: "[-a-zA-Z_][-a-zA-Z0-9_]*(?=\()"
+ token KWID: "[-a-zA-Z_][-a-zA-Z0-9_]*(?=\s*:)"
token ID: "[-a-zA-Z_][-a-zA-Z0-9_]*"
token BANG_IMPORTANT: "!important"
@@ -76,7 +77,10 @@ parser SassExpression:
| ADD u_expr {{ return UnaryOp(operator.pos, u_expr) }}
| atom {{ return atom }}
- rule atom: LPAR expr_lst RPAR {{ return Parentheses(expr_lst) }}
+ rule atom: LPAR (
+ expr_map {{ v = expr_map }}
+ | expr_lst {{ v = Parentheses(expr_lst) }}
+ ) RPAR {{ return v }}
| ID {{ return Literal(parse_bareword(ID)) }}
| BANG_IMPORTANT {{ return Literal(String(BANG_IMPORTANT, quotes=None)) }}
| FNCT {{ v = ArgspecLiteral([]) }}
@@ -91,9 +95,22 @@ parser SassExpression:
| COLOR {{ return Literal(ColorValue(ParserValue(COLOR))) }}
| VAR {{ return Variable(VAR) }}
+
+ rule expr_map: map_items {{ return MapLiteral(map_items) }}
+
+ rule map_items: map_item {{ pairs = [map_item] }}
+ [
+ ","
+ [ map_items {{ pairs.extend(map_items) }}
+ ]
+ ] {{ return pairs }}
+
+ rule map_item: KWID ":" expr_slst {{ return (KWID, expr_slst) }}
+
+
rule argspec: argspec_item {{ v = [argspec_item] }}
(
- COMMA
+ ","
argspec_item {{ v.append(argspec_item) }}
)* {{ return ArgspecLiteral(v) }}
@@ -107,7 +124,7 @@ parser SassExpression:
rule expr_lst: expr_slst {{ v = [expr_slst] }}
(
- COMMA
+ ","
expr_slst {{ v.append(expr_slst) }}
)* {{ return ListLiteral(v) if len(v) > 1 else v[0] }}
diff --git a/scss/src/grammar/grammar.py b/scss/src/grammar/grammar.py
index 4435465..d78b65f 100644
--- a/scss/src/grammar/grammar.py
+++ b/scss/src/grammar/grammar.py
@@ -12,8 +12,8 @@ class SassExpressionScanner(Scanner):
patterns = None
_patterns = [
('":"', ':'),
+ ('","', ','),
('[ \r\t\n]+', '[ \r\t\n]+'),
- ('COMMA', ','),
('LPAR', '\\(|\\['),
('RPAR', '\\)|\\]'),
('END', '$'),
@@ -37,8 +37,10 @@ class SassExpressionScanner(Scanner):
('UNITS', '(?<!\\s)(?:[a-zA-Z]+|%)(?![-\\w])'),
('NUM', '(?:\\d+(?:\\.\\d*)?|\\.\\d+)'),
('COLOR', '#(?:[a-fA-F0-9]{6}|[a-fA-F0-9]{3})(?![a-fA-F0-9])'),
+ ('KWVAR', '\\$[-a-zA-Z0-9_]+(?=\\s*:)'),
('VAR', '\\$[-a-zA-Z0-9_]+'),
('FNCT', '[-a-zA-Z_][-a-zA-Z0-9_]*(?=\\()'),
+ ('KWID', '[-a-zA-Z_][-a-zA-Z0-9_]*(?=\\s*:)'),
('ID', '[-a-zA-Z_][-a-zA-Z0-9_]*'),
('BANG_IMPORTANT', '!important'),
]
@@ -167,9 +169,15 @@ class SassExpression(Parser):
_token_ = self._peek(self.u_expr_chks)
if _token_ == 'LPAR':
LPAR = self._scan('LPAR')
- expr_lst = self.expr_lst()
+ _token_ = self._peek(self.atom_rsts)
+ if _token_ == 'KWID':
+ expr_map = self.expr_map()
+ v = expr_map
+ else: # in self.not_expr_rsts
+ expr_lst = self.expr_lst()
+ v = Parentheses(expr_lst)
RPAR = self._scan('RPAR')
- return Parentheses(expr_lst)
+ return v
elif _token_ == 'ID':
ID = self._scan('ID')
return Literal(parse_bareword(ID))
@@ -180,14 +188,14 @@ class SassExpression(Parser):
FNCT = self._scan('FNCT')
v = ArgspecLiteral([])
LPAR = self._scan('LPAR')
- if self._peek(self.atom_rsts) != 'RPAR':
+ if self._peek(self.atom_rsts_) != 'RPAR':
argspec = self.argspec()
v = argspec
RPAR = self._scan('RPAR')
return CallOp(FNCT, v)
elif _token_ == 'NUM':
NUM = self._scan('NUM')
- if self._peek(self.atom_rsts_) == 'UNITS':
+ if self._peek(self.atom_rsts__) == 'UNITS':
UNITS = self._scan('UNITS')
return Literal(NumberValue(float(NUM), unit=UNITS.lower()))
return Literal(NumberValue(float(NUM)))
@@ -204,11 +212,31 @@ class SassExpression(Parser):
VAR = self._scan('VAR')
return Variable(VAR)
+ def expr_map(self):
+ map_items = self.map_items()
+ return MapLiteral(map_items)
+
+ def map_items(self):
+ map_item = self.map_item()
+ pairs = [map_item]
+ if self._peek(self.map_items_rsts) == '","':
+ self._scan('","')
+ if self._peek(self.map_items_rsts_) == 'KWID':
+ map_items = self.map_items()
+ pairs.extend(map_items)
+ return pairs
+
+ def map_item(self):
+ KWID = self._scan('KWID')
+ self._scan('":"')
+ expr_slst = self.expr_slst()
+ return (KWID, expr_slst)
+
def argspec(self):
argspec_item = self.argspec_item()
v = [argspec_item]
- while self._peek(self.argspec_rsts) == 'COMMA':
- COMMA = self._scan('COMMA')
+ while self._peek(self.map_items_rsts) == '","':
+ self._scan('","')
argspec_item = self.argspec_item()
v.append(argspec_item)
return ArgspecLiteral(v)
@@ -227,8 +255,8 @@ class SassExpression(Parser):
def expr_lst(self):
expr_slst = self.expr_slst()
v = [expr_slst]
- while self._peek(self.expr_lst_rsts) == 'COMMA':
- COMMA = self._scan('COMMA')
+ while self._peek(self.expr_lst_rsts) == '","':
+ self._scan('","')
expr_slst = self.expr_slst()
v.append(expr_slst)
return ListLiteral(v) if len(v) > 1 else v[0]
@@ -242,23 +270,25 @@ class SassExpression(Parser):
return ListLiteral(v, comma=False) if len(v) > 1 else v[0]
m_expr_chks = set(['MUL', 'DIV'])
- comparison_rsts = set(['LPAR', 'QSTR', 'RPAR', 'BANG_IMPORTANT', 'LE', 'COLOR', 'NE', 'LT', 'NUM', 'COMMA', 'GT', 'END', 'SIGN', 'ADD', 'FNCT', 'STR', 'VAR', 'EQ', 'ID', 'AND', 'GE', 'NOT', 'OR'])
- atom_rsts = set(['LPAR', 'BANG_IMPORTANT', 'COLOR', 'QSTR', 'SIGN', 'NOT', 'ADD', 'NUM', 'FNCT', 'STR', 'VAR', 'RPAR', 'ID'])
+ map_items_rsts_ = set(['KWID', 'RPAR'])
+ comparison_rsts = set(['LPAR', 'QSTR', 'RPAR', 'BANG_IMPORTANT', 'LE', 'COLOR', 'NE', 'LT', 'NUM', 'GT', 'END', 'SIGN', 'ADD', 'FNCT', 'STR', 'VAR', 'EQ', 'ID', 'AND', 'GE', 'NOT', 'OR', '","'])
+ atom_rsts = set(['KWID', 'LPAR', 'COLOR', 'QSTR', 'SIGN', 'VAR', 'ADD', 'NUM', 'FNCT', 'STR', 'NOT', 'BANG_IMPORTANT', 'ID'])
+ atom_rsts__ = set(['LPAR', 'SUB', 'QSTR', 'RPAR', 'VAR', 'MUL', 'DIV', 'BANG_IMPORTANT', 'LE', 'COLOR', 'NE', 'LT', 'NUM', 'GT', 'END', 'SIGN', 'GE', 'FNCT', 'STR', 'UNITS', 'EQ', 'ID', 'AND', 'ADD', 'NOT', 'OR', '","'])
u_expr_chks = set(['LPAR', 'COLOR', 'QSTR', 'NUM', 'FNCT', 'STR', 'VAR', 'BANG_IMPORTANT', 'ID'])
- m_expr_rsts = set(['LPAR', 'SUB', 'QSTR', 'RPAR', 'MUL', 'DIV', 'BANG_IMPORTANT', 'LE', 'COLOR', 'NE', 'LT', 'NUM', 'COMMA', 'GT', 'END', 'SIGN', 'GE', 'FNCT', 'STR', 'VAR', 'EQ', 'ID', 'AND', 'ADD', 'NOT', 'OR'])
- argspec_item_rsts_ = set(['LPAR', 'COLOR', 'QSTR', 'SIGN', 'VAR', 'ADD', 'NUM', '":"', 'STR', 'NOT', 'BANG_IMPORTANT', 'ID', 'FNCT'])
- expr_lst_rsts = set(['END', 'COMMA', 'RPAR'])
- argspec_rsts = set(['COMMA', 'RPAR'])
- and_expr_rsts = set(['AND', 'LPAR', 'BANG_IMPORTANT', 'END', 'COLOR', 'QSTR', 'SIGN', 'VAR', 'ADD', 'NUM', 'COMMA', 'FNCT', 'STR', 'NOT', 'ID', 'RPAR', 'OR'])
+ m_expr_rsts = set(['LPAR', 'SUB', 'QSTR', 'RPAR', 'MUL', 'DIV', 'BANG_IMPORTANT', 'LE', 'COLOR', 'NE', 'LT', 'NUM', 'GT', 'END', 'SIGN', 'GE', 'FNCT', 'STR', 'VAR', 'EQ', 'ID', 'AND', 'ADD', 'NOT', 'OR', '","'])
+ map_items_rsts = set(['RPAR', '","'])
+ expr_lst_rsts = set(['RPAR', 'END', '","'])
+ and_expr_rsts = set(['AND', 'LPAR', 'END', 'COLOR', 'QSTR', 'SIGN', 'VAR', 'ADD', 'NUM', 'RPAR', 'FNCT', 'STR', 'NOT', 'ID', 'BANG_IMPORTANT', 'OR', '","'])
u_expr_rsts = set(['LPAR', 'COLOR', 'QSTR', 'SIGN', 'ADD', 'NUM', 'FNCT', 'STR', 'VAR', 'BANG_IMPORTANT', 'ID'])
- expr_rsts = set(['LPAR', 'BANG_IMPORTANT', 'END', 'COLOR', 'QSTR', 'RPAR', 'VAR', 'ADD', 'NUM', 'COMMA', 'FNCT', 'STR', 'NOT', 'ID', 'SIGN', 'OR'])
+ expr_rsts = set(['LPAR', 'END', 'COLOR', 'QSTR', 'SIGN', 'VAR', 'ADD', 'NUM', 'RPAR', 'FNCT', 'STR', 'NOT', 'ID', 'BANG_IMPORTANT', 'OR', '","'])
not_expr_rsts = set(['LPAR', 'COLOR', 'QSTR', 'SIGN', 'VAR', 'ADD', 'NUM', 'FNCT', 'STR', 'NOT', 'BANG_IMPORTANT', 'ID'])
argspec_item_rsts = set(['LPAR', 'COLOR', 'QSTR', 'SIGN', 'NOT', 'ADD', 'NUM', 'FNCT', 'STR', 'VAR', 'BANG_IMPORTANT', 'ID'])
- atom_rsts_ = set(['LPAR', 'SUB', 'QSTR', 'RPAR', 'VAR', 'MUL', 'DIV', 'BANG_IMPORTANT', 'LE', 'COLOR', 'NE', 'LT', 'NUM', 'COMMA', 'GT', 'END', 'SIGN', 'GE', 'FNCT', 'STR', 'UNITS', 'EQ', 'ID', 'AND', 'ADD', 'NOT', 'OR'])
+ atom_rsts_ = set(['LPAR', 'BANG_IMPORTANT', 'COLOR', 'QSTR', 'SIGN', 'NOT', 'ADD', 'NUM', 'FNCT', 'STR', 'VAR', 'RPAR', 'ID'])
comparison_chks = set(['GT', 'GE', 'NE', 'LT', 'LE', 'EQ'])
a_expr_chks = set(['ADD', 'SUB'])
- a_expr_rsts = set(['LPAR', 'SUB', 'QSTR', 'RPAR', 'BANG_IMPORTANT', 'LE', 'COLOR', 'NE', 'LT', 'NUM', 'COMMA', 'GT', 'END', 'SIGN', 'GE', 'FNCT', 'STR', 'VAR', 'EQ', 'ID', 'AND', 'ADD', 'NOT', 'OR'])
- expr_slst_rsts = set(['LPAR', 'BANG_IMPORTANT', 'END', 'COLOR', 'QSTR', 'RPAR', 'VAR', 'ADD', 'NUM', 'COMMA', 'FNCT', 'STR', 'NOT', 'SIGN', 'ID'])
+ a_expr_rsts = set(['LPAR', 'SUB', 'QSTR', 'RPAR', 'BANG_IMPORTANT', 'LE', 'COLOR', 'NE', 'LT', 'NUM', 'GT', 'END', 'SIGN', 'GE', 'FNCT', 'STR', 'VAR', 'EQ', 'ID', 'AND', 'ADD', 'NOT', 'OR', '","'])
+ argspec_item_rsts_ = set(['LPAR', 'COLOR', 'QSTR', 'SIGN', 'VAR', 'ADD', 'NUM', '":"', 'STR', 'NOT', 'BANG_IMPORTANT', 'ID', 'FNCT'])
+ expr_slst_rsts = set(['LPAR', 'END', 'COLOR', 'QSTR', 'SIGN', 'VAR', 'ADD', 'NUM', 'RPAR', 'FNCT', 'STR', 'NOT', 'BANG_IMPORTANT', 'ID', '","'])
### Grammar ends.
diff --git a/scss/tests/files/general/007-maps-example.css b/scss/tests/files/general/007-maps-example.css
new file mode 100644
index 0000000..fea3c4d
--- /dev/null
+++ b/scss/tests/files/general/007-maps-example.css
@@ -0,0 +1,3 @@
+div h1 {
+ color: #F4FAC7;
+}
diff --git a/scss/tests/files/general/007-maps-example.scss b/scss/tests/files/general/007-maps-example.scss
new file mode 100644
index 0000000..5dfce63
--- /dev/null
+++ b/scss/tests/files/general/007-maps-example.scss
@@ -0,0 +1,24 @@
+// Taken from the Ruby documentation
+$themes: (
+ mist: (
+ header: #DCFAC0,
+ text: #00968B,
+ border: #85C79C
+ ),
+ spring: (
+ header: #F4FAC7,
+ text: #C2454E,
+ border: #FFB158
+ ),
+ // ...
+);
+
+@mixin themed-header($theme-name) {
+ h1 {
+ color: map-get(map-get($themes, $theme-name), header);
+ }
+}
+
+div {
+ @include themed-header(spring);
+}
diff --git a/scss/tests/functions/test_core.py b/scss/tests/functions/test_core.py
index ce74da6..c19f873 100644
--- a/scss/tests/functions/test_core.py
+++ b/scss/tests/functions/test_core.py
@@ -298,6 +298,14 @@ def test_index(calc):
# ------------------------------------------------------------------------------
+# Map functions
+
+
+# ...
+
+
+
+# ------------------------------------------------------------------------------
# Introspection functions
def test_type_of(calc):
diff --git a/scss/types.py b/scss/types.py
index d2537a5..afe65f4 100644
--- a/scss/types.py
+++ b/scss/types.py
@@ -680,8 +680,6 @@ class Color(Value):
class String(Value):
- sass_type_name = u'string'
-
"""Represents both CSS quoted string values and CSS identifiers (such as
`left`).
@@ -690,6 +688,8 @@ class String(Value):
Otherwise, double quotes are used.
"""
+ sass_type_name = u'string'
+
def __init__(self, value, quotes='"'):
if isinstance(value, String):
# TODO unclear if this should be here, but many functions rely on
@@ -758,6 +758,34 @@ class String(Value):
return self.__str__()
+### XXX EXPERIMENTAL XXX
+class Map(Value):
+ sass_type_name = u'map'
+
+ def __init__(self, pairs):
+ self.pairs = pairs
+ self.index = {}
+ for key, value in pairs:
+ self.index[key] = value
+
+ def __hash__(self):
+ return hash(self.pairs)
+
+ def __len__(self):
+ return len(self.pairs)
+
+ def __iter__(self):
+ return iter(self.pairs)
+
+ def get_by_key(self, key):
+ return self.index[key]
+
+ def get_by_pos(self, key):
+ return self.pairs[key][1]
+
+ def render(self, compress=False):
+ raise TypeError("maps cannot be rendered as CSS")
+
# Backwards-compatibility.
ColorValue = Color
ListValue = List