一次报全部

👁️ 1 人浏览 💬 0 人评论 ❤️ 添加收藏

(每道题开头都有同一段:上一站的 parser(多记了位置)、show(),和错误类 SemError——错误写成一行「原因@行:列」。)

五句话里四处错。check_all 逐句检查、记下错误、继续;report 按位置排序汇总:

KEYWORDS = ("let", "if", "else", "while")
CMP = (">=", "<=", "==", "!=", "<", ">")

def T(src):
    toks = []
    for ln, line in enumerate(src.split("\n"), 1):
        pos = 0
        for w in line.split():
            pos = line.index(w, pos)
            if w in KEYWORDS:
                k = "KEYWORD"
            elif w[0] == '"':
                k = "STRING"
            elif w.isdigit():
                k = "NUMBER"
            elif w[0].isalpha() or w[0] == "_":
                k = "IDENT"
            else:
                k = "OP"
            toks.append((k, w, ln, pos + 1))
            pos += len(w)
    toks.append(("EOF", "", ln, pos + 1))
    return toks


class ParseError(Exception):
    def __init__(self, want, got, line, col):
        super().__init__("期待「" + want + "」遇到「" + got + "」@" + str(line) + ":" + str(col))


class Parser:
    def __init__(self, toks):
        self.toks, self.i = toks, 0

    def peek(self):
        return self.toks[self.i]

    def advance(self):
        self.i += 1
        return self.toks[self.i - 1]

    def at(self, text):
        return self.peek()[1] == text

    def expect(self, text):
        t = self.peek()
        if t[1] != text:
            raise ParseError(text, t[1] or "文件结尾", t[2], t[3])
        return self.advance()

    def expect_kind(self, kind):
        t = self.peek()
        if t[0] != kind:
            raise ParseError(kind, t[1] or "文件结尾", t[2], t[3])
        return self.advance()

    def program(self):
        stmts = []
        while self.peek()[0] != "EOF":
            stmts.append(self.stmt())
        return ("program", stmts)

    def stmt(self):
        t = self.peek()
        if self.at("let"):
            self.advance()
            name = self.expect_kind("IDENT")
            self.expect("=")
            e = self.expr()
            self.expect(";")
            return ("let", name[1], e, name[2], name[3])
        if self.at("if"):
            self.advance()
            self.expect("(")
            c = self.expr()
            self.expect(")")
            body = self.block()
            alt = None
            if self.at("else"):
                self.advance()
                alt = self.block()
            return ("if", c, body, alt, t[2], t[3])
        if self.at("while"):
            self.advance()
            self.expect("(")
            c = self.expr()
            self.expect(")")
            return ("while", c, self.block(), t[2], t[3])
        name = self.expect_kind("IDENT")
        self.expect("=")
        e = self.expr()
        self.expect(";")
        return ("assign", name[1], e, name[2], name[3])

    def block(self):
        self.expect("{")
        stmts = []
        while not self.at("}"):
            if self.peek()[0] == "EOF":
                self.expect("}")
            stmts.append(self.stmt())
        self.expect("}")
        return stmts

    def expr(self):
        left = self.sum_()
        if self.peek()[1] in CMP:
            t = self.advance()
            return ("bin", t[1], left, self.sum_(), t[2], t[3])
        return left

    def sum_(self):
        left = self.term()
        while self.peek()[1] in ("+", "-"):
            t = self.advance()
            left = ("bin", t[1], left, self.term(), t[2], t[3])
        return left

    def term(self):
        left = self.factor()
        while self.peek()[1] in ("*", "/"):
            t = self.advance()
            left = ("bin", t[1], left, self.factor(), t[2], t[3])
        return left

    def factor(self):
        t = self.peek()
        if t[0] == "NUMBER":
            self.advance()
            return ("num", int(t[1]))
        if t[0] == "STRING":
            self.advance()
            return ("str", t[1][1:-1])
        if t[0] == "IDENT":
            self.advance()
            return ("var", t[1], t[2], t[3])
        if t[1] == "(":
            self.advance()
            e = self.expr()
            self.expect(")")
            return e
        raise ParseError("表达式", t[1] or "文件结尾", t[2], t[3])


def parse(src):
    return Parser(T(src)).program()


