Blame tools/matchcompiler.py

Packit 2035a7
#!/usr/bin/env python
Packit 2035a7
#
Packit 2035a7
# Cppcheck - A tool for static C/C++ code analysis
Packit 2035a7
# Copyright (C) 2007-2018 Cppcheck team.
Packit 2035a7
#
Packit 2035a7
# This program is free software: you can redistribute it and/or modify
Packit 2035a7
# it under the terms of the GNU General Public License as published by
Packit 2035a7
# the Free Software Foundation, either version 3 of the License, or
Packit 2035a7
# (at your option) any later version.
Packit 2035a7
#
Packit 2035a7
# This program is distributed in the hope that it will be useful,
Packit 2035a7
# but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 2035a7
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit 2035a7
# GNU General Public License for more details.
Packit 2035a7
#
Packit 2035a7
# You should have received a copy of the GNU General Public License
Packit 2035a7
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
Packit 2035a7
Packit 2035a7
import io
Packit 2035a7
import os
Packit 2035a7
import sys
Packit 2035a7
import re
Packit 2035a7
import glob
Packit 2035a7
import argparse
Packit 2035a7
import errno
Packit 2035a7
Packit 2035a7
Packit 2035a7
class MatchCompiler:
Packit 2035a7
Packit 2035a7
    def __init__(self, verify_mode=False, show_skipped=False):
Packit 2035a7
        self._verifyMode = verify_mode
Packit 2035a7
        self._showSkipped = show_skipped
Packit 2035a7
        self._reset()
Packit 2035a7
Packit 2035a7
    def _reset(self):
Packit 2035a7
        self._rawMatchFunctions = []
Packit 2035a7
        self._matchFunctionCache = {}
Packit 2035a7
Packit 2035a7
    @staticmethod
Packit 2035a7
    def _generateCacheSignature(
Packit 2035a7
            pattern, endToken=None, varId=None, isFindMatch=False):
Packit 2035a7
        sig = pattern
Packit 2035a7
Packit 2035a7
        if endToken:
Packit 2035a7
            sig += '|ENDTOKEN'
Packit 2035a7
        else:
Packit 2035a7
            sig += '|NO-ENDTOKEN'
Packit 2035a7
Packit 2035a7
        if varId:
Packit 2035a7
            sig += '|VARID'
Packit 2035a7
        else:
Packit 2035a7
            sig += '|NO-VARID'
Packit 2035a7
Packit 2035a7
        if isFindMatch:
Packit 2035a7
            sig += '|ISFINDMATCH'
Packit 2035a7
        else:
Packit 2035a7
            sig += '|NORMALMATCH'
Packit 2035a7
Packit 2035a7
        return sig
Packit 2035a7
Packit 2035a7
    def _lookupMatchFunctionId(
Packit 2035a7
            self, pattern, endToken=None, varId=None, isFindMatch=False):
Packit 2035a7
        signature = self._generateCacheSignature(
Packit 2035a7
            pattern, endToken, varId, isFindMatch)
Packit 2035a7
Packit 2035a7
        if signature in self._matchFunctionCache:
Packit 2035a7
            return self._matchFunctionCache[signature]
Packit 2035a7
Packit 2035a7
        return None
Packit 2035a7
Packit 2035a7
    def _insertMatchFunctionId(
Packit 2035a7
            self, id, pattern, endToken=None, varId=None, isFindMatch=False):
Packit 2035a7
        signature = self._generateCacheSignature(
Packit 2035a7
            pattern, endToken, varId, isFindMatch)
Packit 2035a7
Packit 2035a7
        # function signature should not be in the cache
Packit 2035a7
        assert(
Packit 2035a7
            self._lookupMatchFunctionId(
Packit 2035a7
                pattern,
Packit 2035a7
                endToken,
Packit 2035a7
                varId,
Packit 2035a7
                isFindMatch) is None)
Packit 2035a7
Packit 2035a7
        self._matchFunctionCache[signature] = id
Packit 2035a7
Packit 2035a7
    @staticmethod
Packit 2035a7
    def _compileCmd(tok):
Packit 2035a7
        if tok == '%any%':
Packit 2035a7
            return 'true'
Packit 2035a7
        elif tok == '%assign%':
Packit 2035a7
            return 'tok->isAssignmentOp()'
Packit 2035a7
        elif tok == '%bool%':
Packit 2035a7
            return 'tok->isBoolean()'
Packit 2035a7
        elif tok == '%char%':
Packit 2035a7
            return '(tok->tokType()==Token::eChar)'
Packit 2035a7
        elif tok == '%comp%':
Packit 2035a7
            return 'tok->isComparisonOp()'
Packit 2035a7
        elif tok == '%num%':
