python 50pts
查看原帖
python 50pts
315554
lusers楼主2024/1/29 14:52
import ast
from operator import eq
import shlex
import sys
import dataclasses
from typing import Any, cast
import uuid

@dataclasses.dataclass
class Variable:
    name: str
    requirements: list[str]

def seqmatch(sadi: list[Any], maso: list[Any]):
    if len(maso) > len(sadi):
        return False
    return all(map(lambda x: eq(*x), zip(sadi, maso)))

# debug = print
def debug(*args, **kwargs):
    pass

def parser(prog: str):
    PUNC = '+-*/<=>;'
    prog = ';\n'.join(prog.splitlines())
    lex = shlex.shlex(prog, punctuation_chars=PUNC)
    lex.whitespace_split = True
    # print(repr(list(lex)))

    variables: set[str] = set()
    reqvar: dict[str, list[tuple[uuid.UUID, bool]]] = {}
    requirements: list[tuple[uuid.UUID, bool]] = []
    unreachable: list[bool] = [False]
    cutbranch: set[uuid.UUID] = set()
    leakable: dict[uuid.UUID, tuple[set[str], set[str]]] = {}
    line = 1

    def isVisible(name: str):
        if name not in variables:
            return False

        req = reqvar[name]
        return seqmatch(requirements, req)
    
    def checkVar(name: str):
        if unreachable[-1]:
            return False
        if name.isdigit():
            return False
        if name in lex.punctuation_chars:
            return False
        if not isVisible(name):
            return True
        return False
    
    def printvars(names: list[str]):
        for name in names:
            print(f'Line {line}: variable {name} might not have been initialized')
    
    def reportUnreachable():
        if unreachable[-1]:
            print(f'Line {line}: unreachable code')
        return unreachable[-1]
    
    def leakVar(lhs: str):
        if len(reqvar[lhs]) > 1:
            leakable.setdefault(reqvar[lhs][-2][0], (set(), set()))
            leakable[reqvar[lhs][-2][0]][reqvar[lhs][-2][1]].add(lhs)
            reqvar[lhs].pop()
            if lhs in leakable[reqvar[lhs][-1][0]][not reqvar[lhs][-1][1]]:
                debug(f'Leak (leakable) {lhs} to {reqvar[lhs][:-1]}')
                leakVar(lhs)
        else:
            reqvar[lhs].pop()
    
    def addVar(lhs: str):
        if lhs in variables and len(reqvar[lhs]) == len(requirements) and reqvar[lhs][-1][0] == requirements[-1][0]:
            debug(f'Leak (double declare) {lhs} to {requirements[:-1]} overriding {reqvar.get(lhs)}')
            leakVar(lhs)
        elif not isVisible(lhs):
            if requirements and requirements[-1] in cutbranch:
                debug(f'Leakly declare {lhs} in {requirements[:-1]} over {requirements}')
                variables.add(lhs)
                reqvar[lhs] = requirements[:-1]
                if reqvar[lhs]:
                    leakable.setdefault(reqvar[lhs][-1][0], (set(), set()))
                    leakable[reqvar[lhs][-1][0]][reqvar[lhs][-1][1]].add(lhs)
            else:
                debug(f'Declare {lhs} in {requirements} overriding {reqvar.get(lhs)}')
                variables.add(lhs)
                reqvar[lhs] = requirements.copy()
                if reqvar[lhs]:
                    leakable.setdefault(reqvar[lhs][-1][0], (set(), set()))
                    leakable[reqvar[lhs][-1][0]][reqvar[lhs][-1][1]].add(lhs)
        else:
            debug(f'Skip {lhs} in {requirements} overriding {reqvar.get(lhs)}')

    while True:
        tok = lex.get_token()
        if not tok:
            break
        
        match tok:
            case 'PARAM':
                while True:
                    lhs = lex.get_token()
                    if not lhs or lhs == ';':
                        if lhs:
                            lex.push_token(lhs)
                        break

                    addVar(lhs)
            case 'IF':
                mute = reportUnreachable()
                reported: set[str] = set()
                while True:
                    tok2 = lex.get_token()
                    if not tok2 or tok2 == 'THEN':
                        break
                    if tok2 in reported or checkVar(tok2):
                        reported.add(tok2)
                requirements.append((uuid.uuid4(), False))
                unreachable.append(False)
                if not mute:
                    printvars(list(sorted(reported)))
                debug(f'Enter IF scope {requirements[-1]}')
            case ';':
                line += 1
            case 'ELSE':
                debug(f'Exit IF scope {requirements[-1]}')
                requirements.append((cast(tuple[uuid.UUID, uuid.UUID], requirements.pop())[0], True))
                debug(f'Enter ELSE scope {requirements[-1]}')
                unreachable.pop()
                unreachable.append(False)
            case 'END':
                debug(f'Exit scope {requirements[-1]}')
                lex.get_token()
                requirements.pop()
                unreachable.pop()
            case 'RETURN':
                mute = reportUnreachable()
                debug(f'Return at {requirements[-1] if requirements else None}')
                lhs = lex.get_token()
                if not lhs:
                    break
                if not mute and checkVar(lhs):
                    printvars([lhs])
                if requirements:
                    cutbranch.add(requirements[-1][0])
                    for name, req in reqvar.items():
                        if req and req[-1][0] == requirements[-1][0]:
                            debug(f'Leak (cut branch) {name} to {req[:-1]} overridng {req}')
                            leakVar(name)
                unreachable[-1] = True
            case lhs:
                dry = reportUnreachable()
                op = lex.get_token()
                if not op:
                    break
                assert op in lex.punctuation_chars
                reported: set[str] = set()
                while True:
                    tok2 = lex.get_token()
                    if not tok2 or tok2 == ';':
                        if tok2:
                            lex.push_token(tok2)
                        break
                    if tok2 in reported or checkVar(tok2):
                        reported.add(tok2)

                if not dry:
                    printvars(list(reported))
                    addVar(lhs)


prog = sys.stdin.read()

parser(prog)
2024/1/29 14:52
加载中...