三个 compiler 源文件补 Doxygen 注释:函数功能与参数说明。
- Lexer.cpp:13 个函数 @brief/@param/@return(含 T# 字面量、行列语义) - Parser.cpp:24 个函数(含表达式优先级爬升、token 前瞻区分) - Linker.cpp:24 个函数(含六步流程、三色 DFS 环检测、io 校验) - 文件头统一:设计说明 + 函数清单;代码零改动,ctest 7/7 通过
This commit is contained in:
+280
-14
@@ -3,6 +3,47 @@
|
||||
* @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: <msg> (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<b<c 链式拒绝)
|
||||
* - is_cmp_op 是否比较运算符 token
|
||||
* - parse_add/mul 加减/乘除层(左结合)
|
||||
* - parse_unary 一元负号(-5 = Neg(LitInt 5))
|
||||
* - parse_primary 原子:字面量/括号/ident(变量 | fb.field | 函数调用)
|
||||
* - parse_ident 标识符(非保留名,返回小写)
|
||||
* - parse_pous 对外入口:清空输出 → 构造 Parser → run
|
||||
* - parse_pous_file 读文件后调 parse_pous;打不开算 syntax error
|
||||
*/
|
||||
|
||||
#include "compiler/Parser.h"
|
||||
@@ -16,12 +57,19 @@
|
||||
namespace compiler {
|
||||
namespace {
|
||||
|
||||
// 明确拒绝的保留名(小写命中即报错,见 Doc/compiler/语法.md)
|
||||
// 明确拒绝的保留名(小写命中即报错,见 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) {
|
||||
@@ -31,8 +79,21 @@ namespace {
|
||||
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) {
|
||||
@@ -45,6 +106,13 @@ namespace {
|
||||
pos_ = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 顶层循环:解析直至 END
|
||||
*
|
||||
* @details 每轮要么是 GVL 形态的顶层 VAR_GLOBAL 段(无 POU 的文件),
|
||||
* 要么是一个 POU。
|
||||
* @return true 成功(out 填满);false 语法错误(err 已写)
|
||||
*/
|
||||
bool run() {
|
||||
if (!ok_) {
|
||||
return false;
|
||||
@@ -74,6 +142,12 @@ namespace {
|
||||
private:
|
||||
// ---- 错误 ----
|
||||
|
||||
/**
|
||||
* @brief 组装 "syntax error: <msg> (file, line, col)" 写入 err
|
||||
* @details 位置取当前 token(指向出错处,不回溯到行首)
|
||||
* @param msg 错误描述(不含前缀与位置)
|
||||
* @return 恒 false(便于 return fail(...) 一行退出)
|
||||
*/
|
||||
bool fail(const std::string& msg) {
|
||||
if (err_) {
|
||||
const Token& t = cur();
|
||||
@@ -87,10 +161,30 @@ namespace {
|
||||
|
||||
// ---- 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()) {
|
||||
@@ -99,6 +193,12 @@ namespace {
|
||||
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 + "'");
|
||||
@@ -109,6 +209,14 @@ namespace {
|
||||
|
||||
// ---- 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();
|
||||
@@ -127,6 +235,7 @@ namespace {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 仅 FUNCTION 有返回类型:FUNCTION Add : INT
|
||||
if (p->kind == PouKind::Function) {
|
||||
if (!expect(Tok::COLON, ":")) {
|
||||
return false;
|
||||
@@ -136,6 +245,7 @@ namespace {
|
||||
}
|
||||
}
|
||||
|
||||
// 变量段可多个、可缺省,按声明顺序保留
|
||||
while (is_var_section()) {
|
||||
VarBlock b;
|
||||
if (!parse_var_block(&b)) {
|
||||
@@ -144,6 +254,7 @@ namespace {
|
||||
p->blocks.push_back(std::move(b));
|
||||
}
|
||||
|
||||
// 语句体直到配对 END_*
|
||||
while (!check_end_for(p->kind)) {
|
||||
Stmt s;
|
||||
if (!parse_stmt(&s)) {
|
||||
@@ -161,6 +272,10 @@ namespace {
|
||||
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:
|
||||
@@ -174,6 +289,12 @@ namespace {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @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;
|
||||
@@ -183,10 +304,20 @@ namespace {
|
||||
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;
|
||||
@@ -212,7 +343,12 @@ namespace {
|
||||
return true;
|
||||
}
|
||||
|
||||
// ident (',' ident)* ':' type (':=' literal)? ';' —— 展开成多个 VarDecl
|
||||
/**
|
||||
* @brief 解析一行声明:ident (',' ident)* ':' type (':=' literal)? ';'
|
||||
* @details 同一行多个名字共享类型与初值,展开成多个 VarDecl
|
||||
* @param out 输出 VarDecl 列表
|
||||
* @return true 成功;false 语法错误(err 已写)
|
||||
*/
|
||||
bool parse_vars_line(std::vector<VarDecl>* out) {
|
||||
std::vector<VarDecl> names;
|
||||
while (true) {
|
||||
@@ -258,7 +394,12 @@ namespace {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 初值只允许字面量(BOOL / INT / TIME / TRUE / FALSE)
|
||||
/**
|
||||
* @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:
|
||||
@@ -286,7 +427,13 @@ namespace {
|
||||
}
|
||||
}
|
||||
|
||||
// 类型:BOOL | INT | TIME | TON | TOF | CTU | ident(用户 FB 类型)
|
||||
/**
|
||||
* @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:
|
||||
@@ -333,6 +480,14 @@ namespace {
|
||||
|
||||
// ---- 语句 ----
|
||||
|
||||
/**
|
||||
* @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);
|
||||
@@ -352,6 +507,12 @@ namespace {
|
||||
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)) {
|
||||
@@ -370,6 +531,13 @@ namespace {
|
||||
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)) {
|
||||
@@ -404,6 +572,11 @@ namespace {
|
||||
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
|
||||
@@ -441,6 +614,12 @@ namespace {
|
||||
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
|
||||
@@ -457,6 +636,13 @@ namespace {
|
||||
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<Stmt>* out) {
|
||||
while (!check(Tok::ELSIF) && !check(Tok::ELSE) && !check(Tok::END_IF) &&
|
||||
!check(Tok::END_WHILE) && !check(Tok::END)) {
|
||||
@@ -471,8 +657,20 @@ namespace {
|
||||
|
||||
// ---- 表达式(优先级爬升)----
|
||||
|
||||
/**
|
||||
* @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<Expr> lhs = std::make_unique<Expr>();
|
||||
if (!parse_and(lhs.get())) {
|
||||
@@ -481,7 +679,7 @@ namespace {
|
||||
while (check(Tok::OR)) {
|
||||
advance();
|
||||
auto node = std::make_unique<Expr>();
|
||||
node->kind = ExprKind::Or;
|
||||
node->kind = ExprKind::Or; // 短路语义保留到 12.8 编跳转
|
||||
node->lhs = std::move(lhs);
|
||||
node->rhs = std::make_unique<Expr>();
|
||||
if (!parse_and(node->rhs.get())) {
|
||||
@@ -493,6 +691,11 @@ namespace {
|
||||
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<Expr> lhs = std::make_unique<Expr>();
|
||||
if (!parse_not(lhs.get())) {
|
||||
@@ -501,7 +704,7 @@ namespace {
|
||||
while (check(Tok::AND)) {
|
||||
advance();
|
||||
auto node = std::make_unique<Expr>();
|
||||
node->kind = ExprKind::And;
|
||||
node->kind = ExprKind::And; // 短路语义保留到 12.8 编跳转
|
||||
node->lhs = std::move(lhs);
|
||||
node->rhs = std::make_unique<Expr>();
|
||||
if (!parse_not(node->rhs.get())) {
|
||||
@@ -513,6 +716,12 @@ namespace {
|
||||
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();
|
||||
@@ -523,6 +732,12 @@ namespace {
|
||||
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<Expr> lhs = std::make_unique<Expr>();
|
||||
if (!parse_add(lhs.get())) {
|
||||
@@ -537,7 +752,7 @@ namespace {
|
||||
case Tok::GT: op = BinOp::Gt; break;
|
||||
case Tok::GE: op = BinOp::Ge; break;
|
||||
default:
|
||||
*e = std::move(*lhs);
|
||||
*e = std::move(*lhs); // 无比较符 → 原样上抛
|
||||
return true;
|
||||
}
|
||||
advance();
|
||||
@@ -545,6 +760,7 @@ namespace {
|
||||
if (!parse_add(rhs.get())) {
|
||||
return false;
|
||||
}
|
||||
// 右侧之后再出现比较符 → 链式,v1 拒绝
|
||||
if (is_cmp_op(type())) {
|
||||
return fail("chained comparison not supported");
|
||||
}
|
||||
@@ -555,11 +771,21 @@ namespace {
|
||||
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<Expr> lhs = std::make_unique<Expr>();
|
||||
if (!parse_mul(lhs.get())) {
|
||||
@@ -588,6 +814,11 @@ namespace {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 乘除层:unary (('*'|'/') unary)*(左结合)
|
||||
* @param e 输出 Expr 根(kind=Mul/Div 链或透传下层)
|
||||
* @return true 成功;false 语法错误(err 已写)
|
||||
*/
|
||||
bool parse_mul(Expr* e) {
|
||||
std::unique_ptr<Expr> lhs = std::make_unique<Expr>();
|
||||
if (!parse_unary(lhs.get())) {
|
||||
@@ -616,6 +847,12 @@ namespace {
|
||||
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();
|
||||
@@ -626,6 +863,13 @@ namespace {
|
||||
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:
|
||||
@@ -662,7 +906,7 @@ namespace {
|
||||
}
|
||||
advance();
|
||||
if (check(Tok::DOT)) {
|
||||
// fb.field
|
||||
// fb.field(字段存在性由 12.6 校验)
|
||||
advance();
|
||||
e->kind = ExprKind::Field;
|
||||
e->name = word;
|
||||
@@ -672,7 +916,7 @@ namespace {
|
||||
return true;
|
||||
}
|
||||
if (check(Tok::LPAREN)) {
|
||||
// 函数调用(FUNCTION,有返回值)
|
||||
// 函数调用(FUNCTION,有返回值;与语句层 FB 调用区分)
|
||||
e->kind = ExprKind::Call;
|
||||
e->name = word;
|
||||
advance();
|
||||
@@ -700,6 +944,12 @@ namespace {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 解析标识符
|
||||
* @details 必须是 IDENT 且非保留名
|
||||
* @param out 输出小写标识符文本
|
||||
* @return true 成功;false 语法错误(err 已写)
|
||||
*/
|
||||
bool parse_ident(std::string* out) {
|
||||
if (!check(Tok::IDENT)) {
|
||||
return fail("expected identifier");
|
||||
@@ -714,15 +964,24 @@ namespace {
|
||||
|
||||
// ---- 成员 ----
|
||||
|
||||
std::string* err_;
|
||||
std::vector<Token> tokens_;
|
||||
Unit* out_;
|
||||
size_t pos_ = 0;
|
||||
bool ok_ = true;
|
||||
std::string* err_; // 错误输出(可空)
|
||||
std::vector<Token> 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();
|
||||
@@ -731,6 +990,13 @@ bool parse_pous(const std::string& source_file, const std::string& content,
|
||||
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) {
|
||||
|
||||
Reference in New Issue
Block a user