diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..61dbb37 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,6 @@ +{ + "files.associations": { + "stdbool.h": "c", + "stdint.h": "c" + } +} \ No newline at end of file diff --git a/compiler/NubLang.CLI/GCC.cs b/compiler/NubLang.CLI/GCC.cs index e5eb4fe..5d3399b 100644 --- a/compiler/NubLang.CLI/GCC.cs +++ b/compiler/NubLang.CLI/GCC.cs @@ -27,4 +27,28 @@ public static class GCC return process.ExitCode == 0; } + + public static async Task 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; + } } \ No newline at end of file diff --git a/compiler/NubLang.CLI/Program.cs b/compiler/NubLang.CLI/Program.cs index 4adacf9..e20def1 100644 --- a/compiler/NubLang.CLI/Program.cs +++ b/compiler/NubLang.CLI/Program.cs @@ -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; \ No newline at end of file diff --git a/compiler/NubLang/Diagnostics/Diagnostic.cs b/compiler/NubLang/Diagnostics/Diagnostic.cs index 04b851c..0135bf5 100644 --- a/compiler/NubLang/Diagnostics/Diagnostic.cs +++ b/compiler/NubLang/Diagnostics/Diagnostic.cs @@ -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 tokens) diff --git a/compiler/NubLang/Generation/CGenerator.cs b/compiler/NubLang/Generation/CGenerator.cs new file mode 100644 index 0000000..6adf460 --- /dev/null +++ b/compiler/NubLang/Generation/CGenerator.cs @@ -0,0 +1,518 @@ +using NubLang.Ast; + +namespace NubLang.Generation; + +public class CGenerator +{ + private readonly List _definitions; + private readonly HashSet _structTypes; + private readonly CWriter _writer; + + public CGenerator(List definitions, HashSet 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()) + { + 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()) + { + _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()) + { + 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(); + 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("}"); + } +} \ No newline at end of file diff --git a/compiler/NubLang/Generation/CWriter.cs b/compiler/NubLang/Generation/CWriter.cs new file mode 100644 index 0000000..d74157a --- /dev/null +++ b/compiler/NubLang/Generation/CWriter.cs @@ -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; + } + } +} \ No newline at end of file diff --git a/compiler/NubLang/Syntax/Parser.cs b/compiler/NubLang/Syntax/Parser.cs index 5d27562..67fddaa 100644 --- a/compiler/NubLang/Syntax/Parser.cs +++ b/compiler/NubLang/Syntax/Parser.cs @@ -324,7 +324,7 @@ public sealed class Parser } else { - elseStatement = (Variant)ParseBlock(elseStartIndex); + elseStatement = (Variant)ParseBlock(); } } diff --git a/examples/hello-world/.gitignore b/examples/hello-world/.gitignore new file mode 100644 index 0000000..a8b1f05 --- /dev/null +++ b/examples/hello-world/.gitignore @@ -0,0 +1,3 @@ +.build +out.a +out \ No newline at end of file diff --git a/examples/hello-world/main.nub b/examples/hello-world/main.nub new file mode 100644 index 0000000..f5227e6 --- /dev/null +++ b/examples/hello-world/main.nub @@ -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 +} \ No newline at end of file diff --git a/examples/hello-world/makefile b/examples/hello-world/makefile new file mode 100644 index 0000000..d258aa8 --- /dev/null +++ b/examples/hello-world/makefile @@ -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 diff --git a/examples/hello-world/x86_64.s b/examples/hello-world/x86_64.s new file mode 100644 index 0000000..f55a1d0 --- /dev/null +++ b/examples/hello-world/x86_64.s @@ -0,0 +1,10 @@ +.intel_syntax noprefix + +.text +.globl _start +_start: + mov rdi, rsp + call main + mov rdi, rax + mov rax, 60 + syscall