def show(n):
    if n[0] == "num" or n[0] == "var":
        return str(n[1])
    if n[0] == "str":
        return '"' + n[1] + '"'
    if n[0] == "bin":
        return "(" + show(n[2]) + n[1] + show(n[3]) + ")"
    if n[0] == "let":
        return "let:" + n[1] + "=" + show(n[2])
    if n[0] == "assign":
        return n[1] + "=" + show(n[2])
    if n[0] == "if":
        s = "if[" + show(n[1]) + "]{" + ";".join(show(x) for x in n[2]) + "}"
        if n[3] is not None:
            s += "else{" + ";".join(show(x) for x in n[3]) + "}"
        return s
    if n[0] == "while":
        return "while[" + show(n[1]) + "]{" + ";".join(show(x) for x in n[2]) + "}"
    if n[0] == "program":
        return "|".join(show(x) for x in n[1])
    return "?"


class SemError(Exception):
    def __init__(self, msg, line, col):
        super().__init__(msg + "@" + str(line) + ":" + str(col))
        self.msg, self.line, self.col = msg, line, col


class Scope:
    def __init__(self, parent=None):
        self.vars = {}
        self.parent = parent

    def declare(self, name, typ, line, col):
        if name in self.vars:
            raise SemError("重复声明「" + name + "」", line, col)
        self.vars[name] = typ

    def lookup(self, name):
        s = self
        while s is not None:
            if name in s.vars:
                return s.vars[name]
            s = s.parent
        return None


def type_of(n, scope):
    if n[0] == "num":
        return "num"
    if n[0] == "str":
        return "str"
    if n[0] == "var":
        t = scope.lookup(n[1])
        if t is None:
            raise SemError("未定义「" + n[1] + "」", n[2], n[3])
        return t
    op, l, r = n[1], type_of(n[2], scope), type_of(n[3], scope)
    if l != r:
        raise SemError("类型不匹配「" + l + "」「" + r + "」", n[4], n[5])
    if op in CMP:
        return "bool"
    if op == "+" and l == "str":
        return "str"
    if l != "num":
        raise SemError("类型不匹配「" + l + "」「" + r + "」", n[4], n[5])
    return "num"


def check_stmt(n, scope):
    if n[0] == "let":
        scope.declare(n[1], type_of(n[2], scope), n[3], n[4])
    elif n[0] == "assign":
        t = scope.lookup(n[1])
        if t is None:
            raise SemError("未定义「" + n[1] + "」", n[3], n[4])
        e = type_of(n[2], scope)
        if e != t:
            raise SemError("类型不匹配「" + t + "」「" + e + "」", n[3], n[4])
    elif n[0] == "if":
        c = type_of(n[1], scope)
        if c != "bool":
            raise SemError("条件不是bool「" + c + "」", n[4], n[5])
        check_block(n[2], Scope(scope))
        if n[3] is not None:
            check_block(n[3], Scope(scope))
    elif n[0] == "while":
        c = type_of(n[1], scope)
        if c != "bool":
            raise SemError("条件不是bool「" + c + "」", n[3], n[4])
        check_block(n[2], Scope(scope))


def check_block(stmts, scope):
    for s in stmts:
        check_stmt(s, scope)


def check(program):
    scope = Scope()
    check_block(program[1], scope)
    return scope


def table(scope):
    return ",".join(k + ":" + v for k, v in scope.vars.items())


def check_all(program):
    scope = Scope()
    errors = []
    for s in program[1]:
        try:
            check_stmt(s, scope)
        except SemError as e:
            errors.append(e)
    return scope, errors


def report(errors):
    es = sorted(errors, key=lambda e: (e.line, e.col))
    return str(len(es)) + "|" + "|".join(str(e.line) + ":" + str(e.col) + ":" + e.msg for e in es)

FIVE = 'let x = y ;\nlet x = 1 ;\nlet x = 2 ;\nz = 3 ;\nlet s = "a" + 1 ;'
sc, errs = check_all(parse(FIVE))
print(report(errs))
提交你的答案
请登录后提交答案。
去登录
代码编辑器
Ctrl + Enter 运行
本次输入:
输出:

                        
👩‍🏫
AI
💬 题目评论

全部评论