Packit 2035a7
            return 'tok->isNumber()'
Packit 2035a7
        elif tok == '%cop%':
Packit 2035a7
            return 'tok->isConstOp()'
Packit 2035a7
        elif tok == '%op%':
Packit 2035a7
            return 'tok->isOp()'
Packit 2035a7
        elif tok == '%or%':
Packit 2035a7
            return '(tok->tokType() == Token::eBitOp && tok->str()==MatchCompiler::makeConstString("|") )'
Packit 2035a7
        elif tok == '%oror%':
Packit 2035a7
            return '(tok->tokType() == Token::eLogicalOp && tok->str()==MatchCompiler::makeConstString("||"))'
Packit 2035a7
        elif tok == '%str%':
Packit 2035a7
            return '(tok->tokType()==Token::eString)'
Packit 2035a7
        elif tok == '%type%':
Packit 2035a7
            return '(tok->isName() && tok->varId()==0U && !tok->isKeyword())'
Packit 2035a7
        elif tok == '%name%':
Packit 2035a7
            return 'tok->isName()'
Packit 2035a7
        elif tok == '%var%':
Packit 2035a7
            return '(tok->varId() != 0)'
Packit 2035a7
        elif tok == '%varid%':
Packit 2035a7
            return '(tok->isName() && tok->varId()==varid)'
Packit 2035a7
        elif (len(tok) > 2) and (tok[0] == "%"):
Packit 2035a7
            print("unhandled:" + tok)
Packit 2035a7
Packit 2035a7
        return (
Packit 2035a7
            '(tok->str()==MatchCompiler::makeConstString("' + tok + '"))'
Packit 2035a7
        )
Packit 2035a7
Packit 2035a7
    def _compilePattern(self, pattern, nr, varid,
Packit 2035a7
                        isFindMatch=False, tokenType="const Token"):
Packit 2035a7
        if isFindMatch:
Packit 2035a7
            ret = '\n    ' + tokenType + ' * tok = start_tok;\n'
Packit 2035a7
            returnStatement = 'continue;\n'
Packit 2035a7
        else:
Packit 2035a7
            arg2 = ''
Packit 2035a7
            if varid:
Packit 2035a7
                arg2 = ', const unsigned int varid'
Packit 2035a7
Packit 2035a7
            ret = '// pattern: ' + pattern + '\n'
Packit 2035a7
            ret += 'static bool match' + \
Packit 2035a7
                str(nr) + '(' + tokenType + '* tok' + arg2 + ') {\n'
Packit 2035a7
            returnStatement = 'return false;\n'
Packit 2035a7
Packit 2035a7
        tokens = pattern.split(' ')
Packit 2035a7
        gotoNextToken = ''
Packit 2035a7
        checked_varid = False
Packit 2035a7
        for tok in tokens:
Packit 2035a7
            if tok == '':
Packit 2035a7
                continue
Packit 2035a7
            ret += gotoNextToken
Packit 2035a7
            gotoNextToken = '    tok = tok->next();\n'
Packit 2035a7
Packit 2035a7
            # if varid is provided, check that it's non-zero on first use
Packit 2035a7
            if varid and '%varid%' in tok and not checked_varid:
Packit 2035a7
                ret += '    if (varid==0U)\n'
Packit 2035a7
                ret += '        throw InternalError(tok, "Internal error. Token::Match called with varid 0. ' +\
Packit 2035a7
                    'Please report this to Cppcheck developers");\n'
Packit 2035a7
                checked_varid = True
Packit 2035a7
Packit 2035a7
            # [abc]
Packit 2035a7
            if (len(tok) > 2) and (tok[0] == '[') and (tok[-1] == ']'):
Packit 2035a7
                ret += '    if (!tok || tok->str().size()!=1U || !strchr("' + tok[1:-1] + '", tok->str()[0]))\n'
Packit 2035a7
                ret += '        ' + returnStatement
Packit 2035a7
Packit 2035a7
            # a|b|c
Packit 2035a7
            elif tok.find('|') > 0:
Packit 2035a7
                tokens2 = tok.split('|')
Packit 2035a7
                logicalOp = ' || '
Packit 2035a7
                if "" in tokens2:
Packit 2035a7
                    ret += '    if (tok && ('
Packit 2035a7
                else:
Packit 2035a7
                    ret += '    if (!tok || !('
Packit 2035a7
                first = True
Packit 2035a7
                for tok2 in tokens2:
Packit 2035a7
                    if tok2 == '':
Packit 2035a7
                        continue
Packit 2035a7
                    if not first:
Packit 2035a7
                        ret += logicalOp
Packit 2035a7
                    first = False
