Compare commits
6 Commits
e1d9812f92
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 680197aafe | |||
| 4f87078a87 | |||
| 9338c21449 | |||
| 98de782760 | |||
| fafa2b4946 | |||
| 570a87c3cd |
@@ -6,7 +6,7 @@ Language: Cpp
|
||||
AccessModifierOffset: -4
|
||||
|
||||
# 开括号(开圆括号、开尖括号、开方括号)后的对齐: Align, DontAlign, AlwaysBreak(总是在开括号后换行)
|
||||
AlignAfterOpenBracket: DontAlign
|
||||
AlignAfterOpenBracket: AlwaysBreak
|
||||
|
||||
# 连续赋值时,对齐所有等号
|
||||
AlignConsecutiveAssignments: true
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
from time import time as tt
|
||||
|
||||
def fib(x:int) -> int:
|
||||
if x <= 1: return x;
|
||||
if x <= 1: return x
|
||||
return fib(x-1) + fib(x-2)
|
||||
|
||||
if __name__ == '__main__':
|
||||
t0 = tt()
|
||||
result = fib(30)
|
||||
result = fib(35)
|
||||
t1 = tt()
|
||||
print('cost: ',t1-t0, 'result:', result)
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
# Fig
|
||||
|
||||
## tmd赶工代码质量太差了暑假我要重构
|
||||
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="./Logo/LogoDark.svg">
|
||||
<img src="./Logo/Logo.svg" alt="Fig Logo" width="200">
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
# Fig
|
||||
|
||||
## tmd赶工代码质量太差了暑假我要重构
|
||||
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="./Logo/LogoDark.svg">
|
||||
<img src="./Logo/Logo.svg" alt="Fig Logo" width="200">
|
||||
|
||||
@@ -11,13 +11,23 @@
|
||||
#include <Ast/Expr/IdentiExpr.hpp>
|
||||
#include <Ast/Expr/IndexExpr.hpp>
|
||||
#include <Ast/Expr/InfixExpr.hpp>
|
||||
#include <Ast/Expr/LambdaExpr.hpp>
|
||||
#include <Ast/Expr/LiteralExpr.hpp>
|
||||
#include <Ast/Expr/MemberExpr.hpp>
|
||||
#include <Ast/Expr/NewExpr.hpp>
|
||||
#include <Ast/Expr/PrefixExpr.hpp>
|
||||
#include <Ast/Expr/TernaryExpr.hpp>
|
||||
#include <Ast/Expr/PostfixExpr.hpp>
|
||||
|
||||
#include <Ast/Stmt/ControlFlowStmts.hpp>
|
||||
#include <Ast/Stmt/ExprStmt.hpp>
|
||||
#include <Ast/Stmt/FnDefStmt.hpp>
|
||||
#include <Ast/Stmt/IfStmt.hpp>
|
||||
#include <Ast/Stmt/ImplStmt.hpp>
|
||||
#include <Ast/Stmt/InterfaceDefStmt.hpp>
|
||||
#include <Ast/Stmt/StructDefStmt.hpp>
|
||||
#include <Ast/Stmt/ForStmt.hpp>
|
||||
#include <Ast/Stmt/ImportStmt.hpp>
|
||||
#include <Ast/Stmt/VarDecl.hpp>
|
||||
#include <Ast/Stmt/WhileStmt.hpp>
|
||||
#include <Ast/TypeExpr.hpp>
|
||||
@@ -29,7 +29,10 @@ namespace Fig
|
||||
IndexExpr,
|
||||
CallExpr,
|
||||
MemberExpr, // obj.prop
|
||||
ObjectInitExpr, // new Point{}
|
||||
NewExpr, // new Point{}
|
||||
LambdaExpr,
|
||||
TernaryExpr, // cond ? then : else
|
||||
PostfixExpr, // expr++ / expr--
|
||||
|
||||
/* Statements */
|
||||
ExprStmt,
|
||||
@@ -44,11 +47,16 @@ namespace Fig
|
||||
ReturnStmt,
|
||||
BreakStmt,
|
||||
ContinueStmt,
|
||||
ForStmt, // for loop
|
||||
ImportStmt, // import
|
||||
|
||||
/* Type Expressions */
|
||||
TypeExpr,
|
||||
NamedTypeExpr,
|
||||
NullableTypeExpr
|
||||
NamedTypeExpr, // 废弃,用 IdentiExpr/MemberExpr/ApplyExpr 替代
|
||||
NullableTypeExpr, // 废弃,用 NullableExpr 替代
|
||||
FnTypeExpr,
|
||||
ApplyExpr, // 泛型实例化: List<Int>
|
||||
NullableExpr, // 可空后缀: Int?
|
||||
};
|
||||
|
||||
struct AstNode
|
||||
@@ -60,15 +68,6 @@ namespace Fig
|
||||
virtual ~AstNode() {};
|
||||
};
|
||||
|
||||
struct TypeExpr : public AstNode
|
||||
{
|
||||
TypeExpr()
|
||||
{
|
||||
type = AstType::TypeExpr;
|
||||
}
|
||||
virtual ~TypeExpr() = default;
|
||||
};
|
||||
|
||||
struct Expr : public AstNode
|
||||
{
|
||||
// 语义分析后填充
|
||||
@@ -114,4 +113,55 @@ namespace Fig
|
||||
return "<BlockStmt>";
|
||||
}
|
||||
};
|
||||
|
||||
// --- Type Expressions (inherit Expr — 类型即值) ---
|
||||
|
||||
struct TypeExpr : public Expr
|
||||
{
|
||||
TypeExpr() { type = AstType::TypeExpr; }
|
||||
virtual ~TypeExpr() = default;
|
||||
};
|
||||
|
||||
// ApplyExpr: 泛型实例化,List<Int> → ApplyExpr(base, [Int])
|
||||
struct ApplyExpr final : public Expr
|
||||
{
|
||||
Expr *base; // 基础类型表达式
|
||||
DynArray<Expr *> args; // 泛型参数
|
||||
|
||||
ApplyExpr() { type = AstType::ApplyExpr; }
|
||||
ApplyExpr(Expr *_base, DynArray<Expr *> _args, SourceLocation _loc) :
|
||||
base(_base), args(std::move(_args))
|
||||
{
|
||||
type = AstType::ApplyExpr;
|
||||
location = std::move(_loc);
|
||||
}
|
||||
virtual String toString() const override
|
||||
{
|
||||
String s = base->toString() + "<";
|
||||
for (size_t i = 0; i < args.size(); ++i)
|
||||
{
|
||||
if (i) s += ", ";
|
||||
s += args[i]->toString();
|
||||
}
|
||||
s += ">";
|
||||
return s;
|
||||
}
|
||||
};
|
||||
|
||||
// NullableExpr: 可空后缀 Int? → NullableExpr(Int)
|
||||
struct NullableExpr final : public Expr
|
||||
{
|
||||
Expr *inner;
|
||||
|
||||
NullableExpr() { type = AstType::NullableExpr; }
|
||||
NullableExpr(Expr *_inner, SourceLocation _loc) : inner(_inner)
|
||||
{
|
||||
type = AstType::NullableExpr;
|
||||
location = std::move(_loc);
|
||||
}
|
||||
virtual String toString() const override
|
||||
{
|
||||
return inner->toString() + "?";
|
||||
}
|
||||
};
|
||||
} // namespace Fig
|
||||
|
||||
@@ -47,9 +47,10 @@ namespace Fig
|
||||
type = AstType::CallExpr;
|
||||
}
|
||||
|
||||
CallExpr(Expr *_callee, FnCallArgs _args) : callee(_callee), args(std::move(_args))
|
||||
CallExpr(Expr *_callee, FnCallArgs _args, SourceLocation _location) : callee(_callee), args(std::move(_args))
|
||||
{
|
||||
type = AstType::CallExpr;
|
||||
location = std::move(_location);
|
||||
}
|
||||
|
||||
virtual String toString() const override
|
||||
|
||||
70
src/Ast/Expr/LambdaExpr.hpp
Normal file
70
src/Ast/Expr/LambdaExpr.hpp
Normal file
@@ -0,0 +1,70 @@
|
||||
/*!
|
||||
@file src/Ast/Expr/LambdaExpr.hpp
|
||||
@brief Lambda表达式定义
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Ast/Base.hpp>
|
||||
#include <Ast/Stmt/FnDefStmt.hpp>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
struct LambdaExpr final : public Expr
|
||||
{
|
||||
// func (params) [-> return type] ([=> expr] / [ {stmt} ])
|
||||
|
||||
DynArray<Param *> params;
|
||||
Expr *returnType;
|
||||
AstNode *body; // expr/blockstmt
|
||||
bool isExprBody;
|
||||
|
||||
DynArray<UpvalueInfo> upvalues;
|
||||
|
||||
LambdaExpr()
|
||||
{
|
||||
type = AstType::LambdaExpr;
|
||||
}
|
||||
|
||||
LambdaExpr(
|
||||
DynArray<Param *> _params,
|
||||
Expr *_returnType,
|
||||
AstNode *_body,
|
||||
bool _isExprBody,
|
||||
SourceLocation _location) :
|
||||
params(std::move(_params)),
|
||||
returnType(_returnType),
|
||||
body(_body),
|
||||
isExprBody(_isExprBody)
|
||||
{
|
||||
type = AstType::LambdaExpr;
|
||||
location = std::move(_location);
|
||||
}
|
||||
|
||||
virtual String toString() const override
|
||||
{
|
||||
String specifying = "<LambdaExpr 'func (";
|
||||
for (auto &p : params)
|
||||
{
|
||||
if (p != params.front())
|
||||
{
|
||||
specifying += ", ";
|
||||
}
|
||||
specifying += p->toString();
|
||||
}
|
||||
if (isExprBody)
|
||||
{
|
||||
specifying += ") => ";
|
||||
specifying += body->toString();
|
||||
}
|
||||
else
|
||||
{
|
||||
specifying += ") {";
|
||||
specifying += body->toString();
|
||||
specifying.push_back(U'}');
|
||||
}
|
||||
specifying += "'>";
|
||||
return specifying;
|
||||
}
|
||||
};
|
||||
}; // namespace Fig
|
||||
@@ -11,30 +11,30 @@
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
struct ObjectInitExpr final : public Expr
|
||||
struct NewExpr final : public Expr
|
||||
{
|
||||
struct Arg
|
||||
{
|
||||
String name;
|
||||
Expr *value;
|
||||
};
|
||||
TypeExpr *typeExpr;
|
||||
Expr *typeExpr;
|
||||
DynArray<Arg> args;
|
||||
|
||||
ObjectInitExpr()
|
||||
NewExpr()
|
||||
{
|
||||
type = AstType::ObjectInitExpr;
|
||||
type = AstType::NewExpr;
|
||||
}
|
||||
ObjectInitExpr(TypeExpr *_te, DynArray<Arg> _args, SourceLocation _loc) :
|
||||
NewExpr(Expr *_te, DynArray<Arg> _args, SourceLocation _loc) :
|
||||
typeExpr(_te), args(std::move(_args))
|
||||
{
|
||||
type = AstType::ObjectInitExpr;
|
||||
type = AstType::NewExpr;
|
||||
location = std::move(_loc);
|
||||
}
|
||||
|
||||
virtual String toString() const override
|
||||
{
|
||||
String res = "<ObjectInitExpr 'new " + typeExpr->toString() + "{";
|
||||
String res = "<NewExpr 'new " + typeExpr->toString() + "{";
|
||||
for (size_t i = 0; i < args.size(); ++i)
|
||||
{
|
||||
if (!args[i].name.empty())
|
||||
35
src/Ast/Expr/PostfixExpr.hpp
Normal file
35
src/Ast/Expr/PostfixExpr.hpp
Normal file
@@ -0,0 +1,35 @@
|
||||
/*!
|
||||
@file src/Ast/Expr/PostfixExpr.hpp
|
||||
@brief expr++ / expr--
|
||||
@author PuqiAR (im@puqiar.top)
|
||||
@date 2026-06-06
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Ast/Base.hpp>
|
||||
#include <Ast/Operator.hpp>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
struct PostfixExpr final : public Expr
|
||||
{
|
||||
UnaryOperator op;
|
||||
Expr *operand;
|
||||
|
||||
PostfixExpr() { type = AstType::PostfixExpr; }
|
||||
|
||||
PostfixExpr(UnaryOperator _op, Expr *_operand) :
|
||||
op(_op), operand(_operand)
|
||||
{
|
||||
type = AstType::PostfixExpr;
|
||||
location = _operand->location;
|
||||
}
|
||||
|
||||
virtual String toString() const override
|
||||
{
|
||||
return std::format("<PostfixExpr: {} '{}'>",
|
||||
operand->toString(), magic_enum::enum_name(op));
|
||||
}
|
||||
};
|
||||
} // namespace Fig
|
||||
35
src/Ast/Expr/TernaryExpr.hpp
Normal file
35
src/Ast/Expr/TernaryExpr.hpp
Normal file
@@ -0,0 +1,35 @@
|
||||
/*!
|
||||
@file src/Ast/Expr/TernaryExpr.hpp
|
||||
@brief cond ? then : else
|
||||
@author PuqiAR (im@puqiar.top)
|
||||
@date 2026-06-06
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Ast/Base.hpp>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
struct TernaryExpr final : public Expr
|
||||
{
|
||||
Expr *cond;
|
||||
Expr *thenExpr;
|
||||
Expr *elseExpr;
|
||||
|
||||
TernaryExpr() { type = AstType::TernaryExpr; }
|
||||
|
||||
TernaryExpr(Expr *_cond, Expr *_then, Expr *_else, SourceLocation _loc) :
|
||||
cond(_cond), thenExpr(_then), elseExpr(_else)
|
||||
{
|
||||
type = AstType::TernaryExpr;
|
||||
location = std::move(_loc);
|
||||
}
|
||||
|
||||
virtual String toString() const override
|
||||
{
|
||||
return std::format("<TernaryExpr: {} ? {} : {}>",
|
||||
cond->toString(), thenExpr->toString(), elseExpr->toString());
|
||||
}
|
||||
};
|
||||
} // namespace Fig
|
||||
@@ -16,6 +16,8 @@ namespace Fig
|
||||
{TokenType::Minus, UnaryOperator::Negate},
|
||||
{TokenType::Not, UnaryOperator::Not},
|
||||
{TokenType::Ampersand, UnaryOperator::AddressOf},
|
||||
{TokenType::DoublePlus, UnaryOperator::Increment},
|
||||
{TokenType::DoubleMinus, UnaryOperator::Decrement},
|
||||
};
|
||||
return unaryOpMap;
|
||||
}
|
||||
@@ -40,6 +42,8 @@ namespace Fig
|
||||
|
||||
{TokenType::And, BinaryOperator::LogicalAnd},
|
||||
{TokenType::Or, BinaryOperator::LogicalOr},
|
||||
{TokenType::DoubleAmpersand, BinaryOperator::LogicalAnd},
|
||||
{TokenType::DoublePipe, BinaryOperator::LogicalOr},
|
||||
|
||||
{TokenType::Power, BinaryOperator::Power},
|
||||
|
||||
@@ -49,6 +53,7 @@ namespace Fig
|
||||
{TokenType::AsteriskEqual, BinaryOperator::MultiplyAssign},
|
||||
{TokenType::SlashEqual, BinaryOperator::DivideAssign},
|
||||
{TokenType::PercentEqual, BinaryOperator::ModuloAssign},
|
||||
{TokenType::Caret, BinaryOperator::BitXor},
|
||||
{TokenType::CaretEqual, BinaryOperator::BitXorAssign},
|
||||
|
||||
{TokenType::Pipe, BinaryOperator::BitOr},
|
||||
@@ -56,7 +61,7 @@ namespace Fig
|
||||
{TokenType::ShiftLeft, BinaryOperator::ShiftLeft},
|
||||
{TokenType::ShiftRight, BinaryOperator::ShiftRight},
|
||||
|
||||
{TokenType::Dot, BinaryOperator::MemberAccess},
|
||||
{TokenType::As, BinaryOperator::As},
|
||||
};
|
||||
return binaryOpMap;
|
||||
}
|
||||
@@ -78,6 +83,8 @@ namespace Fig
|
||||
{UnaryOperator::Negate, 20001},
|
||||
{UnaryOperator::Not, 20001},
|
||||
{UnaryOperator::AddressOf, 20001},
|
||||
{UnaryOperator::Increment, 20001},
|
||||
{UnaryOperator::Decrement, 20001},
|
||||
};
|
||||
return unbpm;
|
||||
}
|
||||
@@ -109,6 +116,7 @@ namespace Fig
|
||||
{BinaryOperator::GreaterEqual, 2100},
|
||||
|
||||
{BinaryOperator::Is, 2100},
|
||||
{BinaryOperator::As, 2100},
|
||||
|
||||
{BinaryOperator::ShiftLeft, 3000},
|
||||
{BinaryOperator::ShiftRight, 3000},
|
||||
@@ -117,10 +125,9 @@ namespace Fig
|
||||
{BinaryOperator::Subtract, 4000},
|
||||
{BinaryOperator::Multiply, 4500},
|
||||
{BinaryOperator::Divide, 4500},
|
||||
{BinaryOperator::Modulo, 4500},
|
||||
|
||||
{BinaryOperator::Power, 5000},
|
||||
|
||||
{BinaryOperator::MemberAccess, 40001},
|
||||
};
|
||||
return bnbpm;
|
||||
}
|
||||
@@ -153,6 +160,8 @@ namespace Fig
|
||||
case BinaryOperator::BitXorAssign:
|
||||
case BinaryOperator::Power: return GetBinaryOpLBp(op) - 1;
|
||||
|
||||
case BinaryOperator::As: return GetBinaryOpLBp(op) + 1;
|
||||
|
||||
default:
|
||||
/*
|
||||
左结合, 左绑定力 < 右
|
||||
|
||||
@@ -20,6 +20,8 @@ namespace Fig
|
||||
Negate, // 取反 -
|
||||
Not, // 逻辑非 ! / not
|
||||
AddressOf, // 取引用 &
|
||||
Increment, // ++
|
||||
Decrement, // --
|
||||
|
||||
Count // 哨兵,(int) Count 获得运算符数量(注意,enum必须从 0 开始且不中断)
|
||||
};
|
||||
@@ -60,6 +62,8 @@ namespace Fig
|
||||
ShiftLeft, // 左移
|
||||
ShiftRight, // 右移
|
||||
|
||||
As, // as
|
||||
|
||||
// 成员访问
|
||||
MemberAccess, // .
|
||||
|
||||
|
||||
@@ -11,34 +11,34 @@
|
||||
namespace Fig
|
||||
{
|
||||
struct Param : public AstNode {
|
||||
String name;
|
||||
TypeExpr *typeSpecifier;
|
||||
Expr *defaultValue;
|
||||
Type resolvedType;
|
||||
String name;
|
||||
Expr *typeSpecifier;
|
||||
Expr *defaultValue;
|
||||
Type resolvedType;
|
||||
Param() { type = AstType::AstNode; }
|
||||
virtual ~Param() = default;
|
||||
};
|
||||
|
||||
struct PosParam final : public Param {
|
||||
PosParam(String _n, TypeExpr *_ts, Expr *_dv, SourceLocation _loc) {
|
||||
PosParam(String _n, Expr *_ts, Expr *_dv, SourceLocation _loc) {
|
||||
name = std::move(_n); typeSpecifier = _ts; defaultValue = _dv; location = std::move(_loc);
|
||||
}
|
||||
virtual String toString() const override { return name; }
|
||||
};
|
||||
|
||||
struct FnDefStmt final : public Stmt {
|
||||
String name;
|
||||
String name;
|
||||
DynArray<Param *> params;
|
||||
TypeExpr *returnTypeSpecifier;
|
||||
BlockStmt *body;
|
||||
Type resolvedReturnType;
|
||||
Symbol *resolvedSymbol = nullptr; // 连接物理符号
|
||||
Expr *returnTypeSpecifier;
|
||||
BlockStmt *body;
|
||||
Type resolvedReturnType;
|
||||
Symbol *resolvedSymbol = nullptr; // 连接物理符号
|
||||
|
||||
int protoIndex = -1; // 在CompiledModule扁平化protos的下标
|
||||
DynArray<UpvalueInfo> upvalues;
|
||||
|
||||
FnDefStmt() { type = AstType::FnDefStmt; }
|
||||
FnDefStmt(bool _p, String _n, DynArray<Param *> _pa, TypeExpr *_rt, BlockStmt *_b, SourceLocation _loc)
|
||||
FnDefStmt(bool _p, String _n, DynArray<Param *> _pa, Expr *_rt, BlockStmt *_b, SourceLocation _loc)
|
||||
: name(std::move(_n)), params(std::move(_pa)), returnTypeSpecifier(_rt), body(_b)
|
||||
{
|
||||
type = AstType::FnDefStmt; isPublic = _p; location = std::move(_loc);
|
||||
|
||||
32
src/Ast/Stmt/ForStmt.hpp
Normal file
32
src/Ast/Stmt/ForStmt.hpp
Normal file
@@ -0,0 +1,32 @@
|
||||
/*!
|
||||
@file src/Ast/Stmt/ForStmt.hpp
|
||||
@brief for loop
|
||||
@author PuqiAR (im@puqiar.top)
|
||||
@date 2026-06-06
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Ast/Base.hpp>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
struct ForStmt final : public Stmt
|
||||
{
|
||||
Stmt *init;
|
||||
Expr *cond;
|
||||
Expr *step;
|
||||
BlockStmt *body;
|
||||
|
||||
ForStmt() { type = AstType::ForStmt; }
|
||||
|
||||
ForStmt(Stmt *_init, Expr *_cond, Expr *_step, BlockStmt *_body, SourceLocation _loc) :
|
||||
init(_init), cond(_cond), step(_step), body(_body)
|
||||
{
|
||||
type = AstType::ForStmt;
|
||||
location = std::move(_loc);
|
||||
}
|
||||
|
||||
virtual String toString() const override { return "<ForStmt>"; }
|
||||
};
|
||||
} // namespace Fig
|
||||
@@ -11,11 +11,11 @@ namespace Fig
|
||||
{
|
||||
struct ImplStmt final : public Stmt
|
||||
{
|
||||
TypeExpr *interfaceType;
|
||||
TypeExpr *structType;
|
||||
Expr *interfaceType;
|
||||
Expr *structType;
|
||||
DynArray<FnDefStmt *> methods;
|
||||
|
||||
ImplStmt(TypeExpr *_it, TypeExpr *_st, DynArray<FnDefStmt *> _m, SourceLocation _loc) :
|
||||
ImplStmt(Expr *_it, Expr *_st, DynArray<FnDefStmt *> _m, SourceLocation _loc) :
|
||||
interfaceType(_it), structType(_st), methods(std::move(_m))
|
||||
{
|
||||
type = AstType::ImplStmt;
|
||||
|
||||
33
src/Ast/Stmt/ImportStmt.hpp
Normal file
33
src/Ast/Stmt/ImportStmt.hpp
Normal file
@@ -0,0 +1,33 @@
|
||||
/*!
|
||||
@file src/Ast/Stmt/ImportStmt.hpp
|
||||
@brief import
|
||||
@author PuqiAR (im@puqiar.top)
|
||||
@date 2026-06-06
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Ast/Base.hpp>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
struct ImportStmt final : public Stmt
|
||||
{
|
||||
String path;
|
||||
bool isFileImport;
|
||||
|
||||
ImportStmt() { type = AstType::ImportStmt; }
|
||||
|
||||
ImportStmt(String _path, bool _isFileImport, SourceLocation _loc) :
|
||||
path(std::move(_path)), isFileImport(_isFileImport)
|
||||
{
|
||||
type = AstType::ImportStmt;
|
||||
location = std::move(_loc);
|
||||
}
|
||||
|
||||
virtual String toString() const override
|
||||
{
|
||||
return std::format("<ImportStmt '{}'{}>", path, isFileImport ? " [file]" : "");
|
||||
}
|
||||
};
|
||||
} // namespace Fig
|
||||
@@ -16,8 +16,8 @@ namespace Fig
|
||||
struct Method
|
||||
{
|
||||
String name;
|
||||
DynArray<TypeExpr*> params;
|
||||
TypeExpr *retType;
|
||||
DynArray<Expr*> params;
|
||||
Expr *retType;
|
||||
SourceLocation location;
|
||||
};
|
||||
|
||||
|
||||
@@ -13,9 +13,12 @@ namespace Fig
|
||||
{
|
||||
struct Field
|
||||
{
|
||||
bool isPublic;
|
||||
bool typeInfer;
|
||||
|
||||
String name;
|
||||
TypeExpr *type;
|
||||
bool isPublic;
|
||||
Expr *type;
|
||||
Expr *initExpr;
|
||||
};
|
||||
bool isPublic;
|
||||
String name;
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace Fig
|
||||
struct VarDecl final : public Stmt
|
||||
{
|
||||
String name;
|
||||
TypeExpr *typeSpecifier;
|
||||
Expr *typeSpecifier;
|
||||
bool isInfer; // 是否用了 := 类型推断
|
||||
Expr *initExpr;
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace Fig
|
||||
type = AstType::VarDecl;
|
||||
}
|
||||
|
||||
VarDecl(bool _isPublic, String _name, TypeExpr *_typeSpecifier, bool _isInfer, Expr *_initExpr, SourceLocation _location) :
|
||||
VarDecl(bool _isPublic, String _name, Expr *_typeSpecifier, bool _isInfer, Expr *_initExpr, SourceLocation _location) :
|
||||
name(std::move(_name)),
|
||||
typeSpecifier(_typeSpecifier),
|
||||
isInfer(_isInfer),
|
||||
|
||||
@@ -12,13 +12,13 @@ namespace Fig
|
||||
struct NamedTypeExpr final : public TypeExpr
|
||||
{
|
||||
DynArray<String> path;
|
||||
DynArray<TypeExpr *> arguments;
|
||||
DynArray<Expr *> arguments;
|
||||
|
||||
NamedTypeExpr()
|
||||
{
|
||||
type = AstType::NamedTypeExpr;
|
||||
}
|
||||
NamedTypeExpr(DynArray<String> _p, DynArray<TypeExpr *> _args, SourceLocation _loc) :
|
||||
NamedTypeExpr(DynArray<String> _p, DynArray<Expr *> _args, SourceLocation _loc) :
|
||||
path(std::move(_p)), arguments(std::move(_args))
|
||||
{
|
||||
type = AstType::NamedTypeExpr;
|
||||
@@ -51,9 +51,9 @@ namespace Fig
|
||||
|
||||
struct NullableTypeExpr final : public TypeExpr
|
||||
{
|
||||
TypeExpr *inner;
|
||||
Expr *inner;
|
||||
|
||||
NullableTypeExpr(TypeExpr *_inner, SourceLocation _loc) : inner(_inner)
|
||||
NullableTypeExpr(Expr *_inner, SourceLocation _loc) : inner(_inner)
|
||||
{
|
||||
type = AstType::NullableTypeExpr;
|
||||
location = std::move(_loc);
|
||||
@@ -64,4 +64,37 @@ namespace Fig
|
||||
return std::format("<NullableTypeExpr '{}?'>", inner->toString());
|
||||
}
|
||||
};
|
||||
|
||||
struct FnTypeExpr final : public TypeExpr
|
||||
{
|
||||
// func (paratypes...) -> return_type
|
||||
|
||||
DynArray<Expr *> paraTypes;
|
||||
Expr *returnType;
|
||||
|
||||
FnTypeExpr(DynArray<Expr *> _paraTypes, Expr *_returnType) :
|
||||
paraTypes(std::move(_paraTypes)), returnType(_returnType)
|
||||
{
|
||||
type = AstType::FnTypeExpr;
|
||||
}
|
||||
|
||||
virtual String toString() const override
|
||||
{
|
||||
String detail = "<FnTypeExpr 'func (";
|
||||
|
||||
for (auto &pt : paraTypes)
|
||||
{
|
||||
if (pt != paraTypes.front())
|
||||
{
|
||||
detail += ", ";
|
||||
}
|
||||
detail += pt->toString();
|
||||
}
|
||||
detail += ") -> ";
|
||||
detail += returnType->toString();
|
||||
detail += "'>";
|
||||
|
||||
return detail;
|
||||
}
|
||||
};
|
||||
} // namespace Fig
|
||||
|
||||
@@ -75,6 +75,7 @@ namespace Fig
|
||||
stream << std::format(" [{}] {}\n", i, proto->constants[i].ToString());
|
||||
}
|
||||
}
|
||||
stream << "--- End Disassembly ---\n";
|
||||
}
|
||||
|
||||
Disassembler::Format Disassembler::GetFormat(OpCode op)
|
||||
|
||||
@@ -27,7 +27,11 @@ int main()
|
||||
}
|
||||
|
||||
Lexer lexer(source, filePath);
|
||||
Parser parser(lexer, sm, filePath);
|
||||
|
||||
Diagnostics diagnostics;
|
||||
Parser parser(lexer, sm, filePath, diagnostics);
|
||||
|
||||
diagnostics.EmitAll(sm);
|
||||
|
||||
auto pRes = parser.Parse();
|
||||
if (!pRes)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*!
|
||||
@file src/Compiler/ExprCompiler.cpp
|
||||
@brief 表达式编译器实现:引入水位线(Watermark)与零拷贝复用机制
|
||||
@brief 表达式编译
|
||||
*/
|
||||
|
||||
#include <Ast/Expr/CallExpr.hpp>
|
||||
@@ -12,7 +12,6 @@
|
||||
#include <limits>
|
||||
#include <system_error>
|
||||
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
static Result<Value, Error> parsePhysicalNumber(const String &raw, const SourceLocation &loc)
|
||||
@@ -20,46 +19,69 @@ namespace Fig
|
||||
char buffer[128];
|
||||
int j = 0;
|
||||
bool isFloat = false;
|
||||
|
||||
for (size_t i = 0; i < raw.length() && j < 127; ++i)
|
||||
{
|
||||
char32_t c = raw[i];
|
||||
if (c == '_')
|
||||
continue;
|
||||
|
||||
// 检查开头的无效字符
|
||||
if (j == 0 && (c == '.' || c == 'e' || c == 'E'))
|
||||
return std::unexpected(
|
||||
Error(ErrorType::SyntaxError, "unexpected leading character", "", loc));
|
||||
|
||||
if (c == '.' || c == 'e' || c == 'E')
|
||||
isFloat = true;
|
||||
buffer[j++] = (char) c;
|
||||
}
|
||||
buffer[j] = '\0';
|
||||
|
||||
// 检查16进制/2进制前缀
|
||||
bool isHexOrBin = false;
|
||||
int base = 10;
|
||||
const char *start = buffer;
|
||||
|
||||
if (j >= 2 && buffer[0] == '0')
|
||||
{
|
||||
if (buffer[1] == 'x' || buffer[1] == 'X')
|
||||
{
|
||||
base = 16;
|
||||
start += 2;
|
||||
isHexOrBin = true;
|
||||
}
|
||||
else if (buffer[1] == 'b' || buffer[1] == 'B')
|
||||
{
|
||||
base = 2;
|
||||
start += 2;
|
||||
isHexOrBin = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (isFloat)
|
||||
{
|
||||
// 如果既有浮点标记又是0x开头,可能是16进制浮点
|
||||
auto fmt =
|
||||
(isHexOrBin && base == 16) ? std::chars_format::hex : std::chars_format::general;
|
||||
|
||||
double dVal;
|
||||
auto [ptr, ec] = std::from_chars(buffer, buffer + j, dVal);
|
||||
if (ec != std::errc())
|
||||
return std::unexpected(Error(ErrorType::SyntaxError, "float overflow", "", loc));
|
||||
auto [ptr, ec] = std::from_chars(start, buffer + j, dVal, fmt);
|
||||
|
||||
if (ec != std::errc() || ptr != buffer + j)
|
||||
return std::unexpected(
|
||||
Error(ErrorType::SyntaxError, "invalid float literal", "", loc));
|
||||
|
||||
return Value::FromDouble(dVal);
|
||||
}
|
||||
else
|
||||
else if (isHexOrBin)
|
||||
{
|
||||
int base = 10;
|
||||
const char *start = buffer;
|
||||
if (j > 2 && buffer[0] == '0')
|
||||
{
|
||||
if (buffer[1] == 'x' || buffer[1] == 'X')
|
||||
{
|
||||
base = 16;
|
||||
start += 2;
|
||||
}
|
||||
else if (buffer[1] == 'b' || buffer[1] == 'B')
|
||||
{
|
||||
base = 2;
|
||||
start += 2;
|
||||
}
|
||||
}
|
||||
// 16进制或2进制整数
|
||||
int64_t iVal;
|
||||
auto [ptr, ec] = std::from_chars(start, buffer + j, iVal, base);
|
||||
if (ec != std::errc())
|
||||
return std::unexpected(Error(ErrorType::SyntaxError, "integer overflow", "", loc));
|
||||
|
||||
if (ec != std::errc() || ptr != buffer + j)
|
||||
return std::unexpected(
|
||||
Error(ErrorType::SyntaxError, "integer overflow or invalid literal", "", loc));
|
||||
|
||||
if (iVal >= std::numeric_limits<int32_t>::min()
|
||||
&& iVal <= std::numeric_limits<int32_t>::max())
|
||||
@@ -71,6 +93,27 @@ namespace Fig
|
||||
return Value::FromDouble(static_cast<double>(iVal));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 10进制数字,可能是整数或浮点数
|
||||
double dVal;
|
||||
auto [ptr, ec] = std::from_chars(start, buffer + j, dVal, std::chars_format::general);
|
||||
|
||||
if (ec != std::errc() || ptr != buffer + j)
|
||||
return std::unexpected(
|
||||
Error(ErrorType::SyntaxError, "invalid number literal", "", loc));
|
||||
|
||||
// 检查是否是整数(没有小数部分且不超出int32范围)
|
||||
if (dVal == std::floor(dVal) && dVal >= std::numeric_limits<int32_t>::min()
|
||||
&& dVal <= std::numeric_limits<int32_t>::max())
|
||||
{
|
||||
return Value::FromInt(static_cast<int32_t>(dVal));
|
||||
}
|
||||
else
|
||||
{
|
||||
return Value::FromDouble(dVal);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Result<Register, Error> Compiler::compileExpr(Expr *expr, Register target)
|
||||
@@ -94,7 +137,9 @@ namespace Fig
|
||||
parsePhysicalNumber(manager.GetSub(tok.index, tok.length), l->location);
|
||||
if (!vRes)
|
||||
return std::unexpected(vRes.error());
|
||||
emit(Op::iABx(OpCode::LoadK, r, static_cast<uint16_t>(addConstant(*vRes))), &l->location);
|
||||
emit(
|
||||
Op::iABx(OpCode::LoadK, r, static_cast<uint16_t>(addConstant(*vRes))),
|
||||
&l->location);
|
||||
}
|
||||
else if (tok.type == TokenType::LiteralString)
|
||||
{
|
||||
@@ -122,14 +167,16 @@ namespace Fig
|
||||
|
||||
if (sym->location == SymbolLocation::Local)
|
||||
{
|
||||
// 零拷贝直读:如果是临时求值,直接返回变量的物理槽位,禁止产生副本
|
||||
// no-copy for temp eval
|
||||
if (target == NO_REG)
|
||||
return static_cast<Register>(sym->index);
|
||||
|
||||
// 仅在被强制指定目标(如参数装填)时发射搬运指令
|
||||
if (target != sym->index)
|
||||
{
|
||||
emit(Op::iABx(OpCode::Mov, target, static_cast<uint16_t>(sym->index)), &i->location);
|
||||
emit(
|
||||
Op::iABx(OpCode::Mov, target, static_cast<uint16_t>(sym->index)),
|
||||
&i->location);
|
||||
}
|
||||
return target;
|
||||
}
|
||||
@@ -137,7 +184,9 @@ namespace Fig
|
||||
Register r = (target == NO_REG) ? *allocateReg(i->location) : target;
|
||||
if (sym->location == SymbolLocation::Upvalue)
|
||||
{
|
||||
emit(Op::iABC(OpCode::GetUpval, r, static_cast<uint8_t>(sym->index), 0), &i->location);
|
||||
emit(
|
||||
Op::iABC(OpCode::GetUpval, r, static_cast<uint8_t>(sym->index), 0),
|
||||
&i->location);
|
||||
}
|
||||
else if (sym->location == SymbolLocation::Global)
|
||||
{
|
||||
@@ -176,10 +225,12 @@ namespace Fig
|
||||
{
|
||||
isGlobalFastCall = true;
|
||||
int protoIdx = id->resolvedSymbol->index;
|
||||
emit(Op::iABC(OpCode::FastCall,
|
||||
static_cast<uint8_t>(protoIdx),
|
||||
baseReg,
|
||||
static_cast<uint8_t>(c->args.args.size())),
|
||||
emit(
|
||||
Op::iABC(
|
||||
OpCode::FastCall,
|
||||
static_cast<uint8_t>(protoIdx),
|
||||
baseReg,
|
||||
static_cast<uint8_t>(c->args.args.size())),
|
||||
&c->location);
|
||||
}
|
||||
}
|
||||
@@ -193,14 +244,16 @@ namespace Fig
|
||||
return std::unexpected(r_fn.error());
|
||||
|
||||
// 使用动态 Call 指令,RA 是指向堆闭包的寄存器
|
||||
emit(Op::iABC(OpCode::Call,
|
||||
*r_fn,
|
||||
baseReg,
|
||||
static_cast<uint8_t>(c->args.args.size())),
|
||||
emit(
|
||||
Op::iABC(
|
||||
OpCode::Call,
|
||||
*r_fn,
|
||||
baseReg,
|
||||
static_cast<uint8_t>(c->args.args.size())),
|
||||
&c->location);
|
||||
}
|
||||
|
||||
// 回滚水位线, 释放传参时的临时占用
|
||||
// free arg temps
|
||||
current->freereg = mark;
|
||||
|
||||
// 目若 target 未指定,allocateReg 将复用 baseReg,实现零开销回写
|
||||
@@ -240,24 +293,31 @@ namespace Fig
|
||||
Symbol *sym = lid->resolvedSymbol;
|
||||
if (sym->location == SymbolLocation::Local)
|
||||
{
|
||||
emit(Op::iABx(OpCode::Mov, static_cast<Register>(sym->index), *r_val), &lid->location);
|
||||
emit(
|
||||
Op::iABx(OpCode::Mov, static_cast<Register>(sym->index), *r_val),
|
||||
&lid->location);
|
||||
}
|
||||
else if (sym->location == SymbolLocation::Upvalue)
|
||||
{
|
||||
emit(Op::iABC(
|
||||
OpCode::SetUpval, *r_val, static_cast<Register>(sym->index), 0), &lid->location);
|
||||
emit(
|
||||
Op::iABC(
|
||||
OpCode::SetUpval, *r_val, static_cast<Register>(sym->index), 0),
|
||||
&lid->location);
|
||||
}
|
||||
else
|
||||
{
|
||||
emit(Op::iABx(OpCode::SetGlobal,
|
||||
*r_val,
|
||||
static_cast<uint16_t>(getGlobalID(lid->name))), &lid->location);
|
||||
emit(
|
||||
Op::iABx(
|
||||
OpCode::SetGlobal,
|
||||
*r_val,
|
||||
static_cast<uint16_t>(getGlobalID(lid->name))),
|
||||
&lid->location);
|
||||
}
|
||||
}
|
||||
return r_val;
|
||||
}
|
||||
|
||||
Register mark = current->freereg; // 记录水位线
|
||||
Register mark = current->freereg; // mark
|
||||
|
||||
auto r_l = compileExpr(in->left);
|
||||
if (!r_l)
|
||||
@@ -271,29 +331,61 @@ namespace Fig
|
||||
OpCode op;
|
||||
switch (in->op)
|
||||
{
|
||||
case BinaryOperator::Add: op = isInt ? OpCode::IntFastAdd : OpCode::Add; break;
|
||||
case BinaryOperator::Subtract:
|
||||
op = isInt ? OpCode::IntFastSub : OpCode::Sub;
|
||||
case BinaryOperator::Add: {
|
||||
op = (isInt ? (OpCode::IntFastAdd) : (OpCode::Add));
|
||||
break;
|
||||
case BinaryOperator::Multiply:
|
||||
op = isInt ? OpCode::IntFastMul : OpCode::Mul;
|
||||
}
|
||||
case BinaryOperator::Subtract: {
|
||||
op = (isInt ? (OpCode::IntFastSub) : (OpCode::Sub));
|
||||
break;
|
||||
case BinaryOperator::Divide:
|
||||
op = isInt ? OpCode::IntFastDiv : OpCode::Div;
|
||||
}
|
||||
case BinaryOperator::Multiply: {
|
||||
op = (isInt ? (OpCode::IntFastMul) : (OpCode::Mul));
|
||||
break;
|
||||
case BinaryOperator::Modulo: op = OpCode::Mod; break;
|
||||
case BinaryOperator::BitXor: op = OpCode::BitXor; break;
|
||||
case BinaryOperator::Equal: op = OpCode::Equal; break;
|
||||
case BinaryOperator::NotEqual: op = OpCode::NotEqual; break;
|
||||
case BinaryOperator::Greater: op = OpCode::Greater; break;
|
||||
case BinaryOperator::Less: op = OpCode::Less; break;
|
||||
case BinaryOperator::GreaterEqual: op = OpCode::GreaterEqual; break;
|
||||
case BinaryOperator::LessEqual: op = OpCode::LessEqual; break;
|
||||
default:
|
||||
return std::unexpected(Error(ErrorType::InternalError,
|
||||
}
|
||||
case BinaryOperator::Divide: {
|
||||
op = (isInt ? (OpCode::IntFastDiv) : (OpCode::Div));
|
||||
break;
|
||||
}
|
||||
case BinaryOperator::Modulo: {
|
||||
op = OpCode::Mod;
|
||||
break;
|
||||
}
|
||||
case BinaryOperator::BitXor: {
|
||||
op = OpCode::BitXor;
|
||||
break;
|
||||
}
|
||||
case BinaryOperator::Equal: {
|
||||
op = OpCode::Equal;
|
||||
break;
|
||||
}
|
||||
case BinaryOperator::NotEqual: {
|
||||
op = OpCode::NotEqual;
|
||||
break;
|
||||
}
|
||||
case BinaryOperator::Greater: {
|
||||
op = OpCode::Greater;
|
||||
break;
|
||||
}
|
||||
case BinaryOperator::Less: {
|
||||
op = OpCode::Less;
|
||||
break;
|
||||
}
|
||||
case BinaryOperator::GreaterEqual: {
|
||||
op = OpCode::GreaterEqual;
|
||||
break;
|
||||
}
|
||||
case BinaryOperator::LessEqual: {
|
||||
op = OpCode::LessEqual;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
return std::unexpected(Error(
|
||||
ErrorType::InternalError,
|
||||
"unsupported binary operator",
|
||||
"",
|
||||
in->location));
|
||||
}
|
||||
}
|
||||
|
||||
// 释放左右操作数产生的临时寄存器
|
||||
@@ -318,6 +410,65 @@ namespace Fig
|
||||
return r_d;
|
||||
}
|
||||
|
||||
case AstType::LambdaExpr: {
|
||||
auto l = static_cast<LambdaExpr *>(expr);
|
||||
|
||||
Proto *proto = new Proto;
|
||||
proto->name = l->toString();
|
||||
|
||||
FuncState fs(proto, current);
|
||||
FuncState *old = current;
|
||||
current = &fs;
|
||||
|
||||
if (l->isExprBody)
|
||||
{
|
||||
auto result = compileExpr(static_cast<Expr *>(l->body));
|
||||
if (!result)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
emit(Op::iABC(OpCode::Return, *result, 0, 0), &l->location);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto result = compileStmt(static_cast<BlockStmt *>(l->body));
|
||||
if (!result)
|
||||
{
|
||||
return std::unexpected(result.error());
|
||||
}
|
||||
auto _r = allocateReg(l->body->location);
|
||||
if (!_r)
|
||||
{
|
||||
return _r;
|
||||
}
|
||||
|
||||
Register r = *_r;
|
||||
emit(Op::iABC(OpCode::LoadNull, r, 0, 0), &l->body->location);
|
||||
emit(Op::iABC(OpCode::Return, r, 0, 0), &l->body->location);
|
||||
}
|
||||
|
||||
int protoIndex = (int) module->protos.size();
|
||||
module->protos.push_back(proto);
|
||||
|
||||
current = old;
|
||||
if (target == NO_REG)
|
||||
{
|
||||
auto _r = allocateReg(expr->location);
|
||||
if (!_r)
|
||||
{
|
||||
return _r;
|
||||
}
|
||||
emit(Op::iABx(OpCode::LoadFn, *_r, protoIndex), &l->body->location);
|
||||
return *_r;
|
||||
}
|
||||
else
|
||||
{
|
||||
emit(Op::iABx(OpCode::LoadFn, target, protoIndex), &l->body->location);
|
||||
}
|
||||
return target;
|
||||
}
|
||||
|
||||
default: break;
|
||||
}
|
||||
return std::unexpected(
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*!
|
||||
@file src/Compiler/StmtCompiler.cpp
|
||||
@brief 语句编译器实现:实装水位线机制,彻底消灭硬编码寄存器释放
|
||||
@brief 语句编译
|
||||
*/
|
||||
|
||||
#include <Ast/Stmt/FnDefStmt.hpp>
|
||||
@@ -35,7 +35,7 @@ namespace Fig
|
||||
auto *v = static_cast<VarDecl *>(stmt);
|
||||
if (current->enclosing == nullptr) // 处理全局变量
|
||||
{
|
||||
Register mark = current->freereg; // 记录水位线
|
||||
Register mark = current->freereg; // mark
|
||||
auto regRes = compileExpr(v->initExpr);
|
||||
if (!regRes)
|
||||
return std::unexpected(regRes.error());
|
||||
@@ -74,7 +74,7 @@ namespace Fig
|
||||
Proto *newProto = new Proto();
|
||||
newProto->name = f->name;
|
||||
newProto->numParams = static_cast<uint8_t>(f->params.size());
|
||||
newProto->maxRegisters = newProto->numParams; // 同步水位线
|
||||
newProto->maxRegisters = newProto->numParams; // sync
|
||||
|
||||
f->protoIndex = static_cast<int>(module->protos.size());
|
||||
module->protos.push_back(newProto);
|
||||
@@ -141,7 +141,7 @@ namespace Fig
|
||||
auto *i = static_cast<IfStmt *>(stmt);
|
||||
DynArray<int> exitJumps;
|
||||
|
||||
Register mark = current->freereg; // 记录水位线
|
||||
Register mark = current->freereg; // mark
|
||||
auto r_cond = compileExpr(i->cond);
|
||||
if (!r_cond)
|
||||
return std::unexpected(r_cond.error());
|
||||
@@ -204,7 +204,7 @@ namespace Fig
|
||||
auto *w = static_cast<WhileStmt *>(stmt);
|
||||
int startIdx = static_cast<int>(current->proto->code.size());
|
||||
|
||||
Register mark = current->freereg; // 记录水位线
|
||||
Register mark = current->freereg; // mark
|
||||
auto r_cond = compileExpr(w->cond);
|
||||
if (!r_cond)
|
||||
return std::unexpected(r_cond.error());
|
||||
@@ -228,7 +228,7 @@ namespace Fig
|
||||
|
||||
case AstType::ReturnStmt: {
|
||||
auto *rs = static_cast<ReturnStmt *>(stmt);
|
||||
Register mark = current->freereg; // 记录水位线
|
||||
Register mark = current->freereg; // mark
|
||||
Register retReg;
|
||||
|
||||
if (rs->value)
|
||||
@@ -253,7 +253,7 @@ namespace Fig
|
||||
}
|
||||
|
||||
case AstType::ExprStmt: {
|
||||
Register mark = current->freereg; // 记录水位线
|
||||
Register mark = current->freereg; // mark
|
||||
auto reg = compileExpr(static_cast<ExprStmt *>(stmt)->expr);
|
||||
if (!reg)
|
||||
return std::unexpected(reg.error());
|
||||
|
||||
@@ -17,7 +17,8 @@ namespace Fig
|
||||
ost << color << msg << TerminalColors::Reset;
|
||||
}
|
||||
|
||||
void ColoredPrint(const char *color, const std::string &msg, std::ostream &ost = CoreIO::GetStdErr())
|
||||
void
|
||||
ColoredPrint(const char *color, const std::string &msg, std::ostream &ost = CoreIO::GetStdErr())
|
||||
{
|
||||
ost << color << msg << TerminalColors::Reset;
|
||||
}
|
||||
@@ -30,7 +31,10 @@ namespace Fig
|
||||
std::string MultipleStr(const char *c, size_t n)
|
||||
{
|
||||
std::string buf;
|
||||
for (size_t i = 0; i < n; ++i) { buf += c; }
|
||||
for (size_t i = 0; i < n; ++i)
|
||||
{
|
||||
buf += c;
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
@@ -40,6 +44,7 @@ namespace Fig
|
||||
switch (type)
|
||||
{
|
||||
case UnusedSymbol: return "UnusedSymbol";
|
||||
case UnnecessarySemicolon: return "UnnecessarySemicolon";
|
||||
|
||||
case MayBeNull: return "MaybeNull";
|
||||
|
||||
@@ -61,7 +66,8 @@ namespace Fig
|
||||
case TooManyConstants: return "TooManyConstants";
|
||||
|
||||
case RegisterOverflow: return "RegisterOverflow";
|
||||
case InternalError: return "InternalError";
|
||||
case InternalError:
|
||||
return "InternalError";
|
||||
// default: return "Some one forgot to add case to `ErrorTypeToString`";
|
||||
}
|
||||
return "UnknownError";
|
||||
@@ -69,10 +75,10 @@ namespace Fig
|
||||
|
||||
void PrintSystemInfos()
|
||||
{
|
||||
std::ostream &err = CoreIO::GetStdErr();
|
||||
std::ostream &err = CoreIO::GetStdErr();
|
||||
std::stringstream build_info;
|
||||
build_info << "\r🌘 Fig v" << Core::VERSION << " on " << Core::PLATFORM << ' ' << Core::ARCH << '['
|
||||
<< Core::COMPILER << ']' << '\n'
|
||||
build_info << "\r🌘 Fig v" << Core::VERSION << " on " << Core::PLATFORM << ' ' << Core::ARCH
|
||||
<< '[' << Core::COMPILER << ']' << '\n'
|
||||
<< " Build Time: " << Core::COMPILE_TIME;
|
||||
|
||||
const std::string &build_info_str = build_info.str();
|
||||
@@ -83,35 +89,43 @@ namespace Fig
|
||||
|
||||
void PrintErrorInfo(const Error &error, const SourceManager &srcManager)
|
||||
{
|
||||
static constexpr const char *MinorColor = "\033[38;2;138;227;198m";
|
||||
static constexpr const char *MediumColor = "\033[38;2;255;199;95m";
|
||||
static constexpr const char *MinorColor = "\033[38;2;138;227;198m";
|
||||
static constexpr const char *MediumColor = "\033[38;2;255;199;95m";
|
||||
static constexpr const char *CriticalColor = "\033[38;2;255;107;107m";
|
||||
|
||||
namespace TC = TerminalColors;
|
||||
namespace TC = TerminalColors;
|
||||
std::ostream &err = CoreIO::GetStdErr();
|
||||
|
||||
uint8_t level = ErrorLevel(error.type);
|
||||
// const char *level_name = (level == 1 ? "Minor" : (level == 2 ? "Medium" : "Critical"));
|
||||
const char *level_color = (level == 1 ? MinorColor : (level == 2 ? MediumColor : CriticalColor));
|
||||
const char *level_color =
|
||||
(level == 1 ? MinorColor : (level == 2 ? MediumColor : CriticalColor));
|
||||
|
||||
err << "🔥 "
|
||||
<< level_color
|
||||
//<< '(' << level_name << ')'
|
||||
<< 'E' << static_cast<int>(error.type) << TC::Reset << ": " << level_color << ErrorTypeToString(error.type)
|
||||
<< TC::Reset << '\n';
|
||||
<< 'E' << static_cast<int>(error.type) << TC::Reset << ": " << level_color
|
||||
<< ErrorTypeToString(error.type) << TC::Reset << '\n';
|
||||
|
||||
const SourceLocation &location = error.location;
|
||||
|
||||
err << TC::DarkGray << " ┌─> Fn " << TC::Cyan << '\'' << location.packageName << '.' << location.functionName
|
||||
<< '\'' << " " << location.fileName << " (" << TC::DarkGray << location.sp.line << ":" << location.sp.column
|
||||
<< TC::Cyan << ')' << TC::Reset << '\n';
|
||||
err << TC::DarkGray << " ┌─> Fn " << TC::Cyan << '\'' << location.packageName << '.'
|
||||
<< location.functionName << '\'' << " " << location.fileName << " (" << TC::DarkGray
|
||||
<< location.sp.line << ":" << location.sp.column << TC::Cyan << ')' << TC::Reset
|
||||
<< '\n';
|
||||
err << TC::DarkGray << " │" << '\n' << " │" << TC::Reset << '\n';
|
||||
|
||||
// 尝试打印上3行 下2行
|
||||
|
||||
int64_t line_start = location.sp.line - 3, line_end = location.sp.line + 2;
|
||||
while (!srcManager.HasLine(line_end)) { --line_end; }
|
||||
while (!srcManager.HasLine(line_start)) { ++line_start; }
|
||||
while (!srcManager.HasLine(line_end))
|
||||
{
|
||||
--line_end;
|
||||
}
|
||||
while (!srcManager.HasLine(line_start))
|
||||
{
|
||||
++line_start;
|
||||
}
|
||||
|
||||
const auto &getLineNumWidth = [](size_t l) {
|
||||
unsigned int cnt = 0;
|
||||
@@ -127,30 +141,39 @@ namespace Fig
|
||||
{
|
||||
unsigned int offset = 2 + 2 + 1;
|
||||
// ' └─ '
|
||||
if (i == location.sp.line) { err << TC::DarkGray << " └─ " << TC::Reset; }
|
||||
else if (i < location.sp.line) { err << TC::DarkGray << " │ " << TC::Reset; }
|
||||
if (i == location.sp.line)
|
||||
{
|
||||
err << TC::DarkGray << " └─ " << TC::Reset;
|
||||
}
|
||||
else if (i < location.sp.line)
|
||||
{
|
||||
err << TC::DarkGray << " │ " << TC::Reset;
|
||||
}
|
||||
else
|
||||
{
|
||||
err << MultipleStr(" ", offset);
|
||||
}
|
||||
unsigned int cur_line_number_width = getLineNumWidth(i);
|
||||
|
||||
err << MultipleStr(" ", max_line_number_width - cur_line_number_width) << TC::Yellow << i << TC::Reset;
|
||||
err << MultipleStr(" ", max_line_number_width - cur_line_number_width) << TC::Yellow
|
||||
<< i << TC::Reset;
|
||||
err << " │ " << srcManager.GetLine(i) << '\n';
|
||||
if (i == location.sp.line)
|
||||
{
|
||||
unsigned int error_col_offset = offset + 1 + max_line_number_width + 2;
|
||||
err << MultipleStr(" ", error_col_offset) << MultipleStr(" ", location.sp.column - 1) << TC::LightGreen
|
||||
err << MultipleStr(" ", error_col_offset)
|
||||
<< MultipleStr(" ", location.sp.column - 1) << TC::LightGreen
|
||||
<< MultipleStr("^", location.sp.tok_length) << TC::Reset << '\n';
|
||||
|
||||
err << MultipleStr(" ", error_col_offset)
|
||||
<< MultipleStr(" ", location.sp.column - 1 + location.sp.tok_length / 2) << "╰─ " << level_color
|
||||
<< error.message << TC::Reset << "\n\n";
|
||||
<< MultipleStr(" ", location.sp.column - 1 + location.sp.tok_length / 2)
|
||||
<< "╰─ " << level_color << error.message << TC::Reset << "\n\n";
|
||||
}
|
||||
}
|
||||
err << "\n";
|
||||
err << "❓ " << TC::DarkGray << "Thrower: " << error.thrower_loc.function_name() << " ("
|
||||
<< error.thrower_loc.file_name() << ":" << error.thrower_loc.line() << ")" << TC::Reset << "\n";
|
||||
<< error.thrower_loc.file_name() << ":" << error.thrower_loc.line() << ")" << TC::Reset
|
||||
<< "\n";
|
||||
err << "💡 " << TC::Blue << "Suggestion: " << error.suggestion << TC::Reset;
|
||||
err << '\n';
|
||||
}
|
||||
@@ -158,7 +181,7 @@ namespace Fig
|
||||
void ReportError(const Error &error, const SourceManager &srcManager)
|
||||
{
|
||||
assert(srcManager.read && "ReportError: srcManager doesn't read source");
|
||||
assert(srcManager.HasLine(error.location.sp.line));
|
||||
// assert(srcManager.HasLine(error.location.sp.line));
|
||||
|
||||
PrintSystemInfos();
|
||||
PrintErrorInfo(error, srcManager);
|
||||
|
||||
@@ -24,6 +24,8 @@ namespace Fig
|
||||
{
|
||||
/* Minor */
|
||||
UnusedSymbol = 0,
|
||||
UnnecessarySemicolon,
|
||||
TrailingComma,
|
||||
|
||||
/* Medium */
|
||||
MayBeNull = 1001,
|
||||
@@ -51,7 +53,7 @@ namespace Fig
|
||||
TooManyLocals,
|
||||
TooManyConstants,
|
||||
|
||||
// --- 新增:编译器内部与VM约束 ---
|
||||
// 编译器内部约束
|
||||
RegisterOverflow,
|
||||
InternalError,
|
||||
};
|
||||
|
||||
@@ -138,9 +138,12 @@ namespace Fig
|
||||
manager.LoadFromMemory(sourceCode);
|
||||
|
||||
Lexer lexer(sourceCode, "");
|
||||
Parser parser(lexer, manager, "");
|
||||
|
||||
// 1. 语法检查拦截
|
||||
Diagnostics diagnostics;
|
||||
|
||||
Parser parser(lexer, manager, "", diagnostics);
|
||||
|
||||
// 语法检查拦截
|
||||
auto parserResult = parser.Parse();
|
||||
if (!parserResult)
|
||||
{
|
||||
@@ -148,6 +151,11 @@ namespace Fig
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto &diag : diagnostics.GetErrors())
|
||||
{
|
||||
SendDiagnostics(uri, &diag);
|
||||
}
|
||||
|
||||
Program *program = *parserResult;
|
||||
|
||||
Analyzer analyzer(manager);
|
||||
@@ -160,7 +168,12 @@ namespace Fig
|
||||
return;
|
||||
}
|
||||
|
||||
// 3. 一切完美,发射空数组清空过去的错误红线
|
||||
for (auto &diag : analyzer.GetDiagnostics().GetErrors())
|
||||
{
|
||||
SendDiagnostics(uri, &diag);
|
||||
}
|
||||
|
||||
// 一切完美,发射空数组清空过去的错误红线
|
||||
SendDiagnostics(uri, nullptr);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -14,7 +14,8 @@ namespace Fig
|
||||
StateProtector p(this, {State::ParsingLiteralExpr});
|
||||
|
||||
const Token &literal_token = consumeToken();
|
||||
LiteralExpr *node = arena.Allocate<LiteralExpr>(literal_token, makeSourceLocation(literal_token));
|
||||
LiteralExpr *node =
|
||||
arena.Allocate<LiteralExpr>(literal_token, makeSourceLocation(literal_token));
|
||||
return node;
|
||||
}
|
||||
Result<Expr *, Error> Parser::parseIdentiExpr() // 当前token为Identifier调用
|
||||
@@ -65,8 +66,8 @@ namespace Fig
|
||||
return node;
|
||||
}
|
||||
|
||||
Result<Expr *, Error> Parser::parseIndexExpr(
|
||||
Expr *base) // 由 parseExpression调用, 当前token为 `[`
|
||||
Result<Expr *, Error>
|
||||
Parser::parseIndexExpr(Expr *base) // 由 parseExpression调用, 当前token为 `[`
|
||||
{
|
||||
StateProtector p(this, {State::ParsingIndexExpr});
|
||||
|
||||
@@ -80,7 +81,8 @@ namespace Fig
|
||||
|
||||
if (currentToken().type != TokenType::RightBracket) // `]`
|
||||
{
|
||||
return std::unexpected(Error(ErrorType::SyntaxError,
|
||||
return std::unexpected(Error(
|
||||
ErrorType::SyntaxError,
|
||||
"unclosed brackets",
|
||||
"insert `]`",
|
||||
makeSourceLocation(lbracket_token)));
|
||||
@@ -91,12 +93,13 @@ namespace Fig
|
||||
return indexExpr;
|
||||
}
|
||||
|
||||
Result<Expr *, Error> Parser::parseCallExpr(
|
||||
Expr *callee) // 由 parseExpression调用, 当前token为 `(`
|
||||
Result<Expr *, Error>
|
||||
Parser::parseCallExpr(Expr *callee) // 由 parseExpression调用, 当前token为 `(`
|
||||
{
|
||||
StateProtector p(this, {State::ParsingCallExpr});
|
||||
|
||||
const Token &lparen_token = consumeToken(); // consume `(`
|
||||
const SourceLocation &location = makeSourceLocation(lparen_token);
|
||||
|
||||
FnCallArgs callArgs;
|
||||
|
||||
@@ -104,14 +107,15 @@ namespace Fig
|
||||
if (currentToken().type == TokenType::RightParen)
|
||||
{
|
||||
consumeToken(); // consume `)`
|
||||
return arena.Allocate<CallExpr>(callee, callArgs);
|
||||
return arena.Allocate<CallExpr>(callee, callArgs, location);
|
||||
}
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (currentToken().type == TokenType::EndOfFile)
|
||||
{
|
||||
return std::unexpected(Error(ErrorType::SyntaxError,
|
||||
return std::unexpected(Error(
|
||||
ErrorType::SyntaxError,
|
||||
"fn call has unclosed parenthese",
|
||||
"insert `)`",
|
||||
makeSourceLocation(lparen_token)));
|
||||
@@ -131,7 +135,8 @@ namespace Fig
|
||||
|
||||
if (currentToken().type != TokenType::Comma)
|
||||
{
|
||||
return std::unexpected(Error(ErrorType::SyntaxError,
|
||||
return std::unexpected(Error(
|
||||
ErrorType::SyntaxError,
|
||||
"expected `,` or `)` in argument list",
|
||||
"insert `,`",
|
||||
makeSourceLocation(currentToken())));
|
||||
@@ -140,7 +145,202 @@ namespace Fig
|
||||
consumeToken(); // consume `,`
|
||||
}
|
||||
|
||||
return arena.Allocate<CallExpr>(callee, callArgs);
|
||||
return arena.Allocate<CallExpr>(callee, callArgs, location);
|
||||
}
|
||||
|
||||
Result<Expr *, Error> Parser::parseNewExpr()
|
||||
{
|
||||
// new type{...}
|
||||
StateProtector p(this, {State::ParsingNewExpr});
|
||||
|
||||
SourceLocation location = makeSourceLocation(consumeToken()); // consume `new`
|
||||
|
||||
SET_STOP_AT(TokenType::LeftBrace); // {
|
||||
auto type_result = parseTypeExpr();
|
||||
if (!type_result)
|
||||
{
|
||||
return std::unexpected(type_result.error());
|
||||
}
|
||||
Expr *type = *type_result;
|
||||
|
||||
if (!match(TokenType::LeftBrace))
|
||||
{
|
||||
return std::unexpected(makeUnexpectTokenError("NewExpr", "lbrace {", currentToken()));
|
||||
}
|
||||
|
||||
const Token &lb_token = prevToken();
|
||||
|
||||
/*
|
||||
Positional:
|
||||
new Point{1, 2}
|
||||
Named:
|
||||
new Point{x = 1, y = 2}
|
||||
Shorthand:
|
||||
new Point{y, x}
|
||||
*/
|
||||
|
||||
DynArray<NewExpr::Arg> args;
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (isEOF)
|
||||
{
|
||||
return std::unexpected(Error(
|
||||
ErrorType::SyntaxError,
|
||||
"unclosed `{` in new expr",
|
||||
"insert '}'",
|
||||
makeSourceLocation(lb_token)
|
||||
));
|
||||
}
|
||||
if (args.empty() && match(TokenType::RightBrace)) // 空参
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// named arg
|
||||
if (currentToken().isIdentifier() && peekToken().type == TokenType::Colon)
|
||||
{
|
||||
const Token &name_token = consumeToken();
|
||||
const String &name = srcManager.GetSub(name_token.index, name_token.length);
|
||||
consumeToken(); // consume `:`
|
||||
|
||||
SET_STOP_AT(TokenType::Comma, TokenType::RightBrace); // , / }
|
||||
auto result = parseExpression();
|
||||
if (!result)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
args.push_back(NewExpr::Arg{
|
||||
name,
|
||||
*result
|
||||
});
|
||||
}
|
||||
// shorthand
|
||||
else if (currentToken().isIdentifier()
|
||||
&& (peekToken().type == TokenType::Comma || peekToken().type == TokenType::RightBrace))
|
||||
{
|
||||
const Token &name_token = consumeToken();
|
||||
const String &name = srcManager.GetSub(name_token.index, name_token.length);
|
||||
|
||||
|
||||
|
||||
IdentiExpr *ident =
|
||||
arena.Allocate<IdentiExpr>(name, makeSourceLocation(name_token));
|
||||
args.push_back(NewExpr::Arg{name, ident});
|
||||
}
|
||||
else
|
||||
{
|
||||
SET_STOP_AT(TokenType::Comma, TokenType::RightBrace); // , / }
|
||||
auto result = parseExpression();
|
||||
if (!result)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
args.push_back(NewExpr::Arg{
|
||||
.value = *result
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
if (match(TokenType::Comma))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (match(TokenType::RightBrace))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
NewExpr *newExpr = arena.Allocate<NewExpr>(type, args, location);
|
||||
return newExpr;
|
||||
}
|
||||
|
||||
Result<Expr *, Error> Parser::parseLambdaExpr()
|
||||
{
|
||||
StateProtector p(this, {State::ParsingLambdaExpr});
|
||||
|
||||
SourceLocation location = makeSourceLocation(consumeToken()); // consume `func`
|
||||
|
||||
if (currentToken().isIdentifier())
|
||||
{
|
||||
return std::unexpected(Error(
|
||||
ErrorType::SyntaxError,
|
||||
"lambda expression should not have a name",
|
||||
"remove the name",
|
||||
makeSourceLocation(currentToken())));
|
||||
}
|
||||
|
||||
if (currentToken().type != TokenType::LeftParen)
|
||||
{
|
||||
return std::unexpected(
|
||||
makeUnexpectTokenError("fn def stmt", "lparen '('", currentToken()));
|
||||
}
|
||||
|
||||
DynArray<Param *> params;
|
||||
|
||||
auto paraResult = parseFnParams();
|
||||
if (!paraResult)
|
||||
{
|
||||
return std::unexpected(paraResult.error());
|
||||
}
|
||||
params = *paraResult;
|
||||
|
||||
Expr *returnType = nullptr;
|
||||
Token rightArrowToken;
|
||||
if (match(TokenType::RightArrow)) // ->
|
||||
{
|
||||
rightArrowToken = consumeToken();
|
||||
|
||||
auto result = parseTypeExpr();
|
||||
if (!result)
|
||||
{
|
||||
return std::unexpected(result.error());
|
||||
}
|
||||
returnType = *result;
|
||||
}
|
||||
|
||||
if (match(TokenType::DoubleArrow)) // =>
|
||||
{
|
||||
if (returnType)
|
||||
{
|
||||
return std::unexpected(Error(
|
||||
ErrorType::SyntaxError,
|
||||
"use of expr body but specified return type in lambda expr",
|
||||
"remove `-> ...`",
|
||||
makeSourceLocation(rightArrowToken)));
|
||||
}
|
||||
auto result = parseExpression();
|
||||
if (!result)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
Expr *expr = *result;
|
||||
LambdaExpr *lambda =
|
||||
arena.Allocate<LambdaExpr>(params, returnType, expr, true, location);
|
||||
return lambda;
|
||||
}
|
||||
else if (currentToken().type == TokenType::LeftBrace)
|
||||
{
|
||||
auto result = parseBlockStmt();
|
||||
if (!result)
|
||||
{
|
||||
return std::unexpected(result.error());
|
||||
}
|
||||
|
||||
LambdaExpr *lambda =
|
||||
arena.Allocate<LambdaExpr>(params, returnType, *result, false, location);
|
||||
return lambda;
|
||||
}
|
||||
else
|
||||
{
|
||||
return std::unexpected(
|
||||
makeUnexpectTokenError("LambdaExpr", "darrow => / lbrace {", currentToken()));
|
||||
}
|
||||
}
|
||||
|
||||
Result<Expr *, Error> Parser::parseExpression(BindingPower rbp)
|
||||
@@ -148,6 +348,7 @@ namespace Fig
|
||||
Expr *lhs = nullptr;
|
||||
Token token = currentToken();
|
||||
|
||||
// NUD
|
||||
if (token.isIdentifier())
|
||||
{
|
||||
const auto &lhs_result = parseIdentiExpr();
|
||||
@@ -166,7 +367,7 @@ namespace Fig
|
||||
}
|
||||
lhs = *lhs_result;
|
||||
}
|
||||
else if (IsTokenOp(token.type, false)) // 是否是一元运算符
|
||||
else if (IsTokenOp(token.type, false)) // 是否是一元前缀运算符
|
||||
{
|
||||
const auto &lhs_result = parsePrefixExpr();
|
||||
if (!lhs_result)
|
||||
@@ -186,22 +387,45 @@ namespace Fig
|
||||
const Token &rparen_token = consumeToken(); // consume `)`
|
||||
if (rparen_token.type != TokenType::RightParen)
|
||||
{
|
||||
return std::unexpected(Error(ErrorType::SyntaxError,
|
||||
return std::unexpected(Error(
|
||||
ErrorType::SyntaxError,
|
||||
"unclosed parenthese",
|
||||
"insert `)`",
|
||||
makeSourceLocation(lparen_token)));
|
||||
}
|
||||
lhs = *expr_result;
|
||||
}
|
||||
else if (token.type == TokenType::Function)
|
||||
{
|
||||
auto result = parseLambdaExpr();
|
||||
if (!result)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
lhs = *result;
|
||||
}
|
||||
else if (token.type == TokenType::New)
|
||||
{
|
||||
auto result = parseNewExpr();
|
||||
if (!result)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
lhs = *result;
|
||||
}
|
||||
|
||||
if (!lhs)
|
||||
{
|
||||
return std::unexpected(Error(ErrorType::ExpectedExpression,
|
||||
return std::unexpected(Error(
|
||||
ErrorType::ExpectedExpression,
|
||||
"expected expression",
|
||||
"insert expressions",
|
||||
makeSourceLocation(prevToken())));
|
||||
}
|
||||
|
||||
// LED
|
||||
while (true)
|
||||
{
|
||||
token = currentToken();
|
||||
@@ -210,53 +434,113 @@ namespace Fig
|
||||
break;
|
||||
}
|
||||
|
||||
if (IsTokenOp(token.type /* isBinary = true */)) // 是否为二元运算符
|
||||
// is / as
|
||||
if (token.type == TokenType::Is || token.type == TokenType::As)
|
||||
{
|
||||
BinaryOperator op = TokenToBinaryOp(token);
|
||||
BindingPower lbp = GetBinaryOpLBp(op);
|
||||
if (rbp >= lbp)
|
||||
{
|
||||
break;
|
||||
}
|
||||
consumeToken(); // consume `is` or `as`
|
||||
auto typeRes = parseTypeExpr();
|
||||
if (!typeRes)
|
||||
{
|
||||
return std::unexpected(typeRes.error());
|
||||
}
|
||||
lhs = arena.Allocate<InfixExpr>(lhs, op, *typeRes);
|
||||
}
|
||||
// binary
|
||||
else if (IsTokenOp(token.type /* isBinary = true */))
|
||||
{
|
||||
BinaryOperator op = TokenToBinaryOp(token);
|
||||
BindingPower lbp = GetBinaryOpLBp(op);
|
||||
if (rbp >= lbp)
|
||||
{
|
||||
// 前操作数的右绑定力比当前操作数的左绑定力大
|
||||
// lhs被吸走
|
||||
break;
|
||||
}
|
||||
|
||||
auto result = parseInfixExpr(lhs);
|
||||
if (!result)
|
||||
{
|
||||
resetTermintors();
|
||||
return result;
|
||||
}
|
||||
lhs = *result;
|
||||
}
|
||||
// 后缀运算符优先级非常大,几乎永远跟在操作数后面,因此我们可以直接结合
|
||||
// 而不用走正常路径
|
||||
else if (token.type == TokenType::LeftBracket) // `[`
|
||||
// [index]
|
||||
else if (token.type == TokenType::LeftBracket)
|
||||
{
|
||||
const auto &expr_result = parseIndexExpr(lhs);
|
||||
if (!expr_result)
|
||||
{
|
||||
resetTermintors();
|
||||
return expr_result;
|
||||
}
|
||||
lhs = *expr_result;
|
||||
}
|
||||
else if (token.type == TokenType::LeftParen) // `(`
|
||||
// call
|
||||
else if (token.type == TokenType::LeftParen)
|
||||
{
|
||||
const auto &expr_result = parseCallExpr(lhs);
|
||||
if (!expr_result)
|
||||
{
|
||||
resetTermintors();
|
||||
return expr_result;
|
||||
}
|
||||
lhs = *expr_result;
|
||||
}
|
||||
// .member
|
||||
else if (token.type == TokenType::Dot)
|
||||
{
|
||||
consumeToken(); // consume `.`
|
||||
if (!currentToken().isIdentifier())
|
||||
{
|
||||
return std::unexpected(
|
||||
makeUnexpectTokenError("MemberExpr", "identifier after `.`", currentToken()));
|
||||
}
|
||||
const Token &nameToken = consumeToken();
|
||||
const String &name =
|
||||
srcManager.GetSub(nameToken.index, nameToken.length);
|
||||
SourceLocation loc = makeSourceLocation(nameToken);
|
||||
lhs = arena.Allocate<MemberExpr>(lhs, name, loc);
|
||||
}
|
||||
// x++ x--
|
||||
else if (token.type == TokenType::DoublePlus || token.type == TokenType::DoubleMinus)
|
||||
{
|
||||
UnaryOperator op = TokenToUnaryOp(consumeToken());
|
||||
lhs = arena.Allocate<PostfixExpr>(op, lhs);
|
||||
}
|
||||
// ?:
|
||||
else if (token.type == TokenType::Question)
|
||||
{
|
||||
// ?: 最低优先
|
||||
// 赋值 rbp = 101,所以只有当 rbp < 100 时才可能进到三元
|
||||
// 实际上三元是最低优先级的非赋值运算符,我们给一个很小的 lbp
|
||||
constexpr BindingPower TERNARY_LBP = 150;
|
||||
if (rbp >= TERNARY_LBP)
|
||||
{
|
||||
break;
|
||||
}
|
||||
consumeToken(); // consume `?`
|
||||
auto thenRes = parseExpression(0); // 重置绑定力,右结合
|
||||
if (!thenRes)
|
||||
{
|
||||
return std::unexpected(thenRes.error());
|
||||
}
|
||||
if (!match(TokenType::Colon))
|
||||
{
|
||||
return std::unexpected(
|
||||
makeUnexpectTokenError("TernaryExpr", "`:` for else branch", currentToken()));
|
||||
}
|
||||
auto elseRes = parseExpression(TERNARY_LBP - 1); // 右结合
|
||||
if (!elseRes)
|
||||
{
|
||||
return std::unexpected(elseRes.error());
|
||||
}
|
||||
lhs = arena.Allocate<TernaryExpr>(lhs, *thenRes, *elseRes, lhs->location);
|
||||
}
|
||||
else
|
||||
{
|
||||
return std::unexpected(Error(ErrorType::ExpectedExpression,
|
||||
"expression unexpectedly ended",
|
||||
"insert expressions",
|
||||
makeSourceLocation(token)));
|
||||
return lhs;
|
||||
}
|
||||
}
|
||||
return lhs;
|
||||
|
||||
@@ -15,6 +15,10 @@ namespace Fig
|
||||
|
||||
while (currentToken().type != TokenType::EndOfFile)
|
||||
{
|
||||
if (lexerError)
|
||||
{
|
||||
return std::unexpected(*lexerError);
|
||||
}
|
||||
auto result = parseStatement();
|
||||
if (!result)
|
||||
{
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
#include <Ast/Ast.hpp>
|
||||
#include <Deps/Deps.hpp>
|
||||
#include <Error/Diagnostics.hpp>
|
||||
#include <Error/Error.hpp>
|
||||
#include <Lexer/Lexer.hpp>
|
||||
#include <Token/Token.hpp>
|
||||
@@ -33,6 +34,9 @@ namespace Fig
|
||||
String fileName;
|
||||
bool isEOF = false;
|
||||
|
||||
Diagnostics &diagnostics;
|
||||
std::optional<Error> lexerError; // 词法错误缓存,避免 exit/abort
|
||||
|
||||
// 惰性获取下一个 Token,跳过注释
|
||||
Token nextToken()
|
||||
{
|
||||
@@ -46,8 +50,12 @@ namespace Fig
|
||||
auto result = lexer.NextToken();
|
||||
if (!result)
|
||||
{
|
||||
ReportError(result.error(), srcManager);
|
||||
std::exit(-1);
|
||||
lexerError = result.error();
|
||||
isEOF = true;
|
||||
Token eof = {0, 0, TokenType::EndOfFile};
|
||||
buffer.push_back(eof);
|
||||
index = buffer.size() - 1;
|
||||
return buffer[index];
|
||||
}
|
||||
const Token &token = result.value();
|
||||
if (token.type == TokenType::Comments)
|
||||
@@ -81,8 +89,12 @@ namespace Fig
|
||||
auto result = lexer.NextToken();
|
||||
if (!result)
|
||||
{
|
||||
ReportError(result.error(), srcManager);
|
||||
std::abort();
|
||||
lexerError = result.error();
|
||||
isEOF = true;
|
||||
Token eof = {0, 0, TokenType::EndOfFile};
|
||||
buffer.push_back(eof);
|
||||
index = buffer.size() - 1;
|
||||
return buffer.back();
|
||||
}
|
||||
if (result->type == TokenType::Comments)
|
||||
continue;
|
||||
@@ -116,12 +128,16 @@ namespace Fig
|
||||
enum StateType : std::uint8_t
|
||||
{
|
||||
Standby,
|
||||
|
||||
ParsingLiteralExpr,
|
||||
ParsingIdentiExpr,
|
||||
ParsingInfixExpr,
|
||||
ParsingPrefixExpr,
|
||||
ParsingIndexExpr,
|
||||
ParsingCallExpr,
|
||||
ParsingLambdaExpr,
|
||||
ParsingNewExpr,
|
||||
|
||||
ParsingVarDecl,
|
||||
ParsingIf,
|
||||
ParsingWhile,
|
||||
@@ -129,7 +145,11 @@ namespace Fig
|
||||
ParsingReturn,
|
||||
ParsingBreak,
|
||||
ParsingContinue,
|
||||
ParsingStructDef,
|
||||
|
||||
ParsingTypeParameters,
|
||||
ParsingNamedTypeExpr,
|
||||
ParsingFnTypeExpr,
|
||||
} type = StateType::Standby;
|
||||
std::unordered_set<TokenType> stopAt = {};
|
||||
};
|
||||
@@ -137,7 +157,8 @@ namespace Fig
|
||||
private:
|
||||
const std::unordered_set<TokenType> &getBaseTerminators()
|
||||
{
|
||||
static const std::unordered_set<TokenType> baseTerminators = {TokenType::Semicolon,
|
||||
static const std::unordered_set<TokenType> baseTerminators{
|
||||
TokenType::Semicolon,
|
||||
TokenType::RightParen,
|
||||
TokenType::RightBracket,
|
||||
TokenType::RightBrace,
|
||||
@@ -146,20 +167,10 @@ namespace Fig
|
||||
return baseTerminators;
|
||||
}
|
||||
|
||||
std::unordered_set<TokenType> &getTerminators()
|
||||
{
|
||||
static std::unordered_set<TokenType> terminators(getBaseTerminators());
|
||||
return terminators;
|
||||
}
|
||||
void resetTermintors()
|
||||
{
|
||||
getTerminators() = getBaseTerminators();
|
||||
}
|
||||
|
||||
bool shouldTerminate()
|
||||
{
|
||||
const Token &token = currentToken();
|
||||
if (getTerminators().contains(token.type))
|
||||
if (getBaseTerminators().contains(token.type))
|
||||
return true;
|
||||
for (auto it = stateStack.rbegin(); it < stateStack.rend(); ++it)
|
||||
{
|
||||
@@ -200,34 +211,58 @@ namespace Fig
|
||||
SourceLocation makeSourceLocation(const Token &tok)
|
||||
{
|
||||
auto [line, column] = srcManager.GetLineColumn(tok.index);
|
||||
// 物理防爆盾:防止因解析错位导致的异常列号引起终端 OOM
|
||||
// 防止因解析错位导致的异常列号引起终端 OOM
|
||||
if (column > 5000)
|
||||
column = 1;
|
||||
return SourceLocation(SourcePosition(line, column, tok.length),
|
||||
return SourceLocation(
|
||||
SourcePosition(line, column, tok.length),
|
||||
fileName,
|
||||
"[internal parser]",
|
||||
magic_enum::enum_name(currentState().type).data());
|
||||
}
|
||||
|
||||
inline Error makeUnexpectTokenError(const String &stmt, const String &exp, const Token &got)
|
||||
inline Error makeUnexpectTokenError(
|
||||
const String &stmt,
|
||||
const String &exp,
|
||||
const Token &got,
|
||||
std::source_location th_loc = std::source_location::current())
|
||||
{
|
||||
return Error(ErrorType::SyntaxError,
|
||||
return Error(
|
||||
ErrorType::SyntaxError,
|
||||
std::format(
|
||||
"expect '{}' in {}, got `{}`", exp, stmt, magic_enum::enum_name(got.type)),
|
||||
"none",
|
||||
makeSourceLocation(got));
|
||||
makeSourceLocation(got),
|
||||
th_loc);
|
||||
}
|
||||
|
||||
inline Error makeExpectSemicolonError()
|
||||
inline Error
|
||||
makeExpectSemicolonError(std::source_location th_loc = std::source_location::current())
|
||||
{
|
||||
return Error(ErrorType::SyntaxError,
|
||||
return Error(
|
||||
ErrorType::SyntaxError,
|
||||
"expect ';' after statement",
|
||||
"insert ';'",
|
||||
makeSourceLocation(currentToken()));
|
||||
makeSourceLocation(currentToken()),
|
||||
th_loc);
|
||||
}
|
||||
|
||||
Result<TypeExpr *, Error> parseTypeExpr();
|
||||
Result<TypeExpr *, Error> parseNamedTypeExpr();
|
||||
inline Error makeExpectSemicolonError(
|
||||
const Token &token, std::source_location th_loc = std::source_location::current())
|
||||
{
|
||||
return Error(
|
||||
ErrorType::SyntaxError,
|
||||
"expect ';' after statement",
|
||||
"insert ';'",
|
||||
makeSourceLocation(token),
|
||||
th_loc);
|
||||
}
|
||||
|
||||
Result<decltype(StructDefStmt::typeParameters), Error> parseTypeParameters();
|
||||
|
||||
Result<Expr *, Error> parseTypeExpr();
|
||||
Result<Expr *, Error> parseNamedTypeExpr();
|
||||
Result<Expr *, Error> parseFnTypeExpr();
|
||||
|
||||
Result<Expr *, Error> parseExpression(BindingPower = 0);
|
||||
Result<Expr *, Error> parseLiteralExpr();
|
||||
@@ -237,9 +272,11 @@ namespace Fig
|
||||
Result<Expr *, Error> parseIndexExpr(Expr *);
|
||||
Result<Expr *, Error> parseCallExpr(Expr *);
|
||||
Result<Expr *, Error> parseNewExpr();
|
||||
Result<Expr *, Error> parseLambdaExpr();
|
||||
|
||||
Result<BlockStmt *, Error> parseBlockStmt();
|
||||
Result<VarDecl *, Error> parseVarDecl(bool);
|
||||
Result<VarDecl *, Error> parseConstDecl(bool);
|
||||
Result<IfStmt *, Error> parseIfStmt();
|
||||
Result<WhileStmt *, Error> parseWhileStmt();
|
||||
Result<DynArray<Param *>, Error> parseFnParams();
|
||||
@@ -249,12 +286,14 @@ namespace Fig
|
||||
Result<Stmt *, Error> parseStructDef(bool);
|
||||
Result<Stmt *, Error> parseInterfaceDef(bool);
|
||||
Result<Stmt *, Error> parseImpl();
|
||||
Result<Stmt *, Error> parseForStmt();
|
||||
Result<Stmt *, Error> parseImportStmt();
|
||||
|
||||
Result<Stmt *, Error> parseStatement();
|
||||
|
||||
public:
|
||||
Parser(Lexer &_lexer, SourceManager &_src, String _file) :
|
||||
lexer(_lexer), srcManager(_src), fileName(std::move(_file))
|
||||
Parser(Lexer &_lexer, SourceManager &_src, String _file, Diagnostics &_diagnostics) :
|
||||
lexer(_lexer), srcManager(_src), fileName(std::move(_file)), diagnostics(_diagnostics)
|
||||
{
|
||||
pushState(State());
|
||||
}
|
||||
|
||||
@@ -5,29 +5,39 @@ int main()
|
||||
{
|
||||
using namespace Fig;
|
||||
|
||||
String fileName = "test.fig";
|
||||
String filePath = "T:/Files/Maker/Code/MyCodingLanguage/The Fig Project/Fig/test.fig";
|
||||
String fileName = "test.fig";
|
||||
String filePath =
|
||||
"T:/Files/Maker/Code/MyCodingLanguage/The Fig Project/Fig/" + fileName;
|
||||
|
||||
SourceManager srcManager(filePath);
|
||||
|
||||
String source = srcManager.Read();
|
||||
if (!srcManager.read)
|
||||
{
|
||||
std::cerr << "Couldn't read file";
|
||||
std::cerr << "Couldn't read file: " << filePath << '\n';
|
||||
return 1;
|
||||
}
|
||||
|
||||
Lexer lexer(source, fileName);
|
||||
Parser parser(lexer, srcManager, fileName);
|
||||
auto result = parser.Parse();
|
||||
Lexer lexer(source, fileName);
|
||||
|
||||
Diagnostics diagnostics;
|
||||
Parser parser(lexer, srcManager, fileName, diagnostics);
|
||||
|
||||
auto result = parser.Parse();
|
||||
if (!result)
|
||||
{
|
||||
ReportError(result.error(), srcManager);
|
||||
return 1;
|
||||
}
|
||||
|
||||
diagnostics.EmitAll(srcManager);
|
||||
|
||||
Program *program = *result;
|
||||
for (Stmt *stmt : program->nodes)
|
||||
std::cout << "Parsed " << program->nodes.size() << " statements\n";
|
||||
for (size_t i = 0; i < program->nodes.size(); ++i)
|
||||
{
|
||||
std::cout << stmt->toString() << '\n';
|
||||
std::cout << '[' << i << "] " << program->nodes[i]->toString() << '\n';
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,73 +1,201 @@
|
||||
/*!
|
||||
@file src/Parser/TypeExprParser.cpp
|
||||
@brief 类型表达式解析器实现:支持泛型与空安全
|
||||
@brief 类型表达式解析器实现 — 类型即值,产生 Expr* 而非 TypeExpr*
|
||||
@author PuqiAR (im@puqiar.top)
|
||||
@date 2026-03-08
|
||||
@date 2026-06-06
|
||||
*/
|
||||
|
||||
#include <Parser/Parser.hpp>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
// 解析基础命名类型与泛型: List<Int>
|
||||
Result<TypeExpr *, Error> Parser::parseNamedTypeExpr()
|
||||
Result<decltype(StructDefStmt::typeParameters), Error> Parser::parseTypeParameters()
|
||||
{
|
||||
StateProtector p(this, {State::ParsingNamedTypeExpr});
|
||||
SourceLocation location = makeSourceLocation(currentToken());
|
||||
StateProtector p(this, {State::ParsingTypeParameters});
|
||||
decltype(StructDefStmt::typeParameters) tp;
|
||||
|
||||
const Token &lab = consumeToken(); // consume `<`
|
||||
|
||||
DynArray<String> path;
|
||||
while (true)
|
||||
{
|
||||
const Token &tok = consumeToken();
|
||||
const String &name = srcManager.GetSub(tok.index, tok.length);
|
||||
path.push_back(name);
|
||||
|
||||
if (match(TokenType::Dot))
|
||||
if (isEOF)
|
||||
{
|
||||
if (!currentToken().isIdentifier())
|
||||
return std::unexpected(makeUnexpectTokenError("Type", "identifier", currentToken()));
|
||||
return std::unexpected(Error(
|
||||
ErrorType::SyntaxError,
|
||||
"unclosed `<` in type parameters",
|
||||
"insert '>'",
|
||||
makeSourceLocation(lab)));
|
||||
}
|
||||
else break;
|
||||
if (match(TokenType::Greater)) // >
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (!currentToken().isIdentifier())
|
||||
{
|
||||
return std::unexpected(
|
||||
makeUnexpectTokenError("TypeParams", "tp name", currentToken()));
|
||||
}
|
||||
|
||||
const Token &name_tok = consumeToken();
|
||||
const String &name = srcManager.GetSub(name_tok.index, name_tok.length);
|
||||
tp.push_back(name);
|
||||
|
||||
if (!match(TokenType::Comma))
|
||||
{
|
||||
return std::unexpected(makeUnexpectTokenError(
|
||||
"TypeParams", "comma or type parameter", currentToken()));
|
||||
}
|
||||
}
|
||||
return tp;
|
||||
}
|
||||
|
||||
|
||||
|
||||
Result<Expr *, Error> Parser::parseNamedTypeExpr()
|
||||
{
|
||||
StateProtector p(this, {State::ParsingNamedTypeExpr});
|
||||
|
||||
|
||||
if (!currentToken().isIdentifier())
|
||||
{
|
||||
return std::unexpected(
|
||||
makeUnexpectTokenError("TypeExpr", "type name", currentToken()));
|
||||
}
|
||||
|
||||
DynArray<TypeExpr *> arguments;
|
||||
if (match(TokenType::Less)) // `<`
|
||||
const Token &firstTok = consumeToken();
|
||||
SourceLocation firstLoc = makeSourceLocation(firstTok);
|
||||
const String &firstName = srcManager.GetSub(firstTok.index, firstTok.length);
|
||||
|
||||
IdentiExpr *ident = arena.Allocate<IdentiExpr>(firstName, firstLoc);
|
||||
Expr *base = ident;
|
||||
|
||||
// a.b.c
|
||||
while (match(TokenType::Dot))
|
||||
{
|
||||
if (!currentToken().isIdentifier())
|
||||
{
|
||||
return std::unexpected(
|
||||
makeUnexpectTokenError("TypeExpr", "identifier after `.`", currentToken()));
|
||||
}
|
||||
const Token &tok = consumeToken();
|
||||
const String &name = srcManager.GetSub(tok.index, tok.length);
|
||||
SourceLocation loc = makeSourceLocation(tok);
|
||||
base = arena.Allocate<MemberExpr>(base, name, loc);
|
||||
}
|
||||
|
||||
// generic args
|
||||
if (match(TokenType::Less))
|
||||
{
|
||||
DynArray<Expr *> args;
|
||||
|
||||
while (true)
|
||||
{
|
||||
auto result = parseTypeExpr();
|
||||
if (!result) return std::unexpected(result.error());
|
||||
arguments.push_back(*result);
|
||||
if (!result)
|
||||
return std::unexpected(result.error());
|
||||
args.push_back(*result);
|
||||
|
||||
if (match(TokenType::Greater)) break; // `>`
|
||||
if (match(TokenType::Greater))
|
||||
break; // `>`
|
||||
if (!match(TokenType::Comma))
|
||||
return std::unexpected(makeUnexpectTokenError("TypeArgs", "'>' or ','", currentToken()));
|
||||
return std::unexpected(
|
||||
makeUnexpectTokenError("TypeArgs", "'>' or ','", currentToken()));
|
||||
}
|
||||
}
|
||||
|
||||
return arena.Allocate<NamedTypeExpr>(path, arguments, location);
|
||||
}
|
||||
|
||||
// 解析主入口: 处理 `?` 后缀
|
||||
Result<TypeExpr *, Error> Parser::parseTypeExpr()
|
||||
{
|
||||
TypeExpr *base = nullptr;
|
||||
|
||||
// 目前只支持命名类型 (以后可以加函数类型 (Int)->Int)
|
||||
if (currentToken().isIdentifier())
|
||||
{
|
||||
auto res = parseNamedTypeExpr();
|
||||
if (!res) return std::unexpected(res.error());
|
||||
base = *res;
|
||||
}
|
||||
else return std::unexpected(makeUnexpectTokenError("TypeExpr", "name", currentToken()));
|
||||
|
||||
// 空安全处理: Int?? 也可以,但 Analyzer 会规范化它
|
||||
while (match(TokenType::Question))
|
||||
{
|
||||
base = arena.Allocate<NullableTypeExpr>(base, makeSourceLocation(prevToken()));
|
||||
base = arena.Allocate<ApplyExpr>(base, std::move(args), firstLoc);
|
||||
}
|
||||
|
||||
return base;
|
||||
}
|
||||
}
|
||||
|
||||
Result<Expr *, Error> Parser::parseFnTypeExpr()
|
||||
{
|
||||
StateProtector p(this, {State::ParsingFnTypeExpr});
|
||||
SourceLocation location = makeSourceLocation(consumeToken()); // consume `func`
|
||||
if (!match(TokenType::LeftParen)) // `(`
|
||||
{
|
||||
return std::unexpected(
|
||||
makeUnexpectTokenError("FnTypeExpr", "lparen (", currentToken()));
|
||||
}
|
||||
|
||||
DynArray<Expr *> paraTypes;
|
||||
|
||||
while (true)
|
||||
{
|
||||
auto result = parseTypeExpr();
|
||||
if (!result)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
paraTypes.push_back(*result);
|
||||
|
||||
if (match(TokenType::RightParen))
|
||||
{
|
||||
break;
|
||||
}
|
||||
else if (isEOF)
|
||||
{
|
||||
return std::unexpected(
|
||||
makeUnexpectTokenError("FnTypeExpr", "rparen )", currentToken()));
|
||||
}
|
||||
if (!match(TokenType::Comma))
|
||||
{
|
||||
return std::unexpected(
|
||||
makeUnexpectTokenError("FnTypeExpr", "comma ,", currentToken()));
|
||||
}
|
||||
}
|
||||
|
||||
Expr *returnType = nullptr;
|
||||
|
||||
if (match(TokenType::RightArrow)) // ->
|
||||
{
|
||||
auto result = parseTypeExpr();
|
||||
if (!result)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
returnType = *result;
|
||||
}
|
||||
|
||||
FnTypeExpr *fnTypeExpr = arena.Allocate<FnTypeExpr>(paraTypes, returnType);
|
||||
return fnTypeExpr;
|
||||
}
|
||||
|
||||
|
||||
Result<Expr *, Error> Parser::parseTypeExpr()
|
||||
{
|
||||
Expr *base = nullptr;
|
||||
|
||||
if (currentToken().isIdentifier())
|
||||
{
|
||||
auto result = parseNamedTypeExpr();
|
||||
if (!result)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
base = *result;
|
||||
}
|
||||
else if (currentToken().type == TokenType::Function)
|
||||
{
|
||||
auto result = parseFnTypeExpr();
|
||||
if (!result)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
base = *result;
|
||||
}
|
||||
else
|
||||
{
|
||||
return std::unexpected(makeUnexpectTokenError("TypeExpr", "name", currentToken()));
|
||||
}
|
||||
|
||||
// nullable
|
||||
if (currentToken().type == TokenType::Question) // ?
|
||||
{
|
||||
base = arena.Allocate<NullableExpr>(
|
||||
base, makeSourceLocation(consumeToken())); // consume `?`
|
||||
}
|
||||
|
||||
return base;
|
||||
}
|
||||
} // namespace Fig
|
||||
|
||||
@@ -38,15 +38,18 @@ namespace Fig
|
||||
|
||||
void PrintInfo()
|
||||
{
|
||||
out << std::format("Fig {}, copyright (c) 2025-2026 PuqiAR, under the {} License\n",
|
||||
out << std::format(
|
||||
"Fig {}, copyright (c) 2025-2026 PuqiAR, under the {} License\n",
|
||||
Core::VERSION,
|
||||
Core::LICENSE);
|
||||
out << std::format("Build time: {} [{} x{} on {}]\n",
|
||||
out << std::format(
|
||||
"Build time: {} [{} x{} on {}]\n",
|
||||
Core::COMPILE_TIME,
|
||||
Core::COMPILER,
|
||||
Core::ARCH,
|
||||
Core::PLATFORM);
|
||||
out << "Type '#exit' to exit, '#clear' to clear the the screen, '#license' to see the full license, '#logo' to see a GREAT logo\n";
|
||||
out << '\n';
|
||||
}
|
||||
|
||||
void ClearConsole()
|
||||
@@ -146,17 +149,58 @@ namespace Fig
|
||||
++rline;
|
||||
}
|
||||
}
|
||||
// void PrintError(const Error &error, const String &source)
|
||||
// {
|
||||
// err << "Oops! An error occurred!";
|
||||
// err << "🔥 " << 'E' << static_cast<int>(error.type) << ": " << error.message << '\n';
|
||||
// err << "Line " << rline << ", `" << source << "`\n";
|
||||
// err << "Suggestion: " << error.suggestion << '\n';
|
||||
// err << std::format(
|
||||
// "Thrower: {} ({}:{}:{})\n",
|
||||
// error.thrower_loc.function_name(),
|
||||
// error.thrower_loc.file_name(),
|
||||
// error.thrower_loc.line(),
|
||||
// error.thrower_loc.column());
|
||||
// }
|
||||
|
||||
void PrintError(const Error &error, const String &source)
|
||||
{
|
||||
err << "Oops! An error occurred!";
|
||||
err << "🔥 " << 'E' << static_cast<int>(error.type) << ": " << error.message << '\n';
|
||||
err << "Line " << rline << ", `" << source << "`\n";
|
||||
err << "Suggestion: " << error.suggestion << '\n';
|
||||
err << std::format("Thrower: {} ({}:{}:{})\n",
|
||||
error.thrower_loc.function_name(),
|
||||
error.thrower_loc.file_name(),
|
||||
error.thrower_loc.line(),
|
||||
error.thrower_loc.column());
|
||||
static constexpr const char *MinorColor = "\033[38;2;138;227;198m";
|
||||
static constexpr const char *MediumColor = "\033[38;2;255;199;95m";
|
||||
static constexpr const char *CriticalColor = "\033[38;2;255;107;107m";
|
||||
|
||||
namespace TC = TerminalColors;
|
||||
std::ostream &err = CoreIO::GetStdErr();
|
||||
|
||||
uint8_t level = ErrorLevel(error.type);
|
||||
// const char *level_name = (level == 1 ? "Minor" : (level == 2 ? "Medium" :
|
||||
// "Critical"));
|
||||
const char *const &level_color =
|
||||
(level == 1 ? MinorColor : (level == 2 ? MediumColor : CriticalColor));
|
||||
|
||||
err << "\n";
|
||||
err << "🔥 "
|
||||
<< level_color
|
||||
//<< '(' << level_name << ')'
|
||||
<< 'E' << static_cast<int>(error.type) << TC::Reset << ": " << level_color
|
||||
<< ErrorTypeToString(error.type) << TC::Reset << '\n';
|
||||
|
||||
const SourceLocation &location = error.location;
|
||||
|
||||
err << TC::DarkGray << " ┌─> Fn " << TC::Cyan << '\'' << location.packageName << '.'
|
||||
<< location.functionName << '\'' << " " << location.fileName << " (" << TC::DarkGray
|
||||
<< location.sp.line << ":" << location.sp.column << TC::Cyan << ')' << TC::Reset
|
||||
<< '\n';
|
||||
err << TC::DarkGray << " │" << '\n' << " │" << TC::Reset << '\n';
|
||||
err << TC::DarkGray << " └─ " << TC::Reset;
|
||||
err << source << "\n";
|
||||
|
||||
err << "\n";
|
||||
err << "❓ " << TC::DarkGray << "Thrower: " << error.thrower_loc.function_name() << " ("
|
||||
<< error.thrower_loc.file_name() << ":" << error.thrower_loc.line() << ")"
|
||||
<< TC::Reset << "\n";
|
||||
err << "💡 " << TC::Blue << "Suggestion: " << error.suggestion << TC::Reset;
|
||||
err << '\n';
|
||||
}
|
||||
|
||||
unsigned int Start() // exit code: unsigned int
|
||||
@@ -208,8 +252,11 @@ namespace Fig
|
||||
|
||||
String source(buf);
|
||||
|
||||
Lexer lexer(buf, fileName);
|
||||
Parser parser(lexer, manager, fileName);
|
||||
Lexer lexer(buf, fileName);
|
||||
|
||||
Diagnostics diagnostics;
|
||||
|
||||
Parser parser(lexer, manager, fileName, diagnostics);
|
||||
|
||||
auto _program = parser.Parse();
|
||||
if (!_program)
|
||||
@@ -223,13 +270,15 @@ namespace Fig
|
||||
Analyzer analyzer(manager);
|
||||
auto result = analyzer.Analyze(program);
|
||||
|
||||
analyzer.GetDiagnostics().EmitAll(manager);
|
||||
|
||||
if (!result)
|
||||
{
|
||||
PrintError(result.error(), source);
|
||||
continue;
|
||||
}
|
||||
|
||||
Compiler compiler(manager, analyzer.GetDiagnostics());
|
||||
Compiler compiler(manager, diagnostics);
|
||||
|
||||
auto compile_result = compiler.Compile(program);
|
||||
if (!compile_result)
|
||||
@@ -238,6 +287,8 @@ namespace Fig
|
||||
continue;
|
||||
}
|
||||
|
||||
diagnostics.EmitAll(manager);
|
||||
|
||||
CompiledModule *compiledModule = *compile_result;
|
||||
|
||||
auto exe_result = vm.Execute(compiledModule);
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
|
||||
#include <Ast/Ast.hpp>
|
||||
#include <Ast/Expr/MemberExpr.hpp>
|
||||
#include <Ast/Expr/ObjectInitExpr.hpp>
|
||||
#include <Ast/Stmt/ImplStmt.hpp>
|
||||
#include <Ast/Stmt/InterfaceDefStmt.hpp>
|
||||
#include <Ast/Stmt/StructDefStmt.hpp>
|
||||
@@ -82,8 +81,11 @@ namespace Fig
|
||||
{
|
||||
auto *s = static_cast<StructDefStmt *>(stmt);
|
||||
if (globalTypes.contains(s->name))
|
||||
{
|
||||
return std::unexpected(
|
||||
Error(ErrorType::RedeclarationError, "type redeclared", "", s->location));
|
||||
}
|
||||
|
||||
auto *t = arena.Allocate<StructType>(s->name);
|
||||
typeCtx.allTypes.push_back(t);
|
||||
globalTypes[s->name] = t;
|
||||
@@ -92,10 +94,14 @@ namespace Fig
|
||||
{
|
||||
auto *f = static_cast<FnDefStmt *>(stmt);
|
||||
if (f->name == "main")
|
||||
{
|
||||
hasMain = true;
|
||||
}
|
||||
if (globalSymbols.contains(f->name))
|
||||
{
|
||||
return std::unexpected(
|
||||
Error(ErrorType::RedeclarationError, "func redeclared", "", f->location));
|
||||
}
|
||||
Symbol *sym =
|
||||
arena.Allocate<Symbol>(f->name, Type{}, SymbolLocation::Global, 0, true);
|
||||
globalSymbols[f->name] = sym;
|
||||
@@ -118,7 +124,9 @@ namespace Fig
|
||||
{
|
||||
auto res = resolveTypeExpr(f.type);
|
||||
if (!res)
|
||||
{
|
||||
return std::unexpected(res.error());
|
||||
}
|
||||
st->AddField(f.name, *res, f.isPublic);
|
||||
}
|
||||
}
|
||||
@@ -185,10 +193,11 @@ namespace Fig
|
||||
}
|
||||
Type declT = v->typeSpecifier ? *resolveTypeExpr(v->typeSpecifier) : initT;
|
||||
|
||||
// 🔥 强类型校验:赋值拦截
|
||||
// 赋值拦截
|
||||
if (v->initExpr && !initT.isAssignableTo(declT))
|
||||
{
|
||||
return std::unexpected(Error(ErrorType::TypeError,
|
||||
return std::unexpected(Error(
|
||||
ErrorType::TypeError,
|
||||
"cannot assign '" + initT.toString() + "' to type '" + declT.toString()
|
||||
+ "'",
|
||||
"",
|
||||
@@ -210,7 +219,7 @@ namespace Fig
|
||||
case AstType::FnDefStmt: {
|
||||
auto *f = static_cast<FnDefStmt *>(stmt);
|
||||
|
||||
// 3.10: 局部闭包延迟类型推导
|
||||
// 局部闭包延迟类型推导
|
||||
|
||||
if (!f->resolvedSymbol) // 闭包?
|
||||
{
|
||||
@@ -243,7 +252,8 @@ namespace Fig
|
||||
ScopeGuard scopeGuard(env, true);
|
||||
for (auto *p : f->params)
|
||||
{
|
||||
env.current->locals[p->name] = arena.Allocate<Symbol>(p->name,
|
||||
env.current->locals[p->name] = arena.Allocate<Symbol>(
|
||||
p->name,
|
||||
p->resolvedType,
|
||||
SymbolLocation::Local,
|
||||
env.current->nextLocalId++,
|
||||
@@ -279,7 +289,8 @@ namespace Fig
|
||||
return std::unexpected(c.error());
|
||||
else if (!c->isAssignableTo(typeCtx.GetBasic(TypeTag::Bool)))
|
||||
{
|
||||
return std::unexpected(Error(ErrorType::TypeError,
|
||||
return std::unexpected(Error(
|
||||
ErrorType::TypeError,
|
||||
"condition must be Bool",
|
||||
"",
|
||||
elif->cond->location));
|
||||
@@ -330,11 +341,15 @@ namespace Fig
|
||||
break;
|
||||
}
|
||||
case AstType::BreakStmt:
|
||||
case AstType::ContinueStmt:
|
||||
case AstType::ContinueStmt: {
|
||||
if (state.loopDepth <= 0)
|
||||
{
|
||||
return std::unexpected(
|
||||
Error(ErrorType::SyntaxError, "outside loop", "", stmt->location));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case AstType::ReturnStmt: {
|
||||
auto *rs = static_cast<ReturnStmt *>(stmt);
|
||||
Type retT = typeCtx.GetBasic(TypeTag::Null);
|
||||
@@ -345,10 +360,11 @@ namespace Fig
|
||||
return std::unexpected(res.error());
|
||||
retT = *res;
|
||||
}
|
||||
// 🔥 强类型校验:返回值拦截
|
||||
// 返回值校验
|
||||
if (state.currentFn && !retT.isAssignableTo(state.currentFn->resolvedReturnType))
|
||||
{
|
||||
return std::unexpected(Error(ErrorType::TypeError,
|
||||
return std::unexpected(Error(
|
||||
ErrorType::TypeError,
|
||||
"cannot return '" + retT.toString() + "' from function expecting '"
|
||||
+ state.currentFn->resolvedReturnType.toString() + "'",
|
||||
"",
|
||||
@@ -407,7 +423,8 @@ namespace Fig
|
||||
auto *st = static_cast<StructType *>(targetType.base);
|
||||
if (!st->fieldMap.contains(m->name))
|
||||
{
|
||||
return std::unexpected(Error(ErrorType::TypeError,
|
||||
return std::unexpected(Error(
|
||||
ErrorType::TypeError,
|
||||
"struct '" + st->name + "' has no field named '" + m->name + "'",
|
||||
"",
|
||||
m->location));
|
||||
@@ -415,14 +432,18 @@ namespace Fig
|
||||
// 字段类型
|
||||
return expr->resolvedType = st->fields[st->fieldMap[m->name]].type;
|
||||
}
|
||||
case AstType::ObjectInitExpr: {
|
||||
auto *o = static_cast<ObjectInitExpr *>(expr);
|
||||
case AstType::NewExpr: {
|
||||
auto *o = static_cast<NewExpr *>(expr);
|
||||
auto res = resolveTypeExpr(o->typeExpr);
|
||||
if (!res)
|
||||
{
|
||||
return std::unexpected(res.error());
|
||||
}
|
||||
if (!res->base || res->base->tag != TypeTag::Struct)
|
||||
{
|
||||
return std::unexpected(
|
||||
Error(ErrorType::TypeError, "requires struct", "", o->location));
|
||||
}
|
||||
auto *st = static_cast<StructType *>(res->base);
|
||||
for (auto &arg : o->args)
|
||||
{
|
||||
@@ -431,7 +452,9 @@ namespace Fig
|
||||
Error(ErrorType::TypeError, "unknown field", "", arg.value->location));
|
||||
auto r = analyzeExpr(arg.value);
|
||||
if (!r)
|
||||
{
|
||||
return std::unexpected(r.error());
|
||||
}
|
||||
// 字段赋值类型检查
|
||||
if (!arg.name.empty()
|
||||
&& !r->isAssignableTo(st->fields[st->fieldMap[arg.name]].type))
|
||||
@@ -456,7 +479,8 @@ namespace Fig
|
||||
if (in->op == BinaryOperator::Assign)
|
||||
{
|
||||
if (!r.isAssignableTo(l))
|
||||
return std::unexpected(Error(ErrorType::TypeError,
|
||||
return std::unexpected(Error(
|
||||
ErrorType::TypeError,
|
||||
"cannot assign '" + r.toString() + "' to '" + l.toString() + "'",
|
||||
"",
|
||||
in->location));
|
||||
@@ -511,19 +535,22 @@ namespace Fig
|
||||
Error(ErrorType::TypeError, "callee is not a function", "", c->location));
|
||||
|
||||
auto *ft = static_cast<FuncType *>(calleeType.base);
|
||||
|
||||
if (ft->paramTypes.size() != argTypes.size())
|
||||
{
|
||||
return std::unexpected(Error(ErrorType::TypeError,
|
||||
"expected " + std::to_string(ft->paramTypes.size()) + " arguments, got "
|
||||
+ std::to_string(argTypes.size()),
|
||||
"",
|
||||
return std::unexpected(Error(
|
||||
ErrorType::SyntaxError,
|
||||
std::format(
|
||||
"expected {} arguments, got {}", ft->paramTypes.size(), argTypes.size()),
|
||||
"none",
|
||||
c->location));
|
||||
}
|
||||
for (size_t i = 0; i < argTypes.size(); ++i)
|
||||
{
|
||||
if (!argTypes[i].isAssignableTo(ft->paramTypes[i]))
|
||||
{
|
||||
return std::unexpected(Error(ErrorType::TypeError,
|
||||
return std::unexpected(Error(
|
||||
ErrorType::TypeError,
|
||||
"argument " + std::to_string(i + 1) + " expects '"
|
||||
+ ft->paramTypes[i].toString() + "', got '" + argTypes[i].toString()
|
||||
+ "'",
|
||||
@@ -533,13 +560,86 @@ namespace Fig
|
||||
}
|
||||
return expr->resolvedType = ft->retType;
|
||||
}
|
||||
|
||||
case AstType::LambdaExpr: {
|
||||
auto l = static_cast<LambdaExpr *>(expr);
|
||||
|
||||
Type returnType = typeCtx.GetBasic(TypeTag::Any);
|
||||
|
||||
if (l->returnType)
|
||||
{
|
||||
auto tres = resolveTypeExpr(l->returnType);
|
||||
if (!tres)
|
||||
{
|
||||
return tres;
|
||||
}
|
||||
|
||||
returnType = *tres;
|
||||
}
|
||||
|
||||
FnDefStmt *f = arena.Allocate<FnDefStmt>(
|
||||
false, "LambdaFn", l->params, l->returnType, nullptr, l->location);
|
||||
|
||||
FnStateGuard fnGuard(state.currentFn, f);
|
||||
ScopeGuard scopeGuard(env, true);
|
||||
|
||||
DynArray<Type> paramTypes;
|
||||
for (auto *p : l->params)
|
||||
{
|
||||
auto pres = resolveTypeExpr(p->typeSpecifier);
|
||||
if (!pres)
|
||||
{
|
||||
return pres;
|
||||
}
|
||||
p->resolvedType = *pres;
|
||||
paramTypes.push_back(*pres);
|
||||
|
||||
env.current->locals[p->name] = arena.Allocate<Symbol>(
|
||||
p->name,
|
||||
p->resolvedType,
|
||||
SymbolLocation::Local,
|
||||
env.current->nextLocalId++,
|
||||
false);
|
||||
}
|
||||
|
||||
if (l->isExprBody)
|
||||
{
|
||||
Expr *expr = static_cast<Expr *>(l->body);
|
||||
if (auto r = analyzeExpr(expr); !r)
|
||||
{
|
||||
return r;
|
||||
}
|
||||
if (!expr->resolvedType.isAssignableTo(state.currentFn->resolvedReturnType))
|
||||
{
|
||||
return std::unexpected(Error(
|
||||
ErrorType::TypeError,
|
||||
"cannot return '" + state.currentFn->resolvedReturnType.toString()
|
||||
+ "' from lambda function expecting '"
|
||||
+ state.currentFn->resolvedReturnType.toString() + "'",
|
||||
"",
|
||||
expr->location));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Stmt *stmt = static_cast<Stmt *>(l->body);
|
||||
if (auto r = analyzeStmt(stmt); !r)
|
||||
{
|
||||
return std::unexpected(r.error());
|
||||
}
|
||||
}
|
||||
|
||||
return l->resolvedType = typeCtx.CreateFuncType(paramTypes, returnType);
|
||||
}
|
||||
|
||||
default: break;
|
||||
}
|
||||
|
||||
return expr->resolvedType = typeCtx.GetBasic(TypeTag::Any);
|
||||
}
|
||||
|
||||
Result<Symbol *, Error> Analyzer::resolveSymbolInternal(
|
||||
const String &name, const SourceLocation &loc, Scope *s)
|
||||
Result<Symbol *, Error>
|
||||
Analyzer::resolveSymbolInternal(const String &name, const SourceLocation &loc, Scope *s)
|
||||
{
|
||||
Scope *curr = s;
|
||||
while (curr)
|
||||
@@ -578,7 +678,7 @@ namespace Fig
|
||||
return idx;
|
||||
}
|
||||
|
||||
Result<Type, Error> Analyzer::resolveTypeExpr(TypeExpr *texpr)
|
||||
Result<Type, Error> Analyzer::resolveTypeExpr(Expr *texpr)
|
||||
{
|
||||
if (!texpr)
|
||||
return typeCtx.GetBasic(TypeTag::Any);
|
||||
@@ -607,13 +707,46 @@ namespace Fig
|
||||
return std::unexpected(
|
||||
Error(ErrorType::UseUndeclaredIdentifier, "unknown type", "", texpr->location));
|
||||
}
|
||||
if (texpr->type == AstType::NullableTypeExpr)
|
||||
else if (texpr->type == AstType::NullableTypeExpr)
|
||||
{
|
||||
auto res = resolveTypeExpr(static_cast<NullableTypeExpr *>(texpr)->inner);
|
||||
if (res)
|
||||
res->isNullable = true;
|
||||
if (!res)
|
||||
{
|
||||
return res;
|
||||
}
|
||||
|
||||
res->isNullable = true;
|
||||
return res;
|
||||
}
|
||||
else if (texpr->type == AstType::FnTypeExpr)
|
||||
{
|
||||
auto f = static_cast<FnTypeExpr *>(texpr);
|
||||
|
||||
DynArray<Type> paraTypes;
|
||||
Type returnType = typeCtx.GetBasic(TypeTag::Any);
|
||||
|
||||
for (auto &pt : f->paraTypes)
|
||||
{
|
||||
auto result = resolveTypeExpr(pt);
|
||||
if (!result)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
paraTypes.push_back(*result);
|
||||
}
|
||||
|
||||
if (f->returnType)
|
||||
{
|
||||
auto result = resolveTypeExpr(f->returnType);
|
||||
if (!result)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
returnType = *result;
|
||||
}
|
||||
return typeCtx.CreateFuncType(paraTypes, returnType);
|
||||
}
|
||||
|
||||
return typeCtx.GetBasic(TypeTag::Any);
|
||||
}
|
||||
} // namespace Fig
|
||||
@@ -32,7 +32,7 @@ namespace Fig
|
||||
// 核心递归查找:解决跨越函数边界的捕获问题
|
||||
Result<Symbol*, Error> resolveSymbolInternal(const String &name, const SourceLocation &loc, Scope* startScope);
|
||||
|
||||
Result<Type, Error> resolveTypeExpr(TypeExpr *texpr);
|
||||
Result<Type, Error> resolveTypeExpr(Expr *texpr);
|
||||
Result<void, Error> pass1(Program *prog);
|
||||
Result<void, Error> resolveTypes(Program *prog);
|
||||
Result<void, Error> checkBodies(Program *prog);
|
||||
|
||||
@@ -20,7 +20,12 @@ void runTest(const std::string &path)
|
||||
}
|
||||
|
||||
Lexer lexer(source, String(path));
|
||||
Parser parser(lexer, srcManager, String(path));
|
||||
|
||||
Diagnostics diagnostics;
|
||||
|
||||
Parser parser(lexer, srcManager, String(path), diagnostics);
|
||||
|
||||
diagnostics.EmitAll(srcManager);
|
||||
|
||||
auto pRes = parser.Parse();
|
||||
if (!pRes)
|
||||
|
||||
@@ -39,10 +39,15 @@ namespace Fig
|
||||
bool Type::isAssignableTo(const Type &target) const
|
||||
{
|
||||
if (target.is(TypeTag::Any) || this->is(TypeTag::Any))
|
||||
return true; // Any 逃逸通道
|
||||
{
|
||||
return true; // Any 逃逸
|
||||
}
|
||||
if (this->is(TypeTag::Null) && target.isNullable)
|
||||
{
|
||||
return true; // Null 安全赋值
|
||||
return this->base == target.base && (!this->isNullable || target.isNullable);
|
||||
}
|
||||
|
||||
return *this->base == *target.base && (!this->isNullable || target.isNullable);
|
||||
}
|
||||
|
||||
TypeContext::TypeContext()
|
||||
@@ -65,7 +70,9 @@ namespace Fig
|
||||
TypeContext::~TypeContext()
|
||||
{
|
||||
for (auto t : allTypes)
|
||||
{
|
||||
delete t;
|
||||
}
|
||||
}
|
||||
|
||||
Type TypeContext::GetBasic(TypeTag tag, bool nullable)
|
||||
|
||||
@@ -51,6 +51,11 @@ namespace Fig
|
||||
String name;
|
||||
BaseType(TypeTag t, String n) : tag(t), name(std::move(n)) {}
|
||||
virtual ~BaseType() = default;
|
||||
|
||||
bool operator==(const BaseType &other) const
|
||||
{
|
||||
return tag == other.tag && name == other.name;
|
||||
}
|
||||
};
|
||||
|
||||
class FuncType : public BaseType
|
||||
@@ -62,6 +67,11 @@ namespace Fig
|
||||
BaseType(TypeTag::Function, "Function"), paramTypes(std::move(params)), retType(ret)
|
||||
{
|
||||
}
|
||||
|
||||
bool operator==(const FuncType &other) const
|
||||
{
|
||||
return paramTypes == other.paramTypes && retType == other.retType;
|
||||
}
|
||||
};
|
||||
|
||||
class StructType : public BaseType
|
||||
@@ -85,6 +95,11 @@ namespace Fig
|
||||
fields.push_back({name, type, isPublic, (int) idx});
|
||||
fieldMap[name] = idx;
|
||||
}
|
||||
|
||||
bool operator==(const StructType &other) const
|
||||
{
|
||||
return this == &other; // 即使是两个完全一样的struct, 也认作不同的type
|
||||
}
|
||||
};
|
||||
|
||||
class InterfaceType : public BaseType
|
||||
@@ -98,6 +113,11 @@ namespace Fig
|
||||
};
|
||||
HashMap<String, MethodSig> methods;
|
||||
InterfaceType(String n) : BaseType(TypeTag::Interface, std::move(n)) {}
|
||||
|
||||
bool operator==(const InterfaceType &other) const
|
||||
{
|
||||
return this == &other; // 即使是两个完全一样的interface, 也认作不同的type
|
||||
}
|
||||
};
|
||||
|
||||
class TypeContext
|
||||
|
||||
109
src/VM/Entry.cpp
109
src/VM/Entry.cpp
@@ -5,26 +5,62 @@
|
||||
@date 2026-03-13
|
||||
*/
|
||||
|
||||
|
||||
#include <VM/Entry.hpp>
|
||||
|
||||
#include <chrono>
|
||||
#include <filesystem>
|
||||
|
||||
#include <Core/Core.hpp>
|
||||
#include <SourceManager/SourceManager.hpp>
|
||||
|
||||
#include <Bytecode/Disassembler.hpp>
|
||||
#include <Compiler/Compiler.hpp>
|
||||
#include <Lexer/Lexer.hpp>
|
||||
#include <Parser/Parser.hpp>
|
||||
#include <Repl/Repl.hpp>
|
||||
#include <Sema/Analyzer.hpp>
|
||||
#include <Compiler/Compiler.hpp>
|
||||
#include <VM/VM.hpp>
|
||||
|
||||
namespace Fig::Entry
|
||||
{
|
||||
void RunFromPath(const String &path)
|
||||
void RunFromPath(const String &path, const Config &conf)
|
||||
{
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
using clock = std::chrono::steady_clock;
|
||||
|
||||
auto format_print_time = [](std::chrono::nanoseconds nsecs) {
|
||||
auto &out = CoreIO::GetStdOut();
|
||||
auto count = nsecs.count();
|
||||
|
||||
auto old_flags = out.flags();
|
||||
auto old_precision = out.precision();
|
||||
|
||||
if (count < 1'000)
|
||||
{
|
||||
// < 1μs 纳秒
|
||||
out << count << "ns";
|
||||
}
|
||||
else if (count < 1'000'000)
|
||||
{
|
||||
// 1μs ~ 1ms 微秒 保留 2 位小数
|
||||
out << std::fixed << std::setprecision(2) << (count / 1'000.0) << "μs";
|
||||
}
|
||||
else if (count < 1'000'000'000)
|
||||
{
|
||||
// 1ms ~ 1s 毫秒 保留 2 位小数
|
||||
out << std::fixed << std::setprecision(2) << (count / 1'000'000.0) << "ms";
|
||||
}
|
||||
else
|
||||
{
|
||||
// >= 1s 秒 保留 3 位小数
|
||||
out << std::fixed << std::setprecision(3) << (count / 1'000'000'000.0) << "s";
|
||||
}
|
||||
|
||||
out.flags(old_flags);
|
||||
out.precision(old_precision);
|
||||
};
|
||||
|
||||
fs::path _fspath(path.toStdString());
|
||||
|
||||
if (!fs::exists(_fspath))
|
||||
@@ -53,18 +89,28 @@ namespace Fig::Entry
|
||||
const String &source = manager.GetSource();
|
||||
|
||||
Lexer lexer(source, fileName);
|
||||
Parser parser(lexer, manager, fileName);
|
||||
|
||||
Diagnostics diagnostics;
|
||||
|
||||
Parser parser(lexer, manager, fileName, diagnostics);
|
||||
|
||||
auto parse_start = clock::now();
|
||||
auto parse_result = parser.Parse();
|
||||
auto parse_end = clock::now();
|
||||
|
||||
if (!parse_result)
|
||||
{
|
||||
ReportError(parse_result.error(), manager);
|
||||
std::exit(1);
|
||||
}
|
||||
|
||||
Program *program = *parse_result;
|
||||
|
||||
Analyzer analyer(manager);
|
||||
|
||||
auto analyze_start = clock::now();
|
||||
auto analyze_result = analyer.Analyze(program);
|
||||
auto analyze_end = clock::now();
|
||||
|
||||
if (!analyze_result)
|
||||
{
|
||||
@@ -72,10 +118,12 @@ namespace Fig::Entry
|
||||
std::exit(1);
|
||||
}
|
||||
|
||||
Diagnostics diagnostics;
|
||||
Compiler compiler(manager, diagnostics);
|
||||
|
||||
auto compile_start = clock::now();
|
||||
auto compile_result = compiler.Compile(program);
|
||||
auto compile_end = clock::now();
|
||||
|
||||
diagnostics.EmitAll(manager);
|
||||
|
||||
if (!compile_result)
|
||||
@@ -86,15 +134,66 @@ namespace Fig::Entry
|
||||
|
||||
CompiledModule *compiledModule = *compile_result;
|
||||
|
||||
if (conf.dump)
|
||||
{
|
||||
Disassembler disassembler;
|
||||
disassembler.DisassembleModule(compiledModule);
|
||||
}
|
||||
|
||||
VM vm;
|
||||
|
||||
auto execute_start = clock::now();
|
||||
auto execute_result = vm.Execute(compiledModule);
|
||||
auto execute_end = clock::now();
|
||||
|
||||
if (!execute_result)
|
||||
{
|
||||
ReportError(execute_result.error(), manager);
|
||||
std::exit(1);
|
||||
}
|
||||
|
||||
if (conf.pregs)
|
||||
{
|
||||
vm.PrintRegisters();
|
||||
}
|
||||
|
||||
if (conf.time)
|
||||
{
|
||||
auto parse_time = parse_end - parse_start;
|
||||
CoreIO::GetStdOut() << "Parse: ";
|
||||
format_print_time(parse_time);
|
||||
CoreIO::GetStdOut() << " | ";
|
||||
|
||||
auto analyze_time = analyze_end - analyze_start;
|
||||
CoreIO::GetStdOut() << "Analyze: ";
|
||||
format_print_time(analyze_time);
|
||||
CoreIO::GetStdOut() << " | ";
|
||||
|
||||
auto compile_time = compile_end - compile_start;
|
||||
CoreIO::GetStdOut() << "Compile: ";
|
||||
format_print_time(compile_time);
|
||||
CoreIO::GetStdOut() << " | ";
|
||||
|
||||
auto execute_time = execute_end - execute_start;
|
||||
CoreIO::GetStdOut() << "Execute: ";
|
||||
format_print_time(execute_time);
|
||||
CoreIO::GetStdOut() << " | ";
|
||||
|
||||
auto total = parse_time + analyze_time + compile_time + execute_time;
|
||||
CoreIO::GetStdOut() << "Total: ";
|
||||
format_print_time(total);
|
||||
CoreIO::GetStdOut() << '\n';
|
||||
}
|
||||
|
||||
delete compiledModule;
|
||||
}
|
||||
|
||||
std::uint32_t RunRepl()
|
||||
{
|
||||
Repl repl(CoreIO::GetStdCin(), CoreIO::GetStdOut(), CoreIO::GetStdErr());
|
||||
std::uint32_t result = repl.Start();
|
||||
|
||||
CoreIO::GetStdOut() << "Repl exited with code " << result << '\n';
|
||||
return result;
|
||||
}
|
||||
}; // namespace Fig::Entry
|
||||
@@ -9,5 +9,18 @@
|
||||
|
||||
namespace Fig::Entry
|
||||
{
|
||||
void RunFromPath(const String &);
|
||||
struct Config
|
||||
{
|
||||
enum Mode
|
||||
{
|
||||
Debug,
|
||||
Normal
|
||||
} mode;
|
||||
bool dump;
|
||||
bool pregs;
|
||||
bool time;
|
||||
};
|
||||
|
||||
void RunFromPath(const String &, const Config &conf);
|
||||
std::uint32_t RunRepl();
|
||||
};
|
||||
@@ -209,8 +209,9 @@ namespace Fig
|
||||
"none",
|
||||
*currentFrame->proto->locations[ipIdx]));
|
||||
}
|
||||
else
|
||||
else [[likely]]
|
||||
{
|
||||
|
||||
Object *obj = callee.AsObject();
|
||||
if (!obj->isFunction())
|
||||
{
|
||||
|
||||
@@ -329,7 +329,7 @@ namespace Fig
|
||||
Op::iAsBx(OpCode::Exit_MaxRecursionDepthExceeded, 0, 0);
|
||||
return &POISON_MAX_RECURSION_DEPTH_EXCEED_INST;
|
||||
}
|
||||
|
||||
[[likely]]
|
||||
*currentFrame = CallFrame{nullptr, proto, proto->code.data(), base};
|
||||
return currentFrame->ip;
|
||||
}
|
||||
@@ -343,7 +343,7 @@ namespace Fig
|
||||
Op::iAsBx(OpCode::Exit_MaxRecursionDepthExceeded, 0, 0);
|
||||
return &POISON_MAX_RECURSION_DEPTH_EXCEED_INST;
|
||||
}
|
||||
|
||||
[[likely]]
|
||||
*currentFrame = CallFrame{closure, closure->proto, closure->proto->code.data(), base};
|
||||
return currentFrame->ip;
|
||||
}
|
||||
|
||||
23
src/main.cpp
23
src/main.cpp
@@ -19,9 +19,13 @@ int main(int argc, char **argv)
|
||||
|
||||
ArgParser::ArgumentParser argparser("Fig", "Fig Toolchain");
|
||||
|
||||
argparser.AddFlag('r', "repl");
|
||||
argparser.AddFlag('h', "help").Help("Print the help message");
|
||||
argparser.AddFlag('v', "version").Help("Show toolchain version");
|
||||
argparser.AddFlag("license").Help("Print the license text");
|
||||
argparser.AddFlag("dump").Help("Dump the bytecode");
|
||||
argparser.AddFlag("pregs").Help("Print vm non-null registers");
|
||||
argparser.AddFlag("time").Help("Print the execution time");
|
||||
|
||||
auto res = argparser.Parse(argc, argv);
|
||||
if (!res)
|
||||
@@ -32,9 +36,13 @@ int main(int argc, char **argv)
|
||||
|
||||
auto &args = *res;
|
||||
|
||||
bool runRepl = args.HasFlag("repl");
|
||||
bool showHelp = args.HasFlag("help");
|
||||
bool showVersion = args.HasFlag("version");
|
||||
bool showLicense = args.HasFlag("license");
|
||||
bool dump = args.HasFlag("dump");
|
||||
bool pregs = args.HasFlag("pregs");
|
||||
bool time = args.HasFlag("time");
|
||||
|
||||
if (showHelp)
|
||||
{
|
||||
@@ -44,10 +52,12 @@ int main(int argc, char **argv)
|
||||
|
||||
if (showVersion)
|
||||
{
|
||||
out << std::format("Fig {}, copyright (c) 2025-2026 PuqiAR, under the {} License\n",
|
||||
out << std::format(
|
||||
"Fig {}, copyright (c) 2025-2026 PuqiAR, under the {} License\n",
|
||||
Core::VERSION,
|
||||
Core::LICENSE);
|
||||
out << std::format("Build time: {} [{} x{} on {}]\n",
|
||||
out << std::format(
|
||||
"Build time: {} [{} x{} on {}]\n",
|
||||
Core::COMPILE_TIME,
|
||||
Core::COMPILER,
|
||||
Core::ARCH,
|
||||
@@ -66,6 +76,11 @@ int main(int argc, char **argv)
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (runRepl)
|
||||
{
|
||||
return Entry::RunRepl();
|
||||
}
|
||||
|
||||
if (posSize > 1)
|
||||
{
|
||||
err << "Error: Too more positionals, expect 1. Use Fig [Fig source code file (.fig)]\n";
|
||||
@@ -77,8 +92,10 @@ int main(int argc, char **argv)
|
||||
return 1;
|
||||
}
|
||||
|
||||
Entry::Config config{.mode = Entry::Config::Normal, .dump = dump, .pregs = pregs, .time = time};
|
||||
|
||||
const String &path = positionals.front();
|
||||
Entry::RunFromPath(path);
|
||||
Entry::RunFromPath(path, config);
|
||||
|
||||
return 0;
|
||||
}
|
||||
232
tests/Parser/parser_comprehensive_test.fig
Normal file
232
tests/Parser/parser_comprehensive_test.fig
Normal file
@@ -0,0 +1,232 @@
|
||||
/* ==============================
|
||||
Fig Parser Comprehensive Test
|
||||
============================== */
|
||||
|
||||
// --- 单行注释 ---
|
||||
|
||||
/* ---
|
||||
多行注释
|
||||
--- */
|
||||
|
||||
// ─────────────────────────────────
|
||||
// 1. 变量声明 var
|
||||
// ─────────────────────────────────
|
||||
var x; // Any, 无初始化
|
||||
var a = 10; // 推断 Int
|
||||
var b: Int = 20; // 显式类型
|
||||
var c := 30; // 类型推断锁定
|
||||
var d: Int; // 显式类型,无初始化
|
||||
var e = "hello"; // 推断 String
|
||||
var f = true; // 推断 Bool
|
||||
var g = null; // 推断 Null
|
||||
|
||||
// ─────────────────────────────────
|
||||
// 2. 常量声明 const
|
||||
// ─────────────────────────────────
|
||||
const PI = 3.14159; // 推断 Double
|
||||
const MAX: Int = 100; // 显式类型
|
||||
const NAME := "Fig"; // 类型推断锁定
|
||||
const T: Double = 2.718; // 显式类型
|
||||
|
||||
// ─────────────────────────────────
|
||||
// 3. 函数定义 func
|
||||
// ─────────────────────────────────
|
||||
func noop() {}
|
||||
func add(a: Int, b: Int) -> Int { return a + b; }
|
||||
func greet(name: String) -> String { return "Hello " + name + "!"; }
|
||||
func quick(x) => x * 2; // 箭头简写
|
||||
func noReturn() -> Null { return null; }
|
||||
|
||||
// ─────────────────────────────────
|
||||
// 4. Lambda 表达式
|
||||
// ─────────────────────────────────
|
||||
var lambda1 = func(x, y) => x + y;
|
||||
var lambda2 = func(x: Int) { return x * x; };
|
||||
|
||||
// ─────────────────────────────────
|
||||
// 5. 控制流 if / while / for
|
||||
// ─────────────────────────────────
|
||||
func testControlFlow() {
|
||||
// if-else
|
||||
if true { var z = 1; }
|
||||
if (x > 0) { var z = 2; }
|
||||
if a > 10 { return 1; }
|
||||
else if a > 5 { return 2; }
|
||||
else { return 3; }
|
||||
|
||||
// while
|
||||
while a < 10 { a = a + 1; }
|
||||
while (a > 0) { a = a - 1; }
|
||||
|
||||
// for (C风格,括号可选)
|
||||
for var i = 0; i < 10; i = i + 1 { var _ = i; }
|
||||
for (var j = 0; j < 5; j += 1) { var _ = j; }
|
||||
for ; ; { break; } // 无限循环
|
||||
|
||||
// break / continue
|
||||
while true { break; }
|
||||
while true { continue; }
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ─────────────────────────────────
|
||||
// 6. 表达式运算符
|
||||
// ─────────────────────────────────
|
||||
func testOperators() -> Int {
|
||||
var r = 0;
|
||||
|
||||
// 算术
|
||||
r = 1 + 2;
|
||||
r = 3 - 1;
|
||||
r = 2 * 3;
|
||||
r = 6 / 2;
|
||||
r = 7 % 3;
|
||||
r = 2 ** 3; // 幂运算
|
||||
|
||||
// 比较
|
||||
var b1 = 1 == 1;
|
||||
var b2 = 1 != 2;
|
||||
var b3 = 1 < 2;
|
||||
var b4 = 2 > 1;
|
||||
var b5 = 1 <= 1;
|
||||
var b6 = 2 >= 1;
|
||||
|
||||
// 逻辑
|
||||
var l1 = true and false;
|
||||
var l2 = true or false;
|
||||
var l3 = not true;
|
||||
var l4 = true && false; // &&
|
||||
var l5 = true || false; // ||
|
||||
|
||||
// 复合赋值
|
||||
r += 1;
|
||||
r -= 1;
|
||||
r *= 2;
|
||||
r /= 2;
|
||||
r %= 1;
|
||||
r ^= 1;
|
||||
|
||||
// 位运算
|
||||
var w1 = 1 & 2;
|
||||
var w2 = 1 | 2;
|
||||
var w3 = 1 ^ 2;
|
||||
var w4 = ~1;
|
||||
var w5 = 1 << 2;
|
||||
var w6 = 4 >> 1;
|
||||
|
||||
// 前后缀 ++/--
|
||||
var p = 0;
|
||||
var pre = ++p;
|
||||
var post = p--;
|
||||
var n = --p;
|
||||
|
||||
// 前缀取反
|
||||
var neg = -100;
|
||||
var notExpr = not true;
|
||||
|
||||
// 三元
|
||||
var t = a > 5 ? 1 : 0;
|
||||
|
||||
// 成员访问
|
||||
var m = someObj.field;
|
||||
|
||||
// 类型检查 / 转换
|
||||
var check = x is Int;
|
||||
var cast = x as String;
|
||||
|
||||
// 索引
|
||||
var idx = arr[0];
|
||||
|
||||
// 函数调用
|
||||
var callRes = add(1, 2);
|
||||
|
||||
// 分组
|
||||
var grouped = (1 + 2) * 3;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
// ─────────────────────────────────
|
||||
// 7. struct 结构体
|
||||
// ─────────────────────────────────
|
||||
struct Point {
|
||||
public x: Int;
|
||||
public y: Int;
|
||||
|
||||
public func toString() -> String {
|
||||
return "(" + "x" + "," + "y" + ")";
|
||||
}
|
||||
}
|
||||
|
||||
struct Person {
|
||||
name: String;
|
||||
age: Int = 18; // 默认值
|
||||
|
||||
public func getName() -> String {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
// ─────────────────────────────────
|
||||
// 8. new 表达式
|
||||
// ─────────────────────────────────
|
||||
func testNewExpr() {
|
||||
var p1 = new Point{1, 2}; // 位置参数
|
||||
var p2 = new Point{x: 2, y: 3}; // 命名参数
|
||||
var xx = 114;
|
||||
var yy = 514;
|
||||
var p3 = new Point{yy, xx}; // 简写
|
||||
var p4 = new Point{}; // 空构造
|
||||
}
|
||||
|
||||
// ─────────────────────────────────
|
||||
// 9. interface 接口
|
||||
// ─────────────────────────────────
|
||||
interface Drawable {
|
||||
func draw() -> String;
|
||||
func getLayer() -> Int;
|
||||
}
|
||||
|
||||
interface Printable {
|
||||
func toString() -> String;
|
||||
func getVersion() -> Int {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
// ─────────────────────────────────
|
||||
// 10. impl 实现
|
||||
// ─────────────────────────────────
|
||||
struct Circle {
|
||||
public radius: Double;
|
||||
}
|
||||
|
||||
impl Drawable for Circle {
|
||||
func draw() {
|
||||
return "circle";
|
||||
}
|
||||
func getLayer() {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// ─────────────────────────────────
|
||||
// 11. import 导入
|
||||
// ─────────────────────────────────
|
||||
import std.io;
|
||||
import "path/to/file.fig";
|
||||
|
||||
// ─────────────────────────────────
|
||||
// 12. 闭包与嵌套函数
|
||||
// ─────────────────────────────────
|
||||
func outer(x: Int) -> Function {
|
||||
func inner(n: Int) -> Int {
|
||||
return n * x;
|
||||
}
|
||||
return inner;
|
||||
}
|
||||
|
||||
var closureMaker = func(factor: Int) {
|
||||
return func(n) => n * factor;
|
||||
};
|
||||
Reference in New Issue
Block a user