diff options
Diffstat (limited to 'lib/Parser')
| -rw-r--r-- | lib/Parser/Parser.cc | 17 | ||||
| -rw-r--r-- | lib/Parser/ParserExpression.cc | 55 | ||||
| -rw-r--r-- | lib/Parser/ParserFunction.cc | 38 | ||||
| -rw-r--r-- | lib/Parser/ParserType.cc | 162 |
4 files changed, 266 insertions, 6 deletions
diff --git a/lib/Parser/Parser.cc b/lib/Parser/Parser.cc index 48f4df4..3d45a97 100644 --- a/lib/Parser/Parser.cc +++ b/lib/Parser/Parser.cc @@ -51,7 +51,7 @@ void Parser::syncToStatementStart() { case tok_delete: case tok_constant: case tok_declare: case tok_identifier: case tok_operator: case tok_input: case tok_output: case tok_case: case tok_endcase: case tok_otherwise: case tok_if: case tok_while: case tok_repeat: case tok_for: - case tok_overload: case tok_function: case tok_procedure: case tok_call: case tok_return: + case tok_template: case tok_overload: case tok_function: case tok_procedure: case tok_call: case tok_return: case tok_else: case tok_endif: case tok_endwhile: case tok_until: case tok_next: case tok_endfunction: case tok_endprocedure: case tok_openfile: case tok_readfile: case tok_writefile: case tok_closefile: @@ -97,6 +97,7 @@ std::unique_ptr<StmtAST> Parser::ParseStatement() { } std::unique_ptr<StmtAST> Parser::ParseStatementImpl() { + if (CurTok == tok_template) return ParseTemplateDecl(); if (CurTok == tok_extern) { if (BlockDepth != 1) { sourceDiagnostic(Lex.getLine(), "error: EXTERN is only allowed at top level at line %d\n", Lex.getLine()); @@ -295,17 +296,27 @@ std::unique_ptr<StmtAST> Parser::ParseAssignStmt() { getNextToken(); } + std::vector<std::string> TemplateArgs; + if (CurTok == '<') { + auto Parsed = ParseTemplateArgs(); + if (!Parsed) return nullptr; + TemplateArgs = std::move(*Parsed); + if (CurTok != '(') { + sourceDiagnostic(Line, "error: Expected '(' after explicit template arguments at line %d\n", Line); + return nullptr; + } + } if (CurTok == '(') { getNextToken(); auto Args = ParseExprList(')', true); if (!Args) return nullptr; if (CurTok == '.') { - auto Result = ParseResultMethods(std::make_unique<CallExprAST>(Name, std::move(*Args), Line), Line); + auto Result = ParseResultMethods(std::make_unique<CallExprAST>(Name, std::move(*Args), Line, std::move(TemplateArgs)), Line); if (!Result) return nullptr; return std::make_unique<MemberCallStmtAST>( std::unique_ptr<MemberCallExprAST>(static_cast<MemberCallExprAST *>(Result.release()))); } - return std::make_unique<CallStmtAST>(Name, std::move(*Args), Line); + return std::make_unique<CallStmtAST>(Name, std::move(*Args), Line, std::move(TemplateArgs)); } std::vector<DesignatorAccess> Accesses; diff --git a/lib/Parser/ParserExpression.cc b/lib/Parser/ParserExpression.cc index be22b4e..ef8e1d3 100644 --- a/lib/Parser/ParserExpression.cc +++ b/lib/Parser/ParserExpression.cc @@ -3,6 +3,7 @@ #include "cps/FunctionAST.h" #include <cstdio> #include <limits> +#include <functional> using namespace cps; @@ -40,6 +41,39 @@ std::unique_ptr<ExprAST> Parser::ParseNumberExpr() { return std::make_unique<IntegerExprAST>(Value); } +// Grammar lookahead distinguishes a template call from a comparison without +// consuming tokens. Like C#, A < B, C > (D) selects the template-call grammar. +bool Parser::isTemplateCall() { + if (CurTok != '<') return false; + unsigned Offset = 0; + auto IsTypeToken = [](int Tok) { + return Tok == tok_identifier || Tok == tok_integer_kw || Tok == tok_real_kw || + Tok == tok_boolean_kw || Tok == tok_string_kw || Tok == tok_char_kw || + Tok == tok_c_int || Tok == tok_c_char || Tok == tok_c_string || Tok == tok_c_double; + }; + std::function<bool()> Type = [&] { + if (!IsTypeToken(Lex.peekToken(Offset))) return false; + ++Offset; + if (Lex.peekToken(Offset) == '<') { + ++Offset; + if (!Type()) return false; + while (Lex.peekToken(Offset) == ',') { + ++Offset; + if (!Type()) return false; + } + if (Lex.peekToken(Offset++) != '>') return false; + } + if (Lex.peekToken(Offset) == tok_ellipsis) ++Offset; + return true; + }; + if (!Type()) return false; + while (Lex.peekToken(Offset) == ',') { + ++Offset; + if (!Type()) return false; + } + return Lex.peekToken(Offset) == '>' && Lex.peekToken(Offset + 1) == '('; +} + std::unique_ptr<ExprAST> Parser::ParseIdentifierExpr() { int Line = Lex.getLine(); std::string IdName; @@ -52,6 +86,25 @@ std::unique_ptr<ExprAST> Parser::ParseIdentifierExpr() { getNextToken(); } + if (PackNames.count(IdName)) { + if (CurTok != tok_ellipsis) { + sourceDiagnostic(Line, "error: A parameter pack must be expanded with '...' at line %d\n", Line); + return nullptr; + } + getNextToken(); + return std::make_unique<PackExpansionExprAST>(IdName, Line); + } + if (CurTok == tok_ellipsis) { + sourceDiagnostic(Line, "error: Only a parameter pack may be expanded at line %d\n", Line); + getNextToken(); + return nullptr; + } + std::vector<std::string> TemplateArgs; + if (isTemplateCall()) { + auto Parsed = ParseTemplateArgs(); + if (!Parsed) return nullptr; + TemplateArgs = std::move(*Parsed); + } if (CurTok == '(' && !atCaseBoundary()) { getNextToken(); auto Args = ParseExprList(')', true); @@ -61,7 +114,7 @@ std::unique_ptr<ExprAST> Parser::ParseIdentifierExpr() { Lex.getLine()); return nullptr; } - return ParseResultMethods(std::make_unique<CallExprAST>(IdName, std::move(*Args), Line), Line); + return ParseResultMethods(std::make_unique<CallExprAST>(IdName, std::move(*Args), Line, std::move(TemplateArgs)), Line); } std::vector<DesignatorAccess> Accesses; diff --git a/lib/Parser/ParserFunction.cc b/lib/Parser/ParserFunction.cc index cf8ecf8..a1f04c0 100644 --- a/lib/Parser/ParserFunction.cc +++ b/lib/Parser/ParserFunction.cc @@ -106,6 +106,21 @@ std::optional<std::vector<ParamDecl>> Parser::ParsePrototypeArgs(bool AllowByRef cps::sourceDiagnostic(Lex.getLine(), "error: Expected argument type for '%s'\n", P.Name.c_str()); return std::nullopt; } + if (CurTok == tok_ellipsis) { + if (P.IsArray || !isTemplatePack(P.TypeName)) { + sourceDiagnostic(P.Line, "error: A pack parameter must use the current template pack and cannot be ARRAY at line %d\n", P.Line); + return std::nullopt; + } + P.IsPack = true; + getNextToken(); + if (CurTok != ')') { + sourceDiagnostic(P.Line, "error: A pack parameter must be last at line %d\n", P.Line); + return std::nullopt; + } + } else if (isTemplatePack(P.TypeName)) { + sourceDiagnostic(P.Line, "error: A template parameter pack requires '...' at line %d\n", P.Line); + return std::nullopt; + } for (const ParamDecl &Prev : Params) { if (sameIdentifier(Prev.Name, P.Name)) { @@ -198,7 +213,11 @@ std::unique_ptr<FunctionDefAST> Parser::ParseFunction(const std::string &OwnerCl auto Proto = std::make_unique<PrototypeAST>(Name, std::move(*Args), RetType, false, OwnerClass, IsPublic, IsOverload); Proto->setOperator(Operator); + auto SavedPacks = std::move(PackNames); + PackNames.clear(); + for (const auto &P : Proto->getParams()) if (P.IsPack) PackNames.insert(P.Name); std::vector<std::unique_ptr<StmtAST>> Body = ParseBlock({tok_endfunction}); + PackNames = std::move(SavedPacks); if (CurTok != tok_endfunction) { cps::sourceDiagnostic(Lex.getLine(), "error: expected ENDFUNCTION\n"); @@ -250,7 +269,11 @@ std::unique_ptr<FunctionDefAST> Parser::ParseProcedure(const std::string &OwnerC auto Proto = std::make_unique<PrototypeAST>(Name, std::move(*Args), "VOID", false, OwnerClass, IsPublic, IsOverload); Proto->setOperator(Operator); + auto SavedPacks = std::move(PackNames); + PackNames.clear(); + for (const auto &P : Proto->getParams()) if (P.IsPack) PackNames.insert(P.Name); std::vector<std::unique_ptr<StmtAST>> Body = ParseBlock({tok_endprocedure}); + PackNames = std::move(SavedPacks); if (CurTok != tok_endprocedure) { cps::sourceDiagnostic(Lex.getLine(), "error: expected ENDPROCEDURE\n"); @@ -370,6 +393,17 @@ std::unique_ptr<StmtAST> Parser::ParseCallStmt() { getNextToken(); } + std::vector<std::string> TemplateArgs; + if (CurTok == '<') { + auto Parsed = ParseTemplateArgs(); + if (!Parsed) return nullptr; + TemplateArgs = std::move(*Parsed); + if (CurTok != '(') { + sourceDiagnostic(Line, "error: Expected '(' after explicit template arguments at line %d\n", Line); + return nullptr; + } + } + std::vector<DesignatorAccess> Accesses; if (!ParseDesignatorSuffix(Accesses)) return nullptr; if (!Accesses.empty()) { @@ -387,12 +421,12 @@ std::unique_ptr<StmtAST> Parser::ParseCallStmt() { } if (CurTok == '.') { - auto Result = ParseResultMethods(std::make_unique<CallExprAST>(Callee, std::move(Args), Line), Line); + auto Result = ParseResultMethods(std::make_unique<CallExprAST>(Callee, std::move(Args), Line, std::move(TemplateArgs)), Line); if (!Result) return nullptr; return std::make_unique<MemberCallStmtAST>( std::unique_ptr<MemberCallExprAST>(static_cast<MemberCallExprAST *>(Result.release()))); } - return std::make_unique<CallStmtAST>(Callee, std::move(Args), Line); + return std::make_unique<CallStmtAST>(Callee, std::move(Args), Line, std::move(TemplateArgs)); } std::unique_ptr<StmtAST> Parser::ParseReturnStmt() { diff --git a/lib/Parser/ParserType.cc b/lib/Parser/ParserType.cc index 64330e4..21696d0 100644 --- a/lib/Parser/ParserType.cc +++ b/lib/Parser/ParserType.cc @@ -5,6 +5,136 @@ using namespace cps; +bool Parser::isTemplatePack(const std::string &Name) const { + for (const auto &P : CurrentTemplate) + if (P.IsPack && sameIdentifier(P.Name, Name)) return true; + return false; +} + +// TEMPLATE <T, Rest...> [OVERLOAD] FUNCTION/PROCEDURE or CLASS. +// Invalid nesting still consumes the entire declaration before diagnosing it. +std::unique_ptr<StmtAST> Parser::ParseTemplateDecl() { + int Line = Lex.getLine(); + bool AtTopLevel = BlockDepth == 1 && CurrentTemplate.empty(); + getNextToken(); + if (CurTok != '<') { + sourceDiagnostic(Line, "error: Expected '<' after TEMPLATE at line %d\n", Line); + return nullptr; + } + getNextToken(); + std::vector<TemplateParam> Params; + IdentifierSet Names; + bool Invalid = false; + bool SeenPack = false; + while (true) { + if (CurTok != tok_identifier) { + sourceDiagnostic(Lex.getLine(), "error: Expected template parameter name at line %d\n", Lex.getLine()); + return nullptr; + } + TemplateParam P{Lex.IdentifierStr, false, Lex.getLine()}; + if (!Names.insert(P.Name).second) { + sourceDiagnostic(P.Line, "error: Duplicate template parameter '%s' at line %d\n", P.Name.c_str(), P.Line); + Invalid = true; + } + getNextToken(); + if (CurTok == tok_ellipsis) { + P.IsPack = true; + if (SeenPack) { + sourceDiagnostic(P.Line, "error: A template may have only one parameter pack at line %d\n", P.Line); + Invalid = true; + } + SeenPack = true; + getNextToken(); + } + Params.push_back(std::move(P)); + if (CurTok == '>') break; + if (CurTok != ',') { + sourceDiagnostic(Lex.getLine(), "error: Expected ',' or '>' in TEMPLATE at line %d\n", Lex.getLine()); + return nullptr; + } + if (Params.back().IsPack) { + sourceDiagnostic(Params.back().Line, "error: A template parameter pack must be last at line %d\n", Params.back().Line); + Invalid = true; + } + getNextToken(); + } + getNextToken(); + + auto SavedTemplate = std::move(CurrentTemplate); + auto SavedTypes = TypeNames; + CurrentTemplate = Params; + for (const auto &P : Params) TypeNames.insert(P.Name); + bool IsOverload = CurTok == tok_overload; + if (IsOverload) getNextToken(); + std::unique_ptr<StmtAST> Decl; + if (CurTok == tok_function || CurTok == tok_procedure) { + bool Operator = Lex.peekToken() == tok_operator; + if (Operator) { + sourceDiagnostic(Line, "error: Operator templates are not supported at line %d\n", Line); + Invalid = true; + int End = CurTok == tok_function ? tok_endfunction : tok_endprocedure; + do { getNextToken(); } while (CurTok != End && CurTok != tok_eof); + if (CurTok == End) getNextToken(); + } else { + auto Function = CurTok == tok_function ? ParseFunction({}, true, IsOverload) + : ParseProcedure({}, true, IsOverload); + if (Function) Decl = std::make_unique<TemplateDeclAST>(Params, std::move(Function)); + } + } else if (CurTok == tok_class && !IsOverload) { + int ClassLine = Lex.getLine(); + auto Class = ParseTypeDecl(); + if (Class) { + Class->setLine(ClassLine); + Decl = std::make_unique<TemplateDeclAST>(Params, + std::unique_ptr<ClassTypeDeclAST>(static_cast<ClassTypeDeclAST *>(Class.release()))); + } + } else if (CurTok == tok_type) { + sourceDiagnostic(Line, "error: TEMPLATE cannot prefix TYPE; use CLASS at line %d\n", Line); + ParseTypeDecl(); + Invalid = true; + } else { + sourceDiagnostic(Line, "error: TEMPLATE must be followed by FUNCTION, PROCEDURE or CLASS at line %d\n", Line); + Invalid = true; + } + CurrentTemplate = std::move(SavedTemplate); + TypeNames = std::move(SavedTypes); + if (!AtTopLevel) { + sourceDiagnostic(Line, "error: TEMPLATE declarations are only allowed at top level at line %d\n", Line); + Invalid = true; + } + if (Invalid || !Decl) return nullptr; + if (auto *T = dynamic_cast<TemplateDeclAST *>(Decl.get()); T->getClass()) TypeNames.insert(T->getName()); + Decl->setLine(Line); + return Decl; +} + +std::optional<std::vector<std::string>> Parser::ParseTemplateArgs() { + getNextToken(); // '<' + std::vector<std::string> Args; + while (true) { + int Line = Lex.getLine(); + std::string Type = ParseTypeName(false); + if (Type.empty()) return std::nullopt; + if (CurTok == tok_ellipsis) { + if (!isTemplatePack(Type)) { + sourceDiagnostic(Line, "error: Only the current template parameter pack may use '...' in a type argument at line %d\n", Line); + return std::nullopt; + } + Type += "..."; + getNextToken(); + } + Args.push_back(std::move(Type)); + if (CurTok == '>') break; + if (CurTok != ',') { + sourceDiagnostic(Lex.getLine(), "error: Expected ',' or '>' in template arguments at line %d\n", Lex.getLine()); + return std::nullopt; + } + getNextToken(); + } + getNextToken(); + return Args; +} + // TYPE <id> = (value1, value2, ...) enumerated type // TYPE <id> = ^<type> pointer type // TYPE <id> record type @@ -29,6 +159,7 @@ std::unique_ptr<StmtAST> Parser::ParseTypeDecl() { } std::string Name = CurTok == tok_string_kw ? "STRING" : Lex.IdentifierStr; getNextToken(); + if (IsClass && !CurrentTemplate.empty()) TypeNames.insert(Name); std::unique_ptr<StmtAST> Decl; @@ -122,6 +253,15 @@ std::optional<std::vector<RecordFieldDecl>> Parser::ParseRecordBody( std::vector<RecordFieldDecl> Fields; bool HadFieldError = false; while (CurTok != EndToken && CurTok != tok_eof) { + if (CurTok == tok_template) { + // A class body has no ParseBlock of its own. + ++BlockDepth; + ParseTemplateDecl(); + --BlockDepth; + ++NumErrors; + HadFieldError = true; + continue; + } if (AtField()) { // Class fields default to public, like record fields. bool IsPublic = CurTok != tok_private; @@ -154,6 +294,12 @@ std::optional<std::vector<RecordFieldDecl>> Parser::ParseRecordBody( while (!AtField() && CurTok != EndToken && CurTok != tok_eof) { if (CurTok == tok_class || CurTok == tok_type) ParseTypeDecl(); + else if (CurTok == tok_template) { + ++BlockDepth; + ParseTemplateDecl(); + --BlockDepth; + ++NumErrors; + } else getNextToken(); } @@ -175,6 +321,7 @@ std::optional<std::vector<RecordFieldDecl>> Parser::ParseRecordBody( // <field introducer> <field> : <type>. Array fields are deferred. bool Parser::ParseRecordField(std::vector<RecordFieldDecl> &Out, bool IsPublic) { + int Line = Lex.getLine(); const char *Introducer = CurTok == tok_declare ? "DECLARE" : CurTok == tok_public ? "PUBLIC" : "PRIVATE"; if (CurTok != tok_identifier) getNextToken(); // optional class access keyword @@ -200,6 +347,11 @@ bool Parser::ParseRecordField(std::vector<RecordFieldDecl> &Out, bool IsPublic) std::string TypeName = ParseTypeName(false); if (TypeName.empty()) return false; + if (CurTok == tok_ellipsis || isTemplatePack(TypeName)) { + sourceDiagnostic(Line, "error: Pack fields are not supported at line %d\n", Line); + if (CurTok == tok_ellipsis) getNextToken(); + return false; + } Out.push_back({FieldName, TypeName, IsPublic}); return true; @@ -240,6 +392,16 @@ std::string Parser::ParseTypeName(bool AllowVoid) { return ""; } getNextToken(); + if (CurTok == '<') { + auto Args = ParseTemplateArgs(); + if (!Args) return {}; + TypeName += "<"; + for (size_t I = 0; I < Args->size(); ++I) { + if (I) TypeName += ","; + TypeName += (*Args)[I]; + } + TypeName += ">"; + } return TypeName; } |
