/// <summary> /// Note: The "FOR.." OR "FOR EACH".. content should have been removed from the /// token stream before calling this method /// </summary> private List <ICodeBlock> getForBlockContent(List <IToken> tokens) { string[] endSequenceMet; List <string[]> endSequences = new List <string[]>() { new string[] { "NEXT" } }; CodeBlockHandler codeBlockHandler = new CodeBlockHandler(endSequences); List <ICodeBlock> blockContent = codeBlockHandler.Process(tokens, out endSequenceMet); if (endSequenceMet == null) { throw new Exception("Didn't find end sequence!"); } // Remove end sequence tokens tokens.RemoveRange(0, endSequenceMet.Length); if (tokens.Count > 0) { if (tokens[0] is AbstractEndOfStatementToken) { tokens.RemoveAt(0); } else { throw new Exception("EndOfStatementToken missing after NEXT"); } } // Return code block instance return(blockContent); }
/// <summary> /// The token list will be edited in-place as handlers are able to deal with the content, so the input list should expect to be mutated /// </summary> public override ICodeBlock Process(List <IToken> tokens) { if (tokens == null) { throw new ArgumentNullException("tokens"); } if (tokens.Count < 3) { return(null); } // Look for start of function declaration string[] matchPattern = new string[] { "CLASS" }; if (!base.checkAtomTokenPattern(tokens, matchPattern, false)) { return(null); } if (!(tokens[1] is AtomToken)) { return(null); } if (!(tokens[2] is AbstractEndOfStatementToken)) { return(null); } var classNameToken = tokens[1]; tokens.RemoveRange(0, 3); // Get function content string[] endSequenceMet; var endSequences = new List <string[]>() { new string[] { "END", "CLASS" } }; var codeBlockHandler = new CodeBlockHandler(endSequences); var functionContent = codeBlockHandler.Process(tokens, out endSequenceMet); if (endSequenceMet == null) { throw new Exception("Didn't find encounter end sequence!"); } // Remove end sequence tokens tokens.RemoveRange(0, endSequenceMet.Length); if (tokens.Count > 0) { if (!(tokens[0] is AbstractEndOfStatementToken)) { throw new Exception("EndOfStatementToken missing after END CLASS"); } else { tokens.RemoveAt(0); } } return(new ClassBlock(new NameToken(classNameToken.Content, classNameToken.LineIndex), functionContent)); }
/// <summary> /// This will return just the parsed VBScript content, it will not attempt any translation. It will never return null nor a set containing /// any null references. This may be used to analyse the structure of a script, if so desired. /// </summary> public static IEnumerable <ICodeBlock> Parse(string scriptContent) { // Translate these tokens into ICodeBlock implementations (representing code VBScript structures) string[] endSequenceMet; var handler = new CodeBlockHandler(null); return(handler.Process( GetTokens(scriptContent).ToList(), out endSequenceMet )); }
/// <summary> /// The token list will be edited in-place as handlers are able to deal with the content, so the input list should expect to be mutated /// </summary> public override ICodeBlock Process(List <IToken> tokens) { if (tokens == null) { throw new ArgumentNullException("tokens"); } if (tokens.Count == 0) { return(null); } if (!base.checkAtomTokenPattern(tokens, new string[] { "WITH" }, matchCase: false)) { return(null); } if (tokens.Count < 4) { throw new ArgumentException("Insufficient tokens - invalid"); } // The WITH target will normally be a NameToken - eg. "WITH a" - but may also be wrapped in brackets - eg. "WITH (a)" - or may even // be another redirected reference (from an ancester WITH) - eg. "WITH .Item". We'll use the StatementHandler to determine what // content is part of the WITH target, but we don't directly require the returned Statement - we just needs its tokens (to // generate an Expression for the WithBlock). var token = base.getToken(tokens, offset: 1, allowedTokenTypes: new Type[] { typeof(OpenBrace), typeof(MemberAccessorOrDecimalPointToken), typeof(NameToken) }); var targetTokensSource = tokens.Skip(1).ToList(); var numberOfItemsInTargetTokensSource = targetTokensSource.Count; var target = new StatementHandler().Process(targetTokensSource) as Statement; if (target == null) { throw new ArgumentException("The WITH target must be parseable as a (non-value-setting) statement"); } else if (target.CallPrefix == Statement.CallPrefixOptions.Present) { throw new ArgumentException("The WITH target must be parseable as a statement without a CALL prefix"); } var numberOfItemsProcessedInTarget = numberOfItemsInTargetTokensSource - targetTokensSource.Count; tokens.RemoveRange(0, 1 + numberOfItemsProcessedInTarget); // Remove the "WITH" plus the tokens in the target reference // Get block content string[] endSequenceMet; var endSequences = new List <string[]> { new string[] { "END", "WITH" } }; var codeBlockHandler = new CodeBlockHandler(endSequences); var blockContent = codeBlockHandler.Process(tokens, out endSequenceMet); if (endSequenceMet == null) { throw new Exception("Didn't find end sequence!"); } // Remove end sequence tokens tokens.RemoveRange(0, endSequenceMet.Length); if (tokens.Count > 0) { if (tokens[0] is AbstractEndOfStatementToken) { tokens.RemoveAt(0); } else { throw new Exception("EndOfStatementToken missing after END WITH"); } } // Return code block instance return(new WithBlock(new Expression(target.Tokens), blockContent)); }
private ICodeBlock processMultiLine(List <IToken> tokens, int offsetToTHEN) { // ====================================================================== // Notes on multi-line If's: // ====================================================================== // - Invalid: Combining first statement with IF line // eg. IF (1) THEN Func // Func // END IF // // - Invalid: Trailing same-line-end-of-statement after IF // eg. IF (1) THEN : // Func(0) // END IF // // - Valid: Combining first statement with ELSEIF / ELSE line // eg. IF (1) THEN // Func // ELSEIF (1) THEN Func // ELSEIF (1) THEN // Func // ELSEIF (1) THEN Func // Func // ELSE Func // END IF // // - Valid: Trailing same-line-end-of-statement after ELSEIF / ELSE // eg. IF (1) THEN // Func(0) // ELSEIF(1) THEN : // Func(0) // END IF // // - Invalid: ELSE / ELSEIF must always be the first statement on the line // eg. IF (0) THEN // Func(1):Func(2):ELSE // END IF // // - Valid: IF may be combined with a previous line // eg. Func: IF (0) THEN // Func:Func:ELSE // END IF // ====================================================================== // Grab content inside IF blocks List <string[]> endSequences = new List <string[]>() { new string[] { "END", "IF" }, new string[] { "ELSEIF" }, new string[] { "ELSE" } }; string[] endSequenceMet = new string[] { "IF" }; List <IfBlock.IfBlockSegment> ifContent = new List <IfBlock.IfBlockSegment>(); List <IToken> conditionTokens = null; while (true) { // First loop, we'll match this as the initial "IF" statement so we can // grab the content after it.. if (endSequenceMet == null) { throw new Exception("Didn't find end sequence!"); } // Remove the last end sequence tokens (on the first loop, this will // actually remove the initial "IF" token) tokens.RemoveRange(0, endSequenceMet.Length); // The sequences that indicate further content are single-token ("IF", // "ELSEIF", ELSE") if (endSequenceMet.Length == 1) { // An "ELSE" token won't have any condition content if (endSequenceMet[0].ToUpper() == "ELSE") { conditionTokens = null; } else { // Grab the condition content (up to the "THEN" token) - this will // throw an exception if it can't find "THEN" or there is no content conditionTokens = getConditionContent(tokens).ToList(); } // If the first character is a new-line end-of-statement (which is a // common form), we may as well pull that out as well if ((tokens.Count > 0) && (tokens[0] is AbstractEndOfStatementToken)) { tokens.RemoveAt(0); } } // Try to find the end of this content block (this will remove tokens // from the stream that have been processed by the block handler) var codeBlockHandler = new CodeBlockHandler(endSequences); var blockContent = codeBlockHandler.Process(tokens, out endSequenceMet); if (endSequenceMet == null) { throw new Exception("Didn't find end sequence!"); } // Record this content block if (conditionTokens == null) { ifContent.Add(new IfBlock.IfBlockElseSegment(blockContent)); } else { Expression conditionStatement = new Expression(conditionTokens); ifContent.Add(new IfBlock.IfBlockConditionSegment(conditionStatement, blockContent)); } // If we hit, "END IF" then we're all done if ((endSequenceMet.Length == 2) && (endSequenceMet[0].ToUpper() == "END")) { // Need to remove the final tokens - if we don't break out of the loop // here, then end sequence tokens are removed up at the top tokens.RemoveRange(0, endSequenceMet.Length); break; } } // Expect the next token will be end-of-statement for IF block - drop that too if ((tokens.Count > 0) && (tokens[0] is AbstractEndOfStatementToken)) { tokens.RemoveAt(0); } // Return fully-formed IfBlock return(new IfBlock(ifContent)); }
private ICodeBlock processSingleLine(List <IToken> tokens) { // ====================================================================== // Notes on single-line If's: // ====================================================================== // - Invalid: Combining single line with multi-line // eg. IF (1) THEN Func() ELSE IF (1) THEN // Func() // END IF // // - Invalid: ElseIf formations // eg. IF (1) THEN Func() ELSEIF (2) THEN Func() // // - Combined If statements ARE valid // eg. IF (1) THEN Func() ELSE IF (2) THEN Func() // eg. IF (1) THEN IF (2) THEN Func() // // - Empty initial statement on If, with same-line end-of-statement // eg. IF (1) THEN : Func(2) // // - Dropping statement from else is valid // eg. IF (1) THEN Func() ELSE // // - Nested if statements can only ELSE to the last statement // Valid: IF (1) THEN Func() ELSE IF (1) THEN Func() ELSE Func() // InValid: IF (1) THEN Func() ELSE IF (1) THEN Func() ELSE Func() ELSE Func() // ====================================================================== // Grab content up to new line token (or end of content) List <IToken> ifTokens = new List <IToken>(); foreach (IToken token in tokens) { if (token is EndOfStatementNewLineToken) { break; } else { if ((!(token is AtomToken)) && (!(token is DateLiteralToken)) && (!(token is StringToken)) && (!(token is InlineCommentToken)) && (!(token is EndOfStatementSameLineToken))) { throw new Exception("IfHandler.processSingleLine: Encountered invalid Token - should all be AtomToken, DateLiteralToken, StringToken or EndOfStatementSameLineToken until new-line end of statement"); } ifTokens.Add(token); } } // We'll need to store the number of tokens associated with this IF statement // because we'll pull them out of the stream later (but we'll be manipulating // this ifTokens list as well, so stash the original count now) int ifTokensCount = ifTokens.Count; // Pull "IF" token from the start ifTokens.RemoveAt(0); // Look for "THEN" token (pull handled tokens out of stream) - this will // throw an exception if it can't find "THEN" or there is no content var conditionTokens = getConditionContent(ifTokens).ToList(); // Look for "ELSE" token (if present), ensuring we don't encounter any // "ELSEIF" tokens (which aren't valid in a single line "IF") int offsetElse = -1; for (int index = 0; index < ifTokens.Count; index++) { IToken token = ifTokens[index]; if (token is AtomToken) { if (token.Content.ToUpper() == "ELSE") { offsetElse = index; break; } else if (token.Content.ToUpper() == "ELSEIF") { throw new Exception("Invalid content: ELSEIF found in single-line IF"); } } } // We have the condition statement, now get the Post-THEN content and // Post-ELSE content (may be null) - ie. the condition's "met" and // "not met" code blocks List <IToken> truthTokens, notTokens; if (offsetElse == -1) { truthTokens = base.getTokenListSection(ifTokens, 0); notTokens = null; } else { truthTokens = base.getTokenListSection(ifTokens, 0, offsetElse); notTokens = base.getTokenListSection(ifTokens, offsetElse + 1); } // Note: It's not valid for Post-THEN content to be empty if (truthTokens.Count == 0) { throw new Exception("Empty THEN content in IF"); } // If we've got this far, we can pull out the processed tokens from the input list // - If statement ended at new-line-end-of-statement (as opposed to end of // token stream) then remove that token as well tokens.RemoveRange(0, ifTokensCount); if (tokens.Count > 0) { tokens.RemoveAt(0); } // Translate token sets into code blocks string[] endSequenceMet; CodeBlockHandler codeBlockHandler = new CodeBlockHandler(null); List <ICodeBlock> truthStatement = codeBlockHandler.Process(truthTokens, out endSequenceMet); List <ICodeBlock> notStatement; if (notTokens == null) { notStatement = null; } else { notStatement = codeBlockHandler.Process(notTokens, out endSequenceMet); } Expression conditionStatement = new Expression(conditionTokens); // Generate IfBlock List <IfBlock.IfBlockSegment> ifContent = new List <IfBlock.IfBlockSegment>(); ifContent.Add(new IfBlock.IfBlockConditionSegment(conditionStatement, truthStatement)); if (notStatement != null) { ifContent.Add(new IfBlock.IfBlockElseSegment(notStatement)); } return(new IfBlock(ifContent)); }
/// <summary> /// The token list will be edited in-place as handlers are able to deal with the content, so the input list should expect to be mutated /// Note: This will return a DoBlock if it can process content, as the While structure is effecively just a restricted Do /// </summary> public override ICodeBlock Process(List <IToken> tokens) { if (tokens == null) { throw new ArgumentNullException("tokens"); } if (tokens.Count == 0) { return(null); } // Determine whether we've got a "WHILE" block if (!base.checkAtomTokenPattern(tokens, new string[] { "WHILE" }, false)) { return(null); } if (tokens.Count < 3) { throw new ArgumentException("Insufficient tokens - invalid"); } // Remove WHILE keyword and grab conditional content var lineIndexOfStartOfConstruct = tokens[0].LineIndex; tokens.RemoveAt(0); // Loop for end of line.. var tokensInCondition = new List <IToken>(); while (true) { // Add AtomTokens to list until find EndOfStatement if (base.isEndOfStatement(tokens, 0)) { tokens.RemoveAt(0); break; } IToken tokenCondition = base.getToken_AtomOrDateStringLiteralOnly(tokens, 0); tokensInCondition.Add(tokenCondition); tokens.RemoveAt(0); } var conditionStatement = new Expression(tokensInCondition); // Get block content string[] endSequenceMet; var endSequences = new List <string[]>() { new string[] { "WEND" } }; var codeBlockHandler = new CodeBlockHandler(endSequences); var blockContent = codeBlockHandler.Process(tokens, out endSequenceMet); if (endSequenceMet == null) { throw new Exception("Didn't find end sequence!"); } // Remove end sequence tokens tokens.RemoveRange(0, endSequenceMet.Length); if (tokens.Count > 0) { if (tokens[0] is AbstractEndOfStatementToken) { tokens.RemoveAt(0); } else { throw new Exception("EndOfStatementToken missing after WHILE"); } } // Return code block instance // - Note: While DO..LOOP support EXIT DO, WHILE..WEND loops have no corresponding exit statement (so supportsExit is false) return(new DoBlock(conditionStatement, isPreCondition: true, doUntil: false, supportsExit: false, statements: blockContent, lineIndexOfStartOfConstruct: lineIndexOfStartOfConstruct)); }
/// <summary> /// The token list will be edited in-place as handlers are able to deal with the content, so the input list should expect to be mutated /// </summary> public override ICodeBlock Process(List <IToken> tokens) { if (tokens == null) { throw new ArgumentNullException("tokens"); } if (tokens.Count == 0) { return(null); } if (!base.checkAtomTokenPattern(tokens, new string[] { "SELECT", "CASE" }, false)) { return(null); } // Trim out "SELECT CASE" tokens tokens.RemoveRange(0, 2); // Grab content for the case expression List <IToken> expressionTokens = new List <IToken>(); for (int index = 0; index < tokens.Count; index++) { if (base.isEndOfStatement(tokens, index)) { // Remove expression tokens (plus end-of-statement) from stream tokens.RemoveRange(0, expressionTokens.Count + 1); break; } // Add token to expression (must be Atom or String) expressionTokens.Add(base.getToken_AtomOrDateStringLiteralOnly(tokens, index)); } // Look for the first CASE entry (note: it's allowable for there to be no // CASE entries at all, and case entries can be empty). It is also valid // to have comments outside of the CASE entries, though no other tokens // are valid in those areas. List <CommentStatement> openingComments = new List <CommentStatement>(); List <IToken> tokensIgnored = new List <IToken>(); for (int index = 0; index < tokens.Count; index++) { IToken token = tokens[index]; if (token is CommentToken) { openingComments.Add(new CommentStatement(token.Content, token.LineIndex)); } else if (token is AbstractEndOfStatementToken) { // Ignore blank lines tokensIgnored.Add(token); } else if (token is AtomToken) { if (token.Content.ToUpper() == "CASE") { break; } else if (token.Content.ToUpper() == "END") { if (index == (tokens.Count - 1)) { throw new Exception("Error processing SELECT CASE block - reached end of token stream"); } IToken tokenNext = tokens[index + 1]; if (!(tokenNext is AtomToken)) { throw new Exception("Error processing SELECT CASE block - reached END followed invalid token [" + tokenNext.GetType().ToString() + "]"); } if (tokenNext.Content.ToUpper() != "SELECT") { throw new Exception("Error processing SELECT CASE block - reached non-SELECT END tokens"); } break; } } else { throw new Exception("Invalid token encountered in SELECT CASE block [" + token.GetType().ToString() + "]"); } } tokens.RemoveRange(0, openingComments.Count + tokensIgnored.Count); // Unless we hit "END SELECT" straight away, process CASE blocks List <SelectBlock.CaseBlockSegment> content = new List <SelectBlock.CaseBlockSegment>(); if (tokens[0].Content.ToUpper() != "END") { string[] endSequenceMet; List <string[]> endSequences = new List <string[]>() { new string[] { "CASE" }, new string[] { "END", "SELECT" } }; CodeBlockHandler codeBlockHandler = new CodeBlockHandler(endSequences); while (true) { // Try to grab value(s) for CASE block // - Get lists of tokens (may be multiple values, may be ELSE..) List <List <IToken> > exprValues = base.getEntryList(tokens, 1, new EndOfStatementNewLineToken(tokens[0].LineIndex)); // - Remove the CASE token tokens.RemoveRange(0, 1); // - Remove the exprValues tokens foreach (List <IToken> valueTokens in exprValues) { tokens.RemoveRange(0, valueTokens.Count); } // - Remove the commas between expressions tokens.RemoveRange(0, exprValues.Count - 1); // - Remove the end-of-statement token tokens.RemoveRange(0, 1); // Quick check that it appears valid bool caseElse = false; if (exprValues.Count == 0) { throw new Exception("CASE block with no comparison value"); } else { IToken firstExprToken = exprValues[0][0]; if ((firstExprToken is AtomToken) && (firstExprToken.Content.ToUpper() == "ELSE")) { if ((exprValues.Count > 1) || (exprValues[0].Count != 1)) { throw new Exception("Invalid CASE ELSE opening statement"); } caseElse = true; } } // Try to grab single CASE block content List <ICodeBlock> blockContent = codeBlockHandler.Process(tokens, out endSequenceMet); if (endSequenceMet == null) { throw new Exception("Didn't find end sequence!"); } // Add to CASE block list if (caseElse) { content.Add(new SelectBlock.CaseBlockElseSegment(blockContent)); } else { List <Expression> values = new List <Expression>(); foreach (List <IToken> valueTokens in exprValues) { values.Add(new Expression(valueTokens)); } content.Add(new SelectBlock.CaseBlockExpressionSegment(values, blockContent)); } // If we hit END SELECT then break out of loop, otherwise // go back round to get the next block if (endSequenceMet.Length == 2) { tokens.RemoveRange(0, endSequenceMet.Length); if (tokens.Count > 0) { if (!(tokens[0] is AbstractEndOfStatementToken)) { throw new Exception("EndOfStatementToken missing after END FUNCTION"); } else { tokens.RemoveAt(0); } } break; } } } // All done! return(new SelectBlock(new Expression(expressionTokens), openingComments, content)); }
/// <summary> /// The token list will be edited in-place as handlers are able to deal with the content, so the input list should expect to be mutated /// Note: This handles both FUNCTION and SUB blocks, since they are essentially the same /// </summary> public override ICodeBlock Process(List <IToken> tokens) { if (tokens == null) { throw new ArgumentNullException("tokens"); } if (tokens.Count == 0) { return(null); } // Look for start of function declaration Dictionary <string[], BlockType> matchPatterns = new Dictionary <string[], BlockType>(); // - Sub matchPatterns.Add(new string[] { "SUB" }, BlockType.PublicSub); matchPatterns.Add(new string[] { "PUBLIC", "SUB" }, BlockType.PublicSub); matchPatterns.Add(new string[] { "PRIVATE", "SUB" }, BlockType.PrivateSub); // - Function matchPatterns.Add(new string[] { "FUNCTION" }, BlockType.PublicFunction); matchPatterns.Add(new string[] { "PUBLIC", "FUNCTION" }, BlockType.PublicFunction); matchPatterns.Add(new string[] { "PUBLIC", "DEFAULT", "FUNCTION" }, BlockType.PublicDefaultFunction); matchPatterns.Add(new string[] { "PRIVATE", "FUNCTION" }, BlockType.PrivateFunction); // - Property Get matchPatterns.Add(new string[] { "PROPERTY", "GET" }, BlockType.PublicPropertyGet); matchPatterns.Add(new string[] { "PUBLIC", "PROPERTY", "GET" }, BlockType.PublicPropertyGet); matchPatterns.Add(new string[] { "PUBLIC", "DEFAULT", "PROPERTY", "GET" }, BlockType.PublicDefaultPropertyGet); matchPatterns.Add(new string[] { "PRIVATE", "PROPERTY", "GET" }, BlockType.PrivatePropertyGet); // - Property Let / Set matchPatterns.Add(new string[] { "PROPERTY", "LET" }, BlockType.PublicPropertyLet); matchPatterns.Add(new string[] { "PROPERTY", "SET" }, BlockType.PublicPropertySet); matchPatterns.Add(new string[] { "PUBLIC", "PROPERTY", "LET" }, BlockType.PublicPropertyLet); matchPatterns.Add(new string[] { "PUBLIC", "PROPERTY", "SET" }, BlockType.PublicPropertySet); matchPatterns.Add(new string[] { "PRIVATE", "PROPERTY", "LET" }, BlockType.PrivatePropertyLet); matchPatterns.Add(new string[] { "PRIVATE", "PROPERTY", "SET" }, BlockType.PrivatePropertySet); bool match = false; int matchPatternLength = 0; BlockType blockType = BlockType.Unknown; foreach (string[] matchPattern in matchPatterns.Keys) { if (base.checkAtomTokenPattern(tokens, matchPattern, false)) { match = true; matchPatternLength = matchPattern.Length; blockType = matchPatterns[matchPattern]; break; } } if (!match) { return(null); } // Now look for the rest (function name, parameters, etc..) // - There must be AT LEAST matchPatternLength + 1 tokens (for function // name; the open/close brackets and parameters are optional) if (tokens.Count < matchPatternLength + 1) { return(null); } // - Get IsPublic and funcName (ensure funcName token is AtomToken) bool isPublic = (tokens[0].Content.ToUpper() != "PRIVATE"); if (!(tokens[matchPatternLength] is AtomToken)) { return(null); } var funcNameToken = tokens[matchPatternLength]; // - Get parameters (if specified, they're optional in VBScript) and // remove the tokens we've accounted for for the function header List <FunctionBlock.Parameter> parameters = getFuncParametersAndRemoveTokens(tokens, matchPatternLength + 1); // Determine what the end sequence will look like.. List <string[]> endSequences = new List <string[]>(); switch (blockType) { case BlockType.PublicSub: case BlockType.PrivateSub: endSequences.Add(new string[] { "END", "SUB" }); break; case BlockType.PublicFunction: case BlockType.PublicDefaultFunction: case BlockType.PrivateFunction: endSequences.Add(new string[] { "END", "FUNCTION" }); break; case BlockType.PublicPropertyGet: case BlockType.PublicDefaultPropertyGet: case BlockType.PrivatePropertyGet: case BlockType.PublicPropertyLet: case BlockType.PrivatePropertyLet: case BlockType.PublicPropertySet: case BlockType.PrivatePropertySet: endSequences.Add(new string[] { "END", "PROPERTY" }); break; default: throw new Exception("Ended up with invalid BlockType [" + blockType.ToString() + "] - how did this happen?? :S"); } // Get function content string[] endSequenceMet; CodeBlockHandler codeBlockHandler = new CodeBlockHandler(endSequences); List <ICodeBlock> blockContent = codeBlockHandler.Process(tokens, out endSequenceMet); if (endSequenceMet == null) { throw new Exception("Didn't find end sequence!"); } // Remove end sequence tokens tokens.RemoveRange(0, endSequenceMet.Length); if (tokens.Count > 0) { if (!(tokens[0] is AbstractEndOfStatementToken)) { throw new Exception("EndOfStatementToken missing after END SUB/FUNCTION"); } else { tokens.RemoveAt(0); } } // Return code block instance bool isDefault = ((blockType == BlockType.PublicDefaultFunction) || (blockType == BlockType.PublicDefaultPropertyGet)); if ((blockType == BlockType.PublicSub) || (blockType == BlockType.PrivateSub)) { return(new SubBlock(isPublic, isDefault, new NameToken(funcNameToken.Content, funcNameToken.LineIndex), parameters, blockContent)); } else if ((blockType == BlockType.PublicFunction) || (blockType == BlockType.PublicDefaultFunction) || (blockType == BlockType.PrivateFunction)) { return(new FunctionBlock(isPublic, isDefault, new NameToken(funcNameToken.Content, funcNameToken.LineIndex), parameters, blockContent)); } else if ((blockType == BlockType.PublicPropertyGet) || (blockType == BlockType.PublicDefaultPropertyGet) || (blockType == BlockType.PrivatePropertyGet)) { return(new PropertyBlock(isPublic, isDefault, new NameToken(funcNameToken.Content, funcNameToken.LineIndex), PropertyBlock.PropertyType.Get, parameters, blockContent)); } else if ((blockType == BlockType.PublicPropertySet) || (blockType == BlockType.PrivatePropertySet)) { return(new PropertyBlock(isPublic, isDefault, new NameToken(funcNameToken.Content, funcNameToken.LineIndex), PropertyBlock.PropertyType.Set, parameters, blockContent)); } if ((blockType == BlockType.PublicPropertyLet) || (blockType == BlockType.PrivatePropertyLet)) { return(new PropertyBlock(isPublic, isDefault, new NameToken(funcNameToken.Content, funcNameToken.LineIndex), PropertyBlock.PropertyType.Let, parameters, blockContent)); } else { throw new Exception("Unrecognised BlockType [" + blockType.ToString() + "] - how did this happen??"); } }
/// <summary> /// The token list will be edited in-place as handlers are able to deal with the content, so the input list should expect to be mutated /// </summary> public override ICodeBlock Process(List <IToken> tokens) { if (tokens == null) { throw new ArgumentNullException("tokens"); } if (tokens.Count == 0) { return(null); } // Determine whether we've got a "DO", "DO WHILE" or "DO UNTIL" if (!base.checkAtomTokenPattern(tokens, new string[] { "DO" }, false)) { return(null); } if (tokens.Count < 3) { throw new ArgumentException("Insufficient tokens - invalid"); } var lineIndexOfStartOfConstruct = tokens[0].LineIndex; var preConditionStartToken = base.getToken(tokens, 1, new List <Type> { typeof(AtomToken), typeof(AbstractEndOfStatementToken) }); bool hasPreCondition, doUntil; if (preConditionStartToken is AtomToken) { var doWhile = (preConditionStartToken.Content.ToUpper() == "WHILE"); doUntil = (preConditionStartToken.Content.ToUpper() == "UNTIL"); hasPreCondition = doWhile || doUntil; } else { hasPreCondition = false; doUntil = false; } // Remove DO keyword and grab pre-condition content (if any) tokens.RemoveAt(0); Expression conditionStatement; if (!hasPreCondition) { conditionStatement = null; } else { tokens.RemoveAt(0); // Remove WHILE / UNTIL conditionStatement = ExtractConditionFromTokens(tokens); } // If the token was a WHILE or UNTIL and we extracted a pre-condition following it, then the next token must be an end-of-statement. If there was // no pre-condition then we've only processed the "DO" and the next token must still be an end-of-statement. if (tokens.Count > 0) { if (tokens[0] is AbstractEndOfStatementToken) { tokens.RemoveAt(0); } else { throw new ArgumentException("Expected end-of-statement token after DO keyword (relating to a construct without a pre-condition)"); } } // Get block content string[] endSequenceMet; var endSequences = new List <string[]>() { new string[] { "LOOP" } }; var codeBlockHandler = new CodeBlockHandler(endSequences); var blockContent = codeBlockHandler.Process(tokens, out endSequenceMet); if (endSequenceMet == null) { throw new Exception("Didn't find end sequence!"); } tokens.RemoveAt(0); // Remove "LOOP" // Remove post-condition content (if any) if (!hasPreCondition) { // Note that it's valid in VBScript to have neither a pre- or post-condition and for the loop to continue until an EXIT DO // statement is encountered (in which case we will pass a null conditionStatement to the DoBlock). It's not valid for it // to have both a pre- and post-condition, so if a pre-condition has already been extracted, don't try to extract a // post-condition. var postConditionStartToken = base.getToken(tokens, 0, new List <Type> { typeof(AtomToken), typeof(AbstractEndOfStatementToken) }); if (postConditionStartToken is AtomToken) { var doWhile = (postConditionStartToken.Content.ToUpper() == "WHILE"); doUntil = (postConditionStartToken.Content.ToUpper() == "UNTIL"); if (doWhile || doUntil) { tokens.RemoveAt(0); // Remove WHILE / UNTIL conditionStatement = ExtractConditionFromTokens(tokens); } } } // Whether a post-condition has been processed or the construct terminated at the "LOOP" keyword, the next token (if any) // must be an end-of-statement if (tokens.Count > 0) { if (tokens[0] is AbstractEndOfStatementToken) { tokens.RemoveAt(0); } else { throw new Exception("EndOfStatementToken missing after LOOP"); } } var supportsExit = true; // DO..LOOP supports EXIT DO (while WHILE..WEND loops have no corresponding exit statement) return(new DoBlock(conditionStatement, hasPreCondition, doUntil, supportsExit, blockContent, lineIndexOfStartOfConstruct)); }