/**************************************************************************** ** ** 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; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Linq.Expressions; using System.Reflection; using System.Text; namespace QtVsTools.SyntaxAnalysis { public abstract partial class RegExpr { public partial class Parser { //////////////////////////////////////////////////////////////////////////////////////// /// /// RegExpr.Parser.GetProductionObjects() /// //////////////////////////////////////////////////////////////////////////////////////// /// /// Extract productions from a parse tree. /// /// /// Root node of parse tree, obtained from parsing an input text with a RegExpr /// /// Productions by token id ProductionObjects GetProductionObjects(ParseTree parseTree) { var outputProductions = new ProductionObjects(); var stack = new Stack(); stack.Push(parseTree.Root); while (stack.Any()) { var node = stack.Pop(); // Depth-first traversal if (node.TokenStream == null) { node.TokenStream = new Queue(node.ChildNodes.Values); node.OperandStack = new Stack(); node.OperatorStack = new Stack(); stack.Push(node); continue; } else if (node.TokenStream.Any()) { var nextNode = node.TokenStream.Dequeue(); stack.Push(node); stack.Push(nextNode); continue; } if (node.Parent == null) continue; var operatorStack = node.Parent.OperatorStack; var operandStack = node.Parent.OperandStack; var rule = node.Rule; if (rule == null) { // Default token (without rule definitions) // just use captured value as production node.Production = node.Value; operandStack.Push(node); } else if (rule.Delimiters != Delimiter.None) { // Delimiter token if (rule.Delimiters == Delimiter.Left) { // if left delim, push to operator stack operatorStack.Push(node); } else { // if right delim, unwind operator stack until left delim UnwindOperatorStack(HaltUnwind.AtLeftDelimiter, operatorStack, operandStack); // set left delim as left operand, delimited expr as right operand operandStack.ReverseTop(); // execute delimiter rule node.Production = rule.Execute(node); operandStack.Push(node); } } else if (rule.Operands != Operand.None) { // Operator token // unwind operator stack until lower priority operator or empty UnwindOperatorStack(HaltUnwind.AtLowerPriority, operatorStack, operandStack, rule.Priority); // if operator needs left operand but none is available, error out if (rule.Operands.HasFlag(Operand.Left) && !operandStack.Any()) throw new ParseErrorException(); if (rule.Operands.HasFlag(Operand.Right)) { // if needs right operand, push to operator stack operatorStack.Push(node); } else { // if left operand only, execute rule immediately node.Production = rule.Execute(node); operandStack.Push(node); } } else { // Captured value or embedded captures ("nullary operator") // execute rule immediately node.Production = rule.Execute(node); operandStack.Push(node); } if (node.IsLast) { // Last token // unwind operator stack until empty UnwindOperatorStack(HaltUnwind.WhenEmpty, operatorStack, operandStack); // get output from operand stack foreach (var operand in operandStack.Reverse()) { // check if it's a dangling left delimiter if (operand.Rule != null && operand.Rule.Delimiters == Delimiter.Left) throw new ParseErrorException(); // add production to parent context node.Parent.ChildProductions.Add(operand.TokenId, operand.Production); // add production to output list outputProductions.Add(operand.TokenId, operand.Production); } operandStack.Clear(); } } return outputProductions; } enum HaltUnwind { WhenEmpty, AtLeftDelimiter, AtLowerPriority } void UnwindOperatorStack( HaltUnwind haltingCondition, Stack operatorStack, Stack operandStack, int priority = int.MinValue) { while (operatorStack.Any()) { var node = operatorStack.Pop(); Debug.Assert(node != null); var rule = node.Rule; Debug.Assert(rule != null); if (haltingCondition == HaltUnwind.AtLeftDelimiter && rule.Delimiters == Delimiter.Left ) { // Halting stack unwind: left delimiter found // check if an operand (i.e. delimited expression) is available if (!operandStack.Any()) throw new ParseErrorException(); // execute left delimiter rule node.Production = rule.Execute(node); // add to operands (will be picked up by right delimiter rule) operandStack.Push(node); return; } if (haltingCondition == HaltUnwind.AtLowerPriority && rule.Priority < priority ) { // Halting stack unwind: lower priority operator found // push operator back into stack operatorStack.Push(node); return; } // still haven't found what we're looking for; continue stack unwind node.Production = rule.Execute(node); operandStack.Push(node); } // error-out if didn't find left delimiter if (haltingCondition == HaltUnwind.AtLeftDelimiter) throw new ParseErrorException(); } } /// /// Collection of production objects, grouped by token ID /// public partial class ProductionObjects : IEnumerable> { List> Productions { get; set; } Dictionary> ProductionsByTokenId { get; set; } public ProductionObjects() { Productions = new List>(); ProductionsByTokenId = new Dictionary>(); } public void Add(string tokenId, object prodObj) { Productions.Add(new KeyValuePair(tokenId, prodObj)); List prodObjs; if (!ProductionsByTokenId.TryGetValue(tokenId, out prodObjs)) ProductionsByTokenId.Add(tokenId, prodObjs = new List()); prodObjs.Add(prodObj); } public IEnumerable GetValues(string tokenId) { if (string.IsNullOrEmpty(tokenId)) return Empty(); List tokenProds; if (!ProductionsByTokenId.TryGetValue(tokenId, out tokenProds)) return Empty(); return tokenProds .Where(x => x != null && x is T) .Select(x => (T)x); } public IEnumerable GetValues(Enum tokenId) { return GetValues(tokenId.ToString()); } public IEnumerable GetValues(Token token) { return GetValues(token.Id); } public IEnumerable GetValues(ProductionRule production) { return GetValues(production.Token.Id); } public IEnumerable GetValues(string tokenId) { return GetValues(tokenId); } public IEnumerable GetValues(Enum tokenId) { return GetValues(tokenId.ToString()); } public IEnumerable GetValues(Token token) { return GetValues(token.Id); } public IEnumerable GetValues(IProductionRule production) { return GetValues(production.Token.Id); } public object this[string tokenId, int index = 0] { get { return GetValues(tokenId).ElementAtOrDefault(index); } } public object this[Enum tokenId, int index = 0] { get { return this[tokenId.ToString(), index]; } } public IEnumerator> GetEnumerator() { return ((IEnumerable>)Productions).GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable>)Productions).GetEnumerator(); } } } }