Browse Source

Re-write the blocks generator again (thanks to esdrubal for helping me with the new design).

Now the whitespace/new line handling is done by the output generator per block, instead of having to be managed manually which could lead to all sorts of messy output without being extra careful.

Also the new system limits the usage of generics since C# was too limited to design it properly, and it ended up being more trouble than it was worth. The blocks kinds were also changed to be const int, since enums are very hard to extend and made it hard to provide a common interface for dealing with blocks.
pull/13/merge
triton 12 years ago
parent
commit
b9e57dba78
  1. 9
      src/Generator/Driver.cs
  2. 4
      src/Generator/Generators/CLI/CLIGenerator.cs
  3. 81
      src/Generator/Generators/CLI/CLIHeadersTemplate.cs
  4. 16
      src/Generator/Generators/CLI/CLISourcesTemplate.cs
  5. 64
      src/Generator/Generators/CLI/CLITextTemplate.cs
  6. 4
      src/Generator/Generators/CSharp/CSharpGenerator.cs
  7. 13
      src/Generator/Generators/CSharp/CSharpTextTemplate.cs
  8. 6
      src/Generator/Generators/Generator.cs
  9. 358
      src/Generator/Generators/Template.cs
  10. 25
      src/Generator/Utils/TextGenerator.cs

9
src/Generator/Driver.cs

@ -122,8 +122,7 @@ namespace CppSharp
public List<GeneratorOutput> GenerateCode() public List<GeneratorOutput> GenerateCode()
{ {
var outputs = Generator.Generate(); return Generator.Generate();
return outputs;
} }
public void WriteCode(List<GeneratorOutput> outputs) public void WriteCode(List<GeneratorOutput> outputs)
@ -146,9 +145,7 @@ namespace CppSharp
Diagnostics.EmitMessage(DiagnosticId.FileGenerated, "Generated '{0}'", fileName); Diagnostics.EmitMessage(DiagnosticId.FileGenerated, "Generated '{0}'", fileName);
var filePath = Path.Combine(outputPath, fileName); var filePath = Path.Combine(outputPath, fileName);
File.WriteAllText(Path.GetFullPath(filePath), template.Generate());
var text = template.GenerateText();
File.WriteAllText(Path.GetFullPath(filePath), text);
} }
} }
} }
@ -177,6 +174,7 @@ namespace CppSharp
GeneratePartialClasses = true; GeneratePartialClasses = true;
OutputInteropIncludes = true; OutputInteropIncludes = true;
MaxIndent = 80; MaxIndent = 80;
CommentPrefix = "///";
} }
// General options // General options
@ -218,6 +216,7 @@ namespace CppSharp
public bool WriteOnlyWhenChanged; public bool WriteOnlyWhenChanged;
public Func<TranslationUnit, string> GenerateName; public Func<TranslationUnit, string> GenerateName;
public int MaxIndent; public int MaxIndent;
public string CommentPrefix;
} }
public class InvalidOptionException : Exception public class InvalidOptionException : Exception

4
src/Generator/Generators/CLI/CLIGenerator.cs

