feat: 增加了analyzer, compiler不再分析并且报错, 只生产 bytecode, analyzer作为前端结束最后一道防线检查代码,同时引入一个简单的LSP

This commit is contained in:
2026-02-23 19:57:28 +08:00
parent 852dd27836
commit b7bb889676
28 changed files with 26665 additions and 3153 deletions

420
src/Sema/Analyzer.cpp Normal file
View File

@@ -0,0 +1,420 @@
/*!
@file src/Sema/Analyzer.hpp
@brief 前端类型检查器实现
@author PuqiAR (im@puqiar.top)
@date 2026-02-23
*/
#include <Sema/Analyzer.hpp>
namespace Fig
{
Result<void, Error> Analyzer::analyzeVarDecl(VarDecl *stmt)
{
if (env.Resolve(stmt->name) != std::nullopt)
{
return std::unexpected(Error(ErrorType::RedeclarationError,
std::format("variable `{}` has already defined in this scope", stmt->name),
"change its name",
makeSourceLocation(stmt)));
}
TypeTag initType = TypeTag::Any;
if (stmt->initExpr)
{
const auto &res = analyzeExpr(stmt->initExpr);
if (!res)
{
return res;
}
initType = stmt->initExpr->resolvedType;
}
TypeTag declaredType = TypeTag::Any;
if (stmt->typeSpecifier)
{
// TODO: 解析类型定义
}
if (stmt->isInfer)
{
declaredType = initType;
}
else if (declaredType != TypeTag::Any && declaredType != initType)
{
return std::unexpected(Error(ErrorType::TypeError,
std::format("cannot assign type `{}` to variable {} which speicifer type is '{}'",
magic_enum::enum_name(initType),
stmt->name,
magic_enum::enum_name(declaredType)),
"none",
makeSourceLocation(stmt->initExpr)));
}
env.Define(stmt->name, declaredType, stmt->isPublic, false);
return {};
}
Result<void, Error> Analyzer::analyzeIfStmt(IfStmt *stmt)
{
auto condRes = analyzeExpr(stmt->cond);
if (!condRes)
{
return condRes;
}
if (stmt->cond->resolvedType != TypeTag::Any && stmt->cond->resolvedType != TypeTag::Bool)
{
return std::unexpected(Error(
ErrorType::TypeError,
std::format("if condition must be boolean, got `{}`", magic_enum::enum_name(stmt->cond->resolvedType)),
"ensure condition is boolean",
makeSourceLocation(stmt->cond)
));
}
auto consequentRes = analyzeStmt(stmt->consequent);
if (!consequentRes)
{
return consequentRes;
}
for (ElseIfStmt *elif : stmt->elifs)
{
auto condRes = analyzeExpr(elif->cond);
if (elif->cond->resolvedType != TypeTag::Any
&& elif->cond->resolvedType != TypeTag::Bool)
{
return std::unexpected(Error(ErrorType::TypeError,
std::format("else if condition must be boolean, got `{}`",
magic_enum::enum_name(elif->cond->resolvedType)),
"ensure condition is boolean",
makeSourceLocation(elif->cond)));
}
auto consequentRes = analyzeStmt(elif->consequent);
if (!consequentRes)
{
return consequentRes;
}
}
if (stmt->alternate)
{
auto alternateRes = analyzeStmt(stmt->alternate);
if (!alternateRes)
{
return alternateRes;
}
}
return {};
}
Result<void, Error> Analyzer::analyzeIdentiExpr(IdentiExpr *expr)
{
auto sym = env.Resolve(expr->name);
if (sym == std::nullopt)
{
return std::unexpected(Error(ErrorType::UseUndeclaredIdentifier,
std::format("`{}` has not been defined", expr->name),
"none",
makeSourceLocation(expr)));
}
// TODO: 引入 Module 跨文件 import检查 isPublic
expr->resolvedType = sym->type;
expr->resolvedDepth = sym->depth;
return {};
}
Result<void, Error> Analyzer::analyzeInfixExpr(InfixExpr *expr)
{
auto resL = analyzeExpr(expr->left);
if (!resL)
return std::unexpected(resL.error());
auto resR = analyzeExpr(expr->right);
if (!resR)
return std::unexpected(resR.error());
TypeTag lType = expr->left->resolvedType;
TypeTag rType = expr->right->resolvedType;
switch (expr->op)
{
// 1. 算术族 (+, -, *, /, **)
case BinaryOperator::Add:
if (lType == TypeTag::String && rType == TypeTag::String)
{
expr->resolvedType = TypeTag::String;
break;
}
[[fallthrough]];
case BinaryOperator::Subtract:
case BinaryOperator::Multiply:
case BinaryOperator::Divide:
case BinaryOperator::Power:
if (lType == TypeTag::Int && rType == TypeTag::Int)
{
expr->resolvedType = TypeTag::Int;
}
else if ((lType == TypeTag::Int || lType == TypeTag::Double)
&& (rType == TypeTag::Int || rType == TypeTag::Double))
{
expr->resolvedType = TypeTag::Double;
}
else if (lType == TypeTag::Any || rType == TypeTag::Any)
{
expr->resolvedType = TypeTag::Any;
}
else
{
return std::unexpected(Error(ErrorType::TypeError,
"invalid operands for arithmetic operation",
"ensure both sides are numbers (Int or Double)",
makeSourceLocation(expr->right)));
}
break;
// 2. 整数特化族 (%, &, |, ^, <<, >>)
case BinaryOperator::Modulo:
case BinaryOperator::BitAnd:
case BinaryOperator::BitOr:
case BinaryOperator::BitXor:
case BinaryOperator::ShiftLeft:
case BinaryOperator::ShiftRight:
if (lType == TypeTag::Int && rType == TypeTag::Int)
{
expr->resolvedType = TypeTag::Int;
}
else if (lType == TypeTag::Any || rType == TypeTag::Any)
{
expr->resolvedType = TypeTag::Any;
}
else
{
return std::unexpected(Error(ErrorType::TypeError,
"bitwise and modulo operations require Int operands",
"cast operands to Int before operation",
makeSourceLocation(expr->right)));
}
break;
// 3. 比较族 (==, !=, <, >, <=, >=)
case BinaryOperator::Equal:
case BinaryOperator::NotEqual:
case BinaryOperator::Less:
case BinaryOperator::Greater:
case BinaryOperator::LessEqual:
case BinaryOperator::GreaterEqual:
case BinaryOperator::Is:
if (lType != TypeTag::Any && rType != TypeTag::Any && lType != rType) // lType == rType放行
{
if (!((lType == TypeTag::Int && rType == TypeTag::Double)
|| (lType == TypeTag::Double && rType == TypeTag::Int)))
{
return std::unexpected(Error(ErrorType::TypeError,
"cannot compare different types",
"ensure both sides of the comparison are of the same type",
makeSourceLocation(expr)));
}
}
// TODO: 支持Struct后进行检查右操作数是 Struct才合理
// 如 1.2 is Int --> false
// 1 is Int --> true
expr->resolvedType = TypeTag::Bool;
break;
// 4. 逻辑族 (&&, ||)
case BinaryOperator::LogicalAnd:
case BinaryOperator::LogicalOr:
if (lType == TypeTag::Bool && rType == TypeTag::Bool)
{
expr->resolvedType = TypeTag::Bool;
}
else if (lType == TypeTag::Any || rType == TypeTag::Any)
{
expr->resolvedType = TypeTag::Bool;
}
else
{
return std::unexpected(Error(ErrorType::TypeError,
"logical operators require Bool operands",
"use boolean expressions",
makeSourceLocation(expr)));
}
break;
// 5. 纯赋值与复合赋值族 (=, +=, -=, ...)
case BinaryOperator::Assign:
case BinaryOperator::AddAssign:
case BinaryOperator::SubAssign:
case BinaryOperator::MultiplyAssign:
case BinaryOperator::DivideAssign:
case BinaryOperator::ModuloAssign:
case BinaryOperator::BitXorAssign:
// 左侧必须是合法的 L-Value
if (!isValidLvalue(expr->left))
{
return std::unexpected(
Error(ErrorType::NotAnLvalue,
"invalid assignment target",
"left side must be a variable, property, or indexable target",
makeSourceLocation(expr->left) // 错误精准定位到左侧节点
));
}
// 类型匹配拦截 (纯赋值)
if (expr->op == BinaryOperator::Assign)
{
if (lType != TypeTag::Any && rType != TypeTag::Any && lType != rType)
{
if (!(lType == TypeTag::Double && rType == TypeTag::Int))
{ // 允许 Int 赋给 Double
return std::unexpected(Error(ErrorType::TypeError,
"cannot assign value to variable of different type",
"ensure the assigned value matches the declared type",
makeSourceLocation(expr->right)));
}
}
}
expr->resolvedType = lType;
break;
// 6. 成员访问 (.)
case BinaryOperator::MemberAccess:
if (lType != TypeTag::Struct && lType != TypeTag::Any)
{
return std::unexpected(Error(ErrorType::TypeError,
"member access requires a Struct object",
"check if the left side evaluates to an object",
makeSourceLocation(expr->left)));
}
expr->resolvedType = TypeTag::Any;
break;
default:
return std::unexpected(Error(ErrorType::TypeError,
"unknown binary operator in static analysis",
"this is likely an internal compiler error",
makeSourceLocation(expr)));
}
return {};
}
Result<void, Error> Analyzer::analyzeStmt(Stmt *stmt)
{
if (!stmt)
return {};
switch (stmt->type)
{
case AstType::VarDecl: return analyzeVarDecl(static_cast<VarDecl *>(stmt));
case AstType::ExprStmt: {
auto *exprStmt = static_cast<ExprStmt *>(stmt);
return analyzeExpr(exprStmt->expr); // 表达式语句只需要推导内部表达式即可
}
case AstType::BlockStmt: {
auto *block = static_cast<BlockStmt *>(stmt);
env.EnterScope(); // 进入新大括号,作用域深度 +1
for (auto *s : block->nodes)
{
auto res = analyzeStmt(s);
if (!res)
return std::unexpected(res.error());
}
env.LeaveScope(); // 离开大括号,自动销毁局部类型记录
return {};
}
case AstType::IfStmt: {
return analyzeIfStmt(static_cast<IfStmt *>(stmt));
}
// TODO: 其他语句分析
// default:
// return std::unexpected(Error(ErrorType::TypeError,
// "unsupported statement type in analyzer",
// "internal compiler error",
// makeSourceLocation(stmt)));
}
return {};
}
Result<void, Error> Analyzer::analyzeExpr(Expr *expr)
{
if (!expr)
return {};
switch (expr->type)
{
case AstType::LiteralExpr: {
auto *lit = static_cast<LiteralExpr *>(expr);
switch(lit->token.type)
{
case TokenType::LiteralTrue:
case TokenType::LiteralFalse:
lit->resolvedType = TypeTag::Bool;
break;
case TokenType::LiteralNull:
lit->resolvedType = TypeTag::Null;
break;
case TokenType::LiteralNumber: {
const String &lexeme = manager.GetSub(lit->token.index, lit->token.length);
if (lexeme.contains(U'.') || lexeme.contains(U'e'))
{
lit->resolvedType = TypeTag::Double;
}
else
{
lit->resolvedType = TypeTag::Int;
}
break;
}
case TokenType::LiteralString: {
lit->resolvedType = TypeTag::String;
break;
}
default:
lit->resolvedType = TypeTag::Any;
break;
}
return {};
}
case AstType::IdentiExpr: return analyzeIdentiExpr(static_cast<IdentiExpr *>(expr));
case AstType::InfixExpr:
return analyzeInfixExpr(static_cast<InfixExpr *>(expr));
// TODO: PrefixExpr (前缀), CallExpr (函数调用), MemberExpr (属性访问)
default:
// 对于还没实现的表达式,默认降级为 Any 防止崩溃
expr->resolvedType = TypeTag::Any;
return {};
}
}
Result<void, Error> Analyzer::Analyze(Program *program)
{
for (auto *stmt : program->nodes)
{
auto res = analyzeStmt(stmt);
if (!res)
return std::unexpected(res.error()); // 遇到任何错误,立刻中断并向上传递
}
return {};
}
}; // namespace Fig

