From 570a87c3cdabb09d3a34966e9240cb6d606e5302 Mon Sep 17 00:00:00 2001 From: PuqiAR Date: Wed, 18 Mar 2026 17:30:09 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=E5=87=BD=E6=95=B0?= =?UTF-8?q?=E7=B1=BB=E5=9E=8B=E8=A1=A8=E8=BE=BE=E5=BC=8F=E6=94=AF=E6=8C=81?= =?UTF-8?q?=EF=BC=8C=E6=9B=B4=E6=96=B0=E8=A7=A3=E6=9E=90=E5=99=A8=E5=92=8C?= =?UTF-8?q?=E5=88=86=E6=9E=90=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Ast/Base.hpp | 3 +- src/Ast/TypeExpr.hpp | 33 +++++++++++ src/Parser/Parser.hpp | 2 + src/Parser/TypeExprParser.cpp | 102 +++++++++++++++++++++++++++++----- src/Sema/Analyzer.cpp | 49 +++++++++++++--- src/Sema/Type.cpp | 11 +++- src/Sema/Type.hpp | 20 +++++++ src/VM/Entry.cpp | 22 +++++--- src/VM/Entry.hpp | 11 +++- src/main.cpp | 10 +++- 10 files changed, 228 insertions(+), 35 deletions(-) diff --git a/src/Ast/Base.hpp b/src/Ast/Base.hpp index bbf8566..db5a3a2 100644 --- a/src/Ast/Base.hpp +++ b/src/Ast/Base.hpp @@ -48,7 +48,8 @@ namespace Fig /* Type Expressions */ TypeExpr, NamedTypeExpr, - NullableTypeExpr + NullableTypeExpr, + FnTypeExpr, }; struct AstNode diff --git a/src/Ast/TypeExpr.hpp b/src/Ast/TypeExpr.hpp index 1de3466..82c68d0 100644 --- a/src/Ast/TypeExpr.hpp +++ b/src/Ast/TypeExpr.hpp @@ -64,4 +64,37 @@ namespace Fig return std::format("", inner->toString()); } }; + + struct FnTypeExpr final : public TypeExpr + { + // func (paratypes...) -> return_type + + DynArray paraTypes; + TypeExpr *returnType; + + FnTypeExpr(DynArray _paraTypes, TypeExpr *_returnType) : + paraTypes(std::move(_paraTypes)), returnType(_returnType) + { + type = AstType::FnTypeExpr; + } + + virtual String toString() const override + { + String detail = "toString(); + } + detail += ") -> "; + detail += returnType->toString(); + detail += "'>"; + + return detail; + } + }; } // namespace Fig diff --git a/src/Parser/Parser.hpp b/src/Parser/Parser.hpp index 8dd5411..27fe7d5 100644 --- a/src/Parser/Parser.hpp +++ b/src/Parser/Parser.hpp @@ -130,6 +130,7 @@ namespace Fig ParsingBreak, ParsingContinue, ParsingNamedTypeExpr, + ParsingFnTypeExpr, } type = StateType::Standby; std::unordered_set stopAt = {}; }; @@ -228,6 +229,7 @@ namespace Fig Result parseTypeExpr(); Result parseNamedTypeExpr(); + Result parseFnTypeExpr(); Result parseExpression(BindingPower = 0); Result parseLiteralExpr(); diff --git a/src/Parser/TypeExprParser.cpp b/src/Parser/TypeExprParser.cpp index e3e1812..43c83bc 100644 --- a/src/Parser/TypeExprParser.cpp +++ b/src/Parser/TypeExprParser.cpp @@ -25,9 +25,11 @@ namespace Fig if (match(TokenType::Dot)) { if (!currentToken().isIdentifier()) - return std::unexpected(makeUnexpectTokenError("Type", "identifier", currentToken())); + return std::unexpected( + makeUnexpectTokenError("Type", "identifier", currentToken())); } - else break; + else + break; } DynArray arguments; @@ -36,38 +38,108 @@ namespace Fig while (true) { auto result = parseTypeExpr(); - if (!result) return std::unexpected(result.error()); + if (!result) + return std::unexpected(result.error()); arguments.push_back(*result); - if (match(TokenType::Greater)) break; // `>` + if (match(TokenType::Greater)) + break; // `>` if (!match(TokenType::Comma)) - return std::unexpected(makeUnexpectTokenError("TypeArgs", "'>' or ','", currentToken())); + return std::unexpected( + makeUnexpectTokenError("TypeArgs", "'>' or ','", currentToken())); } } return arena.Allocate(path, arguments, location); } + Result Parser::parseFnTypeExpr() + { + StateProtector p(this, {State::ParsingFnTypeExpr}); + SourceLocation location = makeSourceLocation(consumeToken()); // consume `func` + if (!match(TokenType::LeftParen)) // `(` + { + return std::unexpected( + makeUnexpectTokenError("FnTypeExpr", "lparen (", currentToken())); + } + + DynArray paraTypes; + + while (true) + { + auto result = parseTypeExpr(); + if (!result) + { + return result; + } + paraTypes.push_back(*result); + + if (match(TokenType::RightParen)) + { + break; + } + else if (isEOF) + { + return std::unexpected( + makeUnexpectTokenError("FnTypeExpr", "rparen )", currentToken())); + } + if (!match(TokenType::Comma)) + { + return std::unexpected( + makeUnexpectTokenError("FnTypeExpr", "comma ,", currentToken())); + } + } + + TypeExpr *returnType = nullptr; + + if (match(TokenType::RightArrow)) // -> + { + auto result = parseTypeExpr(); + if (!result) + { + return result; + } + returnType = *result; + } + + FnTypeExpr *fnTypeExpr = arena.Allocate(paraTypes, returnType); + return fnTypeExpr; + } + // 解析主入口: 处理 `?` 后缀 Result Parser::parseTypeExpr() { TypeExpr *base = nullptr; - // 目前只支持命名类型 (以后可以加函数类型 (Int)->Int) if (currentToken().isIdentifier()) { - auto res = parseNamedTypeExpr(); - if (!res) return std::unexpected(res.error()); - base = *res; + auto result = parseNamedTypeExpr(); + if (!result) + { + return result; + } + base = *result; } - else return std::unexpected(makeUnexpectTokenError("TypeExpr", "name", currentToken())); - - // 空安全处理: Int?? 也可以,但 Analyzer 会规范化它 - while (match(TokenType::Question)) + else if (currentToken().type == TokenType::Function) { - base = arena.Allocate(base, makeSourceLocation(prevToken())); + auto result = parseFnTypeExpr(); + if (!result) + { + return result; + } + base = *result; + } + else + { + return std::unexpected(makeUnexpectTokenError("TypeExpr", "name", currentToken())); + } + + // type (?) + if (currentToken().type == TokenType::Question) // ? + { + base = arena.Allocate(base, makeSourceLocation(consumeToken())); // consume `?` } return base; } -} +} // namespace Fig diff --git a/src/Sema/Analyzer.cpp b/src/Sema/Analyzer.cpp index 46c76cc..8e73fbc 100644 --- a/src/Sema/Analyzer.cpp +++ b/src/Sema/Analyzer.cpp @@ -345,7 +345,7 @@ namespace Fig return std::unexpected(res.error()); retT = *res; } - // 🔥 强类型校验:返回值拦截 + // 返回值拦截校验 if (state.currentFn && !retT.isAssignableTo(state.currentFn->resolvedReturnType)) { return std::unexpected(Error(ErrorType::TypeError, @@ -513,10 +513,10 @@ namespace Fig auto *ft = static_cast(calleeType.base); if (ft->paramTypes.size() != argTypes.size()) { - return std::unexpected(Error(ErrorType::TypeError, - "expected " + std::to_string(ft->paramTypes.size()) + " arguments, got " - + std::to_string(argTypes.size()), - "", + return std::unexpected(Error(ErrorType::SyntaxError, + std::format( + "expected {} arguments, go {}", ft->paramTypes.size(), argTypes.size()), + "none", c->location)); } for (size_t i = 0; i < argTypes.size(); ++i) @@ -607,13 +607,46 @@ namespace Fig return std::unexpected( Error(ErrorType::UseUndeclaredIdentifier, "unknown type", "", texpr->location)); } - if (texpr->type == AstType::NullableTypeExpr) + else if (texpr->type == AstType::NullableTypeExpr) { auto res = resolveTypeExpr(static_cast(texpr)->inner); - if (res) - res->isNullable = true; + if (!res) + { + return res; + } + + res->isNullable = true; return res; } + else if (texpr->type == AstType::FnTypeExpr) + { + auto f = static_cast(texpr); + + DynArray paraTypes; + Type returnType = typeCtx.GetBasic(TypeTag::Any); + + for (auto &pt : f->paraTypes) + { + auto result = resolveTypeExpr(pt); + if (!result) + { + return result; + } + paraTypes.push_back(*result); + } + + if (f->returnType) + { + auto result = resolveTypeExpr(f->returnType); + if (!result) + { + return result; + } + returnType = *result; + } + return typeCtx.CreateFuncType(paraTypes, returnType); + } + return typeCtx.GetBasic(TypeTag::Any); } } // namespace Fig \ No newline at end of file diff --git a/src/Sema/Type.cpp b/src/Sema/Type.cpp index 78e6f30..c6e531b 100644 --- a/src/Sema/Type.cpp +++ b/src/Sema/Type.cpp @@ -39,10 +39,15 @@ namespace Fig bool Type::isAssignableTo(const Type &target) const { if (target.is(TypeTag::Any) || this->is(TypeTag::Any)) - return true; // Any 逃逸通道 + { + return true; // Any 逃逸 + } if (this->is(TypeTag::Null) && target.isNullable) + { return true; // Null 安全赋值 - return this->base == target.base && (!this->isNullable || target.isNullable); + } + + return *this->base == *target.base && (!this->isNullable || target.isNullable); } TypeContext::TypeContext() @@ -65,7 +70,9 @@ namespace Fig TypeContext::~TypeContext() { for (auto t : allTypes) + { delete t; + } } Type TypeContext::GetBasic(TypeTag tag, bool nullable) diff --git a/src/Sema/Type.hpp b/src/Sema/Type.hpp index 8d7a481..386636b 100644 --- a/src/Sema/Type.hpp +++ b/src/Sema/Type.hpp @@ -51,6 +51,11 @@ namespace Fig String name; BaseType(TypeTag t, String n) : tag(t), name(std::move(n)) {} virtual ~BaseType() = default; + + bool operator==(const BaseType &other) const + { + return tag == other.tag && name == other.name; + } }; class FuncType : public BaseType @@ -62,6 +67,11 @@ namespace Fig BaseType(TypeTag::Function, "Function"), paramTypes(std::move(params)), retType(ret) { } + + bool operator==(const FuncType &other) const + { + return paramTypes == other.paramTypes && retType == other.retType; + } }; class StructType : public BaseType @@ -85,6 +95,11 @@ namespace Fig fields.push_back({name, type, isPublic, (int) idx}); fieldMap[name] = idx; } + + bool operator==(const StructType &other) const + { + return this == &other; // 即使是两个完全一样的struct, 也认作不同的type + } }; class InterfaceType : public BaseType @@ -98,6 +113,11 @@ namespace Fig }; HashMap methods; InterfaceType(String n) : BaseType(TypeTag::Interface, std::move(n)) {} + + bool operator==(const InterfaceType &other) const + { + return this == &other; // 即使是两个完全一样的interface, 也认作不同的type + } }; class TypeContext diff --git a/src/VM/Entry.cpp b/src/VM/Entry.cpp index 1f6271f..b7a9bb9 100644 --- a/src/VM/Entry.cpp +++ b/src/VM/Entry.cpp @@ -5,7 +5,6 @@ @date 2026-03-13 */ - #include #include @@ -13,15 +12,17 @@ #include #include +#include +#include #include #include #include -#include #include + namespace Fig::Entry { - void RunFromPath(const String &path) + void RunFromPath(const String &path, const Config &conf) { namespace fs = std::filesystem; @@ -49,10 +50,10 @@ namespace Fig::Entry CoreIO::GetStdErr() << "Could not read file: " << path << '\n'; std::exit(1); } - + const String &source = manager.GetSource(); - Lexer lexer(source, fileName); + Lexer lexer(source, fileName); Parser parser(lexer, manager, fileName); auto parse_result = parser.Parse(); @@ -64,7 +65,7 @@ namespace Fig::Entry Program *program = *parse_result; Analyzer analyer(manager); - auto analyze_result = analyer.Analyze(program); + auto analyze_result = analyer.Analyze(program); if (!analyze_result) { @@ -73,7 +74,7 @@ namespace Fig::Entry } Diagnostics diagnostics; - Compiler compiler(manager, diagnostics); + Compiler compiler(manager, diagnostics); auto compile_result = compiler.Compile(program); diagnostics.EmitAll(manager); @@ -86,6 +87,13 @@ namespace Fig::Entry CompiledModule *compiledModule = *compile_result; + if (conf.dump) + { + Disassembler disassembler; + disassembler.DisassembleModule(compiledModule); + return; + } + VM vm; auto execute_result = vm.Execute(compiledModule); diff --git a/src/VM/Entry.hpp b/src/VM/Entry.hpp index efad82d..bea2e12 100644 --- a/src/VM/Entry.hpp +++ b/src/VM/Entry.hpp @@ -9,5 +9,14 @@ namespace Fig::Entry { - void RunFromPath(const String &); + struct Config + { + enum Mode + { + Debug, + Normal + } mode; + bool dump; + }; + void RunFromPath(const String &, const Config &conf); }; \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index ebc8c2d..5f4fc69 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -22,6 +22,7 @@ int main(int argc, char **argv) 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"); auto res = argparser.Parse(argc, argv); if (!res) @@ -35,6 +36,7 @@ int main(int argc, char **argv) bool showHelp = args.HasFlag("help"); bool showVersion = args.HasFlag("version"); bool showLicense = args.HasFlag("license"); + bool dump = args.HasFlag("dump"); if (showHelp) { @@ -77,8 +79,14 @@ int main(int argc, char **argv) return 1; } + Entry::Config config + { + .mode = Entry::Config::Normal, + .dump = dump + }; + const String &path = positionals.front(); - Entry::RunFromPath(path); + Entry::RunFromPath(path, config); return 0; } \ No newline at end of file