diff options
| author | _ov4 <ov4@ov4.org> | 2026-10-17 06:02:21 +0000 |
|---|---|---|
| committer | _ov4 <ov4@ov4.org> | 2026-10-17 06:02:21 +0000 |
| commit | 3425b95cb654f8e1adc98365a4b370aa1fff8b4d (patch) | |
| tree | 4d81b91d0bd807cd6f7ced037533d6e60f5c307f /lib/Parser | |
| parent | 4e22f464387725a5a282f3d72e11cfb694a31a5a (diff) | |
feat(classes): support ordinary member methods and this references
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Diffstat (limited to 'lib/Parser')
| -rw-r--r-- | lib/Parser/Parser.cc | 6 | ||||
| -rw-r--r-- | lib/Parser/ParserExpression.cc | 28 | ||||
| -rw-r--r-- | lib/Parser/ParserFunction.cc | 26 | ||||
| -rw-r--r-- | lib/Parser/ParserType.cc | 28 |
4 files changed, 74 insertions, 14 deletions
diff --git a/lib/Parser/Parser.cc b/lib/Parser/Parser.cc index 36b0a6a..26eeac6 100644 --- a/lib/Parser/Parser.cc +++ b/lib/Parser/Parser.cc @@ -231,6 +231,12 @@ std::unique_ptr<StmtAST> Parser::ParseAssignStmt() { std::vector<DesignatorAccess> Accesses; if (!ParseDesignatorSuffix(Accesses)) return nullptr; + if (CurTok == '(') { + auto Call = ParseMemberCall(Name, std::move(Accesses), Line); + if (!Call) return nullptr; + return std::make_unique<MemberCallStmtAST>(std::move(Call)); + } + if (CurTok != tok_assign) { fprintf(stderr, "error: Expected '<-' after identifier '%s' at line %d\n", Name.c_str(), Line); return nullptr; diff --git a/lib/Parser/ParserExpression.cc b/lib/Parser/ParserExpression.cc index b8952a8..e9b0dcc 100644 --- a/lib/Parser/ParserExpression.cc +++ b/lib/Parser/ParserExpression.cc @@ -59,6 +59,9 @@ std::unique_ptr<ExprAST> Parser::ParseIdentifierExpr() { std::vector<DesignatorAccess> Accesses; if (!ParseDesignatorSuffix(Accesses)) return nullptr; + if (CurTok == '(' && !atCaseBoundary()) + return ParseMemberCall(IdName, std::move(Accesses), Line); + // Degrade rule: shapes that existed before designators keep their nodes. if (Accesses.empty()) return std::make_unique<VariableExprAST>(IdName); @@ -67,6 +70,31 @@ std::unique_ptr<ExprAST> Parser::ParseIdentifierExpr() { return std::make_unique<DesignatorExprAST>(IdName, std::move(Accesses), Line); } +// The final field suffix names the method; preceding suffixes form its receiver. +std::unique_ptr<MemberCallExprAST> Parser::ParseMemberCall( + const std::string &Name, std::vector<DesignatorAccess> Accesses, int Line) { + if (Accesses.empty() || Accesses.back().Kind != AccessKind::Field) { + fprintf(stderr, "error: Expected a method name after '.' at line %d\n", Line); + return nullptr; + } + std::string Method = Accesses.back().FieldName; + Accesses.pop_back(); + auto Receiver = std::make_unique<DesignatorExprAST>(Name, std::move(Accesses), Line); + std::vector<std::unique_ptr<ExprAST>> Args; + if (CurTok == '(') { + getNextToken(); + auto Parsed = ParseExprList(')', true); + if (!Parsed) return nullptr; + Args = std::move(*Parsed); + } + if (CurTok == '[' || CurTok == '.' || CurTok == '^') { + fprintf(stderr, "error: record/array/pointer access on a function result is not supported at line %d\n", + Lex.getLine()); + return nullptr; + } + return std::make_unique<MemberCallExprAST>(std::move(Receiver), Method, std::move(Args), Line); +} + // Postfix accessor chain after an identifier: '[' exprs ']' | '.' IDENT | '^' bool Parser::ParseDesignatorSuffix(std::vector<DesignatorAccess> &Out) { while (true) { diff --git a/lib/Parser/ParserFunction.cc b/lib/Parser/ParserFunction.cc index fe53731..1aef756 100644 --- a/lib/Parser/ParserFunction.cc +++ b/lib/Parser/ParserFunction.cc @@ -128,7 +128,8 @@ std::optional<std::vector<ParamDecl>> Parser::ParsePrototypeArgs(bool AllowByRef return Params; } -std::unique_ptr<StmtAST> Parser::ParseFunction() { +std::unique_ptr<FunctionDefAST> Parser::ParseFunction(const std::string &OwnerClass, bool IsPublic) { + int Line = Lex.getLine(); getNextToken(); if (CurTok != tok_identifier) { fprintf(stderr, "error: Expected function name\n"); @@ -156,7 +157,7 @@ std::unique_ptr<StmtAST> Parser::ParseFunction() { } } - auto Proto = std::make_unique<PrototypeAST>(Name, std::move(*Args), RetType); + auto Proto = std::make_unique<PrototypeAST>(Name, std::move(*Args), RetType, false, OwnerClass, IsPublic); std::vector<std::unique_ptr<StmtAST>> Body = ParseBlock({tok_endfunction}); @@ -166,10 +167,13 @@ std::unique_ptr<StmtAST> Parser::ParseFunction() { } getNextToken(); - return std::make_unique<FunctionDefAST>(std::move(Proto), std::move(Body)); + auto Def = std::make_unique<FunctionDefAST>(std::move(Proto), std::move(Body)); + Def->setLine(Line); + return Def; } -std::unique_ptr<StmtAST> Parser::ParseProcedure() { +std::unique_ptr<FunctionDefAST> Parser::ParseProcedure(const std::string &OwnerClass, bool IsPublic) { + int Line = Lex.getLine(); getNextToken(); if (CurTok != tok_identifier) { fprintf(stderr, "error: Expected procedure name\n"); @@ -181,7 +185,7 @@ std::unique_ptr<StmtAST> Parser::ParseProcedure() { auto Args = ParsePrototypeArgs(/*AllowByRef=*/true); if (!Args) return nullptr; - auto Proto = std::make_unique<PrototypeAST>(Name, std::move(*Args), "VOID"); + auto Proto = std::make_unique<PrototypeAST>(Name, std::move(*Args), "VOID", false, OwnerClass, IsPublic); std::vector<std::unique_ptr<StmtAST>> Body = ParseBlock({tok_endprocedure}); @@ -191,7 +195,9 @@ std::unique_ptr<StmtAST> Parser::ParseProcedure() { } getNextToken(); - return std::make_unique<FunctionDefAST>(std::move(Proto), std::move(Body)); + auto Def = std::make_unique<FunctionDefAST>(std::move(Proto), std::move(Body)); + Def->setLine(Line); + return Def; } std::unique_ptr<StmtAST> Parser::ParseCallStmt() { @@ -205,6 +211,14 @@ std::unique_ptr<StmtAST> Parser::ParseCallStmt() { std::string Callee = Lex.IdentifierStr; getNextToken(); + std::vector<DesignatorAccess> Accesses; + if (!ParseDesignatorSuffix(Accesses)) return nullptr; + if (!Accesses.empty()) { + auto Call = ParseMemberCall(Callee, std::move(Accesses), Line); + if (!Call) return nullptr; + return std::make_unique<MemberCallStmtAST>(std::move(Call)); + } + std::vector<std::unique_ptr<ExprAST>> Args; if (CurTok == '(') { getNextToken(); diff --git a/lib/Parser/ParserType.cc b/lib/Parser/ParserType.cc index a57c8a6..0d664d0 100644 --- a/lib/Parser/ParserType.cc +++ b/lib/Parser/ParserType.cc @@ -89,10 +89,11 @@ std::unique_ptr<StmtAST> Parser::ParseTypeDecl() { return nullptr; } } else { - auto Fields = ParseRecordBody(Name, Line, IsClass ? tok_endclass : tok_endtype); + std::vector<std::unique_ptr<FunctionDefAST>> Methods; + auto Fields = ParseRecordBody(Name, Line, IsClass ? tok_endclass : tok_endtype, &Methods); if (!Fields) return nullptr; if (IsClass) - Decl = std::make_unique<ClassTypeDeclAST>(Name, std::move(*Fields)); + Decl = std::make_unique<ClassTypeDeclAST>(Name, std::move(*Fields), std::move(Methods)); else Decl = std::make_unique<RecordTypeDeclAST>(Name, std::move(*Fields)); } @@ -107,12 +108,14 @@ std::unique_ptr<StmtAST> Parser::ParseTypeDecl() { // Shared record body and field parsing. Fields are data, never statements. std::optional<std::vector<RecordFieldDecl>> Parser::ParseRecordBody( - const std::string &Name, int Line, int EndToken) { + const std::string &Name, int Line, int EndToken, + std::vector<std::unique_ptr<FunctionDefAST>> *Methods) { bool IsClass = EndToken == tok_endclass; const char *Keyword = IsClass ? "CLASS" : "TYPE"; const char *EndKeyword = IsClass ? "ENDCLASS" : "ENDTYPE"; auto AtField = [&] { - return IsClass ? (CurTok == tok_public || CurTok == tok_private || CurTok == tok_identifier) + return IsClass ? (CurTok == tok_public || CurTok == tok_private || CurTok == tok_identifier || + CurTok == tok_function || CurTok == tok_procedure) : CurTok == tok_declare; }; std::vector<RecordFieldDecl> Fields; @@ -121,10 +124,18 @@ std::optional<std::vector<RecordFieldDecl>> Parser::ParseRecordBody( if (AtField()) { // Class fields default to public, like record fields. bool IsPublic = CurTok != tok_private; - if (ParseRecordField(Fields, IsPublic)) continue; + if (IsClass && (CurTok == tok_public || CurTok == tok_private)) getNextToken(); + if (IsClass && (CurTok == tok_function || CurTok == tok_procedure)) { + auto Method = CurTok == tok_function ? ParseFunction(Name, IsPublic) + : ParseProcedure(Name, IsPublic); + if (Method && Methods) { + Methods->push_back(std::move(Method)); + continue; + } + } else if (ParseRecordField(Fields, IsPublic)) continue; } else { fprintf(stderr, "error: Expected %s or %s in %s %s at line %d\n", - IsClass ? "field, PUBLIC or PRIVATE" : "DECLARE", EndKeyword, + IsClass ? "field, method, PUBLIC or PRIVATE" : "DECLARE", EndKeyword, Keyword, Name.c_str(), Lex.getLine()); } HadFieldError = true; @@ -143,8 +154,9 @@ std::optional<std::vector<RecordFieldDecl>> Parser::ParseRecordBody( return std::nullopt; } getNextToken(); // eat body terminator - if (Fields.empty() && !HadFieldError) { - fprintf(stderr, "error: %s %s has no fields at line %d\n", Keyword, Name.c_str(), Line); + if (Fields.empty() && (!Methods || Methods->empty()) && !HadFieldError) { + fprintf(stderr, "error: %s %s has no %s at line %d\n", Keyword, Name.c_str(), + IsClass ? "members" : "fields", Line); return std::nullopt; } if (HadFieldError) return std::nullopt; |
