重构类型系统并改进诊断功能

- 更新了类型系统,新增了类型并优化了结构。
- 引入了基类型和派生类,用于函数、结构体和接口类型。
- 实现了类型上下文,用于管理内置类型和类型解析。
- 添加了诊断类,用于收集和报告警告和错误。
- 通过改进错误处理增强了虚拟机执行,以应对递归限制问题。
- 实现了反汇编器,将字节码转换为代码,以改善调试和分析。
- 添加了新的抽象语法树节点,用于成员表达式、对象初始化、接口和结构体定义。
- 引入了语义错误测试,包括重定义、未声明的变量和无效的结构字段。
This commit is contained in:
2026-03-10 12:33:17 +08:00
parent 90448006ff
commit 0f635ccf2b
47 changed files with 2365 additions and 2541 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -1,100 +1,53 @@
/*!
@file src/Sema/Analyzer.hpp
@brief 前端类型检查器定义
@author PuqiAR (im@puqiar.top)
@date 2026-02-23
@brief 语义分析器定义
*/
#pragma once
#include <Sema/Environment.hpp>
#include <Sema/Type.hpp>
#include <Ast/Ast.hpp>
#include <Deps/Deps.hpp>
#include <Sema/Type.hpp>
#include <Sema/Environment.hpp>
#include <Utils/Arena.hpp>
#include <Error/Diagnostics.hpp>
#include <SourceManager/SourceManager.hpp>
namespace Fig
{
class Analyzer
{
private:
Environment env;
Arena arena;
SourceManager &manager;
TypeContext typeCtx;
Environment env;
Diagnostics diag;
TypeContext typeCtx;
HashMap<String, BaseType*> globalTypes;
HashMap<String, Symbol*> globalSymbols;
TypeInfo *currentReturnType = nullptr; // 正在分析的函数,预期返回类型
bool hasInit = false;
bool hasMain = false;
struct ReturnTypeProtector
{
Analyzer *analyzer;
TypeInfo *prevCurrentReturnType;
// 核心递归查找:解决跨越函数边界的捕获问题
Result<Symbol*, Error> resolveSymbolInternal(const String &name, const SourceLocation &loc, Scope* startScope);
[[nodiscard]]
ReturnTypeProtector(Analyzer *_analyzer, TypeInfo *current) :
analyzer(_analyzer), prevCurrentReturnType(_analyzer->currentReturnType)
{
analyzer->currentReturnType = current;
}
Result<Type, Error> resolveTypeExpr(TypeExpr *texpr);
Result<void, Error> pass1(Program *prog);
Result<void, Error> resolveTypes(Program *prog);
Result<void, Error> checkBodies(Program *prog);
~ReturnTypeProtector()
{
analyzer->currentReturnType = prevCurrentReturnType;
}
ReturnTypeProtector(const ReturnTypeProtector &) = delete;
ReturnTypeProtector &operator=(const ReturnTypeProtector &) = delete;
};
SourceLocation makeSourceLocation(
AstNode *ast, std::source_location loc = std::source_location::current())
{
return SourceLocation(ast->location.sp,
ast->location.fileName,
"[internal analyzer]",
loc.function_name());
}
bool isValidLvalue(Expr *expr)
{
if (expr->type == AstType::IdentiExpr)
{
return true;
}
if (expr->type == AstType::InfixExpr)
{
InfixExpr *infix = static_cast<InfixExpr *>(expr);
if (infix->op == BinaryOperator::MemberAccess)
{
return true;
}
}
if (expr->type == AstType::IndexExpr)
{
return true;
}
return false;
}
Result<TypeInfo *, Error> resolveType(TypeExpr *);
Result<void, Error> analyzeVarDecl(VarDecl *);
Result<void, Error> analyzeIfStmt(IfStmt *);
Result<void, Error> analyzeWhileStmt(WhileStmt *);
Result<void, Error> analyzeFnDefStmt(FnDefStmt *);
Result<void, Error> analyzeReturnStmt(ReturnStmt *);
Result<void, Error> analyzeIdentiExpr(IdentiExpr *);
Result<void, Error> analyzeInfixExpr(InfixExpr *);
Result<void, Error> analyzeCallExpr(CallExpr *);
Result<void, Error> analyzeStmt(Stmt *);
Result<void, Error> analyzeExpr(Expr *);
Result<void, Error> analyzeStmt(Stmt *stmt);
Result<Type, Error> analyzeExpr(Expr *expr);
int addUpvalue(Scope *scope, Symbol *target, bool isLocal);
public:
Result<void, Error> Analyze(Program *);
Analyzer(SourceManager &m) : manager(m) {}
Analyzer(SourceManager &_manager) : manager(_manager), typeCtx() {}
Result<void, Error> Analyze(Program *prog);
Diagnostics& GetDiagnostics() { return diag; }
TypeContext& GetTypeContext() { return typeCtx; }
};
}; // namespace Fig
}

