2012-01-28 14:52:09 +00:00
|
|
|
"""
|
2012-02-19 20:38:19 +00:00
|
|
|
.. module:: parser
|
|
|
|
:synopsis: Lesscss parser.
|
2012-01-28 14:52:09 +00:00
|
|
|
|
|
|
|
http://www.dabeaz.com/ply/ply.html
|
|
|
|
http://www.w3.org/TR/CSS21/grammar.html#scanner
|
|
|
|
http://lesscss.org/#docs
|
|
|
|
|
|
|
|
Copyright (c)
|
|
|
|
See LICENSE for details.
|
2012-02-19 20:38:19 +00:00
|
|
|
.. moduleauthor:: Jóhann T. Maríusson <jtm@robot.is>
|
2012-01-28 14:52:09 +00:00
|
|
|
"""
|
|
|
|
import os
|
2012-02-25 18:28:05 +00:00
|
|
|
import copy
|
2012-01-28 14:52:09 +00:00
|
|
|
import ply.yacc
|
|
|
|
from . import lexer
|
|
|
|
from . import utility
|
2012-02-12 11:50:57 +00:00
|
|
|
from .scope import Scope
|
2012-03-10 16:27:14 +00:00
|
|
|
from .color import Color
|
2012-01-28 14:52:09 +00:00
|
|
|
from lesscpy.plib import *
|
2012-03-18 10:08:36 +00:00
|
|
|
|
2012-01-28 14:52:09 +00:00
|
|
|
class LessParser(object):
|
|
|
|
precedence = (
|
|
|
|
('left', '+', '-'),
|
|
|
|
('left', '*', '/'),
|
|
|
|
)
|
|
|
|
def __init__(self,
|
|
|
|
lex_optimize=True,
|
|
|
|
yacc_optimize=True,
|
2012-02-19 20:38:19 +00:00
|
|
|
tabfile='yacctab',
|
2012-01-28 14:52:09 +00:00
|
|
|
yacc_debug=False,
|
|
|
|
scope=None,
|
|
|
|
outputdir='/tmp',
|
2012-01-30 12:56:33 +00:00
|
|
|
importlvl=0,
|
|
|
|
verbose=False):
|
2012-01-28 14:52:09 +00:00
|
|
|
""" Parser object
|
2012-02-19 20:38:19 +00:00
|
|
|
|
|
|
|
Kwargs:
|
|
|
|
lex_optimize (bool): Optimize lexer
|
|
|
|
yacc_optimize (bool): Optimize parser
|
|
|
|
tabfile (str): Yacc tab filename
|
|
|
|
yacc_debug (bool): yacc debug mode
|
|
|
|
scope (Scope): Inherited scope
|
|
|
|
outputdir (str): Output (debugging)
|
|
|
|
importlvl (int): Import depth
|
|
|
|
verbose (bool): Verbose mode
|
2012-01-28 14:52:09 +00:00
|
|
|
"""
|
2012-01-30 12:56:33 +00:00
|
|
|
self.verbose = verbose
|
2012-01-28 14:52:09 +00:00
|
|
|
self.importlvl = importlvl
|
|
|
|
self.lex = lexer.LessLexer()
|
2012-02-19 20:38:19 +00:00
|
|
|
if not tabfile:
|
|
|
|
tabfile = 'yacctab'
|
2012-01-28 14:52:09 +00:00
|
|
|
|
2012-02-19 20:38:19 +00:00
|
|
|
self.ignored = ('css_comment', 'less_comment',
|
2012-03-16 15:08:45 +00:00
|
|
|
'css_vendor_hack', 'css_keyframes')
|
2012-01-28 14:52:09 +00:00
|
|
|
|
|
|
|
self.tokens = [t for t in self.lex.tokens
|
|
|
|
if t not in self.ignored]
|
|
|
|
self.parser = ply.yacc.yacc(
|
|
|
|
module=self,
|
2012-02-19 20:38:19 +00:00
|
|
|
start='tunit',
|
2012-01-28 14:52:09 +00:00
|
|
|
debug=yacc_debug,
|
|
|
|
optimize=yacc_optimize,
|
2012-02-19 20:38:19 +00:00
|
|
|
tabmodule=tabfile,
|
2012-01-28 14:52:09 +00:00
|
|
|
outputdir=outputdir
|
|
|
|
)
|
2012-02-12 11:50:57 +00:00
|
|
|
self.scope = scope if scope else Scope()
|
2012-01-28 14:52:09 +00:00
|
|
|
self.stash = {}
|
|
|
|
self.result = None
|
|
|
|
self.target = None
|
|
|
|
|
|
|
|
def parse(self, filename='', debuglevel=0):
|
|
|
|
""" Parse file.
|
|
|
|
@param string: Filename
|
|
|
|
@param int: Debuglevel
|
|
|
|
"""
|
2012-02-26 13:29:22 +00:00
|
|
|
if self.verbose: print('Compiling target: %s' % filename)
|
2012-02-12 11:50:57 +00:00
|
|
|
self.scope.push()
|
2012-01-28 14:52:09 +00:00
|
|
|
self.target = filename
|
|
|
|
self.result = self.parser.parse(filename, lexer=self.lex, debug=debuglevel)
|
|
|
|
|
|
|
|
def scopemap(self):
|
|
|
|
""" Output scopemap.
|
|
|
|
"""
|
2012-03-18 10:08:36 +00:00
|
|
|
utility.debug_print(self.result)
|
2012-02-19 20:38:19 +00:00
|
|
|
|
|
|
|
#
|
|
|
|
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
#
|
|
|
|
|
|
|
|
def p_tunit(self, p):
|
|
|
|
""" tunit : unit_list
|
2012-01-28 14:52:09 +00:00
|
|
|
"""
|
2012-03-18 19:05:53 +00:00
|
|
|
p[0] = [u for u in p[1] if u]
|
2012-01-28 14:52:09 +00:00
|
|
|
|
2012-02-19 20:38:19 +00:00
|
|
|
def p_unit_list(self, p):
|
|
|
|
""" unit_list : unit_list unit
|
|
|
|
| unit
|
2012-01-28 14:52:09 +00:00
|
|
|
"""
|
2012-03-18 19:05:53 +00:00
|
|
|
if type(p[1]) is list:
|
|
|
|
if len(p) >= 3:
|
|
|
|
if type(p[2]) is list:
|
|
|
|
p[1].extend(p[2])
|
|
|
|
else:
|
|
|
|
p[1].append(p[2])
|
2012-02-25 17:08:08 +00:00
|
|
|
else:
|
|
|
|
p[1] = [p[1]]
|
2012-01-28 14:52:09 +00:00
|
|
|
p[0] = p[1]
|
|
|
|
|
2012-02-19 20:38:19 +00:00
|
|
|
def p_unit(self, p):
|
|
|
|
""" unit : statement
|
|
|
|
| variable_decl
|
|
|
|
| block_decl
|
|
|
|
| mixin_decl
|
2012-03-18 18:56:23 +00:00
|
|
|
| call_mixin
|
2012-01-28 14:52:09 +00:00
|
|
|
"""
|
2012-02-25 17:08:08 +00:00
|
|
|
p[0] = p[1]
|
2012-01-28 14:52:09 +00:00
|
|
|
|
2012-02-19 20:38:19 +00:00
|
|
|
#
|
|
|
|
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
#
|
2012-01-28 14:52:09 +00:00
|
|
|
|
|
|
|
def p_statement_aux(self, p):
|
2012-02-19 20:38:19 +00:00
|
|
|
""" statement : css_charset t_ws css_string ';'
|
|
|
|
| css_namespace t_ws css_string ';'
|
2012-01-28 14:52:09 +00:00
|
|
|
"""
|
2012-02-26 16:36:13 +00:00
|
|
|
p[0] = Statement(list(p)[1:], p.lineno(1))
|
2012-01-28 14:52:09 +00:00
|
|
|
p[0].parse(None)
|
|
|
|
|
|
|
|
def p_statement_namespace(self, p):
|
2012-02-19 20:38:19 +00:00
|
|
|
""" statement : css_namespace t_ws word css_string ';'
|
2012-01-28 14:52:09 +00:00
|
|
|
"""
|
2012-02-26 16:36:13 +00:00
|
|
|
p[0] = Statement(list(p)[1:], p.lineno(1))
|
2012-01-28 14:52:09 +00:00
|
|
|
p[0].parse(None)
|
|
|
|
|
|
|
|
def p_statement_import(self, p):
|
2012-02-19 20:38:19 +00:00
|
|
|
""" statement : css_import t_ws css_string ';'
|
2012-02-26 13:20:35 +00:00
|
|
|
| css_import t_ws css_string dom ';'
|
2012-01-28 14:52:09 +00:00
|
|
|
"""
|
|
|
|
if self.importlvl > 8:
|
|
|
|
raise ImportError('Recrusive import level too deep > 8 (circular import ?)')
|
2012-02-19 20:38:19 +00:00
|
|
|
ipath = utility.destring(p[3])
|
2012-01-28 14:52:09 +00:00
|
|
|
fn, fe = os.path.splitext(ipath)
|
|
|
|
if not fe or fe.lower() == '.less':
|
|
|
|
try:
|
|
|
|
cpath = os.path.dirname(os.path.abspath(self.target))
|
|
|
|
if not fe: ipath += '.less'
|
|
|
|
filename = "%s%s%s" % (cpath, os.sep, ipath)
|
|
|
|
if os.path.exists(filename):
|
2012-03-18 19:11:12 +00:00
|
|
|
recurse = LessParser(importlvl=self.importlvl+1,
|
|
|
|
verbose=self.verbose, scope=self.scope)
|
2012-01-28 14:52:09 +00:00
|
|
|
recurse.parse(filename=filename, debuglevel=0)
|
|
|
|
else:
|
|
|
|
err = "Cannot import '%s', file not found" % filename
|
2012-03-13 20:26:35 +00:00
|
|
|
self.handle_error(err, p.lineno(1), 'W')
|
2012-01-28 14:52:09 +00:00
|
|
|
p[0] = None
|
|
|
|
except ImportError as e:
|
|
|
|
self.handle_error(e, p)
|
|
|
|
else:
|
2012-02-26 16:36:13 +00:00
|
|
|
p[0] = Statement(list(p)[1:], p.lineno(1))
|
2012-01-28 14:52:09 +00:00
|
|
|
p[0].parse(None)
|
2012-02-19 20:38:19 +00:00
|
|
|
|
2012-01-28 14:52:09 +00:00
|
|
|
#
|
|
|
|
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
2012-02-19 20:38:19 +00:00
|
|
|
#
|
2012-01-28 14:52:09 +00:00
|
|
|
|
2012-02-19 20:38:19 +00:00
|
|
|
def p_block(self, p):
|
|
|
|
""" block_decl : block_open declaration_list brace_close
|
2012-01-28 14:52:09 +00:00
|
|
|
"""
|
|
|
|
try:
|
2012-02-26 15:50:07 +00:00
|
|
|
block = Block(list(p)[1:-1], p.lineno(3))
|
2012-03-18 10:08:36 +00:00
|
|
|
if not self.scope.in_mixin:
|
|
|
|
block.parse(self.scope)
|
2012-01-28 14:52:09 +00:00
|
|
|
p[0] = block
|
|
|
|
except SyntaxError as e:
|
2012-03-06 20:23:27 +00:00
|
|
|
self.handle_error(e, p.lineno(3))
|
2012-01-28 14:52:09 +00:00
|
|
|
p[0] = None
|
2012-02-26 15:50:07 +00:00
|
|
|
self.scope.pop()
|
2012-03-01 16:23:22 +00:00
|
|
|
self.scope.add_block(block)
|
2012-01-28 14:52:09 +00:00
|
|
|
|
2012-02-19 20:38:19 +00:00
|
|
|
def p_block_replace(self, p):
|
|
|
|
""" block_decl : identifier ';'
|
2012-01-28 14:52:09 +00:00
|
|
|
"""
|
2012-02-25 18:28:05 +00:00
|
|
|
m = p[1].parse(None)
|
2012-03-01 16:23:22 +00:00
|
|
|
block = self.scope.blocks(m.raw())
|
2012-02-25 18:28:05 +00:00
|
|
|
if block:
|
2012-03-02 20:15:18 +00:00
|
|
|
p[0] = block.copy(self.scope)
|
2012-02-25 18:28:05 +00:00
|
|
|
else:
|
2012-03-18 14:21:58 +00:00
|
|
|
# fallback to mixin. Allow calls to mixins without parens
|
2012-03-03 19:58:56 +00:00
|
|
|
mixin = self.scope.mixins(m.raw())
|
|
|
|
if mixin:
|
|
|
|
try:
|
|
|
|
p[0] = mixin.call(self.scope)
|
|
|
|
except SyntaxError as e:
|
|
|
|
self.handle_error(e, p.lineno(2))
|
|
|
|
else:
|
2012-03-18 13:12:21 +00:00
|
|
|
self.handle_error('Call unknown block `%s`' % m.raw(True), p.lineno(2))
|
2012-01-28 14:52:09 +00:00
|
|
|
|
2012-02-19 20:38:19 +00:00
|
|
|
def p_block_open(self, p):
|
|
|
|
""" block_open : identifier brace_open
|
2012-01-28 14:52:09 +00:00
|
|
|
"""
|
2012-02-28 21:26:08 +00:00
|
|
|
p[1].parse(self.scope)
|
2012-02-28 20:24:54 +00:00
|
|
|
p[0] = p[1]
|
2012-03-01 15:35:56 +00:00
|
|
|
self.scope.current = p[1]
|
2012-01-28 14:52:09 +00:00
|
|
|
|
2012-02-19 20:38:19 +00:00
|
|
|
def p_font_face_open(self, p):
|
|
|
|
""" block_open : css_font_face t_ws brace_open
|
2012-01-28 14:52:09 +00:00
|
|
|
"""
|
2012-03-16 15:08:45 +00:00
|
|
|
p[0] = Identifier([p[1], p[2]]).parse(self.scope)
|
2012-02-25 18:28:05 +00:00
|
|
|
|
2012-02-19 20:38:19 +00:00
|
|
|
|
2012-01-28 14:52:09 +00:00
|
|
|
#
|
|
|
|
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
2012-02-19 20:38:19 +00:00
|
|
|
#
|
|
|
|
def p_mixin(self, p):
|
|
|
|
""" mixin_decl : open_mixin declaration_list brace_close
|
2012-01-28 14:52:09 +00:00
|
|
|
"""
|
2012-03-03 15:16:29 +00:00
|
|
|
self.scope.add_mixin(Mixin(list(p)[1:], p.lineno(3)).parse(self.scope))
|
2012-02-26 15:50:07 +00:00
|
|
|
self.scope.pop()
|
2012-03-18 10:08:36 +00:00
|
|
|
self.scope.in_mixin = False
|
2012-03-03 15:16:29 +00:00
|
|
|
p[0] = None
|
2012-01-28 14:52:09 +00:00
|
|
|
|
2012-02-19 20:38:19 +00:00
|
|
|
def p_open_mixin(self, p):
|
2012-03-18 13:21:47 +00:00
|
|
|
""" open_mixin : identifier t_popen mixin_args t_pclose brace_open
|
2012-01-28 14:52:09 +00:00
|
|
|
"""
|
2012-03-18 13:21:47 +00:00
|
|
|
p[1].parse(self.scope)
|
|
|
|
p[0] = [p[1], p[3]]
|
2012-03-18 10:08:36 +00:00
|
|
|
self.scope.in_mixin = True
|
2012-01-28 14:52:09 +00:00
|
|
|
|
2012-02-19 20:38:19 +00:00
|
|
|
def p_call_mixin(self, p):
|
2012-03-18 13:21:47 +00:00
|
|
|
""" call_mixin : identifier t_popen mixin_args t_pclose ';'
|
2012-01-28 14:52:09 +00:00
|
|
|
"""
|
2012-03-18 13:21:47 +00:00
|
|
|
p[1].parse(None)
|
|
|
|
mixin = self.scope.mixins(p[1].raw())
|
2012-03-03 15:16:29 +00:00
|
|
|
if mixin:
|
2012-03-03 19:58:56 +00:00
|
|
|
try:
|
2012-03-18 10:08:36 +00:00
|
|
|
if self.scope.in_mixin:
|
|
|
|
p[0] = Deferred(mixin, p[3])
|
|
|
|
else:
|
|
|
|
p[0] = mixin.call(self.scope, p[3])
|
2012-03-03 19:58:56 +00:00
|
|
|
except SyntaxError as e:
|
|
|
|
self.handle_error(e, p.lineno(2))
|
2012-03-18 14:21:58 +00:00
|
|
|
elif not p[3]:
|
|
|
|
# fallback to block. Allow calls of name() to blocks
|
|
|
|
block = self.scope.blocks(p[1].raw())
|
|
|
|
if block:
|
|
|
|
p[0] = block.copy(self.scope)
|
2012-03-03 15:16:29 +00:00
|
|
|
else:
|
2012-03-18 14:21:58 +00:00
|
|
|
if self.scope.in_mixin:
|
|
|
|
p[0] = Deferred(p[1], p[3])
|
|
|
|
else:
|
|
|
|
self.handle_error('Call unknown mixin `%s`' % p[1].raw(True), p.lineno(2))
|
2012-03-13 20:26:35 +00:00
|
|
|
|
|
|
|
def p_mixin_args_arguments(self, p):
|
|
|
|
""" mixin_args : less_arguments
|
|
|
|
"""
|
|
|
|
p[0] = [p[1]]
|
2012-01-28 14:52:09 +00:00
|
|
|
|
2012-02-19 20:38:19 +00:00
|
|
|
def p_mixin_args_aux(self, p):
|
|
|
|
""" mixin_args : mixin_args ',' argument
|
|
|
|
| mixin_args ',' mixin_kwarg
|
2012-03-13 20:26:35 +00:00
|
|
|
| mixin_args argument
|
|
|
|
| mixin_args mixin_kwarg
|
2012-01-28 14:52:09 +00:00
|
|
|
"""
|
2012-03-13 20:26:35 +00:00
|
|
|
p[1].extend(list(p)[2:])
|
2012-01-28 14:52:09 +00:00
|
|
|
p[0] = p[1]
|
2012-02-19 20:38:19 +00:00
|
|
|
|
|
|
|
def p_mixin_args(self, p):
|
|
|
|
""" mixin_args : argument
|
|
|
|
| mixin_kwarg
|
2012-01-28 14:52:09 +00:00
|
|
|
"""
|
|
|
|
p[0] = [p[1]]
|
|
|
|
|
2012-03-03 15:16:29 +00:00
|
|
|
def p_mixin_args_empty(self, p):
|
|
|
|
""" mixin_args : empty
|
|
|
|
"""
|
|
|
|
p[0] = None
|
|
|
|
|
2012-02-19 20:38:19 +00:00
|
|
|
def p_mixin_kwarg(self, p):
|
|
|
|
""" mixin_kwarg : variable ':' argument
|
2012-01-28 14:52:09 +00:00
|
|
|
"""
|
2012-03-03 15:16:29 +00:00
|
|
|
p[0] = Variable(list(p)[1:], p.lineno(2))
|
2012-01-28 14:52:09 +00:00
|
|
|
|
|
|
|
#
|
|
|
|
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
2012-02-19 20:38:19 +00:00
|
|
|
#
|
2012-01-28 14:52:09 +00:00
|
|
|
|
2012-02-19 20:38:19 +00:00
|
|
|
def p_declaration_list(self, p):
|
|
|
|
""" declaration_list : declaration_list declaration
|
|
|
|
| declaration
|
|
|
|
| empty
|
2012-01-28 14:52:09 +00:00
|
|
|
"""
|
2012-02-19 20:38:19 +00:00
|
|
|
if len(p) > 2:
|
|
|
|
p[1].extend(p[2])
|
2012-01-28 14:52:09 +00:00
|
|
|
p[0] = p[1]
|
|
|
|
|
2012-02-19 20:38:19 +00:00
|
|
|
def p_declaration(self, p):
|
|
|
|
""" declaration : variable_decl
|
|
|
|
| property_decl
|
|
|
|
| block_decl
|
|
|
|
| mixin_decl
|
|
|
|
| call_mixin
|
2012-01-28 14:52:09 +00:00
|
|
|
"""
|
2012-02-28 20:24:54 +00:00
|
|
|
p[0] = p[1] if type(p[1]) is list else [p[1]]
|
2012-01-28 14:52:09 +00:00
|
|
|
|
2012-02-19 20:38:19 +00:00
|
|
|
#
|
|
|
|
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
#
|
|
|
|
|
2012-01-28 14:52:09 +00:00
|
|
|
def p_variable_decl(self, p):
|
2012-02-19 20:38:19 +00:00
|
|
|
""" variable_decl : variable ':' style_list ';'
|
2012-01-28 14:52:09 +00:00
|
|
|
"""
|
|
|
|
try:
|
2012-02-26 15:50:07 +00:00
|
|
|
v = Variable(list(p)[1:], p.lineno(4))
|
2012-02-25 17:08:08 +00:00
|
|
|
v.parse(self.scope)
|
2012-03-16 15:39:03 +00:00
|
|
|
self.scope.add_variable(v)
|
2012-01-28 14:52:09 +00:00
|
|
|
except SyntaxError as e:
|
2012-03-18 10:08:36 +00:00
|
|
|
self.handle_error(e, p.lineno(2))
|
2012-01-28 14:52:09 +00:00
|
|
|
p[0] = None
|
|
|
|
|
|
|
|
#
|
|
|
|
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
#
|
|
|
|
|
|
|
|
def p_property_decl(self, p):
|
2012-02-19 20:38:19 +00:00
|
|
|
""" property_decl : prop_open style_list ';'
|
2012-02-26 10:59:21 +00:00
|
|
|
| prop_open style_list css_important ';'
|
2012-02-19 20:38:19 +00:00
|
|
|
| prop_open empty ';'
|
2012-01-28 14:52:09 +00:00
|
|
|
"""
|
2012-02-26 15:50:07 +00:00
|
|
|
l = len(p)
|
|
|
|
p[0] = Property(list(p)[1:-1], p.lineno(l-1))
|
2012-01-28 14:52:09 +00:00
|
|
|
|
2012-03-06 20:23:27 +00:00
|
|
|
def p_property_decl_arguments(self, p):
|
|
|
|
""" property_decl : prop_open less_arguments ';'
|
|
|
|
"""
|
|
|
|
p[0] = Property([p[1], [p[2]]], p.lineno(3))
|
|
|
|
|
2012-02-26 13:20:35 +00:00
|
|
|
def p_prop_open_ie_hack(self, p):
|
2012-02-26 15:50:07 +00:00
|
|
|
""" prop_open : '*' prop_open
|
2012-02-26 13:20:35 +00:00
|
|
|
"""
|
|
|
|
p[0] = (p[1][0], p[2][0])
|
|
|
|
|
2012-02-19 20:38:19 +00:00
|
|
|
def p_prop_open(self, p):
|
|
|
|
""" prop_open : property ':'
|
|
|
|
| vendor_property ':'
|
|
|
|
| word ':'
|
2012-01-28 14:52:09 +00:00
|
|
|
"""
|
2012-02-26 13:20:35 +00:00
|
|
|
p[0] = (p[1][0], '')
|
2012-01-28 14:52:09 +00:00
|
|
|
|
|
|
|
#
|
|
|
|
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
#
|
2012-02-19 20:38:19 +00:00
|
|
|
|
|
|
|
def p_style_list_aux(self, p):
|
|
|
|
""" style_list : style_list style
|
|
|
|
| style_list ',' style
|
2012-03-13 20:26:35 +00:00
|
|
|
| style_list t_ws style
|
2012-02-19 20:38:19 +00:00
|
|
|
"""
|
|
|
|
p[1].extend(list(p)[2:])
|
|
|
|
p[0] = p[1]
|
|
|
|
|
2012-01-28 14:52:09 +00:00
|
|
|
def p_style_list(self, p):
|
2012-02-19 20:38:19 +00:00
|
|
|
""" style_list : style
|
|
|
|
"""
|
|
|
|
p[0] = [p[1]]
|
|
|
|
|
|
|
|
def p_style(self, p):
|
|
|
|
""" style : expression
|
|
|
|
| css_string
|
|
|
|
| word
|
|
|
|
| property
|
|
|
|
| vendor_property
|
|
|
|
| istring
|
|
|
|
| fcall
|
2012-01-28 14:52:09 +00:00
|
|
|
"""
|
|
|
|
p[0] = p[1]
|
|
|
|
|
2012-02-19 20:38:19 +00:00
|
|
|
#
|
|
|
|
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
2012-02-27 18:48:20 +00:00
|
|
|
#
|
|
|
|
|
2012-02-19 20:38:19 +00:00
|
|
|
def p_identifier(self, p):
|
|
|
|
""" identifier : identifier_list
|
2012-03-16 15:08:45 +00:00
|
|
|
| page
|
|
|
|
| page filter
|
2012-01-28 14:52:09 +00:00
|
|
|
"""
|
2012-02-26 15:50:07 +00:00
|
|
|
p[0] = Identifier(p[1], 0)
|
2012-02-19 20:38:19 +00:00
|
|
|
|
|
|
|
def p_identifier_list_aux(self, p):
|
|
|
|
""" identifier_list : identifier_list ',' identifier_group
|
|
|
|
"""
|
|
|
|
p[1].extend([p[2]])
|
|
|
|
p[1].extend(p[3])
|
2012-01-28 14:52:09 +00:00
|
|
|
p[0] = p[1]
|
|
|
|
|
2012-02-19 20:38:19 +00:00
|
|
|
def p_identifier_list(self, p):
|
|
|
|
""" identifier_list : identifier_group
|
2012-01-28 14:52:09 +00:00
|
|
|
"""
|
|
|
|
p[0] = p[1]
|
|
|
|
|
2012-02-19 20:38:19 +00:00
|
|
|
def p_identifier_group_op(self, p):
|
|
|
|
""" identifier_group : identifier_group child_selector ident_parts
|
2012-02-26 15:50:07 +00:00
|
|
|
| identifier_group '+' ident_parts
|
2012-02-26 10:59:21 +00:00
|
|
|
| identifier_group general_sibling_selector ident_parts
|
2012-03-15 18:56:51 +00:00
|
|
|
| identifier_group '*'
|
2012-01-28 14:52:09 +00:00
|
|
|
"""
|
|
|
|
p[1].extend([p[2]])
|
2012-03-15 18:56:51 +00:00
|
|
|
if len(p) > 3: p[1].extend(p[3])
|
2012-01-28 14:52:09 +00:00
|
|
|
p[0] = p[1]
|
|
|
|
|
2012-02-19 20:38:19 +00:00
|
|
|
def p_identifier_group(self, p):
|
|
|
|
""" identifier_group : ident_parts
|
2012-01-28 14:52:09 +00:00
|
|
|
"""
|
2012-02-19 20:38:19 +00:00
|
|
|
p[0] = p[1]
|
2012-01-28 14:52:09 +00:00
|
|
|
|
2012-02-19 20:38:19 +00:00
|
|
|
def p_ident_parts_aux(self, p):
|
|
|
|
""" ident_parts : ident_parts ident_part
|
|
|
|
| ident_parts filter_group
|
2012-01-28 14:52:09 +00:00
|
|
|
"""
|
2012-02-19 20:38:19 +00:00
|
|
|
if type(p[2]) is list:
|
|
|
|
p[1].extend(p[2])
|
|
|
|
else: p[1].append(p[2])
|
2012-01-28 14:52:09 +00:00
|
|
|
p[0] = p[1]
|
|
|
|
|
2012-02-19 20:38:19 +00:00
|
|
|
def p_ident_parts(self, p):
|
|
|
|
""" ident_parts : ident_part
|
|
|
|
| selector
|
|
|
|
| filter_group
|
2012-01-28 14:52:09 +00:00
|
|
|
"""
|
2012-02-19 20:38:19 +00:00
|
|
|
if type(p[1]) is not list:
|
|
|
|
p[1] = [p[1]]
|
|
|
|
p[0] = p[1]
|
2012-01-28 14:52:09 +00:00
|
|
|
|
2012-02-27 18:48:20 +00:00
|
|
|
def p_ident_media(self, p):
|
|
|
|
""" ident_parts : css_media t_ws
|
|
|
|
"""
|
|
|
|
p[0] = list(p)[1:]
|
|
|
|
|
2012-02-19 20:38:19 +00:00
|
|
|
def p_selector(self, p):
|
2012-02-26 17:17:05 +00:00
|
|
|
""" selector : '*'
|
2012-02-26 15:50:07 +00:00
|
|
|
| '+'
|
2012-02-26 10:59:21 +00:00
|
|
|
| child_selector
|
|
|
|
| general_sibling_selector
|
2012-01-28 14:52:09 +00:00
|
|
|
"""
|
|
|
|
p[0] = p[1]
|
|
|
|
|
2012-02-19 20:38:19 +00:00
|
|
|
def p_ident_part(self, p):
|
|
|
|
""" ident_part : class
|
|
|
|
| id
|
|
|
|
| dom
|
2012-02-26 17:17:05 +00:00
|
|
|
| combinator
|
2012-02-27 18:48:20 +00:00
|
|
|
| color
|
2012-01-28 14:52:09 +00:00
|
|
|
"""
|
2012-02-19 20:38:19 +00:00
|
|
|
p[0] = p[1]
|
2012-01-28 14:52:09 +00:00
|
|
|
|
|
|
|
#
|
|
|
|
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
#
|
2012-02-19 20:38:19 +00:00
|
|
|
|
|
|
|
def p_filter_group_aux(self, p):
|
|
|
|
""" filter_group : filter_group filter
|
2012-01-28 14:52:09 +00:00
|
|
|
"""
|
2012-02-19 20:38:19 +00:00
|
|
|
p[1].extend(p[2])
|
2012-01-28 14:52:09 +00:00
|
|
|
p[0] = p[1]
|
|
|
|
|
2012-02-19 20:38:19 +00:00
|
|
|
def p_filter_group(self, p):
|
|
|
|
""" filter_group : filter
|
2012-01-28 14:52:09 +00:00
|
|
|
"""
|
|
|
|
p[0] = p[1]
|
2012-02-19 20:38:19 +00:00
|
|
|
|
|
|
|
def p_filter(self, p):
|
|
|
|
""" filter : css_filter
|
|
|
|
| ':' word
|
|
|
|
| ':' vendor_property
|
2012-03-16 15:08:45 +00:00
|
|
|
| ':' vendor_property t_ws
|
|
|
|
| ':' css_property
|
|
|
|
| ':' css_property t_ws
|
2012-02-19 20:38:19 +00:00
|
|
|
| ':' css_filter
|
|
|
|
| ':' ':' word
|
|
|
|
| ':' ':' vendor_property
|
|
|
|
"""
|
|
|
|
p[0] = list(p)[1:]
|
2012-01-28 14:52:09 +00:00
|
|
|
|
|
|
|
#
|
|
|
|
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
2012-02-19 20:38:19 +00:00
|
|
|
#
|
2012-01-28 14:52:09 +00:00
|
|
|
|
|
|
|
def p_fcall(self, p):
|
2012-02-19 20:38:19 +00:00
|
|
|
""" fcall : word t_popen argument_list t_pclose
|
|
|
|
| property t_popen argument_list t_pclose
|
|
|
|
| vendor_property t_popen argument_list t_pclose
|
2012-02-12 10:11:04 +00:00
|
|
|
| less_open_format argument_list t_pclose
|
2012-02-26 13:20:35 +00:00
|
|
|
| '~' istring
|
|
|
|
| '~' css_string
|
2012-01-28 14:52:09 +00:00
|
|
|
"""
|
2012-02-26 15:50:07 +00:00
|
|
|
p[0] = Call(list(p)[1:], 0)
|
2012-01-28 14:52:09 +00:00
|
|
|
|
|
|
|
#
|
|
|
|
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
#
|
2012-03-15 19:20:04 +00:00
|
|
|
|
|
|
|
def p_argument_list_empty(self, p):
|
|
|
|
""" argument_list : empty
|
|
|
|
"""
|
|
|
|
p[0] = ''
|
2012-01-28 14:52:09 +00:00
|
|
|
|
|
|
|
def p_argument_list_aux(self, p):
|
2012-02-19 20:38:19 +00:00
|
|
|
""" argument_list : argument_list argument
|
|
|
|
| argument_list ',' argument
|
2012-01-28 14:52:09 +00:00
|
|
|
"""
|
2012-02-19 20:38:19 +00:00
|
|
|
p[1].extend(list(p)[2:])
|
2012-01-28 14:52:09 +00:00
|
|
|
p[0] = p[1]
|
|
|
|
|
|
|
|
def p_argument_list(self, p):
|
2012-03-13 20:26:35 +00:00
|
|
|
""" argument_list : argument
|
2012-01-28 14:52:09 +00:00
|
|
|
"""
|
|
|
|
p[0] = [p[1]]
|
|
|
|
|
|
|
|
def p_argument(self, p):
|
|
|
|
""" argument : expression
|
|
|
|
| css_string
|
|
|
|
| istring
|
2012-02-19 20:38:19 +00:00
|
|
|
| word
|
|
|
|
| id
|
2012-01-28 14:52:09 +00:00
|
|
|
| css_uri
|
|
|
|
| '='
|
2012-02-19 20:38:19 +00:00
|
|
|
| fcall
|
2012-01-28 14:52:09 +00:00
|
|
|
"""
|
|
|
|
p[0] = p[1]
|
|
|
|
|
|
|
|
#
|
|
|
|
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
#
|
2012-02-19 20:38:19 +00:00
|
|
|
|
|
|
|
def p_expression_aux(self, p):
|
2012-02-26 15:50:07 +00:00
|
|
|
""" expression : expression '+' expression
|
|
|
|
| expression '-' expression
|
|
|
|
| expression '/' expression
|
|
|
|
| expression '*' expression
|
|
|
|
| word '/' expression
|
2012-02-19 20:38:19 +00:00
|
|
|
"""
|
2012-02-26 15:50:07 +00:00
|
|
|
p[0] = Expression(list(p)[1:], 0)
|
2012-02-19 20:38:19 +00:00
|
|
|
|
|
|
|
def p_expression_p_neg(self, p):
|
2012-02-26 15:50:07 +00:00
|
|
|
""" expression : '-' t_popen expression t_pclose
|
2012-02-19 20:38:19 +00:00
|
|
|
"""
|
|
|
|
p[0] = [p[1], p[3]]
|
|
|
|
|
|
|
|
def p_expression_p(self, p):
|
|
|
|
""" expression : t_popen expression t_pclose
|
|
|
|
"""
|
|
|
|
p[0] = p[2]
|
|
|
|
|
|
|
|
def p_expression(self, p):
|
|
|
|
""" expression : factor
|
|
|
|
"""
|
|
|
|
p[0] = p[1]
|
|
|
|
|
|
|
|
def p_factor(self, p):
|
|
|
|
""" factor : color
|
|
|
|
| number
|
|
|
|
| variable
|
|
|
|
| css_dom
|
|
|
|
"""
|
|
|
|
p[0] = p[1]
|
|
|
|
|
|
|
|
#
|
|
|
|
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
#
|
|
|
|
|
2012-01-28 14:52:09 +00:00
|
|
|
def p_interpolated_str(self, p):
|
2012-02-19 20:38:19 +00:00
|
|
|
""" istring : less_string
|
2012-01-28 14:52:09 +00:00
|
|
|
"""
|
2012-03-03 09:58:47 +00:00
|
|
|
p[0] = String(p[1], p.lineno(1))
|
2012-01-28 14:52:09 +00:00
|
|
|
|
|
|
|
def p_variable_neg(self, p):
|
2012-02-26 15:50:07 +00:00
|
|
|
""" variable : '-' variable
|
2012-01-28 14:52:09 +00:00
|
|
|
"""
|
|
|
|
p[0] = '-' + p[2]
|
|
|
|
|
|
|
|
def p_variable_strange(self, p):
|
2012-02-19 20:38:19 +00:00
|
|
|
""" variable : t_popen variable t_pclose
|
2012-01-28 14:52:09 +00:00
|
|
|
"""
|
|
|
|
p[0] = p[2]
|
|
|
|
|
|
|
|
def p_variable(self, p):
|
2012-02-19 20:38:19 +00:00
|
|
|
""" variable : less_variable
|
|
|
|
| less_variable t_ws
|
2012-01-28 14:52:09 +00:00
|
|
|
"""
|
|
|
|
p[0] = p[1]
|
2012-02-19 20:38:19 +00:00
|
|
|
|
2012-01-28 14:52:09 +00:00
|
|
|
def p_color(self, p):
|
2012-03-15 18:52:26 +00:00
|
|
|
""" color : css_color
|
|
|
|
| css_color t_ws
|
2012-01-28 14:52:09 +00:00
|
|
|
"""
|
2012-03-13 21:12:09 +00:00
|
|
|
try:
|
|
|
|
p[0] = Color().fmt(p[1])
|
2012-03-15 18:52:26 +00:00
|
|
|
if len(p) > 2: p[0] = [p[0], p[2]]
|
2012-03-13 21:12:09 +00:00
|
|
|
except ValueError:
|
|
|
|
self.handle_error('Illegal color value `%s`' % p[1], p.lineno(1), 'W')
|
|
|
|
p[0] = p[1]
|
2012-02-19 20:38:19 +00:00
|
|
|
|
2012-01-28 14:52:09 +00:00
|
|
|
def p_number(self, p):
|
2012-02-19 20:38:19 +00:00
|
|
|
""" number : css_number
|
|
|
|
| css_number t_ws
|
2012-01-28 14:52:09 +00:00
|
|
|
"""
|
2012-02-19 20:38:19 +00:00
|
|
|
p[0] = tuple(list(p)[1:])
|
2012-01-28 14:52:09 +00:00
|
|
|
|
2012-02-19 20:38:19 +00:00
|
|
|
def p_dom(self, p):
|
|
|
|
""" dom : css_dom
|
|
|
|
| css_dom t_ws
|
|
|
|
"""
|
|
|
|
p[0] = tuple(list(p)[1:])
|
|
|
|
|
|
|
|
def p_word(self, p):
|
|
|
|
""" word : css_ident
|
|
|
|
| css_ident t_ws
|
|
|
|
"""
|
|
|
|
p[0] = tuple(list(p)[1:])
|
|
|
|
|
|
|
|
def p_class(self, p):
|
|
|
|
""" class : css_class
|
|
|
|
| css_class t_ws
|
|
|
|
"""
|
|
|
|
p[0] = tuple(list(p)[1:])
|
|
|
|
|
|
|
|
def p_id(self, p):
|
|
|
|
""" id : css_id
|
|
|
|
| css_id t_ws
|
|
|
|
"""
|
|
|
|
p[0] = tuple(list(p)[1:])
|
|
|
|
|
|
|
|
def p_property(self, p):
|
|
|
|
""" property : css_property
|
|
|
|
| css_property t_ws
|
|
|
|
"""
|
|
|
|
p[0] = tuple(list(p)[1:])
|
|
|
|
|
2012-03-16 15:08:45 +00:00
|
|
|
def p_page(self, p):
|
|
|
|
""" page : css_page
|
|
|
|
| css_page t_ws
|
|
|
|
"""
|
|
|
|
p[0] = tuple(list(p)[1:])
|
|
|
|
|
2012-02-19 20:38:19 +00:00
|
|
|
def p_vendor_property(self, p):
|
|
|
|
""" vendor_property : css_vendor_property
|
|
|
|
| css_vendor_property t_ws
|
|
|
|
"""
|
|
|
|
p[0] = tuple(list(p)[1:])
|
|
|
|
|
|
|
|
def p_combinator(self, p):
|
|
|
|
""" combinator : '&' t_ws
|
|
|
|
| '&'
|
|
|
|
"""
|
|
|
|
p[0] = tuple(list(p)[1:])
|
|
|
|
|
2012-02-26 10:59:21 +00:00
|
|
|
|
2012-02-19 20:38:19 +00:00
|
|
|
def p_child_selector(self, p):
|
|
|
|
""" child_selector : '>' t_ws
|
|
|
|
| '>'
|
|
|
|
"""
|
|
|
|
p[0] = tuple(list(p)[1:])
|
2012-01-28 14:52:09 +00:00
|
|
|
|
2012-02-26 10:59:21 +00:00
|
|
|
def p_general_sibling_selector(self, p):
|
|
|
|
""" general_sibling_selector : '~' t_ws
|
|
|
|
| '~'
|
|
|
|
"""
|
|
|
|
p[0] = tuple(list(p)[1:])
|
|
|
|
|
2012-01-28 14:52:09 +00:00
|
|
|
def p_scope_open(self, p):
|
2012-02-25 11:09:49 +00:00
|
|
|
""" brace_open : '{'
|
2012-01-28 14:52:09 +00:00
|
|
|
"""
|
2012-02-12 11:50:57 +00:00
|
|
|
self.scope.push()
|
2012-01-28 14:52:09 +00:00
|
|
|
p[0] = p[1]
|
|
|
|
|
|
|
|
def p_scope_close(self, p):
|
2012-02-25 11:09:49 +00:00
|
|
|
""" brace_close : '}'
|
2012-01-28 14:52:09 +00:00
|
|
|
"""
|
|
|
|
p[0] = p[1]
|
|
|
|
|
2012-02-19 20:38:19 +00:00
|
|
|
def p_empty(self, p):
|
2012-02-25 11:09:49 +00:00
|
|
|
'empty :'
|
2012-02-19 20:38:19 +00:00
|
|
|
pass
|
|
|
|
|
2012-01-28 14:52:09 +00:00
|
|
|
#
|
|
|
|
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
#
|
|
|
|
|
|
|
|
def p_error(self, t):
|
|
|
|
""" Internal error handler
|
|
|
|
@param Lex token: Error token
|
|
|
|
"""
|
2012-01-30 12:56:33 +00:00
|
|
|
if t and self.verbose:
|
2012-03-15 20:05:22 +00:00
|
|
|
print("\x1b[31mE: %s line: %d, Syntax Error, token: `%s`, `%s`\x1b[0m"
|
2012-01-30 13:54:31 +00:00
|
|
|
% (self.target, t.lineno, t.type, t.value))
|
2012-01-28 14:52:09 +00:00
|
|
|
while True:
|
|
|
|
t = self.lex.token()
|
|
|
|
if not t or t.value == '}':
|
|
|
|
if len(self.scope) > 1:
|
|
|
|
self.scope.pop()
|
|
|
|
break
|
|
|
|
self.parser.restart()
|
|
|
|
return t
|
|
|
|
|
2012-03-03 15:16:29 +00:00
|
|
|
def handle_error(self, e, line, t='E'):
|
2012-01-28 14:52:09 +00:00
|
|
|
""" Custom error handler
|
|
|
|
@param Exception: Exception
|
|
|
|
@param Parser token: Parser token
|
|
|
|
@param string: Error level
|
|
|
|
"""
|
2012-03-18 10:08:36 +00:00
|
|
|
# print(e.trace())
|
2012-01-30 12:56:33 +00:00
|
|
|
if self.verbose:
|
2012-03-15 20:05:22 +00:00
|
|
|
color = '\x1b[31m' if t == 'E' else '\x1b[33m'
|
|
|
|
print("%s%s: line: %d: %s\n" % (color, t, line, e), end='\x1b[0m')
|
2012-02-11 18:02:08 +00:00
|
|
|
|