Compare commits

..

5 Commits

Author SHA1 Message Date
4f87078a87 Plan 2026-06-01 18:52:06 +08:00
9338c21449 *(无用) feat: 在VM.cpp中添加likely属性以优化分支预测
refact: 在xmake.lua中优化构建设置
2026-05-10 21:25:05 +08:00
98de782760 feat: 增加repl入口,-r/--repl。 添加计时选项。 -- 我发现一个问题,analyzer没法保存环境。完了。 2026-04-30 21:24:11 +08:00
fafa2b4946 feat: 在解析器中实现 Lambda 和 new 表达式
- 增加了对 Lambda 表达式的初步解析支持,包括参数处理和返回类型。Lambda闭包尚未支持。
- 引入了用于对象初始化的新的表达式,支持可选的命名参数。
- 改进了表达式语法错误的错误报告。
- 更新了解析器和分析器以处理新的表达式类型并验证其语义。
- 修改了现有测试以涵盖新功能并确保其正确性。
- 改进了各种解析和语义错误的诊断。
2026-04-12 10:07:51 +08:00
570a87c3cd feat: 增加函数类型表达式支持,更新解析器和分析器 2026-03-18 17:30:09 +08:00
33 changed files with 1420 additions and 217 deletions

View File

@@ -6,7 +6,7 @@ Language: Cpp
AccessModifierOffset: -4
# 开括号(开圆括号、开尖括号、开方括号)后的对齐: Align, DontAlign, AlwaysBreak(总是在开括号后换行)
AlignAfterOpenBracket: DontAlign
AlignAfterOpenBracket: AlwaysBreak
# 连续赋值时,对齐所有等号
AlignConsecutiveAssignments: true

View File

@@ -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)

View File

@@ -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">

View File

@@ -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">

View File

@@ -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>

View File

@@ -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

View File

@@ -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

View 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

View File

@@ -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())

View File

@@ -13,9 +13,12 @@ namespace Fig
{
struct Field
{
bool isPublic;
bool typeInfer;
String name;
TypeExpr *type;
bool isPublic;
Expr *initExpr;
};
bool isPublic;
String name;

View File

@@ -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

View File

@@ -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)

View File

@@ -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)

View File

@@ -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(

View File

@@ -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);

View File

@@ -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,
};

View File

@@ -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);
}
};

View File

@@ -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;

View File

@@ -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());
}

View File

@@ -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)
{

View File

@@ -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)
{

View File

@@ -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

View File

@@ -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);

View File

@@ -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

View File

@@ -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)

View File

@@ -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)

View File

@@ -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

View File

@@ -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))
@@ -49,22 +85,32 @@ namespace Fig::Entry
CoreIO::GetStdErr() << "Could not read file: " << path << '\n';
std::exit(1);
}
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

View File

@@ -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();
};

View File

@@ -209,8 +209,9 @@ namespace Fig
"none",
*currentFrame->proto->locations[ipIdx]));
}
else
else [[likely]]
{
Object *obj = callee.AsObject();
if (!obj->isFunction())
{

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -15,6 +15,10 @@ elseif is_plat("windows") then
add_ldflags("-Wl,--stack,268435456")
end
if is_mode("release") then
set_optimize("fastest")
end
set_languages("c++23")
add_includedirs("src")