using System.Globalization; 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; } } 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 string EmitExpression(NodeExpression node) { return node switch { NodeExpressionBoolLiteral expression => EmitExpressionBoolLiteral(expression), NodeExpressionFloatLiteral expression => EmitExpressionFloatLiteral(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 EmitExpressionFloatLiteral(NodeExpressionFloatLiteral expression) { return expression.Value.Value.ToString(CultureInfo.InvariantCulture); } 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 node) { return node switch { NodeTypeCustom type => $"struct {type.Name.Ident}", NodeTypeFloat type => type.Width switch { 32 => "float", 64 => "double", }, NodeTypePointer type => $"{CType(type.To)}*", NodeTypeSInt type => type.Width switch { 8 => "byte", 16 => "short", 32 => "int", 64 => "long", }, NodeTypeUInt type => type.Width switch { 8 => "unsigned byte", 16 => "unsigned short", 32 => "unsigned int", 64 => "unsigned long", }, NodeTypeString => "struct string", NubTypeVoid => "void", _ => throw new ArgumentOutOfRangeException(nameof(node)) }; } } 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; } } }