aboutsummaryrefslogtreecommitdiff
path: root/lib/Parser
diff options
context:
space:
mode:
author_ov4 <ov4@ov4.org>2026-10-17 06:02:21 +0000
committer_ov4 <ov4@ov4.org>2026-10-17 06:02:21 +0000
commit3425b95cb654f8e1adc98365a4b370aa1fff8b4d (patch)
tree4d81b91d0bd807cd6f7ced037533d6e60f5c307f /lib/Parser
parent4e22f464387725a5a282f3d72e11cfb694a31a5a (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.cc6
-rw-r--r--lib/Parser/ParserExpression.cc28
-rw-r--r--lib/Parser/ParserFunction.cc26
-rw-r--r--lib/Parser/ParserType.cc28
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;