70
src/Sema/Analyzer.hpp Normal file
View File

@@ -0,0 +1,70 @@
/*!
@file src/Sema/Analyzer.hpp
@brief 前端类型检查器定义
@author PuqiAR (im@puqiar.top)
@date 2026-02-23
*/
#pragma once
#include <Sema/Environment.hpp>
#include <Sema/Type.hpp>
#include <Ast/Ast.hpp>
#include <Deps/Deps.hpp>
namespace Fig
{
class Analyzer
{
private:
Environment env;
SourceManager &manager;
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<void, Error> analyzeVarDecl(VarDecl *);
Result<void, Error> analyzeIfStmt(IfStmt *);
Result<void, Error> analyzeIdentiExpr(IdentiExpr *);
Result<void, Error> analyzeInfixExpr(InfixExpr *);
Result<void, Error> analyzeStmt(Stmt *);
Result<void, Error> analyzeExpr(Expr *);
public:
Result<void, Error> Analyze(Program *);
Analyzer(SourceManager &_manager) : manager(_manager) {}
};
}; // namespace Fig

50
src/Sema/AnalyzerTest.cpp Normal file
View File

@@ -0,0 +1,50 @@
#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 <iostream>
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::cerr << "Read file failed \n";
return 1;
}
Lexer lexer(manager.GetSource(), fileName);
Parser parser(lexer, manager, fileName);
const 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;
}

