/**************************************************************************** ** ** 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.Linq; using QtVsTools.SyntaxAnalysis; namespace QtVsTest.Macros { using static RegExpr; class MacroLines : IEnumerable { readonly List Lines = new List(); public void Add(MacroLine line) { Lines.Add(line); } public IEnumerator GetEnumerator() { return Lines.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return Lines.GetEnumerator(); } } public class MacroLine { } public enum StatementType { Unknown, // Define reusable macro //# macro Macro, // Switch to thread //# thread Thread, // Add reference to assembly and (opt.) namespaces in that assembly //# ref [namespace] [namespace] ... Reference, Ref = Reference, // Add reference to namespace //# using Using, // Declare global variable, shared by called/calling macros //# var [ => ] Var, // Get Visual Studio SDK service and assign it to local variable //# service [service type] Service, // Call macro //# call Call, // Wait until expression evaluation returns non-default value // Optionally assign evaluated value to variable //# wait [timeout] [ ] => Wait, // UI automation command // // Set context based on UI element name path //# ui context [ VS ] => _string_ [, _string_, ... ] // // Set context based on window handle //# ui context HWND => _int_ // // Get reference to UI element pattern. By default, the current context is used as source. // A name path relative to the current context allows using a child element as source. //# ui pattern <_TypeName_> <_VarName_> [ => _string_ [, _string_, ... ] ] // // Get reference to UI element Invoke pattern and immediately call the Invoke() method. //# ui pattern Invoke [ => _string_ [, _string_, ... ] ] // // Get reference to UI element Toggle pattern and immediately call the Toggle() method. //# ui pattern Toggle [ => _string_ [, _string_, ... ] ] Ui, // Close Visual Studio //# quit Quit } public class Statement : MacroLine { public StatementType Type { get; set; } public List Args { get; set; } public string Code { get; set; } } public class CodeLine : MacroLine { public readonly string Code; public CodeLine(string code) { Code = code; } } class MacroParser { Parser MacroTextParser; Token TokenMacro; public static MacroParser Get() { var _this = new MacroParser(); return _this.Initialize() ? _this : null; } enum TokenId { Macro, Code, Statement, StatementType, StatementArg, StatementArgValue, StatementCode, StatementCodeValue, }; bool Initialize() { var charEsc = Char['\\']; var charQuot = Char['\"']; var stmtBegin = new Token("//#"); var codeBegin = new Token("=>"); var stmtType = new Token(TokenId.StatementType, CharWord.Repeat(atLeast: 1)); var quotedArgValue = new Token(TokenId.StatementArgValue, SkipWs_Disable, (charEsc & charQuot | CharSet[~(charQuot + CharCr + CharLf)]).Repeat(atLeast: 1)); var quotedArg = new Token(TokenId.StatementArg, charQuot & quotedArgValue & charQuot.Optional()) { new Rule { Create(TokenId.StatementArgValue, (string value) => value ) } }; var unquotedArg = new Token(TokenId.StatementArg, !LookAhead[charQuot] & CharSet[~CharSpace].Repeat(atLeast: 1)); var stmtArg = !LookAhead[codeBegin] & (quotedArg | unquotedArg); var stmtCodeValue = new Token(TokenId.StatementCodeValue, CharSet[~(CharCr + CharLf)].Repeat()); var stmtCode = new Token(TokenId.StatementCode, codeBegin & stmtCodeValue) { new Rule { Create(TokenId.StatementCodeValue, (string value) => value ) } }; var stmtLine = StartOfLine & new Token(TokenId.Statement, SkipWs_Disable, // '//#' [ ... ] [ '=>' ] stmtBegin & stmtType & stmtArg.Repeat() & stmtCode.Optional()) { new Rule { Capture(value => new Statement { Args = new List() }), Update(TokenId.StatementType, (Statement s, string typeStr) => { StatementType type; if (Enum.TryParse(typeStr, ignoreCase: true, result: out type)) s.Type = type; else s.Type = StatementType.Unknown; }), Update(TokenId.StatementArg, (Statement s, string arg) => s.Args.Add(arg)), Update(TokenId.StatementCode, (Statement s, string code) => s.Code = code) } } & SkipWs & (LineBreak | EndOfFile); var codeLine = StartOfLine & new Token(TokenId.Code, SkipWs_Disable, !LookAhead[stmtBegin & stmtType] & CharSet[~(CharCr + CharLf)].Repeat()) { new Rule { Capture((string value) => new CodeLine(value)) } } & (LineBreak | EndOfFile); TokenMacro = new Token(TokenId.Macro, SkipWs_Disable, (stmtLine | codeLine).Repeat() & EndOfFile) { new Rule { Update(TokenId.Statement, (MacroLines m, Statement s) => m.Add(s)), Update(TokenId.Code, (MacroLines m, CodeLine l) => m.Add(l)) } }; var parser = TokenMacro.Render(defaultTokenWs: CharHorizSpace.Repeat()); if (parser == null) return false; MacroTextParser = parser; return true; } public MacroLines Parse(string macroText) { return MacroTextParser.Parse(macroText) .GetValues(TokenMacro) .FirstOrDefault(); } } }