215 lines
8.4 KiB
C++
215 lines
8.4 KiB
C++
/**
|
||
* @file Parser.h
|
||
* @brief ST 子集递归下降语法分析(AST 定义 + 入口)
|
||
* @details 本文件定义语法层全部公开类型(AST 节点与相关枚举)与两个
|
||
* 解析入口(parse_pous / parse_pous_file)。解析器实现见
|
||
* Parser.cpp,文法详见 Doc/compiler/语法.md。
|
||
* @author
|
||
* @date 2026-08-21
|
||
*/
|
||
|
||
#pragma once
|
||
|
||
#include <cstdint>
|
||
#include <memory>
|
||
#include <string>
|
||
#include <vector>
|
||
|
||
namespace compiler {
|
||
|
||
// ---- 类型引用 ----
|
||
|
||
/**
|
||
* @brief 类型种类。
|
||
* @details 枚举值:Bool=内建 BOOL;Int=内建 INT;Time=内建 TIME;
|
||
* FbBuiltin=内建 FB 类型(TON/TOF/TP/CTU/CTD/CTUD/R_TRIG/F_TRIG);
|
||
* FbUser=用户 FB 类型(存在性由 12.6 链接层校验)。
|
||
*/
|
||
enum class TypeKind { Bool, Int, Time, FbBuiltin, FbUser };
|
||
|
||
/**
|
||
* @brief 类型引用:种类 + (用户 FB 的)类型名。
|
||
*/
|
||
struct TypeRef {
|
||
TypeKind kind = TypeKind::Bool; ///< 类型种类
|
||
std::string name; ///< FbUser 时为用户 FB 类型名(小写)
|
||
};
|
||
|
||
// ---- 变量段 ----
|
||
|
||
/**
|
||
* @brief 单个变量声明。
|
||
* @details 初值可选(v1 仅字面量);line/col 记录声明处位置(报错用)。
|
||
*/
|
||
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; ///< 声明起始列
|
||
};
|
||
|
||
/**
|
||
* @brief 变量段种类。
|
||
* @details 枚举值:Local=VAR、Input=VAR_INPUT、Output=VAR_OUTPUT、
|
||
* Global=VAR_GLOBAL、External=VAR_EXTERNAL。
|
||
*/
|
||
enum class VarSection { Local, Input, Output, Global, External };
|
||
|
||
/**
|
||
* @brief 一段 VAR_* … END_VAR:段种类 + 声明列表。
|
||
*/
|
||
struct VarBlock {
|
||
VarSection section = VarSection::Local; ///< 段种类
|
||
std::vector<VarDecl> vars; ///< 段内声明(按出现顺序)
|
||
};
|
||
|
||
// ---- 表达式 ----
|
||
|
||
/**
|
||
* @brief 表达式节点种类。
|
||
* @details 二目节点(And/Or/Cmp/Add/Sub/Mul/Div)用 lhs/rhs/op;
|
||
* 一元节点(Not/Neg)用 operand;AND/OR 的短路语义保留在
|
||
* AST 中,由 12.8 编译为跳转。
|
||
*/
|
||
enum class ExprKind {
|
||
LitBool, ///< 布尔字面量(int_value 0/1)
|
||
LitInt, ///< 整数字面量(int_value)
|
||
LitTime, ///< 时间字面量(int_value 毫秒)
|
||
VarRef, ///< 变量引用(name)
|
||
Field, ///< FB 字段访问(name.field)
|
||
Not, ///< 逻辑非(operand)
|
||
And, Or, ///< 逻辑与 / 逻辑或(lhs, rhs;短路语义保留到 12.8)
|
||
Cmp, Add, Sub, Mul, Div, ///< 二目:比较 / 加减 / 乘除(lhs, rhs,op 为运算符)
|
||
Neg, ///< 一元负号(operand)
|
||
Call, ///< 函数调用(name + args,有返回值)
|
||
};
|
||
|
||
/**
|
||
* @brief 二目运算符种类。
|
||
* @details 枚举值:Eq/Ne/Lt/Le/Gt/Ge=比较符(= <> < <= > >=);
|
||
* Add/Sub/Mul/Div=算术符(+ - * /)。
|
||
*/
|
||
enum class BinOp { Eq, Ne, Lt, Le, Gt, Ge, Add, Sub, Mul, Div };
|
||
|
||
/**
|
||
* @brief 表达式 AST 节点(带 kind 标签的联合式结构)。
|
||
* @details 按 kind 解释字段:字面量用 int_value;VarRef/Field/Call 用
|
||
* name(+field、args);二目用 lhs/rhs/op;一元 Not/Neg 用
|
||
* operand。
|
||
*/
|
||
struct Expr {
|
||
ExprKind kind = ExprKind::LitBool; ///< 节点种类
|
||
BinOp op = BinOp::Eq; ///< 二目运算(Cmp/Add/Sub/Mul/Div 用)
|
||
std::string name; ///< VarRef / Field 对象 / Call 函数名
|
||
std::string field; ///< Field 字段名
|
||
int64_t int_value = 0; ///< 字面量值(BOOL 0/1、INT、TIME 毫秒)
|
||
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 实参
|
||
};
|
||
|
||
// ---- 语句 ----
|
||
|
||
/**
|
||
* @brief 语句节点种类。
|
||
* @details 枚举值:Assign=赋值;If=IF 语句(含 ELSIF/ELSE);
|
||
* While=WHILE 语句;FbCall=FB 调用语句(无返回值)。
|
||
*/
|
||
enum class StmtKind { Assign, If, While, FbCall };
|
||
|
||
/** @brief 语句节点(前置声明;Stmt 与 IfBranch 相互引用)。 */
|
||
struct Stmt;
|
||
|
||
/**
|
||
* @brief FB 调用实参:命名参数(名 := 表达式)。
|
||
*/
|
||
struct FbArg {
|
||
std::string name; ///< 形参名(小写)
|
||
std::unique_ptr<Expr> value; ///< 实参表达式
|
||
};
|
||
|
||
/**
|
||
* @brief IF 语句的一个 ELSIF 分支:条件 + 分支体。
|
||
*/
|
||
struct IfBranch {
|
||
std::unique_ptr<Expr> cond; ///< 分支条件
|
||
std::vector<Stmt> body; ///< 分支体(条件成立时执行)
|
||
};
|
||
|
||
/**
|
||
* @brief 语句 AST 节点:按 kind 解释各字段组。
|
||
* @details Assign 用 target(+field);If/While 用 cond/body
|
||
* (+elsifs、else_body);FbCall 用 instance/args。
|
||
* fb.field 左值 v1 禁止赋值,在语法层拒绝。
|
||
*/
|
||
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; ///< ELSIF 分支列表
|
||
std::vector<Stmt> else_body; ///< ELSE 分支体
|
||
// FbCall
|
||
std::string instance; ///< FB 实例名
|
||
std::vector<FbArg> args; ///< 命名实参列表
|
||
};
|
||
|
||
// ---- POU ----
|
||
|
||
/**
|
||
* @brief POU 种类。
|
||
* @details 枚举值:Program=PROGRAM;Function=FUNCTION(可有返回类型);
|
||
* FunctionBlock=FUNCTION_BLOCK。
|
||
*/
|
||
enum class PouKind { Program, Function, FunctionBlock };
|
||
|
||
/**
|
||
* @brief 程序组织单元(POU):外壳 + 变量段 + 语句体。
|
||
*/
|
||
struct POU {
|
||
PouKind kind = PouKind::Program; ///< POU 种类
|
||
std::string name; ///< POU 名(小写)
|
||
TypeRef result_type; ///< 返回类型(仅 FUNCTION 才有)
|
||
std::vector<VarBlock> blocks; ///< 变量段(按声明顺序)
|
||
std::vector<Stmt> body; ///< 语句体
|
||
std::string source_file; ///< 来源文件名
|
||
};
|
||
|
||
/**
|
||
* @brief 一个 .st 文件的解析结果。
|
||
* @details GVL 文件形态(无 POU)时只有 globals 非空。
|
||
*/
|
||
struct Unit {
|
||
std::vector<VarBlock> globals; ///< 顶层 VAR_GLOBAL 段(仅 GVL 文件)
|
||
std::vector<POU> pous; ///< POU 列表(按出现顺序)
|
||
};
|
||
|
||
/**
|
||
* @brief 解析一个 .st 文件的全部内容(顶层 VAR_GLOBAL 段或 POU)。
|
||
* @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);
|
||
|
||
/**
|
||
* @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);
|
||
}
|