aboutsummaryrefslogtreecommitdiffstats
path: root/QtVsTools.RegExpr/expression/Renderer.cs
blob: 51604f3050b67e7bb997706e69f87c4e35a2b4d9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
/***************************************************************************************************
 Copyright (C) 2024 The Qt Company Ltd.
 SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
***************************************************************************************************/

using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace QtVsTools.SyntaxAnalysis
{
    public abstract partial class RegExpr
    {
        public class Pattern
        {
            public RegExpr Expr { get; set; }
            public string ExprRender { get; set; }
            public Dictionary<string, Token> Tokens { get; set; }
            public Token Root { get; set; }
        }

        class Renderer
        {
            ////////////////////////////////////////////////////////////////////////////////////////
            ///
            /// RegExpr.Renderer.RenderPattern()
            ///
            ////////////////////////////////////////////////////////////////////////////////////////
            /// <summary>
            /// Transform the RegExpr representation of a regular expression into a pattern string
            /// and a mapping of capture group id's into corresponding token definitions.
            /// </summary>
            /// <param name="rootExpr">RegExpr to render</param>
            /// <param name="wsExpr">Default token white-space</param>
            /// <returns>Pattern object containing pattern string and token map</returns>
            public Pattern RenderPattern(RegExpr rootExpr, RegExpr wsExpr)
            {
                var pattern = new StringBuilder();
                var rootToken = Token.CreateRoot();
                var tokenStack = new Stack<Token>();
                tokenStack.Push(rootToken);
                var tokens = new HashSet<Token>();

                var stack = new Stack<StackFrame>();
                var mode = RenderMode.Default;

                stack.Push(rootExpr);
                while (stack.Any()) {
                    var context = stack.Pop();

                    if (context.Expr == null)
                        continue;

                    var expr = context.Expr;
                    IEnumerable<RegExpr> children = context.Children;
                    RegExpr parent = stack.Any() ? stack.Peek() : null;

                    if (expr is Token token)
                        tokens.Add(token);

                    if (children == null) {
                        children = expr.OnRender(wsExpr, parent, pattern, ref mode, tokenStack);
                        if (children != null && children.Any()) {
                            stack.Push(new StackFrame { Expr = expr, Children = children.Skip(1) });
                            stack.Push(children.First());
                        }
                    } else if (children.Any()) {
                        expr.OnRenderNext(wsExpr, parent, pattern, ref mode, tokenStack);
                        stack.Push(new StackFrame { Expr = expr, Children = children.Skip(1) });
                        stack.Push(children.First());
                    } else {
                        expr.OnRenderEnd(wsExpr, parent, pattern, ref mode, tokenStack);
                    }
                }

                var tokensByCaptureId = tokens
                    .SelectMany(token => token.CaptureIds
                        .Select(captureId => new { Id = captureId, Token = token }))
                    .ToDictionary(idToken => idToken.Id, idToken => idToken.Token);
                tokensByCaptureId.Add(ParseTree.KeyRoot, rootToken);

                return new Pattern
                {
                    Expr = rootExpr,
                    ExprRender = pattern.ToString(),
                    Tokens = tokensByCaptureId,
                    Root = rootToken
                };
            }

            class StackFrame
            {
                public RegExpr Expr { get; set; }
                public IEnumerable<RegExpr> Children { get; set; }

                public static implicit operator StackFrame(RegExpr expr)
                {
                    return new StackFrame { Expr = expr };
                }

                public static implicit operator RegExpr(StackFrame frame)
                {
                    return frame?.Expr;
                }
            }
        }
    }
}