feat: 增加repl入口,-r/--repl。 添加计时选项。 -- 我发现一个问题,analyzer没法保存环境。完了。

This commit is contained in:
2026-04-30 21:24:11 +08:00
parent fafa2b4946
commit 98de782760
7 changed files with 274 additions and 58 deletions

View File

@@ -75,7 +75,7 @@ namespace Fig
stream << std::format(" [{}] {}\n", i, proto->constants[i].ToString());
}
}
stream << "--- End Disassembly ---";
stream << "--- End Disassembly ---\n";
}
Disassembler::Format Disassembler::GetFormat(OpCode op)

View File

@@ -1,6 +1,6 @@
/*!
@file src/Compiler/ExprCompiler.cpp
@brief 表达式编译器实现:引入水位线(Watermark)与零拷贝复用机制
@brief 表达式编译器实现:水位线控制Register与零拷贝复用机制
*/
#include <Ast/Expr/CallExpr.hpp>
@@ -19,46 +19,69 @@ namespace Fig
char buffer[128];
int j = 0;
bool isFloat = false;
for (size_t i = 0; i < raw.length() && j < 127; ++i)
{
char32_t c = raw[i];
if (c == '_')
continue;
// 检查开头的无效字符
if (j == 0 && (c == '.' || c == 'e' || c == 'E'))
return std::unexpected(
Error(ErrorType::SyntaxError, "unexpected leading character", "", loc));
if (c == '.' || c == 'e' || c == 'E')
isFloat = true;
buffer[j++] = (char) c;
}
buffer[j] = '\0';
if (isFloat)
{
double dVal;
auto [ptr, ec] = std::from_chars(buffer, buffer + j, dVal);
if (ec != std::errc())
return std::unexpected(Error(ErrorType::SyntaxError, "float overflow", "", loc));
return Value::FromDouble(dVal);
}
else
{
// 检查16进制/2进制前缀
bool isHexOrBin = false;
int base = 10;
const char *start = buffer;
if (j > 2 && buffer[0] == '0')
if (j >= 2 && buffer[0] == '0')
{
if (buffer[1] == 'x' || buffer[1] == 'X')
{
base = 16;
start += 2;
isHexOrBin = true;
}
else if (buffer[1] == 'b' || buffer[1] == 'B')
{
base = 2;
start += 2;
isHexOrBin = true;
}
}
if (isFloat)
{
// 如果既有浮点标记又是0x开头可能是16进制浮点
auto fmt =
(isHexOrBin && base == 16) ? std::chars_format::hex : std::chars_format::general;
double dVal;
auto [ptr, ec] = std::from_chars(start, buffer + j, dVal, fmt);
if (ec != std::errc() || ptr != buffer + j)
return std::unexpected(
Error(ErrorType::SyntaxError, "invalid float literal", "", loc));
return Value::FromDouble(dVal);
}
else if (isHexOrBin)
{
// 16进制或2进制整数
int64_t iVal;
auto [ptr, ec] = std::from_chars(start, buffer + j, iVal, base);
if (ec != std::errc())
return std::unexpected(Error(ErrorType::SyntaxError, "integer overflow", "", loc));
if (ec != std::errc() || ptr != buffer + j)
return std::unexpected(
Error(ErrorType::SyntaxError, "integer overflow or invalid literal", "", loc));
if (iVal >= std::numeric_limits<int32_t>::min()
&& iVal <= std::numeric_limits<int32_t>::max())
@@ -70,6 +93,27 @@ namespace Fig
return Value::FromDouble(static_cast<double>(iVal));
}
}
else
{
// 10进制数字可能是整数或浮点数
double dVal;
auto [ptr, ec] = std::from_chars(start, buffer + j, dVal, std::chars_format::general);
if (ec != std::errc() || ptr != buffer + j)
return std::unexpected(
Error(ErrorType::SyntaxError, "invalid number literal", "", loc));
// 检查是否是整数(没有小数部分且不超出int32范围)
if (dVal == std::floor(dVal) && dVal >= std::numeric_limits<int32_t>::min()
&& dVal <= std::numeric_limits<int32_t>::max())
{
return Value::FromInt(static_cast<int32_t>(dVal));
}
else
{
return Value::FromDouble(dVal);
}
}
}
Result<Register, Error> Compiler::compileExpr(Expr *expr, Register target)
@@ -287,31 +331,62 @@ namespace Fig
OpCode op;
switch (in->op)
{
case BinaryOperator::Add: op = isInt ? OpCode::IntFastAdd : OpCode::Add; break;
case BinaryOperator::Subtract:
op = isInt ? OpCode::IntFastSub : OpCode::Sub;
case BinaryOperator::Add: {
op = (isInt ? (OpCode::IntFastAdd) : (OpCode::Add));
break;
case BinaryOperator::Multiply:
op = isInt ? OpCode::IntFastMul : OpCode::Mul;
}
case BinaryOperator::Subtract: {
op = (isInt ? (OpCode::IntFastSub) : (OpCode::Sub));
break;
case BinaryOperator::Divide:
op = isInt ? OpCode::IntFastDiv : OpCode::Div;
}
case BinaryOperator::Multiply: {
op = (isInt ? (OpCode::IntFastMul) : (OpCode::Mul));
break;
case BinaryOperator::Modulo: op = OpCode::Mod; break;
case BinaryOperator::BitXor: op = OpCode::BitXor; break;
case BinaryOperator::Equal: op = OpCode::Equal; break;
case BinaryOperator::NotEqual: op = OpCode::NotEqual; break;
case BinaryOperator::Greater: op = OpCode::Greater; break;
case BinaryOperator::Less: op = OpCode::Less; break;
case BinaryOperator::GreaterEqual: op = OpCode::GreaterEqual; break;
case BinaryOperator::LessEqual: op = OpCode::LessEqual; break;
default:
}
case BinaryOperator::Divide: {
op = (isInt ? (OpCode::IntFastDiv) : (OpCode::Div));
break;
}
case BinaryOperator::Modulo: {
op = OpCode::Mod;
break;
}
case BinaryOperator::BitXor: {
op = OpCode::BitXor;
break;
}
case BinaryOperator::Equal: {
op = OpCode::Equal;
break;
}
case BinaryOperator::NotEqual: {
op = OpCode::NotEqual;
break;
}
case BinaryOperator::Greater: {
op = OpCode::Greater;
break;
}
case BinaryOperator::Less: {
op = OpCode::Less;
break;
}
case BinaryOperator::GreaterEqual: {
op = OpCode::GreaterEqual;
break;
}
case BinaryOperator::LessEqual: {
op = OpCode::LessEqual;
break;
}
default: {
return std::unexpected(Error(
ErrorType::InternalError,
"unsupported binary operator",
"",
in->location));
}
}
// 释放左右操作数产生的临时寄存器
current->freereg = mark;

