Team Ai
Datasetpublic

MegaBites-AI/Windows-powershell

sourceHugging Facemitupdated 6mo agoView on Hugging Face
0likes372downloads
Parser.cs8280 linesDownload Raw Back to parser
1// Copyright (c) Microsoft Corporation.2// Licensed under the MIT License.3 4using System.Collections;5using System.Collections.Generic;6using System.Collections.ObjectModel;7using System.Diagnostics.Tracing;8using System.Globalization;9using System.IO;10using System.Linq;11using System.Management.Automation.Runspaces;12using System.Management.Automation.Security;13using System.Management.Automation.Subsystem;14using System.Management.Automation.Subsystem.DSC;15using System.Reflection;16using System.Runtime.CompilerServices;17using System.Threading.Tasks;18using Dsc = Microsoft.PowerShell.DesiredStateConfiguration.Internal;19 20namespace System.Management.Automation.Language21{22    using KeyValuePair = Tuple<ExpressionAst, StatementAst>;23    using IfClause = Tuple<PipelineBaseAst, StatementBlockAst>;24    using SwitchClause = Tuple<ExpressionAst, StatementBlockAst>;25 26    internal enum ParseMode27    {28        Default = 0,29        ModuleAnalysis = 130    }31 32    /// <summary>33    /// The parser that parses PowerShell script and returns a <see cref="ScriptBlockAst"/>, tokens, and error messages34    /// if the script cannot be parsed successfully.35    /// </summary>36    public sealed class Parser37    {38        private readonly Tokenizer _tokenizer;39        internal Token _ungotToken;40        private bool _disableCommaOperator;41        private bool _savingTokens;42        private bool _inConfiguration;43        private ParseMode _parseMode;44 45        internal string _fileName;46 47        internal bool ProduceV2Tokens { get; set; }48 49        internal const string VERBATIM_ARGUMENT = "--%";50        internal const string VERBATIM_PARAMETERNAME = "-%";  // Same as VERBATIM_ARGUMENT w/o the first '-'.51 52        internal Parser()53        {54            _tokenizer = new Tokenizer(this);55            ErrorList = new List<ParseError>();56            _fileName = null;57        }58 59        /// <summary>60        /// Parse input from the specified file.61        /// </summary>62        /// <param name="fileName">The name of the file to parse.</param>63        /// <param name="tokens">Returns the tokens from parsing the script.</param>64        /// <param name="errors">Returns errors, if any, discovered while parsing the script.</param>65        /// <returns>The <see cref="ScriptBlockAst"/> that represents the input script file.</returns>66        public static ScriptBlockAst ParseFile(string fileName, out Token[] tokens, out ParseError[] errors)67        {68            const string scriptSchemaExtension = ".schema.psm1";69            var parseDscResource = false;70            // If the file has the 'schema.psm1' extension, then it is a 'DSC module file' however we don't actually load the71            // module at parse time so we can't use the normal mechanisms to bind the module name for configuration commands.72            // As an alternative, we extract the base name of the module file and use that as the module name for any keywords exported by this file.73            var parser = new Parser();74            if (!string.IsNullOrEmpty(fileName) && fileName.Length > scriptSchemaExtension.Length && fileName.EndsWith(scriptSchemaExtension, StringComparison.OrdinalIgnoreCase))75            {76                parser._keywordModuleName = Path.GetFileName(fileName.AsSpan(0, fileName.Length - scriptSchemaExtension.Length)).ToString();77                parseDscResource = true;78            }79 80            string scriptContents;81            try82            {83                var esi = new ExternalScriptInfo(fileName, fileName);84                scriptContents = esi.ScriptContents;85            }86            catch (Exception e)87            {88                var emptyExtent = new EmptyScriptExtent();89                var errorMsg = string.Format(CultureInfo.CurrentCulture, ParserStrings.FileReadError, e.Message);90                errors = new[] { new ParseError(emptyExtent, "FileReadError", errorMsg) };91                tokens = Array.Empty<Token>();92                return new ScriptBlockAst(emptyExtent, null, new StatementBlockAst(emptyExtent, null, null), false);93            }94 95            var tokenList = new List<Token>();96            ScriptBlockAst result;97            try98            {99                if (!parseDscResource)100                {101                    DynamicKeyword.Push();102                }103 104                result = parser.Parse(fileName, scriptContents, tokenList, out errors, ParseMode.Default);105            }106            catch (Exception e)107            {108                throw new ParseException(ParserStrings.UnrecoverableParserError, e);109            }110            finally111            {112                if (!parseDscResource)113                {114                    DynamicKeyword.Pop();115                }116            }117 118            tokens = tokenList.ToArray();119            return result;120        }121 122        private string _keywordModuleName;123 124        /// <summary>125        /// Parse input that does not come from a file.126        /// </summary>127        /// <param name="input">The input to parse.</param>128        /// <param name="tokens">Returns the tokens from parsing the script.</param>129        /// <param name="errors">Returns errors, if any, discovered while parsing the script.</param>130        /// <returns>The <see cref="ScriptBlockAst"/> that represents the input script file.</returns>131        public static ScriptBlockAst ParseInput(string input, out Token[] tokens, out ParseError[] errors)132        {133            return ParseInput(input, null /* fileName */, out tokens, out errors);134        }135 136        /// <summary>137        /// Parse input that does not come from a file.138        /// </summary>139        /// <param name="input">The input to parse.</param>140        /// <param name="fileName">The fileName if present or null.</param>141        /// <param name="tokens">Returns the tokens from parsing the script.</param>142        /// <param name="errors">Returns errors, if any, discovered while parsing the script.</param>143        /// <returns>The <see cref="ScriptBlockAst"/> that represents the input script file.</returns>144        public static ScriptBlockAst ParseInput(string input, string fileName, out Token[] tokens, out ParseError[] errors)145        {146            ArgumentNullException.ThrowIfNull(input);147 148            Parser parser = new Parser();149            List<Token> tokenList = new List<Token>();150            ScriptBlockAst result;151            try152            {153                result = parser.Parse(fileName, input, tokenList, out errors, ParseMode.Default);154            }155            catch (Exception e)156            {157                throw new ParseException(ParserStrings.UnrecoverableParserError, e);158            }159 160            tokens = tokenList.ToArray();161            return result;162        }163 164        internal ScriptBlockAst Parse(string fileName, string input, List<Token> tokenList, out ParseError[] errors, ParseMode parseMode)165        {166            try167            {168                return ParseTask(fileName, input, tokenList, false, parseMode);169            }170            finally171            {172                errors = ErrorList.ToArray();173            }174        }175 176        private ScriptBlockAst ParseTask(string fileName, string input, List<Token> tokenList, bool recursed, ParseMode parseMode)177        {178            bool etwEnabled = ParserEventSource.Log.IsEnabled();179            if (etwEnabled) ParserEventSource.Log.ParseStart(ParserEventSource.GetFileOrScript(fileName, input), input.Length);180 181            ScriptBlockAst ast = null;182 183            _parseMode = parseMode;184            _fileName = fileName;185            _tokenizer.Initialize(fileName, input, tokenList);186            _savingTokens = (tokenList != null);187            ErrorList.Clear();188 189            try190            {191                ast = ScriptBlockRule(null, isFilter: false);192#if DEBUG193                ast.InternalVisit(new CheckAllParentsSet(ast));194#endif195                ast.ScriptRequirements = _tokenizer.GetScriptRequirements();196 197                if (parseMode == ParseMode.Default)198                {199                    ast.PerformPostParseChecks(this);200                }201#if DEBUG202                ast.InternalVisit(new CheckTypeBuilder());203#endif204            }205            catch (InsufficientExecutionStackException)206            {207                if (!recursed)208                {209                    // We'll try parsing once more, this time on a new thread.  The assumption here is210                    // that the stack was close to overflowing before we tried to parse, and that won't211                    // be a problem on a new thread.212                    var task = new Task<ScriptBlockAst>(() => ParseTask(fileName, input, tokenList, true, parseMode));213                    task.Start();214                    task.Wait();215                    ast = task.Result;216                }217                else218                {219                    ReportError(_tokenizer.CurrentExtent(),220                        nameof(ParserStrings.ScriptTooComplicated),221                        ParserStrings.ScriptTooComplicated);222                }223            }224 225            if (etwEnabled) ParserEventSource.Log.ParseStop();226            return ast;227        }228 229        // This helper routine is used from the runtime to convert a string to a number.230        internal static object ScanNumber(string str, Type toType, bool shouldTryCoercion = true)231        {232            str = str.Trim();233            if (str.Length == 0)234            {235                // For backwards compatibility, we treat the empty string (w/ or w/o whitespace) as the236                // integer constant 0.  This is a slight change in semantics, the empty string was previously237                // only considered a number if we committed to a numeric operation, e.g.:238                //    5 - ""   - this results in 5239                // Returning 0 here means that240                //   "" - ""   - will result in 0, previously was an error.241                // This is mostly of interest for the operators -, /, %, -band, -bor, -bxor as these operators242                // allow strings as the lval and still do a numeric operation, whereas + and * treat string243                // lvals differently.244                return 0;245            }246 247            var tokenizer = (new Parser())._tokenizer;248            tokenizer.Initialize(null, str, null);249            tokenizer.AllowSignedNumbers = true;250            var token = tokenizer.NextToken() as NumberToken;251 252            if (token == null || !tokenizer.IsAtEndOfScript(token.Extent))253            {254                if (shouldTryCoercion)255                {256                    // We call ConvertTo, primarily because we expect it will throw an exception,257                    // but it's possible it could succeed, e.g. if the string had commas, our lexer258                    // will fail, but Convert.ChangeType could succeed.259                    return LanguagePrimitives.ConvertTo(str, toType, CultureInfo.InvariantCulture);260                }261                else262                {263                    throw new ParseException();264                }265            }266 267            return token.Value;268        }269 270        internal static ITypeName ScanType(string typename, bool ignoreErrors)271        {272            typename = typename.Trim();273            if (typename.Length == 0)274            {275                return null;276            }277 278            var parser = new Parser();279            var tokenizer = parser._tokenizer;280            tokenizer.Initialize(null, typename, null);281            var result = parser.TypeNameRule(allowAssemblyQualifiedNames: true, firstTypeNameToken: out _);282 283            SemanticChecks.CheckArrayTypeNameDepth(result, PositionUtilities.EmptyExtent, parser);284            if (!ignoreErrors && result is not null && (parser.ErrorList.Count > 0 || !result.Extent.Text.Equals(typename, StringComparison.OrdinalIgnoreCase)))285            {286                result = null;287            }288 289            return result;290        }291 292        internal static ExpressionAst ScanString(string str)293        {294            str = str.Replace("\"", "\"\"");295            var parser = new Parser();296            parser._tokenizer.Initialize(null, '"' + str + '"', null);297            var strToken = (StringExpandableToken)parser._tokenizer.NextToken();298            var ast = parser.ExpandableStringRule(strToken);299            if (parser.ErrorList.Count > 0)300            {301                throw new ParseException(parser.ErrorList.ToArray());302            }303 304            return ast;305        }306 307        private string _previousFirstTokenText;308        private string _previousLastTokenText;309 310        private static bool IgnoreTokenWhenUpdatingPreviousFirstLast(Token token)311        {312            return (token.Kind == TokenKind.Variable || token.Kind == TokenKind.Generic) &&313                   (token.Text.Equals("$^", StringComparison.OrdinalIgnoreCase) ||314                    token.Text.Equals("$$", StringComparison.OrdinalIgnoreCase));315        }316 317        internal void SetPreviousFirstLastToken(ExecutionContext context)318        {319            var firstToken = _tokenizer.FirstToken;320            if (firstToken != null)321            {322                context.SetVariable(SpecialVariables.FirstTokenVarPath, _previousFirstTokenText);323                if (!IgnoreTokenWhenUpdatingPreviousFirstLast(firstToken))324                {325                    var stringToken = firstToken as StringToken;326                    _previousFirstTokenText = stringToken != null327                        ? stringToken.Value328                        : firstToken.Text;329                }330 331                context.SetVariable(SpecialVariables.LastTokenVarPath, _previousLastTokenText);332 333                var lastToken = _tokenizer.LastToken;334                if (!IgnoreTokenWhenUpdatingPreviousFirstLast(lastToken))335                {336                    var stringToken = lastToken as StringToken;337                    _previousLastTokenText = stringToken != null338                        ? stringToken.Value339                        : lastToken.Text;340                }341            }342        }343 344        internal List<ParseError> ErrorList { get; }345 346        #region Utilities347 348        private void SkipNewlines()349        {350            if (_ungotToken == null || _ungotToken.Kind == TokenKind.NewLine)351            {352                _ungotToken = null;353                _tokenizer.SkipNewlines(skipSemis: false);354            }355        }356 357        private void SkipNewlinesAndSemicolons()358        {359            if (_ungotToken == null || _ungotToken.Kind == TokenKind.NewLine || _ungotToken.Kind == TokenKind.Semi)360            {361                _ungotToken = null;362                _tokenizer.SkipNewlines(skipSemis: true);363            }364        }365 366        private void SyncOnError(bool consumeClosingToken, params TokenKind[] syncTokens)367        {368            int parens = syncTokens.Contains(TokenKind.RParen) ? 1 : 0;369            int curlies = syncTokens.Contains(TokenKind.RCurly) ? 1 : 0;370            int braces = syncTokens.Contains(TokenKind.RBracket) ? 1 : 0;371            while (true)372            {373                Token token = NextToken();374                switch (token.Kind)375                {376                    case TokenKind.LParen: ++parens; break;377                    case TokenKind.RParen:378                        --parens;379                        if (parens == 0 && syncTokens.Contains(TokenKind.RParen))380                        {381                            if (!consumeClosingToken)382                            {383                                UngetToken(token);384                            }385 386                            return;387                        }388 389                        break;390 391                    case TokenKind.LCurly: ++curlies; break;392                    case TokenKind.RCurly:393                        --curlies;394                        if (curlies == 0 && syncTokens.Contains(TokenKind.RCurly))395                        {396                            if (!consumeClosingToken)397                            {398                                UngetToken(token);399                            }400 401                            return;402                        }403 404                        break;405 406                    case TokenKind.LBracket: ++braces; break;407                    case TokenKind.RBracket:408                        --braces;409                        if (braces == 0 && syncTokens.Contains(TokenKind.RBracket))410                        {411                            if (!consumeClosingToken)412                            {413                                UngetToken(token);414                            }415 416                            return;417                        }418 419                        break;420 421                    case TokenKind.EndOfInput:422                        // Never consume <EOF>, but return it to caller423                        UngetToken(token);424                        return;425                }426 427                if (syncTokens.Contains(token.Kind) && parens == 0 && curlies == 0 && braces == 0)428                {429                    break;430                }431            }432        }433 434        private Token NextToken()435        {436            Token token = _ungotToken ?? _tokenizer.NextToken();437            _ungotToken = null;438            return token;439        }440 441        private Token PeekToken()442        {443            Token token = _ungotToken ?? _tokenizer.NextToken();444            _ungotToken ??= token;445            return token;446        }447 448        private Token NextMemberAccessToken(bool allowLBracket)449        {450            // If _ungotToken is not null, we're in some sort of error state, don't return the token.451            if (_ungotToken != null)452                return null;453            return _tokenizer.GetMemberAccessOperator(allowLBracket);454        }455 456        private Token NextInvokeMemberToken()457        {458            // If _ungotToken is not null, we're in some sort of error state, don't return the token.459            if (_ungotToken != null)460                return null;461            return _tokenizer.GetInvokeMemberOpenParen();462        }463 464        private Token NextLBracket()465        {466            if (_ungotToken != null)467            {468                if (_ungotToken.Kind == TokenKind.LBracket) return NextToken();469                // If _ungotToken is not null, we're in some sort of error state, don't return the token.470                return null;471            }472 473            return _tokenizer.GetLBracket();474        }475 476        private StringToken GetVerbatimCommandArgumentToken()477        {478            if (_ungotToken == null || _ungotToken.Kind == TokenKind.Parameter)479            {480                _ungotToken = null;481                return _tokenizer.GetVerbatimCommandArgument();482            }483 484            return null;485        }486 487        private void SkipToken()488        {489            Diagnostics.Assert(_ungotToken != null, "Don't skip a token you didn't unget");490            _ungotToken = null;491        }492 493        private void UngetToken(Token token)494        {495            Diagnostics.Assert(_ungotToken == null, "Only 1 token lookahead is supported");496            _ungotToken = token;497        }498 499        private void SetTokenizerMode(TokenizerMode mode)500        {501            if (mode != _tokenizer.Mode && _ungotToken != null)502            {503                // Only rescan tokens that differ b/w command and expression modes.504                if (!_ungotToken.Kind.HasTrait(TokenFlags.ParseModeInvariant))505                {506                    Resync(_ungotToken);507                }508#if DEBUG509                else if (_ungotToken.Kind != TokenKind.EndOfInput)510                {511                    // Verify the comment above.512                    Token ungotToken = _ungotToken;513                    var oldTokenList = _tokenizer.TokenList;514                    _tokenizer.TokenList = null;515                    Resync(_ungotToken);516                    _tokenizer.Mode = mode;517                    Token rescan = _tokenizer.NextToken();518                    Diagnostics.Assert(ungotToken.Kind == rescan.Kind, "Rescan failed to return same kind");519                    Diagnostics.Assert(ungotToken.Text == rescan.Text, "Rescan failed to return same text");520                    IScriptPosition pos1 = ungotToken.Extent.StartScriptPosition;521                    IScriptPosition pos2 = rescan.Extent.StartScriptPosition;522                    Diagnostics.Assert(pos1.ColumnNumber == pos2.ColumnNumber, "Rescan failed to return same start column");523                    Diagnostics.Assert(pos1.LineNumber == pos2.LineNumber, "Rescan failed to return same start line#");524                    pos1 = ungotToken.Extent.EndScriptPosition;525                    pos2 = rescan.Extent.EndScriptPosition;526                    Diagnostics.Assert(pos1.ColumnNumber == pos2.ColumnNumber, "Rescan failed to return same end column");527                    Diagnostics.Assert(pos1.LineNumber == pos2.LineNumber, "Rescan failed to return same end line#");528                    // Make sure we leave things as they were - Resync clears _ungotToken.529                    _ungotToken = ungotToken;530                    _tokenizer.TokenList = oldTokenList;531                }532#endif533            }534 535            _tokenizer.Mode = mode;536        }537 538        private void Resync(Token token)539        {540            _ungotToken = null;541            _tokenizer.Resync(token);542        }543 544        private void Resync(int restorePoint)545        {546            _ungotToken = null;547            _tokenizer.Resync(restorePoint);548        }549 550        private static bool IsSpecificParameter(Token token, string parameter)551        {552            Diagnostics.Assert(token.Kind == TokenKind.Parameter, "Token must be a ParameterToken");553            var paramToken = (ParameterToken)token;554            return parameter.StartsWith(paramToken.ParameterName, StringComparison.OrdinalIgnoreCase);555        }556 557        internal void RequireStatementTerminator()558        {559            var terminatorToken = PeekToken();560            if (terminatorToken.Kind == TokenKind.NewLine || terminatorToken.Kind == TokenKind.Semi)561            {562                SkipToken();563            }564            else if (terminatorToken.Kind != TokenKind.EndOfInput)565            {566                ReportIncompleteInput(terminatorToken.Extent,567                    nameof(ParserStrings.MissingStatementTerminator),568                    ParserStrings.MissingStatementTerminator);569            }570        }571 572        internal static IScriptExtent ExtentOf(IScriptExtent first, IScriptExtent last)573        {574            if (first is EmptyScriptExtent) return last;575            if (last is EmptyScriptExtent) return first;576 577            Diagnostics.Assert(first is InternalScriptExtent && last is InternalScriptExtent,578                "Private method expects internal position representation");579            InternalScriptExtent l = (InternalScriptExtent)first;580            InternalScriptExtent r = (InternalScriptExtent)last;581            Diagnostics.Assert(l.PositionHelper == r.PositionHelper, "Can't get the extent across files");582            return new InternalScriptExtent(l.PositionHelper, l.StartOffset, r.EndOffset);583        }584 585        internal static IScriptExtent Before(IScriptExtent extent)586        {587            Diagnostics.Assert(extent is InternalScriptExtent, "Private method expects internal position representation");588            InternalScriptExtent scriptExtent = (InternalScriptExtent)extent;589            int offset = scriptExtent.StartOffset - 1;590            if (offset < 0) offset = 0;591            return new InternalScriptExtent(scriptExtent.PositionHelper, offset, offset);592        }593 594        internal static IScriptExtent After(IScriptExtent extent)595        {596            Diagnostics.Assert(extent is InternalScriptExtent, "Private method expects internal position representation");597            InternalScriptExtent scriptExtent = (InternalScriptExtent)extent;598            int offset = scriptExtent.EndOffset;599            return new InternalScriptExtent(scriptExtent.PositionHelper, offset, offset);600        }601 602        internal static IScriptExtent LastCharacterOf(IScriptExtent extent)603        {604            Diagnostics.Assert(extent is InternalScriptExtent, "Private method expects internal position representation");605            InternalScriptExtent scriptExtent = (InternalScriptExtent)extent;606            int offset = scriptExtent.EndOffset - 1;607            if (offset < 0)608            {609                offset = 0;610            }611 612            return new InternalScriptExtent(scriptExtent.PositionHelper, offset, offset);613        }614 615        internal static IScriptExtent ExtentFromFirstOf(params object[] objs)616        {617            foreach (object obj in objs)618            {619                if (obj != null)620                {621                    var token = obj as Token;622                    if (token != null)623                    {624                        return token.Extent;625                    }626 627                    var ast = obj as Ast;628                    if (ast != null)629                    {630                        return ast.Extent;631                    }632 633                    var typename = obj as ITypeName;634                    if (typename != null)635                    {636                        return typename.Extent;637                    }638 639                    Diagnostics.Assert(obj is IScriptExtent, "Only accepts tokens, asts, and IScriptExtents");640                    return (IScriptExtent)obj;641                }642            }643 644            Diagnostics.Assert(false, "One of the objects must not be null");645            return PositionUtilities.EmptyExtent;646        }647 648        internal static IScriptExtent ExtentOf(Token first, Token last) { return ExtentOf(first.Extent, last.Extent); }649 650        internal static IScriptExtent ExtentOf(Ast first, Ast last) { return ExtentOf(first.Extent, last.Extent); }651 652        internal static IScriptExtent ExtentOf(Ast first, Token last) { return ExtentOf(first.Extent, last.Extent); }653 654        internal static IScriptExtent ExtentOf(Token first, Ast last) { return ExtentOf(first.Extent, last.Extent); }655 656        internal static IScriptExtent ExtentOf(IScriptExtent first, Ast last) { return ExtentOf(first, last.Extent); }657 658        internal static IScriptExtent ExtentOf(IScriptExtent first, Token last) { return ExtentOf(first, last.Extent); }659 660        internal static IScriptExtent ExtentOf(Ast first, IScriptExtent last) { return ExtentOf(first.Extent, last); }661 662        internal static IScriptExtent ExtentOf(Token first, IScriptExtent last) { return ExtentOf(first.Extent, last); }663        // private static IScriptExtent Before(Ast ast) { return Before(ast.Extent); }664 665        internal static IScriptExtent Before(Token token) { return Before(token.Extent); }666 667        internal static IScriptExtent After(Ast ast) { return After(ast.Extent); }668 669        internal static IScriptExtent After(Token token) { return After(token.Extent); }670 671        private static IEnumerable<Ast> GetNestedErrorAsts(params object[] asts)672        {673            foreach (var obj in asts)674            {675                if (obj != null)676                {677                    Ast ast = obj as Ast;678                    if (ast != null)679                    {680                        yield return ast;681                    }682                    else683                    {684                        var enumerable = obj as IEnumerable<Ast>;685                        if (enumerable != null)686                        {687                            foreach (var ast2 in enumerable)688                            {689                                if (ast2 != null)690                                {691                                    yield return ast2;692                                }693                            }694                        }695                        else696                        {697                            Diagnostics.Assert(false, "Caller to pass only asts, or IEnumerable<Ast>");698                        }699                    }700                }701            }702        }703 704        /// <summary>705        /// Parses the specified constant hashtable string into a Hashtable object.706        /// </summary>707        /// <param name="input">The Hashtable string.</param>708        /// <param name="result">The Hashtable object.</param>709        /// <returns></returns>710        internal static bool TryParseAsConstantHashtable(string input, out Hashtable result)711        {712            result = null;713 714            if (string.IsNullOrWhiteSpace(input))715            {716                return false;717            }718 719            Token[] throwAwayTokens;720            ParseError[] parseErrors;721            var ast = Parser.ParseInput(input, out throwAwayTokens, out parseErrors);722 723            if (ast == null724                || parseErrors.Length > 0725                || ast.BeginBlock != null726                || ast.ProcessBlock != null727                || ast.CleanBlock != null728                || ast.DynamicParamBlock != null729                || ast.EndBlock.Traps != null)730            {731                return false;732            }733 734            var statements = ast.EndBlock.Statements;735            if (statements.Count != 1)736            {737                return false;738            }739 740            if (!(statements[0] is PipelineAst pipelineAst))741            {742                return false;743            }744 745            var expr = pipelineAst.GetPureExpression();746            if (expr == null)747            {748                return false;749            }750 751            if (!(expr is HashtableAst hashTableAst))752            {753                return false;754            }755 756            object hashtable;757            if (!IsConstantValueVisitor.IsConstant(hashTableAst, out hashtable, forRequires: true))758            {759                return false;760            }761 762            var data = hashtable as Hashtable;763            Diagnostics.Assert((data != null), "IsConstantValueVisitor.IsConstant() should return false when the specified HashtableAst is not a avalid Hashtable");764            result = data;765            return true;766        }767 768        #endregion Utilities769 770        #region Statements771 772        private ScriptBlockAst ScriptBlockRule(Token lCurly, bool isFilter)773        {774            return ScriptBlockRule(lCurly, isFilter, null);775        }776 777        private ScriptBlockAst ScriptBlockRule(Token lCurly, bool isFilter, StatementAst predefinedStatementAst)778        {779            // G  script-block:780            // G      using-statements:opt   param-block:opt   statement-terminators:opt   script-block-body:opt781            // G782            // G  using-statements:783            // G      using-statement784            // G      using-statements   using-statement785 786            // We could set the mode here, but we can avoid rescanning keywords if the caller787            // sets the mode before skipping newlines.788            Diagnostics.Assert(_tokenizer.Mode == TokenizerMode.Command,789                "Caller to make sure the mode is correct.");790 791            // Skipping newlines here saves a more expensive resync if there is no parameter block.792            SkipNewlines();793 794            var usingStatements = lCurly == null ? UsingStatementsRule() : null;795 796            var restorePoint = _tokenizer.GetRestorePoint();797            ParamBlockAst paramBlock = ParamBlockRule();798            if (paramBlock == null)799            {800                // In case we scanned some attributes or type constraints but they didn't801                // belong to a param statement, we need to reparse them (because they will802                // mean something different, such as a type literal expression, or a cast.)803                Resync(restorePoint);804            }805 806            SkipNewlinesAndSemicolons();807 808            return ScriptBlockBodyRule(lCurly, usingStatements, paramBlock, isFilter, predefinedStatementAst);809        }810 811        private List<UsingStatementAst> UsingStatementsRule()812        {813            List<UsingStatementAst> result = null;814 815            while (true)816            {817                var token = PeekToken();818                if (token.Kind == TokenKind.Using)819                {820                    SkipToken();821                    var statement = UsingStatementRule(token);822                    SkipNewlinesAndSemicolons();823                    result ??= new List<UsingStatementAst>();824 825                    var usingStatement = statement as UsingStatementAst;826                    // otherwise returned statement is ErrorStatementAst.827                    // We ignore it here, because error already reported to the parser.828                    if (usingStatement != null)829                    {830                        result.Add(usingStatement);831                    }832 833                    continue;834                }835 836                // Normally we don't need to resync, but our caller may speculatively scan much more than837                // one token, so it needs the restore point to include this token.838                Resync(token);839                break;840            }841 842            return result;843        }844 845        private ParamBlockAst ParamBlockRule()846        {847            // G  param-block:848            // G      new-lines:opt   attribute-list:opt   new-lines:opt   'param'   new-lines:opt849            // G              '('   parameter-list:opt   new-lines:opt   ')'850 851            SkipNewlines();852            List<AttributeBaseAst> candidateAttributes = AttributeListRule(false);853 854            SkipNewlines();855            Token paramToken = PeekToken();856            if (paramToken.Kind != TokenKind.Param)857            {858                return null;859            }860 861            SkipToken();862            SkipNewlines();863            Token lparen = NextToken();864            if (lparen.Kind != TokenKind.LParen)865            {866                UngetToken(lparen);867 868                // This is not an error, we'll end up trying to invoke a command named 'param'.869                return null;870            }871 872            List<ParameterAst> parameters = ParameterListRule();873 874            SkipNewlines();875            Token rParen = NextToken();876            var endExtent = rParen.Extent;877            if (rParen.Kind != TokenKind.RParen)878            {879                // ErrorRecovery: assume we saw the closing paren and continue like normal.880 881                UngetToken(rParen);882                endExtent = Before(rParen);883                ReportIncompleteInput(After(parameters != null && parameters.Count > 0 ? parameters.Last().Extent : lparen.Extent),884                    nameof(ParserStrings.MissingEndParenthesisInFunctionParameterList),885                    ParserStrings.MissingEndParenthesisInFunctionParameterList);886            }887 888            List<AttributeAst> attributes = new List<AttributeAst>();889            if (candidateAttributes != null)890            {891                foreach (AttributeBaseAst attr in candidateAttributes)892                {893                    AttributeAst attribute = attr as AttributeAst;894                    if (attribute != null)895                    {896                        attributes.Add(attribute);897                    }898                    else899                    {900                        // ErrorRecovery: nothing to do, this is a semantic error that is caught in the parser901                        // because the ast only allows attributes, no type constraints.902 903                        ReportError(attr.Extent,904                            nameof(ParserStrings.TypeNotAllowedBeforeParam),905                            ParserStrings.TypeNotAllowedBeforeParam,906                            attr.TypeName.FullName);907                    }908                }909            }910 911            return new ParamBlockAst(ExtentOf(paramToken, endExtent), attributes, parameters);912        }913 914        private List<ParameterAst> ParameterListRule()915        {916            // G  parameter-list:917            // G      script-parameter918            // G      parameter-list   new-lines:opt   ','   script-parameter919 920            List<ParameterAst> parameters = new List<ParameterAst>();921            Token commaToken = null;922            while (true)923            {924                ParameterAst parameter = ParameterRule();925                if (parameter == null)926                {927                    if (commaToken != null)928                    {929                        // ErrorRecovery: ??930 931                        ReportIncompleteInput(After(commaToken),932                            nameof(ParserStrings.MissingExpressionAfterToken),933                            ParserStrings.MissingExpressionAfterToken,934                            commaToken.Kind.Text());935                    }936 937                    break;938                }939 940                parameters.Add(parameter);941                SkipNewlines();942                commaToken = PeekToken();943                if (commaToken.Kind != TokenKind.Comma)944                {945                    break;946                }947 948                SkipToken();949            }950 951            return parameters;952        }953 954        private ParameterAst ParameterRule()955        {956            // G  script-parameter:957            // G      new-lines:opt   attribute-list:opt   new-lines:opt   variable   script-parameter-default:opt958            // G  script-parameter-default:959            // G      new-lines:opt   '='   new-lines:opt   expression960 961            List<AttributeBaseAst> attributes;962            VariableToken variableToken;963            ExpressionAst defaultValue = null;964 965            bool oldDisableCommaOperator = _disableCommaOperator;966            var oldTokenizerMode = _tokenizer.Mode;967            try968            {969                _disableCommaOperator = true;970                SetTokenizerMode(TokenizerMode.Expression);971 972                SkipNewlines();973                attributes = AttributeListRule(false);974                SkipNewlines();975                Token token = NextToken();976                if (token.Kind != TokenKind.Variable && token.Kind != TokenKind.SplattedVariable)977                {978                    UngetToken(token);979                    if (attributes != null)980                    {981                        // ErrorRecovery: skip to closing paren because returning null signals the last parameter.982 983                        ReportIncompleteInput(After(attributes.Last()),984                            nameof(ParserStrings.InvalidFunctionParameter),985                            ParserStrings.InvalidFunctionParameter);986                        SyncOnError(true, TokenKind.RParen);987 988                        // Even though we don't have a complete parameter, we do have attributes.  Intellisense989                        // might want to complete something in the attributes, so we need to return something useful.990                        var extent = ExtentOf(attributes[0].Extent, attributes[attributes.Count - 1].Extent);991                        return new ParameterAst(extent, new VariableExpressionAst(extent, "__error__", false), attributes, null);992                    }993 994                    return null;995                }996 997                variableToken = ((VariableToken)token);998 999                SkipNewlines();1000                Token equalsToken = PeekToken();1001                if (equalsToken.Kind == TokenKind.Equals)1002                {1003                    SkipToken();1004                    SkipNewlines();1005                    defaultValue = ExpressionRule();1006                    if (defaultValue == null)1007                    {1008                        ReportIncompleteInput(After(equalsToken),1009                            nameof(ParserStrings.MissingExpressionAfterToken),1010                            ParserStrings.MissingExpressionAfterToken,1011                            equalsToken.Kind.Text());1012                    }1013                }1014            }1015            finally1016            {1017                _disableCommaOperator = oldDisableCommaOperator;1018                SetTokenizerMode(oldTokenizerMode);1019            }1020 1021            IScriptExtent startExtent = (attributes == null) ? variableToken.Extent : attributes[0].Extent;1022            IScriptExtent endExtent = (defaultValue == null) ? variableToken.Extent : defaultValue.Extent;1023            return new ParameterAst(ExtentOf(startExtent, endExtent),1024                new VariableExpressionAst(variableToken), attributes, defaultValue);1025        }1026 1027        private List<AttributeBaseAst> AttributeListRule(bool inExpressionMode)1028        {1029            // G  attribute-list:1030            // G      attribute1031            // G      attribute-list   attribute1032 1033            List<AttributeBaseAst> attributes = new List<AttributeBaseAst>();1034            AttributeBaseAst attribute = AttributeRule();1035            while (attribute != null)1036            {1037                attributes.Add(attribute);1038                if (!inExpressionMode || attribute is AttributeAst)1039                {1040                    SkipNewlines();1041                }1042 1043                attribute = AttributeRule();1044            }1045 1046            if (attributes.Count == 0)1047            {1048                return null;1049            }1050 1051            return attributes;1052        }1053 1054        private AttributeBaseAst AttributeRule()1055        {1056            // G  attribute:1057            // G      '['   attribute-name   '('   attribute-arguments   ')'   ']'1058            // G  attribute-name:1059            // G      type-spec1060 1061            var lBracket = NextLBracket();1062            if (lBracket == null)1063            {1064                return null;1065            }1066 1067            SkipNewlines();1068 1069            Token firstTypeNameToken;1070            ITypeName typeName = TypeNameRule(allowAssemblyQualifiedNames: true, firstTypeNameToken: out firstTypeNameToken);1071            if (typeName == null)1072            {1073                // ErrorRecovery: Return null so we stop looking for attributes.1074 1075                Resync(lBracket);  // TypeNameRule might have consumed some tokens1076                ReportIncompleteInput(After(lBracket),1077                    nameof(ParserStrings.MissingTypename),1078                    ParserStrings.MissingTypename);1079                return null;1080            }1081 1082            Token lParenOrRBracket = NextToken();1083            if (lParenOrRBracket.Kind == TokenKind.LParen)1084            {1085                SkipNewlines();1086 1087                List<ExpressionAst> positionalArguments = new List<ExpressionAst>();1088                List<NamedAttributeArgumentAst> namedArguments = new List<NamedAttributeArgumentAst>();1089 1090                IScriptExtent lastItemExtent = lParenOrRBracket.Extent;1091                var oldTokenizerMode = _tokenizer.Mode;1092                try1093                {1094                    SetTokenizerMode(TokenizerMode.Expression);1095                    AttributeArgumentsRule(positionalArguments, namedArguments, ref lastItemExtent);1096                }1097                finally1098                {1099                    SetTokenizerMode(oldTokenizerMode);1100                }1101 1102                SkipNewlines();1103                Token rParen = NextToken();1104                if (rParen.Kind != TokenKind.RParen)1105                {1106                    // ErrorRecovery: pretend we saw a ')', attempt to find an ']'.1107 1108                    UngetToken(rParen);1109                    rParen = null;1110                    ReportIncompleteInput(After(lastItemExtent),1111                        nameof(ParserStrings.MissingEndParenthesisInExpression),1112                        ParserStrings.MissingEndParenthesisInExpression);1113                }1114 1115                SkipNewlines();1116                Token rBracket = NextToken();1117                if (rBracket.Kind != TokenKind.RBracket)1118                {1119                    // ErrorRecovery: pretend we saw a ']', return our result.1120 1121                    UngetToken(rBracket);1122                    rBracket = null;1123                    // Don't bother reporting a missing ']' if we reported a missing ')'.1124                    if (rParen != null)1125                    {1126                        ReportIncompleteInput(After(rParen),1127                            nameof(ParserStrings.EndSquareBracketExpectedAtEndOfAttribute),1128                            ParserStrings.EndSquareBracketExpectedAtEndOfAttribute);1129                    }1130                }1131 1132                firstTypeNameToken.TokenFlags |= TokenFlags.AttributeName;1133                return new AttributeAst(ExtentOf(lBracket, ExtentFromFirstOf(rBracket, rParen, lastItemExtent)), typeName, positionalArguments, namedArguments);1134            }1135 1136            if (ProduceV2Tokens)1137            {1138                var typeToken = new Token((InternalScriptExtent)ExtentOf(lBracket, lParenOrRBracket),1139                    TokenKind.Identifier, TokenFlags.TypeName);1140                _tokenizer.ReplaceSavedTokens(lBracket, lParenOrRBracket, typeToken);1141            }1142 1143            if (lParenOrRBracket.Kind != TokenKind.RBracket)1144            {1145                UngetToken(lParenOrRBracket);1146                ReportError(Before(lParenOrRBracket),1147                    nameof(ParserStrings.EndSquareBracketExpectedAtEndOfAttribute),1148                    ParserStrings.EndSquareBracketExpectedAtEndOfAttribute);1149                lParenOrRBracket = null;1150            }1151 1152            return new TypeConstraintAst(ExtentOf(lBracket, ExtentFromFirstOf(lParenOrRBracket, typeName.Extent)), typeName);1153        }1154 1155        private void AttributeArgumentsRule(ICollection<ExpressionAst> positionalArguments,1156                                            ICollection<NamedAttributeArgumentAst> namedArguments,1157                                            ref IScriptExtent lastItemExtent)1158        {1159            // G  attribute-arguments:1160            // G      attribute-argument1161            // G      attribute-argument   new-lines:opt   ','   attribute-arguments1162            // G  attribute-argument:1163            // G      new-lines:opt   expression1164            // G      new-lines:opt   property-name   '='   new-lines:opt   expression1165 1166            bool oldDisableCommaOperator = _disableCommaOperator;1167            Token commaToken = null;1168            HashSet<string> keysSeen = new HashSet<string>();1169            try1170            {1171                _disableCommaOperator = true;1172                while (true)1173                {1174                    SkipNewlines();1175 1176                    StringConstantExpressionAst name = SimpleNameRule();1177                    ExpressionAst expr;1178                    bool expressionOmitted = false;1179 1180                    if (name != null)1181                    {1182                        Token token = PeekToken();1183                        if (token.Kind == TokenKind.Equals)1184                        {1185                            token = NextToken();1186                            SkipNewlines();1187 1188                            expr = ExpressionRule();1189 1190                            if (expr == null)1191                            {1192                                // ErrorRecovery: ?1193 1194                                IScriptExtent errorPosition = After(token);1195                                ReportIncompleteInput(1196                                    errorPosition,1197                                    nameof(ParserStrings.MissingExpressionInNamedArgument),1198                                    ParserStrings.MissingExpressionInNamedArgument);1199                                expr = new ErrorExpressionAst(errorPosition);1200                                SyncOnError(true, TokenKind.Comma, TokenKind.RParen, TokenKind.RBracket, TokenKind.NewLine);

Showing the first 1,200 of 8280 lines. Download the file for the rest.