Skip to content
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
113 changes: 105 additions & 8 deletions ExtensibleParaser/Parser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,19 @@
namespace ExtensibleParaser;

public class Parser(Terminal trivia, Log? log = null)
{
{
#pragma warning disable IDE0079 // Remove unnecessary suppression
#pragma warning disable CA2211 // Non-constant fields should not be visible
/// <summary>
/// Используется только для отладки. Позволяет отображать разобранный код в наследниках Node не храня в нем входной строки.
/// </summary>
[ThreadStatic]
[ThreadStatic]
[Obsolete("This field should be used for debugging purposes only. Do not use it in the visitor parser itself.")]
public static string? Input;
#pragma warning restore CA2211 // Non-constant fields should not be visible
#pragma warning restore IDE0079 // Remove unnecessary suppression

public int ErrorPos { get; private set; }
public int ErrorPos { get; private set; }
public FatalError ErrorInfo { get; private set; }

private int _recoverySkipPos = -1;
Expand All @@ -39,8 +39,8 @@
public Dictionary<string, Rule[]> Rules { get; } = new();
public Dictionary<string, TdoppRule> TdoppRules { get; } = new();

private readonly Dictionary<(int pos, string rule, int precedence), Result> _memo = new();
private readonly Dictionary<(int pos, string rule, int precedence), Result> _memo = new();

public void BuildTdoppRules()
{
var inlineableRules = TdoppRules
Expand Down Expand Up @@ -159,8 +159,8 @@
var normalResult = ParseRule(startRule, minPrecedence: 0, startPos: currentStartPos, input);

if (normalResult.TryGetSuccess(out _, out var newPos) && newPos == input.Length)
return normalResult;
return normalResult;

ErrorInfo = (input, ErrorPos, Location: input.PositionToLineCol(ErrorPos), _expected.ToArray());

if (ErrorPos <= oldErrorPos)
Expand Down Expand Up @@ -380,6 +380,7 @@
OftenMissed o => ParseOftenMissed(o, startPos, input),
AndPredicate a => ParseAndPredicate(a, startPos, input),
NotPredicate n => ParseNotPredicate(n, startPos, input),
SeparatedList sl => ParseSeparatedList(sl, startPos, input),
_ => throw new IndexOutOfRangeException($"Unsupported rule type: {rule.GetType().Name}: {rule}")
};

Expand Down Expand Up @@ -528,7 +529,7 @@
maxFailPos: currentPos // ???
);

Result panicRecovery(Terminal terminal, int startPos, string input)

Check warning on line 532 in ExtensibleParaser/Parser.cs

View workflow job for this annotation

GitHub Actions / build-and-test-windows-latest

The local function 'panicRecovery' is declared but never used

Check warning on line 532 in ExtensibleParaser/Parser.cs

View workflow job for this annotation

GitHub Actions / build-and-test-ubuntu-latest

The local function 'panicRecovery' is declared but never used

Check warning on line 532 in ExtensibleParaser/Parser.cs

View workflow job for this annotation

GitHub Actions / build-and-test-macOS-latest

The local function 'panicRecovery' is declared but never used
{
Log($"Starting recovery for terminal {terminal.Kind} at position {startPos}");
var currentRule = _ruleStack.Peek();
Expand Down Expand Up @@ -627,5 +628,101 @@
}

return Result.Success(new SeqNode(seq.Kind ?? "Seq", elements, startPos, newPos), newPos, maxFailPos);
}
}