View File

@@ -38,15 +38,18 @@ namespace Fig
void PrintInfo()
{
out << std::format("Fig {}, copyright (c) 2025-2026 PuqiAR, under the {} License\n",
out << std::format(
"Fig {}, copyright (c) 2025-2026 PuqiAR, under the {} License\n",
Core::VERSION,
Core::LICENSE);
out << std::format("Build time: {} [{} x{} on {}]\n",
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";
out << '\n';
}
void ClearConsole()
@@ -146,17 +149,58 @@ namespace Fig
++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());
// }
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());
static constexpr const char *MinorColor = "\033[38;2;138;227;198m";
static constexpr const char *MediumColor = "\033[38;2;255;199;95m";
static constexpr const char *CriticalColor = "\033[38;2;255;107;107m";
namespace TC = TerminalColors;
std::ostream &err = CoreIO::GetStdErr();
uint8_t level = ErrorLevel(error.type);
// const char *level_name = (level == 1 ? "Minor" : (level == 2 ? "Medium" :
// "Critical"));
const char *const &level_color =
(level == 1 ? MinorColor : (level == 2 ? MediumColor : CriticalColor));
err << "\n";
err << "🔥 "
<< level_color
//<< '(' << level_name << ')'
<< 'E' << static_cast<int>(error.type) << TC::Reset << ": " << level_color
<< ErrorTypeToString(error.type) << TC::Reset << '\n';
const SourceLocation &location = error.location;
err << TC::DarkGray << " ┌─> Fn " << TC::Cyan << '\'' << location.packageName << '.'
<< location.functionName << '\'' << " " << location.fileName << " (" << TC::DarkGray
<< location.sp.line << ":" << location.sp.column << TC::Cyan << ')' << TC::Reset
<< '\n';
err << TC::DarkGray << "" << '\n' << "" << TC::Reset << '\n';
err << TC::DarkGray << " └─ " << TC::Reset;
err << source << "\n";
err << "\n";
err << "" << TC::DarkGray << "Thrower: " << error.thrower_loc.function_name() << " ("
<< error.thrower_loc.file_name() << ":" << error.thrower_loc.line() << ")"
<< TC::Reset << "\n";
err << "💡 " << TC::Blue << "Suggestion: " << error.suggestion << TC::Reset;
err << '\n';
}
unsigned int Start() // exit code: unsigned int

