diff options
| author | _ov4 <ov4@ov4.org> | 2026-10-17 06:50:29 +0000 |
|---|---|---|
| committer | _ov4 <ov4@ov4.org> | 2026-10-17 06:50:29 +0000 |
| commit | c89477d615fb0ce4d89786ef848ffe6aca1da088 (patch) | |
| tree | b2c5692cfa4946fcd1df034630fc1a678d922b9c | |
| parent | 43451328a5ae74ffa34356259ee02e3786f070c3 (diff) | |
feat(classes): support explicit NEW constructor method calls
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
| -rw-r--r-- | include/cps/Lexer.h | 1 | ||||
| -rw-r--r-- | lib/CodeGen/Designator.cc | 4 | ||||
| -rw-r--r-- | lib/Lexer/Lexer.cc | 1 | ||||
| -rw-r--r-- | lib/Parser/ParserExpression.cc | 4 | ||||
| -rw-r--r-- | lib/Parser/ParserFunction.cc | 12 | ||||
| -rw-r--r-- | tests/i/class-new-methods/case.json | 1 | ||||
| -rw-r--r-- | tests/i/class-new-methods/expected.txt | 3 | ||||
| -rw-r--r-- | tests/i/class-new-methods/notes.md | 5 | ||||
| -rw-r--r-- | tests/i/class-new-methods/program.cps | 16 | ||||
| -rw-r--r-- | tests/iii/class-method-errors/case.json | 4 |
10 files changed, 40 insertions, 11 deletions
diff --git a/include/cps/Lexer.h b/include/cps/Lexer.h index 749ae0b..716dc4a 100644 --- a/include/cps/Lexer.h +++ b/include/cps/Lexer.h @@ -99,6 +99,7 @@ enum Token { tok_c_char = -610, tok_c_string = -611, tok_c_double = -612, + tok_new = -613, tok_try = -700, tok_catch = -701, diff --git a/lib/CodeGen/Designator.cc b/lib/CodeGen/Designator.cc index adf08f1..13eb25f 100644 --- a/lib/CodeGen/Designator.cc +++ b/lib/CodeGen/Designator.cc @@ -56,9 +56,7 @@ void CodeGen::runTypePrePass(const std::vector<std::unique_ptr<StmtAST>> &Statem for (const auto &Method : Class->getMethods()) { SaveAndRestore<int> Location(DiagnosticLine, Method->getLine()); const std::string &Name = Method->getProto()->getName(); - if (sameIdentifier(Name, "NEW")) { - reportError("NEW constructors are not implemented"); - } else if (sameIdentifier(Name, "this")) { + if (sameIdentifier(Name, "this")) { reportError("A method cannot be named this"); } else if (!Names.insert(Name).second) { reportError("Duplicate member %s in class %s", Name.c_str(), Class->getName().c_str()); diff --git a/lib/Lexer/Lexer.cc b/lib/Lexer/Lexer.cc index 9203db1..8f86ced 100644 --- a/lib/Lexer/Lexer.cc +++ b/lib/Lexer/Lexer.cc @@ -150,6 +150,7 @@ int Lexer::readToken() { if (IdentifierStr == "ENDTYPE") return tok_endtype; if (IdentifierStr == "SET") return tok_set; if (IdentifierStr == "DEFINE") return tok_define; + if (IdentifierStr == "NEW") return tok_new; if (IdentifierStr == "CLASS") return tok_class; if (IdentifierStr == "ENDCLASS") return tok_endclass; if (IdentifierStr == "PUBLIC") return tok_public; diff --git a/lib/Parser/ParserExpression.cc b/lib/Parser/ParserExpression.cc index e9b0dcc..f4e074e 100644 --- a/lib/Parser/ParserExpression.cc +++ b/lib/Parser/ParserExpression.cc @@ -109,13 +109,13 @@ bool Parser::ParseDesignatorSuffix(std::vector<DesignatorAccess> &Out) { Out.push_back(std::move(A)); } else if (CurTok == '.') { getNextToken(); - if (CurTok != tok_identifier) { + if (CurTok != tok_identifier && CurTok != tok_new) { fprintf(stderr, "error: Expected field name after '.' at line %d\n", Lex.getLine()); return false; } DesignatorAccess A; A.Kind = AccessKind::Field; - A.FieldName = Lex.IdentifierStr; + A.FieldName = CurTok == tok_new ? "NEW" : Lex.IdentifierStr; Out.push_back(std::move(A)); getNextToken(); } else if (CurTok == '^') { diff --git a/lib/Parser/ParserFunction.cc b/lib/Parser/ParserFunction.cc index ad187c8..1b8b470 100644 --- a/lib/Parser/ParserFunction.cc +++ b/lib/Parser/ParserFunction.cc @@ -132,6 +132,10 @@ std::optional<std::vector<ParamDecl>> Parser::ParsePrototypeArgs(bool AllowByRef std::unique_ptr<FunctionDefAST> Parser::ParseFunction(const std::string &OwnerClass, bool IsPublic) { int Line = Lex.getLine(); getNextToken(); + if (CurTok == tok_new || (CurTok == tok_identifier && sameIdentifier(Lex.IdentifierStr, "NEW"))) { + fprintf(stderr, "error: constructor NEW must be a PROCEDURE at line %d\n", Line); + return nullptr; + } if (CurTok != tok_identifier) { fprintf(stderr, "error: Expected function name\n"); return nullptr; @@ -176,12 +180,16 @@ std::unique_ptr<FunctionDefAST> Parser::ParseFunction(const std::string &OwnerCl std::unique_ptr<FunctionDefAST> Parser::ParseProcedure(const std::string &OwnerClass, bool IsPublic) { int Line = Lex.getLine(); getNextToken(); - if (CurTok != tok_identifier) { + if (CurTok != tok_identifier && CurTok != tok_new) { fprintf(stderr, "error: Expected procedure name\n"); return nullptr; } - std::string Name = Lex.IdentifierStr; + std::string Name = CurTok == tok_new ? "NEW" : Lex.IdentifierStr; + if (OwnerClass.empty() && sameIdentifier(Name, "NEW")) { + fprintf(stderr, "error: constructor NEW is only allowed in a CLASS at line %d\n", Line); + return nullptr; + } getNextToken(); auto Args = ParsePrototypeArgs(/*AllowByRef=*/true); diff --git a/tests/i/class-new-methods/case.json b/tests/i/class-new-methods/case.json new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/tests/i/class-new-methods/case.json @@ -0,0 +1 @@ +{} diff --git a/tests/i/class-new-methods/expected.txt b/tests/i/class-new-methods/expected.txt new file mode 100644 index 0000000..426f8ca --- /dev/null +++ b/tests/i/class-new-methods/expected.txt @@ -0,0 +1,3 @@ +x +call +again diff --git a/tests/i/class-new-methods/notes.md b/tests/i/class-new-methods/notes.md new file mode 100644 index 0000000..53192fb --- /dev/null +++ b/tests/i/class-new-methods/notes.md @@ -0,0 +1,5 @@ +# Explicit NEW calls + +Human review: pending. Expected output was independently traced by AI. + +NEW is an ordinary procedure: the bare member call stores x, CALL with mixed case stores call, and Reset calls this.NEW to store again. DECLARE is extended with arguments in the next implementation step. diff --git a/tests/i/class-new-methods/program.cps b/tests/i/class-new-methods/program.cps new file mode 100644 index 0000000..573fe0f --- /dev/null +++ b/tests/i/class-new-methods/program.cps @@ -0,0 +1,16 @@ +CLASS Box + Value : STRING + PROCEDURE NEW(Text : STRING) + Value <- Text + ENDPROCEDURE + PROCEDURE Reset() + this.NEW("again") + ENDPROCEDURE +ENDCLASS +DECLARE Obj : Box +Obj.NEW("x") +OUTPUT Obj.Value +CALL Obj.new("call") +OUTPUT Obj.Value +Obj.Reset() +OUTPUT Obj.Value diff --git a/tests/iii/class-method-errors/case.json b/tests/iii/class-method-errors/case.json index 655296d..e871f28 100644 --- a/tests/iii/class-method-errors/case.json +++ b/tests/iii/class-method-errors/case.json @@ -9,10 +9,6 @@ "line": 13 }, { - "reason": "NEW constructors are not implemented", - "line": 15 - }, - { "reason": "Duplicate member set in class Box", "line": 17 }, |
