Files
Interpreter/Doc/compiler/语法.md
T
Admin 4ba44459e2 实现 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
2026-08-21 11:07:34 +08:00

7.1 KiB
Raw Permalink Blame History

语法(12.5 递归下降)

compiler 模块的语法分析。输入:单个 .st 文件的 token 流(见 词法.md);输出:AST(不分配寄存器、不写映像)。全工程定案见 初步计划.md 12.1、12.5。

做 / 不做

  • POU 外壳:PROGRAM / FUNCTION / FUNCTION_BLOCK 与对应 END_*
  • 变量段:VAR / VAR_INPUT / VAR_OUTPUT / VAR_GLOBAL / VAR_EXTERNAL
  • 类型名只接受 BOOL INT TIME;内置 FB 名 TON TOF CTU;标识符(用户 FB 类型)
  • 语句:赋值、IF / ELSIF / ELSEWHILE、FB 调用 fb(in := …)、字段 fb.Q
  • 表达式:NOTAND / OR(AST 保留短路语义,12.8 编跳转)、比较、加减乘除、一元负号、函数调用 Add(3, 4)
  • 明确拒绝(syntax error,不静默):VAR_IN_OUTVAR_TEMPREFCLASSANY 等保留名

不做(第一版)

  • 寄存器分配、跳转回填、映像(12.8
  • 链式比较 a < b < c、字符串、REAL、指针、VAR_IN_OUT、接口、TYPE 定义
  • 语义检查(未声明变量等归 12.6 / 12.7)

AST 结构

// 类型引用:类型位置出现的名字
enum class TypeKind { Bool, Int, Time, FbBuiltin, FbUser };
struct TypeRef {
    TypeKind kind;
    std::string name;   // FbUser 时为用户 FB 类型名(小写)
};

struct VarDecl {
    std::string name;
    TypeRef type;
    bool has_init;      // 可选初值(v1 仅 BOOL/INT/TIME 字面量)
    int64_t init_value; // BOOL 0/1、INT 值、TIME 毫秒
    uint32_t line, col;
};

enum class VarSection { Local, Input, Output, Global, External };
struct VarBlock {
    VarSection section;
    std::vector<VarDecl> vars;
};

// 表达式
enum class ExprKind {
    LitBool, LitInt, LitTime,
    VarRef,       // 标识符(局部/全局/外部/实例名)
    Field,        // 对象.字段:fb.Q / fb.ET
    Not, And, Or,
    Cmp, Add, Sub, Mul, Div,   // 二目;比较不连锁
    Neg,                        // 一元负号
    Call,                       // 函数调用(仅 FUNCTION,有返回值)
};
enum class BinOp { Eq, Ne, Lt, Le, Gt, Ge, Add, Sub, Mul, Div };

struct Expr {
    ExprKind kind;
    BinOp op;                 // 二目运算
    std::string name;         // VarRef / Field 对象名 / Call 函数名
    std::string field;        // Field 字段名
    int64_t int_value;        // 字面量值
    std::unique_ptr<Expr> lhs, rhs, operand;  // 二目 / NOT / Neg
    std::vector<std::unique_ptr<Expr>> args;  // Call 实参
};

// 语句
enum class StmtKind { Assign, If, While, FbCall };
struct FbArg { std::string name; std::unique_ptr<Expr> value; };
struct IfBranch { std::unique_ptr<Expr> cond; std::vector<Stmt> body; };

struct Stmt {
    StmtKind kind;
    // Assign
    std::string target;       // 左值标识符
    bool target_is_field;     // 左值是 fb.field(v1:只允许读取,禁止赋值)
    std::string field;
    std::unique_ptr<Expr> value;
    // If / While
    std::unique_ptr<Expr> cond;
    std::vector<Stmt> body;           // IF/WHILE 体
    std::vector<IfBranch> elsifs;
    std::vector<Stmt> else_body;
    // FbCall
    std::string instance;
    std::vector<FbArg> args;
};

enum class PouKind { Program, Function, FunctionBlock };
struct POU {
    PouKind kind;
    std::string name;
    TypeRef result_type;      // FUNCTION 才有
    std::vector<VarBlock> blocks;
    std::vector<Stmt> body;
    std::string source_file;
};

文法(递归下降)

file         := (VAR_GLOBAL vars END_VAR | POU)*
POU          := PROGRAM ident var_block* body END_PROGRAM
              | FUNCTION ident ':' type var_block* body END_FUNCTION
              | FUNCTION_BLOCK ident var_block* body END_FUNCTION_BLOCK
var_block    := VAR vars END_VAR
              | VAR_INPUT vars END_VAR | VAR_OUTPUT vars END_VAR
              | VAR_GLOBAL vars END_VAR | VAR_EXTERNAL vars END_VAR
vars         := (ident (',' ident)* ':' type (':=' literal)? ';')*
type         := BOOL | INT | TIME | TON | TOF | CTU | ident
body         := stmt*
stmt         := ident ('.' ident)? ':=' expr ';'        (* 赋值 *)
              | ident '(' arg (',' arg)* ')' ';'        (* FB 调用 *)
              | IF expr THEN body (ELSIF expr THEN body)* (ELSE body)? END_IF
              | WHILE expr DO body END_WHILE
arg          := ident ':=' expr
expr         := or_expr
or_expr      := and_expr (OR and_expr)*
and_expr     := not_expr (AND not_expr)*
not_expr     := NOT not_expr | cmp_expr
cmp_expr     := add_expr (('='|'<>'|'<'|'<='|'>'|'>=') add_expr)?
add_expr     := mul_expr (('+'|'-') mul_expr)*
mul_expr     := unary (('*'|'/') unary)*
unary        := '-' unary | primary
primary      := INT_LIT | TIME_LIT | TRUE | FALSE | ident
              | ident '.' ident                          (* fb.field *)
              | ident '(' expr (',' expr)* ')'          (* 函数调用 *)
              | '(' expr ')'

要点:

  • file 顶层允许无 POU 的 VAR_GLOBAL 段(GVL 文件形态,如 globals.st); 其他变量段出现在顶层 → syntax error。解析结果:
    struct Unit {
        std::vector<VarBlock> globals;  // 顶层 VAR_GLOBAL 段
        std::vector<POU> pous;
    };
    
  • THEN / DO 是关键字(12.5 修订补入词法表,见 词法.md
  • 赋值左侧 ident.ident 是字段引用,v1 只能读(右侧),左侧字段赋值报错
  • ident(...)语句即 FB 调用(无返回值);在表达式里即函数调用(有返回值)——靠上下文区分,与 12.1 语义一致
  • 比较不连锁(a < b < c → syntax error
  • AND / OR 短路语义保留在 ASTAnd/Or 节点),12.8 编成跳转

明确拒绝清单

IDENT 文本(小写后)命中下列保留名 → syntax error: 'X' is not supported

var_in_out  var_temp  ref  class  any  pointer  interface  method

错误

稳定前缀 syntax error,带文件与行列:

syntax error: expected 'END_PROGRAM' (main.st, line 5, col 3)
syntax error: 'VAR_IN_OUT' is not supported (motor.st, line 2, col 5)
syntax error: chained comparison not supported (main.st, line 7, col 15)

完成标准

  1. examples/line1/*.st 三文件 parse 成功,AST 结构符合文法
  2. parser_test:AST 断言(POU 种类/变量段/语句/表达式优先级)+ 负例(缺 END_*VAR_IN_OUT、链式比较、字段赋值、坏语句)→ syntax error
  3. 全部构建 + ctest 无回归
  4. 用例 10 / 14 / 19 属 12.6 / 12.7 层(非语法层),不在本阶段点亮

执行计划(单步确认)

  1. Parser.h(新建 compiler/include/compiler/Parser.h:上述 AST 结构 + bool parse_pous(source_file, content, out, err)
  2. Parser.cpp 前半:POU 外壳 + 变量段 + 类型(文法 program / POU / var_block / vars / type
  3. Parser.cpp 后半:语句 + 表达式(优先级爬升)+ 拒绝清单
  4. parser_testtests/src/parser_test.cpp + CMakeline1 三文件 AST 断言 + 负例
  5. 验证cmake --build + ctest(新增 parser_syntax 后 6/6 全绿)
  6. 提交Parser.h/cpp + parser_test + 文档