/****************************************************************************
**
** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt VS Tools.
**
** $QT_BEGIN_LICENSE:GPL-EXCEPT$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text.RegularExpressions;
namespace QtVsTools.SyntaxAnalysis
{
public abstract partial class RegExpr
{
////////////////////////////////////////////////////////////////////////////////////////////
///
/// RegExpr.Parser
///
////////////////////////////////////////////////////////////////////////////////////////////
///
/// Rendering of
///
public partial class Parser
{
Renderer Renderer { get; set; }
Pattern Pattern { get; set; }
public Regex Regex { get; private set; }
internal Parser(RegExpr expr, RegExpr defaultTokenWs = null)
{
Renderer = new Renderer();
Refresh(expr, defaultTokenWs);
}
///
/// Parse input text and return productions.
///
///
/// The parsing procedure will first calculate the parse tree corresponding to the input
/// text, given the token data captured. The parse tree is then used to generate all
/// productions, according to the production rules defined for each token.
/// (see also )
///
/// Text to be parsed.
/// Productions by token id
public ProductionObjects Parse(string text)
{
var parseTree = GetParseTree(text);
return GetProductionObjects(parseTree);
}
public void Refresh(RegExpr expr, RegExpr defaultTokenWs = null)
{
// Render Regex string
Pattern = Renderer.RenderPattern(expr, defaultTokenWs);
// Compile Regex
Regex = new Regex(Pattern.ExprRender, RegexOptions.Multiline);
}
///
/// Parse input text using Regex and generate corresponding parse tree.
///
/// Text to be parsed
/// Parse tree
ParseTree GetParseTree(string text)
{
// Match regex pattern
var match = Regex.Match(text);
if (!match.Success || match.Length == 0)
throw new ParseErrorException();
// Flat list of parse-tree nodes, from Regex captures
var nodes = match.Groups.Cast()
.SelectMany((group, groupIdx) => group.Captures.Cast()
.Where(capture => !string.IsNullOrEmpty(capture.Value))
.Select((capture, captureIdx) => new ParseTree.Node
{
CaptureId = Regex.GroupNameFromNumber(groupIdx),
Token = Pattern.Tokens[Regex.GroupNameFromNumber(groupIdx)],
Value = capture.Value,
Begin = capture.Index,
End = capture.Index + capture.Length,
GroupIdx = groupIdx,
CaptureIdx = captureIdx,
}))
.OrderBy(c => c.Begin)
.ToList();
// Node list partitioned by token
var nodesByToken = nodes
.GroupBy(node => node.Token)
.ToDictionary(g => g.Key, g => g.ToArray());
foreach (var node in nodes.Where(n => n.Token != Pattern.Root)) {
// Get nodes captured by parent token
Token parentToken;
if (!node.Token.Parents.TryGetValue(node.CaptureId, out parentToken))
throw new ParseErrorException("Unknown capture ID");
ParseTree.Node[] parentNodes;
if (!nodesByToken.TryGetValue(parentToken, out parentNodes))
throw new ParseErrorException("Missing parent nodes");
// Find parent node
int idx = Array.BinarySearch(parentNodes, node, ParseTree.Node.Comparer);
if (idx < 0) {
idx = (~idx) - 1;
if (idx < 0)
throw new ParseErrorException("Parent node not found");
}
// Attach to parent node
(node.Parent = parentNodes[idx]).ChildNodes.Add(node.Begin, node);
}
// Return parse tree root
return nodesByToken[Pattern.Root].FirstOrDefault();
}
}
public class ParseErrorException : RegExprException
{
public ParseErrorException(string message = null) : base(message) { }
}
}
}