Beispiel #1
0
        /// <summary>
        /// Parses a list of parameters into a list of script objects.
        /// </summary>
        internal SObject[] ParseParameters(string exp)
        {
            // When there is only empty space in the parameter expression, we can save the search and just return an empty array:
            if (string.IsNullOrWhiteSpace(exp))
            {
                return new SObject[] { }
            }
            ;

            var parameters = new List <SObject>();

            var     index = 0;
            var     depth = 0;
            string  parameter;
            SObject parameterObject;
            var     parameterStartIndex = 0;
            var     escaper             = new LeftToRightStringEscapeHelper(exp, 0);

            while (index < exp.Length)
            {
                var t = exp[index];

                escaper.CheckStartAt(index);

                if (!escaper.IsString)
                {
                    if (t == '(' || t == '[' || t == '{')
                    {
                        depth++;
                    }
                    else if (t == ')' || t == ']' || t == '}')
                    {
                        depth--;
                    }
                    else if (t == ',' && depth == 0)
                    {
                        parameter = exp.Substring(parameterStartIndex, index - parameterStartIndex);
                        if (!string.IsNullOrWhiteSpace(parameter))
                        {
                            parameterObject = SObject.Unbox(ExecuteStatement(new ScriptStatement(parameter)));
                            parameters.Add(parameterObject);
                        }

                        parameterStartIndex = index + 1;
                    }
                }

                index++;
            }

            parameter = exp.Substring(parameterStartIndex, index - parameterStartIndex);
            if (!string.IsNullOrWhiteSpace(parameter))
            {
                parameterObject = SObject.Unbox(ExecuteStatement(new ScriptStatement(parameter)));
                parameters.Add(parameterObject);
            }

            return(parameters.ToArray());
        }
