From eb20993e275128851796fbd6cb755610f8bac041 Mon Sep 17 00:00:00 2001 From: PuqiAR Date: Fri, 20 Feb 2026 15:46:33 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0If=E8=AF=AD=E5=8F=A5?= =?UTF-8?q?=E5=8F=8A=E5=9D=97=E8=AF=AD=E5=8F=A5=E8=A7=A3=E6=9E=90=E6=94=AF?= =?UTF-8?q?=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .clang-format | 2 +- src/Ast/Ast.hpp | 1 + src/Ast/Base.hpp | 39 ++++++-- src/Ast/Stmt/IfStmt.hpp | 69 ++++++++++++++ src/Parser/Parser.hpp | 4 +- src/Parser/StmtParser.cpp | 196 +++++++++++++++++++++++++++++++++++--- 6 files changed, 288 insertions(+), 23 deletions(-) create mode 100644 src/Ast/Stmt/IfStmt.hpp diff --git a/.clang-format b/.clang-format index 0935b82..f0cc117 100644 --- a/.clang-format +++ b/.clang-format @@ -108,7 +108,7 @@ BreakConstructorInitializers: AfterColon BreakStringLiterals: false # 每行字符的限制,0表示没有限制 -ColumnLimit: 120 +ColumnLimit: 100 CompactNamespaces: true # 构造函数的初始化列表要么都在同一行,要么都各自一行 diff --git a/src/Ast/Ast.hpp b/src/Ast/Ast.hpp index c4ba8f8..e18d5fa 100644 --- a/src/Ast/Ast.hpp +++ b/src/Ast/Ast.hpp @@ -15,4 +15,5 @@ #include #include +#include #include \ No newline at end of file diff --git a/src/Ast/Base.hpp b/src/Ast/Base.hpp index acdda67..1a0df74 100644 --- a/src/Ast/Base.hpp +++ b/src/Ast/Base.hpp @@ -15,10 +15,11 @@ namespace Fig { enum class AstType : std::uint8_t { - AstNode, // 基类 - Program, // 程序 - Expr, // 表达式 - Stmt, // 语句 + AstNode, // 基类 + Program, // 程序 + Expr, // 表达式 + Stmt, // 语句 + BlockStmt, // 块语句 /* Expressions */ IdentiExpr, // 标识符表达式 @@ -30,8 +31,10 @@ namespace Fig CallExpr, // 后缀表达式,函数调用 /* Statements */ - ExprStmt, // 表达式语句,如 println(1) - VarDecl, // 变量声明 + ExprStmt, // 表达式语句,如 println(1) + VarDecl, // 变量声明 + IfStmt, // If语句 + ElseIfStmt, // ElseIf语句,不准悬空 }; struct AstNode { @@ -81,7 +84,29 @@ namespace Fig virtual String toString() const override { - return "Program"; + return ""; + } + }; + + struct BlockStmt final : public Stmt + { + DynArray nodes; + BlockStmt() + { + type = AstType::BlockStmt; + } + BlockStmt(DynArray _nodes) + { + type = AstType::BlockStmt; + nodes = std::move(_nodes); + if (!_nodes.empty()) + { + location = std::move(_nodes.back()->location); + } + } + virtual String toString() const override + { + return ""; } }; }; // namespace Fig diff --git a/src/Ast/Stmt/IfStmt.hpp b/src/Ast/Stmt/IfStmt.hpp new file mode 100644 index 0000000..1aa6c21 --- /dev/null +++ b/src/Ast/Stmt/IfStmt.hpp @@ -0,0 +1,69 @@ +/*! + @file src/Ast/Stmt/IfStmt.hpp + @brief IfStmt定义 + @author PuqiAR (im@puqiar.top) + @date 2026-02-20 +*/ + +#pragma once + +#include + +namespace Fig +{ + struct ElseIfStmt final : public Stmt + { + Expr *cond; + BlockStmt *consequent; + + ElseIfStmt() + { + type = AstType::ElseIfStmt; + } + + ElseIfStmt(Expr *_cond, BlockStmt *_consequent, SourceLocation _location) : + cond(_cond), consequent(_consequent) + { + type = AstType::ElseIfStmt; + location = std::move(_location); + } + + virtual String toString() const override + { + return std::format("", cond->toString(), consequent->toString()); + } + }; + + struct IfStmt final : public Stmt + { + Expr *cond; + BlockStmt *consequent; + DynArray elifs; + BlockStmt *alternate; + + IfStmt() + { + type = AstType::IfStmt; + } + + IfStmt(Expr *_cond, + BlockStmt *_consequent, + DynArray _elifs, + BlockStmt *_alternate, + SourceLocation _location) : + cond(_cond), consequent(_consequent), elifs(std::move(_elifs)), alternate(_alternate) + { + type = AstType::IfStmt; + location = std::move(_location); + } + + virtual String toString() const override + { + return std::format("", + cond->toString(), + consequent->toString(), + elifs.size(), + alternate->toString()); + } + }; +}; // namespace Fig \ No newline at end of file diff --git a/src/Parser/Parser.hpp b/src/Parser/Parser.hpp index cd3d0fd..ce5b989 100644 --- a/src/Parser/Parser.hpp +++ b/src/Parser/Parser.hpp @@ -158,6 +158,7 @@ namespace Fig ParsingCallExpr, ParsingVarDecl, + ParsingIf, } state; @@ -193,8 +194,9 @@ namespace Fig Result parseExpression(BindingPower = 0, TokenType stop = TokenType::Semicolon, TokenType stop2 = TokenType::Semicolon); /* Statements */ + Result parseBlockStmt(); // 当前token为 { Result parseVarDecl(bool); // 由 parseStatement调用, 当前token为 var - + Result parseIfStmt(); // 由 parseStatement调用, 当前token is if Result parseStatement(); public: diff --git a/src/Parser/StmtParser.cpp b/src/Parser/StmtParser.cpp index 5ef3c49..0deea81 100644 --- a/src/Parser/StmtParser.cpp +++ b/src/Parser/StmtParser.cpp @@ -9,7 +9,34 @@ namespace Fig { - Result Parser::parseVarDecl(bool isPublic) // 由 parseStatement调用, 当前token为 var + Result Parser::parseBlockStmt() // 当前token为 { + { + SourceLocation location = makeSourceLocation(consumeToken()); // consume `{` + BlockStmt *stmt = new BlockStmt(); + while (true) + { + if (isEOF) + { + return std::unexpected(Error(ErrorType::SyntaxError, + "unclosed braces in block stmt", + "insert `}`", + location)); + } + if (match(TokenType::RightBrace)) + { + break; + } + const auto &result = parseStatement(); + if (!result) + { + return std::unexpected(result.error()); + } + stmt->nodes.push_back(*result); + } + return stmt; + } + Result Parser::parseVarDecl( + bool isPublic) // 由 parseStatement调用, 当前token为 var { state = State::ParsingVarDecl; @@ -50,8 +77,147 @@ namespace Fig VarDecl *varDecl = new VarDecl(isPublic, name, typeSpeicifer, initExpr, location); return varDecl; } + + Result Parser::parseIfStmt() // 由 parseStatement调用, 当前token is if + { + state = State::ParsingIf; + SourceLocation location = makeSourceLocation(consumeToken()); // consume `if` + + Expr *cond = nullptr; + if (match(TokenType::LeftParen)) // match and consume `(` + { + const Token &lpToken = prevToken(); + const auto &result = parseExpression(0, TokenType::RightParen, TokenType::LeftBrace); + if (!result) + { + return std::unexpected(result.error()); + } + if (!match(TokenType::RightParen)) + { + return std::unexpected(Error(ErrorType::SyntaxError, + "unclosed parenthese in if condition", + "insert `)`", + makeSourceLocation(lpToken))); + } + cond = *result; + } + else + { + const auto &result = parseExpression(0, TokenType::LeftBrace); + if (!result) + { + return std::unexpected(result.error()); + } + cond = *result; + } + state = State::ParsingIf; + + if (currentToken().type != TokenType::LeftBrace) + { + return std::unexpected( + makeUnexpectTokenError("IfStmt", "LeftBrace `{`", currentToken())); + } + const auto &result = parseBlockStmt(); + if (!result) + { + return std::unexpected(result.error()); + } + BlockStmt *consequent = *result; + + DynArray elifs; + BlockStmt *alternate = nullptr; + + while (match(TokenType::Else)) + { + SourceLocation elseLocation = makeSourceLocation(prevToken()); + if (match(TokenType::If)) + { + // else if + if (alternate) + { + return std::unexpected(Error(ErrorType::SyntaxError, + "else if after else", + "remove else if", + elseLocation)); + } + + Expr *cond = nullptr; + + if (match(TokenType::LeftParen)) // `(` + { + const Token &lpToken = prevToken(); + const auto &result = + parseExpression(0, TokenType::RightParen, TokenType::LeftBrace); + if (!result) + { + return std::unexpected(result.error()); + } + state = State::ParsingIf; + if (!match(TokenType::RightParen)) + { + return std::unexpected(Error(ErrorType::SyntaxError, + "unclosed parenthese in if condition", + "insert `)`", + makeSourceLocation(lpToken))); + } + cond = *result; + } + else + { + const auto &result = parseExpression(0, TokenType::LeftBrace); + if (!result) + { + return std::unexpected(result.error()); + } + state = State::ParsingIf; + cond = *result; + } + if (currentToken().type != TokenType::LeftBrace) + { + return std::unexpected( + makeUnexpectTokenError("ElseIfStmt", "LeftBrace `{`", currentToken())); + } + const auto &result = parseBlockStmt(); + if (!result) + { + return std::unexpected(result.error()); + } + state = State::ParsingIf; + BlockStmt *consequent = *result; + ElseIfStmt *elif = new ElseIfStmt(cond, consequent, elseLocation); + elifs.push_back(elif); + } + else + { + // else + if (alternate) + { + return std::unexpected(Error(ErrorType::SyntaxError, + "duplicate else in if stmt", + "remove it", + elseLocation)); + } + if (currentToken().type != TokenType::LeftBrace) + { + return std::unexpected( + makeUnexpectTokenError("ElseStmt", "LeftBrace `{`", currentToken())); + } + const auto &result = parseBlockStmt(); + if (!result) + { + return std::unexpected(result.error()); + } + state = State::ParsingIf; + alternate = *result; + } + } + IfStmt *ifStmt = new IfStmt(cond, consequent, elifs, alternate, location); + return ifStmt; + } + Result Parser::parseStatement() { + state = State::Standby; if (currentToken().type == TokenType::Public) { consumeToken(); // consume `public` @@ -60,23 +226,25 @@ namespace Fig return parseVarDecl(true); } } - else if (currentToken().type == TokenType::Variable) + if (currentToken().type == TokenType::Variable) { return parseVarDecl(false); } - else + if (currentToken().type == TokenType::If) { - const auto &expr_result = parseExpression(0); - if (!expr_result) - { - return std::unexpected(expr_result.error()); - } - ExprStmt *exprStmt = new ExprStmt(*expr_result); - if (!match(TokenType::Semicolon)) - { - return std::unexpected(makeExpectSemicolonError()); - } - return exprStmt; + return parseIfStmt(); } + + const auto &expr_result = parseExpression(0); + if (!expr_result) + { + return std::unexpected(expr_result.error()); + } + ExprStmt *exprStmt = new ExprStmt(*expr_result); + if (!match(TokenType::Semicolon)) + { + return std::unexpected(makeExpectSemicolonError()); + } + return exprStmt; } }; // namespace Fig \ No newline at end of file