aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/CodeGen/CodeGen.cc7
-rw-r--r--lib/CodeGen/StatementGen.cc2
-rw-r--r--lib/Lexer/Lexer.cc1
-rw-r--r--lib/Parser/Parser.cc17
-rw-r--r--lib/Parser/ParserExpression.cc55
-rw-r--r--lib/Parser/ParserFunction.cc38
-rw-r--r--lib/Parser/ParserType.cc162
7 files changed, 275 insertions, 7 deletions
diff --git a/lib/CodeGen/CodeGen.cc b/lib/CodeGen/CodeGen.cc
index 30b6131..641b424 100644
--- a/lib/CodeGen/CodeGen.cc
+++ b/lib/CodeGen/CodeGen.cc
@@ -6,6 +6,7 @@
#include "cps/ExceptionHandler.h"
#include "Builtins.h"
#include "cps/TypeAST.h"
+#include "cps/TemplateAST.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/Verifier.h"
#include <cstdarg>
@@ -209,7 +210,11 @@ void CodeGen::compile(const std::vector<SourceUnit> &Sources) {
for (const auto &S : Sources[I].Statements) {
SaveAndRestore<int> Location(DiagnosticLine, S->getLine());
std::string TypeName;
- if (auto *F = dynamic_cast<FunctionDefAST*>(S.get())) {
+ if (auto *T = dynamic_cast<TemplateDeclAST*>(S.get())) {
+ auto *Function = T->getFunction();
+ Reserve(T->getName(), Function != nullptr, Function && Function->getProto()->isOverload());
+ Templates[T->getName()].push_back({T, I});
+ } else if (auto *F = dynamic_cast<FunctionDefAST*>(S.get())) {
Reserve(F->getProto()->getName(), true, F->getProto()->isOverload());
if (sameIdentifier(F->getProto()->getName(), "MAIN")) {
if (Entry) reportError("Multiple MAIN definitions in %s and %s",
diff --git a/lib/CodeGen/StatementGen.cc b/lib/CodeGen/StatementGen.cc
index 5b60d29..6bc4ec4 100644
--- a/lib/CodeGen/StatementGen.cc
+++ b/lib/CodeGen/StatementGen.cc
@@ -5,6 +5,7 @@
#include "cps/MemoryHandler.h"
#include "cps/ExceptionHandler.h"
#include "cps/TypeAST.h"
+#include "cps/TemplateAST.h"
using namespace llvm;
using namespace cps;
@@ -18,6 +19,7 @@ void CodeGen::emitStmt(StmtAST *Stmt) {
void CodeGen::emitStmtImpl(StmtAST *Stmt) {
SaveAndRestore<int> Location(DiagnosticLine, Stmt && Stmt->getLine() ? Stmt->getLine() : DiagnosticLine);
if (!Stmt) return;
+ if (dynamic_cast<TemplateDeclAST*>(Stmt)) return;
if (dynamic_cast<IncludeStmtAST*>(Stmt)) return;
if (dynamic_cast<GlobalBlockAST*>(Stmt)) return;
diff --git a/lib/Lexer/Lexer.cc b/lib/Lexer/Lexer.cc
index 6d0246a..91ce88a 100644
--- a/lib/Lexer/Lexer.cc
+++ b/lib/Lexer/Lexer.cc
@@ -221,6 +221,7 @@ int Lexer::readToken() {
if (IdentifierStr == "OPERATOR") return tok_operator;
if (IdentifierStr == "OVERLOAD") return tok_overload;
+ if (IdentifierStr == "TEMPLATE") return tok_template;
if (IdentifierStr == "FUNCTION") return tok_function;
if (IdentifierStr == "ENDFUNCTION") return tok_endfunction;
if (IdentifierStr == "PROCEDURE") return tok_procedure;
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;
}