Mixin guards
This commit is contained in:
parent
26ffa97770
commit
b15674d56b
10
README
10
README
|
@ -76,12 +76,14 @@ Supported features
|
|||
* Mixins
|
||||
* mixins (Nested)
|
||||
* mixins (Nested (Calls))
|
||||
* Guard expressions
|
||||
* Parametered mixins (class)
|
||||
* Parametered mixins (id)
|
||||
* @arguments
|
||||
* Nesting
|
||||
* Escapes ~/e()
|
||||
* Expressions
|
||||
* Keyframe blocks
|
||||
* Color functions:
|
||||
** lighten
|
||||
** darken
|
||||
|
@ -98,6 +100,12 @@ Supported features
|
|||
** decrement
|
||||
** format '%('
|
||||
** add
|
||||
** iscolor
|
||||
** isnumber
|
||||
** isurl
|
||||
** isstring
|
||||
** iskeyword
|
||||
* Keyframe blocks
|
||||
|
||||
Differences from lessc.js
|
||||
=========================
|
||||
|
@ -107,10 +115,8 @@ Differences from lessc.js
|
|||
|
||||
Not supported (yet)
|
||||
===================
|
||||
* Keyframe blocks
|
||||
* mixins (closures)
|
||||
* Pattern-matching
|
||||
* Guard expressions
|
||||
* JavaScript evaluation
|
||||
|
||||
License
|
||||
|
|
|
@ -190,12 +190,13 @@ class LessParser(object):
|
|||
# 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 m: break
|
||||
if res: break
|
||||
p[0] = res
|
||||
else:
|
||||
self.handle_error('Call unknown block `%s`' % m.raw(True), p.lineno(2))
|
||||
|
@ -244,11 +245,6 @@ class LessParser(object):
|
|||
"""
|
||||
p[0] = p[2]
|
||||
|
||||
def p_mixin_guard_not(self, p):
|
||||
""" mixin_guard : less_when less_not mixin_guard_cond_list
|
||||
"""
|
||||
p[0] = p[3]
|
||||
|
||||
def p_mixin_guard_cond_list_aux(self, p):
|
||||
""" mixin_guard_cond_list : mixin_guard_cond_list ',' mixin_guard_cond
|
||||
| mixin_guard_cond_list less_and mixin_guard_cond
|
||||
|
@ -261,6 +257,12 @@ class LessParser(object):
|
|||
""" mixin_guard_cond_list : mixin_guard_cond
|
||||
"""
|
||||
p[0] = [p[1]]
|
||||
|
||||
def p_mixin_guard_cond_rev(self, p):
|
||||
""" mixin_guard_cond : less_not t_popen argument mixin_guard_cmp argument t_pclose
|
||||
| less_not t_popen argument t_pclose
|
||||
"""
|
||||
p[0] = utility.reverse_guard(list(p)[3:-1])
|
||||
|
||||
def p_mixin_guard_cond(self, p):
|
||||
""" mixin_guard_cond : t_popen argument mixin_guard_cmp argument t_pclose
|
||||
|
@ -284,6 +286,7 @@ class LessParser(object):
|
|||
"""
|
||||
p[1].parse(None)
|
||||
mixin = self.scope.mixins(p[1].raw())
|
||||
res = None
|
||||
if mixin:
|
||||
for m in mixin:
|
||||
try:
|
||||
|
@ -302,8 +305,11 @@ class LessParser(object):
|
|||
else:
|
||||
if self.scope.in_mixin:
|
||||
res = Deferred(p[1], p[3])
|
||||
if not res:
|
||||
self.handle_error('Call unknown mixin `%s`' % p[1].raw(True), p.lineno(2))
|
||||
if res is False:
|
||||
args = ''.join([''.join(a) for a in p[3]]) if p[3] else ''
|
||||
self.handle_error('Call unknown mixin `%s(%s)`' %
|
||||
(p[1].raw(True), args),
|
||||
p.lineno(2))
|
||||
p[0] = res
|
||||
|
||||
def p_mixin_args_arguments(self, p):
|
||||
|
|
|
@ -53,6 +53,19 @@ def blocksearch(block, name):
|
|||
if b: return b
|
||||
return False
|
||||
|
||||
def reverse_guard(ll):
|
||||
"""
|
||||
"""
|
||||
rev = {
|
||||
'<': '>',
|
||||
'>': '<',
|
||||
'=': '!=',
|
||||
'!=': '=',
|
||||
'>=': '<=',
|
||||
'<=': '>='
|
||||
}
|
||||
return [rev[l] if l in rev else l for l in ll]
|
||||
|
||||
def debug_print(ll, lvl=0):
|
||||
"""
|
||||
"""
|
||||
|
|
|
@ -0,0 +1,151 @@
|
|||
"""
|
||||
"""
|
||||
lessColors = {
|
||||
'aliceblue':'#f0f8ff',
|
||||
'antiquewhite':'#faebd7',
|
||||
'aqua':'#00ffff',
|
||||
'aquamarine':'#7fffd4',
|
||||
'azure':'#f0ffff',
|
||||
'beige':'#f5f5dc',
|
||||
'bisque':'#ffe4c4',
|
||||
'black':'#000000',
|
||||
'blanchedalmond':'#ffebcd',
|
||||
'blue':'#0000ff',
|
||||
'blueviolet':'#8a2be2',
|
||||
'brown':'#a52a2a',
|
||||
'burlywood':'#deb887',
|
||||
'cadetblue':'#5f9ea0',
|
||||
'chartreuse':'#7fff00',
|
||||
'chocolate':'#d2691e',
|
||||
'coral':'#ff7f50',
|
||||
'cornflowerblue':'#6495ed',
|
||||
'cornsilk':'#fff8dc',
|
||||
'crimson':'#dc143c',
|
||||
'cyan':'#00ffff',
|
||||
'darkblue':'#00008b',
|
||||
'darkcyan':'#008b8b',
|
||||
'darkgoldenrod':'#b8860b',
|
||||
'darkgray':'#a9a9a9',
|
||||
'darkgrey':'#a9a9a9',
|
||||
'darkgreen':'#006400',
|
||||
'darkkhaki':'#bdb76b',
|
||||
'darkmagenta':'#8b008b',
|
||||
'darkolivegreen':'#556b2f',
|
||||
'darkorange':'#ff8c00',
|
||||
'darkorchid':'#9932cc',
|
||||
'darkred':'#8b0000',
|
||||
'darksalmon':'#e9967a',
|
||||
'darkseagreen':'#8fbc8f',
|
||||
'darkslateblue':'#483d8b',
|
||||
'darkslategray':'#2f4f4f',
|
||||
'darkslategrey':'#2f4f4f',
|
||||
'darkturquoise':'#00ced1',
|
||||
'darkviolet':'#9400d3',
|
||||
'deeppink':'#ff1493',
|
||||
'deepskyblue':'#00bfff',
|
||||
'dimgray':'#696969',
|
||||
'dimgrey':'#696969',
|
||||
'dodgerblue':'#1e90ff',
|
||||
'firebrick':'#b22222',
|
||||
'floralwhite':'#fffaf0',
|
||||
'forestgreen':'#228b22',
|
||||
'fuchsia':'#ff00ff',
|
||||
'gainsboro':'#dcdcdc',
|
||||
'ghostwhite':'#f8f8ff',
|
||||
'gold':'#ffd700',
|
||||
'goldenrod':'#daa520',
|
||||
'gray':'#808080',
|
||||
'grey':'#808080',
|
||||
'green':'#008000',
|
||||
'greenyellow':'#adff2f',
|
||||
'honeydew':'#f0fff0',
|
||||
'hotpink':'#ff69b4',
|
||||
'indianred':'#cd5c5c',
|
||||
'indigo':'#4b0082',
|
||||
'ivory':'#fffff0',
|
||||
'khaki':'#f0e68c',
|
||||
'lavender':'#e6e6fa',
|
||||
'lavenderblush':'#fff0f5',
|
||||
'lawngreen':'#7cfc00',
|
||||
'lemonchiffon':'#fffacd',
|
||||
'lightblue':'#add8e6',
|
||||
'lightcoral':'#f08080',
|
||||
'lightcyan':'#e0ffff',
|
||||
'lightgoldenrodyellow':'#fafad2',
|
||||
'lightgray':'#d3d3d3',
|
||||
'lightgrey':'#d3d3d3',
|
||||
'lightgreen':'#90ee90',
|
||||
'lightpink':'#ffb6c1',
|
||||
'lightsalmon':'#ffa07a',
|
||||
'lightseagreen':'#20b2aa',
|
||||
'lightskyblue':'#87cefa',
|
||||
'lightslategray':'#778899',
|
||||
'lightslategrey':'#778899',
|
||||
'lightsteelblue':'#b0c4de',
|
||||
'lightyellow':'#ffffe0',
|
||||
'lime':'#00ff00',
|
||||
'limegreen':'#32cd32',
|
||||
'linen':'#faf0e6',
|
||||
'magenta':'#ff00ff',
|
||||
'maroon':'#800000',
|
||||
'mediumaquamarine':'#66cdaa',
|
||||
'mediumblue':'#0000cd',
|
||||
'mediumorchid':'#ba55d3',
|
||||
'mediumpurple':'#9370d8',
|
||||
'mediumseagreen':'#3cb371',
|
||||
'mediumslateblue':'#7b68ee',
|
||||
'mediumspringgreen':'#00fa9a',
|
||||
'mediumturquoise':'#48d1cc',
|
||||
'mediumvioletred':'#c71585',
|
||||
'midnightblue':'#191970',
|
||||
'mintcream':'#f5fffa',
|
||||
'mistyrose':'#ffe4e1',
|
||||
'moccasin':'#ffe4b5',
|
||||
'navajowhite':'#ffdead',
|
||||
'navy':'#000080',
|
||||
'oldlace':'#fdf5e6',
|
||||
'olive':'#808000',
|
||||
'olivedrab':'#6b8e23',
|
||||
'orange':'#ffa500',
|
||||
'orangered':'#ff4500',
|
||||
'orchid':'#da70d6',
|
||||
'palegoldenrod':'#eee8aa',
|
||||
'palegreen':'#98fb98',
|
||||
'paleturquoise':'#afeeee',
|
||||
'palevioletred':'#d87093',
|
||||
'papayawhip':'#ffefd5',
|
||||
'peachpuff':'#ffdab9',
|
||||
'peru':'#cd853f',
|
||||
'pink':'#ffc0cb',
|
||||
'plum':'#dda0dd',
|
||||
'powderblue':'#b0e0e6',
|
||||
'purple':'#800080',
|
||||
'red':'#ff0000',
|
||||
'rosybrown':'#bc8f8f',
|
||||
'royalblue':'#4169e1',
|
||||
'saddlebrown':'#8b4513',
|
||||
'salmon':'#fa8072',
|
||||
'sandybrown':'#f4a460',
|
||||
'seagreen':'#2e8b57',
|
||||
'seashell':'#fff5ee',
|
||||
'sienna':'#a0522d',
|
||||
'silver':'#c0c0c0',
|
||||
'skyblue':'#87ceeb',
|
||||
'slateblue':'#6a5acd',
|
||||
'slategray':'#708090',
|
||||
'slategrey':'#708090',
|
||||
'snow':'#fffafa',
|
||||
'springgreen':'#00ff7f',
|
||||
'steelblue':'#4682b4',
|
||||
'tan':'#d2b48c',
|
||||
'teal':'#008080',
|
||||
'thistle':'#d8bfd8',
|
||||
'tomato':'#ff6347',
|
||||
'turquoise':'#40e0d0',
|
||||
'violet':'#ee82ee',
|
||||
'wheat':'#f5deb3',
|
||||
'white':'#ffffff',
|
||||
'whitesmoke':'#f5f5f5',
|
||||
'yellow':'#ffff00',
|
||||
'yellowgreen':'#9acd32'
|
||||
}
|
|
@ -2,10 +2,11 @@
|
|||
Calls to builtin functions.
|
||||
"""
|
||||
import re, math
|
||||
from urllib.parse import quote as urlquote
|
||||
from urllib.parse import quote as urlquote, urlparse
|
||||
from .node import Node
|
||||
import lesscpy.lessc.utility as utility
|
||||
import lesscpy.lessc.color as Color
|
||||
from lesscpy.lib.colors import lessColors
|
||||
|
||||
class Call(Node):
|
||||
def parse(self, scope):
|
||||
|
@ -64,6 +65,53 @@ class Call(Node):
|
|||
format = format.replace('%A', '%s')
|
||||
return format % tuple(items)
|
||||
|
||||
def isnumber(self, *args):
|
||||
"""
|
||||
"""
|
||||
if(len(args) > 1):
|
||||
raise SyntaxError('Wrong number of arguments')
|
||||
try:
|
||||
n, u = utility.analyze_number(args[0])
|
||||
except SyntaxError as e:
|
||||
return False
|
||||
return True
|
||||
|
||||
def iscolor(self, *args):
|
||||
"""
|
||||
"""
|
||||
if(len(args) > 1):
|
||||
raise SyntaxError('Wrong number of arguments')
|
||||
return (args[0] in lessColors)
|
||||
|
||||
def isurl(self, *args):
|
||||
"""
|
||||
"""
|
||||
if(len(args) > 1):
|
||||
raise SyntaxError('Wrong number of arguments')
|
||||
arg = utility.destring(args[0])
|
||||
regex = re.compile(r'^(?:http|ftp)s?://' # http:// or https://
|
||||
r'(?:(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+(?:[A-Z]{2,6}\.?|[A-Z0-9-]{2,}\.?)|' #domain...
|
||||
r'localhost|' #localhost...
|
||||
r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})' # ...or ip
|
||||
r'(?::\d+)?' # optional port
|
||||
r'(?:/?|[/?]\S+)$', re.IGNORECASE)
|
||||
return regex.match(arg)
|
||||
|
||||
def isstring(self, *args):
|
||||
"""
|
||||
"""
|
||||
if(len(args) > 1):
|
||||
raise SyntaxError('Wrong number of arguments')
|
||||
regex = re.compile(r'\'[^\']*\'|"[^"]*"')
|
||||
return regex.match(args[0])
|
||||
|
||||
def iskeyword(self, *args):
|
||||
"""
|
||||
"""
|
||||
if(len(args) > 1):
|
||||
raise SyntaxError('Wrong number of arguments')
|
||||
return (args[0] in ('when', 'and', 'not'))
|
||||
|
||||
def increment(self, *args):
|
||||
""" Increment function
|
||||
@param Mixed: value
|
||||
|
|
|
@ -37,7 +37,7 @@ class Mixin(Node):
|
|||
for var in vars:
|
||||
if var: scope.add_variable(var)
|
||||
if not arguments:
|
||||
arguments = [v.value for v in vars]
|
||||
arguments = [v.value for v in vars if v]
|
||||
if arguments:
|
||||
arguments = [' ' if a == ',' else a for a in arguments]
|
||||
else:
|
||||
|
@ -73,29 +73,37 @@ class Mixin(Node):
|
|||
return None
|
||||
return var
|
||||
|
||||
def parse_guards(self, scope, args):
|
||||
def parse_guards(self, scope):
|
||||
"""
|
||||
Parse guards on mixin.
|
||||
"""
|
||||
if self.guards:
|
||||
cor = True if ',' in self.guards else False
|
||||
for g in self.guards:
|
||||
if type(g) is list:
|
||||
e = Expression(g)
|
||||
if not e.parse(scope):
|
||||
res = (g[0].parse(scope)
|
||||
if len(g) == 1
|
||||
else Expression(g).parse(scope))
|
||||
if cor:
|
||||
if res: return True
|
||||
elif not res:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def call(self, scope, args=None):
|
||||
"""
|
||||
Call mixin. Parses a copy of the mixins body
|
||||
in the current scope and returns it.
|
||||
"""
|
||||
self.parse_args(args, scope)
|
||||
if self.parse_guards(scope, args):
|
||||
try:
|
||||
self.parse_args(args, scope)
|
||||
except SyntaxError:
|
||||
return False
|
||||
if self.parse_guards(scope):
|
||||
body = copy.deepcopy(self.body)
|
||||
scope.update([self.scope], -1)
|
||||
body.parse(scope)
|
||||
r = list(utility.flatten([body.parsed, body.inner]))
|
||||
utility.rename(r, scope)
|
||||
return r
|
||||
return False
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
.a {
|
||||
width: 1px;
|
||||
height: -1px;
|
||||
padding: 0;
|
||||
padding: 0;
|
||||
padding: 0;
|
||||
}
|
||||
.b {
|
||||
margin: 11;
|
||||
margin: -11;
|
||||
}
|
||||
.c {
|
||||
height: 5px;
|
||||
width: 5px;
|
||||
}
|
||||
.d {
|
||||
src: 'mobile';
|
||||
padding: 1px;
|
||||
}
|
||||
.e {
|
||||
width: 9px;
|
||||
color: yellow;
|
||||
src: 'http://www.lesscss.org/#-parametric-mixins';
|
||||
filter: 'wtf';
|
||||
ignore: when;
|
||||
}
|
||||
.f {
|
||||
width: 9px;
|
||||
height: -9px;
|
||||
}
|
||||
.a .span5 {
|
||||
width: 5px;
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
.a{width:1px;height:-1px;padding:0;padding:0;padding:0;}
|
||||
.b{margin:11;margin:-11;}
|
||||
.c{height:5px;width:5px;}
|
||||
.d{src:'mobile';padding:1px;}
|
||||
.e{width:9px;color:yellow;src:'http://www.lesscss.org/#-parametric-mixins';filter:'wtf';ignore:when;}
|
||||
.f{width:9px;height:-9px;}
|
||||
.a .span5{width:5px;}
|
|
@ -0,0 +1,111 @@
|
|||
/*
|
||||
Guards
|
||||
*/
|
||||
.inner(@index) {
|
||||
width: @index * 1px;
|
||||
}
|
||||
.sign (@index) when (@index > 0) {
|
||||
.inner(@index);
|
||||
}
|
||||
.sign (@index) when (@index < 0) {
|
||||
height: @index;
|
||||
}
|
||||
.sign (@index: 0) when (@index = 0) {
|
||||
padding: @index;
|
||||
}
|
||||
.a {
|
||||
.sign(1px);
|
||||
.sign(-1px);
|
||||
.sign(0);
|
||||
// No arg
|
||||
.sign();
|
||||
.sign;
|
||||
}
|
||||
// condition list
|
||||
.mixin1 (@a) when (@a > 10), (@a < -10) {
|
||||
margin: @a;
|
||||
}
|
||||
.b{
|
||||
.mixin1(11);
|
||||
.mixin1(-11);
|
||||
}
|
||||
.max (@a, @b) when (@a > @b) {
|
||||
width: @a
|
||||
}
|
||||
.max (@a, @b) when (@a < @b) {
|
||||
height: @b
|
||||
}
|
||||
.c {
|
||||
.max(3px, 5px);
|
||||
.max(5px, 3px);
|
||||
}
|
||||
@media1: mobile;
|
||||
.mmixin (@a) when (@media1 = mobile) {
|
||||
src: 'mobile';
|
||||
padding: @a;
|
||||
}
|
||||
.mmixin (@a) when (@media1 = desktop) {
|
||||
src: 'desktop';
|
||||
padding: @a * 3px;
|
||||
}
|
||||
.d {
|
||||
.mmixin(1px);
|
||||
}
|
||||
/*
|
||||
isType functions
|
||||
*/
|
||||
.mixtype (@a, @b: 0) when (isnumber(@b)) {
|
||||
width: @b;
|
||||
}
|
||||
.mixtype (@a, @b: black) when (iscolor(@b)) {
|
||||
color: @b;
|
||||
}
|
||||
.mixtype (@a, @b: 'http://www.lesscss.org') when (isurl(@b)) {
|
||||
src: @b;
|
||||
}
|
||||
.mixtype (@a, @b: 'somestr') when (isstring(@b)) {
|
||||
filter: @b;
|
||||
}
|
||||
.mixtype (@a, @b: 'somestr') when (iskeyword(@b)) {
|
||||
ignore: @b;
|
||||
}
|
||||
.e {
|
||||
.mixtype (6px, 9px);
|
||||
.mixtype (6px, yellow);
|
||||
.mixtype (6px, 'http://www.lesscss.org/#-parametric-mixins');
|
||||
.mixtype (6px, 'wtf');
|
||||
.mixtype (6px, when);
|
||||
}
|
||||
/*
|
||||
Mixed conditions
|
||||
*/
|
||||
.mixcond (@a) when (isnumber(@a)) and (@a > 0) {
|
||||
width: @a;
|
||||
}
|
||||
/*
|
||||
.mixcond (@a) when (isnumber(@a)) and not (@a != 0) {
|
||||
margin: @a;
|
||||
}
|
||||
*/
|
||||
.mixcond (@b) when not (@b > 0) {
|
||||
height: @b;
|
||||
}
|
||||
.f {
|
||||
.mixcond(9px);
|
||||
// .mixcond(0);
|
||||
.mixcond(-9px);
|
||||
}
|
||||
/*
|
||||
Bootstrap example
|
||||
*/
|
||||
.span(@index) {
|
||||
width: @index * 1px;
|
||||
}
|
||||
.spanX (@index) when (@index > 0) {
|
||||
(~".span@{index}") {
|
||||
.span(@index);
|
||||
}
|
||||
}
|
||||
.a {
|
||||
.spanX(5);
|
||||
}
|
Loading…
Reference in New Issue