Packit 2035a7
                    ret += self._compileCmd(tok2)
Packit 2035a7
Packit 2035a7
                ret += '))\n'
Packit 2035a7
                if "" in tokens2:
Packit 2035a7
                    ret += '        tok = tok->next();\n'
Packit 2035a7
                    gotoNextToken = ''
Packit 2035a7
                else:
Packit 2035a7
                    ret += '        ' + returnStatement
Packit 2035a7
Packit 2035a7
            # !!a
Packit 2035a7
            elif tok[0:2] == "!!":
Packit 2035a7
                ret += '    if (tok && tok->str() == MatchCompiler::makeConstString("' + tok[2:] + '"))\n'
Packit 2035a7
                ret += '        ' + returnStatement
Packit 2035a7
                gotoNextToken = '    tok = tok ? tok->next() : NULL;\n'
Packit 2035a7
Packit 2035a7
            else:
Packit 2035a7
                negatedTok = "!" + self._compileCmd(tok)
Packit 2035a7
                # fold !true => false ; !false => true
Packit 2035a7
                # this avoids cppcheck warnings about condition always being true/false
Packit 2035a7
                if negatedTok == "!false":
Packit 2035a7
                    negatedTok = "true"
Packit 2035a7
                elif negatedTok == "!true":
Packit 2035a7
                    negatedTok = "false"
Packit 2035a7
                ret += '    if (!tok || ' + negatedTok + ')\n'
Packit 2035a7
                ret += '        ' + returnStatement
Packit 2035a7
Packit 2035a7
        if isFindMatch:
Packit 2035a7
            ret += '    return start_tok;\n'
Packit 2035a7
        else:
Packit 2035a7
            ret += '    return true;\n'
Packit 2035a7
            ret += '}\n'
Packit 2035a7
Packit 2035a7
        return ret
Packit 2035a7
Packit 2035a7
    def _compileFindPattern(self, pattern, findmatchnr, endToken, varId):
Packit 2035a7
        more_args = ''
Packit 2035a7
        endCondition = ''
Packit 2035a7
        if endToken:
Packit 2035a7
            more_args += ', const Token * end'
Packit 2035a7
            endCondition = ' && start_tok != end'
Packit 2035a7
        if varId:
Packit 2035a7
            more_args += ', unsigned int varid'
Packit 2035a7
Packit 2035a7
        ret = '// pattern: ' + pattern + '\n'
Packit 2035a7
        ret += 'template<class T> static T * findmatch' + \
Packit 2035a7
            str(findmatchnr) + '(T * start_tok' + more_args + ') {\n'
Packit 2035a7
        ret += '    for (; start_tok' + endCondition + \
Packit 2035a7
            '; start_tok = start_tok->next()) {\n'
Packit 2035a7
Packit 2035a7
        ret += self._compilePattern(pattern, -1, varId, True, 'T')
Packit 2035a7
        ret += '    }\n'
Packit 2035a7
        ret += '    return NULL;\n}\n'
Packit 2035a7
Packit 2035a7
        return ret
Packit 2035a7
Packit 2035a7
    @staticmethod
Packit 2035a7
    def parseMatch(line, pos1):
Packit 2035a7
        parlevel = 0
Packit 2035a7
        args = []
Packit 2035a7
        argstart = 0
Packit 2035a7
        pos = pos1
Packit 2035a7
        inString = False
Packit 2035a7
        while pos < len(line):
Packit 2035a7
            if inString:
Packit 2035a7
                if line[pos] == '\\':
Packit 2035a7
                    pos += 1
Packit 2035a7
                elif line[pos] == '"':
Packit 2035a7
                    inString = False
Packit 2035a7
            elif line[pos] == '"':
Packit 2035a7
                inString = True
Packit 2035a7
            elif line[pos] == '(':
Packit 2035a7
                parlevel += 1
Packit 2035a7
                if parlevel == 1:
Packit 2035a7
                    argstart = pos + 1
Packit 2035a7
            elif line[pos] == ')':
Packit 2035a7
                parlevel -= 1
Packit 2035a7
                if parlevel == 0:
Packit 2035a7
                    ret = [line[pos1:pos + 1]]
Packit 2035a7
                    ret.extend(args)
Packit 2035a7
                    ret.append(line[argstart:pos])
Packit 2035a7
                    return ret
Packit 2035a7
            elif line[pos] == ',' and parlevel == 1:
Packit 2035a7
                args.append(line[argstart:pos])
Packit 2035a7
                argstart = pos + 1
Packit 2035a7
            pos += 1
Packit 2035a7
Packit 2035a7
        return None
Packit 2035a7
Packit 2035a7
    @staticmethod
Packit 2035a7
    def _isInString(line, pos1):
