Files
Interpreter/compiler/src/Parser.cpp
T
Admin 93a4662d6a 三个 compiler 源文件补 Doxygen 注释:函数功能与参数说明。
- Lexer.cpp:13 个函数 @brief/@param/@return(含 T# 字面量、行列语义)
- Parser.cpp:24 个函数(含表达式优先级爬升、token 前瞻区分)
- Linker.cpp:24 个函数(含六步流程、三色 DFS 环检测、io 校验)
- 文件头统一:设计说明 + 函数清单;代码零改动,ctest 7/7 通过
2026-08-21 11:23:37 +08:00

1014 lines
37 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* @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: <msg> (file, line, col)" 返回 false
* - Parser::cur/type/check token 视图:当前 token / 类型 / 是否指定类型
* - Parser::advance 前进一个 tokenEND 处不动)
* - 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"
#include <cstdio>
#include <fstream>
#include <string>
#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 解析结果 Unitglobals / 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: <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();
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 输出 VarBlocksection + 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<VarDecl> 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<VarDecl>* out) {
std::vector<VarDecl> 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 输出 ExprLitBool / 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 输出 Stmtkind=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<Expr>();
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 输出 Stmtkind=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<Expr>();
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 输出 Stmtkind=If
* @return true 成功;false 语法错误(err 已写)
*/
bool parse_if(Stmt* s) {
s->kind = StmtKind::If;
advance(); // IF
s->cond = std::make_unique<Expr>();
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<Expr>();
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 输出 Stmtkind=While
* @return true 成功;false 语法错误(err 已写)
*/
bool parse_while(Stmt* s) {
s->kind = StmtKind::While;
advance(); // WHILE
s->cond = std::make_unique<Expr>();
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<Stmt>* 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<Expr> lhs = std::make_unique<Expr>();
if (!parse_and(lhs.get())) {
return false;
}
while (check(Tok::OR)) {
advance();
auto node = std::make_unique<Expr>();
node->kind = ExprKind::Or; // 短路语义保留到 12.8 编跳转
node->lhs = std::move(lhs);
node->rhs = std::make_unique<Expr>();
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<Expr> lhs = std::make_unique<Expr>();
if (!parse_not(lhs.get())) {
return false;
}
while (check(Tok::AND)) {
advance();
auto node = std::make_unique<Expr>();
node->kind = ExprKind::And; // 短路语义保留到 12.8 编跳转
node->lhs = std::move(lhs);
node->rhs = std::make_unique<Expr>();
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<Expr>();
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<Expr> lhs = std::make_unique<Expr>();
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<Expr>();
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<Expr> lhs = std::make_unique<Expr>();
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<Expr>();
node->kind = k;
node->lhs = std::move(lhs);
node->rhs = std::make_unique<Expr>();
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<Expr> lhs = std::make_unique<Expr>();
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<Expr>();
node->kind = k;
node->lhs = std::move(lhs);
node->rhs = std::make_unique<Expr>();
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<Expr>();
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<Expr>();
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<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 输出 Unitglobals / 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 输出 Unitglobals / 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<char>(in)),
std::istreambuf_iterator<char>());
return parse_pous(path, content, out, err);
}
} // namespace compiler