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:
|
public:
|
||||||
Compiler(SourceManager &m, Diagnostics &d) : manager(m), diag(d) {}
|
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,
|
Function,
|
||||||
Struct,
|
Struct,
|
||||||
Instance,
|
Instance,
|
||||||
|
TypeObj,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct StructObject /* : public Object */; // 结构体基类的定义,前向声明
|
struct StructObject /* : public Object */; // 结构体基类的定义,前向声明
|
||||||
@@ -235,6 +236,37 @@ namespace Fig
|
|||||||
{
|
{
|
||||||
return type == ObjectType::Instance;
|
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
|
} // namespace Fig
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
|
|
||||||
#include <Ast/Operator.hpp>
|
#include <Ast/Operator.hpp>
|
||||||
#include <Object/ObjectBase.hpp>
|
#include <Object/ObjectBase.hpp>
|
||||||
|
#include <Sema/Type.hpp>
|
||||||
|
|
||||||
namespace Fig
|
namespace Fig
|
||||||
{
|
{
|
||||||
@@ -23,33 +24,29 @@ namespace Fig
|
|||||||
// + 6 bytes padding
|
// + 6 bytes padding
|
||||||
};
|
};
|
||||||
*/
|
*/
|
||||||
|
struct FieldMeta
|
||||||
|
{
|
||||||
|
String name;
|
||||||
|
Type type;
|
||||||
|
};
|
||||||
|
|
||||||
struct StructObject final : public Object
|
struct StructObject final : public Object
|
||||||
{
|
{
|
||||||
String name; // 元信息(仅供调试/打印/反射)
|
String name;
|
||||||
|
std::uint8_t fieldCount;
|
||||||
// 内存布局信息
|
FieldMeta *fields; // [fieldCount]
|
||||||
std::uint8_t fieldCount;
|
Object *operators[GetOperatorsSize()];
|
||||||
Object *operators[GetOperatorsSize()];
|
// operators: [UnaryOp 0..N][BinaryOp 0..N], nullptr = 无重载
|
||||||
/*
|
|
||||||
运算符重载,nullptr代表无重载
|
|
||||||
一般为 NativeFunction / Function
|
|
||||||
|
|
||||||
排列:
|
|
||||||
[unary operators ]( binary operators]
|
|
||||||
0 - UnaryOperators::Count BinaryOperators::Count
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
Object *GetUnaryOperator(UnaryOperator _op)
|
Object *GetUnaryOperator(UnaryOperator _op)
|
||||||
{
|
{
|
||||||
std::uint8_t idx = static_cast<std::uint8_t>(_op);
|
return operators[static_cast<std::uint8_t>(_op)];
|
||||||
return operators[idx];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Object *GetBinaryOperator(BinaryOperator _op)
|
Object *GetBinaryOperator(BinaryOperator _op)
|
||||||
{
|
{
|
||||||
std::uint16_t idx = static_cast<std::uint8_t>(UnaryOperator::Count) + static_cast<std::uint8_t>(_op);
|
return operators[static_cast<std::uint8_t>(UnaryOperator::Count)
|
||||||
return operators[idx];
|
+ static_cast<std::uint8_t>(_op)];
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}; // namespace Fig
|
}; // 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
|
@file src/Sema/Analyzer.hpp
|
||||||
@brief 语义分析器定义
|
@brief 语义分析
|
||||||
|
@author PuqiAR (im@puqiar.top)
|
||||||
|
@date 2026-06-06
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <Ast/Ast.hpp>
|
#include <Ast/Ast.hpp>
|
||||||
#include <Sema/Type.hpp>
|
#include <Sema/Type.hpp>
|
||||||
#include <Sema/Environment.hpp>
|
|
||||||
#include <Utils/Arena.hpp>
|
|
||||||
#include <Error/Diagnostics.hpp>
|
#include <Error/Diagnostics.hpp>
|
||||||
#include <SourceManager/SourceManager.hpp>
|
#include <SourceManager/SourceManager.hpp>
|
||||||
|
|
||||||
@@ -16,38 +16,17 @@ namespace Fig
|
|||||||
{
|
{
|
||||||
class Analyzer
|
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:
|
public:
|
||||||
Analyzer(SourceManager &m) : manager(m) {}
|
Analyzer(SourceManager &) {}
|
||||||
|
|
||||||
Result<void, Error> Analyze(Program *prog);
|
Result<void, Error> Analyze(Program *)
|
||||||
|
{
|
||||||
Diagnostics& GetDiagnostics() { return diag; }
|
return {};
|
||||||
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
|
@file src/Sema/Environment.hpp
|
||||||
@brief 树状符号表定义
|
@brief 符号表
|
||||||
|
@author PuqiAR (im@puqiar.top)
|
||||||
|
@date 2026-07-05
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
@@ -10,49 +12,30 @@
|
|||||||
|
|
||||||
namespace Fig
|
namespace Fig
|
||||||
{
|
{
|
||||||
enum class SymbolLocation
|
enum class SymbolKind : uint8_t
|
||||||
{
|
{
|
||||||
Global,
|
Var,
|
||||||
Local,
|
Const,
|
||||||
Upvalue
|
Func,
|
||||||
|
Type,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Symbol
|
struct Symbol
|
||||||
{
|
{
|
||||||
String name;
|
String name;
|
||||||
Type type;
|
Type type;
|
||||||
SymbolLocation location;
|
SymbolKind kind;
|
||||||
int index;
|
int index; // local: register idx, global: global idx, upvalue: upvalue idx
|
||||||
bool isConst;
|
|
||||||
|
|
||||||
Symbol(String n, Type t, SymbolLocation l, int i, bool c) :
|
bool isType() const { return kind == SymbolKind::Type; }
|
||||||
name(std::move(n)), type(t), location(l), index(i), isConst(c)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct UpvalueCapture
|
|
||||||
{
|
|
||||||
Symbol *target;
|
|
||||||
int index;
|
|
||||||
bool isLocal;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Scope
|
struct Scope
|
||||||
{
|
{
|
||||||
Scope *parent = nullptr;
|
Scope *parent = nullptr;
|
||||||
bool isFunctionBoundary = false;
|
bool isFnBoundary = false;
|
||||||
|
|
||||||
HashMap<String, Symbol *> locals;
|
HashMap<String, Symbol *> locals;
|
||||||
DynArray<UpvalueCapture> upvalues;
|
int nextReg = 0;
|
||||||
|
|
||||||
int nextLocalId = 0;
|
|
||||||
|
|
||||||
Scope(Scope *p, bool isFn) : parent(p), isFunctionBoundary(isFn)
|
|
||||||
{
|
|
||||||
if (p && !isFn)
|
|
||||||
nextLocalId = p->nextLocalId;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class Environment
|
class Environment
|
||||||
@@ -60,14 +43,20 @@ namespace Fig
|
|||||||
public:
|
public:
|
||||||
Scope *current = nullptr;
|
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;
|
current = current->parent;
|
||||||
delete old;
|
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
|
@file src/Sema/Type.hpp
|
||||||
@brief 类型系统定义:对齐 NaN-boxing 物理布局
|
@brief 类型系统
|
||||||
|
@author PuqiAR (im@puqiar.top)
|
||||||
|
@date 2026-07-05
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <Deps/Deps.hpp>
|
|
||||||
#include <Error/Error.hpp>
|
#include <Object/ObjectBase.hpp>
|
||||||
|
|
||||||
namespace Fig
|
namespace Fig
|
||||||
{
|
{
|
||||||
enum class TypeTag : std::uint8_t
|
|
||||||
{
|
|
||||||
Int,
|
|
||||||
Double,
|
|
||||||
String,
|
|
||||||
Bool,
|
|
||||||
Null,
|
|
||||||
Any,
|
|
||||||
Function,
|
|
||||||
Struct,
|
|
||||||
Interface
|
|
||||||
};
|
|
||||||
|
|
||||||
class BaseType;
|
|
||||||
|
|
||||||
struct Type
|
struct Type
|
||||||
{
|
{
|
||||||
BaseType *base = nullptr;
|
TypeObject *obj = nullptr;
|
||||||
bool isNullable = false;
|
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;
|
bool isAssignableTo(const Type &target) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
class BaseType
|
inline bool Type::is(TypeTag t) const
|
||||||
{
|
{
|
||||||
public:
|
return obj && obj->tag == t;
|
||||||
TypeTag tag;
|
}
|
||||||
String name;
|
|
||||||
BaseType(TypeTag t, String n) : tag(t), name(std::move(n)) {}
|
|
||||||
virtual ~BaseType() = default;
|
|
||||||
|
|
||||||
bool operator==(const BaseType &other) const
|
inline bool Type::isAssignableTo(const Type &target) const
|
||||||
{
|
|
||||||
return tag == other.tag && name == other.name;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class FuncType : public BaseType
|
|
||||||
{
|
{
|
||||||
public:
|
if (target.is(TypeTag::Any) || is(TypeTag::Any))
|
||||||
DynArray<Type> paramTypes;
|
return true;
|
||||||
Type retType;
|
if (is(TypeTag::Null) && target.isNullable)
|
||||||
FuncType(DynArray<Type> params, Type ret) :
|
return true;
|
||||||
BaseType(TypeTag::Function, "Function"), paramTypes(std::move(params)), retType(ret)
|
return obj == target.obj && (!isNullable || target.isNullable);
|
||||||
{
|
}
|
||||||
}
|
|
||||||
|
|
||||||
bool operator==(const FuncType &other) const
|
|
||||||
{
|
|
||||||
return paramTypes == other.paramTypes && retType == other.retType;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
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
|
} // namespace Fig
|
||||||
|
|||||||
74
xmake.lua
74
xmake.lua
@@ -50,74 +50,6 @@ target("ObjectTest")
|
|||||||
add_files("src/Object/Object.cpp")
|
add_files("src/Object/Object.cpp")
|
||||||
add_files("src/Object/ObjectTest.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")
|
target("Fig")
|
||||||
add_files("src/Core/*.cpp")
|
add_files("src/Core/*.cpp")
|
||||||
add_files("src/Error/Error.cpp")
|
add_files("src/Error/Error.cpp")
|
||||||
@@ -130,13 +62,7 @@ target("Fig")
|
|||||||
add_files("src/Parser/StmtParser.cpp")
|
add_files("src/Parser/StmtParser.cpp")
|
||||||
add_files("src/Parser/TypeExprParser.cpp")
|
add_files("src/Parser/TypeExprParser.cpp")
|
||||||
add_files("src/Parser/Parser.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/Bytecode/Disassembler.cpp")
|
||||||
|
|
||||||
add_files("src/Object/Object.cpp")
|
add_files("src/Object/Object.cpp")
|
||||||
|
|||||||
Reference in New Issue
Block a user