View File

@@ -1,50 +1,59 @@
#include <Sema/Analyzer.hpp>
#include <Deps/Deps.hpp>
#include <Error/Error.hpp>
#include <Lexer/Lexer.hpp>
#include <Parser/Parser.hpp>
#include <SourceManager/SourceManager.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));
Parser parser(lexer, srcManager, String(path));
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()
{
using namespace Fig;
const String &fileName = "test.fig";
const String &filePath = "T:/Files/Maker/Code/MyCodingLanguage/The Fig Project/Fig/test.fig";
SourceManager manager(filePath);
manager.Read();
if (!manager.read)
std::string testDir = "T:/Files/Maker/Code/MyCodingLanguage/The Fig Project/Fig/tests/Sema";
for (const auto &entry : fs::directory_iterator(testDir))
{
std::cerr << "Read file failed \n";
return 1;
if (entry.path().extension() == ".fig")
{
runTest(entry.path().string());
}
}
Lexer lexer(manager.GetSource(), fileName);
Parser parser(lexer, manager, fileName);
auto result = parser.Parse();
if (!result)
{
ReportError(result.error(), manager);
return 1;
}
Program *program = *result;
Analyzer analyzer(manager);
const auto &analyzeResult = analyzer.Analyze(program);
if (!analyzeResult)
{
ReportError(analyzeResult.error(), manager);
return 1;
}
std::cout << "Analyze successfully, PROGRAM OK\n";
return 0;
}
}

View File

@@ -1,147 +1,74 @@
/*!
@file src/Sema/Environment.hpp
@brief 符号和作用域环境定义
@author PuqiAR (im@puqiar.top)
@date 2026-02-23
@brief 树状符号表定义
*/
#pragma once
#include <Deps/Deps.hpp>
#include <Error/Error.hpp>
#include <Sema/Type.hpp>
#include <cassert>
#include <optional>
namespace Fig
{
// 记录在 Analyzer 中的符号元数据
enum class SymbolLocation
{
Global,
Local,
Upvalue
};
struct Symbol
{
String name;
TypeInfo *type;
bool isPublic;
int depth; // 词法作用域深度
bool isConstant; // 是否是 const 声明的不可变常量 (用于报错: 尝试修改常量)
String name;
Type type;
SymbolLocation location;
int index;
bool isConst;
int localId = -1; // Analyzer 虚拟槽位分配
Symbol(String n, Type t, SymbolLocation l, int i, bool c) :
name(std::move(n)), type(t), location(l), index(i), isConst(c)
{
}
};
// 作用域回档水位线
struct ScopeWatermark
struct UpvalueCapture
{
std::size_t symbolCount;
int savedLocalId;
Symbol *target;
int index;
bool isLocal;
};
// 语义分析函数上下文 (隔离局部变量 ID 空间)
struct SemaFuncState
struct Scope
{
SemaFuncState *enclosing = nullptr;
int currentDepth = 0;
int nextLocalId = 0;
DynArray<ScopeWatermark> scopeStack;
Scope *parent = nullptr;
bool isFunctionBoundary = 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;
}
};
class Environment
{
private:
DynArray<Symbol> symbols;
SemaFuncState *current = nullptr;
public:
Environment()
Scope *current = nullptr;
void Push(bool isFn)
{
current = new SemaFuncState();
current = new Scope(current, isFn);
}
~Environment()
void Pop()
{
while (current)
{
SemaFuncState *prev = current->enclosing;
delete current;
current = prev;
}
}
// 函数边界控
void EnterFunction()
{
SemaFuncState *newState = new SemaFuncState();
newState->enclosing = current;
current = newState;
}
void LeaveFunction()
{
assert(current && "Environment: Unmatched LeaveFunction");
SemaFuncState *oldState = current;
current = oldState->enclosing;
delete oldState;
}
// 词法作用域控制
void EnterScope()
{
current->currentDepth++;
current->scopeStack.push_back({symbols.size(), current->nextLocalId});
}
void LeaveScope()
{
assert(current->currentDepth > 0 && "Environment: Unmatched LeaveScope");
current->currentDepth--;
assert(!current->scopeStack.empty());
ScopeWatermark archive = current->scopeStack.back();
current->scopeStack.pop_back();
// 物理截断符号表,回滚槽位发号器以复用物理寄存器
while (symbols.size() > archive.symbolCount)
{
symbols.pop_back();
}
current->nextLocalId = archive.savedLocalId;
}
// 符号操作
// 注册符号, 返回分配的 localId。调用前内部执行同级作用域重定义断言
int Define(const String &name, TypeInfo *type, bool isPublic, bool isConst)
{
for (auto it = symbols.rbegin(); it != symbols.rend(); ++it)
{
if (it->depth < current->currentDepth)
break;
if (it->name == name)
{
assert(false && "Environment.Define: redefinition");
}
}
int allocatedId = current->nextLocalId++;
symbols.push_back({name, type, isPublic, current->currentDepth, isConst, allocatedId});
return allocatedId;
}
// 解析符号。找不到返回 nullopt
std::optional<Symbol> Resolve(const String &name)
{
for (auto it = symbols.rbegin(); it != symbols.rend(); ++it)
{
if (it->name == name)
return *it;
}
return std::nullopt;
}
int GetDepth() const
{
return current->currentDepth;
Scope *old = current;
current = current->parent;
delete old;
}
};
} // namespace Fig
} // namespace Fig

