feat: 增加repl入口,-r/--repl。 添加计时选项。 -- 我发现一个问题,analyzer没法保存环境。完了。
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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';
|
||||
|
||||
// 检查16进制/2进制前缀
|
||||
bool isHexOrBin = false;
|
||||
int base = 10;
|
||||
const char *start = buffer;
|
||||
|
||||
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(buffer, buffer + j, dVal);
|
||||
if (ec != std::errc())
|
||||
return std::unexpected(Error(ErrorType::SyntaxError, "float overflow", "", loc));
|
||||
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
|
||||
else if (isHexOrBin)
|
||||
{
|
||||
int base = 10;
|
||||
const char *start = buffer;
|
||||
if (j > 2 && buffer[0] == '0')
|
||||
{
|
||||
if (buffer[1] == 'x' || buffer[1] == 'X')
|
||||
{
|
||||
base = 16;
|
||||
start += 2;
|
||||
}
|
||||
else if (buffer[1] == 'b' || buffer[1] == 'B')
|
||||
{
|
||||
base = 2;
|
||||
start += 2;
|
||||
}
|
||||
}
|
||||
// 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,30 +331,61 @@ 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));
|
||||
}
|
||||
}
|
||||
|
||||
// 释放左右操作数产生的临时寄存器
|
||||
|
||||
@@ -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
|
||||
@@ -208,7 +252,7 @@ namespace Fig
|
||||
|
||||
String source(buf);
|
||||
|
||||
Lexer lexer(buf, fileName);
|
||||
Lexer lexer(buf, fileName);
|
||||
|
||||
Diagnostics diagnostics;
|
||||
|
||||
|
||||
@@ -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))
|
||||
@@ -53,22 +88,29 @@ namespace Fig::Entry
|
||||
|
||||
const String &source = manager.GetSource();
|
||||
|
||||
Lexer lexer(source, fileName);
|
||||
Lexer lexer(source, fileName);
|
||||
|
||||
Diagnostics diagnostics;
|
||||
|
||||
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_result = analyer.Analyze(program);
|
||||
|
||||
auto analyze_start = clock::now();
|
||||
auto analyze_result = analyer.Analyze(program);
|
||||
auto analyze_end = clock::now();
|
||||
|
||||
if (!analyze_result)
|
||||
{
|
||||
@@ -76,9 +118,12 @@ namespace Fig::Entry
|
||||
std::exit(1);
|
||||
}
|
||||
|
||||
Compiler compiler(manager, diagnostics);
|
||||
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
|
||||
@@ -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();
|
||||
};
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
11
src/main.cpp
11
src/main.cpp
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user