c generator

This commit is contained in:
nub31
2025-10-16 13:44:19 +02:00
parent 4ca2b39652
commit f7f32b8d23
11 changed files with 814 additions and 117 deletions

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

@@ -0,0 +1,6 @@
{
"files.associations": {
"stdbool.h": "c",
"stdint.h": "c"
}
}

View File

@@ -27,4 +27,28 @@ public static class GCC
return process.ExitCode == 0;
}
public static async Task<bool> Compile(string cPath, string outPath)
{
using var process = new Process();
process.StartInfo = new ProcessStartInfo("gcc", ["-ffreestanding", "-nostartfiles", "-c", "-o", outPath, cPath])
{
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true,
CreateNoWindow = true
};
process.Start();
await process.WaitForExitAsync();
var errors = await process.StandardError.ReadToEndAsync();
if (!string.IsNullOrWhiteSpace(errors))
{
await Console.Error.WriteLineAsync(errors);
}
return process.ExitCode == 0;
}
}

View File

@@ -116,28 +116,38 @@ if (diagnostics.Any(diagnostic => diagnostic.Severity == DiagnosticSeverity.Erro
Directory.CreateDirectory(".build");
sw.Restart();
var generator = new CGenerator(definitions, referencedStructTypes);
var c = generator.Emit();
var cFilePath = Path.Combine(".build", "out.c");
var generator = new QBEGenerator(definitions, referencedStructTypes);
var ssa = generator.Emit();
var ssaFilePath = Path.Combine(".build", "out.ssa");
File.WriteAllText(ssaFilePath, ssa);
Console.WriteLine($"Emit ssa: {sw.ElapsedMilliseconds}ms");
sw.Restart();
var asmFilePath = Path.Combine(".build", "out.asm");
var qbeSuccess = await QBE.Invoke(ssaFilePath, asmFilePath);
if (!qbeSuccess) return 1;
Console.WriteLine($"Emit asm: {sw.ElapsedMilliseconds}ms");
sw.Restart();
File.WriteAllText(cFilePath, c);
var objFilePath = Path.Combine(".build", "out.o");
var asmSuccess = await GCC.Assemble(asmFilePath, objFilePath);
var asmSuccess = await GCC.Compile(cFilePath, objFilePath);
if (!asmSuccess) return 1;
Console.WriteLine($"Assemble: {sw.ElapsedMilliseconds}ms");
// sw.Restart();
//
// var generator = new QBEGenerator(definitions, referencedStructTypes);
// var ssa = generator.Emit();
// var ssaFilePath = Path.Combine(".build", "out.ssa");
// File.WriteAllText(ssaFilePath, ssa);
//
// Console.WriteLine($"Emit ssa: {sw.ElapsedMilliseconds}ms");
// sw.Restart();
//
// var asmFilePath = Path.Combine(".build", "out.asm");
// var qbeSuccess = await QBE.Invoke(ssaFilePath, asmFilePath);
// if (!qbeSuccess) return 1;
//
// Console.WriteLine($"Emit asm: {sw.ElapsedMilliseconds}ms");
// sw.Restart();
//
// var objFilePath = Path.Combine(".build", "out.o");
// var asmSuccess = await GCC.Assemble(asmFilePath, objFilePath);
// if (!asmSuccess) return 1;
//
// Console.WriteLine($"Assemble: {sw.ElapsedMilliseconds}ms");
sw.Restart();
return 0;

View File

@@ -82,114 +82,121 @@ public class Diagnostic
public string FormatANSI()
{
var sb = new StringBuilder();
sb.Append(Severity switch
try
{
DiagnosticSeverity.Error => ConsoleColors.Colorize("error", ConsoleColors.Bold + ConsoleColors.Red),
DiagnosticSeverity.Warning => ConsoleColors.Colorize("warning", ConsoleColors.Bold + ConsoleColors.Yellow),
DiagnosticSeverity.Info => ConsoleColors.Colorize("info", ConsoleColors.Bold + ConsoleColors.Blue),
_ => ConsoleColors.Colorize("unknown", ConsoleColors.Bold + ConsoleColors.White)
});
var sb = new StringBuilder();
if (Span.HasValue)
{
sb.Append(ConsoleColors.Colorize($" at {Span.Value}", ConsoleColors.Faint));
}
sb.Append(": ");
sb.Append(ConsoleColors.Colorize(Message, ConsoleColors.BrightWhite));
if (Span.HasValue)
{
sb.AppendLine();
var text = File.ReadAllText(Span.Value.FilePath);
var lines = text.Split('\n');
var startLine = Span.Value.Start.Line;
var endLine = Span.Value.End.Line;
const int CONTEXT_LINES = 3;
var contextStartLine = Math.Max(1, startLine - CONTEXT_LINES);
var contextEndLine = Math.Min(lines.Length, endLine + CONTEXT_LINES);
var numberPadding = contextEndLine.ToString().Length;
var codePadding = lines.Skip(contextStartLine - 1).Take(contextEndLine - contextStartLine + 1).Max(x => x.Length);
sb.Append('╭');
sb.Append(new string('─', numberPadding + 2));
sb.Append('┬');
sb.Append(new string('─', codePadding + 2));
sb.Append('╮');
sb.AppendLine();
var tokenizer = new Tokenizer(Span.Value.FilePath, text);
tokenizer.Tokenize();
for (var i = contextStartLine; i <= contextEndLine; i++)
sb.Append(Severity switch
{
var line = lines[i - 1];
DiagnosticSeverity.Error => ConsoleColors.Colorize("error", ConsoleColors.Bold + ConsoleColors.Red),
DiagnosticSeverity.Warning => ConsoleColors.Colorize("warning", ConsoleColors.Bold + ConsoleColors.Yellow),
DiagnosticSeverity.Info => ConsoleColors.Colorize("info", ConsoleColors.Bold + ConsoleColors.Blue),
_ => ConsoleColors.Colorize("unknown", ConsoleColors.Bold + ConsoleColors.White)
});
sb.Append("│ ");
sb.Append(i.ToString().PadRight(numberPadding));
sb.Append(" │ ");
sb.Append(ApplySyntaxHighlighting(line.PadRight(codePadding), i, tokenizer.Tokens));
sb.Append(" │");
sb.AppendLine();
if (i >= startLine && i <= endLine)
{
var markerStartColumn = 1;
var markerEndColumn = line.Length;
if (i == startLine)
{
markerStartColumn = Math.Min(Span.Value.Start.Column, 1);
}
if (i == endLine)
{
markerEndColumn = Math.Min(Span.Value.End.Column, line.Length);
}
var markerLength = markerEndColumn - markerStartColumn;
var marker = new string('^', markerLength);
var markerColor = Severity switch
{
DiagnosticSeverity.Info => ConsoleColors.Blue,
DiagnosticSeverity.Warning => ConsoleColors.Yellow,
DiagnosticSeverity.Error => ConsoleColors.Red,
_ => ConsoleColors.White
};
sb.Append("│ ");
sb.Append(new string(' ', numberPadding));
sb.Append(" │ ");
sb.Append(new string(' ', markerStartColumn - 1));
sb.Append(ConsoleColors.Colorize(marker, markerColor));
sb.Append(new string(' ', codePadding - markerEndColumn + 1));
sb.Append(" │");
sb.AppendLine();
}
if (Span.HasValue)
{
sb.Append(ConsoleColors.Colorize($" at {Span.Value}", ConsoleColors.Faint));
}
sb.Append('╰');
sb.Append(new string('─', numberPadding + 2));
sb.Append('┴');
sb.Append(new string('─', codePadding + 2));
sb.Append('╯');
}
sb.Append(": ");
sb.Append(ConsoleColors.Colorize(Message, ConsoleColors.BrightWhite));
if (Help != null)
if (Span.HasValue)
{
sb.AppendLine();
var text = File.ReadAllText(Span.Value.FilePath);
var lines = text.Split('\n');
var startLine = Span.Value.Start.Line;
var endLine = Span.Value.End.Line;
const int CONTEXT_LINES = 3;
var contextStartLine = Math.Max(1, startLine - CONTEXT_LINES);
var contextEndLine = Math.Min(lines.Length, endLine + CONTEXT_LINES);
var numberPadding = contextEndLine.ToString().Length;
var codePadding = lines.Skip(contextStartLine - 1).Take(contextEndLine - contextStartLine + 1).Max(x => x.Length);
sb.Append('╭');
sb.Append(new string('─', numberPadding + 2));
sb.Append('┬');
sb.Append(new string('─', codePadding + 2));
sb.Append('╮');
sb.AppendLine();
var tokenizer = new Tokenizer(Span.Value.FilePath, text);
tokenizer.Tokenize();
for (var i = contextStartLine; i <= contextEndLine; i++)
{
var line = lines[i - 1];
sb.Append("│ ");
sb.Append(i.ToString().PadRight(numberPadding));
sb.Append(" │ ");
sb.Append(ApplySyntaxHighlighting(line.PadRight(codePadding), i, tokenizer.Tokens));
sb.Append(" │");
sb.AppendLine();
if (i >= startLine && i <= endLine)
{
var markerStartColumn = 1;
var markerEndColumn = line.Length;
if (i == startLine)
{
markerStartColumn = Math.Min(Span.Value.Start.Column, 1);
}
if (i == endLine)
{
markerEndColumn = Math.Min(Span.Value.End.Column, line.Length);
}
var markerLength = markerEndColumn - markerStartColumn;
var marker = new string('^', markerLength);
var markerColor = Severity switch
{
DiagnosticSeverity.Info => ConsoleColors.Blue,
DiagnosticSeverity.Warning => ConsoleColors.Yellow,
DiagnosticSeverity.Error => ConsoleColors.Red,
_ => ConsoleColors.White
};
sb.Append("│ ");
sb.Append(new string(' ', numberPadding));
sb.Append(" │ ");
sb.Append(new string(' ', markerStartColumn - 1));
sb.Append(ConsoleColors.Colorize(marker, markerColor));
sb.Append(new string(' ', codePadding - markerEndColumn + 1));
sb.Append(" │");
sb.AppendLine();
}
}
sb.Append('╰');
sb.Append(new string('─', numberPadding + 2));
sb.Append('┴');
sb.Append(new string('─', codePadding + 2));
sb.Append('╯');
}
if (Help != null)
{
sb.AppendLine();
sb.Append(ConsoleColors.Colorize($"help: {Help}", ConsoleColors.Cyan));
}
return sb.ToString();
}
catch (Exception e)
{
sb.AppendLine();
sb.Append(ConsoleColors.Colorize($"help: {Help}", ConsoleColors.Cyan));
return ConsoleColors.Colorize("Failed to generate error message", ConsoleColors.Red);
}
return sb.ToString();
}
private static string ApplySyntaxHighlighting(string line, int lineNumber, List<Token> tokens)

View File

@@ -0,0 +1,518 @@
using NubLang.Ast;
namespace NubLang.Generation;
public class CGenerator
{
private readonly List<DefinitionNode> _definitions;
private readonly HashSet<NubStructType> _structTypes;
private readonly CWriter _writer;
public CGenerator(List<DefinitionNode> definitions, HashSet<NubStructType> structTypes)
{
_definitions = definitions;
_structTypes = structTypes;
_writer = new CWriter();
}
private static string MapType(NubType nubType)
{
return nubType switch
{
NubArrayType => "uintptr_t",
NubBoolType => "bool",
NubCStringType => "char*",
NubFloatType floatType => floatType.Width switch
{
32 => "f32",
64 => "f64",
_ => throw new ArgumentOutOfRangeException()
},
NubFuncType => "uintptr_t",
NubIntType intType => intType.Signed switch
{
true => intType.Width switch
{
8 => "i8",
16 => "i16",
32 => "i32",
64 => "i64",
_ => throw new ArgumentOutOfRangeException()
},
false => intType.Width switch
{
8 => "u8",
16 => "u16",
32 => "u32",
64 => "u64",
_ => throw new ArgumentOutOfRangeException()
},
},
NubPointerType => "uintptr_t",
NubStringType => "uintptr_t",
NubStructType structType => StructName(structType.Module, structType.Name),
NubVoidType => "void",
_ => throw new ArgumentOutOfRangeException(nameof(nubType))
};
}
private static string FuncName(string module, string name, string? externSymbol)
{
return externSymbol ?? $"{module}_{name}";
}
private static string StructName(string module, string name)
{
return $"{module}_{name}";
}
private static string StructFuncName(string module, string name, string function)
{
return $"{module}_{name}_{function}";
}
public string Emit()
{
_writer.WriteLine("""
typedef __builtin_va_list va_list;
#define va_start(ap, last) __builtin_va_start(ap, last)
#define va_arg(ap, type) __builtin_va_arg(ap, type)
#define va_end(ap) __builtin_va_end(ap)
#define va_copy(dest, src) __builtin_va_copy(dest, src)
#define NULL ((void*)0)
typedef unsigned long size_t;
typedef unsigned long uintptr_t;
#define offsetof(type, member) __builtin_offsetof(type, member)
typedef unsigned char u8;
typedef signed char i8;
typedef unsigned short u16;
typedef signed short i16;
typedef unsigned int u32;
typedef signed int i32;
typedef unsigned long long u64;
typedef signed long long i64;
typedef float f32;
typedef double f64;
#define I8_C(x) x
#define U8_C(x) x##U
#define I16_C(x) x
#define U16_C(x) x##U
#define I32_C(x) x
#define U32_C(x) x##U
#define I64_C(x) x##LL
#define U64_C(x) x##ULL
""");
_writer.WriteLine();
_writer.WriteLine("// Struct definitions");
foreach (var structType in _structTypes)
{
_writer.WriteLine("typedef struct");
_writer.WriteLine("{");
using (_writer.Indent())
{
foreach (var field in structType.Fields)
{
_writer.WriteLine($"{MapType(field.Type)} {field.Name};");
}
}
_writer.WriteLine($"}} {StructName(structType.Module, structType.Name)};");
_writer.WriteLine();
}
_writer.WriteLine("// Function declarations");
foreach (var funcNode in _definitions.OfType<FuncNode>())
{
var parameters = funcNode.Signature.Parameters.Count != 0
? string.Join(", ", funcNode.Signature.Parameters.Select(x => $"{MapType(x.Type)} {x.Name}"))
: "void";
var name = FuncName(funcNode.Module, funcNode.Name, funcNode.ExternSymbol);
_writer.WriteLine($"{MapType(funcNode.Signature.ReturnType)} {name}({parameters});");
}
_writer.WriteLine();
_writer.WriteLine("// Struct function implementations");
foreach (var structNode in _definitions.OfType<StructNode>())
{
_writer.WriteLine($"// {structNode.Module}::{structNode.Name}");
foreach (var structFuncNode in structNode.Functions)
{
var parameters = structFuncNode.Signature.Parameters.Count != 0
? string.Join(", ", structFuncNode.Signature.Parameters.Select(x => $"{MapType(x.Type)} {x.Name}"))
: "void";
var name = StructFuncName(structNode.Module, structNode.Name, structFuncNode.Name);
_writer.WriteLine($"{MapType(structFuncNode.Signature.ReturnType)} {name}({parameters})");
EmitBlock(structFuncNode.Body);
}
}
_writer.WriteLine();
_writer.WriteLine("// Function implementations");
foreach (var funcNode in _definitions.OfType<FuncNode>())
{
if (funcNode.Body == null) continue;
var parameters = funcNode.Signature.Parameters.Count != 0
? string.Join(", ", funcNode.Signature.Parameters.Select(x => $"{MapType(x.Type)} {x.Name}"))
: "void";
if (funcNode.ExternSymbol == null)
{
_writer.Write("static ");
}
var name = FuncName(funcNode.Module, funcNode.Name, funcNode.ExternSymbol);
_writer.WriteLine($"{MapType(funcNode.Signature.ReturnType)} {name}({parameters})");
EmitBlock(funcNode.Body);
_writer.WriteLine();
}
return _writer.ToString();
}
private void EmitStatement(StatementNode statementNode)
{
switch (statementNode)
{
case AssignmentNode assignmentNode:
EmitAssignment(assignmentNode);
break;
case BlockNode blockNode:
EmitBlock(blockNode);
break;
case BreakNode breakNode:
EmitBreak(breakNode);
break;
case ContinueNode continueNode:
EmitContinue(continueNode);
break;
case DeferNode deferNode:
EmitDefer(deferNode);
break;
case IfNode ifNode:
EmitIf(ifNode);
break;
case ReturnNode returnNode:
EmitReturn(returnNode);
break;
case StatementFuncCallNode statementFuncCallNode:
EmitStatementFuncCall(statementFuncCallNode);
break;
case StatementStructFuncCallNode statementStructFuncCallNode:
EmitStatementStructFuncCall(statementStructFuncCallNode);
break;
case VariableDeclarationNode variableDeclarationNode:
EmitVariableDeclaration(variableDeclarationNode);
break;
case WhileNode whileNode:
EmitWhile(whileNode);
break;
default:
throw new ArgumentOutOfRangeException(nameof(statementNode));
}
}
private void EmitAssignment(AssignmentNode assignmentNode)
{
throw new NotImplementedException();
}
private void EmitBreak(BreakNode breakNode)
{
_writer.WriteLine("break;");
}
private void EmitContinue(ContinueNode continueNode)
{
_writer.WriteLine("continue;");
}
private void EmitDefer(DeferNode deferNode)
{
throw new NotImplementedException();
}
private void EmitIf(IfNode ifNode)
{
var condition = EmitExpression(ifNode.Condition);
_writer.WriteLine($"if ({condition})");
EmitBlock(ifNode.Body);
ifNode.Else?.Match
(
elseIfNode =>
{
_writer.Write("else ");
EmitIf(elseIfNode);
},
elseNode =>
{
_writer.WriteLine("else");
EmitBlock(elseNode);
}
);
}
private void EmitReturn(ReturnNode returnNode)
{
if (returnNode.Value == null)
{
_writer.WriteLine("return;");
}
else
{
var returnValue = EmitExpression(returnNode.Value);
_writer.WriteLine($"return {returnValue};");
}
}
private void EmitStatementFuncCall(StatementFuncCallNode statementFuncCallNode)
{
var funcCall = EmitFuncCall(statementFuncCallNode.FuncCall);
_writer.WriteLine($"{funcCall};");
}
private void EmitStatementStructFuncCall(StatementStructFuncCallNode statementStructFuncCallNode)
{
var structFuncCall = EmitStructFuncCall(statementStructFuncCallNode.StructFuncCall);
_writer.WriteLine($"{structFuncCall};");
}
private void EmitVariableDeclaration(VariableDeclarationNode variableDeclarationNode)
{
if (variableDeclarationNode.Assignment != null)
{
var value = EmitExpression(variableDeclarationNode.Assignment);
_writer.WriteLine($"{MapType(variableDeclarationNode.Type)} {variableDeclarationNode.Name} = {value};");
}
else
{
_writer.WriteLine($"{MapType(variableDeclarationNode.Type)} {variableDeclarationNode.Name};");
}
}
private void EmitWhile(WhileNode whileNode)
{
var condition = EmitExpression(whileNode.Condition);
_writer.WriteLine($"while ({condition})");
EmitBlock(whileNode.Body);
}
private string EmitExpression(ExpressionNode expressionNode)
{
return expressionNode switch
{
ArrayIndexAccessNode arrayIndexAccessNode => EmitArrayIndexAccess(arrayIndexAccessNode),
ArrayInitializerNode arrayInitializerNode => EmitArrayInitializer(arrayInitializerNode),
BinaryExpressionNode binaryExpressionNode => EmitBinaryExpression(binaryExpressionNode),
BoolLiteralNode boolLiteralNode => EmitBoolLiteral(boolLiteralNode),
ConvertFloatNode convertFloatNode => EmitConvertFloat(convertFloatNode),
ConvertIntNode convertIntNode => EmitConvertInt(convertIntNode),
CStringLiteralNode cStringLiteralNode => EmitCStringLiteral(cStringLiteralNode),
DereferenceNode dereferenceNode => EmitDereference(dereferenceNode),
Float32LiteralNode float32LiteralNode => EmitFloat32Literal(float32LiteralNode),
Float64LiteralNode float64LiteralNode => EmitFloat64Literal(float64LiteralNode),
FloatToIntBuiltinNode floatToIntBuiltinNode => EmitFloatToIntBuiltin(floatToIntBuiltinNode),
FuncCallNode funcCallNode => EmitFuncCall(funcCallNode),
FuncIdentifierNode funcIdentifierNode => EmitFuncIdentifier(funcIdentifierNode),
IntLiteralNode intLiteralNode => EmitIntLiteral(intLiteralNode),
AddressOfNode addressOfNode => EmitAddressOf(addressOfNode),
LValueIdentifierNode lValueIdentifierNode => EmitLValueIdentifier(lValueIdentifierNode),
RValueIdentifierNode rValueIdentifierNode => EmitRValueIdentifier(rValueIdentifierNode),
SizeBuiltinNode sizeBuiltinNode => EmitSizeBuiltin(sizeBuiltinNode),
StringLiteralNode stringLiteralNode => EmitStringLiteral(stringLiteralNode),
StructFieldAccessNode structFieldAccessNode => EmitStructFieldAccess(structFieldAccessNode),
StructFuncCallNode structFuncCallNode => EmitStructFuncCall(structFuncCallNode),
StructInitializerNode structInitializerNode => EmitStructInitializer(structInitializerNode),
UIntLiteralNode uIntLiteralNode => EmitUIntLiteral(uIntLiteralNode),
UnaryExpressionNode unaryExpressionNode => EmitUnaryExpression(unaryExpressionNode),
_ => throw new ArgumentOutOfRangeException(nameof(expressionNode))
};
}
private string EmitArrayIndexAccess(ArrayIndexAccessNode arrayIndexAccessNode)
{
throw new NotImplementedException();
}
private string EmitArrayInitializer(ArrayInitializerNode arrayInitializerNode)
{
throw new NotImplementedException();
}
private string EmitBinaryExpression(BinaryExpressionNode binaryExpressionNode)
{
throw new NotImplementedException();
}
private string EmitBoolLiteral(BoolLiteralNode boolLiteralNode)
{
return boolLiteralNode.Value ? "true" : "false";
}
private string EmitConvertFloat(ConvertFloatNode convertFloatNode)
{
throw new NotImplementedException();
}
private string EmitConvertInt(ConvertIntNode convertIntNode)
{
throw new NotImplementedException();
}
private string EmitCStringLiteral(CStringLiteralNode cStringLiteralNode)
{
return $"\"{cStringLiteralNode.Value}\"";
}
private string EmitDereference(DereferenceNode dereferenceNode)
{
throw new NotImplementedException();
}
private string EmitFloat32Literal(Float32LiteralNode float32LiteralNode)
{
throw new NotImplementedException();
}
private string EmitFloat64Literal(Float64LiteralNode float64LiteralNode)
{
throw new NotImplementedException();
}
private string EmitFloatToIntBuiltin(FloatToIntBuiltinNode floatToIntBuiltinNode)
{
throw new NotImplementedException();
}
private string EmitFuncCall(FuncCallNode funcCallNode)
{
var name = EmitExpression(funcCallNode.Expression);
var parameterNames = funcCallNode.Parameters.Select(EmitExpression).ToList();
return name + "(" + string.Join(", ", parameterNames) + ")";
}
private string EmitFuncIdentifier(FuncIdentifierNode funcIdentifierNode)
{
return FuncName(funcIdentifierNode.Module, funcIdentifierNode.Name, funcIdentifierNode.ExternSymbol);
}
private string EmitIntLiteral(IntLiteralNode intLiteralNode)
{
var type = (NubIntType)intLiteralNode.Type;
return type.Width switch
{
8 => $"I8_C({intLiteralNode.Value})",
16 => $"I16_C({intLiteralNode.Value})",
32 => $"I32_C({intLiteralNode.Value})",
64 => $"I64_C({intLiteralNode.Value})",
_ => throw new ArgumentOutOfRangeException()
};
}
private string EmitAddressOf(AddressOfNode addressOfNode)
{
throw new NotImplementedException();
}
private string EmitLValueIdentifier(LValueIdentifierNode lValueIdentifierNode)
{
throw new NotImplementedException();
}
private string EmitRValueIdentifier(RValueIdentifierNode rValueIdentifierNode)
{
throw new NotImplementedException();
}
private string EmitSizeBuiltin(SizeBuiltinNode sizeBuiltinNode)
{
throw new NotImplementedException();
}
private string EmitStringLiteral(StringLiteralNode stringLiteralNode)
{
throw new NotImplementedException();
}
private string EmitStructFieldAccess(StructFieldAccessNode structFieldAccessNode)
{
throw new NotImplementedException();
}
private string EmitStructFuncCall(StructFuncCallNode structFuncCallNode)
{
var name = StructFuncName(structFuncCallNode.Module, structFuncCallNode.StructName, structFuncCallNode.FuncName);
var parameterNames = structFuncCallNode.Parameters.Select(EmitExpression).ToList();
return name + "(" + string.Join(", ", parameterNames) + ")";
}
private string EmitStructInitializer(StructInitializerNode structInitializerNode)
{
var initValues = new List<string>();
foreach (var initializer in structInitializerNode.Initializers)
{
var value = EmitExpression(initializer.Value);
initValues.Add($".{initializer.Key} = {value}");
}
var initString = initValues.Count == 0
? "0"
: string.Join(", ", initValues);
return $"({MapType(structInitializerNode.Type)}){{ {initString} }}";
}
private string EmitUIntLiteral(UIntLiteralNode uIntLiteralNode)
{
var type = (NubIntType)uIntLiteralNode.Type;
return type.Width switch
{
8 => $"U8_C({uIntLiteralNode.Value})",
16 => $"U16_C({uIntLiteralNode.Value})",
32 => $"U32_C({uIntLiteralNode.Value})",
64 => $"U64_C({uIntLiteralNode.Value})",
_ => throw new ArgumentOutOfRangeException()
};
}
private string EmitUnaryExpression(UnaryExpressionNode unaryExpressionNode)
{
var value = EmitExpression(unaryExpressionNode.Operand);
return unaryExpressionNode.Operator switch
{
UnaryOperator.Negate => $"-{value}",
UnaryOperator.Invert => $"!{value}",
_ => throw new ArgumentOutOfRangeException()
};
}
private void EmitBlock(BlockNode blockNode)
{
_writer.WriteLine("{");
using (_writer.Indent())
{
foreach (var statementNode in blockNode.Statements)
{
EmitStatement(statementNode);
}
}
_writer.WriteLine("}");
}
}

View File

@@ -0,0 +1,70 @@
using System.Text;
namespace NubLang.Generation;
internal class CWriter
{
private readonly StringBuilder _builder = new();
private int _indentLevel;
public IDisposable Indent()
{
_indentLevel++;
return new IndentScope(this);
}
public void WriteLine(string text)
{
WriteIndent();
_builder.AppendLine(text);
}
public void Write(string text)
{
WriteIndent();
_builder.Append(text);
}
public void WriteLine()
{
_builder.AppendLine();
}
public override string ToString()
{
return _builder.ToString();
}
private void WriteIndent()
{
if (_builder.Length > 0)
{
var lastChar = _builder[^1];
if (lastChar != '\n' && lastChar != '\r')
return;
}
for (var i = 0; i < _indentLevel; i++)
{
_builder.Append(" ");
}
}
private class IndentScope : IDisposable
{
private readonly CWriter _writer;
private bool _disposed;
public IndentScope(CWriter writer)
{
_writer = writer;
}
public void Dispose()
{
if (_disposed) return;
_writer._indentLevel--;
_disposed = true;
}
}
}

View File

@@ -324,7 +324,7 @@ public sealed class Parser
}
else
{
elseStatement = (Variant<IfSyntax, BlockSyntax>)ParseBlock(elseStartIndex);
elseStatement = (Variant<IfSyntax, BlockSyntax>)ParseBlock();
}
}

3
examples/hello-world/.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
.build
out.a
out

View File

@@ -0,0 +1,34 @@
module "main"
extern "puts" func puts(text: cstring)
struct Human
{
name: cstring
func print()
{
puts("human")
}
}
extern "main" func main(args: []cstring): i64
{
puts("test")
let test: Human = {
name = "uwu"
}
if false
{
puts("uwu")
}
while true
{
puts("count")
}
test.print()
return 0
}

View File

@@ -0,0 +1,15 @@
NUBC = ../../compiler/NubLang.CLI/bin/Debug/net9.0/nubc
out: .build/out.o
gcc -nostartfiles -lm -o out x86_64.s .build/out.o
.build/out.o: $(NUBC) main.nub
$(NUBC) main.nub
.PHONY: $(NUBC)
$(NUBC):
dotnet build ../../compiler/NubLang.CLI/NubLang.CLI.csproj
clean:
@rm -r .build 2>/dev/null || true
@rm out 2>/dev/null || true

View File

@@ -0,0 +1,10 @@
.intel_syntax noprefix
.text
.globl _start
_start:
mov rdi, rsp
call main
mov rdi, rax
mov rax, 60
syscall