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

167
src/LSP/LSPServer.hpp Normal file
View 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