View File

@@ -7,6 +7,7 @@
#include <VM/Entry.hpp>
#include <chrono>
#include <filesystem>
#include <Core/Core.hpp>
@@ -16,16 +17,50 @@
#include <Compiler/Compiler.hpp>
#include <Lexer/Lexer.hpp>
#include <Parser/Parser.hpp>
#include <Repl/Repl.hpp>
#include <Sema/Analyzer.hpp>
#include <VM/VM.hpp>
namespace Fig::Entry
{
void RunFromPath(const String &path, const Config &conf)
{
namespace fs = std::filesystem;
using clock = std::chrono::steady_clock;
auto format_print_time = [](std::chrono::nanoseconds nsecs) {
auto &out = CoreIO::GetStdOut();
auto count = nsecs.count();
auto old_flags = out.flags();
auto old_precision = out.precision();
if (count < 1'000)
{
// < 1μs 纳秒
out << count << "ns";
}
else if (count < 1'000'000)
{
// 1μs ~ 1ms 微秒 保留 2 位小数
out << std::fixed << std::setprecision(2) << (count / 1'000.0) << "μs";
}
else if (count < 1'000'000'000)
{
// 1ms ~ 1s 毫秒 保留 2 位小数
out << std::fixed << std::setprecision(2) << (count / 1'000'000.0) << "ms";
}
else
{
// >= 1s 秒 保留 3 位小数
out << std::fixed << std::setprecision(3) << (count / 1'000'000'000.0) << "s";
}
out.flags(old_flags);
out.precision(old_precision);
};
fs::path _fspath(path.toStdString());
if (!fs::exists(_fspath))
@@ -59,16 +94,23 @@ namespace Fig::Entry
Parser parser(lexer, manager, fileName, diagnostics);
auto parse_start = clock::now();
auto parse_result = parser.Parse();
auto parse_end = clock::now();
if (!parse_result)
{
ReportError(parse_result.error(), manager);
std::exit(1);
}
Program *program = *parse_result;
Analyzer analyer(manager);
auto analyze_start = clock::now();
auto analyze_result = analyer.Analyze(program);
auto analyze_end = clock::now();
if (!analyze_result)
{
@@ -78,7 +120,10 @@ namespace Fig::Entry
Compiler compiler(manager, diagnostics);
auto compile_start = clock::now();
auto compile_result = compiler.Compile(program);
auto compile_end = clock::now();
diagnostics.EmitAll(manager);
if (!compile_result)
@@ -97,7 +142,10 @@ namespace Fig::Entry
VM vm;
auto execute_start = clock::now();
auto execute_result = vm.Execute(compiledModule);
auto execute_end = clock::now();
if (!execute_result)
{
ReportError(execute_result.error(), manager);
@@ -109,6 +157,43 @@ namespace Fig::Entry
vm.PrintRegisters();
}
if (conf.time)
{
auto parse_time = parse_end - parse_start;
CoreIO::GetStdOut() << "Parse: ";
format_print_time(parse_time);
CoreIO::GetStdOut() << " | ";
auto analyze_time = analyze_end - analyze_start;
CoreIO::GetStdOut() << "Analyze: ";
format_print_time(analyze_time);
CoreIO::GetStdOut() << " | ";
auto compile_time = compile_end - compile_start;
CoreIO::GetStdOut() << "Compile: ";
format_print_time(compile_time);
CoreIO::GetStdOut() << " | ";
auto execute_time = execute_end - execute_start;
CoreIO::GetStdOut() << "Execute: ";
format_print_time(execute_time);
CoreIO::GetStdOut() << " | ";
auto total = parse_time + analyze_time + compile_time + execute_time;
CoreIO::GetStdOut() << "Total: ";
format_print_time(total);
CoreIO::GetStdOut() << '\n';
}
delete compiledModule;
}
std::uint32_t RunRepl()
{
Repl repl(CoreIO::GetStdCin(), CoreIO::GetStdOut(), CoreIO::GetStdErr());
std::uint32_t result = repl.Start();
CoreIO::GetStdOut() << "Repl exited with code " << result << '\n';
return result;
}
}; // namespace Fig::Entry

View File

@@ -18,6 +18,9 @@ namespace Fig::Entry
} mode;
bool dump;
bool pregs;
bool time;
};
void RunFromPath(const String &, const Config &conf);
std::uint32_t RunRepl();
};

View File

@@ -329,7 +329,7 @@ namespace Fig
Op::iAsBx(OpCode::Exit_MaxRecursionDepthExceeded, 0, 0);
return &POISON_MAX_RECURSION_DEPTH_EXCEED_INST;
}
[[likely]]
*currentFrame = CallFrame{nullptr, proto, proto->code.data(), base};
return currentFrame->ip;
}
@@ -343,7 +343,7 @@ namespace Fig
Op::iAsBx(OpCode::Exit_MaxRecursionDepthExceeded, 0, 0);
return &POISON_MAX_RECURSION_DEPTH_EXCEED_INST;
}
[[likely]]
*currentFrame = CallFrame{closure, closure->proto, closure->proto->code.data(), base};
return currentFrame->ip;
}

