mirror of https://github.com/mono/CppSharp.git
Browse Source
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
10 changed files with 401 additions and 179 deletions
@ -1,122 +1,340 @@
@@ -1,122 +1,340 @@
|
||||
using System; |
||||
using System.Collections.Generic; |
||||
using System.Linq; |
||||
using System.Text; |
||||
using CppSharp.AST; |
||||
|
||||
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 DriverOptions Options { get; private set; } |
||||
public Library Library { 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; |
||||
Options = driver.Options; |
||||
Library = driver.Library; |
||||
TranslationUnit = unit; |
||||
RootBlock = new Block(); |
||||
ActiveBlock = RootBlock; |
||||
} |
||||
|
||||
public abstract string FileExtension { get; } |
||||
public abstract void Process(); |
||||
|
||||
public string Generate() |
||||
{ |
||||
return RootBlock.Generate(Options); |
||||
} |
||||
|
||||
public abstract void GenerateBlocks(); |
||||
#region Block helpers
|
||||
|
||||
public virtual string GenerateText() |
||||
public void AddBlock(Block block) |
||||
{ |
||||
return base.ToString(); |
||||
ActiveBlock.AddBlock(block); |
||||
} |
||||
|
||||
public void PushBlock(int kind, Declaration decl = null) |
||||
{ |
||||
var block = new Block { Kind = kind, Declaration = decl }; |
||||
PushBlock(block); |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// Represents a (nestable) block of text with a specific kind.
|
||||
/// </summary>
|
||||
public interface IBlock<TBlock, TKind> |
||||
public void PushBlock(Block block) |
||||
{ |
||||
TKind Kind { get; set; } |
||||
List<TBlock> Blocks { get; set; } |
||||
TBlock Parent { get; set; } |
||||
block.Parent = ActiveBlock; |
||||
ActiveBlock.AddBlock(block); |
||||
ActiveBlock = block; |
||||
} |
||||
|
||||
TextGenerator Text { get; set; } |
||||
Declaration Declaration { get; set; } |
||||
public void PopBlock(NewLineKind newLineKind = NewLineKind.Never) |
||||
{ |
||||
ActiveBlock.NewLineKind = newLineKind; |
||||
ActiveBlock = ActiveBlock.Parent; |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// 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 IEnumerable<Block> FindBlocks(int kind) |
||||
{ |
||||
public struct BlockData |
||||
return RootBlock.FindBlocks(kind); |
||||
} |
||||
|
||||
public Block FindBlock(int kind) |
||||
{ |
||||
public TBlock Block; |
||||
public int StartPosition; |
||||
return FindBlocks(kind).Single(); |
||||
} |
||||
|
||||
public List<TBlock> Blocks { get; private set; } |
||||
protected readonly Stack<BlockData> CurrentBlocks; |
||||
#endregion
|
||||
|
||||
#region ITextGenerator implementation
|
||||
|
||||
protected BlockGenerator(Driver driver, TranslationUnit unit) |
||||
: base(driver, unit) |
||||
public uint Indent { get { return ActiveBlock.Indent; } } |
||||
|
||||
public void Write(string msg, params object[] args) |
||||
{ |
||||
Blocks = new List<TBlock>(); |
||||
CurrentBlocks = new Stack<BlockData>(); |
||||
ActiveBlock.Write(msg, args); |
||||
} |
||||
|
||||
public void PushBlock(TKind kind) |
||||
{ |
||||
var data = new BlockData |
||||
public void WriteLine(string msg, params object[] args) |
||||
{ |
||||
Block = new TBlock { Kind = kind }, |
||||
StartPosition = StringBuilder.Length |
||||
}; |
||||
|
||||
CurrentBlocks.Push(data); |
||||
ActiveBlock.WriteLine(msg, args); |
||||
} |
||||
|
||||
public void PopBlock() |
||||
public void WriteLineIndent(string msg, params object[] args) |
||||
{ |
||||
var data = CurrentBlocks.Pop(); |
||||
var block = data.Block; |
||||
ActiveBlock.WriteLineIndent(msg, args); |
||||
} |
||||
|
||||
var text = StringBuilder.ToString().Substring(data.StartPosition); |
||||
block.Text = Clone(); |
||||
block.Text.StringBuilder = new StringBuilder(text); |
||||
public void NewLine() |
||||
{ |
||||
ActiveBlock.NewLine(); |
||||
} |
||||
|
||||
var hasParentBlock = CurrentBlocks.Count > 0; |
||||
if (hasParentBlock) |
||||
CurrentBlocks.Peek().Block.Blocks.Add(block); |
||||
else |
||||
Blocks.Add(block); |
||||
public void NewLineIfNeeded() |
||||
{ |
||||
ActiveBlock.NewLineIfNeeded(); |
||||
} |
||||
|
||||
public TBlock FindBlock(TKind kind) |
||||
public void NeedNewLine() |
||||
{ |
||||
//foreach (var block in Blocks)
|
||||
// if (block.Kind == kind)
|
||||
// return block;
|
||||
return default(TBlock); |
||||
ActiveBlock.NeedNewLine(); |
||||
} |
||||
|
||||
public override string GenerateText() |
||||
public void ResetNewLine() |
||||
{ |
||||
var generator = new TextGenerator(); |
||||
ActiveBlock.ResetNewLine(); |
||||
} |
||||
|
||||
foreach (var block in Blocks) |
||||
GenerateBlock(block, generator); |
||||
public void PushIndent(uint indent = 4u) |
||||
{ |
||||
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) |
||||
gen.Write(block.Text); |
||||
ActiveBlock.WriteStartBraceIndent(); |
||||
} |
||||
|
||||
foreach (var childBlock in block.Blocks) |
||||
GenerateBlock(childBlock, gen); |
||||
public void WriteCloseBraceIndent() |
||||
{ |
||||
ActiveBlock.WriteCloseBraceIndent(); |
||||
} |
||||
|
||||
#endregion
|
||||
} |
||||
} |
Loading…
Reference in new issue