feat: 增加一个很简单的repl(还不能用) 写了readme

This commit is contained in:
2026-03-13 01:28:43 +08:00
parent c0eacfd236
commit 91b5a0e384
7 changed files with 853 additions and 7 deletions

View File

@@ -46,7 +46,7 @@
#if SIZE_MAX == 18446744073709551615ull
#define __FCORE_ARCH "64"
#else
#else
#define __FCORE_ARCH "86"
#endif
@@ -56,12 +56,96 @@ namespace Fig
{
namespace Core
{
inline constexpr std::string_view VERSION = __FCORE_VERSION;
inline constexpr std::string_view LICENSE = "MIT";
inline constexpr std::string_view AUTHOR = "PuqiAR";
inline constexpr std::string_view PLATFORM = __FCORE_PLATFORM;
inline constexpr std::string_view COMPILER = __FCORE_COMPILER;
inline constexpr std::string_view VERSION = __FCORE_VERSION;
inline constexpr std::string_view LICENSE = "MIT";
inline constexpr std::string_view AUTHOR = "PuqiAR";
inline constexpr std::string_view PLATFORM = __FCORE_PLATFORM;
inline constexpr std::string_view COMPILER = __FCORE_COMPILER;
inline constexpr std::string_view COMPILE_TIME = __FCORE_COMPILE_TIME;
inline constexpr std::string_view ARCH = __FCORE_ARCH;
inline constexpr std::string_view ARCH = __FCORE_ARCH;
const std::string LOGO =
R"(
)";
const std::string LICENSE_TEXT =
R"(
MIT License (Fig)
Copyright (c) 2026 PuqiAR <im@puqiar.top>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
1. The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
2. This project includes code from the following projects with their respective licenses:
- argparse (MIT License)
Copyright (c) 2018 Pranav Srinivas Kumar <pranav.srinivas.kumar@gmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- magic_enum (MIT License)
Copyright (c) 2019 - 2024 Daniil Goncharov
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
- json (MIT LICENSE) (for LSP Server JSON-RPC)
Copyright (c) 2013-2026 Niels Lohmann
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
)";
}; // namespace Core
}; // namespace Fig

260
src/Repl/Repl.hpp Normal file
View File

@@ -0,0 +1,260 @@
/*!
@file src/Repl/Repl.hpp
@brief Repl定义
@author PuqiAR (im@puqiar.top)
@date 2026-03-11
*/
#pragma once
#include <Core/Core.hpp>
#include <Core/SourceLocations.hpp>
#include <SourceManager/SourceManager.hpp>
#include <Compiler/Compiler.hpp>
#include <Lexer/Lexer.hpp>
#include <Parser/Parser.hpp>
#include <Sema/Analyzer.hpp>
#include <VM/VM.hpp>
#include <Utils/ConsoleSize.hpp>
namespace Fig
{
class Repl
{
private:
size_t rline = 1;
std::istream &in;
std::ostream &out;
std::ostream &err;
public:
Repl(std::istream &_in, std::ostream &_out, std::ostream &_err) :
in(_in), out(_out), err(_err)
{
}
void PrintInfo()
{
out << std::format("Fig {} copyright (c) 2025-2026 PuqiAR, under the {} License",
Core::VERSION,
Core::LICENSE);
out << std::format("Build time: {} [{} x{} on {}]\n",
Core::COMPILE_TIME,
Core::COMPILER,
Core::ARCH,
Core::PLATFORM);
out << "Type '#exit' to exit, '#clear' to clear the the screen, '#license' to see the full license, '#logo' to see a GREAT logo\n";
}
void ClearConsole()
{
// \033[2J: 清除整个屏幕
// \033[H: 将光标移动到左上角
out << "\033[2J\033[H" << std::flush;
}
void PrintLicense()
{
static DynArray<std::string> license_lines{};
if (license_lines.empty())
{
std::string l;
bool last_was_r = false;
for (size_t i = 0; i < Core::LICENSE_TEXT.size(); ++i)
{
char c = Core::LICENSE_TEXT[i];
if (c == '\r')
{
last_was_r = true;
continue;
}
if (c == '\n')
{
if (!l.empty() || last_was_r)
{
license_lines.push_back(l);
l.clear();
}
last_was_r = false;
continue;
}
if (last_was_r)
{
if (!l.empty())
{
license_lines.push_back(l);
l.clear();
}
last_was_r = false;
}
l += c;
}
if (!l.empty() || last_was_r)
{
license_lines.push_back(l);
}
}
unsigned int lines_per_page = 50;
unsigned int page_printed = 0;
unsigned int lines_printed = 0;
unsigned int total_lines = license_lines.size();
while (true)
{
auto _csize = Utils::getConsoleSize();
if (_csize)
{
lines_per_page = static_cast<unsigned int>((*_csize).first * 0.75);
if (lines_per_page < 1)
lines_per_page = 1;
}
unsigned int lines_this_page =
std::min(lines_per_page, total_lines - lines_printed);
for (unsigned int i = 0; i < lines_this_page; ++i)
{
out << license_lines[lines_printed + i] << '\n';
}
lines_printed += lines_this_page;
page_printed++;
if (lines_printed >= total_lines)
{
break;
}
unsigned int pages_total = (total_lines + lines_per_page - 1) / lines_per_page;
out << "\n"
<< std::format(
"Press any key to continue... ({}/{})", page_printed, pages_total);
in.get();
out << '\n';
++rline;
}
}
void PrintError(const Error &error, const String &source)
{
err << "Oops! An error occurred!";
err << "🔥 " << 'E' << static_cast<int>(error.type) << ": " << error.message << '\n';
err << "Line " << rline << ", `" << source << "`\n";
err << "Suggestion: " << error.suggestion << '\n';
err << std::format("Thrower: {} ({}:{}:{})\n",
error.thrower_loc.function_name(),
error.thrower_loc.file_name(),
error.thrower_loc.line(),
error.thrower_loc.column());
}
unsigned int Start() // exit code: unsigned int
{
// TODO: 多行输入 Repl
PrintInfo();
String fileName = "[REPL]";
String filePath = "src/Repl/Repl.hpp";
SourceManager manager;
VM vm;
Value v = Value::GetNullInstance();
while (true)
{
std::string buf;
out << ">";
std::getline(in, buf);
if (buf.empty())
{
continue;
}
else if (buf == "#exit")
{
return 0;
}
else if (buf == "#clear")
{
ClearConsole();
continue;
}
else if (buf == "#license")
{
PrintLicense();
continue;
}
else if (buf == "#logo")
{
out << Core::LOGO << '\n';
continue;
}
String source(buf);
Lexer lexer(buf, fileName);
Parser parser(lexer, manager, fileName);
auto _program = parser.Parse();
if (!_program)
{
PrintError(_program.error(), source);
continue;
}
Program *program = *_program;
Analyzer analyzer(manager);
auto result = analyzer.Analyze(program);
if (!result)
{
PrintError(result.error(), source);
continue;
}
Compiler compiler(manager, analyzer.GetDiagnostics());
auto compile_result = compiler.Compile(program);
if (!compile_result)
{
PrintError(compile_result.error(), source);
continue;
}
CompiledModule *compiledModule = *compile_result;
auto exe_result = vm.Execute(compiledModule);
if (!exe_result)
{
PrintError(exe_result.error(), source);
continue;
}
v = *exe_result;
if (!v.IsNull())
{
out << v.ToString() << '\n';
}
}
return (v.IsInt() ? v.AsInt() : 0);
}
};
}; // namespace Fig