Beispiel #2
0
        /// <summary>
        /// Returns positions of the given operator in the expression, sorted from left to right.
        /// </summary>
        private static int[] GetOperatorPositions(string exp, string op)
        {
            var operators = new List <int>();

            StringEscapeHelper escaper = new LeftToRightStringEscapeHelper(exp, 0);
            var depth = 0;
            var index = 0;

            while (index < exp.Length)
            {
                var t = exp[index];
                escaper.CheckStartAt(index);

                if (!escaper.IsString)
                {
                    if (t == '(' || t == '[' || t == '{')
                    {
                        depth--;
                    }
                    else if (t == ')' || t == ']' || t == '}')
                    {
                        depth++;
                    }

                    if (t == op[0] && depth == 0)
                    {
                        if (op.Length > 1 && index + op.Length - 1 < exp.Length)
                        {
                            var correctOperator = true;
                            for (var i = 1; i < op.Length; i++)
                            {
                                if (exp[index + i] != op[i])
                                {
                                    correctOperator = false;
                                }
                            }

                            if (correctOperator)
                            {
                                operators.Add(index);
                            }
                        }
                        else if (op.Length == 1)
                        {
                            operators.Add(index);
                        }
                    }
                }
                index++;
            }

            return(operators.ToArray());
        }
        private SObject ExecuteAssignment(ScriptStatement statement)
        {
            var exp = statement.Code;

            var leftSide           = "";
            var rightSide          = "";
            var assignmentOperator = "";

            // Get left and right side of the assignment:
            {
                var depth = 0;
                var index = 0;
                StringEscapeHelper escaper = new LeftToRightStringEscapeHelper(exp, 0);

                while (index < exp.Length && assignmentOperator.Length == 0)
                {
                    var t = exp[index];
                    escaper.CheckStartAt(index);

                    if (!escaper.IsString)
                    {
                        if (t == '(' || t == '[' || t == '{')
                        {
                            depth++;
                        }
                        else if (t == ')' || t == ']' || t == '}')
                        {
                            depth--;
                        }
                        else if (t == '=' && depth == 0)
                        {
                            var previous = ' ';
                            if (index > 0)
                            {
                                previous = exp[index - 1];
                            }

                            if (previous == '+' || previous == '-' || previous == '/' || previous == '*')
                            {
                                assignmentOperator = previous.ToString();
                                leftSide           = exp.Substring(0, index - 1).TrimEnd();
                            }
                            else
                            {
                                assignmentOperator = "=";
                                leftSide           = exp.Substring(0, index).TrimEnd();
                            }

                            rightSide = exp.Substring(index + 1).TrimStart();
                        }
                    }

                    index++;
                }
            }

            // This means it's a function call, which cannot be assigned to:
            if (leftSide.EndsWith(")") || leftSide.Length == 0)
            {
                return(ErrorHandler.ThrowError(ErrorType.ReferenceError, ErrorHandler.MessageReferenceInvalidAssignmentLeft));
            }

            var isIndexer = false;
            var host      = "";
            var member    = "";

            if (leftSide.EndsWith("]"))
            {
                var indexerStartIndex = 0;
                var index             = leftSide.Length - 1;
                var depth             = 0;

                StringEscapeHelper escaper = new RightToLeftStringEscapeHelper(leftSide, index);
                while (index > 0 && !isIndexer)
                {
                    var t = leftSide[index];
                    escaper.CheckStartAt(index);

                    if (!escaper.IsString)
                    {
                        if (t == '(' || t == '{')
                        {
                            depth--;
                        }
                        else if (t == ')' || t == ']' || t == '}')
                        {
                            depth++;
                        }
                        else if (t == '[')
                        {
                            depth--;
                            if (depth == 0 && index > 0)
                            {
                                isIndexer         = true;
                                indexerStartIndex = index;
                            }
                        }
                    }

                    index--;
                }

                if (isIndexer)
                {
                    member = leftSide.Substring(indexerStartIndex + 1);
                    member = member.Remove(member.Length - 1, 1);
                    host   = leftSide.Remove(indexerStartIndex);
                }
            }
            else
            {
                var foundMember = false;

                if (leftSide.Contains("."))
                {
                    var index = leftSide.Length - 1;
                    var depth = 0;
                    StringEscapeHelper escaper = new RightToLeftStringEscapeHelper(leftSide, index);

                    while (index > 0 && !foundMember)
                    {
                        var t = leftSide[index];
                        escaper.CheckStartAt(index);

                        if (!escaper.IsString)
                        {
                            if (t == '(' || t == '[' || t == '{')
                            {
                                depth--;
                            }
                            else if (t == ')' || t == ']' || t == '}')
                            {
                                depth++;
                            }
                            else if (t == '.' && depth == 0)
                            {
                                foundMember = true;
                                host        = leftSide.Substring(0, index);
                                member      = leftSide.Remove(0, index + 1);
                            }
                        }

                        index--;
                    }
                }

                if (!foundMember)
                {
                    host   = SObject.LiteralThis;
                    member = leftSide;
                }
            }

            // When it's an indexer, we parse it as statement:
            var accessor = isIndexer ? SObject.Unbox(ExecuteStatement(new ScriptStatement(member))) : CreateString(member);

            var memberHost = ExecuteStatement(new ScriptStatement(host));
            var value      = SObject.Unbox(ExecuteStatement(new ScriptStatement(rightSide)));

            if (assignmentOperator == "=")
            {
                memberHost.SetMember(this, accessor, isIndexer, value);
            }
            else
            {
                var memberContent = memberHost.GetMember(this, accessor, isIndexer);

                var result = "";

                switch (assignmentOperator)
                {
                case "+":
                    result = ObjectOperators.AddOperator(this, memberContent, value);
                    break;

                case "-":
                    result = ObjectOperators.SubtractOperator(this, memberContent, value);
                    break;

                case "*":
                    result = ObjectOperators.MultiplyOperator(this, memberContent, value);
                    break;

                case "/":
                    result = ObjectOperators.DivideOperator(this, memberContent, value);
                    break;
                }

                memberHost.SetMember(this, accessor, isIndexer, ToScriptObject(result));
            }

            return(value);
        }
