diff options
Diffstat (limited to 'scss/_native.py')
-rw-r--r-- | scss/_native.py | 265 |
1 files changed, 0 insertions, 265 deletions
diff --git a/scss/_native.py b/scss/_native.py deleted file mode 100644 index 2be3d75..0000000 --- a/scss/_native.py +++ /dev/null @@ -1,265 +0,0 @@ -"""Pure-Python scanner and parser, used if _speedups is not available.""" -from __future__ import absolute_import -from __future__ import print_function -from __future__ import unicode_literals - -from collections import deque - -import re - -DEBUG = False - -# TODO copied from __init__ -_blocks_re = re.compile(r'[{},;()\'"\n]') - - -def locate_blocks(codestr): - """ - For processing CSS like strings. - - Either returns all selectors (that can be "smart" multi-lined, as - long as it's joined by `,`, or enclosed in `(` and `)`) with its code block - (the one between `{` and `}`, which can be nested), or the "lose" code - (properties) that doesn't have any blocks. - """ - lineno = 1 - - par = 0 - instr = None - depth = 0 - skip = False - i = init = lose = 0 - start = end = None - lineno_stack = deque() - - for m in _blocks_re.finditer(codestr): - i = m.start(0) - c = codestr[i] - if c == '\n': - lineno += 1 - - if instr is not None: - if c == instr: - instr = None # A string ends (FIXME: needs to accept escaped characters) - elif c in ('"', "'"): - instr = c # A string starts - elif c == '(': # parenthesis begins: - par += 1 - elif c == ')': # parenthesis ends: - par -= 1 - elif not par and not instr: - if c == '{': # block begins: - if depth == 0: - if i > 0 and codestr[i - 1] == '#': # Do not process #{...} as blocks! - skip = True - else: - lineno_stack.append(lineno) - start = i - if lose < init: - _property = codestr[lose:init].strip() - if _property: - yield lineno, _property, None - lose = init - depth += 1 - elif c == '}': # block ends: - if depth <= 0: - raise SyntaxError("Unexpected closing brace on line {0}".format(lineno)) - else: - depth -= 1 - if depth == 0: - if not skip: - end = i - _selectors = codestr[init:start].strip() - _codestr = codestr[start + 1:end].strip() - if _selectors: - yield lineno_stack.pop(), _selectors, _codestr - init = lose = end + 1 - skip = False - elif depth == 0: - if c == ';': # End of property (or block): - init = i - if lose < init: - _property = codestr[lose:init].strip() - if _property: - yield lineno, _property, None - init = lose = i + 1 - if depth > 0: - if not skip: - _selectors = codestr[init:start].strip() - _codestr = codestr[start + 1:].strip() - if _selectors: - yield lineno, _selectors, _codestr - if par: - raise Exception("Missing closing parenthesis somewhere in block: '%s'" % _selectors) - elif instr: - raise Exception("Missing closing string somewhere in block: '%s'" % _selectors) - else: - raise Exception("Block never closed: '%s'" % _selectors) - losestr = codestr[lose:] - for _property in losestr.split(';'): - _property = _property.strip() - lineno += _property.count('\n') - if _property: - yield lineno, _property, None - - -################################################################################ -# Parser - -class Parser(object): - # NOTE: This class has no C equivalent - def __init__(self, scanner): - self._scanner = scanner - self._pos = 0 - self._char_pos = 0 - - def reset(self, input): - self._scanner.reset(input) - self._pos = 0 - self._char_pos = 0 - - def _peek(self, types): - """ - Returns the token type for lookahead; if there are any args - then the list of args is the set of token types to allow - """ - try: - tok = self._scanner.token(self._pos, types) - return tok[2] - except SyntaxError: - return None - - def _scan(self, type): - """ - Returns the matched text, and moves to the next token - """ - tok = self._scanner.token(self._pos, set([type])) - self._char_pos = tok[0] - if tok[2] != type: - raise SyntaxError("SyntaxError[@ char %s: %s]" % (repr(tok[0]), "Trying to find " + type)) - self._pos += 1 - return tok[3] - - -class NoMoreTokens(Exception): - """ - Another exception object, for when we run out of tokens - """ - pass - - -class Scanner(object): - def __init__(self, patterns, ignore, input=None): - """ - Patterns is [(terminal,regex)...] - Ignore is [terminal,...]; - Input is a string - """ - self.reset(input) - self.ignore = ignore - # The stored patterns are a pair (compiled regex,source - # regex). If the patterns variable passed in to the - # constructor is None, we assume that the class already has a - # proper .patterns list constructed - if patterns is not None: - self.patterns = [] - for k, r in patterns: - self.patterns.append((k, re.compile(r))) - - def reset(self, input): - self.tokens = [] - self.restrictions = [] - self.input = input - self.pos = 0 - - def __repr__(self): - """ - Print the last 10 tokens that have been scanned in - """ - output = '' - for t in self.tokens[-10:]: - output = "%s\n (@%s) %s = %s" % (output, t[0], t[2], repr(t[3])) - return output - - def _scan(self, restrict): - """ - Should scan another token and add it to the list, self.tokens, - and add the restriction to self.restrictions - """ - # Keep looking for a token, ignoring any in self.ignore - token = None - while True: - best_pat = None - # Search the patterns for a match, with earlier - # tokens in the list having preference - best_pat_len = 0 - for tok, regex in self.patterns: - if DEBUG: - print("\tTrying %s: %s at pos %d -> %s" % (repr(tok), repr(regex.pattern), self.pos, repr(self.input))) - # First check to see if we're restricting to this token - if restrict and tok not in restrict and tok not in self.ignore: - if DEBUG: - print("\tSkipping %r!" % (tok,)) - continue - m = regex.match(self.input, self.pos) - if m: - # We got a match - best_pat = tok - best_pat_len = len(m.group(0)) - if DEBUG: - print("Match OK! %s: %s at pos %d" % (repr(tok), repr(regex.pattern), self.pos)) - break - - # If we didn't find anything, raise an error - if best_pat is None: - msg = "Bad token found" - if restrict: - msg = "Bad token found while trying to find one of the restricted tokens: %s" % (", ".join(repr(r) for r in restrict)) - raise SyntaxError("SyntaxError[@ char %s: %s]" % (repr(self.pos), msg)) - - # If we found something that isn't to be ignored, return it - if best_pat in self.ignore: - # This token should be ignored... - self.pos += best_pat_len - else: - end_pos = self.pos + best_pat_len - # Create a token with this data - token = ( - self.pos, - end_pos, - best_pat, - self.input[self.pos:end_pos] - ) - break - if token is not None: - self.pos = token[1] - # Only add this token if it's not in the list - # (to prevent looping) - if not self.tokens or token != self.tokens[-1]: - self.tokens.append(token) - self.restrictions.append(restrict) - return 1 - return 0 - - def token(self, i, restrict=None): - """ - Get the i'th token, and if i is one past the end, then scan - for another token; restrict is a list of tokens that - are allowed, or 0 for any token. - """ - tokens_len = len(self.tokens) - if i == tokens_len: # We are at the end, get the next... - tokens_len += self._scan(restrict) - if i < tokens_len: - if restrict and self.restrictions[i] and restrict > self.restrictions[i]: - raise NotImplementedError("Unimplemented: restriction set changed") - return self.tokens[i] - raise NoMoreTokens - - def rewind(self, i): - tokens_len = len(self.tokens) - if i <= tokens_len: - token = self.tokens[i] - self.tokens = self.tokens[:i] - self.restrictions = self.restrictions[:i] - self.pos = token[0] |