diff --git a/lesscpy/lessc/lexer.py b/lesscpy/lessc/lexer.py index 6baf388..f9ea364 100644 --- a/lesscpy/lessc/lexer.py +++ b/lesscpy/lessc/lexer.py @@ -67,13 +67,7 @@ class LessLexer: 'css_vendor_property', 'css_ident', 'css_number', - '>', '&', - '*', - '+', - '-', - '/', - '~', ] significant_ws += list(set(reserved.values())) diff --git a/lesscpy/lessc/parser.py b/lesscpy/lessc/parser.py index 820e21b..50358c1 100644 --- a/lesscpy/lessc/parser.py +++ b/lesscpy/lessc/parser.py @@ -179,7 +179,7 @@ class LessParser(object): """ block_decl : block_open declaration_list brace_close """ try: - block = Block(list(p)[1:-1]) + block = Block(list(p)[1:-1], p.lineno(3)) if not self.scope.in_mixin(): block.parse(self.scope) self.scope.add_block(block) @@ -187,6 +187,7 @@ class LessParser(object): except SyntaxError as e: self.handle_error(e, p) p[0] = None + self.scope.pop() def p_block_replace(self, p): """ block_decl : identifier ';' @@ -224,6 +225,7 @@ class LessParser(object): """ mixin_decl : open_mixin declaration_list brace_close """ p[0] = None + self.scope.pop() def p_open_mixin(self, p): """ open_mixin : class t_popen mixin_args t_pclose brace_open @@ -287,7 +289,7 @@ class LessParser(object): """ variable_decl : variable ':' style_list ';' """ try: - v = Variable(list(p)[1:]) + v = Variable(list(p)[1:], p.lineno(4)) v.parse(self.scope) if self.scope.in_mixin(): self.stash[v.name] = v @@ -307,10 +309,11 @@ class LessParser(object): | prop_open empty ';' | prop_open less_arguments ';' """ - p[0] = Property(list(p)[1:-1]) + l = len(p) + p[0] = Property(list(p)[1:-1], p.lineno(l-1)) def p_prop_open_ie_hack(self, p): - """ prop_open : oper_mul prop_open + """ prop_open : '*' prop_open """ p[0] = (p[1][0], p[2][0]) @@ -354,7 +357,7 @@ class LessParser(object): def p_identifier(self, p): """ identifier : identifier_list """ - p[0] = Identifier(p[1]) + p[0] = Identifier(p[1], 0) def p_identifier_list_aux(self, p): """ identifier_list : identifier_list ',' identifier_group @@ -370,7 +373,7 @@ class LessParser(object): def p_identifier_group_op(self, p): """ identifier_group : identifier_group child_selector ident_parts - | identifier_group oper_add ident_parts + | identifier_group '+' ident_parts | identifier_group general_sibling_selector ident_parts """ p[1].extend([p[2]]) @@ -402,8 +405,8 @@ class LessParser(object): def p_selector(self, p): """ selector : combinator - | oper_mul - | oper_add + | '*' + | '+' | child_selector | general_sibling_selector """ @@ -453,7 +456,7 @@ class LessParser(object): | '~' istring | '~' css_string """ - p[0] = Call(list(p)[1:]) + p[0] = Call(list(p)[1:], 0) # # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -488,16 +491,16 @@ class LessParser(object): # def p_expression_aux(self, p): - """ expression : expression oper_add expression - | expression oper_sub expression - | expression oper_mul expression - | expression oper_div expression - | word oper_div expression + """ expression : expression '+' expression + | expression '-' expression + | expression '/' expression + | expression '*' expression + | word '/' expression """ - p[0] = Expression(list(p)[1:]) + p[0] = Expression(list(p)[1:], 0) def p_expression_p_neg(self, p): - """ expression : oper_sub t_popen expression t_pclose + """ expression : '-' t_popen expression t_pclose """ p[0] = [p[1], p[3]] @@ -529,7 +532,7 @@ class LessParser(object): p[0] = String(p) def p_variable_neg(self, p): - """ variable : oper_sub variable + """ variable : '-' variable """ p[0] = '-' + p[2] @@ -597,29 +600,6 @@ class LessParser(object): """ p[0] = tuple(list(p)[1:]) - def p_oper_add(self, p): - """ oper_add : '+' t_ws - | '+' - """ - p[0] = tuple(list(p)[1:]) - - def p_oper_sub(self, p): - """ oper_sub : '-' t_ws - | '-' - """ - p[0] = tuple(list(p)[1:]) - - def p_oper_mul(self, p): - """ oper_mul : '*' t_ws - | '*' - """ - p[0] = tuple(list(p)[1:]) - - def p_oper_div(self, p): - """ oper_div : '/' t_ws - | '/' - """ - p[0] = tuple(list(p)[1:]) def p_child_selector(self, p): """ child_selector : '>' t_ws @@ -642,7 +622,6 @@ class LessParser(object): def p_scope_close(self, p): """ brace_close : '}' """ - self.scope.pop() p[0] = p[1] def p_empty(self, p): diff --git a/lesscpy/lessc/scope.py b/lesscpy/lessc/scope.py index b353113..990eb15 100644 --- a/lesscpy/lessc/scope.py +++ b/lesscpy/lessc/scope.py @@ -1,5 +1,7 @@ """ """ +from . import utility + class Scope(list): def __init__(self, init=False): super().__init__() @@ -86,4 +88,15 @@ class Scope(list): self[0]['__variables__'].update(scope[0]['__variables__']) self[0]['__blocks__'].extend(scope[0]['__blocks__']) self[0]['__names__'].extend(scope[0]['__names__']) + + def swap(self, name): + """ + """ + if name.startswith('@@'): + var = self.variables(name[1:]) + if var is False: raise SyntaxError('Unknown variable %s' % name) + name = '@' + utility.destring(var.value[0]) + var = self.variables(name) + if var is False: raise SyntaxError('Unknown variable %s' % name) + return var.value \ No newline at end of file diff --git a/lesscpy/plib/call.py b/lesscpy/plib/call.py index 434b340..54b3de8 100644 --- a/lesscpy/plib/call.py +++ b/lesscpy/plib/call.py @@ -4,14 +4,5 @@ from .node import Node import lesscpy.lessc.utility as utility class Call(Node): def parse(self, scope): - self.parsed = [p.parse(scope) - if hasattr(p, 'parse') - else p - for p in utility.flatten(self.tokens)] - return self - - def format(self, fills): - return ''.join([p.format(fills) - if hasattr(p, 'format') - else p - for p in self.parsed]) \ No newline at end of file + parsed = self.process(self.tokens, scope) + return ''.join([p for p in parsed]) diff --git a/lesscpy/plib/expression.py b/lesscpy/plib/expression.py index 3d44885..fd1b6d4 100644 --- a/lesscpy/plib/expression.py +++ b/lesscpy/plib/expression.py @@ -10,14 +10,13 @@ class Expression(Node): @param list: current scope """ assert(len(self.tokens) == 3) - expr = [t.parse(scope) if hasattr(t, 'parse') - else t - for t in self.tokens] + expr = self.process(self.tokens, scope) expr = [self.neg(t, scope) for t in expr] A, O, B = [e[0] if type(e) is tuple else e - for e in expr] + for e in expr + if e != ' '] try: a, ua = utility.analyze_number(A, 'Illegal element in expression') b, ub = utility.analyze_number(B, 'Illegal element in expression') @@ -68,7 +67,7 @@ class Expression(Node): '-': '__sub__', '*': '__mul__', '/': '__truediv__' - }.get(o[0]) + }.get(o) v = getattr(a, operation)(b) if v is NotImplemented: v = getattr(b, operation)(a) diff --git a/lesscpy/plib/identifier.py b/lesscpy/plib/identifier.py index 0678464..2c75b40 100644 --- a/lesscpy/plib/identifier.py +++ b/lesscpy/plib/identifier.py @@ -10,7 +10,10 @@ class Identifier(Node): if scope: scopename.extend(scope.scopename) scopename = ''.join(scopename) - name = ''.join(utility.flatten(self.tokens)) + name = ''.join([t + ' ' + if t in '*>~+' + else t + for t in utility.flatten(self.tokens)]) if name.startswith('&'): scopename = scopename.strip() name = name[1:] diff --git a/lesscpy/plib/node.py b/lesscpy/plib/node.py index 6a5f395..ea7c704 100644 --- a/lesscpy/plib/node.py +++ b/lesscpy/plib/node.py @@ -1,11 +1,39 @@ """ """ +from lesscpy.lessc import utility class Node(object): - def __init__(self, p): + def __init__(self, p, ln): self.tokens = p + self.lineno = ln def parse(self, scope): return self + + def process(self, tokens, scope): + """ + """ + while True: + tokens = list(utility.flatten(tokens)) + done = True + if any(t for t in tokens if hasattr(t, 'parse')): + tokens = [t.parse(scope) + if hasattr(t, 'parse') + else t + for t in tokens] + done = False + if any(t for t in tokens if utility.is_variable(t)): + tokens = self.replace_variables(tokens, scope) + done = False + if done: break + return tokens + + def replace_variables(self, tokens, scope): + """ + """ + return [scope.swap(t) + if utility.is_variable(t) + else t + for t in tokens] def format(self, fills): - return str(type(self)) + raise ValueError('No defined format') diff --git a/lesscpy/plib/property.py b/lesscpy/plib/property.py index 9318e63..cd29144 100644 --- a/lesscpy/plib/property.py +++ b/lesscpy/plib/property.py @@ -15,12 +15,14 @@ class Property(Node): self.property = ''.join(property) self.parsed = [] if style: - style = self.preprocess(style) - self.parsed = [p.parse(scope) - if hasattr(p, 'parse') - else p - for p in utility.flatten(style)] - self.parsed = [p for p in self.parsed if p] + self.parsed = self.process(style, scope) +# style = self.replace_variables(style, scope) +# style = self.preprocess(style) +# self.parsed = [p.parse(scope) +# if hasattr(p, 'parse') +# else p +# for p in style] +# self.parsed = [p for p in self.parsed if p] return self def preprocess(self, style): diff --git a/lesscpy/plib/variable.py b/lesscpy/plib/variable.py index 903016c..cd50ec6 100644 --- a/lesscpy/plib/variable.py +++ b/lesscpy/plib/variable.py @@ -5,4 +5,6 @@ class Variable(Node): def parse(self, scope): """ """ - self.name = self.tokens.pop(0) \ No newline at end of file + self.name = self.tokens.pop(0) + self.value = self.tokens[1] + diff --git a/lesscpy/test/less/variables.less b/lesscpy/test/less/variables.less index e69de29..ae785bb 100644 --- a/lesscpy/test/less/variables.less +++ b/lesscpy/test/less/variables.less @@ -0,0 +1,59 @@ +@a: 2; +@x: @a * @a; +@y: @x + 1; +@z: @x * 2 + @y; + + +.variables { + width: @z + 1cm; // 14cm +} + +@b: @a * 10; +@c: #888; + +@fonts: "Trebuchet MS", Verdana, sans-serif; +@f: @fonts; + +@quotes: "~" "~"; +@q: @quotes; + +.variables { + height: @b + @x + 0px; // 24px + color: @c; + font-family: @f; + quotes: @q; +} + +.redefinition { + @var: 4; + @var: 2; + @var: 3; + three: @var; +} + +.values { + @a: 'Trebuchet'; + @multi: 'A', B, C; + font-family: @a, @a, @a; + color: @c !important; + url: url(@a); + multi: something @multi, @a; +} + +.variable-names { + @var: 'hello'; + @name: 'var'; + name: @@name; +} + +.alpha { + @var: 42; + filter: alpha(opacity=@var); +} + +@lazy: @j; +@j: 100%; + +.lazy-eval { + width: @lazy; +} \ No newline at end of file