@ -17,9 +17,9 @@ namespace CppSharp.Generators.CLI
Type.TypePrinterDelegate += type => type.Visit(typePrinter); Type.TypePrinterDelegate += type => type.Visit(typePrinter);
} }
public override List<TextTemplate> Generate(TranslationUnit unit) public override List<Template> Generate(TranslationUnit unit)
{ {
var outputs = new List<TextTemplate>(); var outputs = new List<Template>();
var header = new CLIHeadersTemplate(Driver, unit); var header = new CLIHeadersTemplate(Driver, unit);
outputs.Add(header); outputs.Add(header);

81
src/Generator/Generators/CLI/CLIHeadersTemplate.cs

@ -19,9 +19,9 @@ namespace CppSharp.Generators.CLI
{ {
} }
public override void GenerateBlocks() public override void Process()
{ {
PushBlock(CLIBlockKind.Header); PushBlock(BlockKind.Header);
PopBlock(); PopBlock();
PushBlock(CLIBlockKind.Includes); PushBlock(CLIBlockKind.Includes);
@ -31,14 +31,13 @@ namespace CppSharp.Generators.CLI
PushBlock(CLIBlockKind.IncludesForwardReferences); PushBlock(CLIBlockKind.IncludesForwardReferences);
WriteLine("#include <{0}>", TranslationUnit.IncludePath); WriteLine("#include <{0}>", TranslationUnit.IncludePath);
GenerateIncludeForwardRefs(); GenerateIncludeForwardRefs();
PopBlock(NewLineKind.BeforeNextBlock);
NewLine(); PopBlock(NewLineKind.Always);
PopBlock();
PopBlock();
GenerateNamespace(TranslationUnit); GenerateNamespace(TranslationUnit);
PushBlock(CLIBlockKind.Footer); PushBlock(BlockKind.Footer);
PopBlock(); PopBlock();
} }
@ -88,12 +87,7 @@ namespace CppSharp.Generators.CLI
} }
foreach (var forwardRef in forwardRefs) foreach (var forwardRef in forwardRefs)
{
WriteLine(forwardRef); WriteLine(forwardRef);
}
if (forwardRefs.Count > 0)
NewLine();
} }
public void GenerateNamespace(Namespace @namespace) public void GenerateNamespace(Namespace @namespace)
@ -103,7 +97,7 @@ namespace CppSharp.Generators.CLI
if (generateNamespace) if (generateNamespace)
{ {
PushBlock(CLIBlockKind.Namespace); PushBlock(CLIBlockKind.Namespace, @namespace);
WriteLine("namespace {0}", isTopLevel WriteLine("namespace {0}", isTopLevel
? Options.OutputNamespace ? Options.OutputNamespace
: SafeIdentifier(@namespace.Name)); : SafeIdentifier(@namespace.Name));
@ -113,55 +107,38 @@ namespace CppSharp.Generators.CLI
// Generate the forward references. // Generate the forward references.
PushBlock(CLIBlockKind.ForwardReferences); PushBlock(CLIBlockKind.ForwardReferences);
GenerateForwardRefs(@namespace); GenerateForwardRefs(@namespace);
PopBlock(); PopBlock(NewLineKind.BeforeNextBlock);
// Generate all the enum declarations for the module. // Generate all the enum declarations for the module.
for (var i = 0; i < @namespace.Enums.Count; ++i) foreach (var @enum in @namespace.Enums)
{ {
var @enum = @namespace.Enums[i];
if (@enum.Ignore || @enum.IsIncomplete) if (@enum.Ignore || @enum.IsIncomplete)
continue; continue;
PushBlock(CLIBlockKind.Enum); PushBlock(CLIBlockKind.Enum, @enum);
GenerateEnum(@enum); GenerateEnum(@enum);
NeedNewLine(); PopBlock(NewLineKind.BeforeNextBlock);
if (i < @namespace.Enums.Count - 1)
NewLine();
PopBlock();
} }
NewLineIfNeeded();
// Generate all the typedef declarations for the module. // Generate all the typedef declarations for the module.
GenerateTypedefs(@namespace); GenerateTypedefs(@namespace);
// Generate all the struct/class declarations for the module. // Generate all the struct/class declarations for the module.
for (var i = 0; i < @namespace.Classes.Count; ++i) foreach (var @class in @namespace.Classes)
{ {
var @class = @namespace.Classes[i];
if (@class.Ignore || @class.IsIncomplete) if (@class.Ignore || @class.IsIncomplete)
continue; continue;
if (@class.IsOpaque) if (@class.IsOpaque)
continue; continue;
PushBlock(CLIBlockKind.Class); PushBlock(CLIBlockKind.Class, @class);
GenerateClass(@class); GenerateClass(@class);
NeedNewLine(); PopBlock(NewLineKind.BeforeNextBlock);
if (i < @namespace.Classes.Count - 1)
NewLine();
PopBlock();
} }
if (@namespace.HasFunctions) if (@namespace.HasFunctions)
{
NewLineIfNeeded();
GenerateFunctions(@namespace); GenerateFunctions(@namespace);
}
foreach(var childNamespace in @namespace.Namespaces) foreach(var childNamespace in @namespace.Namespaces)
GenerateNamespace(childNamespace); GenerateNamespace(childNamespace);
@ -169,7 +146,7 @@ namespace CppSharp.Generators.CLI
if (generateNamespace) if (generateNamespace)
{ {
WriteCloseBraceIndent(); WriteCloseBraceIndent();
PopBlock(); PopBlock(NewLineKind.BeforeNextBlock);
} }
} }
@ -180,16 +157,14 @@ namespace CppSharp.Generators.CLI
if (typedef.Ignore) if (typedef.Ignore)
continue; continue;
if (!GenerateTypedef(typedef)) GenerateTypedef(typedef);
continue;
NewLine();
PopBlock();
} }
} }
public void GenerateFunctions(Namespace @namespace) public void GenerateFunctions(Namespace @namespace)
{ {
PushBlock(CLIBlockKind.FunctionsClass);
WriteLine("public ref class {0}{1}", SafeIdentifier(Options.OutputNamespace), WriteLine("public ref class {0}{1}", SafeIdentifier(Options.OutputNamespace),
TranslationUnit.FileNameWithoutExtension); TranslationUnit.FileNameWithoutExtension);
WriteLine("{"); WriteLine("{");
@ -204,6 +179,8 @@ namespace CppSharp.Generators.CLI
PopIndent(); PopIndent();
WriteLine("};"); WriteLine("};");
PopBlock(NewLineKind.BeforeNextBlock);
} }
public void GenerateDeclarationCommon(Declaration T) public void GenerateDeclarationCommon(Declaration T)
@ -376,7 +353,6 @@ namespace CppSharp.Generators.CLI
public void GenerateClassEvents(Class @class) public void GenerateClassEvents(Class @class)
{ {
PushIndent();
foreach (var @event in @class.Events) foreach (var @event in @class.Events)
{ {
if (@event.Ignore) continue; if (@event.Ignore) continue;
@ -384,7 +360,6 @@ namespace CppSharp.Generators.CLI
var cppTypePrinter = new CppTypePrinter(Driver.TypeDatabase); var cppTypePrinter = new CppTypePrinter(Driver.TypeDatabase);
var cppArgs = cppTypePrinter.VisitParameters(@event.Parameters, hasNames: true); var cppArgs = cppTypePrinter.VisitParameters(@event.Parameters, hasNames: true);
PopIndent();
WriteLine("private:"); WriteLine("private:");
PushIndent(); PushIndent();
@ -410,8 +385,8 @@ namespace CppSharp.Generators.CLI
WriteLine("void raise({0});", cliArgs); WriteLine("void raise({0});", cliArgs);
WriteCloseBraceIndent(); WriteCloseBraceIndent();
PopIndent();
} }
PopIndent();
} }
public void GenerateClassMethods(Class @class) public void GenerateClassMethods(Class @class)
@ -528,6 +503,8 @@ namespace CppSharp.Generators.CLI
if (method.Access != AccessSpecifier.Public) if (method.Access != AccessSpecifier.Public)
return; return;
PushBlock(CLIBlockKind.Method, method);
GenerateDeclarationCommon(method); GenerateDeclarationCommon(method);
if (method.IsStatic) if (method.IsStatic)
@ -541,6 +518,8 @@ namespace CppSharp.Generators.CLI
GenerateMethodParameters(method); GenerateMethodParameters(method);
WriteLine(");"); WriteLine(");");
PopBlock();
} }
public bool GenerateTypedef(TypedefDecl typedef) public bool GenerateTypedef(TypedefDecl typedef)
@ -551,12 +530,14 @@ namespace CppSharp.Generators.CLI
FunctionType function; FunctionType function;
if (typedef.Type.IsPointerTo<FunctionType>(out function)) if (typedef.Type.IsPointerTo<FunctionType>(out function))
{ {
PushBlock(CLIBlockKind.Typedef); PushBlock(CLIBlockKind.Typedef, typedef);
GenerateDeclarationCommon(typedef); GenerateDeclarationCommon(typedef);
WriteLine("public {0};", WriteLine("public {0};",
string.Format(TypePrinter.VisitDelegate(function), string.Format(TypePrinter.VisitDelegate(function),
SafeIdentifier(typedef.Name))); SafeIdentifier(typedef.Name)));
PopBlock(NewLineKind.BeforeNextBlock);
return true; return true;
} }
else if (typedef.Type.IsEnumType()) else if (typedef.Type.IsEnumType())
@ -573,7 +554,11 @@ namespace CppSharp.Generators.CLI
public void GenerateFunction(Function function) public void GenerateFunction(Function function)
{ {
if (function.Ignore) return; if (function.Ignore)
return;
PushBlock(CLIBlockKind.Function, function);
GenerateDeclarationCommon(function); GenerateDeclarationCommon(function);
var retType = function.ReturnType.ToString(); var retType = function.ReturnType.ToString();
@ -582,6 +567,8 @@ namespace CppSharp.Generators.CLI
Write(GenerateParametersList(function.Parameters)); Write(GenerateParametersList(function.Parameters));
WriteLine(");"); WriteLine(");");
PopBlock();
} }
public void GenerateDebug(Declaration decl) public void GenerateDebug(Declaration decl)

