Made some preparations to refactor semantic analysis and type system
This commit is contained in:
@@ -1,67 +0,0 @@
|
||||
#include <Parser/Parser.hpp>
|
||||
#include <Sema/Analyzer.hpp>
|
||||
#include <Compiler/Compiler.hpp>
|
||||
#include <Bytecode/Disassembler.hpp>
|
||||
#include <iostream>
|
||||
#include <filesystem>
|
||||
|
||||
int main()
|
||||
{
|
||||
using namespace Fig;
|
||||
|
||||
String filePath = "T:/Files/Maker/Code/MyCodingLanguage/The Fig Project/Fig/tests/Compiler/test_basic.fig";
|
||||
|
||||
if (!std::filesystem::exists(filePath.toStdString()))
|
||||
{
|
||||
std::cerr << "CRITICAL: Test file not found at: " << filePath << "\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
SourceManager sm{filePath};
|
||||
String source = sm.Read();
|
||||
|
||||
if (!sm.read || source.length() == 0)
|
||||
{
|
||||
std::cerr << "CRITICAL: SourceManager failed to read: " << filePath << "\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
Lexer lexer(source, filePath);
|
||||
|
||||
Diagnostics diagnostics;
|
||||
Parser parser(lexer, sm, filePath, diagnostics);
|
||||
|
||||
diagnostics.EmitAll(sm);
|
||||
|
||||
auto pRes = parser.Parse();
|
||||
if (!pRes)
|
||||
{
|
||||
ReportError(pRes.error(), sm);
|
||||
return 1;
|
||||
}
|
||||
|
||||
Program *program = *pRes;
|
||||
std::cout << "Successfully parsed nodes: " << program->nodes.size() << "\n";
|
||||
|
||||
Analyzer analyzer(sm);
|
||||
auto aRes = analyzer.Analyze(program);
|
||||
if (!aRes)
|
||||
{
|
||||
ReportError(aRes.error(), sm);
|
||||
return 1;
|
||||
}
|
||||
|
||||
Diagnostics diag;
|
||||
Compiler compiler(sm, diag);
|
||||
auto cRes = compiler.Compile(program);
|
||||
if (!cRes)
|
||||
{
|
||||
ReportError(cRes.error(), sm);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// 使用正式的 Disassembler
|
||||
Disassembler::DisassembleModule(*cRes);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,142 +0,0 @@
|
||||
/*!
|
||||
@file src/Compiler/Compiler.cpp
|
||||
@brief 编译器主逻辑实现:物理 Bootstrapper 与双步扫描
|
||||
*/
|
||||
|
||||
#include <Ast/Stmt/FnDefStmt.hpp>
|
||||
#include <Compiler/Compiler.hpp>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
Result<CompiledModule *, Error> Compiler::Compile(Program *program)
|
||||
{
|
||||
module = new CompiledModule();
|
||||
if (program->nodes.empty())
|
||||
{
|
||||
return module;
|
||||
}
|
||||
|
||||
// 预留 Protos[0] 给 Bootstrapper
|
||||
Proto *bootProto = new Proto();
|
||||
bootProto->name = "[bootstrapper]";
|
||||
module->protos.push_back(bootProto);
|
||||
|
||||
int initIdx = -1;
|
||||
int mainIdx = -1;
|
||||
|
||||
SourceLocation *mainFnLoc = nullptr;
|
||||
SourceLocation *initFnLoc = nullptr;
|
||||
|
||||
// 预扫描顶层函数
|
||||
for (auto *stmt : program->nodes)
|
||||
{
|
||||
if (stmt->type == AstType::FnDefStmt)
|
||||
{
|
||||
auto *f = static_cast<FnDefStmt *>(stmt);
|
||||
int idx = (int) module->protos.size();
|
||||
|
||||
Proto *p = new Proto();
|
||||
p->name = f->name;
|
||||
p->numParams = (uint8_t) f->params.size();
|
||||
p->maxRegisters = p->numParams;
|
||||
f->protoIndex = idx;
|
||||
|
||||
module->protos.push_back(p);
|
||||
|
||||
// 连接物理符号到索引
|
||||
if (f->resolvedSymbol)
|
||||
{
|
||||
f->resolvedSymbol->index = idx;
|
||||
}
|
||||
|
||||
if (f->name == "init")
|
||||
{
|
||||
initIdx = idx;
|
||||
initFnLoc = &stmt->location;
|
||||
}
|
||||
if (f->name == "main")
|
||||
{
|
||||
mainIdx = idx;
|
||||
mainFnLoc = &stmt->location;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Bootstrapper 中编译所有语句
|
||||
FuncState bootState(bootProto, nullptr);
|
||||
current = &bootState;
|
||||
|
||||
for (auto *stmt : program->nodes)
|
||||
{
|
||||
auto res = compileStmt(stmt);
|
||||
if (!res)
|
||||
{
|
||||
return std::unexpected(res.error());
|
||||
}
|
||||
}
|
||||
|
||||
// 发射 Bootstrapper 引导指令
|
||||
if (initIdx != -1)
|
||||
{
|
||||
emit(Op::iABC(OpCode::FastCall, (uint8_t) initIdx, 0, 0), initFnLoc);
|
||||
}
|
||||
|
||||
if (mainIdx != -1)
|
||||
{
|
||||
emit(Op::iABC(OpCode::FastCall, (uint8_t) mainIdx, 0, 0), mainFnLoc);
|
||||
}
|
||||
|
||||
emit(Op::iAsBx(OpCode::Exit, 0, 0), &program->nodes.back()->location);
|
||||
|
||||
return module;
|
||||
}
|
||||
|
||||
int Compiler::getGlobalID(const String &name)
|
||||
{
|
||||
if (globalIDMap.contains(name))
|
||||
return globalIDMap[name];
|
||||
int id = (int) globalIDMap.size();
|
||||
globalIDMap[name] = id;
|
||||
return id;
|
||||
}
|
||||
|
||||
Result<Register, Error> Compiler::allocateReg(const SourceLocation &loc)
|
||||
{
|
||||
if (current->freereg >= MAX_REGISTERS)
|
||||
{
|
||||
return std::unexpected(
|
||||
Error(ErrorType::RegisterOverflow, "too many registers", "", loc));
|
||||
}
|
||||
|
||||
Register reg = current->freereg++;
|
||||
if (reg >= current->proto->maxRegisters)
|
||||
{
|
||||
current->proto->maxRegisters = reg + 1;
|
||||
}
|
||||
return reg;
|
||||
}
|
||||
|
||||
void Compiler::freeReg(Register count)
|
||||
{
|
||||
if (current->freereg >= count)
|
||||
{
|
||||
current->freereg -= count;
|
||||
}
|
||||
}
|
||||
|
||||
int Compiler::addConstant(Value val)
|
||||
{
|
||||
if (current->constantMap.contains(val))
|
||||
return current->constantMap[val];
|
||||
int idx = (int) current->proto->constants.size();
|
||||
current->proto->constants.push_back(val);
|
||||
current->constantMap[val] = idx;
|
||||
return idx;
|
||||
}
|
||||
|
||||
void Compiler::emit(Instruction inst, SourceLocation *loc)
|
||||
{
|
||||
current->proto->code.push_back(inst);
|
||||
current->proto->locations.push_back(loc);
|
||||
}
|
||||
} // namespace Fig
|
||||
@@ -50,6 +50,15 @@ namespace Fig
|
||||
|
||||
public:
|
||||
Compiler(SourceManager &m, Diagnostics &d) : manager(m), diag(d) {}
|
||||
Result<CompiledModule *, Error> Compile(Program *program);
|
||||
Result<CompiledModule *, Error> Compile(Program *)
|
||||
{
|
||||
auto *mod = new CompiledModule();
|
||||
auto *boot = new Proto();
|
||||
boot->name = "[bootstrapper]";
|
||||
boot->maxRegisters = 1;
|
||||
boot->code.push_back(Op::iAsBx(OpCode::Exit, 0, 0));
|
||||
mod->protos.push_back(boot);
|
||||
return mod;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,477 +0,0 @@
|
||||
/*!
|
||||
@file src/Compiler/ExprCompiler.cpp
|
||||
@brief 表达式编译
|
||||
*/
|
||||
|
||||
#include <Ast/Expr/CallExpr.hpp>
|
||||
#include <Ast/Expr/IdentiExpr.hpp>
|
||||
#include <Ast/Expr/InfixExpr.hpp>
|
||||
#include <Ast/Expr/LiteralExpr.hpp>
|
||||
#include <Compiler/Compiler.hpp>
|
||||
#include <charconv>
|
||||
#include <limits>
|
||||
#include <system_error>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
static Result<Value, Error> parsePhysicalNumber(const String &raw, const SourceLocation &loc)
|
||||
{
|
||||
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(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 if (isHexOrBin)
|
||||
{
|
||||
// 16进制或2进制整数
|
||||
int64_t iVal;
|
||||
auto [ptr, ec] = std::from_chars(start, buffer + j, iVal, base);
|
||||
|
||||
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())
|
||||
{
|
||||
return Value::FromInt(static_cast<int32_t>(iVal));
|
||||
}
|
||||
else
|
||||
{
|
||||
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)
|
||||
{
|
||||
if (expr == nullptr)
|
||||
{
|
||||
return std::unexpected(
|
||||
Error(ErrorType::InternalError, "null expr in compiler", "", {}));
|
||||
}
|
||||
|
||||
switch (expr->type)
|
||||
{
|
||||
case AstType::LiteralExpr: {
|
||||
auto *l = static_cast<LiteralExpr *>(expr);
|
||||
Register r = (target == NO_REG) ? *allocateReg(l->location) : target;
|
||||
|
||||
const Token &tok = l->literal;
|
||||
if (tok.type == TokenType::LiteralNumber)
|
||||
{
|
||||
auto vRes =
|
||||
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);
|
||||
}
|
||||
else if (tok.type == TokenType::LiteralString)
|
||||
{
|
||||
int kIdx = addConstant(Value::GetNullInstance()); // TODO: String 支持
|
||||
emit(Op::iABx(OpCode::LoadK, r, static_cast<uint16_t>(kIdx)), &l->location);
|
||||
}
|
||||
else if (tok.type == TokenType::LiteralNull)
|
||||
{
|
||||
emit(Op::iABC(OpCode::LoadNull, r, 0, 0), &l->location);
|
||||
}
|
||||
else if (tok.type == TokenType::LiteralTrue)
|
||||
{
|
||||
emit(Op::iABC(OpCode::LoadTrue, r, 0, 0), &l->location);
|
||||
}
|
||||
else if (tok.type == TokenType::LiteralFalse)
|
||||
{
|
||||
emit(Op::iABC(OpCode::LoadFalse, r, 0, 0), &l->location);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
case AstType::IdentiExpr: {
|
||||
auto *i = static_cast<IdentiExpr *>(expr);
|
||||
Symbol *sym = i->resolvedSymbol;
|
||||
|
||||
if (sym->location == SymbolLocation::Local)
|
||||
{
|
||||
// no-copy for temp eval
|
||||
if (target == NO_REG)
|
||||
return static_cast<Register>(sym->index);
|
||||
|
||||
// 仅在被强制指定目标(如参数装填)时发射搬运指令
|
||||
if (target != sym->index)
|
||||
{
|
||||
emit(
|
||||
Op::iABx(OpCode::Mov, target, static_cast<uint16_t>(sym->index)),
|
||||
&i->location);
|
||||
}
|
||||
return target;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
else if (sym->location == SymbolLocation::Global)
|
||||
{
|
||||
int gId = getGlobalID(i->name);
|
||||
emit(Op::iABx(OpCode::GetGlobal, r, static_cast<uint16_t>(gId)), &i->location);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
case AstType::CallExpr: {
|
||||
auto *c = static_cast<CallExpr *>(expr);
|
||||
Register mark = current->freereg; // 记录调用前的栈顶水位
|
||||
Register baseReg = current->freereg; // 锁定滑窗基址
|
||||
|
||||
// 连续装填参数,占据 baseReg, baseReg+1, baseReg+2...
|
||||
for (auto *arg : c->args.args)
|
||||
{
|
||||
auto allocRes = allocateReg(arg->location);
|
||||
if (!allocRes)
|
||||
{
|
||||
return allocRes;
|
||||
}
|
||||
|
||||
Register argTarget = *allocRes;
|
||||
auto res = compileExpr(arg, argTarget);
|
||||
if (!res)
|
||||
return std::unexpected(res.error());
|
||||
}
|
||||
|
||||
bool isGlobalFastCall = false;
|
||||
if (c->callee->type == AstType::IdentiExpr)
|
||||
{
|
||||
auto *id = static_cast<IdentiExpr *>(c->callee);
|
||||
// 只有在全局区的函数,才能使用 FastCall
|
||||
if (id->resolvedSymbol->location == SymbolLocation::Global)
|
||||
{
|
||||
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())),
|
||||
&c->location);
|
||||
}
|
||||
}
|
||||
|
||||
if (!isGlobalFastCall)
|
||||
{
|
||||
// 动态闭包调用
|
||||
// 先获取闭包对象所在的物理寄存器
|
||||
auto r_fn = compileExpr(c->callee);
|
||||
if (!r_fn)
|
||||
return std::unexpected(r_fn.error());
|
||||
|
||||
// 使用动态 Call 指令,RA 是指向堆闭包的寄存器
|
||||
emit(
|
||||
Op::iABC(
|
||||
OpCode::Call,
|
||||
*r_fn,
|
||||
baseReg,
|
||||
static_cast<uint8_t>(c->args.args.size())),
|
||||
&c->location);
|
||||
}
|
||||
|
||||
// free arg temps
|
||||
current->freereg = mark;
|
||||
|
||||
// 目若 target 未指定,allocateReg 将复用 baseReg,实现零开销回写
|
||||
|
||||
Register r_dest;
|
||||
if (target == NO_REG)
|
||||
{
|
||||
auto res = allocateReg(c->location);
|
||||
if (!res)
|
||||
return std::unexpected(res.error());
|
||||
r_dest = *res;
|
||||
}
|
||||
else
|
||||
{
|
||||
r_dest = target;
|
||||
}
|
||||
|
||||
if (r_dest != baseReg)
|
||||
{
|
||||
emit(Op::iABx(OpCode::Mov, r_dest, baseReg), &c->location);
|
||||
}
|
||||
|
||||
return r_dest;
|
||||
}
|
||||
|
||||
case AstType::InfixExpr: {
|
||||
auto *in = static_cast<InfixExpr *>(expr);
|
||||
if (in->op == BinaryOperator::Assign)
|
||||
{
|
||||
auto r_val = compileExpr(in->right, target);
|
||||
if (!r_val)
|
||||
return std::unexpected(r_val.error());
|
||||
|
||||
if (in->left->type == AstType::IdentiExpr)
|
||||
{
|
||||
auto *lid = static_cast<IdentiExpr *>(in->left);
|
||||
Symbol *sym = lid->resolvedSymbol;
|
||||
if (sym->location == SymbolLocation::Local)
|
||||
{
|
||||
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);
|
||||
}
|
||||
else
|
||||
{
|
||||
emit(
|
||||
Op::iABx(
|
||||
OpCode::SetGlobal,
|
||||
*r_val,
|
||||
static_cast<uint16_t>(getGlobalID(lid->name))),
|
||||
&lid->location);
|
||||
}
|
||||
}
|
||||
return r_val;
|
||||
}
|
||||
|
||||
Register mark = current->freereg; // mark
|
||||
|
||||
auto r_l = compileExpr(in->left);
|
||||
if (!r_l)
|
||||
return std::unexpected(r_l.error());
|
||||
auto r_r = compileExpr(in->right);
|
||||
if (!r_r)
|
||||
return std::unexpected(r_r.error());
|
||||
|
||||
bool isInt = in->left->resolvedType.is(TypeTag::Int)
|
||||
&& in->right->resolvedType.is(TypeTag::Int);
|
||||
OpCode op;
|
||||
switch (in->op)
|
||||
{
|
||||
case BinaryOperator::Add: {
|
||||
op = (isInt ? (OpCode::IntFastAdd) : (OpCode::Add));
|
||||
break;
|
||||
}
|
||||
case BinaryOperator::Subtract: {
|
||||
op = (isInt ? (OpCode::IntFastSub) : (OpCode::Sub));
|
||||
break;
|
||||
}
|
||||
case BinaryOperator::Multiply: {
|
||||
op = (isInt ? (OpCode::IntFastMul) : (OpCode::Mul));
|
||||
break;
|
||||
}
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
// 释放左右操作数产生的临时寄存器
|
||||
current->freereg = mark;
|
||||
|
||||
// 复用已释放的物理槽位存放计算结果
|
||||
Register r_d;
|
||||
if (target == NO_REG)
|
||||
{
|
||||
auto res = allocateReg(in->location);
|
||||
if (!res)
|
||||
return std::unexpected(res.error());
|
||||
r_d = *res;
|
||||
}
|
||||
else
|
||||
{
|
||||
r_d = target;
|
||||
}
|
||||
|
||||
emit(Op::iABC(op, r_d, *r_l, *r_r), &in->location);
|
||||
|
||||
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(
|
||||
Error(ErrorType::InternalError, "unsupported expr", "", expr->location));
|
||||
}
|
||||
} // namespace Fig
|
||||
@@ -1,269 +0,0 @@
|
||||
/*!
|
||||
@file src/Compiler/StmtCompiler.cpp
|
||||
@brief 语句编译
|
||||
*/
|
||||
|
||||
#include <Ast/Stmt/FnDefStmt.hpp>
|
||||
#include <Ast/Stmt/IfStmt.hpp>
|
||||
#include <Ast/Stmt/VarDecl.hpp>
|
||||
#include <Ast/Stmt/WhileStmt.hpp>
|
||||
#include <Compiler/Compiler.hpp>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
Result<void, Error> Compiler::compileStmt(Stmt *stmt)
|
||||
{
|
||||
if (stmt == nullptr)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
switch (stmt->type)
|
||||
{
|
||||
case AstType::BlockStmt: {
|
||||
auto *b = static_cast<BlockStmt *>(stmt);
|
||||
for (auto *n : b->nodes)
|
||||
{
|
||||
auto res = compileStmt(n);
|
||||
if (!res)
|
||||
return res;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case AstType::VarDecl: {
|
||||
auto *v = static_cast<VarDecl *>(stmt);
|
||||
if (current->enclosing == nullptr) // 处理全局变量
|
||||
{
|
||||
Register mark = current->freereg; // mark
|
||||
auto regRes = compileExpr(v->initExpr);
|
||||
if (!regRes)
|
||||
return std::unexpected(regRes.error());
|
||||
|
||||
emit(Op::iABx(OpCode::SetGlobal,
|
||||
*regRes,
|
||||
static_cast<uint16_t>(getGlobalID(v->name))),
|
||||
&v->location);
|
||||
current->freereg = mark; // 释放初始化表达式的临时占用
|
||||
}
|
||||
else
|
||||
{
|
||||
// 抬升水位,锁死局部变量的物理槽位
|
||||
Register targetReg = static_cast<Register>(v->localId);
|
||||
while (current->freereg <= targetReg)
|
||||
{
|
||||
auto allocRes = allocateReg(v->location);
|
||||
if (!allocRes)
|
||||
{
|
||||
return std::unexpected(allocRes.error());
|
||||
}
|
||||
}
|
||||
|
||||
auto regRes = compileExpr(v->initExpr, targetReg);
|
||||
if (!regRes)
|
||||
return std::unexpected(regRes.error());
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case AstType::FnDefStmt: {
|
||||
auto *f = static_cast<FnDefStmt *>(stmt);
|
||||
|
||||
if (f->protoIndex == -1) // 闭包环境没有被扫到
|
||||
{
|
||||
Proto *newProto = new Proto();
|
||||
newProto->name = f->name;
|
||||
newProto->numParams = static_cast<uint8_t>(f->params.size());
|
||||
newProto->maxRegisters = newProto->numParams; // sync
|
||||
|
||||
f->protoIndex = static_cast<int>(module->protos.size());
|
||||
module->protos.push_back(newProto);
|
||||
}
|
||||
|
||||
// 获取静态原型 (flat protos)
|
||||
Proto *p = module->protos[f->protoIndex];
|
||||
|
||||
p->upvalues = f->upvalues;
|
||||
|
||||
FuncState fs(p, current);
|
||||
FuncState *old = current;
|
||||
current = &fs;
|
||||
|
||||
auto res = compileStmt(f->body);
|
||||
if (!res)
|
||||
return res;
|
||||
|
||||
if (p->code.empty() || static_cast<OpCode>(p->code.back() & 0xFF) != OpCode::Return)
|
||||
{
|
||||
emit(Op::iABC(OpCode::Return, 0, 0, 0), &f->location);
|
||||
}
|
||||
|
||||
current = old;
|
||||
|
||||
// 如果是局部闭包,在当前栈帧分配寄存器并生成 LoadFn
|
||||
if (f->resolvedSymbol->location == SymbolLocation::Local)
|
||||
{
|
||||
Register targetReg = static_cast<Register>(f->resolvedSymbol->index);
|
||||
|
||||
while (current->freereg <= targetReg)
|
||||
{
|
||||
auto allocRes = allocateReg(f->location);
|
||||
if (!allocRes)
|
||||
return std::unexpected(allocRes.error());
|
||||
}
|
||||
|
||||
// 生成 LoadFn: RA = 目标寄存器, Bx = Proto 在 module->protos 中的绝对索引
|
||||
emit(Op::iABx(OpCode::LoadFn, targetReg, static_cast<uint16_t>(f->protoIndex)),
|
||||
&f->location);
|
||||
}
|
||||
else if (f->resolvedSymbol->location == SymbolLocation::Global)
|
||||
{
|
||||
auto result = allocateReg(f->location);
|
||||
if (!result)
|
||||
{
|
||||
return std::unexpected(result.error());
|
||||
}
|
||||
|
||||
Register r = *result;
|
||||
emit(Op::iABx(OpCode::LoadFn, r, static_cast<uint16_t>(f->protoIndex)),
|
||||
&f->location);
|
||||
|
||||
int gId = getGlobalID(f->name);
|
||||
emit(Op::iABx(OpCode::SetGlobal, r, static_cast<std::uint16_t>(gId)),
|
||||
&f->location);
|
||||
|
||||
freeReg();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case AstType::IfStmt: {
|
||||
auto *i = static_cast<IfStmt *>(stmt);
|
||||
DynArray<int> exitJumps;
|
||||
|
||||
Register mark = current->freereg; // mark
|
||||
auto r_cond = compileExpr(i->cond);
|
||||
if (!r_cond)
|
||||
return std::unexpected(r_cond.error());
|
||||
|
||||
int jmpToNext = static_cast<int>(current->proto->code.size());
|
||||
emit(Op::iAsBx(OpCode::JmpIfFalse, *r_cond, 0), &i->location);
|
||||
current->freereg = mark; // 回收条件表达式临时槽位
|
||||
|
||||
if (auto r = compileStmt(i->consequent); !r)
|
||||
return r;
|
||||
exitJumps.push_back(static_cast<int>(current->proto->code.size()));
|
||||
emit(Op::iAsBx(OpCode::Jmp, 0, 0), &i->location);
|
||||
|
||||
int targetIdx = static_cast<int>(current->proto->code.size());
|
||||
current->proto->code[jmpToNext] = Op::iAsBx(
|
||||
OpCode::JmpIfFalse, *r_cond, static_cast<int16_t>(targetIdx - jmpToNext - 1));
|
||||
|
||||
for (auto *elif : i->elifs)
|
||||
{
|
||||
Register elifMark = current->freereg;
|
||||
auto ec = compileExpr(elif->cond);
|
||||
if (!ec)
|
||||
return std::unexpected(ec.error());
|
||||
|
||||
int nextElif = static_cast<int>(current->proto->code.size());
|
||||
emit(Op::iAsBx(OpCode::JmpIfFalse, *ec, 0), &elif->location);
|
||||
current->freereg = elifMark; // 回收 elif 临时槽位
|
||||
|
||||
if (auto r = compileStmt(elif->consequent); !r)
|
||||
return r;
|
||||
exitJumps.push_back(static_cast<int>(current->proto->code.size()));
|
||||
emit(Op::iAsBx(OpCode::Jmp, 0, 0), &elif->location);
|
||||
|
||||
int target = static_cast<int>(current->proto->code.size());
|
||||
|
||||
current->proto->code.resize(nextElif);
|
||||
current->proto->code[nextElif] = Op::iAsBx(
|
||||
OpCode::JmpIfFalse, *ec, static_cast<int16_t>(target - nextElif - 1));
|
||||
|
||||
current->proto->locations.resize(nextElif);
|
||||
current->proto->locations[nextElif] = &elif->location;
|
||||
}
|
||||
|
||||
if (i->alternate)
|
||||
{
|
||||
if (auto r = compileStmt(i->alternate); !r)
|
||||
return r;
|
||||
}
|
||||
|
||||
int endIdx = static_cast<int>(current->proto->code.size());
|
||||
for (int pos : exitJumps)
|
||||
{
|
||||
current->proto->code[pos] =
|
||||
Op::iAsBx(OpCode::Jmp, 0, static_cast<int16_t>(endIdx - pos - 1));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case AstType::WhileStmt: {
|
||||
auto *w = static_cast<WhileStmt *>(stmt);
|
||||
int startIdx = static_cast<int>(current->proto->code.size());
|
||||
|
||||
Register mark = current->freereg; // mark
|
||||
auto r_cond = compileExpr(w->cond);
|
||||
if (!r_cond)
|
||||
return std::unexpected(r_cond.error());
|
||||
|
||||
int exitJmpIdx = static_cast<int>(current->proto->code.size());
|
||||
emit(Op::iAsBx(OpCode::JmpIfFalse, *r_cond, 0), &w->location);
|
||||
current->freereg = mark; // 回收循环条件临时槽位
|
||||
|
||||
if (auto r = compileStmt(w->body); !r)
|
||||
return r;
|
||||
|
||||
int backJmpIdx = static_cast<int>(current->proto->code.size());
|
||||
emit(Op::iAsBx(OpCode::Jmp, 0, static_cast<int16_t>(startIdx - backJmpIdx - 1)),
|
||||
&w->location);
|
||||
|
||||
int endIdx = static_cast<int>(current->proto->code.size());
|
||||
current->proto->code[exitJmpIdx] = Op::iAsBx(
|
||||
OpCode::JmpIfFalse, *r_cond, static_cast<int16_t>(endIdx - exitJmpIdx - 1));
|
||||
break;
|
||||
}
|
||||
|
||||
case AstType::ReturnStmt: {
|
||||
auto *rs = static_cast<ReturnStmt *>(stmt);
|
||||
Register mark = current->freereg; // mark
|
||||
Register retReg;
|
||||
|
||||
if (rs->value)
|
||||
{
|
||||
auto r = compileExpr(rs->value);
|
||||
if (!r)
|
||||
return std::unexpected(r.error());
|
||||
retReg = *r;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto r = allocateReg(rs->location);
|
||||
if (!r)
|
||||
return std::unexpected(r.error());
|
||||
emit(Op::iABC(OpCode::LoadNull, *r, 0, 0), &rs->location);
|
||||
retReg = *r;
|
||||
}
|
||||
|
||||
emit(Op::iABC(OpCode::Return, retReg, 0, 0), &rs->location);
|
||||
current->freereg = mark; // 回收返回值计算的占用
|
||||
break;
|
||||
}
|
||||
|
||||
case AstType::ExprStmt: {
|
||||
Register mark = current->freereg; // mark
|
||||
auto reg = compileExpr(static_cast<ExprStmt *>(stmt)->expr);
|
||||
if (!reg)
|
||||
return std::unexpected(reg.error());
|
||||
|
||||
current->freereg = mark; // 彻底抛弃孤立表达式的副作用
|
||||
break;
|
||||
}
|
||||
|
||||
default: break;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
} // namespace Fig
|
||||
@@ -1,19 +0,0 @@
|
||||
#include <LSP/LSPServer.hpp>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <fcntl.h>
|
||||
#include <io.h>
|
||||
#endif
|
||||
|
||||
int main()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
_setmode(_fileno(stdin), _O_BINARY);
|
||||
_setmode(_fileno(stdout), _O_BINARY);
|
||||
#endif
|
||||
|
||||
Fig::LspServer server;
|
||||
|
||||
server.Run();
|
||||
return 0;
|
||||
}
|
||||
@@ -196,6 +196,7 @@ namespace Fig
|
||||
Function,
|
||||
Struct,
|
||||
Instance,
|
||||
TypeObj,
|
||||
};
|
||||
|
||||
struct StructObject /* : public Object */; // 结构体基类的定义,前向声明
|
||||
@@ -235,6 +236,37 @@ namespace Fig
|
||||
{
|
||||
return type == ObjectType::Instance;
|
||||
}
|
||||
|
||||
constexpr bool isTypeObj() const
|
||||
{
|
||||
return type == ObjectType::TypeObj;
|
||||
}
|
||||
};
|
||||
|
||||
enum class TypeTag : uint8_t
|
||||
{
|
||||
Null,
|
||||
Int,
|
||||
Double,
|
||||
String,
|
||||
Bool,
|
||||
Any,
|
||||
Type,
|
||||
Function,
|
||||
Struct,
|
||||
Interface,
|
||||
};
|
||||
|
||||
struct TypeObject : Object
|
||||
{
|
||||
String name;
|
||||
TypeTag tag;
|
||||
|
||||
TypeObject(String _name, TypeTag _t) : name(std::move(_name)), tag(_t)
|
||||
{
|
||||
type = ObjectType::TypeObj;
|
||||
klass = nullptr; // TypeObject 自身的 klass 指向 TypeTypeObject,初始化后设置
|
||||
}
|
||||
};
|
||||
} // namespace Fig
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
#include <Ast/Operator.hpp>
|
||||
#include <Object/ObjectBase.hpp>
|
||||
#include <Sema/Type.hpp>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
@@ -23,33 +24,29 @@ namespace Fig
|
||||
// + 6 bytes padding
|
||||
};
|
||||
*/
|
||||
struct FieldMeta
|
||||
{
|
||||
String name;
|
||||
Type type;
|
||||
};
|
||||
|
||||
struct StructObject final : public Object
|
||||
{
|
||||
String name; // 元信息(仅供调试/打印/反射)
|
||||
|
||||
// 内存布局信息
|
||||
String name;
|
||||
std::uint8_t fieldCount;
|
||||
FieldMeta *fields; // [fieldCount]
|
||||
Object *operators[GetOperatorsSize()];
|
||||
/*
|
||||
运算符重载,nullptr代表无重载
|
||||
一般为 NativeFunction / Function
|
||||
|
||||
排列:
|
||||
[unary operators ]( binary operators]
|
||||
0 - UnaryOperators::Count BinaryOperators::Count
|
||||
*/
|
||||
|
||||
// operators: [UnaryOp 0..N][BinaryOp 0..N], nullptr = 无重载
|
||||
|
||||
Object *GetUnaryOperator(UnaryOperator _op)
|
||||
{
|
||||
std::uint8_t idx = static_cast<std::uint8_t>(_op);
|
||||
return operators[idx];
|
||||
return operators[static_cast<std::uint8_t>(_op)];
|
||||
}
|
||||
|
||||
Object *GetBinaryOperator(BinaryOperator _op)
|
||||
{
|
||||
std::uint16_t idx = static_cast<std::uint8_t>(UnaryOperator::Count) + static_cast<std::uint8_t>(_op);
|
||||
return operators[idx];
|
||||
return operators[static_cast<std::uint8_t>(UnaryOperator::Count)
|
||||
+ static_cast<std::uint8_t>(_op)];
|
||||
}
|
||||
};
|
||||
}; // namespace Fig
|
||||
@@ -1,752 +0,0 @@
|
||||
/*!
|
||||
@file src/Sema/Analyzer.cpp
|
||||
@brief 语义分析器实现:实装强类型校验 (Call, Infix, Member, Return, Assign)
|
||||
*/
|
||||
|
||||
#include <Ast/Ast.hpp>
|
||||
#include <Ast/Expr/MemberExpr.hpp>
|
||||
#include <Ast/Stmt/ImplStmt.hpp>
|
||||
#include <Ast/Stmt/InterfaceDefStmt.hpp>
|
||||
#include <Ast/Stmt/StructDefStmt.hpp>
|
||||
#include <Sema/Analyzer.hpp>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
struct AnalyzerState
|
||||
{
|
||||
int loopDepth = 0;
|
||||
FnDefStmt *currentFn = nullptr;
|
||||
} state;
|
||||
|
||||
struct ScopeGuard
|
||||
{
|
||||
Environment &env;
|
||||
ScopeGuard(Environment &e, bool isFn) : env(e)
|
||||
{
|
||||
env.Push(isFn);
|
||||
}
|
||||
~ScopeGuard()
|
||||
{
|
||||
env.Pop();
|
||||
}
|
||||
};
|
||||
|
||||
struct LoopGuard
|
||||
{
|
||||
int &depth;
|
||||
LoopGuard(int &d) : depth(d)
|
||||
{
|
||||
depth++;
|
||||
}
|
||||
~LoopGuard()
|
||||
{
|
||||
depth--;
|
||||
}
|
||||
};
|
||||
|
||||
struct FnStateGuard
|
||||
{
|
||||
FnDefStmt *¤t;
|
||||
FnDefStmt *old;
|
||||
FnStateGuard(FnDefStmt *&c, FnDefStmt *n) : current(c), old(c)
|
||||
{
|
||||
current = n;
|
||||
}
|
||||
~FnStateGuard()
|
||||
{
|
||||
current = old;
|
||||
}
|
||||
};
|
||||
|
||||
Result<void, Error> Analyzer::Analyze(Program *prog)
|
||||
{
|
||||
ScopeGuard guard(env, false);
|
||||
auto r1 = pass1(prog);
|
||||
if (!r1)
|
||||
return r1;
|
||||
auto r2 = resolveTypes(prog);
|
||||
if (!r2)
|
||||
return r2;
|
||||
auto r3 = checkBodies(prog);
|
||||
if (!r3)
|
||||
return r3;
|
||||
return {};
|
||||
}
|
||||
|
||||
Result<void, Error> Analyzer::pass1(Program *prog)
|
||||
{
|
||||
for (auto *stmt : prog->nodes)
|
||||
{
|
||||
if (stmt->type == AstType::StructDefStmt)
|
||||
{
|
||||
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;
|
||||
}
|
||||
else if (stmt->type == AstType::FnDefStmt)
|
||||
{
|
||||
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;
|
||||
env.current->locals[f->name] = sym;
|
||||
f->resolvedSymbol = sym;
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
Result<void, Error> Analyzer::resolveTypes(Program *prog)
|
||||
{
|
||||
for (auto *stmt : prog->nodes)
|
||||
{
|
||||
if (stmt->type == AstType::StructDefStmt)
|
||||
{
|
||||
auto *s = static_cast<StructDefStmt *>(stmt);
|
||||
auto *st = static_cast<StructType *>(globalTypes[s->name]);
|
||||
for (auto &f : s->fields)
|
||||
{
|
||||
auto res = resolveTypeExpr(f.type);
|
||||
if (!res)
|
||||
{
|
||||
return std::unexpected(res.error());
|
||||
}
|
||||
st->AddField(f.name, *res, f.isPublic);
|
||||
}
|
||||
}
|
||||
else if (stmt->type == AstType::FnDefStmt)
|
||||
{
|
||||
auto *f = static_cast<FnDefStmt *>(stmt);
|
||||
auto res = resolveTypeExpr(f->returnTypeSpecifier);
|
||||
if (!res)
|
||||
return std::unexpected(res.error());
|
||||
f->resolvedReturnType = *res;
|
||||
|
||||
DynArray<Type> paramTypes;
|
||||
for (auto *p : f->params)
|
||||
{
|
||||
auto pres = resolveTypeExpr(p->typeSpecifier);
|
||||
if (!pres)
|
||||
return std::unexpected(pres.error());
|
||||
p->resolvedType = *pres;
|
||||
paramTypes.push_back(*pres);
|
||||
}
|
||||
|
||||
f->resolvedSymbol->type = typeCtx.CreateFuncType(std::move(paramTypes), *res);
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
Result<void, Error> Analyzer::checkBodies(Program *prog)
|
||||
{
|
||||
for (auto *stmt : prog->nodes)
|
||||
{
|
||||
auto r = analyzeStmt(stmt);
|
||||
if (!r)
|
||||
return r;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
Result<void, Error> Analyzer::analyzeStmt(Stmt *stmt)
|
||||
{
|
||||
if (!stmt)
|
||||
return {};
|
||||
switch (stmt->type)
|
||||
{
|
||||
case AstType::BlockStmt: {
|
||||
auto *b = static_cast<BlockStmt *>(stmt);
|
||||
ScopeGuard guard(env, false);
|
||||
for (auto *s : b->nodes)
|
||||
{
|
||||
if (auto r = analyzeStmt(s); !r)
|
||||
return r;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case AstType::VarDecl: {
|
||||
auto *v = static_cast<VarDecl *>(stmt);
|
||||
Type initT = typeCtx.GetBasic(TypeTag::Any);
|
||||
if (v->initExpr)
|
||||
{
|
||||
auto res = analyzeExpr(v->initExpr);
|
||||
if (!res)
|
||||
return std::unexpected(res.error());
|
||||
initT = *res;
|
||||
}
|
||||
Type declT = v->typeSpecifier ? *resolveTypeExpr(v->typeSpecifier) : initT;
|
||||
|
||||
// 赋值拦截
|
||||
if (v->initExpr && !initT.isAssignableTo(declT))
|
||||
{
|
||||
return std::unexpected(Error(
|
||||
ErrorType::TypeError,
|
||||
"cannot assign '" + initT.toString() + "' to type '" + declT.toString()
|
||||
+ "'",
|
||||
"",
|
||||
v->location));
|
||||
}
|
||||
|
||||
if (env.current->locals.contains(v->name))
|
||||
return std::unexpected(
|
||||
Error(ErrorType::RedeclarationError, "var redeclared", "", v->location));
|
||||
SymbolLocation loc =
|
||||
env.current->parent ? SymbolLocation::Local : SymbolLocation::Global;
|
||||
int idx = (loc == SymbolLocation::Local) ? env.current->nextLocalId++ : 0;
|
||||
env.current->locals[v->name] =
|
||||
arena.Allocate<Symbol>(v->name, declT, loc, idx, false);
|
||||
v->localId = idx;
|
||||
break;
|
||||
}
|
||||
|
||||
case AstType::FnDefStmt: {
|
||||
auto *f = static_cast<FnDefStmt *>(stmt);
|
||||
|
||||
// 局部闭包延迟类型推导
|
||||
|
||||
if (!f->resolvedSymbol) // 闭包?
|
||||
{
|
||||
SymbolLocation loc =
|
||||
env.current->parent ? SymbolLocation::Local : SymbolLocation::Global;
|
||||
int idx = (loc == SymbolLocation::Local) ? env.current->nextLocalId++ : 0;
|
||||
|
||||
Symbol *sym = arena.Allocate<Symbol>(f->name, Type{}, loc, idx, true);
|
||||
f->resolvedSymbol = sym;
|
||||
env.current->locals[f->name] = sym;
|
||||
|
||||
auto res = resolveTypeExpr(f->returnTypeSpecifier);
|
||||
if (!res)
|
||||
return std::unexpected(res.error());
|
||||
f->resolvedReturnType = *res;
|
||||
|
||||
DynArray<Type> paramTypes;
|
||||
for (auto *p : f->params)
|
||||
{
|
||||
auto pres = resolveTypeExpr(p->typeSpecifier);
|
||||
if (!pres)
|
||||
return std::unexpected(pres.error());
|
||||
p->resolvedType = *pres;
|
||||
paramTypes.push_back(*pres);
|
||||
}
|
||||
f->resolvedSymbol->type = typeCtx.CreateFuncType(std::move(paramTypes), *res);
|
||||
}
|
||||
|
||||
FnStateGuard fnGuard(state.currentFn, f);
|
||||
ScopeGuard scopeGuard(env, true);
|
||||
for (auto *p : f->params)
|
||||
{
|
||||
env.current->locals[p->name] = arena.Allocate<Symbol>(
|
||||
p->name,
|
||||
p->resolvedType,
|
||||
SymbolLocation::Local,
|
||||
env.current->nextLocalId++,
|
||||
false);
|
||||
}
|
||||
if (auto r = analyzeStmt(f->body); !r)
|
||||
return r;
|
||||
|
||||
for (const auto &upval : env.current->upvalues)
|
||||
{
|
||||
f->upvalues.push_back({static_cast<std::uint8_t>(upval.index), upval.isLocal});
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case AstType::IfStmt: {
|
||||
auto *i = static_cast<IfStmt *>(stmt);
|
||||
|
||||
if (auto c = analyzeExpr(i->cond); !c)
|
||||
return std::unexpected(c.error());
|
||||
else if (!c->isAssignableTo(typeCtx.GetBasic(TypeTag::Bool)))
|
||||
{
|
||||
return std::unexpected(Error(
|
||||
ErrorType::TypeError, "condition must be Bool", "", i->cond->location));
|
||||
}
|
||||
if (auto b = analyzeStmt(i->consequent); !b)
|
||||
return b;
|
||||
|
||||
for (auto *elif : i->elifs)
|
||||
{
|
||||
if (auto c = analyzeExpr(elif->cond); !c)
|
||||
return std::unexpected(c.error());
|
||||
else if (!c->isAssignableTo(typeCtx.GetBasic(TypeTag::Bool)))
|
||||
{
|
||||
return std::unexpected(Error(
|
||||
ErrorType::TypeError,
|
||||
"condition must be Bool",
|
||||
"",
|
||||
elif->cond->location));
|
||||
}
|
||||
if (auto b = analyzeStmt(elif->consequent); !b)
|
||||
return b;
|
||||
}
|
||||
|
||||
if (i->alternate)
|
||||
{
|
||||
if (auto a = analyzeStmt(i->alternate); !a)
|
||||
return a;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case AstType::WhileStmt: {
|
||||
bool isWhile = stmt->type == AstType::WhileStmt;
|
||||
Expr *cond = isWhile ? static_cast<WhileStmt *>(stmt)->cond :
|
||||
static_cast<IfStmt *>(stmt)->cond;
|
||||
Stmt *body = isWhile ? static_cast<WhileStmt *>(stmt)->body :
|
||||
static_cast<IfStmt *>(stmt)->consequent;
|
||||
|
||||
if (auto c = analyzeExpr(cond); !c)
|
||||
return std::unexpected(c.error());
|
||||
else if (!c->isAssignableTo(typeCtx.GetBasic(TypeTag::Bool)))
|
||||
{
|
||||
return std::unexpected(
|
||||
Error(ErrorType::TypeError, "condition must be Bool", "", cond->location));
|
||||
}
|
||||
|
||||
if (isWhile)
|
||||
{
|
||||
LoopGuard loopGuard(state.loopDepth);
|
||||
if (auto b = analyzeStmt(body); !b)
|
||||
return b;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (auto b = analyzeStmt(body); !b)
|
||||
return b;
|
||||
auto *i = static_cast<IfStmt *>(stmt);
|
||||
if (i->alternate)
|
||||
{
|
||||
if (auto a = analyzeStmt(i->alternate); !a)
|
||||
return a;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case AstType::BreakStmt:
|
||||
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);
|
||||
if (rs->value)
|
||||
{
|
||||
auto res = analyzeExpr(rs->value);
|
||||
if (!res)
|
||||
return std::unexpected(res.error());
|
||||
retT = *res;
|
||||
}
|
||||
// 返回值校验
|
||||
if (state.currentFn && !retT.isAssignableTo(state.currentFn->resolvedReturnType))
|
||||
{
|
||||
return std::unexpected(Error(
|
||||
ErrorType::TypeError,
|
||||
"cannot return '" + retT.toString() + "' from function expecting '"
|
||||
+ state.currentFn->resolvedReturnType.toString() + "'",
|
||||
"",
|
||||
rs->location));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case AstType::ExprStmt: {
|
||||
auto res = analyzeExpr(static_cast<ExprStmt *>(stmt)->expr);
|
||||
if (!res)
|
||||
return std::unexpected(res.error());
|
||||
break;
|
||||
}
|
||||
default: break;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
Result<Type, Error> Analyzer::analyzeExpr(Expr *expr)
|
||||
{
|
||||
if (!expr)
|
||||
return typeCtx.GetBasic(TypeTag::Null);
|
||||
switch (expr->type)
|
||||
{
|
||||
case AstType::LiteralExpr: {
|
||||
auto t = static_cast<LiteralExpr *>(expr)->literal.type;
|
||||
if (t == TokenType::LiteralNumber)
|
||||
return expr->resolvedType = typeCtx.GetBasic(TypeTag::Int);
|
||||
if (t == TokenType::LiteralString)
|
||||
return expr->resolvedType = typeCtx.GetBasic(TypeTag::String);
|
||||
if (t == TokenType::LiteralTrue || t == TokenType::LiteralFalse)
|
||||
return expr->resolvedType = typeCtx.GetBasic(TypeTag::Bool);
|
||||
return expr->resolvedType = typeCtx.GetBasic(TypeTag::Null);
|
||||
}
|
||||
case AstType::IdentiExpr: {
|
||||
auto *i = static_cast<IdentiExpr *>(expr);
|
||||
auto res = resolveSymbolInternal(i->name, i->location, env.current);
|
||||
if (!res)
|
||||
return std::unexpected(res.error());
|
||||
i->resolvedSymbol = *res;
|
||||
return expr->resolvedType = (*res)->type;
|
||||
}
|
||||
case AstType::MemberExpr: {
|
||||
auto *m = static_cast<MemberExpr *>(expr);
|
||||
auto targetRes = analyzeExpr(m->target);
|
||||
if (!targetRes)
|
||||
return targetRes;
|
||||
|
||||
Type targetType = *targetRes;
|
||||
if (targetType.is(TypeTag::Any))
|
||||
return expr->resolvedType = typeCtx.GetBasic(TypeTag::Any);
|
||||
if (!targetType.is(TypeTag::Struct))
|
||||
return std::unexpected(Error(
|
||||
ErrorType::TypeError, "member access requires struct", "", m->location));
|
||||
|
||||
auto *st = static_cast<StructType *>(targetType.base);
|
||||
if (!st->fieldMap.contains(m->name))
|
||||
{
|
||||
return std::unexpected(Error(
|
||||
ErrorType::TypeError,
|
||||
"struct '" + st->name + "' has no field named '" + m->name + "'",
|
||||
"",
|
||||
m->location));
|
||||
}
|
||||
// 字段类型
|
||||
return expr->resolvedType = st->fields[st->fieldMap[m->name]].type;
|
||||
}
|
||||
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)
|
||||
{
|
||||
if (!arg.name.empty() && !st->fieldMap.contains(arg.name))
|
||||
return std::unexpected(
|
||||
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))
|
||||
{
|
||||
return std::unexpected(Error(
|
||||
ErrorType::TypeError, "field type mismatch", "", arg.value->location));
|
||||
}
|
||||
}
|
||||
return expr->resolvedType = *res;
|
||||
}
|
||||
case AstType::InfixExpr: {
|
||||
auto *in = static_cast<InfixExpr *>(expr);
|
||||
auto lRes = analyzeExpr(in->left);
|
||||
if (!lRes)
|
||||
return lRes;
|
||||
auto rRes = analyzeExpr(in->right);
|
||||
if (!rRes)
|
||||
return rRes;
|
||||
Type l = *lRes;
|
||||
Type r = *rRes;
|
||||
|
||||
if (in->op == BinaryOperator::Assign)
|
||||
{
|
||||
if (!r.isAssignableTo(l))
|
||||
return std::unexpected(Error(
|
||||
ErrorType::TypeError,
|
||||
"cannot assign '" + r.toString() + "' to '" + l.toString() + "'",
|
||||
"",
|
||||
in->location));
|
||||
return expr->resolvedType = l;
|
||||
}
|
||||
|
||||
if (in->op == BinaryOperator::Equal || in->op == BinaryOperator::NotEqual
|
||||
|| in->op == BinaryOperator::Greater || in->op == BinaryOperator::Less
|
||||
|| in->op == BinaryOperator::GreaterEqual
|
||||
|| in->op == BinaryOperator::LessEqual)
|
||||
{
|
||||
return expr->resolvedType = typeCtx.GetBasic(TypeTag::Bool);
|
||||
}
|
||||
|
||||
if (l.is(TypeTag::Any) || r.is(TypeTag::Any))
|
||||
return expr->resolvedType = typeCtx.GetBasic(TypeTag::Any);
|
||||
|
||||
// 算术操作强检查
|
||||
if (in->op == BinaryOperator::Add && l.is(TypeTag::String) && r.is(TypeTag::String))
|
||||
return expr->resolvedType = typeCtx.GetBasic(TypeTag::String);
|
||||
if (l.is(TypeTag::Int) && r.is(TypeTag::Int))
|
||||
return expr->resolvedType = typeCtx.GetBasic(TypeTag::Int);
|
||||
if ((l.is(TypeTag::Int) || l.is(TypeTag::Double))
|
||||
&& (r.is(TypeTag::Int) || r.is(TypeTag::Double)))
|
||||
return expr->resolvedType = typeCtx.GetBasic(TypeTag::Double);
|
||||
|
||||
return std::unexpected(Error(
|
||||
ErrorType::TypeError, "invalid types for binary operator", "", in->location));
|
||||
}
|
||||
case AstType::CallExpr: {
|
||||
auto *c = static_cast<CallExpr *>(expr);
|
||||
auto calleeRes = analyzeExpr(c->callee);
|
||||
if (!calleeRes)
|
||||
return calleeRes;
|
||||
Type calleeType = *calleeRes;
|
||||
|
||||
DynArray<Type> argTypes;
|
||||
for (auto *a : c->args.args)
|
||||
{
|
||||
auto ar = analyzeExpr(a);
|
||||
if (!ar)
|
||||
return std::unexpected(ar.error());
|
||||
argTypes.push_back(*ar);
|
||||
}
|
||||
|
||||
if (calleeType.is(TypeTag::Any))
|
||||
return expr->resolvedType = typeCtx.GetBasic(TypeTag::Any);
|
||||
|
||||
// 函数签名校验
|
||||
if (!calleeType.is(TypeTag::Function))
|
||||
return std::unexpected(
|
||||
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::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,
|
||||
"argument " + std::to_string(i + 1) + " expects '"
|
||||
+ ft->paramTypes[i].toString() + "', got '" + argTypes[i].toString()
|
||||
+ "'",
|
||||
"",
|
||||
c->args.args[i]->location));
|
||||
}
|
||||
}
|
||||
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)
|
||||
{
|
||||
Scope *curr = s;
|
||||
while (curr)
|
||||
{
|
||||
if (curr->locals.contains(name))
|
||||
return curr->locals[name];
|
||||
if (curr->isFunctionBoundary)
|
||||
break;
|
||||
curr = curr->parent;
|
||||
}
|
||||
if (curr && curr->parent)
|
||||
{
|
||||
auto res = resolveSymbolInternal(name, loc, curr->parent);
|
||||
if (!res)
|
||||
return res;
|
||||
Symbol *outer = *res;
|
||||
if (outer->location == SymbolLocation::Global)
|
||||
return outer;
|
||||
int idx = addUpvalue(curr, outer, outer->location == SymbolLocation::Local);
|
||||
return arena.Allocate<Symbol>(
|
||||
name, outer->type, SymbolLocation::Upvalue, idx, outer->isConst);
|
||||
}
|
||||
if (globalSymbols.contains(name))
|
||||
return globalSymbols[name];
|
||||
return std::unexpected(
|
||||
Error(ErrorType::UseUndeclaredIdentifier, "symbol not found", "", loc));
|
||||
}
|
||||
|
||||
int Analyzer::addUpvalue(Scope *s, Symbol *t, bool isL)
|
||||
{
|
||||
for (size_t i = 0; i < s->upvalues.size(); ++i)
|
||||
if (s->upvalues[i].target == t)
|
||||
return (int) i;
|
||||
int idx = (int) s->upvalues.size();
|
||||
s->upvalues.push_back({t, idx, isL});
|
||||
return idx;
|
||||
}
|
||||
|
||||
Result<Type, Error> Analyzer::resolveTypeExpr(Expr *texpr)
|
||||
{
|
||||
if (!texpr)
|
||||
return typeCtx.GetBasic(TypeTag::Any);
|
||||
if (texpr->type == AstType::NamedTypeExpr)
|
||||
{
|
||||
auto *n = static_cast<NamedTypeExpr *>(texpr);
|
||||
if (n->path.empty())
|
||||
return typeCtx.GetBasic(TypeTag::Any);
|
||||
String &root = n->path[0];
|
||||
if (root == "Any")
|
||||
return typeCtx.GetBasic(TypeTag::Any);
|
||||
if (root == "Int")
|
||||
return typeCtx.GetBasic(TypeTag::Int);
|
||||
if (root == "Double")
|
||||
return typeCtx.GetBasic(TypeTag::Double);
|
||||
if (root == "String")
|
||||
return typeCtx.GetBasic(TypeTag::String);
|
||||
if (root == "Bool")
|
||||
return typeCtx.GetBasic(TypeTag::Bool);
|
||||
if (root == "Null")
|
||||
return typeCtx.GetBasic(TypeTag::Null);
|
||||
|
||||
if (globalTypes.contains(root))
|
||||
return Type{globalTypes[root], false};
|
||||
|
||||
return std::unexpected(
|
||||
Error(ErrorType::UseUndeclaredIdentifier, "unknown type", "", texpr->location));
|
||||
}
|
||||
else if (texpr->type == AstType::NullableTypeExpr)
|
||||
{
|
||||
auto res = resolveTypeExpr(static_cast<NullableTypeExpr *>(texpr)->inner);
|
||||
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
|
||||
@@ -1,14 +1,14 @@
|
||||
/*!
|
||||
@file src/Sema/Analyzer.hpp
|
||||
@brief 语义分析器定义
|
||||
@brief 语义分析
|
||||
@author PuqiAR (im@puqiar.top)
|
||||
@date 2026-06-06
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Ast/Ast.hpp>
|
||||
#include <Sema/Type.hpp>
|
||||
#include <Sema/Environment.hpp>
|
||||
#include <Utils/Arena.hpp>
|
||||
#include <Error/Diagnostics.hpp>
|
||||
#include <SourceManager/SourceManager.hpp>
|
||||
|
||||
@@ -16,38 +16,17 @@ namespace Fig
|
||||
{
|
||||
class Analyzer
|
||||
{
|
||||
private:
|
||||
Arena arena;
|
||||
SourceManager &manager;
|
||||
TypeContext typeCtx;
|
||||
Environment env;
|
||||
Diagnostics diag;
|
||||
|
||||
HashMap<String, BaseType*> globalTypes;
|
||||
HashMap<String, Symbol*> globalSymbols;
|
||||
|
||||
bool hasInit = false;
|
||||
bool hasMain = false;
|
||||
|
||||
// 核心递归查找:解决跨越函数边界的捕获问题
|
||||
Result<Symbol*, Error> resolveSymbolInternal(const String &name, const SourceLocation &loc, Scope* startScope);
|
||||
|
||||
Result<Type, Error> resolveTypeExpr(Expr *texpr);
|
||||
Result<void, Error> pass1(Program *prog);
|
||||
Result<void, Error> resolveTypes(Program *prog);
|
||||
Result<void, Error> checkBodies(Program *prog);
|
||||
|
||||
Result<void, Error> analyzeStmt(Stmt *stmt);
|
||||
Result<Type, Error> analyzeExpr(Expr *expr);
|
||||
|
||||
int addUpvalue(Scope *scope, Symbol *target, bool isLocal);
|
||||
|
||||
public:
|
||||
Analyzer(SourceManager &m) : manager(m) {}
|
||||
Analyzer(SourceManager &) {}
|
||||
|
||||
Result<void, Error> Analyze(Program *prog);
|
||||
Result<void, Error> Analyze(Program *)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
Diagnostics& GetDiagnostics() { return diag; }
|
||||
TypeContext& GetTypeContext() { return typeCtx; }
|
||||
Diagnostics &GetDiagnostics() { return diag; }
|
||||
|
||||
private:
|
||||
Diagnostics diag;
|
||||
};
|
||||
}
|
||||
} // namespace Fig
|
||||
|
||||
@@ -1,64 +0,0 @@
|
||||
#include <Parser/Parser.hpp>
|
||||
#include <Sema/Analyzer.hpp>
|
||||
#include <filesystem>
|
||||
#include <iostream>
|
||||
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
void runTest(const std::string &path)
|
||||
{
|
||||
using namespace Fig;
|
||||
std::cout << "\n[TEST] Testing: " << path << std::endl;
|
||||
|
||||
SourceManager srcManager{String(path)};
|
||||
String source = srcManager.Read();
|
||||
if (!srcManager.read)
|
||||
{
|
||||
std::cerr << "FAILED: Could not read file" << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
Lexer lexer(source, String(path));
|
||||
|
||||
Diagnostics diagnostics;
|
||||
|
||||
Parser parser(lexer, srcManager, String(path), diagnostics);
|
||||
|
||||
diagnostics.EmitAll(srcManager);
|
||||
|
||||
auto pRes = parser.Parse();
|
||||
if (!pRes)
|
||||
{
|
||||
std::cerr << "FAILED: Parser Error" << std::endl;
|
||||
ReportError(pRes.error(), srcManager);
|
||||
return;
|
||||
}
|
||||
|
||||
// 修复:确保 analyzer 存活直到错误打印完成
|
||||
Analyzer analyzer(srcManager);
|
||||
auto aRes = analyzer.Analyze(*pRes);
|
||||
|
||||
if (!aRes)
|
||||
{
|
||||
std::cout << "SUCCESS: Analyzer correctly caught error:" << std::endl;
|
||||
ReportError(aRes.error(), srcManager);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cerr << "FAILED: Analyzer missed the semantic error!" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
std::string testDir = "T:/Files/Maker/Code/MyCodingLanguage/The Fig Project/Fig/tests/Sema";
|
||||
for (const auto &entry : fs::directory_iterator(testDir))
|
||||
{
|
||||
if (entry.path().extension() == ".fig")
|
||||
{
|
||||
runTest(entry.path().string());
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
/*!
|
||||
@file src/Sema/Environment.hpp
|
||||
@brief 树状符号表定义
|
||||
@brief 符号表
|
||||
@author PuqiAR (im@puqiar.top)
|
||||
@date 2026-07-05
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
@@ -10,49 +12,30 @@
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
enum class SymbolLocation
|
||||
enum class SymbolKind : uint8_t
|
||||
{
|
||||
Global,
|
||||
Local,
|
||||
Upvalue
|
||||
Var,
|
||||
Const,
|
||||
Func,
|
||||
Type,
|
||||
};
|
||||
|
||||
struct Symbol
|
||||
{
|
||||
String name;
|
||||
Type type;
|
||||
SymbolLocation location;
|
||||
int index;
|
||||
bool isConst;
|
||||
SymbolKind kind;
|
||||
int index; // local: register idx, global: global idx, upvalue: upvalue idx
|
||||
|
||||
Symbol(String n, Type t, SymbolLocation l, int i, bool c) :
|
||||
name(std::move(n)), type(t), location(l), index(i), isConst(c)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
struct UpvalueCapture
|
||||
{
|
||||
Symbol *target;
|
||||
int index;
|
||||
bool isLocal;
|
||||
bool isType() const { return kind == SymbolKind::Type; }
|
||||
};
|
||||
|
||||
struct Scope
|
||||
{
|
||||
Scope *parent = nullptr;
|
||||
bool isFunctionBoundary = false;
|
||||
|
||||
bool isFnBoundary = false;
|
||||
HashMap<String, Symbol *> locals;
|
||||
DynArray<UpvalueCapture> upvalues;
|
||||
|
||||
int nextLocalId = 0;
|
||||
|
||||
Scope(Scope *p, bool isFn) : parent(p), isFunctionBoundary(isFn)
|
||||
{
|
||||
if (p && !isFn)
|
||||
nextLocalId = p->nextLocalId;
|
||||
}
|
||||
int nextReg = 0;
|
||||
};
|
||||
|
||||
class Environment
|
||||
@@ -60,13 +43,19 @@ namespace Fig
|
||||
public:
|
||||
Scope *current = nullptr;
|
||||
|
||||
void Push(bool isFn)
|
||||
void push(bool isFn)
|
||||
{
|
||||
current = new Scope(current, isFn);
|
||||
auto *s = new Scope;
|
||||
s->parent = current;
|
||||
s->isFnBoundary = isFn;
|
||||
if (current && !isFn)
|
||||
s->nextReg = current->nextReg;
|
||||
current = s;
|
||||
}
|
||||
void Pop()
|
||||
|
||||
void pop()
|
||||
{
|
||||
Scope *old = current;
|
||||
auto *old = current;
|
||||
current = current->parent;
|
||||
delete old;
|
||||
}
|
||||
|
||||
@@ -1,100 +0,0 @@
|
||||
/*!
|
||||
@file src/Sema/Type.cpp
|
||||
@brief 类型系统实现
|
||||
*/
|
||||
|
||||
#include <Sema/Type.hpp>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
bool Type::is(TypeTag t) const
|
||||
{
|
||||
return base && base->tag == t;
|
||||
}
|
||||
|
||||
String Type::toString() const
|
||||
{
|
||||
if (!base)
|
||||
return "Unknown";
|
||||
if (base->tag == TypeTag::Function)
|
||||
{
|
||||
auto *ft = static_cast<FuncType *>(base);
|
||||
String sig = "func(";
|
||||
for (size_t i = 0; i < ft->paramTypes.size(); ++i)
|
||||
{
|
||||
sig += ft->paramTypes[i].toString();
|
||||
if (i < ft->paramTypes.size() - 1)
|
||||
sig += ", ";
|
||||
}
|
||||
sig += ") -> " + ft->retType.toString();
|
||||
return sig;
|
||||
}
|
||||
|
||||
String res = base->name;
|
||||
if (isNullable && base->tag != TypeTag::Null)
|
||||
res += "?";
|
||||
return res;
|
||||
}
|
||||
|
||||
bool Type::isAssignableTo(const Type &target) const
|
||||
{
|
||||
if (target.is(TypeTag::Any) || this->is(TypeTag::Any))
|
||||
{
|
||||
return true; // Any 逃逸
|
||||
}
|
||||
if (this->is(TypeTag::Null) && target.isNullable)
|
||||
{
|
||||
return true; // Null 安全赋值
|
||||
}
|
||||
|
||||
return *this->base == *target.base && (!this->isNullable || target.isNullable);
|
||||
}
|
||||
|
||||
TypeContext::TypeContext()
|
||||
{
|
||||
intType = new BaseType(TypeTag::Int, "Int");
|
||||
doubleType = new BaseType(TypeTag::Double, "Double");
|
||||
stringType = new BaseType(TypeTag::String, "String");
|
||||
boolType = new BaseType(TypeTag::Bool, "Bool");
|
||||
anyType = new BaseType(TypeTag::Any, "Any");
|
||||
nullType = new BaseType(TypeTag::Null, "Null");
|
||||
|
||||
allTypes.push_back(intType);
|
||||
allTypes.push_back(doubleType);
|
||||
allTypes.push_back(stringType);
|
||||
allTypes.push_back(boolType);
|
||||
allTypes.push_back(anyType);
|
||||
allTypes.push_back(nullType);
|
||||
}
|
||||
|
||||
TypeContext::~TypeContext()
|
||||
{
|
||||
for (auto t : allTypes)
|
||||
{
|
||||
delete t;
|
||||
}
|
||||
}
|
||||
|
||||
Type TypeContext::GetBasic(TypeTag tag, bool nullable)
|
||||
{
|
||||
BaseType *b = nullptr;
|
||||
switch (tag)
|
||||
{
|
||||
case TypeTag::Int: b = intType; break;
|
||||
case TypeTag::Double: b = doubleType; break;
|
||||
case TypeTag::String: b = stringType; break;
|
||||
case TypeTag::Bool: b = boolType; break;
|
||||
case TypeTag::Any: b = anyType; break;
|
||||
case TypeTag::Null: b = nullType; break;
|
||||
default: break;
|
||||
}
|
||||
return {b, nullable};
|
||||
}
|
||||
|
||||
Type TypeContext::CreateFuncType(DynArray<Type> params, Type ret)
|
||||
{
|
||||
auto *ft = new FuncType(std::move(params), ret);
|
||||
allTypes.push_back(ft);
|
||||
return Type{ft, false};
|
||||
}
|
||||
} // namespace Fig
|
||||
@@ -1,135 +1,36 @@
|
||||
/*!
|
||||
@file src/Sema/Type.hpp
|
||||
@brief 类型系统定义:对齐 NaN-boxing 物理布局
|
||||
@brief 类型系统
|
||||
@author PuqiAR (im@puqiar.top)
|
||||
@date 2026-07-05
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <Deps/Deps.hpp>
|
||||
#include <Error/Error.hpp>
|
||||
|
||||
#include <Object/ObjectBase.hpp>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
enum class TypeTag : std::uint8_t
|
||||
{
|
||||
Int,
|
||||
Double,
|
||||
String,
|
||||
Bool,
|
||||
Null,
|
||||
Any,
|
||||
Function,
|
||||
Struct,
|
||||
Interface
|
||||
};
|
||||
|
||||
class BaseType;
|
||||
|
||||
struct Type
|
||||
{
|
||||
BaseType *base = nullptr;
|
||||
TypeObject *obj = nullptr;
|
||||
bool isNullable = false;
|
||||
|
||||
bool operator==(const Type &other) const
|
||||
{
|
||||
return base == other.base && isNullable == other.isNullable;
|
||||
}
|
||||
bool operator!=(const Type &other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
bool is(TypeTag tag) const;
|
||||
String toString() const;
|
||||
|
||||
bool is(TypeTag t) const;
|
||||
bool isAssignableTo(const Type &target) const;
|
||||
};
|
||||
|
||||
class BaseType
|
||||
{
|
||||
public:
|
||||
TypeTag tag;
|
||||
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
|
||||
{
|
||||
public:
|
||||
DynArray<Type> paramTypes;
|
||||
Type retType;
|
||||
FuncType(DynArray<Type> params, Type ret) :
|
||||
BaseType(TypeTag::Function, "Function"), paramTypes(std::move(params)), retType(ret)
|
||||
inline bool Type::is(TypeTag t) const
|
||||
{
|
||||
return obj && obj->tag == t;
|
||||
}
|
||||
|
||||
bool operator==(const FuncType &other) const
|
||||
inline bool Type::isAssignableTo(const Type &target) const
|
||||
{
|
||||
return paramTypes == other.paramTypes && retType == other.retType;
|
||||
if (target.is(TypeTag::Any) || is(TypeTag::Any))
|
||||
return true;
|
||||
if (is(TypeTag::Null) && target.isNullable)
|
||||
return true;
|
||||
return obj == target.obj && (!isNullable || target.isNullable);
|
||||
}
|
||||
};
|
||||
|
||||
class StructType : public BaseType
|
||||
{
|
||||
public:
|
||||
struct Field
|
||||
{
|
||||
String name;
|
||||
Type type;
|
||||
bool isPublic;
|
||||
int index;
|
||||
};
|
||||
DynArray<Field> fields;
|
||||
HashMap<String, size_t> fieldMap;
|
||||
HashMap<String, struct FnDefStmt *> methods;
|
||||
|
||||
StructType(String n) : BaseType(TypeTag::Struct, std::move(n)) {}
|
||||
void AddField(String name, Type type, bool isPublic)
|
||||
{
|
||||
size_t idx = fields.size();
|
||||
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
|
||||
{
|
||||
public:
|
||||
struct MethodSig
|
||||
{
|
||||
String name;
|
||||
DynArray<Type> params;
|
||||
Type retType;
|
||||
};
|
||||
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
|
||||
{
|
||||
public:
|
||||
DynArray<BaseType *> allTypes;
|
||||
BaseType *intType, *doubleType, *stringType, *boolType, *anyType, *nullType;
|
||||
|
||||
TypeContext();
|
||||
~TypeContext();
|
||||
|
||||
Type GetBasic(TypeTag tag, bool nullable = false);
|
||||
Type CreateFuncType(DynArray<Type> params, Type ret);
|
||||
};
|
||||
} // namespace Fig
|
||||
|
||||
74
xmake.lua
74
xmake.lua
@@ -50,74 +50,6 @@ target("ObjectTest")
|
||||
add_files("src/Object/Object.cpp")
|
||||
add_files("src/Object/ObjectTest.cpp")
|
||||
|
||||
target("AnalyzerTest")
|
||||
add_files("src/Core/*.cpp")
|
||||
add_files("src/Token/Token.cpp")
|
||||
add_files("src/Error/Error.cpp")
|
||||
add_files("src/Lexer/Lexer.cpp")
|
||||
add_files("src/Ast/Operator.cpp")
|
||||
add_files("src/Parser/ExprParser.cpp")
|
||||
add_files("src/Parser/StmtParser.cpp")
|
||||
add_files("src/Parser/TypeExprParser.cpp")
|
||||
add_files("src/Parser/Parser.cpp")
|
||||
add_files("src/Sema/Type.cpp")
|
||||
add_files("src/Sema/Analyzer.cpp")
|
||||
add_files("src/Sema/AnalyzerTest.cpp")
|
||||
|
||||
target("CompilerTest")
|
||||
add_files("src/Core/*.cpp")
|
||||
add_files("src/Token/Token.cpp")
|
||||
add_files("src/Error/Error.cpp")
|
||||
add_files("src/Lexer/Lexer.cpp")
|
||||
add_files("src/Ast/Operator.cpp")
|
||||
add_files("src/Bytecode/Disassembler.cpp")
|
||||
add_files("src/Parser/ExprParser.cpp")
|
||||
add_files("src/Parser/StmtParser.cpp")
|
||||
add_files("src/Parser/TypeExprParser.cpp")
|
||||
add_files("src/Parser/Parser.cpp")
|
||||
add_files("src/Object/Object.cpp")
|
||||
add_files("src/Sema/Type.cpp")
|
||||
add_files("src/Sema/Analyzer.cpp")
|
||||
add_files("src/Compiler/ExprCompiler.cpp")
|
||||
add_files("src/Compiler/StmtCompiler.cpp")
|
||||
add_files("src/Compiler/Compiler.cpp")
|
||||
add_files("src/Compiler/CompileTest.cpp")
|
||||
|
||||
target("LSP")
|
||||
add_files("src/Core/*.cpp")
|
||||
add_files("src/Token/Token.cpp")
|
||||
add_files("src/Error/Error.cpp")
|
||||
add_files("src/Lexer/Lexer.cpp")
|
||||
add_files("src/Ast/Operator.cpp")
|
||||
add_files("src/Parser/ExprParser.cpp")
|
||||
add_files("src/Parser/StmtParser.cpp")
|
||||
add_files("src/Parser/TypeExprParser.cpp")
|
||||
add_files("src/Parser/Parser.cpp")
|
||||
add_files("src/Sema/Type.cpp")
|
||||
add_files("src/Sema/Analyzer.cpp")
|
||||
add_files("src/LSP/LSPServer.cpp")
|
||||
set_filename("Fig-LSP")
|
||||
|
||||
target("ReplTest")
|
||||
add_files("src/Core/*.cpp")
|
||||
add_files("src/Token/Token.cpp")
|
||||
add_files("src/Error/Error.cpp")
|
||||
add_files("src/Lexer/Lexer.cpp")
|
||||
add_files("src/Ast/Operator.cpp")
|
||||
add_files("src/Bytecode/Disassembler.cpp")
|
||||
add_files("src/Parser/ExprParser.cpp")
|
||||
add_files("src/Parser/StmtParser.cpp")
|
||||
add_files("src/Parser/TypeExprParser.cpp")
|
||||
add_files("src/Parser/Parser.cpp")
|
||||
add_files("src/Object/Object.cpp")
|
||||
add_files("src/Sema/Type.cpp")
|
||||
add_files("src/Sema/Analyzer.cpp")
|
||||
add_files("src/Compiler/ExprCompiler.cpp")
|
||||
add_files("src/Compiler/StmtCompiler.cpp")
|
||||
add_files("src/Compiler/Compiler.cpp")
|
||||
add_files("src/VM/VM.cpp")
|
||||
add_files("src/Repl/ReplTest.cpp")
|
||||
|
||||
target("Fig")
|
||||
add_files("src/Core/*.cpp")
|
||||
add_files("src/Error/Error.cpp")
|
||||
@@ -131,12 +63,6 @@ target("Fig")
|
||||
add_files("src/Parser/TypeExprParser.cpp")
|
||||
add_files("src/Parser/Parser.cpp")
|
||||
|
||||
add_files("src/Sema/Type.cpp")
|
||||
add_files("src/Sema/Analyzer.cpp")
|
||||
|
||||
add_files("src/Compiler/ExprCompiler.cpp")
|
||||
add_files("src/Compiler/StmtCompiler.cpp")
|
||||
add_files("src/Compiler/Compiler.cpp")
|
||||
add_files("src/Bytecode/Disassembler.cpp")
|
||||
|
||||
add_files("src/Object/Object.cpp")
|
||||
|
||||
Reference in New Issue
Block a user