From fafa2b49461438527e2de9dad09af9e4b6b6870a Mon Sep 17 00:00:00 2001 From: PuqiAR Date: Sun, 12 Apr 2026 10:07:51 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=9C=A8=E8=A7=A3=E6=9E=90=E5=99=A8?= =?UTF-8?q?=E4=B8=AD=E5=AE=9E=E7=8E=B0=20Lambda=20=E5=92=8C=20new=20?= =?UTF-8?q?=E8=A1=A8=E8=BE=BE=E5=BC=8F=20-=20=E5=A2=9E=E5=8A=A0=E4=BA=86?= =?UTF-8?q?=E5=AF=B9=20Lambda=20=E8=A1=A8=E8=BE=BE=E5=BC=8F=E7=9A=84?= =?UTF-8?q?=E5=88=9D=E6=AD=A5=E8=A7=A3=E6=9E=90=E6=94=AF=E6=8C=81=EF=BC=8C?= =?UTF-8?q?=E5=8C=85=E6=8B=AC=E5=8F=82=E6=95=B0=E5=A4=84=E7=90=86=E5=92=8C?= =?UTF-8?q?=E8=BF=94=E5=9B=9E=E7=B1=BB=E5=9E=8B=E3=80=82Lambda=E9=97=AD?= =?UTF-8?q?=E5=8C=85=E5=B0=9A=E6=9C=AA=E6=94=AF=E6=8C=81=E3=80=82=20-=20?= =?UTF-8?q?=E5=BC=95=E5=85=A5=E4=BA=86=E7=94=A8=E4=BA=8E=E5=AF=B9=E8=B1=A1?= =?UTF-8?q?=E5=88=9D=E5=A7=8B=E5=8C=96=E7=9A=84=E6=96=B0=E7=9A=84=E8=A1=A8?= =?UTF-8?q?=E8=BE=BE=E5=BC=8F=EF=BC=8C=E6=94=AF=E6=8C=81=E5=8F=AF=E9=80=89?= =?UTF-8?q?=E7=9A=84=E5=91=BD=E5=90=8D=E5=8F=82=E6=95=B0=E3=80=82=20-=20?= =?UTF-8?q?=E6=94=B9=E8=BF=9B=E4=BA=86=E8=A1=A8=E8=BE=BE=E5=BC=8F=E8=AF=AD?= =?UTF-8?q?=E6=B3=95=E9=94=99=E8=AF=AF=E7=9A=84=E9=94=99=E8=AF=AF=E6=8A=A5?= =?UTF-8?q?=E5=91=8A=E3=80=82=20-=20=E6=9B=B4=E6=96=B0=E4=BA=86=E8=A7=A3?= =?UTF-8?q?=E6=9E=90=E5=99=A8=E5=92=8C=E5=88=86=E6=9E=90=E5=99=A8=E4=BB=A5?= =?UTF-8?q?=E5=A4=84=E7=90=86=E6=96=B0=E7=9A=84=E8=A1=A8=E8=BE=BE=E5=BC=8F?= =?UTF-8?q?=E7=B1=BB=E5=9E=8B=E5=B9=B6=E9=AA=8C=E8=AF=81=E5=85=B6=E8=AF=AD?= =?UTF-8?q?=E4=B9=89=E3=80=82=20-=20=E4=BF=AE=E6=94=B9=E4=BA=86=E7=8E=B0?= =?UTF-8?q?=E6=9C=89=E6=B5=8B=E8=AF=95=E4=BB=A5=E6=B6=B5=E7=9B=96=E6=96=B0?= =?UTF-8?q?=E5=8A=9F=E8=83=BD=E5=B9=B6=E7=A1=AE=E4=BF=9D=E5=85=B6=E6=AD=A3?= =?UTF-8?q?=E7=A1=AE=E6=80=A7=E3=80=82=20-=20=E6=94=B9=E8=BF=9B=E4=BA=86?= =?UTF-8?q?=E5=90=84=E7=A7=8D=E8=A7=A3=E6=9E=90=E5=92=8C=E8=AF=AD=E4=B9=89?= =?UTF-8?q?=E9=94=99=E8=AF=AF=E7=9A=84=E8=AF=8A=E6=96=AD=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .clang-format | 2 +- src/Ast/Ast.hpp | 6 + src/Ast/Base.hpp | 3 +- src/Ast/Expr/CallExpr.hpp | 3 +- src/Ast/Expr/LambdaExpr.hpp | 70 +++++ .../Expr/{ObjectInitExpr.hpp => NewExpr.hpp} | 12 +- src/Ast/Stmt/StructDefStmt.hpp | 5 +- src/Bytecode/Disassembler.cpp | 1 + src/Compiler/CompileTest.cpp | 6 +- src/Compiler/ExprCompiler.cpp | 114 +++++++-- src/Error/Error.cpp | 73 ++++-- src/Error/Error.hpp | 4 +- src/LSP/LSPServer.hpp | 19 +- src/Parser/ExprParser.cpp | 239 ++++++++++++++++-- src/Parser/Parser.hpp | 57 ++++- src/Parser/ParserTest.cpp | 8 +- src/Parser/StmtParser.cpp | 219 ++++++++++++++-- src/Parser/TypeExprParser.cpp | 43 +++- src/Repl/Repl.hpp | 11 +- src/Sema/Analyzer.cpp | 136 ++++++++-- src/Sema/AnalyzerTest.cpp | 7 +- src/VM/Entry.cpp | 12 +- src/VM/Entry.hpp | 1 + src/main.cpp | 14 +- 24 files changed, 925 insertions(+), 140 deletions(-) create mode 100644 src/Ast/Expr/LambdaExpr.hpp rename src/Ast/Expr/{ObjectInitExpr.hpp => NewExpr.hpp} (74%) diff --git a/.clang-format b/.clang-format index f0cc117..44e8d27 100644 --- a/.clang-format +++ b/.clang-format @@ -6,7 +6,7 @@ Language: Cpp AccessModifierOffset: -4 # 开括号(开圆括号、开尖括号、开方括号)后的对齐: Align, DontAlign, AlwaysBreak(总是在开括号后换行) -AlignAfterOpenBracket: DontAlign +AlignAfterOpenBracket: AlwaysBreak # 连续赋值时,对齐所有等号 AlignConsecutiveAssignments: true diff --git a/src/Ast/Ast.hpp b/src/Ast/Ast.hpp index ed172a1..347905a 100644 --- a/src/Ast/Ast.hpp +++ b/src/Ast/Ast.hpp @@ -11,13 +11,19 @@ #include #include #include +#include #include +#include +#include #include #include #include #include #include +#include +#include +#include #include #include #include \ No newline at end of file diff --git a/src/Ast/Base.hpp b/src/Ast/Base.hpp index db5a3a2..746b28a 100644 --- a/src/Ast/Base.hpp +++ b/src/Ast/Base.hpp @@ -29,7 +29,8 @@ namespace Fig IndexExpr, CallExpr, MemberExpr, // obj.prop - ObjectInitExpr, // new Point{} + NewExpr, // new Point{} + LambdaExpr, /* Statements */ ExprStmt, diff --git a/src/Ast/Expr/CallExpr.hpp b/src/Ast/Expr/CallExpr.hpp index e541445..9e6fa6c 100644 --- a/src/Ast/Expr/CallExpr.hpp +++ b/src/Ast/Expr/CallExpr.hpp @@ -47,9 +47,10 @@ namespace Fig type = AstType::CallExpr; } - CallExpr(Expr *_callee, FnCallArgs _args) : callee(_callee), args(std::move(_args)) + CallExpr(Expr *_callee, FnCallArgs _args, SourceLocation _location) : callee(_callee), args(std::move(_args)) { type = AstType::CallExpr; + location = std::move(_location); } virtual String toString() const override diff --git a/src/Ast/Expr/LambdaExpr.hpp b/src/Ast/Expr/LambdaExpr.hpp new file mode 100644 index 0000000..f418812 --- /dev/null +++ b/src/Ast/Expr/LambdaExpr.hpp @@ -0,0 +1,70 @@ +/*! + @file src/Ast/Expr/LambdaExpr.hpp + @brief Lambda表达式定义 +*/ + +#pragma once + +#include +#include + +namespace Fig +{ + struct LambdaExpr final : public Expr + { + // func (params) [-> return type] ([=> expr] / [ {stmt} ]) + + DynArray params; + TypeExpr *returnType; + AstNode *body; // expr/blockstmt + bool isExprBody; + + DynArray upvalues; + + LambdaExpr() + { + type = AstType::LambdaExpr; + } + + LambdaExpr( + DynArray _params, + TypeExpr *_returnType, + AstNode *_body, + bool _isExprBody, + SourceLocation _location) : + params(std::move(_params)), + returnType(_returnType), + body(_body), + isExprBody(_isExprBody) + { + type = AstType::LambdaExpr; + location = std::move(_location); + } + + virtual String toString() const override + { + String specifying = "toString(); + } + if (isExprBody) + { + specifying += ") => "; + specifying += body->toString(); + } + else + { + specifying += ") {"; + specifying += body->toString(); + specifying.push_back(U'}'); + } + specifying += "'>"; + return specifying; + } + }; +}; // namespace Fig \ No newline at end of file diff --git a/src/Ast/Expr/ObjectInitExpr.hpp b/src/Ast/Expr/NewExpr.hpp similarity index 74% rename from src/Ast/Expr/ObjectInitExpr.hpp rename to src/Ast/Expr/NewExpr.hpp index 507d3c8..29b785d 100644 --- a/src/Ast/Expr/ObjectInitExpr.hpp +++ b/src/Ast/Expr/NewExpr.hpp @@ -11,7 +11,7 @@ namespace Fig { - struct ObjectInitExpr final : public Expr + struct NewExpr final : public Expr { struct Arg { @@ -21,20 +21,20 @@ namespace Fig TypeExpr *typeExpr; DynArray args; - ObjectInitExpr() + NewExpr() { - type = AstType::ObjectInitExpr; + type = AstType::NewExpr; } - ObjectInitExpr(TypeExpr *_te, DynArray _args, SourceLocation _loc) : + NewExpr(TypeExpr *_te, DynArray _args, SourceLocation _loc) : typeExpr(_te), args(std::move(_args)) { - type = AstType::ObjectInitExpr; + type = AstType::NewExpr; location = std::move(_loc); } virtual String toString() const override { - String res = "toString() + "{"; + String res = "toString() + "{"; for (size_t i = 0; i < args.size(); ++i) { if (!args[i].name.empty()) diff --git a/src/Ast/Stmt/StructDefStmt.hpp b/src/Ast/Stmt/StructDefStmt.hpp index d70a3b5..edf2add 100644 --- a/src/Ast/Stmt/StructDefStmt.hpp +++ b/src/Ast/Stmt/StructDefStmt.hpp @@ -13,9 +13,12 @@ namespace Fig { struct Field { + bool isPublic; + bool typeInfer; + String name; TypeExpr *type; - bool isPublic; + Expr *initExpr; }; bool isPublic; String name; diff --git a/src/Bytecode/Disassembler.cpp b/src/Bytecode/Disassembler.cpp index fe4583a..b666233 100644 --- a/src/Bytecode/Disassembler.cpp +++ b/src/Bytecode/Disassembler.cpp @@ -75,6 +75,7 @@ namespace Fig stream << std::format(" [{}] {}\n", i, proto->constants[i].ToString()); } } + stream << "--- End Disassembly ---"; } Disassembler::Format Disassembler::GetFormat(OpCode op) diff --git a/src/Compiler/CompileTest.cpp b/src/Compiler/CompileTest.cpp index 3e29fff..6350653 100644 --- a/src/Compiler/CompileTest.cpp +++ b/src/Compiler/CompileTest.cpp @@ -27,7 +27,11 @@ int main() } Lexer lexer(source, filePath); - Parser parser(lexer, sm, filePath); + + Diagnostics diagnostics; + Parser parser(lexer, sm, filePath, diagnostics); + + diagnostics.EmitAll(sm); auto pRes = parser.Parse(); if (!pRes) diff --git a/src/Compiler/ExprCompiler.cpp b/src/Compiler/ExprCompiler.cpp index 090e84f..79797c7 100644 --- a/src/Compiler/ExprCompiler.cpp +++ b/src/Compiler/ExprCompiler.cpp @@ -12,7 +12,6 @@ #include #include - namespace Fig { static Result parsePhysicalNumber(const String &raw, const SourceLocation &loc) @@ -94,7 +93,9 @@ namespace Fig parsePhysicalNumber(manager.GetSub(tok.index, tok.length), l->location); if (!vRes) return std::unexpected(vRes.error()); - emit(Op::iABx(OpCode::LoadK, r, static_cast(addConstant(*vRes))), &l->location); + emit( + Op::iABx(OpCode::LoadK, r, static_cast(addConstant(*vRes))), + &l->location); } else if (tok.type == TokenType::LiteralString) { @@ -129,7 +130,9 @@ namespace Fig // 仅在被强制指定目标(如参数装填)时发射搬运指令 if (target != sym->index) { - emit(Op::iABx(OpCode::Mov, target, static_cast(sym->index)), &i->location); + emit( + Op::iABx(OpCode::Mov, target, static_cast(sym->index)), + &i->location); } return target; } @@ -137,7 +140,9 @@ namespace Fig Register r = (target == NO_REG) ? *allocateReg(i->location) : target; if (sym->location == SymbolLocation::Upvalue) { - emit(Op::iABC(OpCode::GetUpval, r, static_cast(sym->index), 0), &i->location); + emit( + Op::iABC(OpCode::GetUpval, r, static_cast(sym->index), 0), + &i->location); } else if (sym->location == SymbolLocation::Global) { @@ -176,10 +181,12 @@ namespace Fig { isGlobalFastCall = true; int protoIdx = id->resolvedSymbol->index; - emit(Op::iABC(OpCode::FastCall, - static_cast(protoIdx), - baseReg, - static_cast(c->args.args.size())), + emit( + Op::iABC( + OpCode::FastCall, + static_cast(protoIdx), + baseReg, + static_cast(c->args.args.size())), &c->location); } } @@ -193,10 +200,12 @@ namespace Fig return std::unexpected(r_fn.error()); // 使用动态 Call 指令,RA 是指向堆闭包的寄存器 - emit(Op::iABC(OpCode::Call, - *r_fn, - baseReg, - static_cast(c->args.args.size())), + emit( + Op::iABC( + OpCode::Call, + *r_fn, + baseReg, + static_cast(c->args.args.size())), &c->location); } @@ -240,18 +249,25 @@ namespace Fig Symbol *sym = lid->resolvedSymbol; if (sym->location == SymbolLocation::Local) { - emit(Op::iABx(OpCode::Mov, static_cast(sym->index), *r_val), &lid->location); + emit( + Op::iABx(OpCode::Mov, static_cast(sym->index), *r_val), + &lid->location); } else if (sym->location == SymbolLocation::Upvalue) { - emit(Op::iABC( - OpCode::SetUpval, *r_val, static_cast(sym->index), 0), &lid->location); + emit( + Op::iABC( + OpCode::SetUpval, *r_val, static_cast(sym->index), 0), + &lid->location); } else { - emit(Op::iABx(OpCode::SetGlobal, - *r_val, - static_cast(getGlobalID(lid->name))), &lid->location); + emit( + Op::iABx( + OpCode::SetGlobal, + *r_val, + static_cast(getGlobalID(lid->name))), + &lid->location); } } return r_val; @@ -290,7 +306,8 @@ namespace Fig case BinaryOperator::GreaterEqual: op = OpCode::GreaterEqual; break; case BinaryOperator::LessEqual: op = OpCode::LessEqual; break; default: - return std::unexpected(Error(ErrorType::InternalError, + return std::unexpected(Error( + ErrorType::InternalError, "unsupported binary operator", "", in->location)); @@ -318,6 +335,65 @@ namespace Fig return r_d; } + case AstType::LambdaExpr: { + auto l = static_cast(expr); + + Proto *proto = new Proto; + proto->name = l->toString(); + + FuncState fs(proto, current); + FuncState *old = current; + current = &fs; + + if (l->isExprBody) + { + auto result = compileExpr(static_cast(l->body)); + if (!result) + { + return result; + } + + emit(Op::iABC(OpCode::Return, *result, 0, 0), &l->location); + } + else + { + auto result = compileStmt(static_cast(l->body)); + if (!result) + { + return std::unexpected(result.error()); + } + auto _r = allocateReg(l->body->location); + if (!_r) + { + return _r; + } + + Register r = *_r; + emit(Op::iABC(OpCode::LoadNull, r, 0, 0), &l->body->location); + emit(Op::iABC(OpCode::Return, r, 0, 0), &l->body->location); + } + + int protoIndex = (int) module->protos.size(); + module->protos.push_back(proto); + + current = old; + if (target == NO_REG) + { + auto _r = allocateReg(expr->location); + if (!_r) + { + return _r; + } + emit(Op::iABx(OpCode::LoadFn, *_r, protoIndex), &l->body->location); + return *_r; + } + else + { + emit(Op::iABx(OpCode::LoadFn, target, protoIndex), &l->body->location); + } + return target; + } + default: break; } return std::unexpected( diff --git a/src/Error/Error.cpp b/src/Error/Error.cpp index c816bfa..9d5e235 100644 --- a/src/Error/Error.cpp +++ b/src/Error/Error.cpp @@ -17,7 +17,8 @@ namespace Fig ost << color << msg << TerminalColors::Reset; } - void ColoredPrint(const char *color, const std::string &msg, std::ostream &ost = CoreIO::GetStdErr()) + void + ColoredPrint(const char *color, const std::string &msg, std::ostream &ost = CoreIO::GetStdErr()) { ost << color << msg << TerminalColors::Reset; } @@ -30,7 +31,10 @@ namespace Fig std::string MultipleStr(const char *c, size_t n) { std::string buf; - for (size_t i = 0; i < n; ++i) { buf += c; } + for (size_t i = 0; i < n; ++i) + { + buf += c; + } return buf; } @@ -40,6 +44,7 @@ namespace Fig switch (type) { case UnusedSymbol: return "UnusedSymbol"; + case UnnecessarySemicolon: return "UnnecessarySemicolon"; case MayBeNull: return "MaybeNull"; @@ -61,7 +66,8 @@ namespace Fig case TooManyConstants: return "TooManyConstants"; case RegisterOverflow: return "RegisterOverflow"; - case InternalError: return "InternalError"; + case InternalError: + return "InternalError"; // default: return "Some one forgot to add case to `ErrorTypeToString`"; } return "UnknownError"; @@ -69,10 +75,10 @@ namespace Fig void PrintSystemInfos() { - std::ostream &err = CoreIO::GetStdErr(); + std::ostream &err = CoreIO::GetStdErr(); std::stringstream build_info; - build_info << "\r🌘 Fig v" << Core::VERSION << " on " << Core::PLATFORM << ' ' << Core::ARCH << '[' - << Core::COMPILER << ']' << '\n' + build_info << "\r🌘 Fig v" << Core::VERSION << " on " << Core::PLATFORM << ' ' << Core::ARCH + << '[' << Core::COMPILER << ']' << '\n' << " Build Time: " << Core::COMPILE_TIME; const std::string &build_info_str = build_info.str(); @@ -83,35 +89,43 @@ namespace Fig void PrintErrorInfo(const Error &error, const SourceManager &srcManager) { - 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 *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; + 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 *level_color = (level == 1 ? MinorColor : (level == 2 ? MediumColor : CriticalColor)); + const char *level_color = + (level == 1 ? MinorColor : (level == 2 ? MediumColor : CriticalColor)); err << "🔥 " << level_color //<< '(' << level_name << ')' - << 'E' << static_cast(error.type) << TC::Reset << ": " << level_color << ErrorTypeToString(error.type) - << TC::Reset << '\n'; + << 'E' << static_cast(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 << " ┌─> 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'; // 尝试打印上3行 下2行 int64_t line_start = location.sp.line - 3, line_end = location.sp.line + 2; - while (!srcManager.HasLine(line_end)) { --line_end; } - while (!srcManager.HasLine(line_start)) { ++line_start; } + while (!srcManager.HasLine(line_end)) + { + --line_end; + } + while (!srcManager.HasLine(line_start)) + { + ++line_start; + } const auto &getLineNumWidth = [](size_t l) { unsigned int cnt = 0; @@ -127,30 +141,39 @@ namespace Fig { unsigned int offset = 2 + 2 + 1; // ' └─ ' - if (i == location.sp.line) { err << TC::DarkGray << " └─ " << TC::Reset; } - else if (i < location.sp.line) { err << TC::DarkGray << " │ " << TC::Reset; } + if (i == location.sp.line) + { + err << TC::DarkGray << " └─ " << TC::Reset; + } + else if (i < location.sp.line) + { + err << TC::DarkGray << " │ " << TC::Reset; + } else { err << MultipleStr(" ", offset); } unsigned int cur_line_number_width = getLineNumWidth(i); - err << MultipleStr(" ", max_line_number_width - cur_line_number_width) << TC::Yellow << i << TC::Reset; + err << MultipleStr(" ", max_line_number_width - cur_line_number_width) << TC::Yellow + << i << TC::Reset; err << " │ " << srcManager.GetLine(i) << '\n'; if (i == location.sp.line) { unsigned int error_col_offset = offset + 1 + max_line_number_width + 2; - err << MultipleStr(" ", error_col_offset) << MultipleStr(" ", location.sp.column - 1) << TC::LightGreen + err << MultipleStr(" ", error_col_offset) + << MultipleStr(" ", location.sp.column - 1) << TC::LightGreen << MultipleStr("^", location.sp.tok_length) << TC::Reset << '\n'; err << MultipleStr(" ", error_col_offset) - << MultipleStr(" ", location.sp.column - 1 + location.sp.tok_length / 2) << "╰─ " << level_color - << error.message << TC::Reset << "\n\n"; + << MultipleStr(" ", location.sp.column - 1 + location.sp.tok_length / 2) + << "╰─ " << level_color << error.message << TC::Reset << "\n\n"; } } err << "\n"; err << "❓ " << TC::DarkGray << "Thrower: " << error.thrower_loc.function_name() << " (" - << error.thrower_loc.file_name() << ":" << error.thrower_loc.line() << ")" << TC::Reset << "\n"; + << error.thrower_loc.file_name() << ":" << error.thrower_loc.line() << ")" << TC::Reset + << "\n"; err << "💡 " << TC::Blue << "Suggestion: " << error.suggestion << TC::Reset; err << '\n'; } @@ -158,7 +181,7 @@ namespace Fig void ReportError(const Error &error, const SourceManager &srcManager) { assert(srcManager.read && "ReportError: srcManager doesn't read source"); - assert(srcManager.HasLine(error.location.sp.line)); + // assert(srcManager.HasLine(error.location.sp.line)); PrintSystemInfos(); PrintErrorInfo(error, srcManager); diff --git a/src/Error/Error.hpp b/src/Error/Error.hpp index ba6e2d9..0e846a1 100644 --- a/src/Error/Error.hpp +++ b/src/Error/Error.hpp @@ -24,6 +24,8 @@ namespace Fig { /* Minor */ UnusedSymbol = 0, + UnnecessarySemicolon, + TrailingComma, /* Medium */ MayBeNull = 1001, @@ -51,7 +53,7 @@ namespace Fig TooManyLocals, TooManyConstants, - // --- 新增:编译器内部与VM约束 --- + // 编译器内部约束 RegisterOverflow, InternalError, }; diff --git a/src/LSP/LSPServer.hpp b/src/LSP/LSPServer.hpp index 5d1f26e..dbcacef 100644 --- a/src/LSP/LSPServer.hpp +++ b/src/LSP/LSPServer.hpp @@ -138,9 +138,12 @@ namespace Fig manager.LoadFromMemory(sourceCode); Lexer lexer(sourceCode, ""); - Parser parser(lexer, manager, ""); - // 1. 语法检查拦截 + Diagnostics diagnostics; + + Parser parser(lexer, manager, "", diagnostics); + + // 语法检查拦截 auto parserResult = parser.Parse(); if (!parserResult) { @@ -148,6 +151,11 @@ namespace Fig return; } + for (auto &diag : diagnostics.GetErrors()) + { + SendDiagnostics(uri, &diag); + } + Program *program = *parserResult; Analyzer analyzer(manager); @@ -160,7 +168,12 @@ namespace Fig return; } - // 3. 一切完美,发射空数组清空过去的错误红线 + for (auto &diag : analyzer.GetDiagnostics().GetErrors()) + { + SendDiagnostics(uri, &diag); + } + + // 一切完美,发射空数组清空过去的错误红线 SendDiagnostics(uri, nullptr); } }; diff --git a/src/Parser/ExprParser.cpp b/src/Parser/ExprParser.cpp index db80d22..d9f4f6f 100644 --- a/src/Parser/ExprParser.cpp +++ b/src/Parser/ExprParser.cpp @@ -14,7 +14,8 @@ namespace Fig StateProtector p(this, {State::ParsingLiteralExpr}); const Token &literal_token = consumeToken(); - LiteralExpr *node = arena.Allocate(literal_token, makeSourceLocation(literal_token)); + LiteralExpr *node = + arena.Allocate(literal_token, makeSourceLocation(literal_token)); return node; } Result Parser::parseIdentiExpr() // 当前token为Identifier调用 @@ -65,8 +66,8 @@ namespace Fig return node; } - Result Parser::parseIndexExpr( - Expr *base) // 由 parseExpression调用, 当前token为 `[` + Result + Parser::parseIndexExpr(Expr *base) // 由 parseExpression调用, 当前token为 `[` { StateProtector p(this, {State::ParsingIndexExpr}); @@ -80,7 +81,8 @@ namespace Fig if (currentToken().type != TokenType::RightBracket) // `]` { - return std::unexpected(Error(ErrorType::SyntaxError, + return std::unexpected(Error( + ErrorType::SyntaxError, "unclosed brackets", "insert `]`", makeSourceLocation(lbracket_token))); @@ -91,12 +93,13 @@ namespace Fig return indexExpr; } - Result Parser::parseCallExpr( - Expr *callee) // 由 parseExpression调用, 当前token为 `(` + Result + Parser::parseCallExpr(Expr *callee) // 由 parseExpression调用, 当前token为 `(` { StateProtector p(this, {State::ParsingCallExpr}); const Token &lparen_token = consumeToken(); // consume `(` + const SourceLocation &location = makeSourceLocation(lparen_token); FnCallArgs callArgs; @@ -104,14 +107,15 @@ namespace Fig if (currentToken().type == TokenType::RightParen) { consumeToken(); // consume `)` - return arena.Allocate(callee, callArgs); + return arena.Allocate(callee, callArgs, location); } while (true) { if (currentToken().type == TokenType::EndOfFile) { - return std::unexpected(Error(ErrorType::SyntaxError, + return std::unexpected(Error( + ErrorType::SyntaxError, "fn call has unclosed parenthese", "insert `)`", makeSourceLocation(lparen_token))); @@ -131,7 +135,8 @@ namespace Fig if (currentToken().type != TokenType::Comma) { - return std::unexpected(Error(ErrorType::SyntaxError, + return std::unexpected(Error( + ErrorType::SyntaxError, "expected `,` or `)` in argument list", "insert `,`", makeSourceLocation(currentToken()))); @@ -140,7 +145,186 @@ namespace Fig consumeToken(); // consume `,` } - return arena.Allocate(callee, callArgs); + return arena.Allocate(callee, callArgs, location); + } + + Result Parser::parseNewExpr() + { + // new type{...} + StateProtector p(this, {State::ParsingNewExpr}); + + SourceLocation location = makeSourceLocation(consumeToken()); // consume `new` + + SET_STOP_AT(TokenType::LeftBrace); // { + auto type_result = parseTypeExpr(); + if (!type_result) + { + return std::unexpected(type_result.error()); + } + TypeExpr *type = *type_result; + + if (!match(TokenType::LeftBrace)) + { + return std::unexpected(makeUnexpectTokenError("NewExpr", "lbrace {", currentToken())); + } + + const Token &lb_token = prevToken(); + + /* + Positional: + new Point{1, 2} + Named: + new Point{x = 1, y = 2} + */ + + DynArray args; + + while (true) + { + if (isEOF) + { + return std::unexpected(Error( + ErrorType::SyntaxError, + "unclosed `{` in new expr", + "insert '}'", + makeSourceLocation(lb_token) + )); + } + if (args.empty() && match(TokenType::RightBrace)) // 空参 + { + break; + } + + if (currentToken().isIdentifier() && peekToken().type == TokenType::Assign) + { + const Token &name_token = consumeToken(); + const String &name = srcManager.GetSub(name_token.index, name_token.length); + consumeToken(); // consume `=` + + SET_STOP_AT(TokenType::Comma, TokenType::RightBrace); // , / } + auto result = parseExpression(); + if (!result) + { + return result; + } + + args.push_back(NewExpr::Arg{ + name, + *result + }); + } + else + { + SET_STOP_AT(TokenType::Comma, TokenType::RightBrace); // , / } + auto result = parseExpression(); + if (!result) + { + return result; + } + + args.push_back(NewExpr::Arg{ + .value = *result + }); + } + + + if (match(TokenType::Comma)) + { + continue; + } + + if (match(TokenType::RightBrace)) + { + break; + } + } + + NewExpr *newExpr = arena.Allocate(type, args, location); + return newExpr; + } + + Result Parser::parseLambdaExpr() + { + StateProtector p(this, {State::ParsingLambdaExpr}); + + SourceLocation location = makeSourceLocation(consumeToken()); // consume `func` + + if (currentToken().isIdentifier()) + { + return std::unexpected(Error( + ErrorType::SyntaxError, + "lambda expression should not have a name", + "remove the name", + makeSourceLocation(currentToken()))); + } + + if (currentToken().type != TokenType::LeftParen) + { + return std::unexpected( + makeUnexpectTokenError("fn def stmt", "lparen '('", currentToken())); + } + + DynArray params; + + auto paraResult = parseFnParams(); + if (!paraResult) + { + return std::unexpected(paraResult.error()); + } + params = *paraResult; + + TypeExpr *returnType = nullptr; + Token rightArrowToken; + if (match(TokenType::RightArrow)) // -> + { + rightArrowToken = consumeToken(); + + auto result = parseTypeExpr(); + if (!result) + { + return std::unexpected(result.error()); + } + returnType = *result; + } + + if (match(TokenType::DoubleArrow)) // => + { + if (returnType) + { + return std::unexpected(Error( + ErrorType::SyntaxError, + "use of expr body but specified return type in lambda expr", + "remove `-> ...`", + makeSourceLocation(rightArrowToken))); + } + auto result = parseExpression(); + if (!result) + { + return result; + } + + Expr *expr = *result; + LambdaExpr *lambda = + arena.Allocate(params, returnType, expr, true, location); + return lambda; + } + else if (currentToken().type == TokenType::LeftBrace) + { + auto result = parseBlockStmt(); + if (!result) + { + return std::unexpected(result.error()); + } + + LambdaExpr *lambda = + arena.Allocate(params, returnType, *result, false, location); + return lambda; + } + else + { + return std::unexpected( + makeUnexpectTokenError("LambdaExpr", "darrow => / lbrace {", currentToken())); + } } Result Parser::parseExpression(BindingPower rbp) @@ -186,17 +370,39 @@ namespace Fig const Token &rparen_token = consumeToken(); // consume `)` if (rparen_token.type != TokenType::RightParen) { - return std::unexpected(Error(ErrorType::SyntaxError, + return std::unexpected(Error( + ErrorType::SyntaxError, "unclosed parenthese", "insert `)`", makeSourceLocation(lparen_token))); } lhs = *expr_result; } + else if (token.type == TokenType::Function) + { + auto result = parseLambdaExpr(); + if (!result) + { + return result; + } + + lhs = *result; + } + else if (token.type == TokenType::New) + { + auto result = parseNewExpr(); + if (!result) + { + return result; + } + + lhs = *result; + } if (!lhs) { - return std::unexpected(Error(ErrorType::ExpectedExpression, + return std::unexpected(Error( + ErrorType::ExpectedExpression, "expected expression", "insert expressions", makeSourceLocation(prevToken()))); @@ -253,10 +459,11 @@ namespace Fig } else { - return std::unexpected(Error(ErrorType::ExpectedExpression, - "expression unexpectedly ended", - "insert expressions", - makeSourceLocation(token))); + // return std::unexpected(Error(ErrorType::ExpectedExpression, + // "expression unexpectedly ended", + // "insert expressions", + // makeSourceLocation(token))); + return lhs; } } return lhs; diff --git a/src/Parser/Parser.hpp b/src/Parser/Parser.hpp index 27fe7d5..e80ec37 100644 --- a/src/Parser/Parser.hpp +++ b/src/Parser/Parser.hpp @@ -9,6 +9,7 @@ #include #include +#include #include #include #include @@ -33,6 +34,8 @@ namespace Fig String fileName; bool isEOF = false; + Diagnostics &diagnostics; + // 惰性获取下一个 Token,跳过注释 Token nextToken() { @@ -116,12 +119,16 @@ namespace Fig enum StateType : std::uint8_t { Standby, + ParsingLiteralExpr, ParsingIdentiExpr, ParsingInfixExpr, ParsingPrefixExpr, ParsingIndexExpr, ParsingCallExpr, + ParsingLambdaExpr, + ParsingNewExpr, + ParsingVarDecl, ParsingIf, ParsingWhile, @@ -129,6 +136,9 @@ namespace Fig ParsingReturn, ParsingBreak, ParsingContinue, + ParsingStructDef, + + ParsingTypeParameters, ParsingNamedTypeExpr, ParsingFnTypeExpr, } type = StateType::Standby; @@ -138,7 +148,8 @@ namespace Fig private: const std::unordered_set &getBaseTerminators() { - static const std::unordered_set baseTerminators = {TokenType::Semicolon, + static const std::unordered_set baseTerminators{ + TokenType::Semicolon, TokenType::RightParen, TokenType::RightBracket, TokenType::RightBrace, @@ -201,32 +212,55 @@ namespace Fig SourceLocation makeSourceLocation(const Token &tok) { auto [line, column] = srcManager.GetLineColumn(tok.index); - // 物理防爆盾:防止因解析错位导致的异常列号引起终端 OOM + // 防止因解析错位导致的异常列号引起终端 OOM if (column > 5000) column = 1; - return SourceLocation(SourcePosition(line, column, tok.length), + return SourceLocation( + SourcePosition(line, column, tok.length), fileName, "[internal parser]", magic_enum::enum_name(currentState().type).data()); } - inline Error makeUnexpectTokenError(const String &stmt, const String &exp, const Token &got) + inline Error makeUnexpectTokenError( + const String &stmt, + const String &exp, + const Token &got, + std::source_location th_loc = std::source_location::current()) { - return Error(ErrorType::SyntaxError, + return Error( + ErrorType::SyntaxError, std::format( "expect '{}' in {}, got `{}`", exp, stmt, magic_enum::enum_name(got.type)), "none", - makeSourceLocation(got)); + makeSourceLocation(got), + th_loc); } - inline Error makeExpectSemicolonError() + inline Error + makeExpectSemicolonError(std::source_location th_loc = std::source_location::current()) { - return Error(ErrorType::SyntaxError, + return Error( + ErrorType::SyntaxError, "expect ';' after statement", "insert ';'", - makeSourceLocation(currentToken())); + makeSourceLocation(currentToken()), + th_loc); } + inline Error makeExpectSemicolonError( + const Token &token, std::source_location th_loc = std::source_location::current()) + { + return Error( + ErrorType::SyntaxError, + "expect ';' after statement", + "insert ';'", + makeSourceLocation(token), + th_loc); + } + + Result parseTypeParameters(); + Result parseTypeExpr(); Result parseNamedTypeExpr(); Result parseFnTypeExpr(); @@ -239,6 +273,7 @@ namespace Fig Result parseIndexExpr(Expr *); Result parseCallExpr(Expr *); Result parseNewExpr(); + Result parseLambdaExpr(); Result parseBlockStmt(); Result parseVarDecl(bool); @@ -255,8 +290,8 @@ namespace Fig Result parseStatement(); public: - Parser(Lexer &_lexer, SourceManager &_src, String _file) : - lexer(_lexer), srcManager(_src), fileName(std::move(_file)) + Parser(Lexer &_lexer, SourceManager &_src, String _file, Diagnostics &_diagnostics) : + lexer(_lexer), srcManager(_src), fileName(std::move(_file)), diagnostics(_diagnostics) { pushState(State()); } diff --git a/src/Parser/ParserTest.cpp b/src/Parser/ParserTest.cpp index aada0f8..2c3da36 100644 --- a/src/Parser/ParserTest.cpp +++ b/src/Parser/ParserTest.cpp @@ -18,13 +18,19 @@ int main() } Lexer lexer(source, fileName); - Parser parser(lexer, srcManager, fileName); + + Diagnostics diagnostics; + Parser parser(lexer, srcManager, fileName, diagnostics); + auto result = parser.Parse(); if (!result) { ReportError(result.error(), srcManager); return 1; } + + diagnostics.EmitAll(srcManager); + Program *program = *result; for (Stmt *stmt : program->nodes) { diff --git a/src/Parser/StmtParser.cpp b/src/Parser/StmtParser.cpp index 2225024..a26aed2 100644 --- a/src/Parser/StmtParser.cpp +++ b/src/Parser/StmtParser.cpp @@ -17,7 +17,8 @@ namespace Fig { if (isEOF) { - return std::unexpected(Error(ErrorType::SyntaxError, + return std::unexpected(Error( + ErrorType::SyntaxError, "unclosed braces in block stmt", "insert '}'", location)); @@ -75,7 +76,8 @@ namespace Fig { if (typeSpeicifer) { - return std::unexpected(Error(ErrorType::SyntaxError, + return std::unexpected(Error( + ErrorType::SyntaxError, "used type infer but specifying the type", "change `:=` to '='", makeSourceLocation(prevToken()))); @@ -115,7 +117,8 @@ namespace Fig } if (!match(TokenType::RightParen)) { - return std::unexpected(Error(ErrorType::SyntaxError, + return std::unexpected(Error( + ErrorType::SyntaxError, "unclosed parenthese in if condition", "insert `)`", makeSourceLocation(lpToken))); @@ -155,7 +158,8 @@ namespace Fig { if (alternate) { - return std::unexpected(Error(ErrorType::SyntaxError, + return std::unexpected(Error( + ErrorType::SyntaxError, "else if after else", "remove else if", elseLocation)); @@ -175,7 +179,8 @@ namespace Fig } if (!match(TokenType::RightParen)) { - return std::unexpected(Error(ErrorType::SyntaxError, + return std::unexpected(Error( + ErrorType::SyntaxError, "unclosed parenthese in if condition", "insert `)`", makeSourceLocation(lpToken))); @@ -210,7 +215,8 @@ namespace Fig { if (alternate) { - return std::unexpected(Error(ErrorType::SyntaxError, + return std::unexpected(Error( + ErrorType::SyntaxError, "duplicate else in if stmt", "remove it", elseLocation)); @@ -252,7 +258,8 @@ namespace Fig if (!match(TokenType::RightParen)) { - return std::unexpected(Error(ErrorType::SyntaxError, + return std::unexpected(Error( + ErrorType::SyntaxError, "unclosed parenthese in while condition", "insert ')'", makeSourceLocation(lpToken))); @@ -289,8 +296,6 @@ namespace Fig Result, Error> Parser::parseFnParams() { - StateProtector p(this, {State::ParsingFnDefStmt}); - const Token &lpToken = consumeToken(); DynArray params; @@ -298,7 +303,8 @@ namespace Fig { if (isEOF) { - return std::unexpected(Error(ErrorType::SyntaxError, + return std::unexpected(Error( + ErrorType::SyntaxError, "unclosed parenthese in function parameters", "insert ')'", makeSourceLocation(lpToken))); @@ -353,6 +359,7 @@ namespace Fig Result Parser::parseFnDefStmt(bool isPublic) { + StateProtector p(this, {State::ParsingFnDefStmt}); SourceLocation location = makeSourceLocation(consumeToken()); if (!currentToken().isIdentifier()) @@ -389,19 +396,49 @@ namespace Fig returnType = *result; } - if (currentToken().type != TokenType::LeftBrace) + BlockStmt *body = nullptr; + if (match(TokenType::DoubleArrow)) // => + { + auto result = parseExpression(); + if (!result) + { + return std::unexpected(result.error()); + } + + // if (!match(TokenType::Semicolon)) // ; + // { + // return std::unexpected(makeExpectSemicolonError(prevToken())); + // } + + if (match(TokenType::Semicolon)) + { + diagnostics.Report(Error( + ErrorType::UnnecessarySemicolon, + "`;` is unnecessary in this context", + "try remove `;`", + makeSourceLocation(prevToken()))); + } + + Expr *expr = *result; + ReturnStmt *returnStmt = arena.Allocate(expr, expr->location); + + body = arena.Allocate(); + body->nodes.push_back(returnStmt); + } + else if (currentToken().type == TokenType::LeftBrace) + { + auto bodyResult = parseBlockStmt(); + if (!bodyResult) + { + return std::unexpected(bodyResult.error()); + } + body = *bodyResult; + } + else { return std::unexpected( - makeUnexpectTokenError("fn def stmt", "function body '{'", currentToken())); + makeUnexpectTokenError("fn def stmt", "function body '=>' / '{'", currentToken())); } - BlockStmt *body = nullptr; - auto bodyResult = parseBlockStmt(); - - if (!bodyResult) - { - return std::unexpected(bodyResult.error()); - } - body = *bodyResult; FnDefStmt *fnDef = arena.Allocate(isPublic, name, params, returnType, body, location); @@ -429,6 +466,127 @@ namespace Fig return returnStmt; } + Result Parser::parseStructDef(bool isPublic) + { + StateProtector p(this, {State::ParsingStructDef}); + + SourceLocation location = makeSourceLocation(consumeToken()); // consume `struct` + if (!currentToken().isIdentifier()) + { + return std::unexpected( + makeUnexpectTokenError("StructDef", "struct name", currentToken())); + } + + const Token &name_tok = consumeToken(); // consume name + const String &name = srcManager.GetSub(name_tok.index, name_tok.length); + + StructDefStmt *stDef = arena.Allocate(); + + if (currentToken().type == TokenType::Less) // < + { + auto result = parseTypeParameters(); + if (!result) + { + return std::unexpected(result.error()); + } + + stDef->typeParameters = *result; + } + + if (!match(TokenType::LeftBrace)) + { + return std::unexpected( + makeUnexpectTokenError("StructDef", "lbrace '{'", currentToken())); + } + + const Token &lb_tok = prevToken(); // `{` + + while (true) + { + if (isEOF) + { + return std::unexpected(Error( + ErrorType::SyntaxError, + "unclosed braces in struct def", + "insert '}'", + makeSourceLocation(lb_tok))); + } + if (match(TokenType::RightBrace)) + { + break; + } + + // (public) field_name (: Type) (= expr) / (:= expr) + + bool isPublic = match(TokenType::Public); + if (currentToken().isIdentifier()) + { + const Token &name_tok = consumeToken(); + const String &field_name = srcManager.GetSub(name_tok.index, name_tok.length); + + if (match(TokenType::Walrus)) // := + { + auto result = parseExpression(); + if (!result) + { + return std::unexpected(result.error()); + } + + stDef->fields.push_back( + StructDefStmt::Field{isPublic, true, field_name, nullptr, *result}); + } + else + { + TypeExpr *type = nullptr; + Expr *initExpr = nullptr; + + if (match(TokenType::Colon)) // : + { + auto result = parseTypeExpr(); + if (!result) + { + return std::unexpected(result.error()); + } + type = *result; + } + + if (match(TokenType::Assign)) + { + auto result = parseExpression(); + if (!result) + { + return std::unexpected(result.error()); + } + initExpr = *result; + } + stDef->fields.push_back( + StructDefStmt::Field{isPublic, false, field_name, type, initExpr}); + } + if (!match(TokenType::Semicolon)) + { + return std::unexpected(makeExpectSemicolonError()); + } + } + else if (currentToken().type == TokenType::Function) + { + auto result = parseFnDefStmt(isPublic); + if (!result) + { + return result; + } + + stDef->methods.push_back(*result); + } + else + { + return std::unexpected( + makeUnexpectTokenError("StructDef", "field or method", currentToken())); + } + } + + return stDef; + } + Result Parser::parseStatement() { StateProtector p(this, {State::Standby}); @@ -446,6 +604,11 @@ namespace Fig return parseFnDefStmt(true); } + if (currentToken().type == TokenType::Struct) + { + return parseStructDef(true); + } + return std::unexpected( makeUnexpectTokenError("public", "var/const/func/struct", currentToken())); } @@ -470,11 +633,16 @@ namespace Fig return parseWhileStmt(); } - if (currentToken().type == TokenType::Function) + if (currentToken().type == TokenType::Function && peekToken().isIdentifier()) { return parseFnDefStmt(false); } + if (currentToken().type == TokenType::Struct) + { + return parseStructDef(false); + } + if (currentToken().type == TokenType::Return) { return parseReturnStmt(); @@ -507,6 +675,15 @@ namespace Fig return nullptr; } + if (currentToken().type == TokenType::Semicolon) + { + return std::unexpected(Error( + ErrorType::SyntaxError, + "null statement is not allowed here", + "remove `;`", + makeSourceLocation(currentToken()))); + } + const auto &expr_result = parseExpression(); if (!expr_result) { diff --git a/src/Parser/TypeExprParser.cpp b/src/Parser/TypeExprParser.cpp index 43c83bc..34df7db 100644 --- a/src/Parser/TypeExprParser.cpp +++ b/src/Parser/TypeExprParser.cpp @@ -9,6 +9,46 @@ namespace Fig { + Result Parser::parseTypeParameters() + { + StateProtector p(this, {State::ParsingTypeParameters}); + decltype(StructDefStmt::typeParameters) tp; + + const Token &lab = consumeToken(); // consume `<` + + while (true) + { + if (isEOF) + { + return std::unexpected(Error( + ErrorType::SyntaxError, + "unclosed `<` in type parameters", + "insert '>'", + makeSourceLocation(lab))); + } + if (match(TokenType::Greater)) // > + { + break; + } + if (!currentToken().isIdentifier()) + { + return std::unexpected( + makeUnexpectTokenError("TypeParams", "tp name", currentToken())); + } + + const Token &name_tok = consumeToken(); + const String &name = srcManager.GetSub(name_tok.index, name_tok.length); + tp.push_back(name); + + if (!match(TokenType::Comma)) + { + return std::unexpected(makeUnexpectTokenError( + "TypeParams", "comma or type parameter", currentToken())); + } + } + return tp; + } + // 解析基础命名类型与泛型: List Result Parser::parseNamedTypeExpr() { @@ -137,7 +177,8 @@ namespace Fig // type (?) if (currentToken().type == TokenType::Question) // ? { - base = arena.Allocate(base, makeSourceLocation(consumeToken())); // consume `?` + base = arena.Allocate( + base, makeSourceLocation(consumeToken())); // consume `?` } return base; diff --git a/src/Repl/Repl.hpp b/src/Repl/Repl.hpp index 2c06d71..0935c7e 100644 --- a/src/Repl/Repl.hpp +++ b/src/Repl/Repl.hpp @@ -209,7 +209,10 @@ namespace Fig String source(buf); Lexer lexer(buf, fileName); - Parser parser(lexer, manager, fileName); + + Diagnostics diagnostics; + + Parser parser(lexer, manager, fileName, diagnostics); auto _program = parser.Parse(); if (!_program) @@ -223,13 +226,15 @@ namespace Fig Analyzer analyzer(manager); auto result = analyzer.Analyze(program); + analyzer.GetDiagnostics().EmitAll(manager); + if (!result) { PrintError(result.error(), source); continue; } - Compiler compiler(manager, analyzer.GetDiagnostics()); + Compiler compiler(manager, diagnostics); auto compile_result = compiler.Compile(program); if (!compile_result) @@ -238,6 +243,8 @@ namespace Fig continue; } + diagnostics.EmitAll(manager); + CompiledModule *compiledModule = *compile_result; auto exe_result = vm.Execute(compiledModule); diff --git a/src/Sema/Analyzer.cpp b/src/Sema/Analyzer.cpp index 8e73fbc..6a96c6f 100644 --- a/src/Sema/Analyzer.cpp +++ b/src/Sema/Analyzer.cpp @@ -5,7 +5,6 @@ #include #include -#include #include #include #include @@ -82,8 +81,11 @@ namespace Fig { auto *s = static_cast(stmt); if (globalTypes.contains(s->name)) + { return std::unexpected( Error(ErrorType::RedeclarationError, "type redeclared", "", s->location)); + } + auto *t = arena.Allocate(s->name); typeCtx.allTypes.push_back(t); globalTypes[s->name] = t; @@ -92,10 +94,14 @@ namespace Fig { auto *f = static_cast(stmt); if (f->name == "main") + { hasMain = true; + } if (globalSymbols.contains(f->name)) + { return std::unexpected( Error(ErrorType::RedeclarationError, "func redeclared", "", f->location)); + } Symbol *sym = arena.Allocate(f->name, Type{}, SymbolLocation::Global, 0, true); globalSymbols[f->name] = sym; @@ -118,7 +124,9 @@ namespace Fig { auto res = resolveTypeExpr(f.type); if (!res) + { return std::unexpected(res.error()); + } st->AddField(f.name, *res, f.isPublic); } } @@ -185,10 +193,11 @@ namespace Fig } Type declT = v->typeSpecifier ? *resolveTypeExpr(v->typeSpecifier) : initT; - // 🔥 强类型校验:赋值拦截 + // 赋值拦截 if (v->initExpr && !initT.isAssignableTo(declT)) { - return std::unexpected(Error(ErrorType::TypeError, + return std::unexpected(Error( + ErrorType::TypeError, "cannot assign '" + initT.toString() + "' to type '" + declT.toString() + "'", "", @@ -210,7 +219,7 @@ namespace Fig case AstType::FnDefStmt: { auto *f = static_cast(stmt); - // 3.10: 局部闭包延迟类型推导 + // 局部闭包延迟类型推导 if (!f->resolvedSymbol) // 闭包? { @@ -243,7 +252,8 @@ namespace Fig ScopeGuard scopeGuard(env, true); for (auto *p : f->params) { - env.current->locals[p->name] = arena.Allocate(p->name, + env.current->locals[p->name] = arena.Allocate( + p->name, p->resolvedType, SymbolLocation::Local, env.current->nextLocalId++, @@ -279,7 +289,8 @@ namespace Fig return std::unexpected(c.error()); else if (!c->isAssignableTo(typeCtx.GetBasic(TypeTag::Bool))) { - return std::unexpected(Error(ErrorType::TypeError, + return std::unexpected(Error( + ErrorType::TypeError, "condition must be Bool", "", elif->cond->location)); @@ -330,11 +341,15 @@ namespace Fig break; } case AstType::BreakStmt: - case AstType::ContinueStmt: + case AstType::ContinueStmt: { if (state.loopDepth <= 0) + { return std::unexpected( Error(ErrorType::SyntaxError, "outside loop", "", stmt->location)); + } break; + } + case AstType::ReturnStmt: { auto *rs = static_cast(stmt); Type retT = typeCtx.GetBasic(TypeTag::Null); @@ -345,10 +360,11 @@ namespace Fig return std::unexpected(res.error()); retT = *res; } - // 返回值拦截校验 + // 返回值校验 if (state.currentFn && !retT.isAssignableTo(state.currentFn->resolvedReturnType)) { - return std::unexpected(Error(ErrorType::TypeError, + return std::unexpected(Error( + ErrorType::TypeError, "cannot return '" + retT.toString() + "' from function expecting '" + state.currentFn->resolvedReturnType.toString() + "'", "", @@ -407,7 +423,8 @@ namespace Fig auto *st = static_cast(targetType.base); if (!st->fieldMap.contains(m->name)) { - return std::unexpected(Error(ErrorType::TypeError, + return std::unexpected(Error( + ErrorType::TypeError, "struct '" + st->name + "' has no field named '" + m->name + "'", "", m->location)); @@ -415,14 +432,18 @@ namespace Fig // 字段类型 return expr->resolvedType = st->fields[st->fieldMap[m->name]].type; } - case AstType::ObjectInitExpr: { - auto *o = static_cast(expr); + case AstType::NewExpr: { + auto *o = static_cast(expr); auto res = resolveTypeExpr(o->typeExpr); if (!res) + { return std::unexpected(res.error()); + } if (!res->base || res->base->tag != TypeTag::Struct) + { return std::unexpected( Error(ErrorType::TypeError, "requires struct", "", o->location)); + } auto *st = static_cast(res->base); for (auto &arg : o->args) { @@ -431,7 +452,9 @@ namespace Fig Error(ErrorType::TypeError, "unknown field", "", arg.value->location)); auto r = analyzeExpr(arg.value); if (!r) + { return std::unexpected(r.error()); + } // 字段赋值类型检查 if (!arg.name.empty() && !r->isAssignableTo(st->fields[st->fieldMap[arg.name]].type)) @@ -456,7 +479,8 @@ namespace Fig if (in->op == BinaryOperator::Assign) { if (!r.isAssignableTo(l)) - return std::unexpected(Error(ErrorType::TypeError, + return std::unexpected(Error( + ErrorType::TypeError, "cannot assign '" + r.toString() + "' to '" + l.toString() + "'", "", in->location)); @@ -511,11 +535,13 @@ namespace Fig Error(ErrorType::TypeError, "callee is not a function", "", c->location)); auto *ft = static_cast(calleeType.base); + if (ft->paramTypes.size() != argTypes.size()) { - return std::unexpected(Error(ErrorType::SyntaxError, + return std::unexpected(Error( + ErrorType::SyntaxError, std::format( - "expected {} arguments, go {}", ft->paramTypes.size(), argTypes.size()), + "expected {} arguments, got {}", ft->paramTypes.size(), argTypes.size()), "none", c->location)); } @@ -523,7 +549,8 @@ namespace Fig { if (!argTypes[i].isAssignableTo(ft->paramTypes[i])) { - return std::unexpected(Error(ErrorType::TypeError, + return std::unexpected(Error( + ErrorType::TypeError, "argument " + std::to_string(i + 1) + " expects '" + ft->paramTypes[i].toString() + "', got '" + argTypes[i].toString() + "'", @@ -533,13 +560,86 @@ namespace Fig } return expr->resolvedType = ft->retType; } + + case AstType::LambdaExpr: { + auto l = static_cast(expr); + + Type returnType = typeCtx.GetBasic(TypeTag::Any); + + if (l->returnType) + { + auto tres = resolveTypeExpr(l->returnType); + if (!tres) + { + return tres; + } + + returnType = *tres; + } + + FnDefStmt *f = arena.Allocate( + false, "LambdaFn", l->params, l->returnType, nullptr, l->location); + + FnStateGuard fnGuard(state.currentFn, f); + ScopeGuard scopeGuard(env, true); + + DynArray paramTypes; + for (auto *p : l->params) + { + auto pres = resolveTypeExpr(p->typeSpecifier); + if (!pres) + { + return pres; + } + p->resolvedType = *pres; + paramTypes.push_back(*pres); + + env.current->locals[p->name] = arena.Allocate( + p->name, + p->resolvedType, + SymbolLocation::Local, + env.current->nextLocalId++, + false); + } + + if (l->isExprBody) + { + Expr *expr = static_cast(l->body); + if (auto r = analyzeExpr(expr); !r) + { + return r; + } + if (!expr->resolvedType.isAssignableTo(state.currentFn->resolvedReturnType)) + { + return std::unexpected(Error( + ErrorType::TypeError, + "cannot return '" + state.currentFn->resolvedReturnType.toString() + + "' from lambda function expecting '" + + state.currentFn->resolvedReturnType.toString() + "'", + "", + expr->location)); + } + } + else + { + Stmt *stmt = static_cast(l->body); + if (auto r = analyzeStmt(stmt); !r) + { + return std::unexpected(r.error()); + } + } + + return l->resolvedType = typeCtx.CreateFuncType(paramTypes, returnType); + } + default: break; } + return expr->resolvedType = typeCtx.GetBasic(TypeTag::Any); } - Result Analyzer::resolveSymbolInternal( - const String &name, const SourceLocation &loc, Scope *s) + Result + Analyzer::resolveSymbolInternal(const String &name, const SourceLocation &loc, Scope *s) { Scope *curr = s; while (curr) diff --git a/src/Sema/AnalyzerTest.cpp b/src/Sema/AnalyzerTest.cpp index ca680ae..910fa33 100644 --- a/src/Sema/AnalyzerTest.cpp +++ b/src/Sema/AnalyzerTest.cpp @@ -20,7 +20,12 @@ void runTest(const std::string &path) } Lexer lexer(source, String(path)); - Parser parser(lexer, srcManager, String(path)); + + Diagnostics diagnostics; + + Parser parser(lexer, srcManager, String(path), diagnostics); + + diagnostics.EmitAll(srcManager); auto pRes = parser.Parse(); if (!pRes) diff --git a/src/VM/Entry.cpp b/src/VM/Entry.cpp index b7a9bb9..4992fce 100644 --- a/src/VM/Entry.cpp +++ b/src/VM/Entry.cpp @@ -54,7 +54,10 @@ namespace Fig::Entry const String &source = manager.GetSource(); Lexer lexer(source, fileName); - Parser parser(lexer, manager, fileName); + + Diagnostics diagnostics; + + Parser parser(lexer, manager, fileName, diagnostics); auto parse_result = parser.Parse(); if (!parse_result) @@ -73,7 +76,6 @@ namespace Fig::Entry std::exit(1); } - Diagnostics diagnostics; Compiler compiler(manager, diagnostics); auto compile_result = compiler.Compile(program); @@ -91,7 +93,6 @@ namespace Fig::Entry { Disassembler disassembler; disassembler.DisassembleModule(compiledModule); - return; } VM vm; @@ -103,6 +104,11 @@ namespace Fig::Entry std::exit(1); } + if (conf.pregs) + { + vm.PrintRegisters(); + } + delete compiledModule; } }; // namespace Fig::Entry \ No newline at end of file diff --git a/src/VM/Entry.hpp b/src/VM/Entry.hpp index bea2e12..e5b33d1 100644 --- a/src/VM/Entry.hpp +++ b/src/VM/Entry.hpp @@ -17,6 +17,7 @@ namespace Fig::Entry Normal } mode; bool dump; + bool pregs; }; void RunFromPath(const String &, const Config &conf); }; \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 5f4fc69..fc62882 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -23,6 +23,7 @@ int main(int argc, char **argv) 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"); auto res = argparser.Parse(argc, argv); if (!res) @@ -37,6 +38,7 @@ int main(int argc, char **argv) bool showVersion = args.HasFlag("version"); bool showLicense = args.HasFlag("license"); bool dump = args.HasFlag("dump"); + bool pregs = args.HasFlag("pregs"); if (showHelp) { @@ -46,10 +48,12 @@ int main(int argc, char **argv) if (showVersion) { - 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, @@ -79,11 +83,7 @@ int main(int argc, char **argv) return 1; } - Entry::Config config - { - .mode = Entry::Config::Normal, - .dump = dump - }; + Entry::Config config{.mode = Entry::Config::Normal, .dump = dump, .pregs = pregs}; const String &path = positionals.front(); Entry::RunFromPath(path, config);