16
src/Generator/Generators/CLI/CLISourcesTemplate.cs

@ -19,9 +19,9 @@ namespace CppSharp.Generators.CLI
} }
public override void GenerateBlocks() public override void Process()
{ {
PushBlock(CLIBlockKind.Header); PushBlock(BlockKind.Header);
PopBlock(); PopBlock();
var file = Path.GetFileNameWithoutExtension(TranslationUnit.FileName) var file = Path.GetFileNameWithoutExtension(TranslationUnit.FileName)
@ -48,7 +48,7 @@ namespace CppSharp.Generators.CLI
GenerateNamespace(TranslationUnit); GenerateNamespace(TranslationUnit);
PushBlock(CLIBlockKind.Footer); PushBlock(BlockKind.Footer);
PopBlock(); PopBlock();
} }
@ -78,7 +78,7 @@ namespace CppSharp.Generators.CLI
var includeName = Path.GetFileNameWithoutExtension(translationUnit.FileName); var includeName = Path.GetFileNameWithoutExtension(translationUnit.FileName);
if (includeName == Path.GetFileNameWithoutExtension(((TextTemplate) this).TranslationUnit.FileName)) if (includeName == Path.GetFileNameWithoutExtension(TranslationUnit.FileName))
continue; continue;
includes.Add(string.Format("#include \"{0}.h\"", includeName.Replace('\\', '/'))); includes.Add(string.Format("#include \"{0}.h\"", includeName.Replace('\\', '/')));
@ -544,6 +544,11 @@ namespace CppSharp.Generators.CLI
WriteStartBraceIndent(); WriteStartBraceIndent();
PushBlock(CLIBlockKind.MethodBody, method);
if (method.IsProxy)
goto SkipImpl;
if (@class.IsRefType) if (@class.IsRefType)
{ {
if (method.Kind == CXXMethodKind.Constructor) if (method.Kind == CXXMethodKind.Constructor)
@ -569,6 +574,9 @@ namespace CppSharp.Generators.CLI
GenerateValueTypeConstructorCall(method, @class); GenerateValueTypeConstructorCall(method, @class);
} }
SkipImpl:
PopBlock();
WriteCloseBraceIndent(); WriteCloseBraceIndent();
} }