Packit 2035a7
        pos = 0
Packit 2035a7
        inString = False
Packit 2035a7
        while pos != pos1:
Packit 2035a7
            if line[pos] == '\\':
Packit 2035a7
                pos += 1
Packit 2035a7
            elif line[pos] == '"':
Packit 2035a7
                inString = not inString
Packit 2035a7
            pos += 1
Packit 2035a7
        return inString
Packit 2035a7
Packit 2035a7
    @staticmethod
Packit 2035a7
    def _parseStringComparison(line, pos1):
Packit 2035a7
        startPos = 0
Packit 2035a7
        pos = pos1
Packit 2035a7
        inString = False
Packit 2035a7
        while pos < len(line):
Packit 2035a7
            if inString:
Packit 2035a7
                if line[pos] == '\\':
Packit 2035a7
                    pos += 1
Packit 2035a7
                elif line[pos] == '"':
Packit 2035a7
                    inString = False
Packit 2035a7
                    endPos = pos + 1
Packit 2035a7
                    return startPos, endPos
Packit 2035a7
            elif line[pos] == '"':
Packit 2035a7
                startPos = pos
Packit 2035a7
                inString = True
Packit 2035a7
            pos += 1
Packit 2035a7
Packit 2035a7
        return None
Packit 2035a7
Packit 2035a7
    @staticmethod
Packit 2035a7
    def _compileVerifyTokenMatch(
Packit 2035a7
            is_simplematch, verifyNumber, pattern, patternNumber, varId):
Packit 2035a7
        more_args = ''
Packit 2035a7
        if varId:
Packit 2035a7
            more_args = ', const unsigned int varid'
Packit 2035a7
Packit 2035a7
        ret = 'static bool match_verify' + \
Packit 2035a7
            str(verifyNumber) + '(const Token *tok' + more_args + ') {\n'
Packit 2035a7
Packit 2035a7
        origMatchName = 'Match'
Packit 2035a7
        if is_simplematch:
Packit 2035a7
            origMatchName = 'simpleMatch'
Packit 2035a7
            assert(varId is None)
Packit 2035a7
Packit 2035a7
        ret += '    bool res_compiled_match = match' + \
Packit 2035a7
            str(patternNumber) + '(tok'
Packit 2035a7
        if varId:
Packit 2035a7
            ret += ', varid'
Packit 2035a7
        ret += ');\n'
Packit 2035a7
Packit 2035a7
        ret += '    bool res_parsed_match = Token::' + \
Packit 2035a7
            origMatchName + '(tok, "' + pattern + '"'
Packit 2035a7
        if varId:
Packit 2035a7
            ret += ', varid'
Packit 2035a7
        ret += ');\n'
Packit 2035a7
Packit 2035a7
        ret += '\n'
Packit 2035a7
        # Don't use assert() here, it's disabled for optimized builds.
Packit 2035a7
        # We also need to verify builds in 'release' mode
Packit 2035a7
        ret += '    if (res_parsed_match != res_compiled_match) {\n'
Packit 2035a7
        # ret += '        std::cout << "res_parsed_match' + str(verifyNumber) +\
Packit 2035a7
        #     ': " << res_parsed_match << ", res_compiled_match: " << res_compiled_match << "\\n";\n'
Packit 2035a7
        # ret += '        if (tok)\n'
Packit 2035a7
        # ret += '            std::cout << "tok: " << tok->str();\n'
Packit 2035a7
        # ret += '        if (tok->next())\n'
Packit 2035a7
        # ret += '            std::cout << "tok next: " << tok->next()->str();\n'
Packit 2035a7
        ret += '        throw InternalError(tok, "Internal error.' +\
Packit 2035a7
            'compiled match returned different result than parsed match: ' + pattern + '");\n'
Packit 2035a7
        ret += '    }\n'
Packit 2035a7
        ret += '    return res_compiled_match;\n'
Packit 2035a7
        ret += '}\n'
Packit 2035a7
Packit 2035a7
        return ret
Packit 2035a7
Packit 2035a7
    def _replaceSpecificTokenMatch(
Packit 2035a7
            self, is_simplematch, line, start_pos, end_pos, pattern, tok, varId):
Packit 2035a7
        more_args = ''
Packit 2035a7
        if varId:
Packit 2035a7
            more_args = ',' + varId
Packit 2035a7
Packit 2035a7
        # Compile function or use previously compiled one
Packit 2035a7
        patternNumber = self._lookupMatchFunctionId(
Packit 2035a7
            pattern, None, varId, False)
Packit 2035a7
Packit 2035a7
        if patternNumber is None:
Packit 2035a7
            patternNumber = len(self._rawMatchFunctions) + 1
