feat: 增加了analyzer, compiler不再分析并且报错, 只生产 bytecode, analyzer作为前端结束最后一道防线检查代码,同时引入一个简单的LSP
This commit is contained in:
@@ -9,6 +9,8 @@
|
||||
#include <Core/SourceLocations.hpp>
|
||||
#include <Deps/Deps.hpp>
|
||||
|
||||
#include <Sema/Type.hpp>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace Fig
|
||||
@@ -49,6 +51,10 @@ namespace Fig
|
||||
|
||||
struct Expr : public AstNode
|
||||
{
|
||||
TypeTag resolvedType = TypeTag::Any;
|
||||
// TODO: 可选的常量折叠
|
||||
// 拓展 isConstExpr和 constValue槽位
|
||||
|
||||
Expr()
|
||||
{
|
||||
type = AstType::Expr;
|
||||
|
||||
@@ -16,6 +16,10 @@ namespace Fig
|
||||
{
|
||||
String name;
|
||||
|
||||
// Analyzer槽位,存储具体深度
|
||||
int resolvedDepth = -1; // 代表未解析
|
||||
bool isGlobal = false; // 是否全局/对外公开 (isPublic)
|
||||
|
||||
IdentiExpr()
|
||||
{
|
||||
type = AstType::IdentiExpr;
|
||||
|
||||
@@ -23,6 +23,7 @@ namespace Fig
|
||||
|
||||
IndexExpr(Expr *_base, Expr *_index) : base(_base), index(_index)
|
||||
{
|
||||
type = AstType::IndexExpr;
|
||||
location = base->location;
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ namespace Fig
|
||||
{
|
||||
String name;
|
||||
Expr *typeSpecifier;
|
||||
bool isInfer; // 是否用了 := 类型推断
|
||||
Expr *initExpr;
|
||||
|
||||
VarDecl()
|
||||
@@ -23,9 +24,10 @@ namespace Fig
|
||||
type = AstType::VarDecl;
|
||||
}
|
||||
|
||||
VarDecl(bool _isPublic, String _name, Expr *_typeSpecifier, Expr *_initExpr, SourceLocation _location) :
|
||||
VarDecl(bool _isPublic, String _name, Expr *_typeSpecifier, bool _isInfer, Expr *_initExpr, SourceLocation _location) :
|
||||
name(std::move(_name)),
|
||||
typeSpecifier(_typeSpecifier),
|
||||
isInfer(_isInfer),
|
||||
initExpr(_initExpr) // location 指向关键字 var/const位置
|
||||
{
|
||||
type = AstType::VarDecl;
|
||||
|
||||
@@ -11,13 +11,13 @@ namespace Fig
|
||||
{
|
||||
Result<std::uint8_t, Error> Compiler::CompileIdentiExpr(IdentiExpr *ie)
|
||||
{
|
||||
if (!HasLocal(ie->name))
|
||||
{
|
||||
return std::unexpected(Error(ErrorType::UseUndeclaredIdentifier,
|
||||
std::format("`{}` has not been defined", ie->name),
|
||||
"none",
|
||||
makeSourceLocation(ie)));
|
||||
}
|
||||
// if (!HasLocal(ie->name))
|
||||
// {
|
||||
// return std::unexpected(Error(ErrorType::UseUndeclaredIdentifier,
|
||||
// std::format("`{}` has not been defined", ie->name),
|
||||
// "none",
|
||||
// makeSourceLocation(ie)));
|
||||
// }
|
||||
return ResolveLocal(ie->name);
|
||||
}
|
||||
Result<std::uint8_t, Error> Compiler::CompileLiteral(
|
||||
|
||||
@@ -12,13 +12,13 @@ namespace Fig
|
||||
Result<void, Error> Compiler::CompileVarDecl(VarDecl *varDecl)
|
||||
{
|
||||
const String &name = varDecl->name;
|
||||
if (HasLocalInCurrentScope(name))
|
||||
{
|
||||
return std::unexpected(Error(ErrorType::RedeclarationError,
|
||||
std::format("variable `{}` has already defined in this scope", name),
|
||||
"change its name",
|
||||
makeSourceLocation(varDecl)));
|
||||
}
|
||||
// if (HasLocalInCurrentScope(name))
|
||||
// {
|
||||
// return std::unexpected(Error(ErrorType::RedeclarationError,
|
||||
// std::format("variable `{}` has already defined in this scope", name),
|
||||
// "change its name",
|
||||
// makeSourceLocation(varDecl)));
|
||||
// }
|
||||
std::uint8_t varReg;
|
||||
if (varDecl->initExpr)
|
||||
{
|
||||
|
||||
@@ -55,6 +55,7 @@ namespace Fig
|
||||
case RedeclarationError: return "RedeclarationError";
|
||||
case UseUndeclaredIdentifier: return "UseUndeclaredIdentifier";
|
||||
case NotAnLvalue: return "NotAnLvalue";
|
||||
case TypeError: return "TypeError";
|
||||
// default: return "Some one forgot to add case to `ErrorTypeToString`";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,10 +42,11 @@ namespace Fig
|
||||
ExpectedExpression,
|
||||
SyntaxError,
|
||||
|
||||
// compiler errors
|
||||
// analyzer errors
|
||||
RedeclarationError,
|
||||
UseUndeclaredIdentifier,
|
||||
NotAnLvalue
|
||||
NotAnLvalue,
|
||||
TypeError,
|
||||
};
|
||||
|
||||
const char *ErrorTypeToString(ErrorType type);
|
||||
|
||||
19
src/LSP/FigLSPServer.cpp
Normal file
19
src/LSP/FigLSPServer.cpp
Normal file
@@ -0,0 +1,19 @@
|
||||
#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;
|
||||
}
|
||||
167
src/LSP/LSPServer.hpp
Normal file
167
src/LSP/LSPServer.hpp
Normal file
@@ -0,0 +1,167 @@
|
||||
#pragma once
|
||||
|
||||
#include <Ast/Ast.hpp>
|
||||
#include <Lexer/Lexer.hpp>
|
||||
#include <Parser/Parser.hpp>
|
||||
#include <Sema/Analyzer.hpp>
|
||||
|
||||
#include <Error/Error.hpp>
|
||||
#include <SourceManager/SourceManager.hpp>
|
||||
|
||||
#include <charconv> // C++17/20 字符串转数字
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#include <Utils/json/json.hpp>
|
||||
using json = nlohmann::json;
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
|
||||
class LspServer
|
||||
{
|
||||
public:
|
||||
void Run()
|
||||
{
|
||||
std::ios_base::sync_with_stdio(false);
|
||||
std::cin.tie(NULL);
|
||||
|
||||
while (true)
|
||||
{
|
||||
std::string line;
|
||||
if (!std::getline(std::cin, line))
|
||||
break; // 退出循环
|
||||
|
||||
// 解析 HTTP 风格的 Header: "Content-Length: 123\r"
|
||||
if (line.starts_with("Content-Length: "))
|
||||
{
|
||||
std::size_t length = 0;
|
||||
// 使用 C++ 极速的 from_chars 替代 stoi
|
||||
auto res = std::from_chars(line.data() + 16, line.data() + line.size(), length);
|
||||
if (res.ec != std::errc())
|
||||
continue;
|
||||
|
||||
// 消费 Header 和 Body 之间那行空的 "\r\n"
|
||||
std::string emptyLine;
|
||||
std::getline(std::cin, emptyLine);
|
||||
|
||||
// 读取对应字节的 JSON Body
|
||||
std::string jsonBody(length, '\0');
|
||||
std::cin.read(jsonBody.data(), length);
|
||||
|
||||
// 派发给路由
|
||||
HandleMessage(jsonBody);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void HandleMessage(const std::string &rawJson)
|
||||
{
|
||||
json req = json::parse(rawJson, nullptr, false);
|
||||
if (req.is_discarded())
|
||||
return; // 忽略解析失败的报文
|
||||
|
||||
std::string method = req.value("method", "");
|
||||
|
||||
if (method == "initialize")
|
||||
{
|
||||
Respond(req["id"], R"({
|
||||
"capabilities": {
|
||||
"textDocumentSync": 1,
|
||||
"hoverProvider": true
|
||||
}
|
||||
})");
|
||||
}
|
||||
else if (method == "textDocument/didOpen")
|
||||
{
|
||||
std::string uri = req["params"]["textDocument"]["uri"];
|
||||
std::string text = req["params"]["textDocument"]["text"];
|
||||
PublishDiagnostics(uri, text);
|
||||
}
|
||||
else if (method == "textDocument/didChange")
|
||||
{
|
||||
std::string uri = req["params"]["textDocument"]["uri"];
|
||||
std::string text = req["params"]["contentChanges"][0]["text"];
|
||||
PublishDiagnostics(uri, text);
|
||||
}
|
||||
}
|
||||
|
||||
void Respond(int id, const std::string &resultJsonString)
|
||||
{
|
||||
std::string response = "{\"jsonrpc\":\"2.0\",\"id\":" + std::to_string(id)
|
||||
+ ",\"result\":" + resultJsonString + "}";
|
||||
std::cout << "Content-Length: " << response.size() << "\r\n\r\n"
|
||||
<< response << std::flush;
|
||||
}
|
||||
|
||||
void SendDiagnostics(const std::string &uri, const Error *err = nullptr)
|
||||
{
|
||||
json diagnostics = json::array(); // 默认空数组,代码无错时擦除红线
|
||||
|
||||
if (err)
|
||||
{
|
||||
// LSP 规定行列号必须从 0 开始算
|
||||
int startLine = err->location.sp.line - 1;
|
||||
int startChar = err->location.sp.column - 1;
|
||||
int endLine = startLine;
|
||||
int endChar = startChar + err->location.sp.tok_length;
|
||||
|
||||
std::string fullMessage = err->message.toStdString();
|
||||
if (!err->suggestion.empty())
|
||||
{
|
||||
fullMessage += " 💡suggestion: " + err->suggestion.toStdString();
|
||||
}
|
||||
|
||||
diagnostics.push_back(
|
||||
{{"range",
|
||||
{{"start", {{"line", startLine}, {"character", startChar}}},
|
||||
{"end", {{"line", endLine}, {"character", endChar}}}}},
|
||||
{"severity", 1}, // 1 = 致命错误红线
|
||||
{"source", "Fig LSP Server"},
|
||||
{"message", fullMessage}});
|
||||
}
|
||||
|
||||
// 组装 Notification
|
||||
json notification = {{"jsonrpc", "2.0"},
|
||||
{"method", "textDocument/publishDiagnostics"},
|
||||
{"params", {{"uri", uri}, {"diagnostics", diagnostics}}}};
|
||||
|
||||
std::string response = notification.dump();
|
||||
std::cout << "Content-Length: " << response.size() << "\r\n\r\n"
|
||||
<< response << std::flush;
|
||||
}
|
||||
|
||||
void PublishDiagnostics(const std::string &uri, const std::string &sourceCode)
|
||||
{
|
||||
SourceManager manager;
|
||||
manager.LoadFromMemory(sourceCode);
|
||||
|
||||
Lexer lexer(sourceCode, "");
|
||||
Parser parser(lexer, manager, "");
|
||||
|
||||
// 1. 语法检查拦截
|
||||
auto parserResult = parser.Parse();
|
||||
if (!parserResult)
|
||||
{
|
||||
SendDiagnostics(uri, &parserResult.error());
|
||||
return;
|
||||
}
|
||||
|
||||
Program *program = *parserResult;
|
||||
|
||||
Analyzer analyzer(manager);
|
||||
|
||||
// 语义检查拦截
|
||||
auto analyzerResult = analyzer.Analyze(program);
|
||||
if (!analyzerResult)
|
||||
{
|
||||
SendDiagnostics(uri, &analyzerResult.error());
|
||||
return;
|
||||
}
|
||||
|
||||
// 3. 一切完美,发射空数组清空过去的错误红线
|
||||
SendDiagnostics(uri, nullptr);
|
||||
}
|
||||
};
|
||||
} // namespace Fig
|
||||
@@ -64,7 +64,7 @@ namespace Fig
|
||||
String value; // 用于判断是标识符还是关键字
|
||||
value.push_back(rd.produce()); // 加入第一个
|
||||
|
||||
while (CharUtils::isIdentifierContinue(rd.current())) // continue: _ / 0-9 / aA - zZ
|
||||
while (!rd.isAtEnd() && CharUtils::isIdentifierContinue(rd.current())) // continue: _ / 0-9 / aA - zZ
|
||||
{
|
||||
tok.length++;
|
||||
value.push_back(rd.produce());
|
||||
|
||||
@@ -165,12 +165,12 @@ namespace Fig
|
||||
// 让 VM 的 OP_EQ 指令极简:`if (RA == RB)`
|
||||
[[nodiscard]] constexpr bool operator==(const Value &other) const
|
||||
{
|
||||
// IEEE 754 规定浮点数有 +0.0 == -0.0 的特殊规则
|
||||
// IEEE 754 规定浮点数有 +0.0 == -0.0 的特殊规则,所以不直接比对raw bits而是转换成 double进行C++比对
|
||||
if (IsDouble() && other.IsDouble())
|
||||
{
|
||||
return AsDouble() == other.AsDouble();
|
||||
}
|
||||
// 直接比较 64 位整数内存
|
||||
// 直接比较 64 位整数内存,所以堆对象比较为地址(运算符重载由Compiler处理)
|
||||
return v_ == other.v_;
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,12 @@ namespace Fig
|
||||
{
|
||||
return std::unexpected(result.error());
|
||||
}
|
||||
program->nodes.push_back(*result);
|
||||
Stmt *stmt = *result;
|
||||
if (!stmt)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
program->nodes.push_back(stmt);
|
||||
}
|
||||
return program;
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ namespace Fig
|
||||
Expr *typeSpeicifer = nullptr;
|
||||
if (match(TokenType::Colon)) // `:`
|
||||
{
|
||||
const auto &result = parseExpression(0, TokenType::Assign);
|
||||
const auto &result = parseExpression(0, TokenType::Assign, TokenType::Walrus);
|
||||
if (!result)
|
||||
{
|
||||
return std::unexpected(result.error());
|
||||
@@ -61,6 +61,7 @@ namespace Fig
|
||||
}
|
||||
|
||||
Expr *initExpr = nullptr;
|
||||
bool isInfer = false;
|
||||
if (match(TokenType::Assign))
|
||||
{
|
||||
const auto &result = parseExpression();
|
||||
@@ -70,11 +71,30 @@ namespace Fig
|
||||
}
|
||||
initExpr = *result;
|
||||
}
|
||||
else if (match(TokenType::Walrus)) // :=
|
||||
{
|
||||
if (typeSpeicifer) // 指定了类型同时使用 :=
|
||||
{
|
||||
return std::unexpected(Error(
|
||||
ErrorType::SyntaxError,
|
||||
"used type infer but specifying the type",
|
||||
"change `:=` to '='",
|
||||
makeSourceLocation(prevToken()) // :=
|
||||
));
|
||||
}
|
||||
const auto &result = parseExpression();
|
||||
if (!result)
|
||||
{
|
||||
return std::unexpected(result.error());
|
||||
}
|
||||
initExpr = *result;
|
||||
isInfer = true; // 使用类型自动推断 :=
|
||||
}
|
||||
if (!match(TokenType::Semicolon))
|
||||
{
|
||||
makeExpectSemicolonError();
|
||||
return std::unexpected(makeExpectSemicolonError());
|
||||
}
|
||||
VarDecl *varDecl = new VarDecl(isPublic, name, typeSpeicifer, initExpr, location);
|
||||
VarDecl *varDecl = new VarDecl(isPublic, name, typeSpeicifer, isInfer, initExpr, location);
|
||||
return varDecl;
|
||||
}
|
||||
|
||||
@@ -237,7 +257,11 @@ namespace Fig
|
||||
return parseIfStmt();
|
||||
}
|
||||
|
||||
const auto &expr_result = parseExpression(0);
|
||||
if (isEOF)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
const auto &expr_result = parseExpression();
|
||||
if (!expr_result)
|
||||
{
|
||||
return std::unexpected(expr_result.error());
|
||||
|
||||
420
src/Sema/Analyzer.cpp
Normal file
420
src/Sema/Analyzer.cpp
Normal 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
70
src/Sema/Analyzer.hpp
Normal 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
50
src/Sema/AnalyzerTest.cpp
Normal 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
80
src/Sema/Environment.hpp
Normal 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
28
src/Sema/Type.hpp
Normal 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
|
||||
};
|
||||
@@ -100,6 +100,11 @@ namespace Fig
|
||||
return source;
|
||||
}
|
||||
|
||||
String &GetSource()
|
||||
{
|
||||
return source;
|
||||
}
|
||||
|
||||
std::pair<size_t, size_t> GetLineColumn(size_t index) const
|
||||
{
|
||||
if (lineStartIndex.empty())
|
||||
@@ -130,5 +135,12 @@ namespace Fig
|
||||
size_t column = index - lineStartIndex[line] + 1;
|
||||
return {line + 1, column};
|
||||
}
|
||||
|
||||
void LoadFromMemory(String src)
|
||||
{
|
||||
source = std::move(src);
|
||||
read = true;
|
||||
preprocessLineIndices();
|
||||
}
|
||||
};
|
||||
}; // namespace Fig
|
||||
25526
src/Utils/json/json.hpp
Normal file
25526
src/Utils/json/json.hpp
Normal file
File diff suppressed because it is too large
Load Diff
11
src/main.cpp
11
src/main.cpp
@@ -4,6 +4,7 @@
|
||||
#include <Lexer/Lexer.hpp>
|
||||
#include <Parser/Parser.hpp>
|
||||
#include <SourceManager/SourceManager.hpp>
|
||||
#include <Sema/Analyzer.hpp>
|
||||
#include <VM/VM.hpp>
|
||||
|
||||
#include <iostream>
|
||||
@@ -37,6 +38,16 @@ int main()
|
||||
}
|
||||
Program *program = *program_result;
|
||||
|
||||
Analyzer analyzer(manager);
|
||||
const auto &analyzeResult = analyzer.Analyze(program);
|
||||
if (!analyzeResult)
|
||||
{
|
||||
ReportError(analyzeResult.error(), manager);
|
||||
return 1;
|
||||
}
|
||||
std::cout << "analyzer: Program OK, PASSED\n";
|
||||
|
||||
|
||||
Compiler compiler(fileName, manager);
|
||||
const auto &proto_result = compiler.Compile(program);
|
||||
if (!proto_result)
|
||||
|
||||
Reference in New Issue
Block a user