Beispiel #4
0
        internal static ScriptStatement[] GetStatements(ScriptProcessor processor, string code)
        {
            var statements = new List <ScriptStatement>();

            var statement = new StringBuilder();

            var index               = 0;
            var depth               = 0;
            var lineNumber          = 1;
            var lineNumberBuffer    = 0;     // buffers line counts from the start of a statement.
            var isComment           = false;
            var isControlStatement  = false; // If the current statement is a control statement.
            var isCompoundStatement = false; // If the current statement is bunch of statements wrapped in { ... }

            StringEscapeHelper escaper = new LeftToRightStringEscapeHelper(code, 0);

            while (index < code.Length)
            {
                var t = code[index];

                if (!isComment)
                {
                    escaper.CheckStartAt(index);
                }
                else
                {
                    escaper.JumpTo(index);
                }

                if (!escaper.IsString)
                {
                    // Check if a block comment is starting (/*):
                    if (!isComment && t == '/' && index + 1 < code.Length && code[index + 1] == '*')
                    {
                        isComment = true;
                        index++; // Jump over * char.
                    }

                    if (!isComment)
                    {
                        // Check if a line comment is starting (//):
                        if (t == '/' && index + 1 < code.Length && code[index + 1] == '/')
                        {
                            // We jump to the end of the line and ignore everything between the current index and the end of the line:
                            if (code.IndexOf("\n", index + 1, StringComparison.Ordinal) > -1)
                            {
                                index = code.IndexOf("\n", index + 1, StringComparison.Ordinal) + 1;
                            }
                            else
                            {
                                index = code.Length;
                            }

                            continue;
                        }

                        statement.Append(t);

                        if (t == '(')
                        {
                            depth++;
                        }
                        else if (t == ')')
                        {
                            depth--;

                            if (isControlStatement)
                            {
                                var statementStr = statement.ToString();
                                var s            = statementStr.Trim();
                                if (s.StartsWith("if") || s.StartsWith("else if") || s.StartsWith("function") || s.StartsWith("for") || s.StartsWith("while") || s.StartsWith("catch"))
                                {
                                    var extraLines = statementStr.Replace("\r", "").TakeWhile(c => c == '\n').Count(); // count the starting lines
                                    statements.Add(new ScriptStatement(s, GetStatementType(s, true), lineNumber + extraLines));
                                    statement.Clear();
                                    lineNumber      += lineNumberBuffer;
                                    lineNumberBuffer = 0;

                                    isControlStatement = false;
                                }
                            }
                        }
                        else if (t == '{')
                        {
                            depth++;

                            if (depth == 1)
                            {
                                var statementStr = statement.ToString();
                                var s            = statementStr.Trim();

                                if (isControlStatement)
                                {
                                    var extraLines = statementStr.Replace("\r", "").TakeWhile(c => c == '\n').Count(); // count the starting lines

                                    s = s.Remove(s.Length - 1, 1).Trim();
                                    statements.Add(new ScriptStatement(s, GetStatementType(s, true), lineNumber + extraLines));

                                    lineNumber      += lineNumberBuffer;
                                    lineNumberBuffer = 0;

                                    statement.Clear();
                                    statement.Append('{');
                                    isCompoundStatement = true;
                                    isControlStatement  = false;
                                }
                                else
                                {
                                    if (s == "{")
                                    {
                                        isCompoundStatement = true;
                                    }
                                }
                            }
                        }
                        else if (t == '}')
                        {
                            depth--;
                            if (depth == 0 && isCompoundStatement)
                            {
                                // This could also be an object declaration...
                                // In the case that the statement started with "{" (example statement: {} + []), this will happen.
                                // To check if this is in fact an object, we look right and see if there is:
                                //   - an operator => object ("+*-/&|<>.[(")
                                //   - nothing => statement

                                var foundOperator = false;
                                var charFindIndex = index + 1;

                                while (!foundOperator && charFindIndex < code.Length)
                                {
                                    var testChar = code[charFindIndex];
                                    if (ObjectDiscoverToken.Contains(testChar))
                                    {
                                        if (testChar == '/' && // next statement is actually a comment, not a / operator (followed by / or *)
                                            charFindIndex + 1 < code.Length &&
                                            (code[charFindIndex + 1] == '/' || code[charFindIndex + 1] == '*'))
                                        {
                                            charFindIndex = code.Length;
                                        }
                                        else
                                        {
                                            foundOperator = true;
                                        }
                                    }
                                    else if (!char.IsWhiteSpace(testChar)) // We found something that is not an operator or whitespace, so this is the end of a compound statement.
                                    {
                                        charFindIndex = code.Length;
                                    }

                                    charFindIndex++;
                                }

                                if (!foundOperator)
                                {
                                    var statementStr = statement.ToString();
                                    var extraLines   = statementStr.Replace("\r", "").TakeWhile(c => c == '\n').Count(); // count the starting lines
                                    var s            = statementStr.Trim();
                                    statements.Add(new ScriptStatement(s, StatementType.Executable, lineNumber + extraLines)
                                    {
                                        IsCompoundStatement = true
                                    });
                                    statement.Clear();
                                    lineNumber      += lineNumberBuffer;
                                    lineNumberBuffer = 0;
                                }

                                isCompoundStatement = false;
                            }
                        }
                        else if (t == ';' && depth == 0)
                        {
                            var statementStr = statement.ToString();
                            var extraLines   = statementStr.Replace("\r", "").TakeWhile(c => c == '\n').Count(); // count the starting lines
                            var s            = statementStr.Trim().TrimEnd(';');
                            statements.Add(new ScriptStatement(s, GetStatementType(s, false), lineNumber + extraLines));
                            statement.Clear();
                            lineNumber      += lineNumberBuffer;
                            lineNumberBuffer = 0;
                        }
                        else if (!isCompoundStatement && !isControlStatement)
                        {
                            var statementStr = statement.ToString();
                            var extraLines   = statementStr.Replace("\r", "").TakeWhile(c => c == '\n').Count(); // count the starting lines
                            var s            = statementStr.TrimStart();

                            var nextChar = 'X'; // Set to something that is not matching with the condition below.
                            if (code.Length > index + 1)
                            {
                                nextChar = code[index + 1];
                            }

                            // Check if it's actually a control statement by looking if the next char matches (whitespace, ";" or "(")
                            if ((char.IsWhiteSpace(nextChar) || nextChar == ';' || nextChar == '(') && ControlStatements.Contains(s))
                            {
                                isControlStatement = true;
                                if (s.StartsWith("else"))
                                {
                                    if (index + 3 < code.Length)
                                    {
                                        var check = code.Substring(index + 1, 3);
                                        if (check != " if")
                                        {
                                            statements.Add(new ScriptStatement("else", StatementType.Else, lineNumber + extraLines));
                                            statement.Clear();
                                            isControlStatement = false;
                                            lineNumber        += lineNumberBuffer;
                                            lineNumberBuffer   = 0;
                                        }
                                    }
                                }
                            }
                        }
                    }
                    else
                    {
                        // Check if a block comment is ending (/*):
                        if (t == '*' && index + 1 < code.Length && code[index + 1] == '/')
                        {
                            isComment = false;
                            index++; // Jump over / char.
                        }
                    }
                }
                else
                {
                    statement.Append(t);
                }

                if (t == '\n')
                {
                    lineNumberBuffer++;
                }

                index++;
            }

            if (isCompoundStatement)
            {
                processor.ErrorHandler.ThrowError(ErrorType.SyntaxError, ErrorHandler.MessageSyntaxMissingEndOfCompoundStatement);
            }

            if (isComment)
            {
                processor.ErrorHandler.ThrowError(ErrorType.SyntaxError, ErrorHandler.MessageSyntaxUnterminatedComment);
            }

            if (isControlStatement)
            {
                processor.ErrorHandler.ThrowError(ErrorType.SyntaxError, ErrorHandler.MessageSyntaxExpectedExpression, "end of script");
            }

            // an executable statement not closed with ";" is getting added here:
            var leftOver = statement.ToString().Trim();

            if (leftOver.Length > 0)
            {
                statements.Add(new ScriptStatement(leftOver, GetStatementType(leftOver, false), lineNumber));
            }

            return(statements.ToArray());
        }