64
src/Generator/Generators/CLI/CLITextTemplate.cs

@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using CppSharp.AST; using CppSharp.AST;
namespace CppSharp.Generators.CLI namespace CppSharp.Generators.CLI
@ -22,41 +23,20 @@ namespace CppSharp.Generators.CLI
} }
} }
public enum CLIBlockKind public class CLIBlockKind
{ {
Unknown, public const int Includes = BlockKind.LAST + 1;
Header, public const int IncludesForwardReferences = BlockKind.LAST + 2;
Includes, public const int Namespace = BlockKind.LAST + 3;
IncludesForwardReferences, public const int ForwardReferences = BlockKind.LAST + 4;
Namespace, public const int Enum = BlockKind.LAST + 5;
ForwardReferences, public const int Typedef = BlockKind.LAST + 6;
Footer, public const int Class = BlockKind.LAST + 7;
Enum, public const int Method = BlockKind.LAST + 8;
Typedef, public const int MethodBody = BlockKind.LAST + 9;
Class, public const int Usings = BlockKind.LAST + 10;
Method, public const int FunctionsClass = BlockKind.LAST + 11;
Usings public const int Function = BlockKind.LAST + 12;
}
public class CLIBlock : IBlock<CLIBlock, CLIBlockKind>
{
public CLIBlockKind Kind { get; set; }
public List<CLIBlock> Blocks { get; set; }
public CLIBlock Parent { get; set; }
public TextGenerator Text { get; set; }
public Declaration Declaration { get; set; }
public CLIBlock()
{
Blocks = new List<CLIBlock>();
Kind = CLIBlockKind.Unknown;
}
public override string ToString()
{
return Kind.ToString();
}
} }
/// <summary> /// <summary>
@ -64,7 +44,7 @@ namespace CppSharp.Generators.CLI
/// for source (CLISourcesTemplate) and header (CLIHeadersTemplate) /// for source (CLISourcesTemplate) and header (CLIHeadersTemplate)
/// files. /// files.
/// </summary> /// </summary>
public abstract class CLITextTemplate : BlockGenerator<CLIBlockKind, CLIBlock> public abstract class CLITextTemplate : Template
{ {
public CLITypePrinter TypePrinter { get; set; } public CLITypePrinter TypePrinter { get; set; }
@ -79,7 +59,7 @@ namespace CppSharp.Generators.CLI
public abstract override string FileExtension { get; } public abstract override string FileExtension { get; }
public abstract override void GenerateBlocks(); public abstract override void Process();
#region Helpers #region Helpers
@ -100,21 +80,21 @@ namespace CppSharp.Generators.CLI
if (string.IsNullOrWhiteSpace(comment)) if (string.IsNullOrWhiteSpace(comment))
return; return;
// Wrap the comment to the line width. PushBlock(BlockKind.BlockComment);
var maxSize = (int)(Options.MaxIndent - CurrentIndent.Count - "/// ".Length);
var lines = StringHelpers.WordWrapLines(comment, maxSize);
WriteLine("/// <summary>"); WriteLine("/// <summary>");
foreach (string line in lines) WriteLine(comment);
WriteLine(string.Format("/// {0}", line.TrimEnd()));
WriteLine("/// </summary>"); WriteLine("/// </summary>");
PopBlock();
} }
public void GenerateInlineSummary(string comment) public void GenerateInlineSummary(string comment)
{ {
if (String.IsNullOrWhiteSpace(comment)) if (String.IsNullOrWhiteSpace(comment))
return; return;
PushBlock(BlockKind.InlineComment);
WriteLine("/// <summary> {0} </summary>", comment); WriteLine("/// <summary> {0} </summary>", comment);
PopBlock();
} }
public void GenerateMethodParameters(Method method) public void GenerateMethodParameters(Method method)

