Esempio n. 1
0
        /// <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);
        }
Esempio n. 2
0
        /// <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
                       ));
        }
Esempio n. 4
0
        /// <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));
        }
Esempio n. 5
0
        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));
        }
Esempio n. 6
0
        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));
        }
Esempio n. 8
0
        private void AddContent(List <string> sourceCode, StringBuilder output)
        {
            var codeBlockHandler = new CodeBlockHandler(output);

            for (var i = 0; i < sourceCode.Count; i++)
            {
                var line    = sourceCode[i];
                var trimmed = line.TrimStart();

                if (trimmed.StartsWith("//md ") || trimmed.Equals("//md"))
                {
                    codeBlockHandler.Exit();
                    trimmed = trimmed.Remove(0, trimmed.Length > 5 ? 5 : 4);
                    output.AppendLine(trimmed);
                }
                else if (trimmed.StartsWith("//md_hide-next"))
                {
                    i++;
                }
                else if (trimmed.StartsWith("//md_sc"))
                {
                    codeBlockHandler.Exit();
                    var shortcode = trimmed.Replace("//md_sc ", "");
                    HandleShortcode(shortcode, output);
                }
                else if (trimmed.StartsWith("#region hidden"))
                {
                    var nestLevel = 1;
                    i++;

                    var description = trimmed.Replace("#region hidden:", "");

                    if (description != "" && trimmed.Contains("hidden:"))
                    {
                        output.AppendLine();
                        output.AppendLine(line.Replace("#region hidden:", "/* ") + " */");
                        output.AppendLine();
                    }

                    while (i < sourceCode.Count)
                    {
                        var nextLine = sourceCode[i].Trim();

                        if (nextLine.StartsWith("#region"))
                        {
                            nestLevel++;
                        }
                        else if (nextLine.StartsWith("#endregion"))
                        {
                            nestLevel--;
                        }

                        if (nestLevel == 0)
                        {
                            break;
                        }

                        i++;
                    }
                }
                else if (codeBlockHandler.IsInside)
                {
                    output.AppendLine(line);
                }
                else if (!string.IsNullOrEmpty(trimmed))
                {
                    codeBlockHandler.Enter();
                    output.AppendLine(line);
                }
                else
                {
                    output.AppendLine();
                }
            }

            codeBlockHandler.Exit();
        }
Esempio n. 9
0
        private void AddResults <TRoom>(StringBuilder output, IExampleGrid2D <TRoom> example)
        {
            var sourceCode       = sourceCodeParser.GetMethod("GetResults", false);
            var results          = example.GetResults().ToList();
            var codeBlockHandler = new CodeBlockHandler(output);

            output.AppendLine("## Results");
            output.AppendLine();

            var resultsCounter = 0;

            foreach (var line in sourceCode)
            {
                var trimmed = line.Trim();

                if (trimmed.StartsWith("//md"))
                {
                    codeBlockHandler.Exit();

                    if (trimmed.Length > 5)
                    {
                        trimmed = trimmed.Remove(0, 5);
                    }
                    else
                    {
                        trimmed = trimmed.Remove(0, 4);
                    }

                    output.AppendLine(trimmed);
                }
                else if (trimmed.Contains("yield") && !trimmed.Contains("yield break"))
                {
                    codeBlockHandler.Exit();

                    var levelDescription = results[resultsCounter];
                    var initStopwatch    = new Stopwatch();
                    initStopwatch.Start();
                    var generator = new GraphBasedGeneratorGrid2D <TRoom>(levelDescription);
                    generator.InjectRandomGenerator(new Random(0));
                    initStopwatch.Stop();

                    var layoutDrawer = new GraphBasedGenerator.Grid2D.Drawing.SVGLayoutDrawer <TRoom>();
                    var oldMapDrawer = new DungeonDrawer <TRoom>();

                    var times = new List <long>();

                    for (int i = 0; i < 4; i++)
                    {
                        var generatorStopwatch = new Stopwatch();
                        generatorStopwatch.Start();
                        var level = generator.GenerateLayout();
                        generatorStopwatch.Stop();

                        times.Add(generatorStopwatch.ElapsedMilliseconds);

                        Console.WriteLine(generatorStopwatch.ElapsedMilliseconds + initStopwatch.ElapsedMilliseconds);

                        var svg = layoutDrawer.DrawLayout(level, 800, forceSquare: true, flipY: true, fixedFontSize: 30);
                        File.WriteAllText(Path.Combine(AssetsFolder, $"{resultsCounter}_{i}.svg"),
                                          svg);

                        var bitmap = oldMapDrawer.DrawLayout(level, new DungeonDrawerOptions()
                        {
                            Width             = 2000,
                            Height            = 2000,
                            PaddingPercentage = 0.1f,
                        });
                        bitmap.Save(Path.Combine(AssetsFolder, $"{resultsCounter}_{i}.png"));
                    }

                    var summaryDrawer = new GeneratorSummaryDrawer <TRoom>();
                    var summary       = summaryDrawer.Draw(levelDescription, 5000, generator);
                    summary.Save(Path.Combine(AssetsFolder, $"{resultsCounter}_summary.png"));

                    output.AppendLine();
                    output.AppendLine("<Gallery cols={2}>");
                    for (int i = 0; i < 4; i++)
                    {
                        output.AppendLine(
                            $"<GalleryImage src={{require('./{example.Options.DocsFileName}/{resultsCounter}_{i}.png').default}} />");
                    }

                    output.AppendLine("</Gallery>");

                    output.AppendLine();
                    output.AppendLine("<div style={{ textAlign: 'center', marginTop: '-15px' }}>");
                    output.AppendLine();
                    output.AppendLine($"*Average time to generate the level: {((times.Average() + initStopwatch.ElapsedMilliseconds) / 1000).ToString("F", CultureInfo.InvariantCulture)}s ({((initStopwatch.ElapsedMilliseconds) / 1000d).ToString("F", CultureInfo.InvariantCulture)}s init, {((times.Average()) / 1000).ToString("F", CultureInfo.InvariantCulture)}s generation itself)*");
                    output.AppendLine();
                    output.AppendLine("</div>");
                    output.AppendLine();

                    resultsCounter++;
                }
                else if (codeBlockHandler.IsInside)
                {
                    output.AppendLine(line);
                }
                else if (!string.IsNullOrEmpty(trimmed))
                {
                    codeBlockHandler.Enter();
                    output.AppendLine(line);
                }
                else
                {
                    output.AppendLine();
                }
            }

            codeBlockHandler.Exit();
        }
Esempio n. 10
0
        /// <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??");
            }
        }
Esempio n. 12
0
        /// <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));
        }