// пока сделал разделение что было наглядно на ревью, по идее нужно объединить после обсуждения норм или не норм.
private Result ParseSeparatedList(SeparatedList listRule, int startPos, string input)
{
Log($"Parsing at {startPos} SeparatedList: {listRule}");

var elements = new List<ISyntaxNode>();
var delimiters = new List<ISyntaxNode>();

int currentPos = startPos;

// Первый элемент
var firstResult = ParseAlternative(listRule.Element, currentPos, input);

if (!firstResult.TryGetSuccess(out var firstNode, out var newPos))
{
if (listRule.CanBeEmpty)
{
// Обработка пустого списка
return Result.Success(
new ListNode(listRule.Kind, elements, delimiters, startPos, startPos),
newPos: startPos,
maxFailPos: startPos);
}

Log($"SeparatedList: first element required at {currentPos}");
return Result.Failure(firstResult.MaxFailPos);
}

var maxFailPos = firstResult.MaxFailPos;
elements.Add(firstNode);
currentPos = newPos;

// Последующие элементы
while (true)
{
// Парсинг разделителя
var sepResult = ParseAlternative(listRule.Separator, currentPos, input);
if (sepResult.MaxFailPos > maxFailPos)
maxFailPos = sepResult.MaxFailPos;

if (!sepResult.TryGetSuccess(out var sepNode, out newPos))
{
if (listRule.EndBehavior == SeparatorEndBehavior.Required)
{
Log($"Missing separator at {currentPos}.");
return Result.Failure(maxFailPos);
}

break;
}

// Добавляем разделитель
delimiters.Add(sepNode);
currentPos = newPos;

// Парсинг элемента после разделителя
var elemResult = ParseAlternative(listRule.Element, currentPos, input);
if (elemResult.MaxFailPos > maxFailPos)
maxFailPos = elemResult.MaxFailPos;

if (!elemResult.TryGetSuccess(out var elemNode, out newPos))
{
if (listRule.EndBehavior == SeparatorEndBehavior.Forbidden)
{
Log($"End sepearator should not be present {currentPos}.");
return Result.Failure(maxFailPos);
}

break;
}

elements.Add(elemNode);
currentPos = newPos;
}

if (listRule.EndBehavior == SeparatorEndBehavior.Forbidden)
{
// Парсинг разделителя
var sepResult = ParseAlternative(listRule.Separator, currentPos, input);
if (sepResult.MaxFailPos > maxFailPos)
maxFailPos = sepResult.MaxFailPos;

if (sepResult.TryGetSuccess(out var sepNode, out newPos))
{
Log($"End sepearator should not be present {currentPos}.");
return Result.Failure(maxFailPos);
}
}

return Result.Success(
new ListNode(listRule.Kind, elements, delimiters, startPos, currentPos),
newPos: currentPos,
maxFailPos: maxFailPos
);
}
}
47 changes: 46 additions & 1 deletion ExtensibleParaser/Rules.cs
Original file line number Diff line number Diff line change
Expand Up @@ -200,11 +200,56 @@ public override IEnumerable<Rule> GetSubRules<T>()
}
}

public record SeparatedList(
Rule Element,
Rule Separator,
string Kind,
SeparatorEndBehavior EndBehavior = SeparatorEndBehavior.Optional,
bool CanBeEmpty = true)
: Rule(Kind)
{
public override string ToString() => $"({Element}; {Separator} {EndBehavior})*{(CanBeEmpty ? "" : "+")}";

public override Rule InlineReferences(Dictionary<string, Rule> inlineableRules)
{
var inlinedElement = Element.InlineReferences(inlineableRules);
var inlinedSeparator = Separator.InlineReferences(inlineableRules);
return new SeparatedList(inlinedElement, inlinedSeparator, Kind, EndBehavior, CanBeEmpty);
}

public override IEnumerable<Rule> GetSubRules<T>()
{
if (this is T)
yield return this;
foreach (var subRule in Element.GetSubRules<T>())
yield return subRule;

// Мб. не требуется.
foreach (var subRule in Separator.GetSubRules<T>())
yield return subRule;
}
}

public enum SeparatorEndBehavior
{
/// <summary>
/// опциональный - конечный разделитель может быть, а может не быть
/// </summary>
Optional,
/// <summary>
/// обязательный - разделитель должен быть в конце обязательно
/// </summary>
Required,
/// <summary>
/// запрещён - разделитель должен в конце обязательно отсутствовать
/// </summary>
Forbidden
}

public abstract record RecoveryTerminal(string Kind) : Terminal(Kind);

public record EmptyTerminal(string Kind) : RecoveryTerminal(Kind)
{
public override int TryMatch(string input, int position) => 0;
public override string ToString() => Kind;
}

10 changes: 10 additions & 0 deletions ExtensibleParaser/SyntaxTree.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ public interface ISyntaxVisitor
{
void Visit(TerminalNode node);
void Visit(SeqNode node);
void Visit(ListNode node);
void Visit(SomeNode node);
void Visit(NoneNode node);
}
Expand Down Expand Up @@ -76,6 +77,15 @@ public record SeqNode(string Kind, IReadOnlyList<ISyntaxNode> Elements, int Star
public override void Accept(ISyntaxVisitor visitor) => visitor.Visit(this);
}