Packit 2035a7
            self._insertMatchFunctionId(
Packit 2035a7
                patternNumber,
Packit 2035a7
                pattern,
Packit 2035a7
                None,
Packit 2035a7
                varId,
Packit 2035a7
                False)
Packit 2035a7
            self._rawMatchFunctions.append(
Packit 2035a7
                self._compilePattern(pattern, patternNumber, varId))
Packit 2035a7
Packit 2035a7
        functionName = "match"
Packit 2035a7
        if self._verifyMode:
Packit 2035a7
            verifyNumber = len(self._rawMatchFunctions) + 1
Packit 2035a7
            self._rawMatchFunctions.append(
Packit 2035a7
                self._compileVerifyTokenMatch(
Packit 2035a7
                    is_simplematch,
Packit 2035a7
                    verifyNumber,
Packit 2035a7
                    pattern,
Packit 2035a7
                    patternNumber,
Packit 2035a7
                    varId))
Packit 2035a7
Packit 2035a7
            # inject verify function
Packit 2035a7
            functionName = "match_verify"
Packit 2035a7
            patternNumber = verifyNumber
Packit 2035a7
Packit 2035a7
        return (
Packit 2035a7
            line[:start_pos] + functionName + str(
Packit 2035a7
                patternNumber) + '(' + tok + more_args + ')' + line[start_pos + end_pos:]
Packit 2035a7
        )
Packit 2035a7
Packit 2035a7
    def _replaceTokenMatch(self, line, linenr, filename):
Packit 2035a7
        while True:
Packit 2035a7
            is_simplematch = False
Packit 2035a7
            pos1 = line.find('Token::Match(')
Packit 2035a7
            if pos1 == -1:
Packit 2035a7
                is_simplematch = True
Packit 2035a7
                pos1 = line.find('Token::simpleMatch(')
Packit 2035a7
            if pos1 == -1:
Packit 2035a7
                break
Packit 2035a7
Packit 2035a7
            res = self.parseMatch(line, pos1)
Packit 2035a7
            if res is None:
Packit 2035a7
                break
Packit 2035a7
Packit 2035a7
            # assert that Token::Match has either 2 or 3 arguments
Packit 2035a7
            assert(len(res) == 3 or len(res) == 4)
Packit 2035a7
Packit 2035a7
            end_pos = len(res[0])
Packit 2035a7
            tok = res[1]
Packit 2035a7
            raw_pattern = res[2]
Packit 2035a7
            varId = None
Packit 2035a7
            if len(res) == 4:
Packit 2035a7
                varId = res[3]
Packit 2035a7
Packit 2035a7
            res = re.match(r'\s*"((?:.|\\")*?)"\s*$', raw_pattern)
Packit 2035a7
            if res is None:
Packit 2035a7
                if self._showSkipped:
Packit 2035a7
                    print(filename + ":" + str(linenr) + " skipping match pattern:" + raw_pattern)
Packit 2035a7
                break  # Non-const pattern - bailout
Packit 2035a7
Packit 2035a7
            pattern = res.group(1)
Packit 2035a7
            line = self._replaceSpecificTokenMatch(
Packit 2035a7
                is_simplematch,
Packit 2035a7
                line,
Packit 2035a7
                pos1,
Packit 2035a7
                end_pos,
Packit 2035a7
                pattern,
Packit 2035a7
                tok,
Packit 2035a7
                varId)
Packit 2035a7
Packit 2035a7
        return line
Packit 2035a7
Packit 2035a7
    @staticmethod
Packit 2035a7
    def _compileVerifyTokenFindMatch(
Packit 2035a7
            is_findsimplematch, verifyNumber, pattern, patternNumber, endToken, varId):
Packit 2035a7
        more_args = ''
Packit 2035a7
        if endToken:
Packit 2035a7
            more_args += ', const Token * endToken'
Packit 2035a7
        if varId:
Packit 2035a7
            more_args += ', const unsigned int varid'
Packit 2035a7
Packit 2035a7
        ret = 'template < class T > static T * findmatch_verify' + \
Packit 2035a7
            str(verifyNumber) + '(T * tok' + more_args + ') {\n'
Packit 2035a7
Packit 2035a7
        origFindMatchName = 'findmatch'
Packit 2035a7
        if is_findsimplematch:
Packit 2035a7
            origFindMatchName = 'findsimplematch'
Packit 2035a7
            assert(varId is None)
Packit 2035a7
Packit 2035a7
        ret += '    T * res_compiled_findmatch = findmatch' + \
Packit 2035a7
            str(patternNumber) + '(tok'
Packit 2035a7
        if endToken:
Packit 2035a7
            ret += ', endToken'
