Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AST Rework #17

Merged
merged 5 commits into from
Jun 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 28 additions & 12 deletions Broccolini/Parsing/Parser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ private static SectionChildIniNode ParseSectionChildNode(IParserInput input)
{
_ when IsComment(input) => ParseComment(input),
_ when IsKeyValue(input) => ParseKeyValue(input),
_ => ParseTrivia(input),
_ => ParseUnrecognized(input),
};

private static bool IsSection(IParserInput input)
Expand All @@ -60,6 +60,17 @@ private static bool IsKeyValue(IParserInput input)
}

private static IniNode ParseSection(IParserInput input)
{
var header = ParseSectionHeader(input);
var newLine = input.ReadOrNull<IniToken.NewLine>();
var children = ParseSectionChildren(input);
return new SectionIniNode(header, children)
{
NewLine = newLine,
};
}

private static IniSectionHeader ParseSectionHeader(IParserInput input)
{
var leadingTrivia = input.ReadOrNull<IniToken.WhiteSpace>();
var openingBracketToken = input.Read();
Expand All @@ -68,29 +79,26 @@ private static IniNode ParseSection(IParserInput input)
var triviaBeforeClosingBracket = input.ReadOrNull<IniToken.WhiteSpace>();
var closingBracket = input.ReadOrNull<IniToken.ClosingBracket>();
var trailingTrivia = input.ReadWhile(static t => t is not IniToken.NewLine);
var newLine = input.ReadOrNull<IniToken.NewLine>();
var children = ParseSectionChildren(input);
return new SectionIniNode(name, children)
return new IniSectionHeader(name)
{
LeadingTrivia = leadingTrivia,
OpeningBracket = (IniToken.OpeningBracket)openingBracketToken,
TriviaAfterOpeningBracket = triviaAfterOpeningBracket,
TriviaBeforeClosingBracket = triviaBeforeClosingBracket,
ClosingBracket = closingBracket,
TrailingTrivia = trailingTrivia,
NewLine = newLine,
};
}

