feat: implement for-loops with proper scope management
- Add support for C-style for loops: for (init; condition; increment) { body }
- Implement three-level scoping: loop context + per-iteration contexts
- Add semicolon disabler for increment statements using RAII guards
- Support break/continue/return control flow with scope validation
- Fix semicolon handling in parser for flexible for-loop syntax
This commit is contained in:
30
include/Ast/ForSt.hpp
Normal file
30
include/Ast/ForSt.hpp
Normal file
@@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
#include <Ast/astBase.hpp>
|
||||
|
||||
namespace Fig::Ast
|
||||
{
|
||||
class ForSt final : public StatementAst
|
||||
{
|
||||
public:
|
||||
Statement initSt;
|
||||
Expression condition;
|
||||
Statement incrementSt;
|
||||
BlockStatement body;
|
||||
|
||||
ForSt()
|
||||
{
|
||||
type = AstType::ForSt;
|
||||
}
|
||||
ForSt(Statement _initSt, Expression _condition, Statement _incrementSt, BlockStatement _body) :
|
||||
initSt(std::move(_initSt)),
|
||||
condition(std::move(_condition)),
|
||||
incrementSt(std::move(_incrementSt)),
|
||||
body(std::move(_body))
|
||||
{
|
||||
type = AstType::ForSt;
|
||||
}
|
||||
};
|
||||
|
||||
using For = std::shared_ptr<ForSt>;
|
||||
};
|
||||
@@ -47,6 +47,7 @@ namespace Fig::Ast
|
||||
|
||||
VarAssignSt,
|
||||
WhileSt,
|
||||
ForSt,
|
||||
ReturnSt,
|
||||
BreakSt,
|
||||
ContinueSt,
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <Ast/ContainerInitExprs.hpp>
|
||||
#include <Ast/ControlSt.hpp>
|
||||
#include <Ast/ExpressionStmt.hpp>
|
||||
#include <Ast/ForSt.hpp>
|
||||
#include <Ast/FunctionCall.hpp>
|
||||
#include <Ast/functionParameters.hpp>
|
||||
#include <Ast/FunctionDefSt.hpp>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
#include <unordered_map>
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
|
||||
#include <context_forward.hpp>
|
||||
#include <fig_string.hpp>
|
||||
@@ -10,7 +10,7 @@
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
struct Context
|
||||
class Context : public std::enable_shared_from_this<Context>
|
||||
{
|
||||
private:
|
||||
FString scopeName;
|
||||
@@ -145,6 +145,33 @@ namespace Fig
|
||||
}
|
||||
throw RuntimeError(FStringView(std::format("Variable '{}' not defined", name.toBasicString())));
|
||||
}
|
||||
bool isInFunctionContext()
|
||||
{
|
||||
ContextPtr ctx = shared_from_this();
|
||||
while (ctx)
|
||||
{
|
||||
if (ctx->getScopeName().find(u8"<Function ") == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
ctx = ctx->parent;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool isInLoopContext()
|
||||
{
|
||||
ContextPtr ctx = shared_from_this();
|
||||
while (ctx)
|
||||
{
|
||||
if (ctx->getScopeName().find(u8"<While ") == 0 or
|
||||
ctx->getScopeName().find(u8"<For ") == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
ctx = ctx->parent;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
void printStackTrace(std::ostream &os = std::cerr, int indent = 0) const
|
||||
{
|
||||
const Context *ctx = this;
|
||||
|
||||
@@ -4,6 +4,6 @@
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
struct Context;
|
||||
class Context;
|
||||
using ContextPtr = std::shared_ptr<Context>;
|
||||
};
|
||||
@@ -111,6 +111,7 @@ namespace Fig
|
||||
Value evalBinary(const Ast::BinaryExpr &);
|
||||
Value evalUnary(const Ast::UnaryExpr &);
|
||||
|
||||
StatementResult evalBlockStatement(const Ast::BlockStatement &, ContextPtr = nullptr);
|
||||
StatementResult evalStatement(const Ast::Statement &);
|
||||
|
||||
Value evalFunctionCall(const Function &, const Ast::FunctionArguments &, FString fnName = u8"<anonymous>");
|
||||
|
||||
@@ -81,7 +81,6 @@ namespace Fig
|
||||
{
|
||||
return warnings;
|
||||
}
|
||||
|
||||
Token nextToken();
|
||||
|
||||
Token scanNumber();
|
||||
|
||||
@@ -29,6 +29,28 @@ namespace Fig
|
||||
|
||||
std::stack<Ast::Expression> exprStack;
|
||||
|
||||
bool needSemicolon = true;
|
||||
|
||||
class SemicolonDisabler
|
||||
{
|
||||
Parser &p;
|
||||
bool original;
|
||||
|
||||
public:
|
||||
SemicolonDisabler(Parser &parser) :
|
||||
p(parser), original(p.needSemicolon)
|
||||
{
|
||||
p.needSemicolon = false;
|
||||
}
|
||||
~SemicolonDisabler()
|
||||
{
|
||||
p.needSemicolon = original;
|
||||
}
|
||||
// disable copy and assign
|
||||
SemicolonDisabler(const SemicolonDisabler &) = delete;
|
||||
SemicolonDisabler &operator=(const SemicolonDisabler &) = delete;
|
||||
};
|
||||
|
||||
void pushNode(const Ast::AstBase &_node)
|
||||
{
|
||||
Ast::AstBase node = _node;
|
||||
@@ -109,7 +131,7 @@ namespace Fig
|
||||
return currentAAI;
|
||||
}
|
||||
|
||||
inline Token nextToken()
|
||||
inline const Token &nextToken()
|
||||
{
|
||||
// 没有Rollback时, 存在 currentTokenIndex = tokenPruduced - 1
|
||||
next();
|
||||
@@ -143,7 +165,7 @@ namespace Fig
|
||||
setCurrentAAI(Ast::AstAddressInfo{.line = tok.line, .column = tok.column});
|
||||
previousTokens.push_back(tok);
|
||||
}
|
||||
inline Token currentToken()
|
||||
inline const Token ¤tToken()
|
||||
{
|
||||
if (tokenPruduced == 0) return nextToken();
|
||||
return previousTokens.at(currentTokenIndex);
|
||||
@@ -229,6 +251,41 @@ namespace Fig
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] SemicolonDisabler disableSemicolon()
|
||||
{
|
||||
return SemicolonDisabler(*this);
|
||||
}
|
||||
|
||||
void expectSemicolon()
|
||||
{
|
||||
// if need semicolon and stream has `;`, consume it. if not need semicolon, do nothing
|
||||
|
||||
if (!needSemicolon)
|
||||
{
|
||||
// disabled semicolon check
|
||||
if (currentToken().getType() == TokenType::Semicolon)
|
||||
{
|
||||
next(); // consume `;`
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// normal semicolon check
|
||||
expectConsume(TokenType::Semicolon);
|
||||
}
|
||||
|
||||
void expectConsume(TokenType type, FString expected)
|
||||
{
|
||||
expect(type, expected);
|
||||
next();
|
||||
}
|
||||
|
||||
void expectConsume(TokenType type)
|
||||
{
|
||||
expect(type);
|
||||
next();
|
||||
}
|
||||
|
||||
bool isNext(TokenType type)
|
||||
{
|
||||
return peekToken().getType() == type;
|
||||
@@ -244,12 +301,15 @@ namespace Fig
|
||||
Value __parseValue();
|
||||
Ast::ValueExpr __parseValueExpr();
|
||||
Ast::FunctionParameters __parseFunctionParameters(); // entry: current is Token::LeftParen
|
||||
Ast::Statement __parseStatement(); // entry: (idk)
|
||||
Ast::BlockStatement __parseBlockStatement(); // entry: current is Token::LeftBrace
|
||||
Ast::VarAssign __parseVarAssign(FString); // entry: current is Token::Assign, para1 is var name
|
||||
Ast::If __parseIf(); // entry: current is Token::If
|
||||
Ast::While __parseWhile(); // entry: current is Token::While
|
||||
Ast::Statement __parseIncrementStatement(); // only allowed in __parseFor function
|
||||
Ast::For __parseFor(); // entry: current is Token::For
|
||||
Ast::Return __parseReturn(); // entry: current is Token::Return
|
||||
Ast::Break __parseBreak(); // entry: current is Token::Break
|
||||
Ast::Continue __parseContinue(); // entry: current is Token::Continue
|
||||
|
||||
Ast::VarExpr __parseVarExpr(FString);
|
||||
Ast::FunctionDef __parseFunctionDef(bool); // entry: current is Token::Identifier (isPublic: Bool)
|
||||
@@ -266,7 +326,8 @@ namespace Fig
|
||||
Ast::Expression __parseTupleOrParenExpr(); // entry: current is `(`
|
||||
|
||||
Ast::FunctionLiteralExpr __parseFunctionLiteralExpr(); // entry: current is Token::LParen after Token::Function
|
||||
|
||||
|
||||
Ast::Statement __parseStatement(); // entry: (idk)
|
||||
Ast::Expression parseExpression(Precedence, TokenType = TokenType::Semicolon, TokenType = TokenType::Semicolon);
|
||||
std::vector<Ast::AstBase> parseAll();
|
||||
};
|
||||
|
||||
@@ -35,6 +35,8 @@ namespace Fig
|
||||
Implement, // implement
|
||||
Public, // public
|
||||
Return, // return
|
||||
Break, // break
|
||||
Continue, // continue
|
||||
|
||||
// TypeNull, // Null
|
||||
// TypeInt, // Int
|
||||
@@ -130,7 +132,7 @@ namespace Fig
|
||||
column = _column;
|
||||
return *this;
|
||||
}
|
||||
FString getValue()
|
||||
const FString& getValue() const
|
||||
{
|
||||
return value;
|
||||
}
|
||||
@@ -151,7 +153,7 @@ namespace Fig
|
||||
return type == TokenType::LiteralNull || type == TokenType::LiteralBool || type == TokenType::LiteralNumber || type == TokenType::LiteralString;
|
||||
}
|
||||
|
||||
TokenType getType()
|
||||
TokenType getType() const
|
||||
{
|
||||
return type;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user