public record ListNode(string Kind, IReadOnlyList<ISyntaxNode> Elements, IReadOnlyList<ISyntaxNode> Delimiters, int StartPos, int EndPos, bool HasTrailingSeparator = false, bool IsRecovery = false)
: Node(Kind, StartPos, EndPos, IsRecovery)
{
public override void Accept(ISyntaxVisitor visitor) => visitor.Visit(this);

public override string ToString(string input) =>
$"[{string.Join(", ", Elements.Select(e => e.ToString(input)))}]";
}

public abstract record OptionalNode(string Kind, int StartPos, int EndPos) : Node(Kind, StartPos, EndPos);

public record SomeNode(string Kind, ISyntaxNode Value, int StartPos, int EndPos)
Expand Down
1 change: 1 addition & 0 deletions Nitra.sln
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{02EA681E-C7D8-13C7-8484-4AC65E1B71E8}"
ProjectSection(SolutionItems) = preProject
.editorconfig = .editorconfig
global.json = global.json
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TerminalGenerator", "TerminalGenerator\TerminalGenerator.csproj", "{688DD31C-5C27-4FB9-851C-21DE6A83E624}"
Expand Down
5 changes: 5 additions & 0 deletions Parsers/DotParser/DotVisitor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,11 @@ public void Visit(NoneNode _)
CurrentResult = null;
}

public void Visit(ListNode node)
{
throw new NotImplementedException("It is skipped in this language.");
}

private record DotStatementList(IReadOnlyList<DotStatement> Statements) : DotAst
{
public override string ToString() => string.Join("\n", Statements);
Expand Down
5 changes: 5 additions & 0 deletions Tests/ParaserTests/Calc/CalcTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@ public void Visit(SeqNode node)
};
}

public void Visit(ListNode node)
{
throw new NotImplementedException("It is skipped in this language..");
}

public void Visit(SomeNode node) => node.Value.Accept(this);
public void Visit(NoneNode node) => Result = null;
}
Expand Down
22 changes: 18 additions & 4 deletions Tests/ParaserTests/MiniC/Ast.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,16 @@ public abstract record Ast
public abstract override string ToString();
}

public record VarDecl(Token Type, Identifier Name) : Ast
public record VarDecl(Token Type, Identifier Name) : Decl
{
public override string ToString() => $"VarDecl: {Name}";
}

public record ArrayDecl(Token Type, Identifier Name, IReadOnlyList<Number> Parameters) : Decl
{
public override string ToString() => $"ArrayDecl: {Type}[] {Name} = {{{string.Join(",", Parameters)}}}";
}

public record Number(int Value) : Expr
{
public override string ToString() => Value.ToString();
Expand Down Expand Up @@ -54,7 +59,7 @@ public record ExprStmt(Expr Expr) : Ast
public override string ToString() => $"ExprStmt: {Expr}";
}

public record FunctionDecl(Identifier Name, IReadOnlyList<Identifier> Parameters, Block Body) : Ast
public record FunctionDecl(Identifier Name, IReadOnlyList<Identifier> Parameters, Block Body) : Decl
{
public override string ToString() =>
$"FunctionDecl: {Name}({string.Join(", ", Parameters)}) {Body}";
Expand All @@ -65,16 +70,21 @@ public record CallExpr(Identifier Name, Args Arguments) : Expr
public override string ToString() => $"Call: {Name}({Arguments})";
}

public record Args(IReadOnlyList<Expr> Arguments) : Ast
public record Args(IReadOnlyList<Expr> Arguments) : AstListItems
{
public override string ToString() => string.Join(", ", Arguments);
}

public record Params(IReadOnlyList<Identifier> Parameters) : Ast
public record Params(IReadOnlyList<Identifier> Parameters) : AstListItems
{
public override string ToString() => $"Params: ({string.Join(", ", Parameters)})";
}

public record ArrayDeclItems(IReadOnlyList<Number> Numbers) : AstListItems
{
public override string ToString() => string.Join(", ", Numbers);
}

public record Block(IReadOnlyList<Ast> Statements, bool HasBraces = false) : Ast
{
public override string ToString()
Expand All @@ -100,4 +110,8 @@ public record ReturnStmt(Expr Value) : Ast
public override string ToString() => $"Return({Value})";
}

public abstract record AstListItems : Ast;

public abstract record Decl : Ast;

public abstract record Expr : Ast;
Loading