名前なども少し整えた。
成果物
前回まで
コード
- Program.cs
- CodeGenerator.cs
- CodeTree.cs
- CodeNode.cs
- BlockCodeNode.cs
- CodeNode.cs
Program.cs
using System; namespace CodeGen { class Program { static void Main(string[] args) { var gen = new CodeGenerator(); var tree = new CodeTree() .Add(new CodeNode("using System;")) .Add(new CodeNode("")) .Add(new CodeBlock("namespace MyNamespace") .Add(new CodeBlock("class MyClass") .Add(new CodeBlock("static void Main(string[] args)") .Add(new CodeNode(@"Console.WriteLine(""Hello world"");"))))); Console.WriteLine(gen.Generate(tree)); } } }
CodeGenerator.cs
using System; using System.Text; namespace CodeGen { class CodeGenerator { public string Indent { get; set; } = " "; private StringBuilder builder; private BlockCodeGenerator blockGen; public CodeGenerator() { this.builder = new StringBuilder(); this.blockGen = new BlockCodeGenerator(this.builder, Indent); } public string Generate(CodeTree tree) { builder.Clear(); foreach(CodeNode node in tree.Children) { _Generate(node, 0); } return builder.ToString().TrimStart('\n'); // 先頭の改行を1つ削除 } private void _Generate(CodeNode node, int indent) { builder.Append("\n"); builder.Insert(builder.Length, Indent, indent).Append(node.Value); blockGen.Blocking(node, indent); if (0 < node.Children.Count) { indent++; foreach(CodeNode n in node.Children) { _Generate(n, indent); } indent--; } blockGen.Blocked(node, indent); } class BlockCodeGenerator { public string Start { get; private set; } public string End { get; private set; } public string Indent { get; private set; } private StringBuilder builder; public BlockCodeGenerator(in StringBuilder builder, in string Indent, string start="{", string end="}") => (this.builder, this.Indent, Start, End) = (builder, Indent, start, end); public void Blocking(CodeNode node, int indent) => Block(node, Start, indent); public void Blocked(CodeNode node, int indent) => Block(node, End, indent); private void Block(CodeNode node, string keyword, int indent) { if (node is CodeBlock n) { switch (n.Style) { case CodeBlock.StyleType.None: return; case CodeBlock.StyleType.Minimum: builder.Append(keyword); return; case CodeBlock.StyleType.Space: builder.Append($" {keyword}"); return; case CodeBlock.StyleType.NewLine: builder.AppendLine().Insert(builder.Length, Indent, indent).Append(keyword); return; default: return; } } } } } }
ブロック文の作成を内部クラスBlockCodeGenerator
に一任した。まだ汚い。builder
やIndent
はDRYに共有したい。
CodeTree.cs
using System; using System.Collections.Generic; using System.Collections.ObjectModel; // ReadOnlyCollection namespace CodeGen { class CodeTree { protected IList<CodeNode> children; public IReadOnlyList<CodeNode> Children { get; } public CodeTree() { children = new List<CodeNode>(); Children = new ReadOnlyCollection<CodeNode>(children); } public CodeTree Add(CodeNode node) { children.Add(node); return this; } } }
CodeNode.cs
using System; using System.Collections.Generic; using System.Collections.ObjectModel; // ReadOnlyCollection namespace CodeGen { class CodeNode : CodeTree { public string Value { get; } public CodeNode(string value) : base() => Value = value; public new CodeNode Add(CodeNode node) { children.Add(node); return this; } } }
CodeBlock.cs
using System; using System.IO; using Microsoft.CSharp; using System.CodeDom.Compiler; namespace CodeGen { class CodeBlock: CodeNode { public enum StyleType { None, Minimum, Space, NewLine }; public StyleType Style { get; private set; } public CodeBlock(string value, StyleType style=StyleType.NewLine) : base(value) => Style = style; } }
BlockCodeNode
をCodeBlock
に改名した。CodeDomのCodeNamespaceなどをみると、クラス名の先頭がCode
で統一されているので踏襲してみた。
実行結果
using System; namespace MyNamespace { class MyClass { static void Main(string[] args) { Console.WriteLine("Hello world"); } } }
所感
名前空間とかクラスとかの生成に特化したクラスも作りたい。
対象環境
- Raspbierry pi 3 Model B+
- Raspbian stretch 9.0 2018-11-13 ※
- bash 4.4.12(1)-release ※
- SQLite 3.29.0 ※
- C# dotnet 3.0.100 ※
$ uname -a Linux raspberrypi 4.19.42-v7+ #1218 SMP Tue May 14 00:48:17 BST 2019 armv7l GNU/Linux