private static KeyValueIniNode ParseKeyValue(IParserInput input)
{
var leadingTrivia = input.ReadOrNull<IniToken.WhiteSpace>();
var leadingTrivia = input.ReadWhile(t => t is IniToken.WhiteSpace);
var key = string.Concat(input.ReadWhileExcludeTrailingWhitespace(static t => t is not IniToken.EqualsSign));
var triviaBeforeEqualsSign = input.ReadOrNull<IniToken.WhiteSpace>();
var equalsSign = input.Read();
var triviaAfterEqualsSign = input.ReadOrNull<IniToken.WhiteSpace>();
var (quote, value) = ParseQuotedValue(input);
var trailingTrivia = input.ReadOrNull<IniToken.WhiteSpace>();
var trailingTrivia = input.ReadWhile(t => t is IniToken.WhiteSpace);
var newLine = input.ReadOrNull<IniToken.NewLine>();
return new KeyValueIniNode(key, value)
{
Expand All @@ -106,11 +114,11 @@ private static KeyValueIniNode ParseKeyValue(IParserInput input)

private static CommentIniNode ParseComment(IParserInput input)
{
var leadingTrivia = input.ReadOrNull<IniToken.WhiteSpace>();
var leadingTrivia = input.ReadWhile(t => t is IniToken.WhiteSpace);
var semicolon = (IniToken.Semicolon)input.Read();
var triviaAfterSemicolon = input.ReadOrNull<IniToken.WhiteSpace>();
var text = string.Concat(input.ReadWhileExcludeTrailingWhitespace(static t => t is not IniToken.NewLine));
var trailingTrivia = input.ReadOrNull<IniToken.WhiteSpace>();
var trailingTrivia = input.ReadWhile(t => t is IniToken.WhiteSpace);
var newLine = input.ReadOrNull<IniToken.NewLine>();
return new CommentIniNode(text)
{
Expand All @@ -122,11 +130,19 @@ private static CommentIniNode ParseComment(IParserInput input)
};
}

private static UnrecognizedIniNode ParseTrivia(IParserInput input)
=> new(input.ReadWhile(t => t is not IniToken.NewLine))
private static UnrecognizedIniNode ParseUnrecognized(IParserInput input)
{
var leadingTrivia = input.ReadWhile(t => t is IniToken.WhiteSpace);
var content = input.ReadWhile(t => t is not IniToken.NewLine);
var trailingTrivia = input.ReadWhile(t => t is IniToken.WhiteSpace);
var newLine = input.ReadOrNull<IniToken.NewLine>();
return new UnrecognizedIniNode(content)
{
NewLine = input.ReadOrNull<IniToken.NewLine>(),
LeadingTrivia = leadingTrivia,
NewLine = newLine,
TrailingTrivia = trailingTrivia,
};
}

private static (IniToken.Quote?, string) ParseQuotedValue(IParserInput input)
{
Expand Down
24 changes: 1 addition & 23 deletions Broccolini/PublicAPI.Shipped.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,10 @@ Broccolini.IniParser
Broccolini.SemanticModelExtensions
Broccolini.Syntax.CommentIniNode
Broccolini.Syntax.CommentIniNode.CommentIniNode(string! text) -> void
Broccolini.Syntax.CommentIniNode.LeadingTrivia.get -> Broccolini.Syntax.IniToken.WhiteSpace?
Broccolini.Syntax.CommentIniNode.LeadingTrivia.init -> void
Broccolini.Syntax.CommentIniNode.Semicolon.get -> Broccolini.Syntax.IniToken.Semicolon!
Broccolini.Syntax.CommentIniNode.Semicolon.init -> void
Broccolini.Syntax.CommentIniNode.Text.get -> string!
Broccolini.Syntax.CommentIniNode.Text.init -> void
Broccolini.Syntax.CommentIniNode.TrailingTrivia.get -> Broccolini.Syntax.IniToken.WhiteSpace?
Broccolini.Syntax.CommentIniNode.TrailingTrivia.init -> void
Broccolini.Syntax.CommentIniNode.TriviaAfterSemicolon.get -> Broccolini.Syntax.IniToken.WhiteSpace?
Broccolini.Syntax.CommentIniNode.TriviaAfterSemicolon.init -> void
Broccolini.Syntax.IIniNodeVisitor
Expand Down Expand Up @@ -59,12 +55,8 @@ Broccolini.Syntax.KeyValueIniNode.EqualsSign.init -> void
Broccolini.Syntax.KeyValueIniNode.Key.get -> string!
Broccolini.Syntax.KeyValueIniNode.Key.init -> void
Broccolini.Syntax.KeyValueIniNode.KeyValueIniNode(string! key, string! value) -> void
Broccolini.Syntax.KeyValueIniNode.LeadingTrivia.get -> Broccolini.Syntax.IniToken.WhiteSpace?
Broccolini.Syntax.KeyValueIniNode.LeadingTrivia.init -> void
Broccolini.Syntax.KeyValueIniNode.Quote.get -> Broccolini.Syntax.IniToken.Quote?
Broccolini.Syntax.KeyValueIniNode.Quote.init -> void
Broccolini.Syntax.KeyValueIniNode.TrailingTrivia.get -> Broccolini.Syntax.IniToken.WhiteSpace?
Broccolini.Syntax.KeyValueIniNode.TrailingTrivia.init -> void
Broccolini.Syntax.KeyValueIniNode.TriviaAfterEqualsSign.get -> Broccolini.Syntax.IniToken.WhiteSpace?
Broccolini.Syntax.KeyValueIniNode.TriviaAfterEqualsSign.init -> void
Broccolini.Syntax.KeyValueIniNode.TriviaBeforeEqualsSign.get -> Broccolini.Syntax.IniToken.WhiteSpace?
Expand All @@ -76,22 +68,8 @@ Broccolini.Syntax.SectionChildIniNode.SectionChildIniNode(Broccolini.Syntax.Sect
Broccolini.Syntax.SectionIniNode
Broccolini.Syntax.SectionIniNode.Children.get -> System.Collections.Immutable.IImmutableList<Broccolini.Syntax.SectionChildIniNode!>!
Broccolini.Syntax.SectionIniNode.Children.init -> void
Broccolini.Syntax.SectionIniNode.ClosingBracket.get -> Broccolini.Syntax.IniToken.ClosingBracket?
Broccolini.Syntax.SectionIniNode.ClosingBracket.init -> void
Broccolini.Syntax.SectionIniNode.Equals(Broccolini.Syntax.SectionIniNode? other) -> bool
Broccolini.Syntax.SectionIniNode.LeadingTrivia.get -> Broccolini.Syntax.IniToken.WhiteSpace?
Broccolini.Syntax.SectionIniNode.LeadingTrivia.init -> void
Broccolini.Syntax.SectionIniNode.Name.get -> string!
Broccolini.Syntax.SectionIniNode.Name.init -> void
Broccolini.Syntax.SectionIniNode.OpeningBracket.get -> Broccolini.Syntax.IniToken.OpeningBracket!
Broccolini.Syntax.SectionIniNode.OpeningBracket.init -> void
Broccolini.Syntax.SectionIniNode.SectionIniNode(string! name, System.Collections.Immutable.IImmutableList<Broccolini.Syntax.SectionChildIniNode!>! children) -> void
Broccolini.Syntax.SectionIniNode.TrailingTrivia.get -> System.Collections.Immutable.IImmutableList<Broccolini.Syntax.IniToken!>!
Broccolini.Syntax.SectionIniNode.TrailingTrivia.init -> void
Broccolini.Syntax.SectionIniNode.TriviaAfterOpeningBracket.get -> Broccolini.Syntax.IniToken.WhiteSpace?
Broccolini.Syntax.SectionIniNode.TriviaAfterOpeningBracket.init -> void
Broccolini.Syntax.SectionIniNode.TriviaBeforeClosingBracket.get -> Broccolini.Syntax.IniToken.WhiteSpace?
Broccolini.Syntax.SectionIniNode.TriviaBeforeClosingBracket.init -> void
Broccolini.Syntax.SectionIniNode.Equals(Broccolini.Syntax.SectionIniNode? other) -> bool
Broccolini.Syntax.UnrecognizedIniNode
Broccolini.Syntax.UnrecognizedIniNode.Equals(Broccolini.Syntax.UnrecognizedIniNode? other) -> bool
Broccolini.Syntax.UnrecognizedIniNode.Tokens.get -> System.Collections.Immutable.IImmutableList<Broccolini.Syntax.IniToken!>!
Expand Down
28 changes: 28 additions & 0 deletions Broccolini/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
@@ -1 +1,29 @@
#nullable enable
Broccolini.Syntax.IniNode.LeadingTrivia.get -> System.Collections.Immutable.IImmutableList<Broccolini.Syntax.IniToken!>!
Broccolini.Syntax.IniNode.LeadingTrivia.init -> void
Broccolini.Syntax.IniNode.TrailingTrivia.get -> System.Collections.Immutable.IImmutableList<Broccolini.Syntax.IniToken!>!
Broccolini.Syntax.IniNode.TrailingTrivia.init -> void
Broccolini.Syntax.IniSectionHeader
Broccolini.Syntax.IniSectionHeader.ClosingBracket.get -> Broccolini.Syntax.IniToken.ClosingBracket?
Broccolini.Syntax.IniSectionHeader.ClosingBracket.init -> void
Broccolini.Syntax.IniSectionHeader.Equals(Broccolini.Syntax.IniSectionHeader? other) -> bool
Broccolini.Syntax.IniSectionHeader.IniSectionHeader(string! name) -> void
Broccolini.Syntax.IniSectionHeader.LeadingTrivia.get -> Broccolini.Syntax.IniToken.WhiteSpace?
Broccolini.Syntax.IniSectionHeader.LeadingTrivia.init -> void
Broccolini.Syntax.IniSectionHeader.Name.get -> string!
Broccolini.Syntax.IniSectionHeader.Name.init -> void
Broccolini.Syntax.IniSectionHeader.OpeningBracket.get -> Broccolini.Syntax.IniToken.OpeningBracket!
Broccolini.Syntax.IniSectionHeader.OpeningBracket.init -> void
Broccolini.Syntax.IniSectionHeader.TrailingTrivia.get -> System.Collections.Immutable.IImmutableList<Broccolini.Syntax.IniToken!>!
Broccolini.Syntax.IniSectionHeader.TrailingTrivia.init -> void
Broccolini.Syntax.IniSectionHeader.TriviaAfterOpeningBracket.get -> Broccolini.Syntax.IniToken.WhiteSpace?
Broccolini.Syntax.IniSectionHeader.TriviaAfterOpeningBracket.init -> void
Broccolini.Syntax.IniSectionHeader.TriviaBeforeClosingBracket.get -> Broccolini.Syntax.IniToken.WhiteSpace?
Broccolini.Syntax.IniSectionHeader.TriviaBeforeClosingBracket.init -> void
Broccolini.Syntax.SectionIniNode.Header.get -> Broccolini.Syntax.IniSectionHeader!
Broccolini.Syntax.SectionIniNode.Header.init -> void
Broccolini.Syntax.SectionIniNode.SectionIniNode(Broccolini.Syntax.IniSectionHeader! header, System.Collections.Immutable.IImmutableList<Broccolini.Syntax.SectionChildIniNode!>! children) -> void
override Broccolini.Syntax.IniNode.GetHashCode() -> int
override Broccolini.Syntax.IniSectionHeader.GetHashCode() -> int
override Broccolini.Syntax.IniSectionHeader.ToString() -> string!
virtual Broccolini.Syntax.IniNode.Equals(Broccolini.Syntax.IniNode? other) -> bool
111 changes: 79 additions & 32 deletions Broccolini/Syntax/IniDocument.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,18 @@ private protected IniNode() { }
protected IniNode(IniNode original)
{
NewLine = original.NewLine;
LeadingTrivia = original.LeadingTrivia;
TrailingTrivia = original.TrailingTrivia;
}

/// <summary>Leading whitespace and empty lines.</summary>
[EditorBrowsable(EditorBrowsableState.Advanced)]
public IImmutableList<IniToken> LeadingTrivia { get; init; } = ImmutableArray<IniToken>.Empty;

/// <summary>Trailing whitespace and empty lines.</summary>
[EditorBrowsable(EditorBrowsableState.Advanced)]
public IImmutableList<IniToken> TrailingTrivia { get; init; } = ImmutableArray<IniToken>.Empty;

[EditorBrowsable(EditorBrowsableState.Advanced)]
public IniToken.NewLine? NewLine { get; init; }

Expand All @@ -70,6 +80,20 @@ public override string ToString()
return visitor.ToString();
}

public virtual bool Equals(IniNode? other)
=> other is not null
&& EqualityContract == other.EqualityContract
&& LeadingTrivia.SequenceEqual(other.LeadingTrivia)
&& TrailingTrivia.SequenceEqual(other.TrailingTrivia)
&& NewLine == other.NewLine;

public override int GetHashCode()
=> HashCode.Combine(
EqualityContract,
LeadingTrivia.Count,
TrailingTrivia.Count,
NewLine);

private protected abstract void InternalImplementorsOnly();
}

Expand Down Expand Up @@ -102,9 +126,6 @@ public KeyValueIniNode(string key, string value)

public string Value { get; init; }

/// <summary>Leading whitespace.</summary>
public IniToken.WhiteSpace? LeadingTrivia { get; init; }

/// <summary>Whitespace between key and equals sign.</summary>
public IniToken.WhiteSpace? TriviaBeforeEqualsSign { get; init; }

Expand All @@ -116,9 +137,6 @@ public KeyValueIniNode(string key, string value)
/// <summary>Opening and closing quote when value is quoted.</summary>
public IniToken.Quote? Quote { get; init; }

/// <summary>Whitespace after value.</summary>
public IniToken.WhiteSpace? TrailingTrivia { get; init; }

[EditorBrowsable(EditorBrowsableState.Advanced)]
public override void Accept(IIniNodeVisitor visitor) => visitor.Visit(this);

Expand Down Expand Up @@ -172,17 +190,11 @@ public CommentIniNode(string text)

public string Text { get; init; }

/// <summary>Leading whitespace.</summary>
public IniToken.WhiteSpace? LeadingTrivia { get; init; }

public IniToken.Semicolon Semicolon { get; init; } = new();

/// <summary>Whitespace between semicolon and text.</summary>
public IniToken.WhiteSpace? TriviaAfterSemicolon { get; init; }

/// <summary>Trailing whitespace.</summary>
public IniToken.WhiteSpace? TrailingTrivia { get; init; }

[EditorBrowsable(EditorBrowsableState.Advanced)]
public override void Accept(IIniNodeVisitor visitor) => visitor.Visit(this);

Expand All @@ -195,23 +207,65 @@ private protected override void InternalImplementorsOnly() { }
/// <code>[section]
/// key = value</code>
/// Use <see cref="IniSyntaxFactory.Section"/> to create this node.</summary>
[DebuggerDisplay("{OpeningBracket,nq}{Name,nq}{ClosingBracketDebugView,nq}")]
[DebuggerDisplay("{Header,nq}")]
public sealed record SectionIniNode : IniNode
{
/// <summary>Creates a section node without validating the parts.
/// Prefer <see cref="IniSyntaxFactory.Section"/> over this constructor.</summary>
[EditorBrowsable(EditorBrowsableState.Advanced)]
public SectionIniNode(string name, IImmutableList<SectionChildIniNode> children)
public SectionIniNode(IniSectionHeader header, IImmutableList<SectionChildIniNode> children)
{
Name = name;
Header = header;
Children = children;
}

public string Name { get; init; }
public string Name => Header.Name;

[EditorBrowsable(EditorBrowsableState.Advanced)]
public IniSectionHeader Header { get; init; }

[EditorBrowsable(EditorBrowsableState.Advanced)]
public IImmutableList<SectionChildIniNode> Children { get; init; }

// intentionally omitted from equality as it is only set for a short time during editing.
internal IniToken.NewLine? NewLineHint { get; init; }

[EditorBrowsable(EditorBrowsableState.Advanced)]
public override void Accept(IIniNodeVisitor visitor) => visitor.Visit(this);

public bool Equals(SectionIniNode? other)
=> other is not null
&& base.Equals(other)
&& Header == other.Header
&& Children.SequenceEqual(other.Children);

public override int GetHashCode()
=> HashCode.Combine(
base.GetHashCode(),
Header,
Children.Count);

public override string ToString() => base.ToString();

private protected override void InternalImplementorsOnly() { }
}

/// <summary>A section header:
/// <code>[section]</code></summary>
[DebuggerDisplay("{OpeningBracket,nq}{Name,nq}{ClosingBracketDebugView,nq}")]
public sealed record IniSectionHeader
{
/// <summary>Creates a section node without validating the parts.
/// Prefer <see cref="IniSyntaxFactory.Section"/> over this constructor.</summary>
[EditorBrowsable(EditorBrowsableState.Advanced)]
public IniSectionHeader(string name)
{
Name = name;
}

public string Name { get; init; }


/// <summary>Leading whitespace.</summary>
[EditorBrowsable(EditorBrowsableState.Advanced)]
public IniToken.WhiteSpace? LeadingTrivia { get; init; }
Expand All @@ -234,40 +288,33 @@ public SectionIniNode(string name, IImmutableList<SectionChildIniNode> children)
[EditorBrowsable(EditorBrowsableState.Advanced)]
public IImmutableList<IniToken> TrailingTrivia { get; init; } = ImmutableArray<IniToken>.Empty;

internal IniToken.NewLine? NewLineHint { get; init; }

[DebuggerBrowsable(DebuggerBrowsableState.Never)]
private string ClosingBracketDebugView => ClosingBracket?.ToString() ?? string.Empty;

[EditorBrowsable(EditorBrowsableState.Advanced)]
public override void Accept(IIniNodeVisitor visitor) => visitor.Visit(this);
public override string ToString()
{
var visitor = new ToStringVisitor();
visitor.Visit(this);
return visitor.ToString();
}

public bool Equals(SectionIniNode? other)
public bool Equals(IniSectionHeader? other)
=> other is not null
&& Name == other.Name
&& Children.SequenceEqual(other.Children)
&& LeadingTrivia == other.LeadingTrivia
&& OpeningBracket == other.OpeningBracket
&& TriviaAfterOpeningBracket == other.TriviaAfterOpeningBracket
&& TriviaBeforeClosingBracket == other.TriviaBeforeClosingBracket
&& ClosingBracket == other.ClosingBracket
&& TrailingTrivia.SequenceEqual(other.TrailingTrivia)
&& NewLine == other.NewLine;
&& TrailingTrivia.SequenceEqual(other.TrailingTrivia);

public override int GetHashCode()
=> HashCode.Combine(
Name,
Children.Count,
LeadingTrivia,
OpeningBracket,
TriviaAfterOpeningBracket,
TriviaBeforeClosingBracket,
ClosingBracket,
HashCode.Combine(
TrailingTrivia,
NewLine));

public override string ToString() => base.ToString();

private protected override void InternalImplementorsOnly() { }
TrailingTrivia);
}
2 changes: 1 addition & 1 deletion Broccolini/Syntax/IniSyntaxFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ public static KeyValueIniNode KeyValue(string key, string value)
public static SectionIniNode Section(string name)
{
ValidateSectionName(name);
return new SectionIniNode(name, ImmutableArray<SectionChildIniNode>.Empty);
return new SectionIniNode(new IniSectionHeader(name), ImmutableArray<SectionChildIniNode>.Empty);
}

/// <summary>Creates a whitespace token.</summary>
Expand Down
Loading