实现 12.5 递归下降语法:AST + 拒绝清单。

- Parser.h/cpp:POU 外壳(PROGRAM/FUNCTION/FUNCTION_BLOCK)、变量段、语句(赋值/IF/WHILE/FB 调用)、表达式优先级爬升(NOT/AND/OR 短路/比较/算术/函数调用/负号)
- Unit 结构:顶层 VAR_GLOBAL 段(GVL 文件形态)+ POU 列表
- 拒绝:VAR_IN_OUT/REF/CLASS/ANY 等保留名、链式比较、FB 字段赋值、顶层非 GVL 段
- THEN/DO 补入关键字表(12.5 修订,同步 12.1 与词法文档)
- parser_test:98 断言(line1 三文件 AST、FUNCTION 调用、IF/WHILE/TON、9 类负例),ctest 6/6
This commit is contained in:
2026-08-21 11:07:34 +08:00
parent d4e2c1ae7c
commit 4ba44459e2
11 changed files with 1387 additions and 1 deletions
+2 -1
View File
@@ -9,7 +9,8 @@ project(Compiler
# 词法、语法、符号表、类型检查、codegen、链接都留在 compiler 一个库里
add_library(compiler STATIC
./src/Lexer.cpp
./src/Project.cpp)
./src/Project.cpp
./src/Parser.cpp)
target_include_directories(compiler PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include)
target_include_directories(compiler PRIVATE ${CMAKE_SOURCE_DIR}/third_party/tomlplusplus/include)
+2
View File
@@ -37,6 +37,8 @@ namespace compiler {
END_IF,
WHILE,
END_WHILE,
THEN, // 12.5 修订:IF/WHILE 语法需要,补入关键字表
DO, // 同上
AND,
OR,
NOT,
+133
View File
@@ -0,0 +1,133 @@
/**
* @file Parser.h
* @brief ST 子集递归下降语法分析(AST 定义 + 入口)
* @author
* @date 2026-08-21
*/
#pragma once
#include <cstdint>
#include <memory>
#include <string>
#include <vector>
namespace compiler {
// ---- 类型引用 ----
enum class TypeKind { Bool, Int, Time, FbBuiltin, FbUser };
struct TypeRef {
TypeKind kind = TypeKind::Bool;
std::string name; // FbUser 时为用户 FB 类型名(小写)
};
// ---- 变量段 ----
struct VarDecl {
std::string name;
TypeRef type;
bool has_init = false; // 可选初值(v1 仅字面量)
int64_t init_value = 0; // BOOL 0/1、INT 值、TIME 毫秒
uint32_t line = 0;
uint32_t col = 0;
};
enum class VarSection { Local, Input, Output, Global, External };
struct VarBlock {
VarSection section = VarSection::Local;
std::vector<VarDecl> vars;
};
// ---- 表达式 ----
enum class ExprKind {
LitBool, // int_value 0/1
LitInt, // int_value
LitTime, // int_value 毫秒
VarRef, // name
Field, // name.field
Not, // operand
And, Or, // lhs, rhs(短路语义保留到 12.8
Cmp, Add, Sub, Mul, Div, // lhs, rhsop
Neg, // operand(一元负号)
Call, // name + args(函数调用,有返回值)
};
enum class BinOp { Eq, Ne, Lt, Le, Gt, Ge, Add, Sub, Mul, Div };
struct Expr {
ExprKind kind = ExprKind::LitBool;
BinOp op = BinOp::Eq; // 二目运算
std::string name; // VarRef / Field 对象 / Call 函数名
std::string field; // Field 字段名
int64_t int_value = 0; // 字面量值
std::unique_ptr<Expr> lhs;
std::unique_ptr<Expr> rhs;
std::unique_ptr<Expr> operand; // Not / Neg
std::vector<std::unique_ptr<Expr>> args; // Call 实参
};
// ---- 语句 ----
enum class StmtKind { Assign, If, While, FbCall };
struct Stmt; // 前置声明(IfBranch / Stmt 相互引用)
struct FbArg {
std::string name;
std::unique_ptr<Expr> value;
};
struct IfBranch {
std::unique_ptr<Expr> cond;
std::vector<Stmt> body;
};
// 前置声明:Stmt 内部自引用
struct Stmt {
StmtKind kind = StmtKind::Assign;
// Assign
std::string target; // 左值标识符
bool target_is_field = false; // 左值 fb.field(v1 禁止赋值,语法层拒绝)
std::string field;
std::unique_ptr<Expr> value;
// If / While
std::unique_ptr<Expr> cond;
std::vector<Stmt> body;
std::vector<IfBranch> elsifs;
std::vector<Stmt> else_body;
// FbCall
std::string instance;
std::vector<FbArg> args;
};
// ---- POU ----
enum class PouKind { Program, Function, FunctionBlock };
struct POU {
PouKind kind = PouKind::Program;
std::string name;
TypeRef result_type; // FUNCTION 才有
std::vector<VarBlock> blocks;
std::vector<Stmt> body;
std::string source_file;
};
// 一个 .st 文件的解析结果:GVL 文件形态(无 POU)时只有 globals 非空
struct Unit {
std::vector<VarBlock> globals; // 顶层 VAR_GLOBAL 段(仅 GVL 文件)
std::vector<POU> pous;
};
// 解析一个 .st 文件的全部内容(顶层 VAR_GLOBAL 段或 POU)。
// 失败返回 falseerr 前缀 "syntax error",带文件与行列。
bool parse_pous(const std::string& source_file, const std::string& content,
Unit* out, std::string* err);
// 读文件后解析
bool parse_pous_file(const std::string& path, Unit* out, std::string* err);
}
+2
View File
@@ -41,6 +41,8 @@ namespace {
{"end_if", Tok::END_IF},
{"while", Tok::WHILE},
{"end_while", Tok::END_WHILE},
{"then", Tok::THEN},
{"do", Tok::DO},
{"and", Tok::AND},
{"or", Tok::OR},
{"not", Tok::NOT},
+747
View File
@@ -0,0 +1,747 @@
/**
* @file Parser.cpp
* @brief ST 子集递归下降语法分析
* @author
* @date 2026-08-21
*/
#include "compiler/Parser.h"
#include <cstdio>
#include <fstream>
#include <string>
#include "compiler/Lexer.h"
namespace compiler {
namespace {
// 明确拒绝的保留名(小写命中即报错,见 Doc/compiler/语法.md
const char* const kForbidden[] = {
"var_in_out", "var_temp", "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;
}
class Parser {
public:
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;
}
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:
// ---- 错误 ----
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 视图 ----
const Token& cur() const { return tokens_[pos_]; }
Tok type() const { return cur().type; }
bool check(Tok t) const { return type() == t; }
const Token& advance() {
const Token& t = tokens_[pos_];
if (pos_ + 1 < tokens_.size()) {
++pos_;
}
return t;
}
bool expect(Tok t, const char* what) {
if (!check(t)) {
return fail(std::string("expected '") + what + "'");
}
advance();
return true;
}
// ---- POU ----
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;
}
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));
}
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);
}
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;
}
}
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;
}
bool check_end_for(PouKind k) const { return check(end_tok_for(k)); }
// ---- 变量段 ----
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;
}
// ident (',' ident)* ':' type (':=' literal)? ';' —— 展开成多个 VarDecl
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;
}
// 初值只允许字面量(BOOL / INT / TIME / TRUE / FALSE
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");
}
}
// 类型:BOOL | INT | TIME | TON | TOF | CTU | ident(用户 FB 类型)
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");
}
}
// ---- 语句 ----
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);
}
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, ";");
}
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, ";");
}
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");
}
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");
}
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;
}
// ---- 表达式(优先级爬升)----
bool parse_expr(Expr* e) { return parse_or(e); }
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;
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;
}
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;
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;
}
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);
}
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;
}
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;
}
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;
}
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;
}
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;
}
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);
}
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
advance();
e->kind = ExprKind::Field;
e->name = word;
if (!parse_ident(&e->field)) {
return false;
}
return true;
}
if (check(Tok::LPAREN)) {
// 函数调用(FUNCTION,有返回值)
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");
}
}
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_;
Unit* out_;
size_t pos_ = 0;
bool ok_ = true;
};
} // namespace
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();
}
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