Compare commits

..

3 Commits

Author SHA1 Message Date
nub31
e5227f7a99 ... 2026-02-27 22:32:29 +01:00
nub31
73ecbf8e9b extern 2026-02-27 22:20:32 +01:00
nub31
e512650440 ... 2026-02-27 21:29:28 +01:00
13 changed files with 211 additions and 160 deletions

View File

@@ -46,7 +46,9 @@ public class Generator
if (!moduleGraph.TryResolveIdentifier(function.Module, function.Name.Ident, true, out var info)) if (!moduleGraph.TryResolveIdentifier(function.Module, function.Name.Ident, true, out var info))
throw new UnreachableException($"Module graph does not have info about the function {function.Module}::{function.Name.Ident}. This should have been caught earlier"); throw new UnreachableException($"Module graph does not have info about the function {function.Module}::{function.Name.Ident}. This should have been caught earlier");
if (info.Kind == Module.DefinitionKind.Internal) if (info.Source == Module.DefinitionSource.Imported || info.Extern)
writer.Write("extern ");
else if (info.Source == Module.DefinitionSource.Internal && !info.Extern && !info.Exported)
writer.Write("static "); writer.Write("static ");
var parameters = function.Parameters.Select(x => CType(x.Type, x.Name.Ident)); var parameters = function.Parameters.Select(x => CType(x.Type, x.Name.Ident));
@@ -85,15 +87,10 @@ public class Generator
{ {
foreach (var (name, info) in module.GetIdentifiers()) foreach (var (name, info) in module.GetIdentifiers())
{ {
switch (info.Kind) if (info.Source == Module.DefinitionSource.Imported || info.Extern)
{ writer.Write("extern ");
case Module.DefinitionKind.External: else if (info.Source == Module.DefinitionSource.Internal && !info.Extern && !info.Exported)
writer.Write("extern "); writer.Write("static ");
break;
case Module.DefinitionKind.Internal:
writer.Write("static ");
break;
}
if (info.Type is NubTypeFunc fn) if (info.Type is NubTypeFunc fn)
writer.WriteLine($"{CType(fn.ReturnType, info.MangledName)}({string.Join(", ", fn.Parameters.Select(p => CType(p)))});"); writer.WriteLine($"{CType(fn.ReturnType, info.MangledName)}({string.Join(", ", fn.Parameters.Select(p => CType(p)))});");
@@ -122,7 +119,7 @@ public class Generator
writer.WriteLine("{"); writer.WriteLine("{");
using (writer.Indent()) using (writer.Indent())
{ {
writer.WriteLine("const char *data;"); writer.WriteLine("char *data;");
writer.WriteLine("size_t length;"); writer.WriteLine("size_t length;");
} }
writer.WriteLine("};"); writer.WriteLine("};");
@@ -366,7 +363,9 @@ public class Generator
TypedNodeExpressionStringLiteral expression => $"({CType(expression.Type)}){{ \"{expression.Value.Value}\", {expression.Value.Value.Length} }}", TypedNodeExpressionStringLiteral expression => $"({CType(expression.Type)}){{ \"{expression.Value.Value}\", {expression.Value.Value.Length} }}",
TypedNodeExpressionStructLiteral expression => EmitExpressionStructLiteral(expression), TypedNodeExpressionStructLiteral expression => EmitExpressionStructLiteral(expression),
TypedNodeExpressionEnumLiteral expression => EmitExpressionEnumLiteral(expression), TypedNodeExpressionEnumLiteral expression => EmitExpressionEnumLiteral(expression),
TypedNodeExpressionMemberAccess expression => EmitExpressionMemberAccess(expression), TypedNodeExpressionStructMemberAccess expression => EmitExpressionMemberAccess(expression),
TypedNodeExpressionStringLength expression => EmitExpressionStringLength(expression),
TypedNodeExpressionStringPointer expression => EmitExpressionStringPointer(expression),
TypedNodeExpressionLocalIdent expression => expression.Name, TypedNodeExpressionLocalIdent expression => expression.Name,
TypedNodeExpressionGlobalIdent expression => EmitNodeExpressionGlobalIdent(expression), TypedNodeExpressionGlobalIdent expression => EmitNodeExpressionGlobalIdent(expression),
TypedNodeExpressionFuncCall expression => EmitExpressionFuncCall(expression), TypedNodeExpressionFuncCall expression => EmitExpressionFuncCall(expression),
@@ -442,12 +441,24 @@ public class Generator
return $"({CType(expression.Type)}){{ .tag = {tag}, .{enumVariantType.Variant} = {value} }}"; return $"({CType(expression.Type)}){{ .tag = {tag}, .{enumVariantType.Variant} = {value} }}";
} }
private string EmitExpressionMemberAccess(TypedNodeExpressionMemberAccess expression) private string EmitExpressionMemberAccess(TypedNodeExpressionStructMemberAccess expression)
{ {
var target = EmitExpression(expression.Target); var target = EmitExpression(expression.Target);
return $"{target}.{expression.Name.Ident}"; return $"{target}.{expression.Name.Ident}";
} }
private string EmitExpressionStringLength(TypedNodeExpressionStringLength expression)
{
var target = EmitExpression(expression.Target);
return $"{target}.length";
}
private string EmitExpressionStringPointer(TypedNodeExpressionStringPointer expression)
{
var target = EmitExpression(expression.Target);
return $"{target}.data";
}
private string EmitNodeExpressionGlobalIdent(TypedNodeExpressionGlobalIdent expression) private string EmitNodeExpressionGlobalIdent(TypedNodeExpressionGlobalIdent expression)
{ {
if (!moduleGraph.TryResolveIdentifier(expression.Module, expression.Name, true, out var info)) if (!moduleGraph.TryResolveIdentifier(expression.Module, expression.Name, true, out var info))

View File

@@ -86,16 +86,16 @@ public class ModuleGraph
{ {
case Manifest.Module.TypeInfoStruct s: case Manifest.Module.TypeInfoStruct s:
{ {
var info = new Module.TypeInfoStruct(Module.DefinitionKind.External, s.Packed); var info = new Module.TypeInfoStruct(Module.DefinitionSource.Imported, s.Exported, s.Packed);
var fields = s.Fields.Select(x => new Module.TypeInfoStruct.Field(x.Name, x.Type)).ToList(); var fields = s.Fields.Select(x => new Module.TypeInfoStruct.Field(x.Name, x.Type)).ToList();
info.SetFields(fields); info.SetFields(fields);
module.AddType(name, info); module.AddType(name, info);
break; break;
} }
case Manifest.Module.TypeInfoEnum s: case Manifest.Module.TypeInfoEnum e:
{ {
var info = new Module.TypeInfoEnum(Module.DefinitionKind.External); var info = new Module.TypeInfoEnum(Module.DefinitionSource.Imported, e.Exported);
var variants = s.Variants.Select(v => new Module.TypeInfoEnum.Variant(v.Name, v.Type)).ToList(); var variants = e.Variants.Select(v => new Module.TypeInfoEnum.Variant(v.Name, v.Type)).ToList();
info.SetVariants(variants); info.SetVariants(variants);
module.AddType(name, info); module.AddType(name, info);
break; break;
@@ -107,7 +107,7 @@ public class ModuleGraph
foreach (var (name, identifier) in manifestModule.Identifiers) foreach (var (name, identifier) in manifestModule.Identifiers)
{ {
module.AddIdentifier(name, new Module.IdentifierInfo(Module.DefinitionKind.External, identifier.Type, identifier.MangledName)); module.AddIdentifier(name, new Module.IdentifierInfo(Module.DefinitionSource.Imported, identifier.Exported, identifier.Extern, identifier.Type, identifier.MangledName));
} }
} }
} }
@@ -118,14 +118,12 @@ public class ModuleGraph
foreach (var structDef in ast.Definitions.OfType<NodeDefinitionStruct>()) foreach (var structDef in ast.Definitions.OfType<NodeDefinitionStruct>())
{ {
var kind = structDef.Exported ? Module.DefinitionKind.Exported : Module.DefinitionKind.Internal; module.AddType(structDef.Name.Ident, new Module.TypeInfoStruct(Module.DefinitionSource.Internal, structDef.Exported, structDef.Packed));
module.AddType(structDef.Name.Ident, new Module.TypeInfoStruct(kind, structDef.Packed));
} }
foreach (var enumDef in ast.Definitions.OfType<NodeDefinitionEnum>()) foreach (var enumDef in ast.Definitions.OfType<NodeDefinitionEnum>())
{ {
var kind = enumDef.Exported ? Module.DefinitionKind.Exported : Module.DefinitionKind.Internal; module.AddType(enumDef.Name.Ident, new Module.TypeInfoEnum(Module.DefinitionSource.Internal, enumDef.Exported));
module.AddType(enumDef.Name.Ident, new Module.TypeInfoEnum(kind));
} }
} }
@@ -165,18 +163,32 @@ public class ModuleGraph
foreach (var funcDef in ast.Definitions.OfType<NodeDefinitionFunc>()) foreach (var funcDef in ast.Definitions.OfType<NodeDefinitionFunc>())
{ {
var parameters = funcDef.Parameters.Select(x => ResolveType(x.Type, module.Name)).ToList(); var parameters = funcDef.Parameters.Select(x => ResolveType(x.Type, module.Name)).ToList();
var returnType = ResolveType(funcDef.ReturnType, module.Name); var returnType = funcDef.ReturnType == null ? NubTypeVoid.Instance : ResolveType(funcDef.ReturnType, module.Name);
var funcType = NubTypeFunc.Get(parameters, returnType); var funcType = NubTypeFunc.Get(parameters, returnType);
var kind = funcDef.Exported ? Module.DefinitionKind.Exported : Module.DefinitionKind.Internal; var info = new Module.IdentifierInfo(Module.DefinitionSource.Internal, funcDef.Exported, false, funcType, NameMangler.Mangle(module.Name, funcDef.Name.Ident, funcType));
var info = new Module.IdentifierInfo(kind, funcType, NameMangler.Mangle(module.Name, funcDef.Name.Ident, funcType)); module.AddIdentifier(funcDef.Name.Ident, info);
}
foreach (var funcDef in ast.Definitions.OfType<NodeDefinitionExternFunc>())
{
var parameters = funcDef.Parameters.Select(x => ResolveType(x.Type, module.Name)).ToList();
var returnType = funcDef.ReturnType == null ? NubTypeVoid.Instance : ResolveType(funcDef.ReturnType, module.Name);
var funcType = NubTypeFunc.Get(parameters, returnType);
var info = new Module.IdentifierInfo(Module.DefinitionSource.Internal, funcDef.Exported, true, funcType, funcDef.Name.Ident);
module.AddIdentifier(funcDef.Name.Ident, info); module.AddIdentifier(funcDef.Name.Ident, info);
} }
foreach (var globalVariable in ast.Definitions.OfType<NodeDefinitionGlobalVariable>()) foreach (var globalVariable in ast.Definitions.OfType<NodeDefinitionGlobalVariable>())
{ {
var type = ResolveType(globalVariable.Type, module.Name); var type = ResolveType(globalVariable.Type, module.Name);
var kind = globalVariable.Exported ? Module.DefinitionKind.Exported : Module.DefinitionKind.Internal; var info = new Module.IdentifierInfo(Module.DefinitionSource.Internal, globalVariable.Exported, false, type, NameMangler.Mangle(module.Name, globalVariable.Name.Ident, type));
var info = new Module.IdentifierInfo(kind, type, NameMangler.Mangle(module.Name, globalVariable.Name.Ident, type)); module.AddIdentifier(globalVariable.Name.Ident, info);
}
foreach (var globalVariable in ast.Definitions.OfType<NodeDefinitionExternGlobalVariable>())
{
var type = ResolveType(globalVariable.Type, module.Name);
var info = new Module.IdentifierInfo(Module.DefinitionSource.Internal, globalVariable.Exported, true, type, NameMangler.Mangle(module.Name, globalVariable.Name.Ident, type));
module.AddIdentifier(globalVariable.Name.Ident, info); module.AddIdentifier(globalVariable.Name.Ident, info);
} }
} }
@@ -333,7 +345,7 @@ public class Module(string name)
return false; return false;
} }
if (searchPrivate || info.Kind is DefinitionKind.External or DefinitionKind.Exported) if (searchPrivate || info.Source == DefinitionSource.Internal)
{ {
customType = info; customType = info;
return true; return true;
@@ -352,7 +364,7 @@ public class Module(string name)
return false; return false;
} }
if (searchPrivate || info.Kind is DefinitionKind.External or DefinitionKind.Exported) if (searchPrivate || info.Source == DefinitionSource.Internal)
{ {
identifierType = info; identifierType = info;
return true; return true;
@@ -372,26 +384,28 @@ public class Module(string name)
identifiers.Add(name, info); identifiers.Add(name, info);
} }
public enum DefinitionKind public enum DefinitionSource
{ {
Internal, Internal,
Exported, Imported,
External,
} }
public class IdentifierInfo(DefinitionKind kind, NubType type, string mangledName) public class IdentifierInfo(DefinitionSource source, bool exported, bool @extern, NubType type, string mangledName)
{ {
public DefinitionKind Kind { get; } = kind; public DefinitionSource Source { get; } = source;
public bool Exported { get; } = exported;
public bool Extern { get; } = @extern;
public NubType Type { get; } = type; public NubType Type { get; } = type;
public string MangledName { get; } = mangledName; public string MangledName { get; } = mangledName;
} }
public abstract class TypeInfo(DefinitionKind kind) public abstract class TypeInfo(DefinitionSource source, bool exported)
{ {
public DefinitionKind Kind { get; } = kind; public DefinitionSource Source { get; } = source;
public bool Exported { get; } = exported;
} }
public class TypeInfoStruct(DefinitionKind kind, bool packed) : TypeInfo(kind) public class TypeInfoStruct(DefinitionSource source, bool exported, bool packed) : TypeInfo(source, exported)
{ {
private IReadOnlyList<Field>? fields; private IReadOnlyList<Field>? fields;
@@ -410,7 +424,7 @@ public class Module(string name)
} }
} }
public class TypeInfoEnum(DefinitionKind kind) : TypeInfo(kind) public class TypeInfoEnum(DefinitionSource source, bool exported) : TypeInfo(source, exported)
{ {
private IReadOnlyList<Variant>? variants; private IReadOnlyList<Variant>? variants;

View File

@@ -70,25 +70,8 @@ public record Manifest(Dictionary<string, Manifest.Module> Modules)
foreach (var module in moduleGraph.GetModules()) foreach (var module in moduleGraph.GetModules())
{ {
var types = new Dictionary<string, Module.TypeInfo>(); var types = module.GetTypes().ToDictionary(x => x.Key, x => ConvertType(x.Value));
var identifiers = new Dictionary<string, Module.IdentifierInfo>(); var identifiers = module.GetIdentifiers().ToDictionary(x => x.Key, x => new Module.IdentifierInfo(x.Value.Type, x.Value.Exported, x.Value.Extern, x.Value.MangledName));
foreach (var (name, typeInfo) in module.GetTypes())
{
if (typeInfo.Kind == Compiler.Module.DefinitionKind.Exported)
{
types.Add(name, ConvertType(typeInfo));
}
}
foreach (var (name, identifierInfo) in module.GetIdentifiers())
{
if (identifierInfo.Kind == Compiler.Module.DefinitionKind.Exported)
{
identifiers.Add(name, new Module.IdentifierInfo(identifierInfo.Type, identifierInfo.MangledName));
}
}
modules[module.Name] = new Module(types, identifiers); modules[module.Name] = new Module(types, identifiers);
} }
@@ -98,8 +81,8 @@ public record Manifest(Dictionary<string, Manifest.Module> Modules)
{ {
return typeInfo switch return typeInfo switch
{ {
Compiler.Module.TypeInfoStruct s => new Module.TypeInfoStruct(s.Packed, s.Fields.Select(x => new Module.TypeInfoStruct.Field(x.Name, x.Type)).ToList()), Compiler.Module.TypeInfoStruct s => new Module.TypeInfoStruct(s.Exported, s.Packed, s.Fields.Select(x => new Module.TypeInfoStruct.Field(x.Name, x.Type)).ToList()),
Compiler.Module.TypeInfoEnum e => new Module.TypeInfoEnum(e.Variants.Select(v => new Module.TypeInfoEnum.Variant(v.Name, v.Type)).ToList()), Compiler.Module.TypeInfoEnum e => new Module.TypeInfoEnum(e.Exported, e.Variants.Select(v => new Module.TypeInfoEnum.Variant(v.Name, v.Type)).ToList()),
_ => throw new ArgumentOutOfRangeException(nameof(typeInfo)) _ => throw new ArgumentOutOfRangeException(nameof(typeInfo))
}; };
} }
@@ -108,18 +91,18 @@ public record Manifest(Dictionary<string, Manifest.Module> Modules)
public record Module(Dictionary<string, Module.TypeInfo> Types, Dictionary<string, Module.IdentifierInfo> Identifiers) public record Module(Dictionary<string, Module.TypeInfo> Types, Dictionary<string, Module.IdentifierInfo> Identifiers)
{ {
public record IdentifierInfo(NubType Type, string MangledName); public record IdentifierInfo(NubType Type, bool Exported, bool Extern, string MangledName);
[JsonDerivedType(typeof(TypeInfoStruct), "struct")] [JsonDerivedType(typeof(TypeInfoStruct), "struct")]
[JsonDerivedType(typeof(TypeInfoEnum), "enum")] [JsonDerivedType(typeof(TypeInfoEnum), "enum")]
public abstract record TypeInfo; public abstract record TypeInfo(bool Exported);
public record TypeInfoStruct(bool Packed, IReadOnlyList<TypeInfoStruct.Field> Fields) : TypeInfo public record TypeInfoStruct(bool Exported, bool Packed, IReadOnlyList<TypeInfoStruct.Field> Fields) : TypeInfo(Exported)
{ {
public record Field(string Name, NubType Type); public record Field(string Name, NubType Type);
} }
public record TypeInfoEnum(IReadOnlyList<TypeInfoEnum.Variant> Variants) : TypeInfo public record TypeInfoEnum(bool Exported, IReadOnlyList<TypeInfoEnum.Variant> Variants) : TypeInfo(Exported)
{ {
public record Variant(string Name, NubType Type); public record Variant(string Name, NubType Type);
} }

View File

@@ -65,6 +65,10 @@ public class Parser
Next(); Next();
modifiers[Keyword.Packed] = keyword; modifiers[Keyword.Packed] = keyword;
break; break;
case Keyword.Extern:
Next();
modifiers[Keyword.Extern] = keyword;
break;
default: default:
goto modifier_done; goto modifier_done;
} }
@@ -75,6 +79,7 @@ public class Parser
if (TryExpectKeyword(Keyword.Func)) if (TryExpectKeyword(Keyword.Func))
{ {
var exported = modifiers.Remove(Keyword.Export); var exported = modifiers.Remove(Keyword.Export);
var @extern = modifiers.Remove(Keyword.Extern);
foreach (var modifier in modifiers) foreach (var modifier in modifiers)
// todo(nub31): Add to diagnostics instead of throwing // todo(nub31): Add to diagnostics instead of throwing
@@ -93,12 +98,19 @@ public class Parser
parameters.Add(new NodeDefinitionFunc.Param(TokensFrom(paramStartIndex), parameterName, parameterType)); parameters.Add(new NodeDefinitionFunc.Param(TokensFrom(paramStartIndex), parameterName, parameterType));
} }
ExpectSymbol(Symbol.Colon); NodeType? returnType = null;
var returnType = ParseType(); if (TryExpectSymbol(Symbol.Colon))
returnType = ParseType();
var body = ParseStatement(); if (@extern)
{
return new NodeDefinitionFunc(TokensFrom(startIndex), exported, name, parameters, body, returnType); return new NodeDefinitionExternFunc(TokensFrom(startIndex), exported, name, parameters, returnType);
}
else
{
var body = ParseStatement();
return new NodeDefinitionFunc(TokensFrom(startIndex), exported, name, parameters, body, returnType);
}
} }
if (TryExpectKeyword(Keyword.Struct)) if (TryExpectKeyword(Keyword.Struct))
@@ -704,13 +716,21 @@ public abstract class Node(List<Token> tokens)
public abstract class NodeDefinition(List<Token> tokens) : Node(tokens); public abstract class NodeDefinition(List<Token> tokens) : Node(tokens);
public class NodeDefinitionFunc(List<Token> tokens, bool exported, TokenIdent name, List<NodeDefinitionFunc.Param> parameters, NodeStatement body, NodeType returnType) : NodeDefinition(tokens) public class NodeDefinitionExternFunc(List<Token> tokens, bool exported, TokenIdent name, List<NodeDefinitionFunc.Param> parameters, NodeType? returnType) : NodeDefinition(tokens)
{
public bool Exported { get; } = exported;
public TokenIdent Name { get; } = name;
public List<NodeDefinitionFunc.Param> Parameters { get; } = parameters;
public NodeType? ReturnType { get; } = returnType;
}
public class NodeDefinitionFunc(List<Token> tokens, bool exported, TokenIdent name, List<NodeDefinitionFunc.Param> parameters, NodeStatement body, NodeType? returnType) : NodeDefinition(tokens)
{ {
public bool Exported { get; } = exported; public bool Exported { get; } = exported;
public TokenIdent Name { get; } = name; public TokenIdent Name { get; } = name;
public List<Param> Parameters { get; } = parameters; public List<Param> Parameters { get; } = parameters;
public NodeStatement Body { get; } = body; public NodeStatement Body { get; } = body;
public NodeType ReturnType { get; } = returnType; public NodeType? ReturnType { get; } = returnType;
public class Param(List<Token> tokens, TokenIdent name, NodeType type) : Node(tokens) public class Param(List<Token> tokens, TokenIdent name, NodeType type) : Node(tokens)
{ {
@@ -746,6 +766,13 @@ public class NodeDefinitionEnum(List<Token> tokens, bool exported, TokenIdent na
} }
} }
public class NodeDefinitionExternGlobalVariable(List<Token> tokens, bool exported, TokenIdent name, NodeType type) : NodeDefinition(tokens)
{
public bool Exported { get; } = exported;
public TokenIdent Name { get; } = name;
public NodeType Type { get; } = type;
}
public class NodeDefinitionGlobalVariable(List<Token> tokens, bool exported, TokenIdent name, NodeType type) : NodeDefinition(tokens) public class NodeDefinitionGlobalVariable(List<Token> tokens, bool exported, TokenIdent name, NodeType type) : NodeDefinition(tokens)
{ {
public bool Exported { get; } = exported; public bool Exported { get; } = exported;

View File

@@ -133,13 +133,13 @@ File.WriteAllText(".build/out.c", output);
if (compileLib) if (compileLib)
{ {
Process.Start("gcc", ["-Og", "-fvisibility=hidden", "-fno-builtin", "-c", "-o", ".build/out.o", ".build/out.c", .. archivePaths]).WaitForExit(); Process.Start("gcc", ["-Og", "-fno-builtin", "-c", "-o", ".build/out.o", ".build/out.c", .. archivePaths]).WaitForExit();
Process.Start("ar", ["rcs", ".build/out.a", ".build/out.o"]).WaitForExit(); Process.Start("ar", ["rcs", ".build/out.a", ".build/out.o"]).WaitForExit();
NubLib.Pack(".build/out.nublib", ".build/out.a", Manifest.Create(moduleGraph)); NubLib.Pack(".build/out.nublib", ".build/out.a", Manifest.Create(moduleGraph));
} }
else else
{ {
Process.Start("gcc", ["-Og", "-fvisibility=hidden", "-fno-builtin", "-o", ".build/out", ".build/out.c", .. archivePaths]).WaitForExit(); Process.Start("gcc", ["-Og", "-fno-builtin", "-o", ".build/out", ".build/out.c", .. archivePaths]).WaitForExit();
} }
return 0; return 0;

View File

@@ -397,6 +397,7 @@ public class Tokenizer
"return" => new TokenKeyword(line, startColumn, column - startColumn, Keyword.Return), "return" => new TokenKeyword(line, startColumn, column - startColumn, Keyword.Return),
"module" => new TokenKeyword(line, startColumn, column - startColumn, Keyword.Module), "module" => new TokenKeyword(line, startColumn, column - startColumn, Keyword.Module),
"export" => new TokenKeyword(line, startColumn, column - startColumn, Keyword.Export), "export" => new TokenKeyword(line, startColumn, column - startColumn, Keyword.Export),
"extern" => new TokenKeyword(line, startColumn, column - startColumn, Keyword.Extern),
"true" => new TokenBoolLiteral(line, startColumn, column - startColumn, true), "true" => new TokenBoolLiteral(line, startColumn, column - startColumn, true),
"false" => new TokenBoolLiteral(line, startColumn, column - startColumn, false), "false" => new TokenBoolLiteral(line, startColumn, column - startColumn, false),
_ => new TokenIdent(line, startColumn, column - startColumn, value) _ => new TokenIdent(line, startColumn, column - startColumn, value)
@@ -545,6 +546,7 @@ public enum Keyword
Return, Return,
Module, Module,
Export, Export,
Extern,
} }
public class TokenKeyword(int line, int column, int length, Keyword keyword) : Token(line, column, length) public class TokenKeyword(int line, int column, int length, Keyword keyword) : Token(line, column, length)
@@ -611,6 +613,7 @@ public static class TokenExtensions
Keyword.Return => "return", Keyword.Return => "return",
Keyword.Module => "module", Keyword.Module => "module",
Keyword.Export => "export", Keyword.Export => "export",
Keyword.Extern => "extern",
_ => throw new ArgumentOutOfRangeException(nameof(symbol), symbol, null) _ => throw new ArgumentOutOfRangeException(nameof(symbol), symbol, null)
}; };
} }

