Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
lexer.py482 linesDownload Raw Back to mako
1# mako/lexer.py2# Copyright 2006-2024 the Mako authors and contributors <see AUTHORS file>3#4# This module is part of Mako and is released under5# the MIT License: http://www.opensource.org/licenses/mit-license.php6 7"""provides the Lexer class for parsing template strings into parse trees."""8 9import codecs10import re11 12from mako import exceptions13from mako import parsetree14from mako.pygen import adjust_whitespace15 16_regexp_cache = {}17 18 19class Lexer:20    def __init__(21        self, text, filename=None, input_encoding=None, preprocessor=None22    ):23        self.text = text24        self.filename = filename25        self.template = parsetree.TemplateNode(self.filename)26        self.matched_lineno = 127        self.matched_charpos = 028        self.lineno = 129        self.match_position = 030        self.tag = []31        self.control_line = []32        self.ternary_stack = []33        self.encoding = input_encoding34 35        if preprocessor is None:36            self.preprocessor = []37        elif not hasattr(preprocessor, "__iter__"):38            self.preprocessor = [preprocessor]39        else:40            self.preprocessor = preprocessor41 42    @property43    def exception_kwargs(self):44        return {45            "source": self.text,46            "lineno": self.matched_lineno,47            "pos": self.matched_charpos,48            "filename": self.filename,49        }50 51    def match(self, regexp, flags=None):52        """compile the given regexp, cache the reg, and call match_reg()."""53 54        try:55            reg = _regexp_cache[(regexp, flags)]56        except KeyError:57            reg = re.compile(regexp, flags) if flags else re.compile(regexp)58            _regexp_cache[(regexp, flags)] = reg59 60        return self.match_reg(reg)61 62    def match_reg(self, reg):63        """match the given regular expression object to the current text64        position.65 66        if a match occurs, update the current text and line position.67 68        """69 70        mp = self.match_position71 72        match = reg.match(self.text, self.match_position)73        if match:74            (start, end) = match.span()75            self.match_position = end + 1 if end == start else end76            self.matched_lineno = self.lineno77            cp = mp - 178            if cp >= 0 and cp < self.textlength:79                cp = self.text[: cp + 1].rfind("\n")80            self.matched_charpos = mp - cp81            self.lineno += self.text[mp : self.match_position].count("\n")82        return match83 84    def parse_until_text(self, watch_nesting, *text):85        startpos = self.match_position86        text_re = r"|".join(text)87        brace_level = 088        paren_level = 089        bracket_level = 090        while True:91            match = self.match(r"#.*\n")92            if match:93                continue94            match = self.match(95                r"(\"\"\"|\'\'\'|\"|\')[^\\]*?(\\.[^\\]*?)*\1", re.S96            )97            if match:98                continue99            match = self.match(r"(%s)" % text_re)100            if match and not (101                watch_nesting102                and (brace_level > 0 or paren_level > 0 or bracket_level > 0)103            ):104                return (105                    self.text[106                        startpos : self.match_position - len(match.group(1))107                    ],108                    match.group(1),109                )110            elif not match:111                match = self.match(r"(.*?)(?=\"|\'|#|%s)" % text_re, re.S)112            if match:113                brace_level += match.group(1).count("{")114                brace_level -= match.group(1).count("}")115                paren_level += match.group(1).count("(")116                paren_level -= match.group(1).count(")")117                bracket_level += match.group(1).count("[")118                bracket_level -= match.group(1).count("]")119                continue120            raise exceptions.SyntaxException(121                "Expected: %s" % ",".join(text), **self.exception_kwargs122            )123 124    def append_node(self, nodecls, *args, **kwargs):125        kwargs.setdefault("source", self.text)126        kwargs.setdefault("lineno", self.matched_lineno)127        kwargs.setdefault("pos", self.matched_charpos)128        kwargs["filename"] = self.filename129        node = nodecls(*args, **kwargs)130        if len(self.tag):131            self.tag[-1].nodes.append(node)132        else:133            self.template.nodes.append(node)134        # build a set of child nodes for the control line135        # (used for loop variable detection)136        # also build a set of child nodes on ternary control lines137        # (used for determining if a pass needs to be auto-inserted138        if self.control_line:139            control_frame = self.control_line[-1]140            control_frame.nodes.append(node)141            if (142                not (143                    isinstance(node, parsetree.ControlLine)144                    and control_frame.is_ternary(node.keyword)145                )146                and self.ternary_stack147                and self.ternary_stack[-1]148            ):149                self.ternary_stack[-1][-1].nodes.append(node)150        if isinstance(node, parsetree.Tag):151            if len(self.tag):152                node.parent = self.tag[-1]153            self.tag.append(node)154        elif isinstance(node, parsetree.ControlLine):155            if node.isend:156                self.control_line.pop()157                self.ternary_stack.pop()158            elif node.is_primary:159                self.control_line.append(node)160                self.ternary_stack.append([])161            elif self.control_line and self.control_line[-1].is_ternary(162                node.keyword163            ):164                self.ternary_stack[-1].append(node)165            elif self.control_line and not self.control_line[-1].is_ternary(166                node.keyword167            ):168                raise exceptions.SyntaxException(169                    "Keyword '%s' not a legal ternary for keyword '%s'"170                    % (node.keyword, self.control_line[-1].keyword),171                    **self.exception_kwargs,172                )173 174    _coding_re = re.compile(r"#.*coding[:=]\s*([-\w.]+).*\r?\n")175 176    def decode_raw_stream(self, text, decode_raw, known_encoding, filename):177        """given string/unicode or bytes/string, determine encoding178        from magic encoding comment, return body as unicode179        or raw if decode_raw=False180 181        """182        if isinstance(text, str):183            m = self._coding_re.match(text)184            encoding = m and m.group(1) or known_encoding or "utf-8"185            return encoding, text186 187        if text.startswith(codecs.BOM_UTF8):188            text = text[len(codecs.BOM_UTF8) :]189            parsed_encoding = "utf-8"190            m = self._coding_re.match(text.decode("utf-8", "ignore"))191            if m is not None and m.group(1) != "utf-8":192                raise exceptions.CompileException(193                    "Found utf-8 BOM in file, with conflicting "194                    "magic encoding comment of '%s'" % m.group(1),195                    text.decode("utf-8", "ignore"),196                    0,197                    0,198                    filename,199                )200        else:201            m = self._coding_re.match(text.decode("utf-8", "ignore"))202            parsed_encoding = m.group(1) if m else known_encoding or "utf-8"203        if decode_raw:204            try:205                text = text.decode(parsed_encoding)206            except UnicodeDecodeError:207                raise exceptions.CompileException(208                    "Unicode decode operation of encoding '%s' failed"209                    % parsed_encoding,210                    text.decode("utf-8", "ignore"),211                    0,212                    0,213                    filename,214                )215 216        return parsed_encoding, text217 218    def parse(self):219        self.encoding, self.text = self.decode_raw_stream(220            self.text, True, self.encoding, self.filename221        )222 223        for preproc in self.preprocessor:224            self.text = preproc(self.text)225 226        # push the match marker past the227        # encoding comment.228        self.match_reg(self._coding_re)229 230        self.textlength = len(self.text)231 232        while True:233            if self.match_position > self.textlength:234                break235 236            if self.match_end():237                break238            if self.match_expression():239                continue240            if self.match_control_line():241                continue242            if self.match_comment():243                continue244            if self.match_tag_start():245                continue246            if self.match_tag_end():247                continue248            if self.match_python_block():249                continue250            if self.match_percent():251                continue252            if self.match_text():253                continue254 255            if self.match_position > self.textlength:256                break257            # TODO: no coverage here258            raise exceptions.MakoException("assertion failed")259 260        if len(self.tag):261            raise exceptions.SyntaxException(262                "Unclosed tag: <%%%s>" % self.tag[-1].keyword,263                **self.exception_kwargs,264            )265        if len(self.control_line):266            raise exceptions.SyntaxException(267                "Unterminated control keyword: '%s'"268                % self.control_line[-1].keyword,269                self.text,270                self.control_line[-1].lineno,271                self.control_line[-1].pos,272                self.filename,273            )274        return self.template275 276    def match_tag_start(self):277        reg = r"""278            \<%     # opening tag279 280            ([\w\.\:]+)   # keyword281 282            ((?:\s+\w+|\s*=\s*|"[^"]*?"|'[^']*?'|\s*,\s*)*)  # attrname, = \283                                               #        sign, string expression284                                               # comma is for backwards compat285                                               # identified in #366286 287            \s*     # more whitespace288 289            (/)?>   # closing290 291        """292 293        match = self.match(294            reg,295            re.I | re.S | re.X,296        )297 298        if not match:299            return False300 301        keyword, attr, isend = match.groups()302        self.keyword = keyword303        attributes = {}304        if attr:305            for att in re.findall(306                r"\s*(\w+)\s*=\s*(?:'([^']*)'|\"([^\"]*)\")", attr307            ):308                key, val1, val2 = att309                text = val1 or val2310                text = text.replace("\r\n", "\n")311                attributes[key] = text312        self.append_node(parsetree.Tag, keyword, attributes)313        if isend:314            self.tag.pop()315        elif keyword == "text":316            match = self.match(r"(.*?)(?=\</%text>)", re.S)317            if not match:318                raise exceptions.SyntaxException(319                    "Unclosed tag: <%%%s>" % self.tag[-1].keyword,320                    **self.exception_kwargs,321                )322            self.append_node(parsetree.Text, match.group(1))323            return self.match_tag_end()324        return True325 326    def match_tag_end(self):327        match = self.match(r"\</%[\t ]*([^\t ]+?)[\t ]*>")328        if match:329            if not len(self.tag):330                raise exceptions.SyntaxException(331                    "Closing tag without opening tag: </%%%s>"332                    % match.group(1),333                    **self.exception_kwargs,334                )335            elif self.tag[-1].keyword != match.group(1):336                raise exceptions.SyntaxException(337                    "Closing tag </%%%s> does not match tag: <%%%s>"338                    % (match.group(1), self.tag[-1].keyword),339                    **self.exception_kwargs,340                )341            self.tag.pop()342            return True343        else:344            return False345 346    def match_end(self):347        match = self.match(r"\Z", re.S)348        if not match:349            return False350 351        string = match.group()352        if string:353            return string354        else:355            return True356 357    def match_percent(self):358        match = self.match(r"(?<=^)(\s*)%%(%*)", re.M)359        if match:360            self.append_node(361                parsetree.Text, match.group(1) + "%" + match.group(2)362            )363            return True364        else:365            return False366 367    def match_text(self):368        match = self.match(369            r"""370                (.*?)         # anything, followed by:371                (372                 (?<=\n)(?=[ \t]*(?=%|\#\#))  # an eval or line-based373                                            # comment, preceded by a374                                            # consumed newline and whitespace375                 |376                 (?=\${)      # an expression377                 |378                 (?=</?[%&])  # a substitution or block or call start or end379                              # - don't consume380                 |381                 (\\\r?\n)    # an escaped newline  - throw away382                 |383                 \Z           # end of string384                )""",385            re.X | re.S,386        )387 388        if match:389            text = match.group(1)390            if text:391                self.append_node(parsetree.Text, text)392            return True393        else:394            return False395 396    def match_python_block(self):397        match = self.match(r"<%(!)?")398        if match:399            line, pos = self.matched_lineno, self.matched_charpos400            text, end = self.parse_until_text(False, r"%>")401            # the trailing newline helps402            # compiler.parse() not complain about indentation403            text = adjust_whitespace(text) + "\n"404            self.append_node(405                parsetree.Code,406                text,407                match.group(1) == "!",408                lineno=line,409                pos=pos,410            )411            return True412        else:413            return False414 415    def match_expression(self):416        match = self.match(r"\${")417        if not match:418            return False419 420        line, pos = self.matched_lineno, self.matched_charpos421        text, end = self.parse_until_text(True, r"\|", r"}")422        if end == "|":423            escapes, end = self.parse_until_text(True, r"}")424        else:425            escapes = ""426        text = text.replace("\r\n", "\n")427        self.append_node(428            parsetree.Expression,429            text,430            escapes.strip(),431            lineno=line,432            pos=pos,433        )434        return True435 436    def match_control_line(self):437        match = self.match(438            r"(?<=^)[\t ]*(%(?!%)|##)[\t ]*((?:(?:\\\r?\n)|[^\r\n])*)"439            r"(?:\r?\n|\Z)",440            re.M,441        )442        if not match:443            return False444 445        operator = match.group(1)446        text = match.group(2)447        if operator == "%":448            m2 = re.match(r"(end)?(\w+)\s*(.*)", text)449            if not m2:450                raise exceptions.SyntaxException(451                    "Invalid control line: '%s'" % text,452                    **self.exception_kwargs,453                )454            isend, keyword = m2.group(1, 2)455            isend = isend is not None456 457            if isend:458                if not len(self.control_line):459                    raise exceptions.SyntaxException(460                        "No starting keyword '%s' for '%s'" % (keyword, text),461                        **self.exception_kwargs,462                    )463                elif self.control_line[-1].keyword != keyword:464                    raise exceptions.SyntaxException(465                        "Keyword '%s' doesn't match keyword '%s'"466                        % (text, self.control_line[-1].keyword),467                        **self.exception_kwargs,468                    )469            self.append_node(parsetree.ControlLine, keyword, isend, text)470        else:471            self.append_node(parsetree.Comment, text)472        return True473 474    def match_comment(self):475        """matches the multiline version of a comment"""476        match = self.match(r"<%doc>(.*?)</%doc>", re.S)477        if match:478            self.append_node(parsetree.Comment, match.group(1))479            return True480        else:481            return False482 
codekingpro/portable-devtools · Team Ai