Packit 2035a7
        if varId:
Packit 2035a7
            ret += ', varid'
Packit 2035a7
        ret += ');\n'
Packit 2035a7
Packit 2035a7
        ret += '    T * res_parsed_findmatch = Token::' + \
Packit 2035a7
            origFindMatchName + '(tok, "' + pattern + '"'
Packit 2035a7
        if endToken:
Packit 2035a7
            ret += ', endToken'
Packit 2035a7
        if varId:
Packit 2035a7
            ret += ', varid'
Packit 2035a7
        ret += ');\n'
Packit 2035a7
Packit 2035a7
        ret += '\n'
Packit 2035a7
        # Don't use assert() here, it's disabled for optimized builds.
Packit 2035a7
        # We also need to verify builds in 'release' mode
Packit 2035a7
        ret += '    if (res_parsed_findmatch != res_compiled_findmatch) {\n'
Packit 2035a7
        ret += '        throw InternalError(tok, "Internal error. ' +\
Packit 2035a7
            'compiled findmatch returned different result than parsed findmatch: ' + pattern + '");\n'
Packit 2035a7
        ret += '    }\n'
Packit 2035a7
        ret += '    return res_compiled_findmatch;\n'
Packit 2035a7
        ret += '}\n'
Packit 2035a7
Packit 2035a7
        return ret
Packit 2035a7
Packit 2035a7
    def _replaceSpecificFindTokenMatch(
Packit 2035a7
            self, is_findsimplematch, line, start_pos, end_pos, pattern, tok, endToken, varId):
Packit 2035a7
        more_args = ''
Packit 2035a7
        if endToken:
Packit 2035a7
            more_args += ',' + endToken
Packit 2035a7
        if varId:
Packit 2035a7
            more_args += ',' + varId
Packit 2035a7
Packit 2035a7
        # Compile function or use previously compiled one
Packit 2035a7
        findMatchNumber = self._lookupMatchFunctionId(
Packit 2035a7
            pattern, endToken, varId, True)
Packit 2035a7
Packit 2035a7
        if findMatchNumber is None:
Packit 2035a7
            findMatchNumber = len(self._rawMatchFunctions) + 1
Packit 2035a7
            self._insertMatchFunctionId(
Packit 2035a7
                findMatchNumber,
Packit 2035a7
                pattern,
Packit 2035a7
                endToken,
Packit 2035a7
                varId,
Packit 2035a7
                True)
Packit 2035a7
            self._rawMatchFunctions.append(
Packit 2035a7
                self._compileFindPattern(
Packit 2035a7
                    pattern,
Packit 2035a7
                    findMatchNumber,
Packit 2035a7
                    endToken,
Packit 2035a7
                    varId))
Packit 2035a7
Packit 2035a7
        functionName = "findmatch"
Packit 2035a7
        if self._verifyMode:
Packit 2035a7
            verifyNumber = len(self._rawMatchFunctions) + 1
Packit 2035a7
            self._rawMatchFunctions.append(
Packit 2035a7
                self._compileVerifyTokenFindMatch(
Packit 2035a7
                    is_findsimplematch,
Packit 2035a7
                    verifyNumber,
Packit 2035a7
                    pattern,
Packit 2035a7
                    findMatchNumber,
Packit 2035a7
                    endToken,
Packit 2035a7
                    varId))
Packit 2035a7
Packit 2035a7
            # inject verify function
Packit 2035a7
            functionName = "findmatch_verify"
Packit 2035a7
            findMatchNumber = verifyNumber
Packit 2035a7
Packit 2035a7
        return (
Packit 2035a7
            line[:start_pos] + functionName + str(
Packit 2035a7
                findMatchNumber) + '(' + tok + more_args + ') ' + line[start_pos + end_pos:]
Packit 2035a7
        )
Packit 2035a7
Packit 2035a7
    def _replaceTokenFindMatch(self, line, linenr, filename):
Packit 2035a7
        while True:
Packit 2035a7
            is_findsimplematch = True
Packit 2035a7
            pos1 = line.find('Token::findsimplematch(')
Packit 2035a7
            if pos1 == -1:
Packit 2035a7
                is_findsimplematch = False
Packit 2035a7
                pos1 = line.find('Token::findmatch(')
Packit 2035a7
            if pos1 == -1:
Packit 2035a7
                break
Packit 2035a7
Packit 2035a7
            res = self.parseMatch(line, pos1)
Packit 2035a7
            if res is None:
Packit 2035a7
                break
Packit 2035a7
Packit 2035a7
            # assert that Token::find(simple)match has either 2, 3 or 4 arguments
Packit 2035a7
            assert(len(res) >= 3 or len(res) < 6)
