This commit is contained in:
jtm
2012-04-08 13:17:22 +00:00
parent 0a30f4a0e3
commit 64b496093c
6 changed files with 41 additions and 102 deletions

View File

@@ -81,30 +81,16 @@ class LessParser(object):
self.scope.push()
self.target = filename
self.result = self.parser.parse(filename, lexer=self.lex, debug=debuglevel)
self.post_parse(self.result)
self.post_parse()
def post_parse(self, lst):
"""Post process stage. less.js seems to allow calls to mixins not
yet declared. Deferreds are used in place of real mixins. After
parsing we go over the parse tree and reparse deferred's.
args:
lst (list): Parse list
def post_parse(self):
"""
if type(lst) is list:
for u in lst: self.post_parse(u)
elif type(lst) is Block:
"""
for pu in self.result:
try:
lst.parsed = list(utility.flatten([t.parse(self.scope, self.verbose)
if type(t) is Deferred else t
for t in lst.parsed]))
pu.parse(self.scope)
except SyntaxError as e:
self.handle_error(e, 0, 'W')
self.post_parse(lst.parsed)
elif type(lst) is Deferred:
try:
lst = lst.parse(self.scope, True)
except SyntaxError as e:
self.handle_error(e, lst.lineno, 'W')
self.handle_error(e, 0)
def scopemap(self):
""" Output scopemap.
@@ -195,16 +181,9 @@ class LessParser(object):
def p_block(self, p):
""" block_decl : block_open declaration_list brace_close
"""
try:
block = Block(list(p)[1:-1], p.lineno(3))
if not self.scope.in_mixin:
block.parse(self.scope)
p[0] = block
except SyntaxError as e:
self.handle_error(e, p.lineno(3))
p[0] = None
p[0] = Block(list(p)[1:-1], p.lineno(3))
self.scope.pop()
self.scope.add_block(block)
self.scope.add_block(p[0])
def p_block_replace(self, p):
""" block_decl : identifier ';'
@@ -215,18 +194,7 @@ class LessParser(object):
p[0] = block.copy(self.scope)
else:
# fallback to mixin. Allow calls to mixins without parens
mixin = self.scope.mixins(m.raw())
if mixin:
res = None
for m in mixin:
try:
res = m.call(self.scope)
except SyntaxError as e:
self.handle_error(e, p.lineno(2))
if res: break
p[0] = res
else:
self.handle_error('Call unknown block `%s`' % m.raw(True), p.lineno(2))
p[0] = Deferred(p[1], None, p.lineno(2))
def p_block_open(self, p):
""" block_open : identifier brace_open
@@ -242,11 +210,11 @@ class LessParser(object):
""" block_open : css_font_face t_ws brace_open
"""
p[0] = Identifier([p[1], p[2]]).parse(self.scope)
#
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#
def p_mixin(self, p):
""" mixin_decl : open_mixin declaration_list brace_close
"""
@@ -311,34 +279,8 @@ class LessParser(object):
def p_call_mixin(self, p):
""" call_mixin : identifier t_popen mixin_args_list t_pclose ';'
"""
# Try with scope first
p[1].parse(self.scope)
mixin = self.scope.mixins(p[1].raw())
if not mixin:
p[1].parse(None)
mixin = self.scope.mixins(p[1].raw())
res = False
if mixin:
for m in mixin:
try:
if self.scope.in_mixin:
res = Deferred(m, p[3], p.lineno(4))
else:
res = m.call(self.scope, p[3])
if res: break
except SyntaxError as e:
self.handle_error(e, p.lineno(4))
elif not p[3] or not p[3][0]:
# fallback to block. Allow calls of name() to blocks
block = self.scope.blocks(p[1].raw())
if block:
res = block.copy(self.scope)
else:
if self.scope.in_mixin:
res = Deferred(p[1], p[3], p.lineno(4))
if res is False:
res = Deferred(p[1], p[3], p.lineno(4))
p[0] = res
p[1].parse(None)
p[0] = Deferred(p[1], p[3], p.lineno(4))
def p_mixin_args_arguments(self, p):
""" mixin_args_list : less_arguments
@@ -418,13 +360,7 @@ class LessParser(object):
def p_variable_decl(self, p):
""" variable_decl : variable ':' style_list ';'
"""
try:
v = Variable(list(p)[1:], p.lineno(4))
v.parse(self.scope)
self.scope.add_variable(v)
except SyntaxError as e:
self.handle_error(e, p.lineno(2))
p[0] = None
p[0] = Variable(list(p)[1:-1], p.lineno(4))
#
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@@ -64,7 +64,7 @@ class Scope(list):
Args:
mixin (Mixin): Mixin object
"""
raw = mixin.name.raw()
raw = mixin.tokens[0][0].raw()
if raw in self._mixins:
self._mixins[raw].append(mixin)
else:

View File

@@ -29,12 +29,13 @@ class Block(Node):
self
"""
if not self.parsed:
scope.push()
self.name, inner = self.tokens
if not inner: inner = []
self.parsed = [p.parse(scope)
for p in inner
if p and type(p) is not type(self)]
self.parsed = list(utility.flatten(self.parsed))
self.parsed = [p for p in utility.flatten(self.parsed) if p]
if not inner:
self.inner = []
else:
@@ -42,6 +43,7 @@ class Block(Node):
if p and type(p) is type(self)]
if self.inner:
self.inner = [p.parse(scope) for p in self.inner]
scope.pop()
return self
def raw(self, clean=False):
@@ -72,7 +74,7 @@ class Block(Node):
'proplist': ''.join([p.fmt(fills) for p in self.parsed if p]),
})
out.append(f % fills)
if self.inner:
if hasattr(self, 'inner'):
if self.name.subparse: # @media
inner = ''.join([p.fmt(fills) for p in self.inner])
inner = inner.replace(fills['nl'],

View File

@@ -19,8 +19,7 @@ class Deferred(Node):
mixin (Mixin): Mixin object
args (list): Call arguments
"""
self.mixin = mixin
self.args = args
self.tokens = [mixin, args]
self.lineno = lineno
def parse(self, scope, error=False):
@@ -30,17 +29,18 @@ class Deferred(Node):
returns:
mixed
"""
if hasattr(self.mixin, 'call'):
return self.mixin.call(scope, self.args)
mixins = scope.mixins(self.mixin.raw())
mixin, args = self.tokens
if hasattr(mixin, 'call'):
return mixin.call(scope, args)
mixins = scope.mixins(mixin.raw())
if mixins:
for mixin in mixins:
res = mixin.call(scope, self.args)
res = mixin.call(scope, args)
if res: return res
else:
res = self
if error:
raise SyntaxError('NameError `%s`' % self.mixin.raw(True))
raise SyntaxError('NameError `%s`' % mixin.raw(True))
return res
def fmt(self, fills):

View File

@@ -28,7 +28,7 @@ class Mixin(Node):
self
"""
self.name, args, self.guards = self.tokens[0]
self.args = list(utility.flatten(args)) if args else []
self.args = [a for a in utility.flatten(args) if a]
self.body = Block([None, self.tokens[1]], 0)
self.scope = copy.deepcopy(scope[-1])
return self
@@ -52,22 +52,19 @@ class Mixin(Node):
"""
arguments = zip(args, [' '] * len(args)) if args and args[0] else None
if self.args:
parsed = [v.parse(scope)
if hasattr(v, 'parse') else v
parsed = [v if hasattr(v, 'parse') else v
for v in copy.deepcopy(self.args)]
args = args if type(args) is list else [args]
vars = [self._parse_arg(var, arg, scope)
for arg, var in itertools.zip_longest([a for a in args],
parsed)]
for var in vars:
if var: scope.add_variable(var)
for var in vars:
if var: var.parse(scope)
if not arguments:
arguments = [v.value for v in vars if v]
if not arguments:
arguments = ''
scope.add_variable(Variable(['@arguments',
None,
arguments]).parse(scope))
Variable(['@arguments', None, arguments]).parse(scope)
def _parse_arg(self, var, arg, scope):
"""
@@ -78,9 +75,10 @@ class Mixin(Node):
if utility.is_variable(arg[0]):
tmp = scope.variables(arg[0])
if not tmp: return None
var.value = tmp.value
val = tmp.value
else:
var.value = arg
val = arg
var = Variable(var.tokens[:-1] + [val])
else:
#arg
if utility.is_variable(var):
@@ -89,9 +87,10 @@ class Mixin(Node):
elif utility.is_variable(arg[0]):
tmp = scope.variables(arg[0])
if not tmp: return None
var = Variable([var, None, tmp.value]).parse(scope)
val = tmp.value
else:
var = Variable([var, None, arg]).parse(scope)
val = arg
var = Variable([var, None, arg])
else:
return None
return var

View File

@@ -17,13 +17,15 @@ class Variable(Node):
returns:
self
"""
self.name = self.tokens.pop(0)
self.value = self.tokens[1]
self.name, _, self.value = self.tokens
if type(self.name) is tuple:
if len(self.name) > 1:
self.name, pad = self.name
self.value.append(pad)
else:
self.name = self.name[0]
return self
scope.add_variable(self)
def fmt(self, fills):
return ''