This commit is contained in:
nub31
2025-10-26 16:07:49 +01:00
parent 828e20ddb6
commit 3febaaea81
15 changed files with 256 additions and 44 deletions

5
.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,5 @@
{
"files.associations": {
"ref.h": "c"
}
}

View File

@@ -554,6 +554,16 @@ public class DereferenceNode(List<Token> tokens, NubType type, ExpressionNode ta
} }
} }
public class RefDereferenceNode(List<Token> tokens, NubType type, ExpressionNode target) : LValueExpressionNode(tokens, type)
{
public ExpressionNode Target { get; } = target;
public override IEnumerable<Node> Children()
{
yield return Target;
}
}
public class SizeNode(List<Token> tokens, NubType TargetType) : RValueExpressionNode(tokens, new NubIntType(false, 64)) public class SizeNode(List<Token> tokens, NubType TargetType) : RValueExpressionNode(tokens, new NubIntType(false, 64))
{ {
public NubType TargetType { get; } = TargetType; public NubType TargetType { get; } = TargetType;
@@ -574,6 +584,19 @@ public class CastNode(List<Token> tokens, NubType type, ExpressionNode value) :
} }
} }
public class RefStructInitializerNode(List<Token> tokens, NubType type, Dictionary<IdentifierToken, ExpressionNode> initializers) : RValueExpressionNode(tokens, type)
{
public Dictionary<IdentifierToken, ExpressionNode> Initializers { get; } = initializers;
public override IEnumerable<Node> Children()
{
foreach (var initializer in Initializers)
{
yield return initializer.Value;
}
}
}
public class EnumReferenceIntermediateNode(List<Token> tokens, IdentifierToken moduleToken, IdentifierToken nameToken) : IntermediateExpression(tokens) public class EnumReferenceIntermediateNode(List<Token> tokens, IdentifierToken moduleToken, IdentifierToken nameToken) : IntermediateExpression(tokens)
{ {
public IdentifierToken ModuleToken { get; } = moduleToken; public IdentifierToken ModuleToken { get; } = moduleToken;

View File

@@ -57,6 +57,15 @@ public sealed class NubPointerType(NubType baseType) : NubType
public override int GetHashCode() => HashCode.Combine(typeof(NubPointerType), BaseType); public override int GetHashCode() => HashCode.Combine(typeof(NubPointerType), BaseType);
} }
public class NubRefType(NubType baseType) : NubType
{
public NubType BaseType { get; } = baseType;
public override string ToString() => "&" + BaseType;
public override bool Equals(NubType? other) => other is NubRefType;
public override int GetHashCode() => HashCode.Combine(typeof(NubRefType));
}
public class NubFuncType(List<NubType> parameters, NubType returnType) : NubType public class NubFuncType(List<NubType> parameters, NubType returnType) : NubType
{ {
public List<NubType> Parameters { get; } = parameters; public List<NubType> Parameters { get; } = parameters;

View File

@@ -795,15 +795,16 @@ public sealed class TypeChecker
} }
} }
private DereferenceNode CheckDereference(DereferenceSyntax expression, NubType? _) private ExpressionNode CheckDereference(DereferenceSyntax expression, NubType? _)
{ {
var target = CheckExpression(expression.Target); var target = CheckExpression(expression.Target);
if (target.Type is not NubPointerType pointerType)
{
throw new TypeCheckerException(Diagnostic.Error($"Cannot dereference non-pointer type {target.Type}").At(expression).Build());
}
return new DereferenceNode(expression.Tokens, pointerType.BaseType, target); return target.Type switch
{
NubPointerType pointerType => new DereferenceNode(expression.Tokens, pointerType.BaseType, target),
NubRefType refType => new RefDereferenceNode(expression.Tokens, refType.BaseType, target),
_ => throw new TypeCheckerException(Diagnostic.Error($"Cannot dereference non-pointer type {target.Type}").At(expression).Build())
};
} }
private FuncCallNode CheckFuncCall(FuncCallSyntax expression, NubType? _) private FuncCallNode CheckFuncCall(FuncCallSyntax expression, NubType? _)
@@ -1019,7 +1020,9 @@ public sealed class TypeChecker
} }
} }
if (target.Type is NubStructType structType) switch (target.Type)
{
case NubStructType structType:
{ {
var field = structType.Fields.FirstOrDefault(x => x.Name == expression.MemberToken.Value); var field = structType.Fields.FirstOrDefault(x => x.Name == expression.MemberToken.Value);
if (field == null) if (field == null)
@@ -1032,12 +1035,15 @@ public sealed class TypeChecker
return new StructFieldAccessNode(expression.Tokens, field.Type, target, expression.MemberToken); return new StructFieldAccessNode(expression.Tokens, field.Type, target, expression.MemberToken);
} }
default:
{
throw new TypeCheckerException(Diagnostic throw new TypeCheckerException(Diagnostic
.Error($"Cannot access struct member {expression.MemberToken.Value} on type {target.Type}") .Error($"Cannot access struct member {expression.MemberToken.Value} on type {target.Type}")
.At(expression) .At(expression)
.Build()); .Build());
} }
}
}
private static long CalculateSignedEnumFieldValue(EnumSyntax enumDef, EnumFieldSyntax field) private static long CalculateSignedEnumFieldValue(EnumSyntax enumDef, EnumFieldSyntax field)
{ {
@@ -1083,7 +1089,7 @@ public sealed class TypeChecker
throw new UnreachableException(); throw new UnreachableException();
} }
private StructInitializerNode CheckStructInitializer(StructInitializerSyntax expression, NubType? expectedType) private ExpressionNode CheckStructInitializer(StructInitializerSyntax expression, NubType? expectedType)
{ {
NubStructType? structType = null; NubStructType? structType = null;
@@ -1097,9 +1103,14 @@ public sealed class TypeChecker
structType = checkedStructType; structType = checkedStructType;
} }
else if (expectedType is NubStructType expectedStructType) else
{ {
structType = expectedStructType; structType = expectedType switch
{
NubStructType expectedStructType => expectedStructType,
NubRefType { BaseType: NubStructType expectedStructType } => expectedStructType,
_ => structType
};
} }
if (structType == null) if (structType == null)
@@ -1142,8 +1153,15 @@ public sealed class TypeChecker
.Build()); .Build());
} }
if (expectedType is NubRefType refType && refType.BaseType == structType)
{
return new RefStructInitializerNode(expression.Tokens, refType, initializers);
}
else
{
return new StructInitializerNode(expression.Tokens, structType, initializers); return new StructInitializerNode(expression.Tokens, structType, initializers);
} }
}
private BlockNode CheckBlock(BlockSyntax node) private BlockNode CheckBlock(BlockSyntax node)
{ {
@@ -1197,6 +1215,7 @@ public sealed class TypeChecker
SliceTypeSyntax slice => new NubSliceType(ResolveType(slice.BaseType)), SliceTypeSyntax slice => new NubSliceType(ResolveType(slice.BaseType)),
ConstArrayTypeSyntax arr => new NubConstArrayType(ResolveType(arr.BaseType), arr.Size), ConstArrayTypeSyntax arr => new NubConstArrayType(ResolveType(arr.BaseType), arr.Size),
PointerTypeSyntax ptr => new NubPointerType(ResolveType(ptr.BaseType)), PointerTypeSyntax ptr => new NubPointerType(ResolveType(ptr.BaseType)),
RefTypeSyntax r => new NubRefType(ResolveType(r.BaseType)),
StringTypeSyntax => new NubStringType(), StringTypeSyntax => new NubStringType(),
CustomTypeSyntax c => ResolveCustomType(c), CustomTypeSyntax c => ResolveCustomType(c),
VoidTypeSyntax => new NubVoidType(), VoidTypeSyntax => new NubVoidType(),

View File

@@ -10,15 +10,16 @@ public static class CType
{ {
NubVoidType => "void" + (variableName != null ? $" {variableName}" : ""), NubVoidType => "void" + (variableName != null ? $" {variableName}" : ""),
NubBoolType => "bool" + (variableName != null ? $" {variableName}" : ""), NubBoolType => "bool" + (variableName != null ? $" {variableName}" : ""),
NubIntType intType => CreateIntType(intType, variableName), NubIntType i => CreateIntType(i, variableName),
NubFloatType floatType => CreateFloatType(floatType, variableName), NubFloatType f => CreateFloatType(f, variableName),
NubPointerType ptr => CreatePointerType(ptr, variableName), NubPointerType p => CreatePointerType(p, variableName),
NubRefType r => CreateRefType(r, variableName),
NubSliceType => "struct nub_slice" + (variableName != null ? $" {variableName}" : ""), NubSliceType => "struct nub_slice" + (variableName != null ? $" {variableName}" : ""),
NubStringType => "struct nub_string" + (variableName != null ? $" {variableName}" : ""), NubStringType => "struct nub_string" + (variableName != null ? $" {variableName}" : ""),
NubConstArrayType arr => CreateConstArrayType(arr, variableName, constArraysAsPointers), NubConstArrayType a => CreateConstArrayType(a, variableName, constArraysAsPointers),
NubArrayType arr => CreateArrayType(arr, variableName), NubArrayType a => CreateArrayType(a, variableName),
NubFuncType fn => CreateFuncType(fn, variableName), NubFuncType f => CreateFuncType(f, variableName),
NubStructType st => $"struct {st.Module}_{st.Name}_{NameMangler.Mangle(st)}" + (variableName != null ? $" {variableName}" : ""), NubStructType s => $"struct {s.Module}_{s.Name}_{NameMangler.Mangle(s)}" + (variableName != null ? $" {variableName}" : ""),
_ => throw new NotSupportedException($"C type generation not supported for: {type}") _ => throw new NotSupportedException($"C type generation not supported for: {type}")
}; };
} }
@@ -47,6 +48,12 @@ public static class CType
return cType + (varName != null ? $" {varName}" : ""); return cType + (varName != null ? $" {varName}" : "");
} }
private static string CreateRefType(NubRefType ptr, string? varName)
{
var baseType = Create(ptr.BaseType);
return baseType + "*" + (varName != null ? $" {varName}" : "");
}
private static string CreatePointerType(NubPointerType ptr, string? varName) private static string CreatePointerType(NubPointerType ptr, string? varName)
{ {
var baseType = Create(ptr.BaseType); var baseType = Create(ptr.BaseType);

View File

@@ -10,6 +10,7 @@ public class Generator
private readonly CompilationUnit _compilationUnit; private readonly CompilationUnit _compilationUnit;
private readonly IndentedTextWriter _writer; private readonly IndentedTextWriter _writer;
private readonly Stack<List<DeferNode>> _deferStack = []; private readonly Stack<List<DeferNode>> _deferStack = [];
private readonly Stack<List<string>> _refCleanupStack = [];
private int _tmpIndex; private int _tmpIndex;
public Generator(CompilationUnit compilationUnit) public Generator(CompilationUnit compilationUnit)
@@ -32,6 +33,12 @@ public class Generator
public string Emit() public string Emit()
{ {
_writer.WriteLine(""" _writer.WriteLine("""
#include <stddef.h>
void *rc_alloc(size_t size, void (*destructor)(void *self));
void rc_retain(void *obj);
void rc_release(void *obj);
struct nub_string struct nub_string
{ {
unsigned long long length; unsigned long long length;
@@ -262,14 +269,18 @@ public class Generator
EmitStatement(blockDefers[i].Statement); EmitStatement(blockDefers[i].Statement);
} }
var refCleanups = _refCleanupStack.Peek();
foreach (var refCleanup in refCleanups)
{
_writer.WriteLine($"rc_release({refCleanup});");
}
_writer.WriteLine("return;"); _writer.WriteLine("return;");
} }
else else
{ {
var returnValue = EmitExpression(returnNode.Value); var returnValue = EmitExpression(returnNode.Value);
if (_deferStack.Peek().Count != 0)
{
var tmp = NewTmp(); var tmp = NewTmp();
_writer.WriteLine($"{CType.Create(returnNode.Value.Type, tmp)} = {returnValue};"); _writer.WriteLine($"{CType.Create(returnNode.Value.Type, tmp)} = {returnValue};");
@@ -279,15 +290,15 @@ public class Generator
EmitStatement(blockDefers[i].Statement); EmitStatement(blockDefers[i].Statement);
} }
var refCleanups = _refCleanupStack.Peek();
foreach (var refCleanup in refCleanups)
{
_writer.WriteLine($"rc_release({refCleanup});");
}
EmitLine(returnNode.Tokens.FirstOrDefault()); EmitLine(returnNode.Tokens.FirstOrDefault());
_writer.WriteLine($"return {tmp};"); _writer.WriteLine($"return {tmp};");
} }
else
{
EmitLine(returnNode.Tokens.FirstOrDefault());
_writer.WriteLine($"return {returnValue};");
}
}
} }
private void EmitStatementFuncCall(StatementFuncCallNode statementFuncCallNode) private void EmitStatementFuncCall(StatementFuncCallNode statementFuncCallNode)
@@ -345,6 +356,8 @@ public class Generator
FuncCallNode funcCallNode => EmitFuncCall(funcCallNode), FuncCallNode funcCallNode => EmitFuncCall(funcCallNode),
FuncIdentifierNode funcIdentifierNode => FuncName(funcIdentifierNode.ModuleToken.Value, funcIdentifierNode.NameToken.Value, funcIdentifierNode.ExternSymbolToken?.Value), FuncIdentifierNode funcIdentifierNode => FuncName(funcIdentifierNode.ModuleToken.Value, funcIdentifierNode.NameToken.Value, funcIdentifierNode.ExternSymbolToken?.Value),
AddressOfNode addressOfNode => EmitAddressOf(addressOfNode), AddressOfNode addressOfNode => EmitAddressOf(addressOfNode),
RefDereferenceNode refDereferenceNode => EmitRefDereference(refDereferenceNode),
RefStructInitializerNode refStructInitializerNode => EmitRefStructInitializer(refStructInitializerNode),
SizeNode sizeBuiltinNode => $"sizeof({CType.Create(sizeBuiltinNode.TargetType)})", SizeNode sizeBuiltinNode => $"sizeof({CType.Create(sizeBuiltinNode.TargetType)})",
SliceIndexAccessNode sliceIndexAccessNode => EmitSliceArrayIndexAccess(sliceIndexAccessNode), SliceIndexAccessNode sliceIndexAccessNode => EmitSliceArrayIndexAccess(sliceIndexAccessNode),
StringLiteralNode stringLiteralNode => EmitStringLiteral(stringLiteralNode), StringLiteralNode stringLiteralNode => EmitStringLiteral(stringLiteralNode),
@@ -489,6 +502,38 @@ public class Generator
return $"&{value}"; return $"&{value}";
} }
private string EmitRefDereference(RefDereferenceNode refDereferenceNode)
{
var pointer = EmitExpression(refDereferenceNode.Target);
return $"*{pointer}";
}
private string EmitRefStructInitializer(RefStructInitializerNode refStructInitializerNode)
{
var type = (NubRefType)refStructInitializerNode.Type;
var structType = (NubStructType)type.BaseType;
var tmp = NewTmp();
_writer.WriteLine($"{CType.Create(type)} {tmp} = ({CType.Create(type)})rc_alloc(sizeof({CType.Create(structType)}), NULL);");
var initValues = new List<string>();
foreach (var initializer in refStructInitializerNode.Initializers)
{
var value = EmitExpression(initializer.Value);
initValues.Add($".{initializer.Key.Value} = {value}");
}
var initString = initValues.Count == 0
? "0"
: string.Join(", ", initValues);
_writer.WriteLine($"*{tmp} = ({CType.Create(structType)}){{{initString}}};");
_refCleanupStack.Peek().Add(tmp);
return tmp;
}
private string EmitSliceArrayIndexAccess(SliceIndexAccessNode sliceIndexAccessNode) private string EmitSliceArrayIndexAccess(SliceIndexAccessNode sliceIndexAccessNode)
{ {
var targetType = (NubSliceType)sliceIndexAccessNode.Target.Type; var targetType = (NubSliceType)sliceIndexAccessNode.Target.Type;
@@ -581,6 +626,7 @@ public class Generator
private void EmitBlock(BlockNode blockNode) private void EmitBlock(BlockNode blockNode)
{ {
_deferStack.Push([]); _deferStack.Push([]);
_refCleanupStack.Push([]);
foreach (var statementNode in blockNode.Statements) foreach (var statementNode in blockNode.Statements)
{ {
@@ -592,5 +638,11 @@ public class Generator
{ {
EmitStatement(blockDefers[i].Statement); EmitStatement(blockDefers[i].Statement);
} }
var refCleanups = _refCleanupStack.Pop();
foreach (var refCleanup in refCleanups)
{
_writer.WriteLine($"rc_release({refCleanup});");
}
} }
} }

View File

@@ -716,6 +716,12 @@ public sealed class Parser
} }
} }
if (TryExpectSymbol(Symbol.Ampersand))
{
var baseType = ParseType();
return new RefTypeSyntax(GetTokens(startIndex), baseType);
}
if (TryExpectSymbol(Symbol.Caret)) if (TryExpectSymbol(Symbol.Caret))
{ {
var baseType = ParseType(); var baseType = ParseType();

View File

@@ -150,4 +150,6 @@ public record ConstArrayTypeSyntax(List<Token> Tokens, TypeSyntax BaseType, ulon
public record CustomTypeSyntax(List<Token> Tokens, IdentifierToken? ModuleToken, IdentifierToken NameToken) : TypeSyntax(Tokens); public record CustomTypeSyntax(List<Token> Tokens, IdentifierToken? ModuleToken, IdentifierToken NameToken) : TypeSyntax(Tokens);
public record RefTypeSyntax(List<Token> Tokens, TypeSyntax BaseType) : TypeSyntax(Tokens);
#endregion #endregion

6
examples/playgroud/build.sh Executable file
View File

@@ -0,0 +1,6 @@
#!/bin/bash
set -euo pipefail
obj=$(nubc main.nub)
clang $obj ../../runtime/.build/runtime.o -o .build/out

View File

@@ -0,0 +1,20 @@
module main
extern "puts" func puts(text: ^i8)
struct Human
{
age: u64
name: ^i8
}
extern "main" func main(argc: i64, argv: [?]^i8): i64
{
let x: &Human = {
age = 23
name = "test"
}
puts(x^.name)
return 0
}

1
runtime/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
.build

6
runtime/build.sh Executable file
View File

@@ -0,0 +1,6 @@
#!/bin/bash
set -euo pipefail
mkdir -p .build
clang -c runtime.c -o .build/runtime.o

42
runtime/ref.c Normal file
View File

@@ -0,0 +1,42 @@
#include "ref.h"
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
void *rc_alloc(size_t size, void (*destructor)(void *self))
{
printf("rc_alloc %zu bytes\n", size);
ref_header *header = malloc(sizeof(ref_header) + size);
if (!header)
{
exit(69);
}
header->ref_count = 1;
header->destructor = destructor;
return (void *)(header + 1);
}
void rc_retain(void *obj)
{
printf("rc_retain\n");
ref_header *header = ((ref_header *)obj) - 1;
header->ref_count++;
}
void rc_release(void *obj)
{
ref_header *header = ((ref_header *)obj) - 1;
printf("rc_release\n");
if (--header->ref_count == 0)
{
if (header->destructor)
{
header->destructor(obj);
}
free(header);
printf("rc_free\n");
}
}

13
runtime/ref.h Normal file
View File

@@ -0,0 +1,13 @@
#pragma once
#include <stddef.h>
typedef struct
{
int ref_count;
void (*destructor)(void *self);
} ref_header;
void *rc_alloc(size_t size, void (*destructor)(void *self));
void rc_retain(void *obj);
void rc_release(void *obj);

1
runtime/runtime.c Normal file
View File

@@ -0,0 +1 @@
#include "ref.c"