Compare commits
5 Commits
e1d9812f92
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 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,19 @@
|
||||
#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/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/VarDecl.hpp>
|
||||
#include <Ast/Stmt/WhileStmt.hpp>
|
||||
#include <Ast/TypeExpr.hpp>
|
||||
@@ -29,7 +29,8 @@ namespace Fig
|
||||
IndexExpr,
|
||||
CallExpr,
|
||||
MemberExpr, // obj.prop
|
||||
ObjectInitExpr, // new Point{}
|
||||
NewExpr, // new Point{}
|
||||
LambdaExpr,
|
||||
|
||||
/* Statements */
|
||||
ExprStmt,
|
||||
@@ -48,7 +49,8 @@ namespace Fig
|
||||
/* Type Expressions */
|
||||
TypeExpr,
|
||||
NamedTypeExpr,
|
||||
NullableTypeExpr
|
||||
NullableTypeExpr,
|
||||
FnTypeExpr,
|
||||
};
|
||||
|
||||
struct AstNode
|
||||
|
||||
@@ -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;
|
||||
TypeExpr *returnType;
|
||||
AstNode *body; // expr/blockstmt
|
||||
bool isExprBody;
|
||||
|
||||
DynArray<UpvalueInfo> upvalues;
|
||||
|
||||
LambdaExpr()
|
||||
{
|
||||
type = AstType::LambdaExpr;
|
||||
}
|
||||
|
||||
LambdaExpr(
|
||||
DynArray<Param *> _params,
|
||||
TypeExpr *_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,7 +11,7 @@
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
struct ObjectInitExpr final : public Expr
|
||||
struct NewExpr final : public Expr
|
||||
{
|
||||
struct Arg
|
||||
{
|
||||
@@ -21,20 +21,20 @@ namespace Fig
|
||||
TypeExpr *typeExpr;
|
||||
DynArray<Arg> args;
|
||||
|
||||
ObjectInitExpr()
|
||||
NewExpr()
|
||||
{
|
||||
type = AstType::ObjectInitExpr;
|
||||
type = AstType::NewExpr;
|
||||
}
|
||||
ObjectInitExpr(TypeExpr *_te, DynArray<Arg> _args, SourceLocation _loc) :
|
||||
NewExpr(TypeExpr *_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())
|
||||
@@ -13,9 +13,12 @@ namespace Fig
|
||||
{
|
||||
struct Field
|
||||
{
|
||||
bool isPublic;
|
||||
bool typeInfer;
|
||||
|
||||
String name;
|
||||
TypeExpr *type;
|
||||
bool isPublic;
|
||||
Expr *initExpr;
|
||||
};
|
||||
bool isPublic;
|
||||
String name;
|
||||
|
||||
@@ -64,4 +64,37 @@ namespace Fig
|
||||
return std::format("<NullableTypeExpr '{}?'>", inner->toString());
|
||||
}
|
||||
};
|
||||
|
||||
struct FnTypeExpr final : public TypeExpr
|
||||
{
|
||||
// func (paratypes...) -> return_type
|
||||
|
||||
DynArray<TypeExpr *> paraTypes;
|
||||
TypeExpr *returnType;
|
||||
|
||||
FnTypeExpr(DynArray<TypeExpr *> _paraTypes, TypeExpr *_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 表达式编译器实现:水位线控制Register与零拷贝复用机制
|
||||
*/
|
||||
|
||||
#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)
|
||||
{
|
||||
@@ -129,7 +174,9 @@ namespace Fig
|
||||
// 仅在被强制指定目标(如参数装填)时发射搬运指令
|
||||
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,10 +244,12 @@ 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);
|
||||
}
|
||||
|
||||
@@ -240,18 +293,25 @@ 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;
|
||||
@@ -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(
|
||||
|
||||
@@ -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,186 @@ 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());
|
||||
}
|
||||
TypeExpr *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}
|
||||
*/
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
if (currentToken().isIdentifier() && peekToken().type == TokenType::Assign)
|
||||
{
|
||||
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
|
||||
});
|
||||
}
|
||||
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;
|
||||
|
||||
TypeExpr *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)
|
||||
@@ -186,17 +370,39 @@ 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())));
|
||||
@@ -253,10 +459,11 @@ namespace Fig
|
||||
}
|
||||
else
|
||||
{
|
||||
return std::unexpected(Error(ErrorType::ExpectedExpression,
|
||||
"expression unexpectedly ended",
|
||||
"insert expressions",
|
||||
makeSourceLocation(token)));
|
||||
// return std::unexpected(Error(ErrorType::ExpectedExpression,
|
||||
// "expression unexpectedly ended",
|
||||
// "insert expressions",
|
||||
// makeSourceLocation(token)));
|
||||
return lhs;
|
||||
}
|
||||
}
|
||||
return lhs;
|
||||
|
||||
@@ -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,8 @@ namespace Fig
|
||||
String fileName;
|
||||
bool isEOF = false;
|
||||
|
||||
Diagnostics &diagnostics;
|
||||
|
||||
// 惰性获取下一个 Token,跳过注释
|
||||
Token nextToken()
|
||||
{
|
||||
@@ -116,12 +119,16 @@ namespace Fig
|
||||
enum StateType : std::uint8_t
|
||||
{
|
||||
Standby,
|
||||
|
||||
ParsingLiteralExpr,
|
||||
ParsingIdentiExpr,
|
||||
ParsingInfixExpr,
|
||||
ParsingPrefixExpr,
|
||||
ParsingIndexExpr,
|
||||
ParsingCallExpr,
|
||||
ParsingLambdaExpr,
|
||||
ParsingNewExpr,
|
||||
|
||||
ParsingVarDecl,
|
||||
ParsingIf,
|
||||
ParsingWhile,
|
||||
@@ -129,7 +136,11 @@ namespace Fig
|
||||
ParsingReturn,
|
||||
ParsingBreak,
|
||||
ParsingContinue,
|
||||
ParsingStructDef,
|
||||
|
||||
ParsingTypeParameters,
|
||||
ParsingNamedTypeExpr,
|
||||
ParsingFnTypeExpr,
|
||||
} type = StateType::Standby;
|
||||
std::unordered_set<TokenType> stopAt = {};
|
||||
};
|
||||
@@ -137,7 +148,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,
|
||||
@@ -200,34 +212,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);
|
||||
}
|
||||
|
||||
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<TypeExpr *, Error> parseTypeExpr();
|
||||
Result<TypeExpr *, Error> parseNamedTypeExpr();
|
||||
Result<TypeExpr *, Error> parseFnTypeExpr();
|
||||
|
||||
Result<Expr *, Error> parseExpression(BindingPower = 0);
|
||||
Result<Expr *, Error> parseLiteralExpr();
|
||||
@@ -237,6 +273,7 @@ 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);
|
||||
@@ -253,8 +290,8 @@ namespace Fig
|
||||
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());
|
||||
}
|
||||
|
||||
@@ -18,13 +18,19 @@ int main()
|
||||
}
|
||||
|
||||
Lexer lexer(source, fileName);
|
||||
Parser parser(lexer, srcManager, 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)
|
||||
{
|
||||
|
||||
@@ -17,7 +17,8 @@ namespace Fig
|
||||
{
|
||||
if (isEOF)
|
||||
{
|
||||
return std::unexpected(Error(ErrorType::SyntaxError,
|
||||
return std::unexpected(Error(
|
||||
ErrorType::SyntaxError,
|
||||
"unclosed braces in block stmt",
|
||||
"insert '}'",
|
||||
location));
|
||||
@@ -75,7 +76,8 @@ namespace Fig
|
||||
{
|
||||
if (typeSpeicifer)
|
||||
{
|
||||
return std::unexpected(Error(ErrorType::SyntaxError,
|
||||
return std::unexpected(Error(
|
||||
ErrorType::SyntaxError,
|
||||
"used type infer but specifying the type",
|
||||
"change `:=` to '='",
|
||||
makeSourceLocation(prevToken())));
|
||||
@@ -115,7 +117,8 @@ namespace Fig
|
||||
}
|
||||
if (!match(TokenType::RightParen))
|
||||
{
|
||||
return std::unexpected(Error(ErrorType::SyntaxError,
|
||||
return std::unexpected(Error(
|
||||
ErrorType::SyntaxError,
|
||||
"unclosed parenthese in if condition",
|
||||
"insert `)`",
|
||||
makeSourceLocation(lpToken)));
|
||||
@@ -155,7 +158,8 @@ namespace Fig
|
||||
{
|
||||
if (alternate)
|
||||
{
|
||||
return std::unexpected(Error(ErrorType::SyntaxError,
|
||||
return std::unexpected(Error(
|
||||
ErrorType::SyntaxError,
|
||||
"else if after else",
|
||||
"remove else if",
|
||||
elseLocation));
|
||||
@@ -175,7 +179,8 @@ namespace Fig
|
||||
}
|
||||
if (!match(TokenType::RightParen))
|
||||
{
|
||||
return std::unexpected(Error(ErrorType::SyntaxError,
|
||||
return std::unexpected(Error(
|
||||
ErrorType::SyntaxError,
|
||||
"unclosed parenthese in if condition",
|
||||
"insert `)`",
|
||||
makeSourceLocation(lpToken)));
|
||||
@@ -210,7 +215,8 @@ namespace Fig
|
||||
{
|
||||
if (alternate)
|
||||
{
|
||||
return std::unexpected(Error(ErrorType::SyntaxError,
|
||||
return std::unexpected(Error(
|
||||
ErrorType::SyntaxError,
|
||||
"duplicate else in if stmt",
|
||||
"remove it",
|
||||
elseLocation));
|
||||
@@ -252,7 +258,8 @@ namespace Fig
|
||||
|
||||
if (!match(TokenType::RightParen))
|
||||
{
|
||||
return std::unexpected(Error(ErrorType::SyntaxError,
|
||||
return std::unexpected(Error(
|
||||
ErrorType::SyntaxError,
|
||||
"unclosed parenthese in while condition",
|
||||
"insert ')'",
|
||||
makeSourceLocation(lpToken)));
|
||||
@@ -289,8 +296,6 @@ namespace Fig
|
||||
|
||||
Result<DynArray<Param *>, Error> Parser::parseFnParams()
|
||||
{
|
||||
StateProtector p(this, {State::ParsingFnDefStmt});
|
||||
|
||||
const Token &lpToken = consumeToken();
|
||||
DynArray<Param *> params;
|
||||
|
||||
@@ -298,7 +303,8 @@ namespace Fig
|
||||
{
|
||||
if (isEOF)
|
||||
{
|
||||
return std::unexpected(Error(ErrorType::SyntaxError,
|
||||
return std::unexpected(Error(
|
||||
ErrorType::SyntaxError,
|
||||
"unclosed parenthese in function parameters",
|
||||
"insert ')'",
|
||||
makeSourceLocation(lpToken)));
|
||||
@@ -353,6 +359,7 @@ namespace Fig
|
||||
|
||||
Result<FnDefStmt *, Error> Parser::parseFnDefStmt(bool isPublic)
|
||||
{
|
||||
StateProtector p(this, {State::ParsingFnDefStmt});
|
||||
SourceLocation location = makeSourceLocation(consumeToken());
|
||||
|
||||
if (!currentToken().isIdentifier())
|
||||
@@ -389,19 +396,49 @@ namespace Fig
|
||||
returnType = *result;
|
||||
}
|
||||
|
||||
if (currentToken().type != TokenType::LeftBrace)
|
||||
BlockStmt *body = nullptr;
|
||||
if (match(TokenType::DoubleArrow)) // =>
|
||||
{
|
||||
auto result = parseExpression();
|
||||
if (!result)
|
||||
{
|
||||
return std::unexpected(result.error());
|
||||
}
|
||||
|
||||
// if (!match(TokenType::Semicolon)) // ;
|
||||
// {
|
||||
// return std::unexpected(makeExpectSemicolonError(prevToken()));
|
||||
// }
|
||||
|
||||
if (match(TokenType::Semicolon))
|
||||
{
|
||||
diagnostics.Report(Error(
|
||||
ErrorType::UnnecessarySemicolon,
|
||||
"`;` is unnecessary in this context",
|
||||
"try remove `;`",
|
||||
makeSourceLocation(prevToken())));
|
||||
}
|
||||
|
||||
Expr *expr = *result;
|
||||
ReturnStmt *returnStmt = arena.Allocate<ReturnStmt>(expr, expr->location);
|
||||
|
||||
body = arena.Allocate<BlockStmt>();
|
||||
body->nodes.push_back(returnStmt);
|
||||
}
|
||||
else if (currentToken().type == TokenType::LeftBrace)
|
||||
{
|
||||
auto bodyResult = parseBlockStmt();
|
||||
if (!bodyResult)
|
||||
{
|
||||
return std::unexpected(bodyResult.error());
|
||||
}
|
||||
body = *bodyResult;
|
||||
}
|
||||
else
|
||||
{
|
||||
return std::unexpected(
|
||||
makeUnexpectTokenError("fn def stmt", "function body '{'", currentToken()));
|
||||
makeUnexpectTokenError("fn def stmt", "function body '=>' / '{'", currentToken()));
|
||||
}
|
||||
BlockStmt *body = nullptr;
|
||||
auto bodyResult = parseBlockStmt();
|
||||
|
||||
if (!bodyResult)
|
||||
{
|
||||
return std::unexpected(bodyResult.error());
|
||||
}
|
||||
body = *bodyResult;
|
||||
|
||||
FnDefStmt *fnDef =
|
||||
arena.Allocate<FnDefStmt>(isPublic, name, params, returnType, body, location);
|
||||
@@ -429,6 +466,127 @@ namespace Fig
|
||||
return returnStmt;
|
||||
}
|
||||
|
||||
Result<Stmt *, Error> Parser::parseStructDef(bool isPublic)
|
||||
{
|
||||
StateProtector p(this, {State::ParsingStructDef});
|
||||
|
||||
SourceLocation location = makeSourceLocation(consumeToken()); // consume `struct`
|
||||
if (!currentToken().isIdentifier())
|
||||
{
|
||||
return std::unexpected(
|
||||
makeUnexpectTokenError("StructDef", "struct name", currentToken()));
|
||||
}
|
||||
|
||||
const Token &name_tok = consumeToken(); // consume name
|
||||
const String &name = srcManager.GetSub(name_tok.index, name_tok.length);
|
||||
|
||||
StructDefStmt *stDef = arena.Allocate<StructDefStmt>();
|
||||
|
||||
if (currentToken().type == TokenType::Less) // <
|
||||
{
|
||||
auto result = parseTypeParameters();
|
||||
if (!result)
|
||||
{
|
||||
return std::unexpected(result.error());
|
||||
}
|
||||
|
||||
stDef->typeParameters = *result;
|
||||
}
|
||||
|
||||
if (!match(TokenType::LeftBrace))
|
||||
{
|
||||
return std::unexpected(
|
||||
makeUnexpectTokenError("StructDef", "lbrace '{'", currentToken()));
|
||||
}
|
||||
|
||||
const Token &lb_tok = prevToken(); // `{`
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (isEOF)
|
||||
{
|
||||
return std::unexpected(Error(
|
||||
ErrorType::SyntaxError,
|
||||
"unclosed braces in struct def",
|
||||
"insert '}'",
|
||||
makeSourceLocation(lb_tok)));
|
||||
}
|
||||
if (match(TokenType::RightBrace))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// (public) field_name (: Type) (= expr) / (:= expr)
|
||||
|
||||
bool isPublic = match(TokenType::Public);
|
||||
if (currentToken().isIdentifier())
|
||||
{
|
||||
const Token &name_tok = consumeToken();
|
||||
const String &field_name = srcManager.GetSub(name_tok.index, name_tok.length);
|
||||
|
||||
if (match(TokenType::Walrus)) // :=
|
||||
{
|
||||
auto result = parseExpression();
|
||||
if (!result)
|
||||
{
|
||||
return std::unexpected(result.error());
|
||||
}
|
||||
|
||||
stDef->fields.push_back(
|
||||
StructDefStmt::Field{isPublic, true, field_name, nullptr, *result});
|
||||
}
|
||||
else
|
||||
{
|
||||
TypeExpr *type = nullptr;
|
||||
Expr *initExpr = nullptr;
|
||||
|
||||
if (match(TokenType::Colon)) // :
|
||||
{
|
||||
auto result = parseTypeExpr();
|
||||
if (!result)
|
||||
{
|
||||
return std::unexpected(result.error());
|
||||
}
|
||||
type = *result;
|
||||
}
|
||||
|
||||
if (match(TokenType::Assign))
|
||||
{
|
||||
auto result = parseExpression();
|
||||
if (!result)
|
||||
{
|
||||
return std::unexpected(result.error());
|
||||
}
|
||||
initExpr = *result;
|
||||
}
|
||||
stDef->fields.push_back(
|
||||
StructDefStmt::Field{isPublic, false, field_name, type, initExpr});
|
||||
}
|
||||
if (!match(TokenType::Semicolon))
|
||||
{
|
||||
return std::unexpected(makeExpectSemicolonError());
|
||||
}
|
||||
}
|
||||
else if (currentToken().type == TokenType::Function)
|
||||
{
|
||||
auto result = parseFnDefStmt(isPublic);
|
||||
if (!result)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
stDef->methods.push_back(*result);
|
||||
}
|
||||
else
|
||||
{
|
||||
return std::unexpected(
|
||||
makeUnexpectTokenError("StructDef", "field or method", currentToken()));
|
||||
}
|
||||
}
|
||||
|
||||
return stDef;
|
||||
}
|
||||
|
||||
Result<Stmt *, Error> Parser::parseStatement()
|
||||
{
|
||||
StateProtector p(this, {State::Standby});
|
||||
@@ -446,6 +604,11 @@ namespace Fig
|
||||
return parseFnDefStmt(true);
|
||||
}
|
||||
|
||||
if (currentToken().type == TokenType::Struct)
|
||||
{
|
||||
return parseStructDef(true);
|
||||
}
|
||||
|
||||
return std::unexpected(
|
||||
makeUnexpectTokenError("public", "var/const/func/struct", currentToken()));
|
||||
}
|
||||
@@ -470,11 +633,16 @@ namespace Fig
|
||||
return parseWhileStmt();
|
||||
}
|
||||
|
||||
if (currentToken().type == TokenType::Function)
|
||||
if (currentToken().type == TokenType::Function && peekToken().isIdentifier())
|
||||
{
|
||||
return parseFnDefStmt(false);
|
||||
}
|
||||
|
||||
if (currentToken().type == TokenType::Struct)
|
||||
{
|
||||
return parseStructDef(false);
|
||||
}
|
||||
|
||||
if (currentToken().type == TokenType::Return)
|
||||
{
|
||||
return parseReturnStmt();
|
||||
@@ -507,6 +675,15 @@ namespace Fig
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (currentToken().type == TokenType::Semicolon)
|
||||
{
|
||||
return std::unexpected(Error(
|
||||
ErrorType::SyntaxError,
|
||||
"null statement is not allowed here",
|
||||
"remove `;`",
|
||||
makeSourceLocation(currentToken())));
|
||||
}
|
||||
|
||||
const auto &expr_result = parseExpression();
|
||||
if (!expr_result)
|
||||
{
|
||||
|
||||
@@ -9,6 +9,46 @@
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
Result<decltype(StructDefStmt::typeParameters), Error> Parser::parseTypeParameters()
|
||||
{
|
||||
StateProtector p(this, {State::ParsingTypeParameters});
|
||||
decltype(StructDefStmt::typeParameters) tp;
|
||||
|
||||
const Token &lab = consumeToken(); // consume `<`
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (isEOF)
|
||||
{
|
||||
return std::unexpected(Error(
|
||||
ErrorType::SyntaxError,
|
||||
"unclosed `<` in type parameters",
|
||||
"insert '>'",
|
||||
makeSourceLocation(lab)));
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
// 解析基础命名类型与泛型: List<Int>
|
||||
Result<TypeExpr *, Error> Parser::parseNamedTypeExpr()
|
||||
{
|
||||
@@ -25,9 +65,11 @@ namespace Fig
|
||||
if (match(TokenType::Dot))
|
||||
{
|
||||
if (!currentToken().isIdentifier())
|
||||
return std::unexpected(makeUnexpectTokenError("Type", "identifier", currentToken()));
|
||||
return std::unexpected(
|
||||
makeUnexpectTokenError("Type", "identifier", currentToken()));
|
||||
}
|
||||
else break;
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
DynArray<TypeExpr *> arguments;
|
||||
@@ -36,38 +78,109 @@ namespace Fig
|
||||
while (true)
|
||||
{
|
||||
auto result = parseTypeExpr();
|
||||
if (!result) return std::unexpected(result.error());
|
||||
if (!result)
|
||||
return std::unexpected(result.error());
|
||||
arguments.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::parseFnTypeExpr()
|
||||
{
|
||||
StateProtector p(this, {State::ParsingFnTypeExpr});
|
||||
SourceLocation location = makeSourceLocation(consumeToken()); // consume `func`
|
||||
if (!match(TokenType::LeftParen)) // `(`
|
||||
{
|
||||
return std::unexpected(
|
||||
makeUnexpectTokenError("FnTypeExpr", "lparen (", currentToken()));
|
||||
}
|
||||
|
||||
DynArray<TypeExpr *> 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()));
|
||||
}
|
||||
}
|
||||
|
||||
TypeExpr *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<TypeExpr *, Error> Parser::parseTypeExpr()
|
||||
{
|
||||
TypeExpr *base = nullptr;
|
||||
|
||||
// 目前只支持命名类型 (以后可以加函数类型 (Int)->Int)
|
||||
if (currentToken().isIdentifier())
|
||||
{
|
||||
auto res = parseNamedTypeExpr();
|
||||
if (!res) return std::unexpected(res.error());
|
||||
base = *res;
|
||||
auto result = parseNamedTypeExpr();
|
||||
if (!result)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
base = *result;
|
||||
}
|
||||
else return std::unexpected(makeUnexpectTokenError("TypeExpr", "name", currentToken()));
|
||||
|
||||
// 空安全处理: Int?? 也可以,但 Analyzer 会规范化它
|
||||
while (match(TokenType::Question))
|
||||
else if (currentToken().type == TokenType::Function)
|
||||
{
|
||||
base = arena.Allocate<NullableTypeExpr>(base, makeSourceLocation(prevToken()));
|
||||
auto result = parseFnTypeExpr();
|
||||
if (!result)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
base = *result;
|
||||
}
|
||||
else
|
||||
{
|
||||
return std::unexpected(makeUnexpectTokenError("TypeExpr", "name", currentToken()));
|
||||
}
|
||||
|
||||
// type (?)
|
||||
if (currentToken().type == TokenType::Question) // ?
|
||||
{
|
||||
base = arena.Allocate<NullableTypeExpr>(
|
||||
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)
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user