10
src/Repl/ReplTest.cpp Normal file
View File

@@ -0,0 +1,10 @@
#include <Repl/Repl.hpp>
#include <Core/Core.hpp>
int main()
{
using namespace Fig;
Repl repl(CoreIO::GetStdCin(), CoreIO::GetStdOut(), CoreIO::GetStdErr());
repl.Start();
}

88
src/Utils/ConsoleSize.hpp Normal file
View File

@@ -0,0 +1,88 @@
#include <optional>
#include <utility>
#ifdef _WIN32
#include <windows.h>
#else
#include <sys/ioctl.h>
#include <unistd.h>
#endif
namespace Fig::Utils
{
/**
* 获取控制台窗口的大小(行数和列数)
* @return 如果成功,返回包含 (rows, cols) 的 optional失败返回 std::nullopt
*/
inline std::optional<std::pair<int, int>> getConsoleSize()
{
#ifdef _WIN32
// Windows: GetConsoleScreenBufferInfo
CONSOLE_SCREEN_BUFFER_INFO csbi;
HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
if (hConsole == INVALID_HANDLE_VALUE)
{
return std::nullopt;
}
if (!GetConsoleScreenBufferInfo(hConsole, &csbi))
{
return std::nullopt;
}
// 窗口大小 = 右下角坐标 - 左上角坐标 + 1
int cols = csbi.srWindow.Right - csbi.srWindow.Left + 1;
int rows = csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
return std::make_pair(rows, cols);
#else
// Linux / macOS / Unix: ioctl
struct winsize w;
// 标准输出被重定向到文件 ?
if (!isatty(STDOUT_FILENO))
{
// 不是终端,获取环境变量
char *cols_env = getenv("COLUMNS");
char *rows_env = getenv("LINES");
if (cols_env && rows_env)
{
int cols = std::stoi(cols_env);
int rows = std::stoi(rows_env);
return std::make_pair(rows, cols);
}
return std::nullopt;
}
if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &w) == -1)
{
return std::nullopt;
}
int cols = w.ws_col;
int rows = w.ws_row;
// 如果 ws_col 或 ws_row 为 0使用环境变量
if (cols == 0 || rows == 0)
{
char *cols_env = getenv("COLUMNS");
char *rows_env = getenv("LINES");
if (cols_env)
cols = std::stoi(cols_env);
if (rows_env)
rows = std::stoi(rows_env);
}
if (cols > 0 && rows > 0)
{
return std::make_pair(rows, cols);
}
return std::nullopt;
#endif
}
} // namespace Fig::Utils