Beispiel #5
0
        /// <summary>
        /// Captures an element right from the starting index.
        /// </summary>
        private static ElementCapture CaptureRight(string exp, int index)
        {
            if (string.IsNullOrWhiteSpace(exp))
            {
                return new ElementCapture()
                       {
                           Length = 0, StartIndex = 0, Identifier = "", Depth = 0
                       }
            }
            ;

            var identifier         = "";
            var foundSeparatorChar = false;
            var depth = 0;
            StringEscapeHelper escaper = new LeftToRightStringEscapeHelper(exp, index);

            while (index < exp.Length && !foundSeparatorChar)
            {
                var t = exp[index];

                escaper.CheckStartAt(index);

                if (!escaper.IsString)
                {
                    if (t == ')' || t == ']' || t == '}')
                    {
                        depth--;
                    }
                    else if (t == '(' || t == '[' || t == '{')
                    {
                        depth++;
                    }
                }
                if (t == '-' && string.IsNullOrWhiteSpace(identifier))
                {
                    identifier += "-";
                }
                else
                {
                    if (!escaper.IsString && depth == 0)
                    {
                        if (t == '.')
                        {
                            // Check if the '.' is not a decimal separator:
                            if (!Regex.IsMatch(identifier.Trim(), RegexNumleftdot))
                            {
                                foundSeparatorChar = true;
                            }
                        }
                        else if (IdentifierSeparators.Contains(t))
                        {
                            foundSeparatorChar = true;
                        }
                    }

                    // Append the char to the identifier:
                    if (!foundSeparatorChar)
                    {
                        identifier += t;
                    }
                }

                index++;
            }

            if (foundSeparatorChar)
            {
                return new ElementCapture()
                       {
                           StartIndex = index - 1 - identifier.Length, Length = identifier.Length, Identifier = identifier.Trim(), Depth = depth
                       }
            }
            ;
            else
            {
                return new ElementCapture()
                       {
                           StartIndex = index - identifier.Length, Length = identifier.Length, Identifier = identifier.Trim(), Depth = depth
                       }
            };
        }