93
src/Sema/Type.cpp Normal file
View File

@@ -0,0 +1,93 @@
/*!
@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

View File

@@ -1,171 +1,115 @@
/*!
@file src/Sema/Type.hpp
@brief 前端类型检查的类型定义和类型驻留池
@author PuqiAR (im@puqiar.top)
@date 2026-02-23
@brief 类型系统定义:对齐 NaN-boxing 物理布局
*/
#pragma once
#include <Deps/Deps.hpp>
#include <cstdint>
#include <Error/Error.hpp>
namespace Fig
{
enum class TypeTag : std::uint8_t
{
Any, // 动态类型底线
Null, // 空值
Int,
Double,
Bool,
String,
Bool,
Null,
Any,
Function,
Struct,
Interface
};
struct TypeInfo
class BaseType;
struct Type
{
BaseType *base = 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 isAssignableTo(const Type &target) const;
};
class BaseType
{
public:
TypeTag tag;
String name; // 完整路径序列化, 如 Int, std.file.File
String name;
BaseType(TypeTag t, String n) : tag(t), name(std::move(n)) {}
virtual ~BaseType() = default;
};
bool isAny() const
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)
{
return tag == TypeTag::Any;
}
bool isNull() const
{
return tag == TypeTag::Null;
}
bool isInt() const
{
return tag == TypeTag::Int;
}
bool isDouble() const
{
return tag == TypeTag::Double;
}
bool isBool() const
{
return tag == TypeTag::Bool;
}
bool isString() const
{
return tag == TypeTag::String;
}
bool isFunction() const
{
return tag == TypeTag::Function;
}
bool isStruct() const
{
return tag == TypeTag::Struct;
}
};
// 全局唯一类型驻留池
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, class 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;
}
};
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)) {}
};
class TypeContext
{
private:
DynArray<TypeInfo *> allTypes;
// 缓存
TypeInfo *typeAny;
TypeInfo *typeNull;
TypeInfo *typeInt;
TypeInfo *typeDouble;
TypeInfo *typeBool;
TypeInfo *typeString;
TypeInfo *typeFunction;
TypeInfo *typeStruct;
public:
TypeInfo *GetAny()
{
return typeAny;
}
TypeInfo *GetNull()
{
return typeNull;
}
TypeInfo *GetInt()
{
return typeInt;
}
TypeInfo *GetDouble()
{
return typeDouble;
}
TypeInfo *GetBool()
{
return typeBool;
}
TypeInfo *GetString()
{
return typeString;
}
TypeInfo *GetFunction()
{
return typeFunction;
}
TypeInfo *GetStruct()
{
return typeStruct;
}
DynArray<BaseType *> allTypes;
BaseType *intType, *doubleType, *stringType, *boolType, *anyType, *nullType;
TypeInfo *ResolveTypePath(const String &fullName)
{
for (auto *t : allTypes)
{
if (t->name == fullName)
return t;
}
return nullptr; // 没找到该类型
}
TypeInfo *ResolveTypePath(const DynArray<String> &path)
{
// TODO: 支持Module 系统, 查 Module 的导出表
String fullName = path.empty() ? "" : path[0];
for (size_t i = 1; i < path.size(); ++i)
{
fullName += "." + path[i];
}
return ResolveTypePath(fullName);
}
~TypeContext()
{
for (TypeInfo *t : allTypes)
delete t;
}
TypeContext()
{
typeAny = createBuiltin(TypeTag::Any, "Any");
typeNull = createBuiltin(TypeTag::Null, "Null");
typeInt = createBuiltin(TypeTag::Int, "Int");
typeDouble = createBuiltin(TypeTag::Double, "Double");
typeBool = createBuiltin(TypeTag::Bool, "Bool");
typeString = createBuiltin(TypeTag::String, "String");
typeFunction = createBuiltin(TypeTag::Function, "Function");
typeStruct = createBuiltin(TypeTag::Struct, "Struct");
}
private:
TypeInfo *createBuiltin(TypeTag tag, String name)
{
TypeInfo *t = new TypeInfo{tag, std::move(name)};
allTypes.push_back(t);
return t;
}
TypeContext();
~TypeContext();
Type GetBasic(TypeTag tag, bool nullable = false);
Type CreateFuncType(DynArray<Type> params, Type ret);
};
}; // namespace Fig
} // namespace Fig