View File

@@ -34,14 +34,21 @@ public class TypeChecker
var invalidParameter = false; var invalidParameter = false;
TypedNodeStatement? body = null; TypedNodeStatement? body = null;
try if (function.ReturnType == null)
{ {
functionReturnType = ResolveType(function.ReturnType); functionReturnType = NubTypeVoid.Instance;
} }
catch (CompileException e) else
{ {
diagnostics.Add(e.Diagnostic); try
return null; {
functionReturnType = ResolveType(function.ReturnType);
}
catch (CompileException e)
{
diagnostics.Add(e.Diagnostic);
return null;
}
} }
using (scope.EnterScope()) using (scope.EnterScope())
@@ -119,20 +126,9 @@ public class TypeChecker
if (statement.Expression is not NodeExpressionFuncCall funcCall) if (statement.Expression is not NodeExpressionFuncCall funcCall)
throw BasicError("Expected statement or function call", statement); throw BasicError("Expected statement or function call", statement);
var target = CheckExpression(funcCall.Target, null); var expr = CheckExpressionFuncCall(funcCall, null);
if (target.Type is not NubTypeFunc funcType)
throw BasicError("Expected a function type", target);
if (funcType.Parameters.Count != funcCall.Parameters.Count) return new TypedNodeStatementFuncCall(expr.Tokens, expr.Target, expr.Parameters);
throw BasicError($"Expected {funcType.Parameters.Count} parameters but got {funcCall.Parameters.Count}", funcCall);
var parameters = new List<TypedNodeExpression>();
for (int i = 0; i < funcCall.Parameters.Count; i++)
{
parameters.Add(CheckExpression(funcCall.Parameters[i], funcType.Parameters[i]));
}
return new TypedNodeStatementFuncCall(statement.Tokens, target, parameters);
} }
private TypedNodeStatementIf CheckStatementIf(NodeStatementIf statement) private TypedNodeStatementIf CheckStatementIf(NodeStatementIf statement)
@@ -406,15 +402,34 @@ public class TypeChecker
private TypedNodeExpressionIntLiteral CheckExpressionIntLiteral(NodeExpressionIntLiteral expression, NubType? expectedType) private TypedNodeExpressionIntLiteral CheckExpressionIntLiteral(NodeExpressionIntLiteral expression, NubType? expectedType)
{ {
return new TypedNodeExpressionIntLiteral(expression.Tokens, NubTypeSInt.Get(32), expression.Value); NubType? type = null;
if (expectedType is NubTypeSInt or NubTypeUInt)
type = expectedType;
type ??= NubTypeSInt.Get(32);
return new TypedNodeExpressionIntLiteral(expression.Tokens, type, expression.Value);
} }
private TypedNodeExpressionMemberAccess CheckExpressionMemberAccess(NodeExpressionMemberAccess expression, NubType? expectedType) private TypedNodeExpression CheckExpressionMemberAccess(NodeExpressionMemberAccess expression, NubType? expectedType)
{ {
var target = CheckExpression(expression.Target, null); var target = CheckExpression(expression.Target, null);
switch (target.Type) switch (target.Type)
{ {
case NubTypeString stringType:
{
switch (expression.Name.Ident)
{
case "length":
return new TypedNodeExpressionStringLength(expression.Tokens, NubTypeUInt.Get(64), target);
case "ptr":
return new TypedNodeExpressionStringPointer(expression.Tokens, NubTypePointer.Get(NubTypeUInt.Get(8)), target);
default:
throw BasicError($"'{expression.Name.Ident}' is not a member of type string", expression.Name);
}
}
case NubTypeStruct structType: case NubTypeStruct structType:
{ {
if (!moduleGraph.TryResolveModule(structType.Module, out var module)) if (!moduleGraph.TryResolveModule(structType.Module, out var module))
@@ -430,24 +445,15 @@ public class TypeChecker
if (field == null) if (field == null)
throw BasicError($"Struct '{target.Type}' does not have a field matching the name '{expression.Name.Ident}'", target); throw BasicError($"Struct '{target.Type}' does not have a field matching the name '{expression.Name.Ident}'", target);
return new TypedNodeExpressionMemberAccess(expression.Tokens, field.Type, target, expression.Name); return new TypedNodeExpressionStructMemberAccess(expression.Tokens, field.Type, target, expression.Name);
} }
case NubTypeEnumVariant enumVariantType: case NubTypeAnonymousStruct anonymousStructType:
{ {
if (!moduleGraph.TryResolveModule(enumVariantType.EnumType.Module, out var module)) var field = anonymousStructType.Fields.FirstOrDefault(x => x.Name == expression.Name.Ident);
throw BasicError($"Module '{enumVariantType.EnumType.Module}' not found", expression.Target); if (field == null)
throw BasicError($"Struct '{target.Type}' does not have a field matching the name '{expression.Name.Ident}'", target);
if (!module.TryResolveType(enumVariantType.EnumType.Name, currentModule == enumVariantType.EnumType.Module, out var typeDef)) return new TypedNodeExpressionStructMemberAccess(expression.Tokens, field.Type, target, expression.Name);
throw BasicError($"Type '{enumVariantType.EnumType.Name}' not found in module '{enumVariantType.EnumType.Module}'", expression.Target);
if (typeDef is not Module.TypeInfoEnum enumDef)
throw BasicError($"Type '{enumVariantType.EnumType.Module}::{enumVariantType.EnumType.Name}' is not an enum", expression.Target);
var variant = enumDef.Variants.FirstOrDefault(x => x.Name == enumVariantType.Variant);
if (variant == null)
throw BasicError($"Type '{target.Type}' does not have a variant named '{enumVariantType.Variant}'", expression.Target);
return new TypedNodeExpressionMemberAccess(expression.Tokens, variant.Type, target, expression.Name);
} }
default: default:
throw BasicError($"{target.Type} has no member '{expression.Name.Ident}'", target); throw BasicError($"{target.Type} has no member '{expression.Name.Ident}'", target);
@@ -466,7 +472,11 @@ public class TypeChecker
var parameters = new List<TypedNodeExpression>(); var parameters = new List<TypedNodeExpression>();
for (int i = 0; i < expression.Parameters.Count; i++) for (int i = 0; i < expression.Parameters.Count; i++)
{ {
parameters.Add(CheckExpression(expression.Parameters[i], funcType.Parameters[i])); var parameter = CheckExpression(expression.Parameters[i], funcType.Parameters[i]);
if (!parameter.Type.IsAssignableTo(funcType.Parameters[i]))
throw BasicError($"Parameter {i + 1} does is not assignable to '{funcType.Parameters[i]}'", parameter);
parameters.Add(parameter);
} }
return new TypedNodeExpressionFuncCall(expression.Tokens, funcType.ReturnType, target, parameters); return new TypedNodeExpressionFuncCall(expression.Tokens, funcType.ReturnType, target, parameters);
@@ -484,7 +494,7 @@ public class TypeChecker
var type = ResolveType(expression.Type); var type = ResolveType(expression.Type);
if (type is not NubTypeStruct structType) if (type is not NubTypeStruct structType)
throw BasicError("Type of struct literal is not a struct", expression); throw BasicError("Type of struct literal is not a struct", expression);
if (!moduleGraph.TryResolveType(structType.Module, structType.Name, structType.Module == currentModule, out var info)) if (!moduleGraph.TryResolveType(structType.Module, structType.Name, structType.Module == currentModule, out var info))
throw BasicError($"Type '{structType}' struct literal not found", expression); throw BasicError($"Type '{structType}' struct literal not found", expression);
@@ -531,7 +541,24 @@ public class TypeChecker
return new TypedNodeExpressionStructLiteral(expression.Tokens, structType, initializers); return new TypedNodeExpressionStructLiteral(expression.Tokens, structType, initializers);
} }
// todo(nub31): Infer anonymous struct types if expectedType is anonymous struct else if (expectedType is NubTypeAnonymousStruct anonymousStructType)
{
var initializers = new List<TypedNodeExpressionStructLiteral.Initializer>();
foreach (var initializer in expression.Initializers)
{
var field = anonymousStructType.Fields.FirstOrDefault(x => x.Name == initializer.Name.Ident);
if (field == null)
throw BasicError($"Field '{initializer.Name.Ident}' does not exist on anonymous struct '{anonymousStructType}'", initializer.Name);
var value = CheckExpression(initializer.Value, field.Type);
if (!value.Type.IsAssignableTo(field.Type))
throw BasicError($"Type of assignment ({value.Type}) does not match expected type of field '{field.Name}' ({field.Type})", initializer.Name);
initializers.Add(new TypedNodeExpressionStructLiteral.Initializer(initializer.Tokens, initializer.Name, value));
}
return new TypedNodeExpressionStructLiteral(expression.Tokens, anonymousStructType, initializers);
}
else else
{ {
var initializers = new List<TypedNodeExpressionStructLiteral.Initializer>(); var initializers = new List<TypedNodeExpressionStructLiteral.Initializer>();
@@ -849,12 +876,22 @@ public class TypedNodeExpressionEnumLiteral(List<Token> tokens, NubType type, Ty
public TypedNodeExpression Value { get; } = value; public TypedNodeExpression Value { get; } = value;
} }
public class TypedNodeExpressionMemberAccess(List<Token> tokens, NubType type, TypedNodeExpression target, TokenIdent name) : TypedNodeExpression(tokens, type) public class TypedNodeExpressionStructMemberAccess(List<Token> tokens, NubType type, TypedNodeExpression target, TokenIdent name) : TypedNodeExpression(tokens, type)
{ {
public TypedNodeExpression Target { get; } = target; public TypedNodeExpression Target { get; } = target;
public TokenIdent Name { get; } = name; public TokenIdent Name { get; } = name;
} }
public class TypedNodeExpressionStringLength(List<Token> tokens, NubType type, TypedNodeExpression target) : TypedNodeExpression(tokens, type)
{
public TypedNodeExpression Target { get; } = target;
}
public class TypedNodeExpressionStringPointer(List<Token> tokens, NubType type, TypedNodeExpression target) : TypedNodeExpression(tokens, type)
{
public TypedNodeExpression Target { get; } = target;
}
public class TypedNodeExpressionFuncCall(List<Token> tokens, NubType type, TypedNodeExpression target, List<TypedNodeExpression> parameters) : TypedNodeExpression(tokens, type) public class TypedNodeExpressionFuncCall(List<Token> tokens, NubType type, TypedNodeExpression target, List<TypedNodeExpression> parameters) : TypedNodeExpression(tokens, type)
{ {
public TypedNodeExpression Target { get; } = target; public TypedNodeExpression Target { get; } = target;

View File

@@ -1,12 +1,15 @@
set -e set -e
pushd core
dotnet run --project ../../compiler mem.nub print.nub --type=lib
popd
pushd math pushd math
dotnet run --project ../../compiler math.nub --type=lib dotnet run --project ../../compiler math.nub --type=lib
# pushd .build
# unzip out.nublib
# popd
popd popd
pushd program pushd program
dotnet run --project ../../compiler main.nub ../math/.build/out.nublib dotnet run --project ../../compiler main.nub ../math/.build/out.nublib ../core/.build/out.nublib
popd popd
./program/.build/out

4
examples/core/mem.nub Normal file
View File

@@ -0,0 +1,4 @@
module core
export extern func malloc(size: u64): ^void
export extern func free(size: ^void)

8
examples/core/print.nub Normal file
View File

@@ -0,0 +1,8 @@
module core
extern func puts(text: ^u8)
export func print(text: string)
{
puts(text.ptr)
}

View File

@@ -2,5 +2,8 @@ module main
func main(): i32 func main(): i32
{ {
return math::add(1 2) core::print("Hello, world")
} let ptr = core::malloc(64)
core::free(ptr)
return 0
}

View File

@@ -1,32 +0,0 @@
module main
let global: i32
func main(): i32 {
let x: i32 = 23
x = 24
if !true {
x = 49
return x
} else {
x = 3
}
let i: i32 = 0
x = 1 + 2 * 34
while i < 10 {
i = i + 1
x = i
}
let me: test::person = struct test::person { age = 21 name = "Oliver" }
x = test::do_something(me.name)
test::do_something(me.name)
main::global = 123
return main::global
}

View File

@@ -1,10 +0,0 @@
module test
export packed struct person {
age: i32
name: string
}
export func do_something(name: string): i32 {
return 3
}