Files
Interpreter/compiler/include/compiler/Parser.h
T

215 lines
8.4 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.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=内建 BOOLInt=内建 INTTime=内建 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)用 operandAND/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_valueVarRef/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.fieldv1 禁止赋值,语法层拒绝)
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=PROGRAMFunction=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 输出 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);
/**
* @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);
}