This commit is contained in:
nub31
2025-05-26 20:39:41 +02:00
parent dcf62e3e05
commit 3279eb7df5
8 changed files with 145 additions and 107 deletions

View File

@@ -1,5 +1,6 @@
namespace c namespace c
extern func printf(fmt: string, ...args: any)
extern func printf(fmt: string, ...args: any) extern func printf(fmt: string, ...args: any)
extern func getchar(): i32 extern func getchar(): i32
extern func puts(str: string) extern func puts(str: string)

View File

@@ -6,31 +6,35 @@ namespace Nub.Lang.Backend;
public class Generator public class Generator
{ {
private readonly List<DefinitionNode> _definitions; private List<SourceFile> _sourceFiles = [];
private readonly StringBuilder _builder = new(); private StringBuilder _builder = new();
private readonly Dictionary<string, Variable> _variables = new(); private Dictionary<string, Variable> _variables = [];
private readonly List<string> _strings = []; private List<string> _strings = [];
private readonly Stack<string> _breakLabels = new(); private Stack<string> _breakLabels = [];
private readonly Stack<string> _continueLabels = new(); private Stack<string> _continueLabels = [];
private int _variableIndex; private int _variableIndex;
private bool _codeIsReachable = true; private bool _codeIsReachable = true;
public Generator(List<DefinitionNode> definitions) public string Generate(List<SourceFile> sourceFiles)
{ {
_definitions = definitions; _sourceFiles = sourceFiles;
} _builder = new StringBuilder();
_variables = new Dictionary<string, Variable>();
public string Generate() _strings = [];
{ _breakLabels = [];
foreach (var structDefinition in _definitions.OfType<StructDefinitionNode>()) _continueLabels = [];
_variableIndex = 0;
_codeIsReachable = true;
foreach (var structDef in _sourceFiles.SelectMany(f => f.Definitions).OfType<StructDefinitionNode>())
{ {
GenerateStructDefinition(structDefinition); GenerateStructDefinition(structDef);
_builder.AppendLine(); _builder.AppendLine();
} }
foreach (var funcDefinition in _definitions.OfType<LocalFuncDefinitionNode>()) foreach (var funcDef in _sourceFiles.SelectMany(f => f.Definitions).OfType<LocalFuncDefinitionNode>())
{ {
GenerateFuncDefinition(funcDefinition); GenerateFuncDefinition(funcDef);
_builder.AppendLine(); _builder.AppendLine();
} }
@@ -213,12 +217,12 @@ public class Generator
throw new ArgumentOutOfRangeException(); throw new ArgumentOutOfRangeException();
} }
} }
case NubStructType nubCustomType: case NubStructType nubStructType:
{ {
var definition = _definitions.OfType<StructDefinitionNode>().FirstOrDefault(s => s.Name == nubCustomType.Name); var definition = LookupStructDefinition(nubStructType.Namespace, nubStructType.Name);
if (definition == null) if (definition == null)
{ {
throw new Exception($"Cannot determine size of non-existent type {nubCustomType}"); throw new Exception($"Cannot determine size of non-existent type {nubStructType}");
} }
return definition.Fields.Sum(f => QbeTypeSize(f.Type)); return definition.Fields.Sum(f => QbeTypeSize(f.Type));
@@ -362,12 +366,8 @@ public class Generator
private string GenerateFuncCall(FuncCall funcCall) private string GenerateFuncCall(FuncCall funcCall)
{ {
var parameterDefinitions = _definitions var funcDefinition = LookupFuncSignature(funcCall.Namespace, funcCall.Name, funcCall.Parameters.Select(p => p.Type).ToList());
.OfType<IFuncSignature>() if (funcDefinition == null)
.FirstOrDefault(d => d.Name == funcCall.Name)
?.Parameters;
if (parameterDefinitions == null)
{ {
throw new Exception($"Unknown function {funcCall}"); throw new Exception($"Unknown function {funcCall}");
} }
@@ -376,19 +376,19 @@ public class Generator
for (var i = 0; i < funcCall.Parameters.Count; i++) for (var i = 0; i < funcCall.Parameters.Count; i++)
{ {
if (i < parameterDefinitions.Count && parameterDefinitions[i].Variadic) if (i < funcDefinition.Parameters.Count && funcDefinition.Parameters[i].Variadic)
{ {
parameterStrings.Add("..."); parameterStrings.Add("...");
} }
NubType expectedType; NubType expectedType;
if (i < parameterDefinitions.Count) if (i < funcDefinition.Parameters.Count)
{ {
expectedType = parameterDefinitions[i].Type; expectedType = funcDefinition.Parameters[i].Type;
} }
else if (parameterDefinitions[^1].Variadic) else if (funcDefinition.Parameters[^1].Variadic)
{ {
expectedType = parameterDefinitions[^1].Type; expectedType = funcDefinition.Parameters[^1].Type;
} }
else else
{ {
@@ -1295,7 +1295,7 @@ public class Generator
private string GenerateStructInitializer(StructInitializerNode structInitializer) private string GenerateStructInitializer(StructInitializerNode structInitializer)
{ {
var structDefinition = _definitions.OfType<StructDefinitionNode>().FirstOrDefault(s => s.Name == structInitializer.StructType.Name); var structDefinition = LookupStructDefinition(structInitializer.StructType.Namespace, structInitializer.StructType.Name);
if (structDefinition == null) if (structDefinition == null)
{ {
throw new Exception($"Struct {structInitializer.StructType.Name} is not defined"); throw new Exception($"Struct {structInitializer.StructType.Name} is not defined");
@@ -1399,10 +1399,7 @@ public class Generator
} }
case NubStructType structType: case NubStructType structType:
{ {
var structDefinition = _definitions var structDefinition = LookupStructDefinition(structType.Namespace, structType.Namespace);
.OfType<StructDefinitionNode>()
.FirstOrDefault(s => s.Name == structType.Name);
if (structDefinition == null) if (structDefinition == null)
{ {
throw new Exception($"Struct {structType.Name} is not defined"); throw new Exception($"Struct {structType.Name} is not defined");
@@ -1449,6 +1446,24 @@ public class Generator
return $"v{++_variableIndex}"; return $"v{++_variableIndex}";
} }
private IFuncSignature? LookupFuncSignature(string @namespace, string name, List<NubType> parameters)
{
return _sourceFiles
.Where(f => f.Namespace == @namespace)
.SelectMany(f => f.Definitions)
.OfType<IFuncSignature>()
.FirstOrDefault(f => f.SignatureMatches(name, parameters));
}
private StructDefinitionNode? LookupStructDefinition(string @namespace, string name)
{
return _sourceFiles
.Where(f => f.Namespace == @namespace)
.SelectMany(f => f.Definitions)
.OfType<StructDefinitionNode>()
.FirstOrDefault(d => d.Name == name);
}
private class Variable private class Variable
{ {
public required string Pointer { get; init; } public required string Pointer { get; init; }

View File

@@ -8,10 +8,41 @@ public interface IFuncSignature
public List<FuncParameter> Parameters { get; } public List<FuncParameter> Parameters { get; }
public Optional<NubType> ReturnType { get; } public Optional<NubType> ReturnType { get; }
public bool SignatureMatches(string name, List<NubType> parameters)
{
if (Name != name) return false;
if (Parameters.Count == 0 && parameters.Count == 0) return true;
if (Parameters.Count > parameters.Count) return false;
for (var i = 0; i < parameters.Count; i++)
{
if (i > Parameters.Count)
{
if (Parameters[^1].Variadic)
{
if (!NubType.IsCompatibleWith(parameters[i], Parameters[^1].Type))
{
return false;
}
}
else
{
return false;
}
}
else if (!NubType.IsCompatibleWith(parameters[i], Parameters[i].Type))
{
return false;
}
}
return true;
}
public string ToString() => $"{Name}({string.Join(", ", Parameters.Select(p => p.ToString()))}){(ReturnType.HasValue ? ": " + ReturnType.Value : "")}"; public string ToString() => $"{Name}({string.Join(", ", Parameters.Select(p => p.ToString()))}){(ReturnType.HasValue ? ": " + ReturnType.Value : "")}";
} }
public class LocalFuncDefinitionNode(IReadOnlyList<Token> tokens, Optional<string> documentation, string name, List<FuncParameter> parameters, BlockNode body, Optional<NubType> returnType, bool global) : DefinitionNode(tokens, documentation) public class LocalFuncDefinitionNode(IReadOnlyList<Token> tokens, Optional<string> documentation, string name, List<FuncParameter> parameters, BlockNode body, Optional<NubType> returnType, bool global) : DefinitionNode(tokens, documentation), IFuncSignature
{ {
public string Name { get; } = name; public string Name { get; } = name;
public List<FuncParameter> Parameters { get; } = parameters; public List<FuncParameter> Parameters { get; } = parameters;

View File

@@ -484,17 +484,36 @@ public class Parser
case Symbol.New: case Symbol.New:
{ {
var type = ParseType(); var type = ParseType();
Dictionary<string, ExpressionNode> initializers = []; switch (type)
ExpectSymbol(Symbol.OpenBrace);
while (!TryExpectSymbol(Symbol.CloseBrace))
{ {
var name = ExpectIdentifier().Value; case NubStructType structType:
ExpectSymbol(Symbol.Assign); {
var value = ParseExpression(); Dictionary<string, ExpressionNode> initializers = [];
initializers.Add(name, value); ExpectSymbol(Symbol.OpenBrace);
} while (!TryExpectSymbol(Symbol.CloseBrace))
{
var name = ExpectIdentifier().Value;
ExpectSymbol(Symbol.Assign);
var value = ParseExpression();
initializers.Add(name, value);
}
expr = new StructInitializerNode(GetTokensForNode(startIndex), type, initializers); expr = new StructInitializerNode(GetTokensForNode(startIndex), structType, initializers);
break;
}
case NubArrayType arrayType:
{
throw new NotImplementedException();
}
default:
{
throw new ParseException(Diagnostic
.Error($"Cannot use new keyword on type {type}")
.At(symbolToken)
.Build());
}
}
break; break;
} }
case Symbol.Ampersand: case Symbol.Ampersand:

View File

@@ -2,8 +2,8 @@
namespace Nub.Lang.Frontend.Parsing; namespace Nub.Lang.Frontend.Parsing;
public class StructInitializerNode(IReadOnlyList<Token> tokens, NubType structType, Dictionary<string, ExpressionNode> initializers) : ExpressionNode(tokens) public class StructInitializerNode(IReadOnlyList<Token> tokens, NubStructType structType, Dictionary<string, ExpressionNode> initializers) : ExpressionNode(tokens)
{ {
public NubType StructType { get; } = structType; public NubStructType StructType { get; } = structType;
public Dictionary<string, ExpressionNode> Initializers { get; } = initializers; public Dictionary<string, ExpressionNode> Initializers { get; } = initializers;
} }

View File

@@ -11,7 +11,7 @@ public class TypeChecker
private NubType? _currentFunctionReturnType; private NubType? _currentFunctionReturnType;
private bool _hasReturnStatement; private bool _hasReturnStatement;
public DiagnosticsResult<List<DefinitionNode>> TypeCheck(List<SourceFile> sourceFiles) public DiagnosticsResult TypeCheck(List<SourceFile> sourceFiles)
{ {
_variables = new Dictionary<string, NubType>(); _variables = new Dictionary<string, NubType>();
_diagnostics = []; _diagnostics = [];
@@ -19,6 +19,16 @@ public class TypeChecker
_hasReturnStatement = false; _hasReturnStatement = false;
_sourceFiles = sourceFiles; _sourceFiles = sourceFiles;
var externFunctionNames = _sourceFiles
.SelectMany(f => f.Definitions)
.OfType<ExternFuncDefinitionNode>()
.ToArray();
foreach (var funcName in externFunctionNames.Where(x => externFunctionNames.Count(y => x.Name == y.Name) > 1))
{
ReportError($"Extern function '{funcName}' has been declared more than once", funcName);
}
foreach (var structDef in _sourceFiles.SelectMany(f => f.Definitions).OfType<StructDefinitionNode>()) foreach (var structDef in _sourceFiles.SelectMany(f => f.Definitions).OfType<StructDefinitionNode>())
{ {
TypeCheckStructDef(structDef); TypeCheckStructDef(structDef);
@@ -28,8 +38,8 @@ public class TypeChecker
{ {
TypeCheckFuncDef(funcDef); TypeCheckFuncDef(funcDef);
} }
return new DiagnosticsResult<List<DefinitionNode>>(_diagnostics, _sourceFiles.SelectMany(f => f.Definitions).ToList()); return new DiagnosticsResult(_diagnostics);
} }
private void TypeCheckStructDef(StructDefinitionNode structDef) private void TypeCheckStructDef(StructDefinitionNode structDef)
@@ -120,14 +130,14 @@ public class TypeChecker
{ {
if (varAssign.ExplicitType.HasValue) if (varAssign.ExplicitType.HasValue)
{ {
if (!AreTypesCompatible(existingVariable, varAssign.ExplicitType.Value)) if (!NubType.IsCompatibleWith(existingVariable, varAssign.ExplicitType.Value))
{ {
ReportError($"Explicit type '{varAssign.ExplicitType.Value}' on variable '{varAssign.Name}' is not compatible with declared type '{existingVariable}'", varAssign); ReportError($"Explicit type '{varAssign.ExplicitType.Value}' on variable '{varAssign.Name}' is not compatible with declared type '{existingVariable}'", varAssign);
return; return;
} }
} }
if (!AreTypesCompatible(valueType, existingVariable)) if (!NubType.IsCompatibleWith(valueType, existingVariable))
{ {
ReportError($"Cannot assign expression of type '{valueType}' to variable '{varAssign.Name}' of type '{existingVariable}'", varAssign); ReportError($"Cannot assign expression of type '{valueType}' to variable '{varAssign.Name}' of type '{existingVariable}'", varAssign);
} }
@@ -137,7 +147,7 @@ public class TypeChecker
if (varAssign.ExplicitType.HasValue) if (varAssign.ExplicitType.HasValue)
{ {
var explicitType = varAssign.ExplicitType.Value; var explicitType = varAssign.ExplicitType.Value;
if (!AreTypesCompatible(valueType, explicitType)) if (!NubType.IsCompatibleWith(valueType, explicitType))
{ {
ReportError($"Cannot assign expression of type '{valueType}' to variable '{varAssign.Name}' of type '{explicitType}'", varAssign); ReportError($"Cannot assign expression of type '{valueType}' to variable '{varAssign.Name}' of type '{explicitType}'", varAssign);
return; return;
@@ -208,7 +218,7 @@ public class TypeChecker
continue; continue;
} }
if (!AreTypesCompatible(argType, paramType)) if (!NubType.IsCompatibleWith(argType, paramType))
{ {
ReportError($"Parameter {i + 1} of function '{funcCall.Name}' expects type '{paramType}', but got '{argType}'", funcCall.Parameters[i]); ReportError($"Parameter {i + 1} of function '{funcCall.Name}' expects type '{paramType}', but got '{argType}'", funcCall.Parameters[i]);
} }
@@ -260,7 +270,7 @@ public class TypeChecker
return; return;
} }
if (!AreTypesCompatible(returnType, _currentFunctionReturnType)) if (!NubType.IsCompatibleWith(returnType, _currentFunctionReturnType))
{ {
ReportError($"Return value of type '{returnType}' is not compatible with function return type '{_currentFunctionReturnType}'", returnNode.Value.Value); ReportError($"Return value of type '{returnType}' is not compatible with function return type '{_currentFunctionReturnType}'", returnNode.Value.Value);
} }
@@ -405,17 +415,10 @@ public class TypeChecker
{ {
var initialized = new HashSet<string>(); var initialized = new HashSet<string>();
var structType = structInit.StructType; var definition = LookupStructDefinition(structInit.StructType.Namespace, structInit.StructType.Name);
if (structType is not NubStructType customType)
{
ReportError($"Type '{structType}' is not a struct type", structInit);
return null;
}
var definition = LookupStructDefinition(customType.Namespace, customType.Name);
if (definition == null) if (definition == null)
{ {
ReportError($"Struct type '{customType.Name}' is not defined", structInit); ReportError($"Struct type '{structInit.StructType.Name}' is not defined", structInit);
return null; return null;
} }
@@ -424,12 +427,12 @@ public class TypeChecker
var definitionField = definition.Fields.FirstOrDefault(f => f.Name == initializer.Key); var definitionField = definition.Fields.FirstOrDefault(f => f.Name == initializer.Key);
if (definitionField == null) if (definitionField == null)
{ {
ReportError($"Field '{initializer.Key}' does not exist in struct '{customType.Name}'", initializer.Value); ReportError($"Field '{initializer.Key}' does not exist in struct '{structInit.StructType.Name}'", initializer.Value);
continue; continue;
} }
var initializerType = TypeCheckExpression(initializer.Value); var initializerType = TypeCheckExpression(initializer.Value);
if (initializerType != null && !AreTypesCompatible(initializerType, definitionField.Type)) if (initializerType != null && !NubType.IsCompatibleWith(initializerType, definitionField.Type))
{ {
ReportError($"Cannot initialize field '{initializer.Key}' of type '{definitionField.Type}' with expression of type '{initializerType}'", initializer.Value); ReportError($"Cannot initialize field '{initializer.Key}' of type '{definitionField.Type}' with expression of type '{initializerType}'", initializer.Value);
} }
@@ -446,11 +449,11 @@ public class TypeChecker
{ {
if (!initialized.Contains(field.Name)) if (!initialized.Contains(field.Name))
{ {
ReportError($"Struct field '{field.Name}' is not initialized on type '{customType.Name}'", structInit); ReportError($"Struct field '{field.Name}' is not initialized on type '{structInit.StructType.Name}'", structInit);
} }
} }
return structType; return structInit.StructType;
} }
private NubType? TypeCheckUnaryExpression(UnaryExpressionNode unaryExpression) private NubType? TypeCheckUnaryExpression(UnaryExpressionNode unaryExpression)
@@ -539,11 +542,6 @@ public class TypeChecker
_diagnostics.Add(diagnostic); _diagnostics.Add(diagnostic);
} }
private static bool AreTypesCompatible(NubType sourceType, NubType targetType)
{
return targetType.Equals(NubPrimitiveType.Any) || sourceType.Equals(targetType);
}
private static bool IsNumeric(NubType type) private static bool IsNumeric(NubType type)
{ {
if (type is not NubPrimitiveType primitiveType) if (type is not NubPrimitiveType primitiveType)
@@ -598,38 +596,7 @@ public class TypeChecker
.Where(f => f.Namespace == @namespace) .Where(f => f.Namespace == @namespace)
.SelectMany(f => f.Definitions) .SelectMany(f => f.Definitions)
.OfType<IFuncSignature>() .OfType<IFuncSignature>()
.FirstOrDefault(SignatureMatches); .FirstOrDefault(f => f.SignatureMatches(name, parameters));
bool SignatureMatches(IFuncSignature node)
{
if (node.Name != name) return false;
if (node.Parameters.Count == 0 && parameters.Count == 0) return true;
if (node.Parameters.Count > parameters.Count) return false;
for (var i = 0; i < parameters.Count; i++)
{
if (i > node.Parameters.Count)
{
if (node.Parameters[^1].Variadic)
{
if (!AreTypesCompatible(parameters[i], node.Parameters[^1].Type))
{
return false;
}
}
else
{
return false;
}
}
else if (!AreTypesCompatible(parameters[i], node.Parameters[i].Type))
{
return false;
}
}
return true;
}
} }
private StructDefinitionNode? LookupStructDefinition(string @namespace, string name) private StructDefinitionNode? LookupStructDefinition(string @namespace, string name)

View File

@@ -11,6 +11,11 @@ public abstract class NubType
public string Name { get; } public string Name { get; }
public static bool IsCompatibleWith(NubType sourceType, NubType targetType)
{
return targetType.Equals(NubPrimitiveType.Any) || sourceType.Equals(targetType);
}
public override bool Equals(object? obj) => obj is NubType item && Name.Equals(item.Name); public override bool Equals(object? obj) => obj is NubType item && Name.Equals(item.Name);
public override int GetHashCode() => HashCode.Combine(Name); public override int GetHashCode() => HashCode.Combine(Name);
public override string ToString() => Name; public override string ToString() => Name;

View File

@@ -58,8 +58,8 @@ internal static class Program
typeCheckResult.PrintAllDiagnostics(); typeCheckResult.PrintAllDiagnostics();
error = error || typeCheckResult.HasErrors; error = error || typeCheckResult.HasErrors;
var generator = new Generator(typeCheckResult.Value); var generator = new Generator();
var result = generator.Generate(); var result = generator.Generate(files);
Console.Out.Write(result); Console.Out.Write(result);