/** * @file Parser.cpp * @brief ST 子集递归下降语法分析 * @author * @date 2026-08-21 * * @details 设计说明(文法详见 Doc/compiler/语法.md): * - 输入:lexer 产出的 token 流(含末尾 END);输出:Unit(顶层全局段 + POU 列表) * - 递归下降 + 每层一个函数,token 流只前移不回溯(FB 调用/赋值用一 token 前瞻区分) * - 表达式按优先级爬升:or → and → not → cmp → add → mul → unary → primary, * AND/OR 的短路语义保留在 AST(12.8 编成跳转) * - 错误:稳定前缀 "syntax error" + 当前 token 的文件与行列 * * 函数清单: * - is_forbidden 命中保留名(var_in_out/ref/class/any…)→ true * - Parser::Parser (构造)先整体 lex 成 token 流;词法失败透传 lex error * - Parser::run 顶层循环:顶层 VAR_GLOBAL 段(GVL 形态)或 POU 直到 END * - Parser::fail 组装 "syntax error: (file, line, col)" 返回 false * - Parser::cur/type/check token 视图:当前 token / 类型 / 是否指定类型 * - Parser::advance 前进一个 token(END 处不动) * - Parser::expect 断言当前是 t,否则报 "expected 'what'" * - parse_pou POU 外壳:PROGRAM/FUNCTION[:类型]/FUNCTION_BLOCK + 段 + 体 + END_* * - is_var_section 当前 token 是否五种变量段关键字之一 * - end_tok_for POU 种类 → 配对结束关键字 * - check_end_for 是否已到本 POU 的配对结束关键字(语句体循环出口) * - parse_var_block VAR_* … END_VAR:逐行收集声明,定段种类 * - parse_vars_line 一行声明展开成多个 VarDecl(共享类型与初值) * - parse_literal_expr 初值:只吃字面量(TRUE/FALSE/INT/TIME) * - parse_type 类型名:BOOL/INT/TIME/TON/TOF/CTU/标识符 * - parse_stmt 语句分发:IF/WHILE 直入;IDENT 前瞻区分 FB 调用 vs 赋值 * - parse_assign ident := expr ;(fb.field 左值拒绝) * - parse_fb_call ident(名 := expr, …); 命名实参 FB 调用 * - parse_if IF … THEN … (ELSIF …)* (ELSE …)? END_IF * - parse_while WHILE … DO … END_WHILE * - parse_body 连续语句,遇 ELSIF/ELSE/END_IF/END_WHILE/END 停 * - parse_expr 表达式入口 → parse_or * - parse_or/and OR/AND 层(短路保留在 AST,左结合) * - parse_not NOT 前缀一元(可连写) * - parse_cmp 比较层(a #include #include #include "compiler/Lexer.h" namespace compiler { namespace { // 明确拒绝的保留名(小写命中即报错,见 Doc/compiler/语法.md)。 // 这些词在词法层不是关键字(lex 成 IDENT),必须在语法层显式拦截, // 避免 VAR_IN_OUT / REF / CLASS 等被静默当作用户标识符。 const char* const kForbidden[] = { "var_in_out", "var_temp", "ref", "class", "any", "pointer", "interface", "method", }; /** * @brief 判断单词是否命中保留名清单 * @param word 小写单词 * @return true 命中(var_in_out / ref / class / any / pointer / interface / method) */ bool is_forbidden(const std::string& word) { for (const char* f : kForbidden) { if (word == f) { return true; } } return false; } /** * @brief ST 递归下降解析器 * * @details 两阶段:构造时一次性 lex 成 token 流,再逐层解析; * 不做流式交互。token 流只前移不回溯。 */ class Parser { public: /** * @brief 构造解析器(先整体 lex 成 token 流) * @param source_file 源文件名(报错用) * @param content 源文本 * @param out 解析结果 Unit(globals / pous) * @param err 错误输出;可为 nullptr(静默) */ Parser(const std::string& source_file, const std::string& content, Unit* out, std::string* err) : err_(err), out_(out) { std::string lex_err; if (!lex(source_file, content, &tokens_, &lex_err)) { ok_ = false; *err_ = lex_err; return; } pos_ = 0; } /** * @brief 顶层循环:解析直至 END * * @details 每轮要么是 GVL 形态的顶层 VAR_GLOBAL 段(无 POU 的文件), * 要么是一个 POU。 * @return true 成功(out 填满);false 语法错误(err 已写) */ bool run() { if (!ok_) { return false; } while (!check(Tok::END)) { if (is_var_section()) { // 顶层变量段只允许 VAR_GLOBAL(GVL 文件形态) if (!check(Tok::VAR_GLOBAL)) { return fail("only VAR_GLOBAL allowed at file top level"); } VarBlock b; if (!parse_var_block(&b)) { return false; } out_->globals.push_back(std::move(b)); continue; } POU p; if (!parse_pou(&p)) { return false; } out_->pous.push_back(std::move(p)); } return true; } private: // ---- 错误 ---- /** * @brief 组装 "syntax error: (file, line, col)" 写入 err * @details 位置取当前 token(指向出错处,不回溯到行首) * @param msg 错误描述(不含前缀与位置) * @return 恒 false(便于 return fail(...) 一行退出) */ bool fail(const std::string& msg) { if (err_) { const Token& t = cur(); char buf[128]; std::snprintf(buf, sizeof buf, " (%s, line %u, col %u)", t.source_file.c_str(), t.line, t.col); *err_ = "syntax error: " + msg + buf; } return false; } // ---- token 视图 ---- /** * @brief 当前 token(不消费) * @return tokens_[pos_] 的引用 */ const Token& cur() const { return tokens_[pos_]; } /** * @brief 当前 token 的类型 * @return cur().type */ Tok type() const { return cur().type; } /** * @brief 当前 token 是否为指定类型 * @param t 期望的 token 类型 * @return true 匹配 */ bool check(Tok t) const { return type() == t; } /** * @brief 前进一个 token * @details END 处原地不动(pos_ 永不越界) * @return 前进前的 token 引用 */ const Token& advance() { const Token& t = tokens_[pos_]; if (pos_ + 1 < tokens_.size()) { ++pos_; } return t; } /** * @brief 断言当前 token 是 t,否则报错 * @param t 期望的 token 类型 * @param what 错误信息中的名字(如 "END_IF") * @return true 匹配并前进;false 不匹配(err 已写) */ bool expect(Tok t, const char* what) { if (!check(t)) { return fail(std::string("expected '") + what + "'"); } advance(); return true; } // ---- POU ---- /** * @brief 解析 POU 外壳 * * @details PROGRAM / FUNCTION [: 返回类型] / FUNCTION_BLOCK, * 后接 0+ 变量段、0+ 语句,最后必须是配对 END_*。 * @param p 输出 POU * @return true 成功;false 语法错误(err 已写) */ bool parse_pou(POU* p) { if (check(Tok::PROGRAM)) { advance(); p->kind = PouKind::Program; } else if (check(Tok::FUNCTION)) { advance(); p->kind = PouKind::Function; } else if (check(Tok::FUNCTION_BLOCK)) { advance(); p->kind = PouKind::FunctionBlock; } else { return fail("expected POU (PROGRAM / FUNCTION / FUNCTION_BLOCK)"); } if (!parse_ident(&p->name)) { return false; } // 仅 FUNCTION 有返回类型:FUNCTION Add : INT if (p->kind == PouKind::Function) { if (!expect(Tok::COLON, ":")) { return false; } if (!parse_type(&p->result_type)) { return false; } } // 变量段可多个、可缺省,按声明顺序保留 while (is_var_section()) { VarBlock b; if (!parse_var_block(&b)) { return false; } p->blocks.push_back(std::move(b)); } // 语句体直到配对 END_* while (!check_end_for(p->kind)) { Stmt s; if (!parse_stmt(&s)) { return false; } p->body.push_back(std::move(s)); } const char* end_kw = nullptr; switch (p->kind) { case PouKind::Program: end_kw = "END_PROGRAM"; break; case PouKind::Function: end_kw = "END_FUNCTION"; break; case PouKind::FunctionBlock: end_kw = "END_FUNCTION_BLOCK"; break; } return expect(end_tok_for(p->kind), end_kw); } /** * @brief 当前 token 是否为五种变量段关键字之一 * @return true 为 VAR / VAR_INPUT / VAR_OUTPUT / VAR_GLOBAL / VAR_EXTERNAL */ bool is_var_section() const { switch (type()) { case Tok::VAR: case Tok::VAR_INPUT: case Tok::VAR_OUTPUT: case Tok::VAR_GLOBAL: case Tok::VAR_EXTERNAL: return true; default: return false; } } /** * @brief POU 种类 → 配对结束关键字 * @param k POU 种类 * @return PROGRAM → END_PROGRAM、FUNCTION → END_FUNCTION、 * FUNCTION_BLOCK → END_FUNCTION_BLOCK */ Tok end_tok_for(PouKind k) const { switch (k) { case PouKind::Program: return Tok::END_PROGRAM; case PouKind::Function: return Tok::END_FUNCTION; case PouKind::FunctionBlock: return Tok::END_FUNCTION_BLOCK; } return Tok::END; } /** * @brief 当前 token 是否已到本 POU 的配对结束关键字 * @param k POU 种类 * @return true 语句体循环应停止 */ bool check_end_for(PouKind k) const { return check(end_tok_for(k)); } // ---- 变量段 ---- /** * @brief 解析 VAR_* … END_VAR 变量段 * @param b 输出 VarBlock(section + vars) * @return true 成功;false 语法错误(err 已写) */ bool parse_var_block(VarBlock* b) { switch (type()) { case Tok::VAR: b->section = VarSection::Local; break; case Tok::VAR_INPUT: b->section = VarSection::Input; break; case Tok::VAR_OUTPUT: b->section = VarSection::Output; break; case Tok::VAR_GLOBAL: b->section = VarSection::Global; break; case Tok::VAR_EXTERNAL: b->section = VarSection::External; break; default: return fail("expected variable section"); } advance(); while (!check(Tok::END_VAR)) { std::vector line; if (!parse_vars_line(&line)) { return false; } for (VarDecl& d : line) { b->vars.push_back(std::move(d)); } } advance(); // END_VAR return true; } /** * @brief 解析一行声明:ident (',' ident)* ':' type (':=' literal)? ';' * @details 同一行多个名字共享类型与初值,展开成多个 VarDecl * @param out 输出 VarDecl 列表 * @return true 成功;false 语法错误(err 已写) */ bool parse_vars_line(std::vector* out) { std::vector names; while (true) { VarDecl d; d.line = cur().line; d.col = cur().col; if (!parse_ident(&d.name)) { return false; } names.push_back(std::move(d)); if (!check(Tok::COMMA)) { break; } advance(); } if (!expect(Tok::COLON, ":")) { return false; } TypeRef t; if (!parse_type(&t)) { return false; } bool has_init = false; int64_t init = 0; if (check(Tok::ASSIGN)) { advance(); Expr e; if (!parse_literal_expr(&e)) { return false; } has_init = true; init = e.int_value; } if (!expect(Tok::SEMI, ";")) { return false; } for (VarDecl& d : names) { d.type = t; d.has_init = has_init; d.init_value = init; out->push_back(std::move(d)); } return true; } /** * @brief 解析初值字面量 * @details 只允许字面量(TRUE/FALSE/INT/TIME);表达式初值 v1 不做 * @param e 输出 Expr(LitBool / LitInt / LitTime) * @return true 成功;false 非字面量(err 已写) */ bool parse_literal_expr(Expr* e) { switch (type()) { case Tok::TRUE: e->kind = ExprKind::LitBool; e->int_value = 1; advance(); return true; case Tok::FALSE: e->kind = ExprKind::LitBool; e->int_value = 0; advance(); return true; case Tok::INT_LIT: e->kind = ExprKind::LitInt; e->int_value = cur().int_value; advance(); return true; case Tok::TIME_LIT: e->kind = ExprKind::LitTime; e->int_value = cur().int_value; advance(); return true; default: return fail("expected literal initializer"); } } /** * @brief 解析类型名 * @details 内建标量 BOOL/INT/TIME、内建 FB TON/TOF/CTU、 * 或标识符(用户 FB 类型,存在性由 12.6 链接层校验) * @param t 输出 TypeRef * @return true 成功;false 语法错误(err 已写) */ bool parse_type(TypeRef* t) { switch (type()) { case Tok::BOOL: t->kind = TypeKind::Bool; advance(); return true; case Tok::INT: t->kind = TypeKind::Int; advance(); return true; case Tok::TIME: t->kind = TypeKind::Time; advance(); return true; case Tok::TON: t->kind = TypeKind::FbBuiltin; t->name = "ton"; advance(); return true; case Tok::TOF: t->kind = TypeKind::FbBuiltin; t->name = "tof"; advance(); return true; case Tok::CTU: t->kind = TypeKind::FbBuiltin; t->name = "ctu"; advance(); return true; case Tok::IDENT: { const std::string word = cur().text; if (is_forbidden(word)) { return fail("'" + word + "' is not supported"); } t->kind = TypeKind::FbUser; t->name = word; advance(); return true; } default: return fail("expected type name"); } } // ---- 语句 ---- /** * @brief 语句分发 * @details IF / WHILE 关键字直入;IDENT 用一 token 前瞻区分: * ident '(' → FB 调用(无返回值,语句形态); * ident ('.' ident)? ':=' → 赋值 * @param s 输出 Stmt * @return true 成功;false 语法错误(err 已写) */ bool parse_stmt(Stmt* s) { if (check(Tok::IF)) { return parse_if(s); } if (check(Tok::WHILE)) { return parse_while(s); } if (!check(Tok::IDENT)) { return fail("expected statement"); } // ident '(' → FB 调用;ident ('.' ident)? ':=' → 赋值 const size_t save = pos_; advance(); const bool is_call = check(Tok::LPAREN); pos_ = save; return is_call ? parse_fb_call(s) : parse_assign(s); } /** * @brief 解析赋值语句:ident ':=' expr ';' * @details 左值仅标识符;ident.ident 左值(fb.field 赋值)v1 明确拒绝 * @param s 输出 Stmt(kind=Assign) * @return true 成功;false 语法错误(err 已写) */ bool parse_assign(Stmt* s) { s->kind = StmtKind::Assign; if (!parse_ident(&s->target)) { return false; } if (check(Tok::DOT)) { return fail("assignment to FB field is not supported"); } if (!expect(Tok::ASSIGN, ":=")) { return false; } s->value = std::make_unique(); if (!parse_expr(s->value.get())) { return false; } return expect(Tok::SEMI, ";"); } /** * @brief 解析 FB 调用语句:ident '(' arg (',' arg)* ')' ';' * @details 全部命名实参(位置实参 v1 不做); * 字段名与类型由 12.6 / 12.7 校验 * @param s 输出 Stmt(kind=FbCall) * @return true 成功;false 语法错误(err 已写) */ bool parse_fb_call(Stmt* s) { s->kind = StmtKind::FbCall; if (!parse_ident(&s->instance)) { return false; } if (!expect(Tok::LPAREN, "(")) { return false; } if (!check(Tok::RPAREN)) { while (true) { FbArg a; if (!parse_ident(&a.name)) { return false; } if (!expect(Tok::ASSIGN, ":=")) { return false; } a.value = std::make_unique(); if (!parse_expr(a.value.get())) { return false; } s->args.push_back(std::move(a)); if (!check(Tok::COMMA)) { break; } advance(); } } if (!expect(Tok::RPAREN, ")")) { return false; } return expect(Tok::SEMI, ";"); } /** * @brief 解析 IF 语句:IF cond THEN body (ELSIF cond THEN body)* (ELSE body)? END_IF * @param s 输出 Stmt(kind=If) * @return true 成功;false 语法错误(err 已写) */ bool parse_if(Stmt* s) { s->kind = StmtKind::If; advance(); // IF s->cond = std::make_unique(); if (!parse_expr(s->cond.get())) { return false; } if (!expect(Tok::THEN, "THEN")) { return false; } if (!parse_body(&s->body)) { return false; } while (check(Tok::ELSIF)) { advance(); IfBranch b; b.cond = std::make_unique(); if (!parse_expr(b.cond.get())) { return false; } if (!expect(Tok::THEN, "THEN")) { return false; } if (!parse_body(&b.body)) { return false; } s->elsifs.push_back(std::move(b)); } if (check(Tok::ELSE)) { advance(); if (!parse_body(&s->else_body)) { return false; } } return expect(Tok::END_IF, "END_IF"); } /** * @brief 解析 WHILE 语句:WHILE cond DO body END_WHILE * @details 终止性由 cycle_limit 保证,不做证明 * @param s 输出 Stmt(kind=While) * @return true 成功;false 语法错误(err 已写) */ bool parse_while(Stmt* s) { s->kind = StmtKind::While; advance(); // WHILE s->cond = std::make_unique(); if (!parse_expr(s->cond.get())) { return false; } if (!expect(Tok::DO, "DO")) { return false; } if (!parse_body(&s->body)) { return false; } return expect(Tok::END_WHILE, "END_WHILE"); } /** * @brief 解析语句体(连续语句) * @details 遇 ELSIF / ELSE / END_IF / END_WHILE / END 停止; * 停止 token 由调用方消费,这里不越界 * @param out 输出 Stmt 列表 * @return true 成功;false 语法错误(err 已写) */ bool parse_body(std::vector* out) { while (!check(Tok::ELSIF) && !check(Tok::ELSE) && !check(Tok::END_IF) && !check(Tok::END_WHILE) && !check(Tok::END)) { Stmt s; if (!parse_stmt(&s)) { return false; } out->push_back(std::move(s)); } return true; } // ---- 表达式(优先级爬升)---- /** * @brief 表达式入口 * @details 优先级从低到高:or < and < not < cmp < add < mul < unary < primary。 * 每个二目层用「局部链 + 末尾 move」累积左结合树(避免自引用指针)。 * @param e 输出 Expr 根 * @return true 成功;false 语法错误(err 已写) */ bool parse_expr(Expr* e) { return parse_or(e); } /** * @brief OR 层:and_expr (OR and_expr)*(左结合) * @param e 输出 Expr 根(kind=Or 链或透传下层) * @return true 成功;false 语法错误(err 已写) */ bool parse_or(Expr* e) { std::unique_ptr lhs = std::make_unique(); if (!parse_and(lhs.get())) { return false; } while (check(Tok::OR)) { advance(); auto node = std::make_unique(); node->kind = ExprKind::Or; // 短路语义保留到 12.8 编跳转 node->lhs = std::move(lhs); node->rhs = std::make_unique(); if (!parse_and(node->rhs.get())) { return false; } lhs = std::move(node); } *e = std::move(*lhs); return true; } /** * @brief AND 层:not_expr (AND not_expr)*(左结合) * @param e 输出 Expr 根(kind=And 链或透传下层) * @return true 成功;false 语法错误(err 已写) */ bool parse_and(Expr* e) { std::unique_ptr lhs = std::make_unique(); if (!parse_not(lhs.get())) { return false; } while (check(Tok::AND)) { advance(); auto node = std::make_unique(); node->kind = ExprKind::And; // 短路语义保留到 12.8 编跳转 node->lhs = std::move(lhs); node->rhs = std::make_unique(); if (!parse_not(node->rhs.get())) { return false; } lhs = std::move(node); } *e = std::move(*lhs); return true; } /** * @brief NOT 层:NOT not_expr | cmp_expr * @details NOT 是前缀一元,可连写(NOT NOT x);比 AND/OR 紧 * @param e 输出 Expr 根(kind=Not 或透传 cmp) * @return true 成功;false 语法错误(err 已写) */ bool parse_not(Expr* e) { if (check(Tok::NOT)) { advance(); e->kind = ExprKind::Not; e->operand = std::make_unique(); return parse_not(e->operand.get()); } return parse_cmp(e); } /** * @brief 比较层:add_expr (比较符 add_expr)? * @details 比较不连锁(a < b < c 拒绝);结果类型 BOOL 由 12.7 校验 * @param e 输出 Expr 根(kind=Cmp 或透传 add) * @return true 成功;false 语法错误(err 已写) */ bool parse_cmp(Expr* e) { std::unique_ptr lhs = std::make_unique(); if (!parse_add(lhs.get())) { return false; } BinOp op; switch (type()) { case Tok::EQ: op = BinOp::Eq; break; case Tok::NE: op = BinOp::Ne; break; case Tok::LT: op = BinOp::Lt; break; case Tok::LE: op = BinOp::Le; break; case Tok::GT: op = BinOp::Gt; break; case Tok::GE: op = BinOp::Ge; break; default: *e = std::move(*lhs); // 无比较符 → 原样上抛 return true; } advance(); auto rhs = std::make_unique(); if (!parse_add(rhs.get())) { return false; } // 右侧之后再出现比较符 → 链式,v1 拒绝 if (is_cmp_op(type())) { return fail("chained comparison not supported"); } e->kind = ExprKind::Cmp; e->op = op; e->lhs = std::move(lhs); e->rhs = std::move(rhs); return true; } /** * @brief 是否比较运算符 token(= <> < <= > >=) * @param t token 类型 * @return true 是比较运算符 */ bool is_cmp_op(Tok t) const { return t == Tok::EQ || t == Tok::NE || t == Tok::LT || t == Tok::LE || t == Tok::GT || t == Tok::GE; } /** * @brief 加减层:mul_expr (('+'|'-') mul_expr)*(左结合) * @param e 输出 Expr 根(kind=Add/Sub 链或透传下层) * @return true 成功;false 语法错误(err 已写) */ bool parse_add(Expr* e) { std::unique_ptr lhs = std::make_unique(); if (!parse_mul(lhs.get())) { return false; } while (true) { ExprKind k; if (check(Tok::PLUS)) { k = ExprKind::Add; } else if (check(Tok::MINUS)) { k = ExprKind::Sub; } else { break; } advance(); auto node = std::make_unique(); node->kind = k; node->lhs = std::move(lhs); node->rhs = std::make_unique(); if (!parse_mul(node->rhs.get())) { return false; } lhs = std::move(node); } *e = std::move(*lhs); return true; } /** * @brief 乘除层:unary (('*'|'/') unary)*(左结合) * @param e 输出 Expr 根(kind=Mul/Div 链或透传下层) * @return true 成功;false 语法错误(err 已写) */ bool parse_mul(Expr* e) { std::unique_ptr lhs = std::make_unique(); if (!parse_unary(lhs.get())) { return false; } while (true) { ExprKind k; if (check(Tok::STAR)) { k = ExprKind::Mul; } else if (check(Tok::SLASH)) { k = ExprKind::Div; } else { break; } advance(); auto node = std::make_unique(); node->kind = k; node->lhs = std::move(lhs); node->rhs = std::make_unique(); if (!parse_unary(node->rhs.get())) { return false; } lhs = std::move(node); } *e = std::move(*lhs); return true; } /** * @brief 一元负号层:'-' unary | primary * @details 可连写(- - x);注意 -5 不是字面量,而是 Neg(LitInt 5) * @param e 输出 Expr 根(kind=Neg 或透传 primary) * @return true 成功;false 语法错误(err 已写) */ bool parse_unary(Expr* e) { if (check(Tok::MINUS)) { advance(); e->kind = ExprKind::Neg; e->operand = std::make_unique(); return parse_unary(e->operand.get()); } return parse_primary(e); } /** * @brief 原子:字面量 / 括号表达式 / ident * @details ident 后的 '.' 或 '(' 决定形态(变量 | fb.field | 函数调用), * 与语句层的区分逻辑一致 * @param e 输出 Expr * @return true 成功;false 语法错误(err 已写) */ bool parse_primary(Expr* e) { switch (type()) { case Tok::INT_LIT: e->kind = ExprKind::LitInt; e->int_value = cur().int_value; advance(); return true; case Tok::TIME_LIT: e->kind = ExprKind::LitTime; e->int_value = cur().int_value; advance(); return true; case Tok::TRUE: e->kind = ExprKind::LitBool; e->int_value = 1; advance(); return true; case Tok::FALSE: e->kind = ExprKind::LitBool; e->int_value = 0; advance(); return true; case Tok::LPAREN: { advance(); if (!parse_expr(e)) { return false; } return expect(Tok::RPAREN, ")"); } case Tok::IDENT: { const std::string word = cur().text; if (is_forbidden(word)) { return fail("'" + word + "' is not supported"); } advance(); if (check(Tok::DOT)) { // fb.field(字段存在性由 12.6 校验) advance(); e->kind = ExprKind::Field; e->name = word; if (!parse_ident(&e->field)) { return false; } return true; } if (check(Tok::LPAREN)) { // 函数调用(FUNCTION,有返回值;与语句层 FB 调用区分) e->kind = ExprKind::Call; e->name = word; advance(); if (!check(Tok::RPAREN)) { while (true) { auto arg = std::make_unique(); if (!parse_expr(arg.get())) { return false; } e->args.push_back(std::move(arg)); if (!check(Tok::COMMA)) { break; } advance(); } } return expect(Tok::RPAREN, ")"); } e->kind = ExprKind::VarRef; e->name = word; return true; } default: return fail("expected expression"); } } /** * @brief 解析标识符 * @details 必须是 IDENT 且非保留名 * @param out 输出小写标识符文本 * @return true 成功;false 语法错误(err 已写) */ bool parse_ident(std::string* out) { if (!check(Tok::IDENT)) { return fail("expected identifier"); } if (is_forbidden(cur().text)) { return fail("'" + cur().text + "' is not supported"); } *out = cur().text; advance(); return true; } // ---- 成员 ---- std::string* err_; // 错误输出(可空) std::vector tokens_; // 整段 token 流(构造时一次性 lex 完成) Unit* out_; // 解析结果 size_t pos_ = 0; // 当前 token 下标 bool ok_ = true; // 词法阶段是否成功 }; } // namespace /** * @brief 对源文本做语法分析(对外入口) * @details 成功时 out 填满 globals/pous;失败时 out 内容不保证(以返回值为准) * @param source_file 源文件名(报错用) * @param content 源文本 * @param out 输出 Unit(globals / pous,先清空) * @param err 错误输出;可为 nullptr(静默) * @return true 成功;false 失败(err 前缀 "syntax error") */ bool parse_pous(const std::string& source_file, const std::string& content, Unit* out, std::string* err) { out->globals.clear(); out->pous.clear(); Parser p(source_file, content, out, err); return p.run(); } /** * @brief 读取文件后语法分析 * @param path 文件路径 * @param out 输出 Unit(globals / pous) * @param err 错误输出;可为 nullptr(静默) * @return true 成功;false 失败(文件打不开也算 syntax error) */ bool parse_pous_file(const std::string& path, Unit* out, std::string* err) { std::ifstream in(path, std::ios::binary); if (!in) { if (err) { *err = "syntax error: cannot open file (" + path + ")"; } return false; } std::string content((std::istreambuf_iterator(in)), std::istreambuf_iterator()); return parse_pous(path, content, out, err); } } // namespace compiler