View File

@@ -19,11 +19,13 @@ int main(int argc, char **argv)
ArgParser::ArgumentParser argparser("Fig", "Fig Toolchain");
argparser.AddFlag('r', "repl");
argparser.AddFlag('h', "help").Help("Print the help message");
argparser.AddFlag('v', "version").Help("Show toolchain version");
argparser.AddFlag("license").Help("Print the license text");
argparser.AddFlag("dump").Help("Dump the bytecode");
argparser.AddFlag("pregs").Help("Print vm non-null registers");
argparser.AddFlag("time").Help("Print the execution time");
auto res = argparser.Parse(argc, argv);
if (!res)
@@ -34,11 +36,13 @@ int main(int argc, char **argv)
auto &args = *res;
bool runRepl = args.HasFlag("repl");
bool showHelp = args.HasFlag("help");
bool showVersion = args.HasFlag("version");
bool showLicense = args.HasFlag("license");
bool dump = args.HasFlag("dump");
bool pregs = args.HasFlag("pregs");
bool time = args.HasFlag("time");
if (showHelp)
{
@@ -72,6 +76,11 @@ int main(int argc, char **argv)
return 0;
}
if (runRepl)
{
return Entry::RunRepl();
}
if (posSize > 1)
{
err << "Error: Too more positionals, expect 1. Use Fig [Fig source code file (.fig)]\n";
@@ -83,7 +92,7 @@ int main(int argc, char **argv)
return 1;
}
Entry::Config config{.mode = Entry::Config::Normal, .dump = dump, .pregs = pregs};
Entry::Config config{.mode = Entry::Config::Normal, .dump = dump, .pregs = pregs, .time = time};
const String &path = positionals.front();
Entry::RunFromPath(path, config);