实现 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:
@@ -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)
|
||||
|
||||
@@ -37,6 +37,8 @@ namespace compiler {
|
||||
END_IF,
|
||||
WHILE,
|
||||
END_WHILE,
|
||||
THEN, // 12.5 修订:IF/WHILE 语法需要,补入关键字表
|
||||
DO, // 同上
|
||||
AND,
|
||||
OR,
|
||||
NOT,
|
||||
|
||||
@@ -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, rhs,op
|
||||
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)。
|
||||
// 失败返回 false,err 前缀 "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);
|
||||
}
|
||||
@@ -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},
|
||||
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user