4
src/Generator/Generators/CSharp/CSharpGenerator.cs

@ -16,9 +16,9 @@ namespace CppSharp.Generators.CSharp
AST.Type.TypePrinterDelegate += type => type.Visit(typePrinter).Type; AST.Type.TypePrinterDelegate += type => type.Visit(typePrinter).Type;
} }
public override List<TextTemplate> Generate(TranslationUnit unit) public override List<Template> Generate(TranslationUnit unit)
{ {
var outputs = new List<TextTemplate>(); var outputs = new List<Template>();
var template = new CSharpTextTemplate(Driver, unit, typePrinter); var template = new CSharpTextTemplate(Driver, unit, typePrinter);
outputs.Add(template); outputs.Add(template);

13
src/Generator/Generators/CSharp/CSharpTextTemplate.cs

@ -63,7 +63,12 @@ namespace CppSharp.Generators.CSharp
} }
} }
public class CSharpTextTemplate : TextTemplate public class CSharpBlockKind
{
}
public class CSharpTextTemplate : Template
{ {
public CSharpTypePrinter TypePrinter { get; private set; } public CSharpTypePrinter TypePrinter { get; private set; }
@ -112,7 +117,7 @@ namespace CppSharp.Generators.CSharp
#endregion #endregion
public override void GenerateBlocks() public override void Process()
{ {
GenerateHeader(); GenerateHeader();
@ -259,9 +264,11 @@ namespace CppSharp.Generators.CSharp
if (String.IsNullOrWhiteSpace(comment)) if (String.IsNullOrWhiteSpace(comment))
return; return;
PushBlock(BlockKind.BlockComment);
WriteLine("/// <summary>"); WriteLine("/// <summary>");
WriteLine("/// {0}", comment); WriteLine("/// {0}", comment);
WriteLine("/// </summary>"); WriteLine("/// </summary>");
PopBlock();
} }
public void GenerateInlineSummary(string comment) public void GenerateInlineSummary(string comment)
@ -269,7 +276,9 @@ namespace CppSharp.Generators.CSharp
if (String.IsNullOrWhiteSpace(comment)) if (String.IsNullOrWhiteSpace(comment))
return; return;
PushBlock(BlockKind.InlineComment);
WriteLine("/// <summary>{0}</summary>", comment); WriteLine("/// <summary>{0}</summary>", comment);
PopBlock();
} }
#region Classes #region Classes

