Skip to content

Commit 4c9cc1d

Browse files
Serg2DFXSerg2dfx
andauthored
Поддержка разбора списков с разделителями. (#5)
* Поддержка разбора списков с разделителями. * separated list fix * review fix in Parser.cs * make simple. with tests * build fix * del unused * review fix part1 * reademe fix * del space * ParseSeparatedList code join after tests --------- Co-authored-by: Serg2dfx <Serg2dfx@WORKER3>
1 parent 9a813c5 commit 4c9cc1d

File tree

14 files changed

+743
-32
lines changed

14 files changed

+743
-32
lines changed

ExtensibleParaser/Parser.cs

Lines changed: 105 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,19 +8,19 @@
88
namespace ExtensibleParaser;
99

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

23-
public int ErrorPos { get; private set; }
23+
public int ErrorPos { get; private set; }
2424
public FatalError ErrorInfo { get; private set; }
2525

2626
private int _recoverySkipPos = -1;
@@ -39,8 +39,8 @@ private void Log(string message, LogImportance importance = LogImportance.Normal
3939
public Dictionary<string, Rule[]> Rules { get; } = new();
4040
public Dictionary<string, TdoppRule> TdoppRules { get; } = new();
4141

42-
private readonly Dictionary<(int pos, string rule, int precedence), Result> _memo = new();
43-
42+
private readonly Dictionary<(int pos, string rule, int precedence), Result> _memo = new();
43+
4444
public void BuildTdoppRules()
4545
{
4646
var inlineableRules = TdoppRules
@@ -159,8 +159,8 @@ public Result Parse(string input, string startRule, out int triviaLength, int st
159159
var normalResult = ParseRule(startRule, minPrecedence: 0, startPos: currentStartPos, input);
160160

161161
if (normalResult.TryGetSuccess(out _, out var newPos) && newPos == input.Length)
162-
return normalResult;
163-
162+
return normalResult;
163+
164164
ErrorInfo = (input, ErrorPos, Location: input.PositionToLineCol(ErrorPos), _expected.ToArray());
165165

166166
if (ErrorPos <= oldErrorPos)
@@ -380,6 +380,7 @@ private Result ParseAlternative(
380380
OftenMissed o => ParseOftenMissed(o, startPos, input),
381381
AndPredicate a => ParseAndPredicate(a, startPos, input),
382382
NotPredicate n => ParseNotPredicate(n, startPos, input),
383+
SeparatedList sl => ParseSeparatedList(sl, startPos, input),
383384
_ => throw new IndexOutOfRangeException($"Unsupported rule type: {rule.GetType().Name}: {rule}")
384385
};
385386

@@ -627,5 +628,101 @@ private Result ParseSeq(
627628
}
628629

629630
return Result.Success(new SeqNode(seq.Kind ?? "Seq", elements, startPos, newPos), newPos, maxFailPos);
630-
}
631+
}
632+
633+
// пока сделал разделение что было наглядно на ревью, по идее нужно объединить после обсуждения норм или не норм.
634+
private Result ParseSeparatedList(SeparatedList listRule, int startPos, string input)
635+
{
636+
Log($"Parsing at {startPos} SeparatedList: {listRule}");
637+
638+
var elements = new List<ISyntaxNode>();
639+
var delimiters = new List<ISyntaxNode>();
640+
641+
int currentPos = startPos;
642+
643+
// Первый элемент
644+
var firstResult = ParseAlternative(listRule.Element, currentPos, input);
645+
646+
if (!firstResult.TryGetSuccess(out var firstNode, out var newPos))
647+
{
648+
if (listRule.CanBeEmpty)
649+
{
650+
// Обработка пустого списка
651+
return Result.Success(
652+
new ListNode(listRule.Kind, elements, delimiters, startPos, startPos),
653+
newPos: startPos,
654+
maxFailPos: startPos);
655+
}
656+
657+
Log($"SeparatedList: first element required at {currentPos}");
658+
return Result.Failure(firstResult.MaxFailPos);
659+
}
660+
661+
var maxFailPos = firstResult.MaxFailPos;
662+
elements.Add(firstNode);
663+
currentPos = newPos;
664+
665+
// Последующие элементы
666+
while (true)
667+
{
668+
// Парсинг разделителя
669+
var sepResult = ParseAlternative(listRule.Separator, currentPos, input);
670+
if (sepResult.MaxFailPos > maxFailPos)
671+
maxFailPos = sepResult.MaxFailPos;
672+
673+
if (!sepResult.TryGetSuccess(out var sepNode, out newPos))
674+
{
675+
if (listRule.EndBehavior == SeparatorEndBehavior.Required)
676+
{
677+
Log($"Missing separator at {currentPos}.");
678+
return Result.Failure(maxFailPos);
679+
}
680+
681+
break;
682+
}
683+
684+
// Добавляем разделитель
685+
delimiters.Add(sepNode);
686+
currentPos = newPos;
687+
688+
// Парсинг элемента после разделителя
689+
var elemResult = ParseAlternative(listRule.Element, currentPos, input);
690+
if (elemResult.MaxFailPos > maxFailPos)
691+
maxFailPos = elemResult.MaxFailPos;
692+
693+
if (!elemResult.TryGetSuccess(out var elemNode, out newPos))
694+
{
695+
if (listRule.EndBehavior == SeparatorEndBehavior.Forbidden)
696+
{
697+
Log($"End sepearator should not be present {currentPos}.");
698+
return Result.Failure(maxFailPos);
699+
}
700+
701+
break;
702+
}
703+
704+
elements.Add(elemNode);
705+
currentPos = newPos;
706+
}
707+
708+
if (listRule.EndBehavior == SeparatorEndBehavior.Forbidden)
709+
{
710+
// Парсинг разделителя
711+
var sepResult = ParseAlternative(listRule.Separator, currentPos, input);
712+
if (sepResult.MaxFailPos > maxFailPos)
713+
maxFailPos = sepResult.MaxFailPos;
714+
715+
if (sepResult.TryGetSuccess(out var sepNode, out newPos))
716+
{
717+
Log($"End sepearator should not be present {currentPos}.");
718+
return Result.Failure(maxFailPos);
719+
}
720+
}
721+
722+
return Result.Success(
723+
new ListNode(listRule.Kind, elements, delimiters, startPos, currentPos),
724+
newPos: currentPos,
725+
maxFailPos: maxFailPos
726+
);
727+
}
631728
}

ExtensibleParaser/Rules.cs

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -200,11 +200,56 @@ public override IEnumerable<Rule> GetSubRules<T>()
200200
}
201201
}
202202