Packit 2035a7
Packit 2035a7
            g0 = res[0]
Packit 2035a7
            tok = res[1]
Packit 2035a7
            pattern = res[2]
Packit 2035a7
Packit 2035a7
            # Check for varId
Packit 2035a7
            varId = None
Packit 2035a7
            if not is_findsimplematch and "%varid%" in g0:
Packit 2035a7
                if len(res) == 5:
Packit 2035a7
                    varId = res[4]
Packit 2035a7
                else:
Packit 2035a7
                    varId = res[3]
Packit 2035a7
Packit 2035a7
            # endToken support. We resolve the overloaded type by checking if varId is used or not.
Packit 2035a7
            # Function prototypes:
Packit 2035a7
            #     Token *findsimplematch(const Token *tok, const char pattern[]);
Packit 2035a7
            #     Token *findsimplematch(const Token *tok, const char pattern[], const Token *end);
Packit 2035a7
            #     Token *findmatch(const Token *tok, const char pattern[], unsigned int varId = 0);
Packit 2035a7
            # Token *findmatch(const Token *tok, const char pattern[], const
Packit 2035a7
            # Token *end, unsigned int varId = 0);
Packit 2035a7
            endToken = None
Packit 2035a7
            if ((is_findsimplematch and len(res) == 4) or
Packit 2035a7
               (not is_findsimplematch and varId and (len(res) == 5)) or
Packit 2035a7
               (not is_findsimplematch and varId is None and len(res) == 4)):
Packit 2035a7
                endToken = res[3]
Packit 2035a7
Packit 2035a7
            res = re.match(r'\s*"((?:.|\\")*?)"\s*$', pattern)
Packit 2035a7
            if res is None:
Packit 2035a7
                if self._showSkipped:
Packit 2035a7
                    print(filename + ":" + str(linenr) + " skipping findmatch pattern:" + pattern)
Packit 2035a7
                break  # Non-const pattern - bailout
Packit 2035a7
Packit 2035a7
            pattern = res.group(1)
Packit 2035a7
            line = self._replaceSpecificFindTokenMatch(
Packit 2035a7
                is_findsimplematch,
Packit 2035a7
                line,
Packit 2035a7
                pos1,
Packit 2035a7
                len(g0),
Packit 2035a7
                pattern,
Packit 2035a7
                tok,
Packit 2035a7
                endToken,
Packit 2035a7
                varId)
Packit 2035a7
Packit 2035a7
        return line
Packit 2035a7
Packit 2035a7
    def _replaceCStrings(self, line):
Packit 2035a7
        while True:
Packit 2035a7
            match = re.search('(==|!=) *"', line)
Packit 2035a7
            if not match:
Packit 2035a7
                break
Packit 2035a7
Packit 2035a7
            if self._isInString(line, match.start()):
Packit 2035a7
                break
Packit 2035a7
Packit 2035a7
            res = self._parseStringComparison(line, match.start())
Packit 2035a7
            if res is None:
Packit 2035a7
                break
Packit 2035a7
Packit 2035a7
            startPos = res[0]
Packit 2035a7
            endPos = res[1]
Packit 2035a7
            text = line[startPos + 1:endPos - 1]
Packit 2035a7
            line = line[:startPos] + 'MatchCompiler::makeConstStringBegin' +\
Packit 2035a7
                text + 'MatchCompiler::makeConstStringEnd' + line[endPos:]
Packit 2035a7
        line = line.replace('MatchCompiler::makeConstStringBegin', 'MatchCompiler::makeConstString("')
Packit 2035a7
        line = line.replace('MatchCompiler::makeConstStringEnd', '")')
Packit 2035a7
        return line
Packit 2035a7
Packit 2035a7
    def convertFile(self, srcname, destname, line_directive):
Packit 2035a7
        self._reset()
Packit 2035a7
Packit 2035a7
        fin = io.open(srcname, "rt", encoding="utf-8")
Packit 2035a7
        srclines = fin.readlines()
Packit 2035a7
        fin.close()
Packit 2035a7
Packit 2035a7
        header = '#include "token.h"\n'
Packit 2035a7
        header += '#include "errorlogger.h"\n'
Packit 2035a7
        header += '#include "matchcompiler.h"\n'
Packit 2035a7
        header += '#include <string>\n'
Packit 2035a7
        header += '#include <cstring>\n'
Packit 2035a7
        # header += '#include <iostream>\n'
Packit 2035a7
        code = ''
Packit 2035a7
Packit 2035a7
        linenr = 0
Packit 2035a7
        for line in srclines:
Packit 2035a7
            linenr += 1
Packit 2035a7
            # Compile Token::Match and Token::simpleMatch