Beispiel #6
0
        /// <summary>
        /// Resolves parentheses and adds ".call" to direct function calls on variables.
        /// </summary>
        private string ResolveParentheses(string exp)
        {
            if (exp.Contains("(") && exp.Contains(")"))
            {
                var index = 0;
                var depth = 0;
                var parenthesesStartIndex = -1;
                var newExpression         = new StringBuilder();
                var escaper = new LeftToRightStringEscapeHelper(exp, 0);

                while (index < exp.Length)
                {
                    var t = exp[index];
                    escaper.CheckStartAt(index);

                    if (!escaper.IsString)
                    {
                        if (t == '{' || t == '[')
                        {
                            depth++;
                            if (parenthesesStartIndex == -1)
                            {
                                newExpression.Append(t);
                            }
                        }
                        else if (t == '}' || t == ']')
                        {
                            depth--;
                            if (parenthesesStartIndex == -1)
                            {
                                newExpression.Append(t);
                            }
                        }
                        else if (t == '(')
                        {
                            if (depth == 0 && parenthesesStartIndex == -1)
                            {
                                var capture    = CaptureLeft(newExpression.ToString(), newExpression.Length - 1);
                                var identifier = capture.Identifier;
                                if (capture.Depth == 0 || identifier == "")
                                {
                                    if (identifier.Length == 0)
                                    {
                                        parenthesesStartIndex = index;
                                    }
                                    else
                                    {
                                        var testExp = newExpression.ToString().Remove(capture.StartIndex).Trim();

                                        if (testExp.Length > 0 && testExp.Last() == '.' || StatementProcessor.ControlStatements.Contains(identifier) || identifier.StartsWith("new "))
                                        {
                                            newExpression.Append(t);
                                        }
                                        else
                                        {
                                            newExpression.Append(".call" + t);
                                        }
                                        depth++;
                                    }
                                }
                                else
                                {
                                    newExpression.Append(t);
                                    depth++;
                                }
                            }
                            else
                            {
                                depth++;
                                if (parenthesesStartIndex == -1)
                                {
                                    newExpression.Append(t);
                                }
                            }
                        }
                        else if (t == ')')
                        {
                            if (depth == 0 && parenthesesStartIndex > -1)
                            {
                                var parenthesesCode = exp.Substring(parenthesesStartIndex + 1, index - parenthesesStartIndex - 1);

                                if (parenthesesCode.Length > 0)
                                {
                                    if (parenthesesCode.Contains("=>") && Regex.IsMatch(parenthesesCode, RegexLambda))
                                    {
                                        newExpression.Append(BuildLambdaFunction(parenthesesCode));
                                    }
                                    else
                                    {
                                        var returnObject = ExecuteStatement(new ScriptStatement(parenthesesCode));
                                        newExpression.Append(returnObject.ToScriptObject());
                                    }
                                }
                                else
                                {
                                    // check for lambda statement
                                    // if this turns out to be a lambda statement, then the whole expression is this lambda statement.
                                    // therefore, discard everything and just add the converted function code taken from the lambda statement.
                                    var nonParenthesesCode = exp.Substring(index + 1).Trim();
                                    if (nonParenthesesCode.StartsWith("=>"))
                                    {
                                        return(BuildLambdaFunction(exp));
                                    }
                                }

                                parenthesesStartIndex = -1;
                            }
                            else
                            {
                                depth--;
                                if (parenthesesStartIndex == -1)
                                {
                                    newExpression.Append(t);
                                }
                            }
                        }
                        else
                        {
                            if (parenthesesStartIndex == -1)
                            {
                                newExpression.Append(t);
                            }
                        }
                    }
                    else
                    {
                        if (parenthesesStartIndex == -1)
                        {
                            newExpression.Append(t);
                        }
                    }

                    index++;
                }

                return(newExpression.ToString());
            }

            return(exp);
        }