using System.Text; namespace Compiler; public sealed class Generator(List nodes) { public static string Emit(List nodes) { return new Generator(nodes).Emit(); } private IndentedTextWriter writer = new(); private string Emit() { writer.WriteLine(""" struct string { const char *data; int length; }; """); foreach (var node in nodes.OfType()) { var parameters = node.Parameters.Select(x => CType(x.Type, x.Name.Ident)); writer.WriteLine($"{CType(node.ReturnType, node.Name.Ident)}({string.Join(", ", parameters)});"); } writer.WriteLine(); foreach (var node in nodes.OfType()) { var parameters = node.Parameters.Select(x => CType(x.Type, x.Name.Ident)); writer.WriteLine($"{CType(node.ReturnType, node.Name.Ident)}({string.Join(", ", parameters)})"); writer.WriteLine("{"); using (writer.Indent()) { EmitStatement(node.Body); } writer.WriteLine("}"); writer.WriteLine(); } return writer.ToString(); } private void EmitStatement(NodeStatement node) { switch (node) { case NodeStatementBlock statement: EmitStatementBlock(statement); break; case NodeStatementFuncCall statement: EmitStatementFuncCall(statement); break; case NodeStatementReturn statement: EmitStatementReturn(statement); break; } } private void EmitStatementBlock(NodeStatementBlock node) { writer.WriteLine("{"); using (writer.Indent()) { foreach (var statement in node.Statements) EmitStatement(statement); } writer.WriteLine("}"); } private void EmitStatementFuncCall(NodeStatementFuncCall node) { var name = EmitExpression(node.Func); var parameterValues = node.Parameters.Select(EmitExpression).ToList(); writer.WriteLine($"{name}({string.Join(", ", parameterValues)});"); } private void EmitStatementReturn(NodeStatementReturn statement) { var value = EmitExpression(statement.Value); writer.WriteLine($"return {value};"); } private string EmitExpression(NodeExpression node) { return node switch { NodeExpressionBoolLiteral expression => EmitExpressionBoolLiteral(expression), NodeExpressionIntLiteral expression => EmitExpressionIntLiteral(expression), NodeExpressionStringLiteral expression => EmitExpressionStringLiteral(expression), NodeExpressionIdent expression => EmitExpressionIdent(expression), }; } private string EmitExpressionBoolLiteral(NodeExpressionBoolLiteral expression) { return expression.Value.Value ? "1" : "0"; } private string EmitExpressionIntLiteral(NodeExpressionIntLiteral expression) { return expression.Value.Value.ToString(); } private string EmitExpressionStringLiteral(NodeExpressionStringLiteral expression) { return $"(struct string){{ \"{expression.Value.Value}\", {expression.Value.Value.Length} }}"; } private string EmitExpressionIdent(NodeExpressionIdent expression) { return expression.Value.Ident; } private static string CType(NodeType type, string? variableName = null) { return type switch { NodeTypeVoid => "void" + (variableName != null ? $" {variableName}" : ""), NodeTypeBool => "bool" + (variableName != null ? $" {variableName}" : ""), NodeTypeSInt intType => CTypeSInt(intType, variableName), NodeTypeUInt intType => CTypeUInt(intType, variableName), NodeTypePointer ptr => CType(ptr.To) + "*" + (variableName != null ? $" {variableName}" : ""), NodeTypeString => "struct string" + (variableName != null ? $" {variableName}" : ""), NodeTypeFunc fn => CTypeFunc(fn, variableName), }; } private static string CTypeSInt(NodeTypeSInt intType, string? varName) { var cType = intType.Width switch { 8 => "char", 16 => "short", 32 => "int", 64 => "long long", }; return cType + (varName != null ? $" {varName}" : ""); } private static string CTypeUInt(NodeTypeUInt intType, string? varName) { var cType = intType.Width switch { 8 => "unsigned char", 16 => "unsigned short", 32 => "unsigned int", 64 => "unsigned long long", }; return cType + (varName != null ? $" {varName}" : ""); } private static string CTypeFunc(NodeTypeFunc fn, string? varName) { var returnType = CType(fn.ReturnType); var parameters = string.Join(", ", fn.Parameters.Select(p => CType(p))); if (string.IsNullOrEmpty(parameters)) { parameters = "void"; } if (varName != null) { return $"{returnType} (*{varName})({parameters})"; } return $"{returnType} (*)({parameters})"; } } internal class IndentedTextWriter { 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 IndentedTextWriter _writer; private bool _disposed; public IndentScope(IndentedTextWriter writer) { _writer = writer; } public void Dispose() { if (_disposed) return; _writer._indentLevel--; _disposed = true; } } }