203+
public record SeparatedList(
204+
Rule Element,
205+
Rule Separator,
206+
string Kind,
207+
SeparatorEndBehavior EndBehavior = SeparatorEndBehavior.Optional,
208+
bool CanBeEmpty = true)
209+
: Rule(Kind)
210+
{
211+
public override string ToString() => $"({Element}; {Separator} {EndBehavior})*{(CanBeEmpty ? "" : "+")}";
212+
213+
public override Rule InlineReferences(Dictionary<string, Rule> inlineableRules)
214+
{
215+
var inlinedElement = Element.InlineReferences(inlineableRules);
216+
var inlinedSeparator = Separator.InlineReferences(inlineableRules);
217+
return new SeparatedList(inlinedElement, inlinedSeparator, Kind, EndBehavior, CanBeEmpty);
218+
}
219+
220+
public override IEnumerable<Rule> GetSubRules<T>()
221+
{
222+
if (this is T)
223+
yield return this;
224+
foreach (var subRule in Element.GetSubRules<T>())
225+
yield return subRule;
226+
227+
// Мб. не требуется.
228+
foreach (var subRule in Separator.GetSubRules<T>())
229+
yield return subRule;
230+
}
231+
}
232+
233+
public enum SeparatorEndBehavior
234+
{
235+
/// <summary>
236+
/// опциональный - конечный разделитель может быть, а может не быть
237+
/// </summary>
238+
Optional,
239+
/// <summary>
240+
/// обязательный - разделитель должен быть в конце обязательно
241+
/// </summary>
242+
Required,
243+
/// <summary>
244+
/// запрещён - разделитель должен в конце обязательно отсутствовать
245+
/// </summary>
246+
Forbidden
247+
}
248+
203249
public abstract record RecoveryTerminal(string Kind) : Terminal(Kind);
204250

205251
public record EmptyTerminal(string Kind) : RecoveryTerminal(Kind)
206252
{
207253
public override int TryMatch(string input, int position) => 0;
208254
public override string ToString() => Kind;
209255
}
210-

ExtensibleParaser/SyntaxTree.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ public interface ISyntaxVisitor
1818
{
1919
void Visit(TerminalNode node);
2020
void Visit(SeqNode node);
21+
void Visit(ListNode node);
2122
void Visit(SomeNode node);
2223
void Visit(NoneNode node);
2324
}
@@ -76,6 +77,15 @@ public record SeqNode(string Kind, IReadOnlyList<ISyntaxNode> Elements, int Star
7677
public override void Accept(ISyntaxVisitor visitor) => visitor.Visit(this);
7778
}
7879