80
src/Sema/Environment.hpp Normal file
View File

@@ -0,0 +1,80 @@
/*!
@file src/Sema/Environment.hpp
@brief 符号和作用域环境定义
@author PuqiAR (im@puqiar.top)
@date 2026-02-23
*/
#pragma once
#include <Sema/Type.hpp>
#include <Deps/Deps.hpp>
#include <Error/Error.hpp>
#include <optional>
namespace Fig
{
// 记录在 Analyzer 中的符号元数据
struct Symbol
{
String name;
TypeTag type;
bool isPublic;
int depth; // 词法作用域深度
bool isConstant; // 是否是 const 声明的不可变常量 (用于报错: 尝试修改常量)
};
class Environment
{
private:
DynArray<Symbol> symbols;
int currentDepth = 0;
public:
void EnterScope()
{
currentDepth++;
}
void LeaveScope()
{
while (!symbols.empty() && symbols.back().depth > currentDepth)
{
symbols.pop_back();
}
currentDepth--;
}
// 注册符号, 调用前确保无重复 (内部assert)
void Define(const String &name, TypeTag type, bool isPublic, bool isConst)
{
for (auto it = symbols.rbegin(); it != symbols.rend(); ++it)
{
if (it->depth < currentDepth)
break;
if (it->name == name)
{
assert(false && "Environment.Define: redefinition");
}
}
symbols.push_back({name, type, isPublic, currentDepth, isConst});
}
// 解析符号。找不到返回 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 currentDepth;
}
};
} // namespace Fig

28
src/Sema/Type.hpp Normal file
View File

@@ -0,0 +1,28 @@
/*!
@file src/Sema/Type.hpp
@brief 前端类型检查的类型定义
@author PuqiAR (im@puqiar.top)
@date 2026-02-23
*/
#pragma once
#include <cstdint>
namespace Fig
{
enum class TypeTag : std::uint8_t
{
Any, // 动态类型底线
Null, // 空值
Int,
Double,
Bool,
String,
Function,
Struct,
};
// TODO: 复杂类型的推导(泛型,结构体)
// 添加 TypeInfo 结构体,目前先用 TypeTag
};