diff --git a/example/src/main.nub b/example/src/main.nub index d86e614..68ecaf5 100644 --- a/example/src/main.nub +++ b/example/src/main.nub @@ -1,4 +1,4 @@ -template struct Box +struct Box { value: T } diff --git a/src/compiler/NubLang/Syntax/Parsing/Node/DefinitionSyntax.cs b/src/compiler/NubLang/Syntax/Parsing/Node/DefinitionSyntax.cs index a816e6e..ce7f373 100644 --- a/src/compiler/NubLang/Syntax/Parsing/Node/DefinitionSyntax.cs +++ b/src/compiler/NubLang/Syntax/Parsing/Node/DefinitionSyntax.cs @@ -3,24 +3,26 @@ using NubLang.Syntax.Tokenization; namespace NubLang.Syntax.Parsing.Node; -public abstract record DefinitionSyntax(IReadOnlyList Tokens, string Namespace) : SyntaxNode(Tokens); +public record TemplateParameterSyntax(IReadOnlyList Tokens, string Name) : SyntaxNode(Tokens); + +public abstract record DefinitionSyntax(IReadOnlyList Tokens, string Namespace, IReadOnlyList Parameters) : SyntaxNode(Tokens); public record FuncParameterSyntax(IReadOnlyList Tokens, string Name, TypeSyntax Type) : SyntaxNode(Tokens); public record FuncSignatureSyntax(IReadOnlyList Tokens, IReadOnlyList Parameters, TypeSyntax ReturnType) : SyntaxNode(Tokens); -public record LocalFuncSyntax(IReadOnlyList Tokens, string Namespace, string Name, FuncSignatureSyntax Signature, BlockSyntax Body) : DefinitionSyntax(Tokens, Namespace); +public record LocalFuncSyntax(IReadOnlyList Tokens, string Namespace, IReadOnlyList Parameters, string Name, FuncSignatureSyntax Signature, BlockSyntax Body) : DefinitionSyntax(Tokens, Namespace, Parameters); -public record ExternFuncSyntax(IReadOnlyList Tokens, string Namespace, string Name, string CallName, FuncSignatureSyntax Signature) : DefinitionSyntax(Tokens, Namespace); +public record ExternFuncSyntax(IReadOnlyList Tokens, string Namespace, IReadOnlyList Parameters, string Name, string CallName, FuncSignatureSyntax Signature) : DefinitionSyntax(Tokens, Namespace, Parameters); public record StructFieldSyntax(IReadOnlyList Tokens, int Index, string Name, TypeSyntax Type, Optional Value) : SyntaxNode(Tokens); -public record StructSyntax(IReadOnlyList Tokens, string Namespace, string Name, IReadOnlyList Fields) : DefinitionSyntax(Tokens, Namespace); +public record StructSyntax(IReadOnlyList Tokens, string Namespace, IReadOnlyList Parameters, string Name, IReadOnlyList Fields) : DefinitionSyntax(Tokens, Namespace, Parameters); public record TraitFuncSyntax(IReadOnlyList Tokens, string Name, FuncSignatureSyntax Signature) : SyntaxNode(Tokens); -public record TraitSyntax(IReadOnlyList Tokens, string Namespace, string Name, IReadOnlyList Functions) : DefinitionSyntax(Tokens, Namespace); +public record TraitSyntax(IReadOnlyList Tokens, string Namespace, IReadOnlyList Parameters, string Name, IReadOnlyList Functions) : DefinitionSyntax(Tokens, Namespace, Parameters); public record TraitFuncImplSyntax(IReadOnlyList Tokens, string Name, FuncSignatureSyntax Signature, BlockSyntax Body) : SyntaxNode(Tokens); -public record TraitImplSyntax(IReadOnlyList Tokens, string Namespace, TypeSyntax TraitType, TypeSyntax ForType, IReadOnlyList Functions) : DefinitionSyntax(Tokens, Namespace); \ No newline at end of file +public record TraitImplSyntax(IReadOnlyList Tokens, string Namespace, IReadOnlyList Parameters, TypeSyntax TraitType, TypeSyntax ForType, IReadOnlyList Functions) : DefinitionSyntax(Tokens, Namespace, Parameters); \ No newline at end of file diff --git a/src/compiler/NubLang/Syntax/Parsing/Parser.cs b/src/compiler/NubLang/Syntax/Parsing/Parser.cs index 9aa7ad3..0c5ad9f 100644 --- a/src/compiler/NubLang/Syntax/Parsing/Parser.cs +++ b/src/compiler/NubLang/Syntax/Parsing/Parser.cs @@ -38,39 +38,65 @@ public sealed class Parser try { - var definition = ParseDefinition(startIndex); + var keyword = ExpectSymbol(); + var name = ExpectIdentifier(); + var templateParameters = new List(); + + if (TryExpectSymbol(Symbol.LessThan)) + { + while (!TryExpectSymbol(Symbol.GreaterThan)) + { + var argumentStartIndex = _tokenIndex; + var identifier = ExpectIdentifier(); + if (!TryExpectSymbol(Symbol.Comma) && Peek().TryGetValue(out var token) && token is not SymbolToken { Symbol: Symbol.GreaterThan }) + { + _diagnostics.Add(Diagnostic + .Warning("Missing comma between template parameters") + .WithHelp("Add a ',' to separate parameters") + .At(token) + .Build()); + } + + var parameter = new TemplateParameterSyntax(GetTokens(argumentStartIndex), identifier.Value); + templateParameters.Add(parameter); + } + } + + var definition = keyword.Symbol switch + { + Symbol.Extern => ParseExtern(startIndex, name, templateParameters), + Symbol.Func => ParseFunc(startIndex, name, templateParameters), + Symbol.Struct => ParseStruct(startIndex, name, templateParameters), + Symbol.Trait => ParseTrait(startIndex, name, templateParameters), + Symbol.Impl => ParseImpl(startIndex, name, templateParameters), + _ => throw new ParseException(Diagnostic + .Error($"Expected 'extern', 'func', 'struct', 'trait' or 'impl' but found '{keyword.Symbol}'") + .WithHelp("Valid definition keywords are 'extern', 'func', 'struct', 'trait' and 'impl'") + .At(keyword) + .Build()) + }; + definitions.Add(definition); } catch (ParseException ex) { _diagnostics.Add(ex.Diagnostic); - RecoverToNextDefinition(); + while (Peek().HasValue) + { + var token = Peek().Value; + if (token is SymbolToken { Symbol: Symbol.Extern or Symbol.Func or Symbol.Struct or Symbol.Trait or Symbol.Impl }) + { + break; + } + + Next(); + } } } return new SyntaxTree(_namespace, definitions, _diagnostics); } - private DefinitionSyntax ParseDefinition(int startIndex) - { - var keyword = ExpectSymbol(); - var node = keyword.Symbol switch - { - Symbol.Extern => ParseExtern(startIndex), - Symbol.Func => ParseFunc(startIndex), - Symbol.Struct => ParseStruct(startIndex), - Symbol.Trait => ParseTrait(startIndex), - Symbol.Impl => ParseImpl(startIndex), - _ => throw new ParseException(Diagnostic - .Error($"Expected 'extern', 'func', 'struct', 'trait' or 'impl' but found '{keyword.Symbol}'") - .WithHelp("Valid definition keywords are 'extern', 'func', 'struct', 'trait' and 'impl'") - .At(keyword) - .Build()) - }; - - return node; - } - private FuncSignatureSyntax ParseFuncSignature(FuncParameterSyntax? thisArg = null) { var startIndex = _tokenIndex; @@ -113,19 +139,18 @@ public sealed class Parser return new FuncParameterSyntax(GetTokens(startIndex), name.Value, type); } - private DefinitionSyntax ParseExtern(int startIndex) + private DefinitionSyntax ParseExtern(int startIndex, IdentifierToken name, IReadOnlyList templateParameters) { var keyword = ExpectSymbol(); return keyword.Symbol switch { - Symbol.Func => ParseExternFunc(startIndex), + Symbol.Func => ParseExternFunc(startIndex, name, templateParameters), _ => throw new ParseException(Diagnostic.Error($"Unexpected symbol {keyword.Symbol} after extern declaration").At(keyword).Build()) }; } - private ExternFuncSyntax ParseExternFunc(int startIndex) + private ExternFuncSyntax ParseExternFunc(int startIndex, IdentifierToken name, IReadOnlyList templateParameters) { - var name = ExpectIdentifier(); var callName = name.Value; if (TryExpectSymbol(Symbol.Calls)) @@ -135,25 +160,22 @@ public sealed class Parser var signature = ParseFuncSignature(); - return new ExternFuncSyntax(GetTokens(startIndex), _namespace, name.Value, callName, signature); + return new ExternFuncSyntax(GetTokens(startIndex), _namespace, templateParameters, name.Value, callName, signature); } - private LocalFuncSyntax ParseFunc(int startIndex) + private LocalFuncSyntax ParseFunc(int startIndex, IdentifierToken name, IReadOnlyList templateParameters) { - var name = ExpectIdentifier(); var signature = ParseFuncSignature(); var body = ParseBlock(); - return new LocalFuncSyntax(GetTokens(startIndex), _namespace, name.Value, signature, body); + return new LocalFuncSyntax(GetTokens(startIndex), _namespace, templateParameters, name.Value, signature, body); } - private StructSyntax ParseStruct(int startIndex) + private DefinitionSyntax ParseStruct(int startIndex, IdentifierToken name, IReadOnlyList templateParameters) { - var name = ExpectIdentifier().Value; - ExpectSymbol(Symbol.OpenBrace); - List variables = []; + List fields = []; var fieldIndex = 0; @@ -161,27 +183,25 @@ public sealed class Parser { var fieldStartIndex = _tokenIndex; - var variableName = ExpectIdentifier().Value; + var fieldName = ExpectIdentifier().Value; ExpectSymbol(Symbol.Colon); - var variableType = ParseType(); + var fieldType = ParseType(); - var variableValue = Optional.Empty(); + var fieldValue = Optional.Empty(); if (TryExpectSymbol(Symbol.Assign)) { - variableValue = ParseExpression(); + fieldValue = ParseExpression(); } - variables.Add(new StructFieldSyntax(GetTokens(fieldStartIndex), fieldIndex++, variableName, variableType, variableValue)); + fields.Add(new StructFieldSyntax(GetTokens(fieldStartIndex), fieldIndex++, fieldName, fieldType, fieldValue)); } - return new StructSyntax(GetTokens(startIndex), _namespace, name, variables); + return new StructSyntax(GetTokens(startIndex), _namespace, templateParameters, name.Value, fields); } - private TraitSyntax ParseTrait(int startIndex) + private TraitSyntax ParseTrait(int startIndex, IdentifierToken name, IReadOnlyList templateParameters) { - var name = ExpectIdentifier().Value; - ExpectSymbol(Symbol.OpenBrace); List functions = []; @@ -198,10 +218,10 @@ public sealed class Parser functions.Add(new TraitFuncSyntax(GetTokens(funcStartIndex), funcName, signature)); } - return new TraitSyntax(GetTokens(startIndex), _namespace, name, functions); + return new TraitSyntax(GetTokens(startIndex), _namespace, templateParameters, name.Value, functions); } - private TraitImplSyntax ParseImpl(int startIndex) + private TraitImplSyntax ParseImpl(int startIndex, IdentifierToken name, IReadOnlyList templateParameters) { var traitType = ParseType(); ExpectSymbol(Symbol.For); @@ -222,7 +242,7 @@ public sealed class Parser functions.AddRange(new TraitFuncImplSyntax(GetTokens(funcStartIndex), functionName, signature, body)); } - return new TraitImplSyntax(GetTokens(startIndex), _namespace, traitType, forType, functions); + return new TraitImplSyntax(GetTokens(startIndex), _namespace, templateParameters, traitType, forType, functions); } private StatementSyntax ParseStatement() @@ -813,20 +833,6 @@ public sealed class Parser return identifier; } - private void RecoverToNextDefinition() - { - while (Peek().HasValue) - { - var token = Peek().Value; - if (token is SymbolToken { Symbol: Symbol.Extern or Symbol.Func or Symbol.Struct or Symbol.Trait or Symbol.Impl or Symbol.Template }) - { - break; - } - - Next(); - } - } - private Optional Peek(int offset = 0) { var peekIndex = _tokenIndex + offset; diff --git a/src/compiler/NubLang/Syntax/Tokenization/Token.cs b/src/compiler/NubLang/Syntax/Tokenization/Token.cs index f8ac2eb..08cf2a6 100644 --- a/src/compiler/NubLang/Syntax/Tokenization/Token.cs +++ b/src/compiler/NubLang/Syntax/Tokenization/Token.cs @@ -73,5 +73,4 @@ public enum Symbol Extern, Semi, Arrow, - Template } \ No newline at end of file diff --git a/src/compiler/NubLang/Syntax/Tokenization/Tokenizer.cs b/src/compiler/NubLang/Syntax/Tokenization/Tokenizer.cs index b1ee7c4..7dc0731 100644 --- a/src/compiler/NubLang/Syntax/Tokenization/Tokenizer.cs +++ b/src/compiler/NubLang/Syntax/Tokenization/Tokenizer.cs @@ -23,7 +23,6 @@ public sealed class Tokenizer ["impl"] = Symbol.Impl, ["for"] = Symbol.For, ["extern"] = Symbol.Extern, - ["template"] = Symbol.Template, }; private static readonly Dictionary Chians = new()