80+
public record ListNode(string Kind, IReadOnlyList<ISyntaxNode> Elements, IReadOnlyList<ISyntaxNode> Delimiters, int StartPos, int EndPos, bool HasTrailingSeparator = false, bool IsRecovery = false)
81+
: Node(Kind, StartPos, EndPos, IsRecovery)
82+
{
83+
public override void Accept(ISyntaxVisitor visitor) => visitor.Visit(this);
84+
85+
public override string ToString(string input) =>
86+
$"[{string.Join(", ", Elements.Select(e => e.ToString(input)))}]";
87+
}
88+
7989
public abstract record OptionalNode(string Kind, int StartPos, int EndPos) : Node(Kind, StartPos, EndPos);
8090

8191
public record SomeNode(string Kind, ISyntaxNode Value, int StartPos, int EndPos)

Nitra.sln

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ EndProject
1010
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{02EA681E-C7D8-13C7-8484-4AC65E1B71E8}"
1111
ProjectSection(SolutionItems) = preProject
1212
.editorconfig = .editorconfig
13+
global.json = global.json
1314
EndProjectSection
1415
EndProject
1516
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TerminalGenerator", "TerminalGenerator\TerminalGenerator.csproj", "{688DD31C-5C27-4FB9-851C-21DE6A83E624}"

Parsers/DotParser/DotVisitor.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,11 @@ public void Visit(NoneNode _)
123123
CurrentResult = null;
124124
}
125125

126+
public void Visit(ListNode node)
127+
{
128+
throw new NotImplementedException("It is skipped in this language.");
129+
}
130+
126131
private record DotStatementList(IReadOnlyList<DotStatement> Statements) : DotAst
127132
{
128133
public override string ToString() => string.Join("\n", Statements);

Tests/ParaserTests/Calc/CalcTest.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,11 @@ public void Visit(SeqNode node)
4747
};
4848
}
4949

50+
public void Visit(ListNode node)
51+
{
52+
throw new NotImplementedException("It is skipped in this language..");
53+
}
54+
5055
public void Visit(SomeNode node) => node.Value.Accept(this);
5156
public void Visit(NoneNode node) => Result = null;
5257
}

Tests/ParaserTests/MiniC/Ast.cs

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,16 @@ public abstract record Ast
99
public abstract override string ToString();
1010
}
1111

12-
public record VarDecl(Token Type, Identifier Name) : Ast
12+
public record VarDecl(Token Type, Identifier Name) : Decl
1313
{
1414
public override string ToString() => $"VarDecl: {Name}";
1515
}
1616

17+
public record ArrayDecl(Token Type, Identifier Name, IReadOnlyList<Number> Parameters) : Decl
18+
{
19+
public override string ToString() => $"ArrayDecl: {Type}[] {Name} = {{{string.Join(",", Parameters)}}}";
20+
}
21+
1722
public record Number(int Value) : Expr
1823
{
1924
public override string ToString() => Value.ToString();
@@ -54,7 +59,7 @@ public record ExprStmt(Expr Expr) : Ast
5459
public override string ToString() => $"ExprStmt: {Expr}";
5560
}
5661

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

68-
public record Args(IReadOnlyList<Expr> Arguments) : Ast
73+
public record Args(IReadOnlyList<Expr> Arguments) : AstListItems
6974
{
7075
public override string ToString() => string.Join(", ", Arguments);
7176
}
7277

73-
public record Params(IReadOnlyList<Identifier> Parameters) : Ast
78+
public record Params(IReadOnlyList<Identifier> Parameters) : AstListItems
7479
{
7580
public override string ToString() => $"Params: ({string.Join(", ", Parameters)})";
7681
}
7782

83+
public record ArrayDeclItems(IReadOnlyList<Number> Numbers) : AstListItems
84+
{
85+
public override string ToString() => string.Join(", ", Numbers);
86+
}
87+
7888
public record Block(IReadOnlyList<Ast> Statements, bool HasBraces = false) : Ast
7989
{
8090
public override string ToString()
@@ -100,4 +110,8 @@ public record ReturnStmt(Expr Value) : Ast
100110
public override string ToString() => $"Return({Value})";
101111
}
102112

113+
public abstract record AstListItems : Ast;
114+
115+
public abstract record Decl : Ast;
116+
103117
public abstract record Expr : Ast;

0 commit comments

Comments
 (0)