6
src/Generator/Generators/Generator.cs

@ -26,7 +26,7 @@ namespace CppSharp.Generators
/// <summary> /// <summary>
/// Text templates with generated output. /// Text templates with generated output.
/// </summary> /// </summary>
public List<TextTemplate> Templates; public List<Template> Templates;
} }
/// <summary> /// <summary>
@ -79,7 +79,7 @@ namespace CppSharp.Generators
continue; continue;
foreach (var template in templates) foreach (var template in templates)
template.GenerateBlocks(); template.Process();
var output = new GeneratorOutput var output = new GeneratorOutput
{ {
@ -97,6 +97,6 @@ namespace CppSharp.Generators
/// <summary> /// <summary>
/// Generates the outputs for a given translation unit. /// Generates the outputs for a given translation unit.
/// </summary> /// </summary>
public abstract List<TextTemplate> Generate(TranslationUnit unit); public abstract List<Template> Generate(TranslationUnit unit);
} }
} }

358
src/Generator/Generators/Template.cs

@ -1,122 +1,340 @@
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Text; using System.Text;
using CppSharp.AST; using CppSharp.AST;
namespace CppSharp.Generators namespace CppSharp.Generators
{ {
public abstract class TextTemplate : TextGenerator public enum NewLineKind
{
Never,
Always,
BeforeNextBlock
}
public class BlockKind
{
public const int Unknown = 0;
public const int BlockComment = 1;
public const int InlineComment = 2;
public const int Header = 3;
public const int Footer = 4;
public const int LAST = 5;
}
public class Block : ITextGenerator
{
public TextGenerator Text { get; set; }
public int Kind { get; set; }
public NewLineKind NewLineKind { get; set; }
public Block Parent { get; set; }
private List<Block> Blocks { get; set; }
public Declaration Declaration { get; set; }
private bool hasIndentChanged;
private bool isSubBlock;
public Block() : this(BlockKind.Unknown)
{
}
public Block(int kind)
{
Kind = kind;
Blocks = new List<Block>();
Text = new TextGenerator();
hasIndentChanged = false;
isSubBlock = false;
}
public void AddBlock(Block block)
{
if (Text.StringBuilder.Length != 0 || hasIndentChanged)
{
hasIndentChanged = false;
var newBlock = new Block { Text = Text.Clone(), isSubBlock = true };
Text.StringBuilder.Clear();
AddBlock(newBlock);
}
block.Parent = this;
Blocks.Add(block);
}
public IEnumerable<Block> FindBlocks(int kind)
{
foreach (var block in Blocks)
{
if (block.Kind == kind)
yield return block;
foreach (var childBlock in block.FindBlocks(kind))
yield return childBlock;
}
}
public virtual string Generate(DriverOptions options)
{
if (Blocks.Count == 0)
return Text.ToString();
var builder = new StringBuilder();
uint totalIndent = 0;
Block previousBlock = null;
foreach (var childBlock in Blocks)
{
var childText = childBlock.Generate(options);
if (string.IsNullOrEmpty(childText))
continue;
var lines = childText.SplitAndKeep(Environment.NewLine).ToList();
if (previousBlock != null &&
previousBlock.NewLineKind == NewLineKind.BeforeNextBlock)
builder.AppendLine();
if (childBlock.isSubBlock)
totalIndent = 0;
if (childBlock.Kind == BlockKind.BlockComment)
{
// Wrap the comment to the max line width.
var maxSize = options.MaxIndent - totalIndent;
maxSize -= options.CommentPrefix.Length + 2;
lines = StringHelpers.WordWrapLines(childText, (int)maxSize);
for (var i = 0; i < lines.Count; ++i)
{
var line = lines[i];
if (!line.StartsWith(options.CommentPrefix))
lines[i] = options.CommentPrefix + " " + line;
}
}
foreach (var line in lines)
{
if (string.IsNullOrEmpty(line))
continue;
if (!string.IsNullOrWhiteSpace(line))
builder.Append(new string(' ', (int)totalIndent));
builder.Append(line);
if (!line.EndsWith(Environment.NewLine))
builder.AppendLine();
}
if (childBlock.NewLineKind == NewLineKind.Always)
builder.AppendLine();
totalIndent += childBlock.Text.Indent;
previousBlock = childBlock;
}
if (Text.StringBuilder.Length != 0)
builder.Append(Text.StringBuilder);
return builder.ToString();
}
#region ITextGenerator implementation
public uint Indent { get { return Text.Indent; } }
public void Write(string msg, params object[] args)
{
Text.Write(msg, args);
}
public void WriteLine(string msg, params object[] args)
{
Text.WriteLine(msg, args);
}
public void WriteLineIndent(string msg, params object[] args)
{
Text.WriteLineIndent(msg, args);
}
public void NewLine()
{
Text.NewLine();
}
public void NewLineIfNeeded()
{
Text.NewLineIfNeeded();
}
public void NeedNewLine()
{
Text.NeedNewLine();
}
public void ResetNewLine()
{
Text.ResetNewLine();
}
public void PushIndent(uint indent = 4u)
{
hasIndentChanged = true;
Text.PushIndent(indent);
}
public void PopIndent()
{
hasIndentChanged = true;
Text.PopIndent();
}
public void WriteStartBraceIndent()
{
Text.WriteStartBraceIndent();
}
public void WriteCloseBraceIndent()
{
Text.WriteCloseBraceIndent();
}
#endregion
}
public abstract class Template : ITextGenerator
{ {
public Driver Driver { get; private set; } public Driver Driver { get; private set; }
public DriverOptions Options { get; private set; } public DriverOptions Options { get; private set; }
public Library Library { get; private set; }
public TranslationUnit TranslationUnit { get; private set; } public TranslationUnit TranslationUnit { get; private set; }
protected TextTemplate(Driver driver, TranslationUnit unit) public Block RootBlock { get; private set; }
public Block ActiveBlock { get; private set; }
public abstract string FileExtension { get; }
protected Template(Driver driver, TranslationUnit unit)
{ {
Driver = driver; Driver = driver;
Options = driver.Options; Options = driver.Options;
Library = driver.Library;
TranslationUnit = unit; TranslationUnit = unit;
RootBlock = new Block();
ActiveBlock = RootBlock;
} }
public abstract string FileExtension { get; } public abstract void Process();
public abstract void GenerateBlocks(); public string Generate()
{
return RootBlock.Generate(Options);
}
public virtual string GenerateText() #region Block helpers
public void AddBlock(Block block)
{ {
return base.ToString(); ActiveBlock.AddBlock(block);
} }
}
/// <summary> public void PushBlock(int kind, Declaration decl = null)
/// Represents a (nestable) block of text with a specific kind. {
/// </summary> var block = new Block { Kind = kind, Declaration = decl };
public interface IBlock<TBlock, TKind> PushBlock(block);
{ }
TKind Kind { get; set; }
List<TBlock> Blocks { get; set; }
TBlock Parent { get; set; }
TextGenerator Text { get; set; } public void PushBlock(Block block)
Declaration Declaration { get; set; } {
} block.Parent = ActiveBlock;
ActiveBlock.AddBlock(block);
ActiveBlock = block;
}
/// <summary> public void PopBlock(NewLineKind newLineKind = NewLineKind.Never)
/// Block generator used by language-specific generators to generate
/// their output.
/// </summary>
public abstract class BlockGenerator<TKind, TBlock> : TextTemplate
where TBlock : IBlock<TBlock, TKind>, new()
{
public struct BlockData
{ {
public TBlock Block; ActiveBlock.NewLineKind = newLineKind;
public int StartPosition; ActiveBlock = ActiveBlock.Parent;
} }
public List<TBlock> Blocks { get; private set; } public IEnumerable<Block> FindBlocks(int kind)
protected readonly Stack<BlockData> CurrentBlocks; {
return RootBlock.FindBlocks(kind);
}
protected BlockGenerator(Driver driver, TranslationUnit unit) public Block FindBlock(int kind)
: base(driver, unit)
{ {
Blocks = new List<TBlock>(); return FindBlocks(kind).Single();
CurrentBlocks = new Stack<BlockData>();
} }
public void PushBlock(TKind kind) #endregion
#region ITextGenerator implementation
public uint Indent { get { return ActiveBlock.Indent; } }
public void Write(string msg, params object[] args)
{ {
var data = new BlockData ActiveBlock.Write(msg, args);
{ }
Block = new TBlock { Kind = kind },
StartPosition = StringBuilder.Length
};
CurrentBlocks.Push(data); public void WriteLine(string msg, params object[] args)
{
ActiveBlock.WriteLine(msg, args);
} }
public void PopBlock() public void WriteLineIndent(string msg, params object[] args)
{ {
var data = CurrentBlocks.Pop(); ActiveBlock.WriteLineIndent(msg, args);
var block = data.Block; }
var text = StringBuilder.ToString().Substring(data.StartPosition); public void NewLine()
block.Text = Clone(); {
block.Text.StringBuilder = new StringBuilder(text); ActiveBlock.NewLine();
}
var hasParentBlock = CurrentBlocks.Count > 0; public void NewLineIfNeeded()
if (hasParentBlock) {
CurrentBlocks.Peek().Block.Blocks.Add(block); ActiveBlock.NewLineIfNeeded();
else
Blocks.Add(block);
} }
public TBlock FindBlock(TKind kind) public void NeedNewLine()
{ {
//foreach (var block in Blocks) ActiveBlock.NeedNewLine();
// if (block.Kind == kind)
// return block;
return default(TBlock);
} }
public override string GenerateText() public void ResetNewLine()
{ {
var generator = new TextGenerator(); ActiveBlock.ResetNewLine();
}
foreach (var block in Blocks) public void PushIndent(uint indent = 4u)
GenerateBlock(block, generator); {
ActiveBlock.PushIndent(indent);
}
return generator.ToString(); public void PopIndent()
{
ActiveBlock.PopIndent();
} }
void GenerateBlock(TBlock block, TextGenerator gen) public void WriteStartBraceIndent()
{ {
if (block.Blocks.Count == 0) ActiveBlock.WriteStartBraceIndent();
gen.Write(block.Text); }
foreach (var childBlock in block.Blocks) public void WriteCloseBraceIndent()
GenerateBlock(childBlock, gen); {
ActiveBlock.WriteCloseBraceIndent();
} }
#endregion
} }
} }

