diff --git a/example/src/main.nub b/example/src/main.nub index 40026cd..289a030 100644 --- a/example/src/main.nub +++ b/example/src/main.nub @@ -1,57 +1,16 @@ -struct Name +template struct Box { - first: cstring - last: cstring -} - -struct Human -{ - name: Name - age: i64 -} - -trait Printable -{ - func print() -} - -impl Printable for Human -{ - func print() { - c::puts(this.name.first) - } + value: T } func main(args: []cstring): i64 { - let human = alloc Human + let box = alloc Box { - name = alloc Name { - first = "john" - last = "doe" - } - age = 23 + value = "bob" } - human.print() - print_result(12, func (num) => num == 12) - - let x: cstring = "test" - - let addr = x& - - c::puts(addr^) + c::puts(box.value) return 0 } - -func print_result(num: u64, delegate: func(u64): bool) { - if (delegate(num)) - { - c::puts("true") - } - else - { - c::puts("false") - } -} \ No newline at end of file diff --git a/src/compiler/NubLang/NubLang.csproj b/src/compiler/NubLang/NubLang.csproj index 3cd156f..906da4f 100644 --- a/src/compiler/NubLang/NubLang.csproj +++ b/src/compiler/NubLang/NubLang.csproj @@ -11,9 +11,4 @@ - - - - - diff --git a/src/compiler/NubLang/NubType.cs b/src/compiler/NubLang/NubType.cs index f926a51..7a48e8a 100644 --- a/src/compiler/NubLang/NubType.cs +++ b/src/compiler/NubLang/NubType.cs @@ -34,7 +34,7 @@ public abstract class NubType : IEquatable return false; } - throw new ArgumentException($"Type {this} is not a simple type, not a compex type"); + throw new ArgumentException($"Type {this} is not a simple type nor a compex type"); } public abstract int Size(BoundDefinitionTable definitionTable); @@ -225,10 +225,11 @@ public class NubStringType : NubComplexType public override int GetHashCode() => HashCode.Combine(typeof(NubStringType)); } -public class NubCustomType(string @namespace, string name) : NubComplexType +public class NubCustomType(string @namespace, string name, IReadOnlyList parameters) : NubComplexType { public string Namespace { get; } = @namespace; public string Name { get; } = name; + public IReadOnlyList Parameters { get; } = parameters; public CustomTypeKind Kind(BoundDefinitionTable definitionTable) { @@ -288,9 +289,22 @@ public class NubCustomType(string @namespace, string name) : NubComplexType } } - public override string ToString() => $"{Namespace}::{Name}"; - public override bool Equals(NubType? other) => other is NubCustomType custom && Namespace == custom.Namespace && Name == custom.Name; - public override int GetHashCode() => HashCode.Combine(typeof(NubCustomType), Namespace, Name); + public override string ToString() => $"{Namespace}::{Name}{(Parameters.Any() ? $"<{string.Join(",", Parameters)}>" : "")}"; + public override bool Equals(NubType? other) => other is NubCustomType custom && Namespace == custom.Namespace && Name == custom.Name && Parameters.SequenceEqual(custom.Parameters); + + public override int GetHashCode() + { + var hash = new HashCode(); + hash.Add(typeof(NubFuncType)); + hash.Add(Namespace); + hash.Add(Name); + foreach (var param in Parameters) + { + hash.Add(param); + } + + return hash.ToHashCode(); + } } public enum CustomTypeKind diff --git a/src/compiler/NubLang/Syntax/Binding/Binder.cs b/src/compiler/NubLang/Syntax/Binding/Binder.cs index 6376b35..17972e2 100644 --- a/src/compiler/NubLang/Syntax/Binding/Binder.cs +++ b/src/compiler/NubLang/Syntax/Binding/Binder.cs @@ -34,7 +34,7 @@ public sealed class Binder { try { - definitions.Add(BindTopLevel(definition)); + definitions.Add(BindDefinition(definition)); } catch (BindException e) { @@ -45,15 +45,15 @@ public sealed class Binder return new BoundSyntaxTree(_syntaxTree.Namespace, definitions, diagnostics); } - private BoundDefinition BindTopLevel(DefinitionSyntax node) + private BoundDefinition BindDefinition(DefinitionSyntax node) { return node switch { - ExternFuncSyntax topLevel => BindExternFuncDefinition(topLevel), - TraitImplSyntax topLevel => BindTraitImplementation(topLevel), - TraitSyntax topLevel => BindTraitDefinition(topLevel), - LocalFuncSyntax topLevel => BindLocalFuncDefinition(topLevel), - StructSyntax topLevel => BindStruct(topLevel), + ExternFuncSyntax definition => BindExternFuncDefinition(definition), + TraitImplSyntax definition => BindTraitImplementation(definition), + TraitSyntax definition => BindTraitDefinition(definition), + LocalFuncSyntax definition => BindLocalFuncDefinition(definition), + StructSyntax definition => BindStruct(definition), _ => throw new ArgumentOutOfRangeException(nameof(node)) }; } @@ -403,7 +403,7 @@ public sealed class Binder { if (traits.Length > 1) { - throw new BindException(Diagnostic.Error($"Trait {customType.Namespace}::{customType.Name} has multiple definitions").Build()); + throw new BindException(Diagnostic.Error($"Trait {customType} has multiple definitions").Build()); } var trait = traits[0]; @@ -413,7 +413,7 @@ public sealed class Binder { if (traits.Length > 1) { - throw new BindException(Diagnostic.Error($"Trait {trait.Namespace}::{trait.Name} has multiple functions with the name {expression.Member}").Build()); + throw new BindException(Diagnostic.Error($"Trait {customType} has multiple functions with the name {expression.Member}").Build()); } var traitFunc = traitFuncs[0]; @@ -428,7 +428,7 @@ public sealed class Binder { if (structs.Length > 1) { - throw new BindException(Diagnostic.Error($"Struct {customType.Namespace}::{customType.Name} has multiple definitions").Build()); + throw new BindException(Diagnostic.Error($"Struct {customType} has multiple definitions").Build()); } var @struct = structs[0]; @@ -438,7 +438,7 @@ public sealed class Binder { if (fields.Length > 1) { - throw new BindException(Diagnostic.Error($"Struct {@struct.Namespace}::{@struct.Name} has multiple fields with the name {expression.Member}").Build()); + throw new BindException(Diagnostic.Error($"Struct {customType} has multiple fields with the name {expression.Member}").Build()); } var field = fields[0]; @@ -462,12 +462,12 @@ public sealed class Binder if (structs.Length == 0) { - throw new BindException(Diagnostic.Error($"Struct {structType.Namespace}::{structType.Name} is not defined").Build()); + throw new BindException(Diagnostic.Error($"Struct {structType} is not defined").Build()); } if (structs.Length > 1) { - throw new BindException(Diagnostic.Error($"Struct {structType.Namespace}::{structType.Name} has multiple definitions").Build()); + throw new BindException(Diagnostic.Error($"Struct {structType} has multiple definitions").Build()); } var @struct = structs[0]; @@ -480,18 +480,18 @@ public sealed class Binder if (fields.Length == 0) { - throw new BindException(Diagnostic.Error($"Struct {@struct.Namespace}::{@struct.Name} does not have a field with the name {field}").Build()); + throw new BindException(Diagnostic.Error($"Struct {structType} does not have a field with the name {field}").Build()); } if (fields.Length > 1) { - throw new BindException(Diagnostic.Error($"Struct {@struct.Namespace}::{@struct.Name} has multiple fields with the name {field}").Build()); + throw new BindException(Diagnostic.Error($"Struct {structType} has multiple fields with the name {field}").Build()); } initializers[field] = BindExpression(initializer, fields[0].Type); } - return new BoundStructInitializer(expression.Tokens, structType, new NubCustomType(@struct.Namespace, @struct.Name), initializers); + return new BoundStructInitializer(expression.Tokens, structType, initializers); } private BoundUnaryExpression BindUnaryExpression(UnaryExpressionSyntax expression) diff --git a/src/compiler/NubLang/Syntax/Binding/Node/BoundExpression.cs b/src/compiler/NubLang/Syntax/Binding/Node/BoundExpression.cs index e218bd5..70f4da0 100644 --- a/src/compiler/NubLang/Syntax/Binding/Node/BoundExpression.cs +++ b/src/compiler/NubLang/Syntax/Binding/Node/BoundExpression.cs @@ -52,6 +52,6 @@ public record BoundTraitImplFuncAccess(IReadOnlyList Tokens, NubType Type public record BoundTraitFuncAccess(IReadOnlyList Tokens, NubType Type, NubCustomType TraitType, BoundExpression Target, string FuncName) : BoundExpression(Tokens, Type); -public record BoundStructInitializer(IReadOnlyList Tokens, NubType Type, NubCustomType StructType, Dictionary Initializers) : BoundExpression(Tokens, Type); +public record BoundStructInitializer(IReadOnlyList Tokens, NubCustomType StructType, Dictionary Initializers) : BoundExpression(Tokens, StructType); public record BoundDereference(IReadOnlyList Tokens, NubType Type, BoundExpression Expression) : BoundExpression(Tokens, Type); \ No newline at end of file diff --git a/src/compiler/NubLang/Syntax/Parsing/Node/SyntaxTree.cs b/src/compiler/NubLang/Syntax/Parsing/Node/SyntaxTree.cs index fee54d1..bae6008 100644 --- a/src/compiler/NubLang/Syntax/Parsing/Node/SyntaxTree.cs +++ b/src/compiler/NubLang/Syntax/Parsing/Node/SyntaxTree.cs @@ -3,7 +3,7 @@ using NubLang.Syntax.Tokenization; namespace NubLang.Syntax.Parsing.Node; -public record SyntaxTree(string Namespace, IReadOnlyList Definitions, IReadOnlyList Diagnostics); +public record SyntaxTree(string Namespace, IReadOnlyList Definitions, IReadOnlyList Templates, IReadOnlyList Diagnostics); public abstract record SyntaxNode(IReadOnlyList Tokens); diff --git a/src/compiler/NubLang/Syntax/Parsing/Node/TemplateSyntax.cs b/src/compiler/NubLang/Syntax/Parsing/Node/TemplateSyntax.cs new file mode 100644 index 0000000..52e628c --- /dev/null +++ b/src/compiler/NubLang/Syntax/Parsing/Node/TemplateSyntax.cs @@ -0,0 +1,7 @@ +using NubLang.Syntax.Tokenization; + +namespace NubLang.Syntax.Parsing.Node; + +public abstract record TemplateSyntax(IReadOnlyList Tokens, string Namespace, IReadOnlyList TemplateParameters) : SyntaxNode(Tokens); + +public record StructTemplateSyntax(IReadOnlyList Tokens, string Namespace, IReadOnlyList TemplateParameters, StructSyntax Struct) : TemplateSyntax(Tokens, Namespace, TemplateParameters); \ 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 00eb01f..3f12dbe 100644 --- a/src/compiler/NubLang/Syntax/Parsing/Parser.cs +++ b/src/compiler/NubLang/Syntax/Parsing/Parser.cs @@ -30,13 +30,35 @@ public sealed class Parser _namespace = ExpectIdentifier().Value; } - List definitions = []; + var definitions = new List(); + var templates = new List(); while (Peek().HasValue) { + var startIndex = _tokenIndex; + try { - definitions.Add(ParseDefinition()); + if (TryExpectSymbol(Symbol.Template)) + { + var templateArguments = new List(); + + ExpectSymbol(Symbol.LessThan); + while (!TryExpectSymbol(Symbol.GreaterThan)) + { + templateArguments.Add(ExpectIdentifier().Value); + } + + ExpectSymbol(Symbol.Struct); + + var definition = ParseStruct(_tokenIndex); + templates.Add(new StructTemplateSyntax(GetTokens(startIndex), _namespace, templateArguments, definition)); + } + else + { + var definition = ParseDefinition(startIndex); + definitions.Add(definition); + } } catch (ParseException ex) { @@ -45,13 +67,11 @@ public sealed class Parser } } - return new SyntaxTree(_namespace, definitions, _diagnostics); + return new SyntaxTree(_namespace, definitions, templates, _diagnostics); } - private DefinitionSyntax ParseDefinition() + private DefinitionSyntax ParseDefinition(int startIndex) { - var startIndex = _tokenIndex; - var keyword = ExpectSymbol(); var node = keyword.Symbol switch { @@ -61,8 +81,8 @@ public sealed class Parser Symbol.Trait => ParseTrait(startIndex), Symbol.Impl => ParseImpl(startIndex), _ => throw new ParseException(Diagnostic - .Error($"Expected 'func' or 'struct', but found '{keyword.Symbol}'") - .WithHelp("Valid definition keywords are 'func' and 'struct'") + .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()) }; @@ -660,7 +680,17 @@ public sealed class Parser @namespace = ExpectIdentifier().Value; } - return new NubCustomType(@namespace, name.Value); + var parameters = new List(); + + if (TryExpectSymbol(Symbol.LessThan)) + { + while (!TryExpectSymbol(Symbol.GreaterThan)) + { + parameters.Add(ParseType()); + } + } + + return new NubCustomType(@namespace, name.Value, parameters); } } @@ -803,7 +833,7 @@ public sealed class Parser 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 }) + if (token is SymbolToken { Symbol: Symbol.Extern or Symbol.Func or Symbol.Struct or Symbol.Trait or Symbol.Impl or Symbol.Template }) { break; } @@ -830,7 +860,7 @@ public sealed class Parser private List GetTokens(int startIndex) { - return _tokens.Skip(startIndex).Take(Math.Min(_tokenIndex, _tokens.Count() - 1) - startIndex).ToList(); + return _tokens.Skip(startIndex).Take(Math.Min(_tokenIndex, _tokens.Count - 1) - startIndex).ToList(); } } diff --git a/src/compiler/NubLang/Syntax/Tokenization/Token.cs b/src/compiler/NubLang/Syntax/Tokenization/Token.cs index 08cf2a6..f8ac2eb 100644 --- a/src/compiler/NubLang/Syntax/Tokenization/Token.cs +++ b/src/compiler/NubLang/Syntax/Tokenization/Token.cs @@ -73,4 +73,5 @@ 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 17885a0..b1ee7c4 100644 --- a/src/compiler/NubLang/Syntax/Tokenization/Tokenizer.cs +++ b/src/compiler/NubLang/Syntax/Tokenization/Tokenizer.cs @@ -22,7 +22,8 @@ public sealed class Tokenizer ["trait"] = Symbol.Trait, ["impl"] = Symbol.Impl, ["for"] = Symbol.For, - ["extern"] = Symbol.Extern + ["extern"] = Symbol.Extern, + ["template"] = Symbol.Template, }; private static readonly Dictionary Chians = new()