Example #1
0
        private void _executeForeach(ForeachExpression expr, List <Line> relatedLines, Line baseLine)
        {
            var contents     = relatedLines.Select(l => l.Content).ToArray();
            var iterateThose = expr.Arguments.Arguments.Select(parseExpr).ToList();

            unpackPackedArguments();
            //get smallest index and iterate it.
            var min  = iterateThose.Min(i => i.Count);
            var vars = Context.Variables;

            for (int i = 0; i < min; i++)
            {
                //set variables
                if (expr.Depth > 0)
                {
                    vars[$"i{expr.Depth}"] = new NumberScalar(i);
                }
                else
                {
                    vars["i"] = new NumberScalar(i);
                }
                for (int j = 0; j < iterateThose.Count; j++)
                {
                    vars[$"__{j + 1 + expr.Depth * 100}__"] = iterateThose[j][i];
                }

                var variables = new List <string>(); //a list of all added variables that will be cleaned after this i iteration.

                //now here we iterate contents and set all variables in it.
                for (var contentIndex = 0; contentIndex < contents.Length; contentIndex++)
                {
                    var content = contents[contentIndex];
                    //iterate lines, one at a time
                    // ReSharper disable once RedundantToStringCall
                    var          copy                   = content.ToString().Replace("|#", "#");
                    bool         changed                = false;
                    int          last_access_index      = 0;
                    const string HashtagExpressionRegex = @"(?<!\\)\#\((?:[^()]|(?<open>\()|(?<-open>\)))+(?(open)(?!))\)";
                    var          hashtagExprs           = Regex.Matches(copy, HashtagExpressionRegex, Regexes.DefaultRegexOptions).Cast <Match>().ToArray();

                    //replace all emit commands
                    copy = ExpressionLexer.ReplaceRegex(copy, @"(?<!\\)\#([0-9]+)", match => {
                        var key = $"__{match.Groups[1].Value}__";
                        if (hashtagExprs.Any(m => m.IsMatchNestedTo(match)))
                        {
                            //it is inside hashtagExpr #(...)
                            return(key);
                        }

                        return(_emit(vars[key]));
                    });

                    var ew = new ExpressionWalker(ExpressionLexer.Tokenize(copy, ExpressionToken.StringLiteral));

                    if (ew.HasNext)
                    {
                        do
                        {
_restart:
                            if (changed)
                            {
                                changed = false;
                                var cleanedCopy = new string(' ', last_access_index) + copy.Substring(last_access_index);
                                ew = new ExpressionWalker(ExpressionLexer.Tokenize(cleanedCopy, ExpressionToken.StringLiteral));
                            }

                            var current = ew.Current;
                            //iterate all tokens of that line

                            if (current.Token == ExpressionToken.Mod && ew.HasNext)
                            {
                                if (ew.HasBack && ew.PeakBack.Token == ExpressionToken.Escape)
                                {
                                    continue;
                                }

                                var expr_ew = new ExpressionWalker(ExpressionLexer.Tokenize(copy.Substring(current.Match.Index)));
                                //var offset = current.Match.Index;
                                //var hashtag = expr_ew.Current;
                                current = expr_ew.NextToken();
                                switch (current.Token)
                                {
                                case ExpressionToken.Foreach:
                                    var code        = contents.SkipWhile(s => s != content).StringJoin();
                                    var e           = ForeachExpression.Parse(code);
                                    var foreachExpr = (ForeachExpression)e.Related[0];
                                    foreachExpr.Depth = expr.Depth + 1;
                                    //no need to mark lines from e for deletion, they are already marked beforehand.
                                    _executeForeach(foreachExpr, e.RelatedLines, baseLine);
                                    contentIndex += e.RelatedLines.Count + 2 - 1;     //first for the %foreach line, second for the closer %, -1 because we increment index by one on next iteration.
                                    goto _skipline;

                                default:
                                    continue;
                                }
                            }

                            if (current.Token == ExpressionToken.Hashtag && ew.HasNext)
                            {
                                if (ew.HasBack && ew.PeakBack.Token == ExpressionToken.Escape)
                                {
                                    continue;
                                }

                                var offset  = current.Match.Index;
                                var expr_ew = new ExpressionWalker(ExpressionLexer.Tokenize(copy.Substring(current.Match.Index)));

                                var hashtag = expr_ew.Current;
                                current = expr_ew.NextToken();
                                switch (current.Token)
                                {
                                case ExpressionToken.Literal:
                                    //this is variable declaration %varname = expr
                                    var peak = expr_ew.PeakNext.Token;
                                    if (peak == ExpressionToken.Equal)
                                    {
                                        var e       = VariableDeclarationExpression.Parse(expr_ew);
                                        var varname = e.Name.AsString();
                                        if (!Context.Variables.ContainsKey(varname))
                                        {
                                            variables.Add(varname);
                                        }
                                        CompileAction(new ParserAction(ParserToken.Declaration, new List <Expression>()
                                        {
                                            e
                                        }), new OList <ParserAction>(0));

                                        goto _skipline;
                                    }

                                    break;

                                case ExpressionToken.LeftParen: {
                                    //it is an expression.

                                    expr_ew.NextOrThrow();
                                    var    expression = Expression.ParseExpression(expr_ew);
                                    object val        = EvaluateObject(expression, baseLine);
                                    if (val is ReferenceData rd)     //make sure references are unpacked
                                    {
                                        val = rd.UnpackReference(Context);
                                    }
                                    expr_ew.IsCurrentOrThrow(ExpressionToken.RightParen);
                                    var emit = val is Data d?d.Emit() : val.ToString();

                                    copy = copy
                                           .Remove(offset + hashtag.Match.Index, expr_ew.Current.Match.Index + 1 - hashtag.Match.Index)
                                           .Insert(offset + hashtag.Match.Index, emit);
                                    last_access_index = hashtag.Match.Index + emit.Length;
                                    changed           = true;
                                    goto _restart;
                                }

                                case ExpressionToken.NumberLiteral: {
                                    if (expr_ew.HasNext && expr_ew.PeakNext.Token == ExpressionToken.LeftBracet)
                                    {
                                        //it is an indexer call.
                                        //todo indexer
                                    }
                                    else
                                    {
                                        //it is a simple emit
                                        var    key = $"#{expr_ew.Current.Match.Value}";
                                        object val = vars[$"__{expr_ew.Current.Match.Value}__"];

                                        copy    = Regex.Replace(copy, Regex.Escape(key), _emit(val));
                                        changed = true;
                                    }

                                    goto _restart;
                                }

                                default:
                                    continue;
                                }
                            }

                            //incase it is escaped, continue.
                        } while (ew.Next());
                    }

_nextline:
                    //cleanup escapes
                    copy = copy.Replace("\\#", "#");

                    baseLine.ReplaceOrAppend(copy + (copy.EndsWith("\n") ? "" : "\n"));
                    _skipline :;
                }

                foreach (var variable in variables)
                {
                    Context.Variables.Remove(variable);
                }
            }

            if (expr.Depth == 0)
            {
                Context.Variables.Remove("i");
            }
            else
            {
                Context.Variables.Remove($"i{expr.Depth}");
            }
            for (var i = 0; i < iterateThose.Count; i++)
            {
                Context.Variables.Remove($"__{i + 1 + expr.Depth * 100}__");
            }

            if (!baseLine.ContentWasModified)
            {
                baseLine.MarkedForDeletion = true;
            }

            IList parseExpr(Expression arg)
            {
                var ev = EvaluateObject(arg, baseLine);

                if (ev is ReferenceData d)
                {
                    ev = d.UnpackReference(Context);
                }

                if (ev is StringScalar ss)
                {
                    return(ss.ToCharArray());
                }

                if (ev is NetObject no)
                {
                    ev = no.Value;
                }

                return((IList)ev);
            }

            void unpackPackedArguments()
            {
                //unpack PackedArguments
                for (var i = iterateThose.Count - 1; i >= 0; i--)
                {
                    if (iterateThose[i] is PackedArguments pa)
                    {
                        iterateThose.InsertRange(i, pa.Objects.Select(o => (IList)o));
                    }
                }

                iterateThose.RemoveAll(it => it is PackedArguments);
            }
        }
        public static ParsedCode Parse(string code, Dictionary <string, object> variables = null, InterpreterOptions opts = null)
        {
            code = code.Replace("\r", ""); //todo this might cause throws in osx.
            StringSpan output_sb;
            var        output = new LineBuilder(output_sb = StringSpan.Create(code));

            variables = variables ?? new Dictionary <string, object>();
            opts      = opts ?? new InterpreterOptions();
            // Define the context of our expression
            var ew = new ExpressionWalker(ExpressionLexer.Tokenize(code).Where(t => t.Token != ExpressionToken.UnixNewLine).ToList());

            //if no tokens detected
            if (ew.Count == 0)
            {
                return(new ParsedCode()
                {
                    OriginalCode = code, Output = output, Variables = variables, Options = opts, ParseActions = new OList <ParserAction>(0)
                });
            }

            var parserTokens = new OList <ParserAction>();

            do
            {
                var current = ew.Current;

                switch (ew.Current.Token)
                {
                case ExpressionToken.Mod: {
                    //copypastes.Add(sb.Substring(from, ew.Current.Match.Index + ew.Current.Match.Length - 1));
                    current = ew.NextToken();
                    if (current == null)
                    {
                        break;
                    }
                    switch (current.Token)
                    {
                    case ExpressionToken.Template: {
                        //this is import %import namespace.type as aliasnmae
                        var template = TemplateExpression.Parse(ew);
                        parserTokens += new ParserAction(ParserToken.Template, output.MarkDeleteLinesRelated(template.Matches()), template);
                        break;
                    }

                    case ExpressionToken.Import: {
                        //this is import %import namespace.type as aliasnmae
                        var import = ImportExpression.Parse(ew);
                        parserTokens += new ParserAction(ParserToken.Import, output.MarkDeleteLinesRelated(import.Matches()), import);
                        break;
                    }

                    case ExpressionToken.Literal: {
                        //this is variable declaration %varname = expr
                        var peak = ew.PeakNext.Token;
                        if (peak == ExpressionToken.Equal)
                        {
                            var expr = VariableDeclarationExpression.Parse(ew);
                            parserTokens += new ParserAction(ParserToken.Declaration, output.MarkDeleteLinesRelated(expr.Matches()), expr);
                        }
                        else
                        {
                            break;
                        }

                        break;
                    }

                    case ExpressionToken.LeftParen: {
                        //it is an expression block %(expr)
                        ew.NextOrThrow();

                        var expr = Expression.ParseExpression(ew);
                        parserTokens += new ParserAction(ParserToken.Expression, output.MarkDeleteLinesRelated(expr.Matches()), expr);
                        ew.IsCurrentOrThrow(ExpressionToken.RightParen);
                        ew.Next();
                        break;
                    }

                    case ExpressionToken.Foreach: {
                        parserTokens += ForeachExpression.Parse(ew, code, output);
                        break;
                    }

                    case ExpressionToken.CommentRow:
                        //skip untill we hit newline
                        ew.SkipForwardWhile(t => t.Token != ExpressionToken.NewLine);         //todo test
                        break;

                    default: {
                        var precentageLine = output.GetLineAt(ew.PeakBack.Match.Index);
                        if (precentageLine.CleanContent() != "%")
                        {
                            throw new UnexpectedTokenException(current.Token, $"The given token was not expected at line {precentageLine.LineNumber}, offset: {current.Match.Index - precentageLine.StartIndex}");
                        }
                        break;
                    }
                    }

                    break;
                }

                default:
                    break;
                }
            } while (ew.Next());

            return(new ParsedCode()
            {
                OriginalCode = code,
                Output = output,
                Variables = variables,
                ETokens = (List <TokenMatch>)ew.Walking,
                ParseActions = parserTokens,
                Options = opts
            });
        }