25
src/Generator/Utils/TextGenerator.cs

@ -5,15 +5,36 @@ using System.Text;
namespace CppSharp namespace CppSharp
{ {
public class TextGenerator public interface ITextGenerator
{ {
const uint DefaultIndent = 4; uint Indent { get; }
void Write(string msg, params object[] args);
void WriteLine(string msg, params object[] args);
void WriteLineIndent(string msg, params object[] args);
void NewLine();
void NewLineIfNeeded();
void NeedNewLine();
void ResetNewLine();
void PushIndent(uint indent = TextGenerator.DefaultIndent);
void PopIndent();
void WriteStartBraceIndent();
void WriteCloseBraceIndent();
}
public class TextGenerator : ITextGenerator
{
public const uint DefaultIndent = 4;
public StringBuilder StringBuilder; public StringBuilder StringBuilder;
protected bool IsStartOfLine; protected bool IsStartOfLine;
protected bool NeedsNewLine; protected bool NeedsNewLine;
protected readonly Stack<uint> CurrentIndent; protected readonly Stack<uint> CurrentIndent;
public uint Indent
{
get { return (uint)CurrentIndent.Sum(u => (int)u); }
}
public TextGenerator() public TextGenerator()
{ {
StringBuilder = new StringBuilder(); StringBuilder = new StringBuilder();

Loading…
Cancel
Save