Packit 2035a7
            line = self._replaceTokenMatch(line, linenr, srcname)
Packit 2035a7
Packit 2035a7
            # Compile Token::findsimplematch
Packit 2035a7
            line = self._replaceTokenFindMatch(line, linenr, srcname)
Packit 2035a7
Packit 2035a7
            # Cache plain C-strings in C++ strings
Packit 2035a7
            line = self._replaceCStrings(line)
Packit 2035a7
Packit 2035a7
            code += line
Packit 2035a7
Packit 2035a7
        # Compute matchFunctions
Packit 2035a7
        strFunctions = ''
Packit 2035a7
        for function in self._rawMatchFunctions:
Packit 2035a7
            strFunctions += function
Packit 2035a7
Packit 2035a7
        lineno = ''
Packit 2035a7
        if line_directive:
Packit 2035a7
            lineno = '#line 1 "' + srcname + '"\n'
Packit 2035a7
Packit 2035a7
        fout = io.open(destname, 'wt', encoding="utf-8")
Packit 2035a7
        fout.write(header + strFunctions + lineno + code)
Packit 2035a7
        fout.close()
Packit 2035a7
Packit 2035a7
Packit 2035a7
def main():
Packit 2035a7
    # Main program
Packit 2035a7
Packit 2035a7
    # Argument handling
Packit 2035a7
    parser = argparse.ArgumentParser(
Packit 2035a7
        description='Compile Token::Match() calls into native C++ code')
Packit 2035a7
    parser.add_argument('--verify', action='store_true', default=False,
Packit 2035a7
                        help='verify compiled matches against on-the-fly parser. Slow!')
Packit 2035a7
    parser.add_argument('--show-skipped', action='store_true', default=False,
Packit 2035a7
                        help='show skipped (non-static) patterns')
Packit 2035a7
    parser.add_argument('--read-dir', default="lib",
Packit 2035a7
                        help='directory from which files are read')
Packit 2035a7
    parser.add_argument('--write-dir', default="build",
Packit 2035a7
                        help='directory into which files are written')
Packit 2035a7
    parser.add_argument('--prefix', default="",
Packit 2035a7
                        help='prefix for build files')
Packit 2035a7
    parser.add_argument('--line', action='store_true', default=False,
Packit 2035a7
                        help='add line directive to input files into build files')
Packit 2035a7
    parser.add_argument('file', nargs='*',
Packit 2035a7
                        help='file to complile')
Packit 2035a7
    args = parser.parse_args()
Packit 2035a7
    lib_dir = args.read_dir
Packit 2035a7
    build_dir = args.write_dir
Packit 2035a7
    line_directive = args.line
Packit 2035a7
    files = args.file
Packit 2035a7
Packit 2035a7
    # Check if we are invoked from the right place
Packit 2035a7
    if not os.path.exists(lib_dir):
Packit 2035a7
        print('Directory "' + lib_dir + '"not found.')
Packit 2035a7
        sys.exit(-1)
Packit 2035a7
Packit 2035a7
    # Create build directory if needed
Packit 2035a7
    try:
Packit 2035a7
        os.makedirs(build_dir)
Packit 2035a7
    except OSError as e:
Packit 2035a7
        # due to race condition in case of parallel build,
Packit 2035a7
        # makedirs may fail. Ignore that; if there's actual
Packit 2035a7
        # problem with directory creation, it'll be caught
Packit 2035a7
        # by the following isdir check
Packit 2035a7
        if e.errno != errno.EEXIST:
Packit 2035a7
            raise
Packit 2035a7
Packit 2035a7
    if not os.path.isdir(build_dir):
Packit 2035a7
        raise Exception(build_dir + ' is not a directory')
Packit 2035a7
Packit 2035a7
    mc = MatchCompiler(verify_mode=args.verify,
Packit 2035a7
                       show_skipped=args.show_skipped)
Packit 2035a7
Packit 2035a7
    if not files:
Packit 2035a7
        # select all *.cpp files in lib_dir
Packit 2035a7
        for f in glob.glob(lib_dir + '/*.cpp'):
Packit 2035a7
            files.append(f[len(lib_dir) + 1:])
Packit 2035a7
Packit 2035a7
    # convert files
Packit 2035a7
    for fi in files:
Packit 2035a7
        pi = lib_dir + '/' + fi
Packit 2035a7
        fo = args.prefix + fi
Packit 2035a7
        po = build_dir + '/' + fo
Packit 2035a7
        print(pi + ' => ' + po)
Packit 2035a7
        mc.convertFile(pi, po, line_directive)
Packit 2035a7
Packit 2035a7
if __name__ == '__main__':
Packit 2035a7
    main()