Пример #1
0
        public static ScriptNestedExpression Wrap(ScriptExpression expression, bool transferTrivia = false)
        {
            if (expression == null)
            {
                throw new ArgumentNullException(nameof(expression));
            }
            var nested = new ScriptNestedExpression()
            {
                Span       = expression.Span,
                Expression = expression
            };

            if (!transferTrivia)
            {
                return(nested);
            }

            var firstTerminal = expression.FindFirstTerminal();

            firstTerminal?.MoveLeadingTriviasTo(nested.OpenParen);

            var lastTerminal = expression.FindLastTerminal();

            lastTerminal?.MoveTrailingTriviasTo(nested.CloseParen, true);

            return(nested);
        }
 public BinaryExpressionOrOperator(ScriptExpression expression, FunctionCallKind kind)
 {
     Expression    = expression;
     Operator      = 0;
     OperatorToken = null;
     CallKind      = kind;
 }
Пример #3
0
        private ScriptExpression TransformKeyword(ScriptExpression leftOperand)
        {
            // In case we are in liquid and we are assigning to a textscript keyword, we escape the variable with a nested expression
            if (_isLiquid && leftOperand is IScriptVariablePath &&
                IsTextScriptKeyword(((IScriptVariablePath)leftOperand).GetFirstPath()) &&
                !(leftOperand is ScriptNestedExpression))
            {
                ScriptNestedExpression nestedExpression = new ScriptNestedExpression
                {
                    Expression = leftOperand,
                    Span       = leftOperand.Span
                };

                // If the variable has any trivia, we copy them to the NestedExpression instead
                if (_isKeepTrivia && leftOperand.Trivias != null)
                {
                    nestedExpression.Trivias = leftOperand.Trivias;
                    leftOperand.Trivias      = null;
                }

                return(nestedExpression);
            }

            return(leftOperand);
        }
Пример #4
0
        public void Literal_Float(string value)
        {
            float            i    = Single.Parse(value);
            ScriptExpression expr = new ScriptExpression(value);

            Assert.That((float)expr.Evaluate(null), Is.EqualTo(i));
        }
Пример #5
0
 public Branch(string branchName, uint textPosition, ScriptExpression condition, ScriptExpression targetScript)
 {
     BranchName   = branchName;
     TextPosition = textPosition;
     Condition    = condition;
     TargetScript = targetScript;
 }
Пример #6
0
        public void Literal_Bool(string value)
        {
            bool             i    = Boolean.Parse(value);
            ScriptExpression expr = new ScriptExpression(value);

            Assert.That((bool)expr.Evaluate(null), Is.EqualTo(i));
        }
Пример #7
0
        private void CreateScriptReference(ScriptInfo info, ushort returnTypeOpcode, uint textPosition, short lineNumber)
        {
            var scriptReference = new ScriptExpression
            {
                Index = _currentIndex,
                Opcode = info.Opcode,
                ReturnType = returnTypeOpcode,
                Type = ScriptExpressionType.ScriptReference,
                Next = DatumIndex.Null,
                StringOffset = textPosition,
                Value = new LongExpressionValue(_currentIndex.Next),
                LineNumber = lineNumber
            };
            OpenDatumAddExpressionIncrement(scriptReference);

            var nameExpression = new ScriptExpression
            {
                Index = _currentIndex,
                Opcode = info.Opcode,
                ReturnType = _opcodes.GetTypeInfo("function_name").Opcode,
                Type = ScriptExpressionType.Expression,
                Next = DatumIndex.Null,
                StringOffset = _strings.Cache(info.Name),
                Value = new LongExpressionValue(0),
                LineNumber = lineNumber
            };
            OpenDatumAddExpressionIncrement(nameExpression);
        }
Пример #8
0
        public override void ExitCond(HS_Gen1Parser.CondContext context)
        {
            if (_debug)
            {
                _logger.Cond(context, CompilerContextAction.Exit);
            }

            // Link to the compiler generated begin call of the last open cond group.
            LinkDatum();

            // Add the final expression of the cond construct. Not sure why the official Blam Script compiler adds these.
            ushort typeOpcode = _opcodes.GetTypeInfo(_condReturnType).Opcode;
            var expression = new ScriptExpression
            {
                Index = _currentIndex,
                Opcode = typeOpcode,
                ReturnType = typeOpcode,
                Type = ScriptExpressionType.Expression,
                Next = DatumIndex.Null,
                StringOffset = context.GetCorrectTextPosition(_missingCarriageReturnPositions),
                Value = new LongExpressionValue(0),
                LineNumber = 0
            };
            AddExpressionIncrement(expression);

            // Open the first group.
            int firstGroupIndex = _condIndeces.Pop();
            OpenDatum(firstGroupIndex);
        }
Пример #9
0
        /// <summary>
        /// Returns the caller hierarchy
        /// </summary>
        /// <param name="scriptExpression">Script expression</param>
        /// <returns>Caller hierarchy</returns>
        public static List <string> GetCallerHierarchy(ScriptExpression scriptExpression)
        {
            List <string> hierarchy = new List <string>();

            ScriptMemberExpression memberExpression = scriptExpression as ScriptMemberExpression;

            while (memberExpression != null)
            {
                hierarchy.Add(memberExpression.Member.ToString());

                if (memberExpression.Target is ScriptMemberExpression)
                {
                    memberExpression = memberExpression.Target as ScriptMemberExpression;
                }
                else if (memberExpression.Target is ScriptVariable)
                {
                    ScriptVariable rootVariable = memberExpression.Target as ScriptVariable;
                    hierarchy.Add(rootVariable.Name);
                    break;
                }
                else
                {
                    break;
                }
            }

            hierarchy.Reverse();
            return(hierarchy);
        }
Пример #10
0
        private void CreateInitialBegin(string returnType, uint textPosition)
        {
            FunctionInfo info = _opcodes.GetFunctionInfo("begin").First();

            // Create the begin call.
            var beginCall = new ScriptExpression
            {
                Index = _currentIndex,
                Opcode = info.Opcode,
                ReturnType = _opcodes.GetTypeInfo(returnType).Opcode,
                Type = ScriptExpressionType.Group,
                Next = DatumIndex.Null,
                StringOffset = textPosition,
                Value = new LongExpressionValue(_currentIndex.Next),
                LineNumber = 0
            };
            AddExpressionIncrement(beginCall);

            // Create the function name.
            var beginName = new ScriptExpression
            {
                Index = _currentIndex,
                Opcode = info.Opcode,
                ReturnType = _opcodes.GetTypeInfo("function_name").Opcode,
                Type = ScriptExpressionType.Expression,
                Next = DatumIndex.Null,
                StringOffset = _strings.Cache(info.Name),
                Value = new LongExpressionValue(0),
                LineNumber = 0
            };
            OpenDatumAddExpressionIncrement(beginName);
        }
Пример #11
0
        public void Literal_String(string value)
        {
            string           i    = value.Trim('\"');
            ScriptExpression expr = new ScriptExpression(value);

            Assert.That((string)expr.Evaluate(null), Is.EqualTo(i));
        }
Пример #12
0
        public void Operation_Math()
        {
            ScriptExpression expr;

            Pair <string, Val>[] test = new Pair <string, Val>[]
            {
                new Pair <string, Val>("1 + 1", new Val(1 + 1)),
                new Pair <string, Val>("2 - 3", new Val(2 - 3)),
                new Pair <string, Val>("4 * 5.2", new Val(4 * 5.2f)),
                new Pair <string, Val>("10 / 0.25", new Val(10 / 0.25f)),
                new Pair <string, Val>("10 % 3", new Val(10 % 3)),
                new Pair <string, Val>("2 * (4 + 5)", new Val(2 * (4 + 5))),
                new Pair <string, Val>("3 - (6 - 2 / 2)", new Val(3 - (6 - 2 / 2))),
                new Pair <string, Val>("((2 + 2) * (1.0 / 2)) - ((2 + 2) / (2 / 2))", new Val(((2 + 2) * (1.0f / 2)) - ((2 + 2) / (2 / 2)))),

                // int division 1 / 2 -> 0
                new Pair <string, Val>("((2 + 2) * (1 / 2)) - ((2 + 2) / (2 / 2))", new Val(((2 + 2) * (1 / 2)) - ((2 + 2) / (2 / 2)))),
            };

            foreach (Pair <string, Val> p in test)
            {
                expr = new ScriptExpression(p.t);
                Console.Write("{0} = {1}".F(p.t, p.u.Value));
                Assert.AreEqual(p.u, expr.Evaluate(null));
                Console.WriteLine(" ... OK!");
            }
        }
Пример #13
0
        private void FunctionOpcodesFromExpressions(ScriptExpressionTable expressions, SortedDictionary <int, string> dict)
        {
            foreach (ScriptExpression exp in expressions.ExpressionsAsReadonly)
            {
                if (exp.Type == ScriptExpressionType.Group)
                {
                    DatumIndex       index = new DatumIndex(exp.Value.UintValue);
                    ScriptExpression name  = expressions.FindExpression(index);

                    if (exp.Opcode != name.Opcode)
                    {
                        Console.WriteLine($"Warning: Non-matching opcodes! Call Opcode: {exp.Opcode} Function Name Opcode: {name.Opcode}");
                    }

                    if (dict.TryGetValue(name.Opcode, out string funcName) && funcName != name.StringValue)
                    {
                        Console.WriteLine($"Warning: Duplicate opcodes! Opcode: {name.Opcode.ToString("X3")} Name: \"{name.StringValue}\"\n");
                    }
                    else
                    {
                        dict[name.Opcode] = name.StringValue;
                    }
                }
            }
        }
Пример #14
0
 /// <summary>
 /// Sets the target expression with the specified value.
 /// </summary>
 /// <param name="target">The target expression.</param>
 /// <param name="value">The value.</param>
 /// <exception cref="System.ArgumentNullException">If target is null</exception>
 public void SetValue(ScriptExpression target, object value)
 {
     if (target == null)
     {
         throw new ArgumentNullException(nameof(target));
     }
     GetOrSetValue(target, value, true, 0);
 }
Пример #15
0
 /// <summary>
 /// Gets the value from the specified expression using the current <see cref="ScriptObject"/> bound to the model context.
 /// </summary>
 /// <param name="target">The expression</param>
 /// <returns>The value of the expression</returns>
 public object GetValue(ScriptExpression target)
 {
     _getOrSetValueLevel++;
     try {
         return(GetOrSetValue(target, null, false));
     } finally {
         _getOrSetValueLevel--;
     }
 }
Пример #16
0
        private void WalkExpressions(ScriptExpression origExp, ScriptExpression modExp)
        {
            ScriptExpression origNext = origExp;
            ScriptExpression modNext  = modExp;

            while (origNext != null && modNext != null)
            {
                if (origNext.Type != modNext.Type)
                {
                    _output.WriteLine($"[CRITICAL] Unequal Expression Types. \"{origNext.Type}\" / \"{modNext.Type}\" "
                                      + $"Original Index: \"{origNext.Index.Index.ToString("X4")}\" "
                                      + $"Modified Index: \"{modNext.Index.Index.ToString("X4")}\"");
                    _output.WriteLine();
                    return;
                }

                switch (origNext.Type)
                {
                case ScriptExpressionType.Expression:
                    CompareNormalExpressions(origNext, modNext);
                    break;

                case ScriptExpressionType.GlobalsReference:
                    CompareGlobalsReferences(origNext, modNext);
                    break;

                case ScriptExpressionType.ScriptReference:
                    CompareScriptReferences(origNext, modNext);
                    break;

                case ScriptExpressionType.ParameterReference:
                    CompareParameterReferences(origNext, modNext);
                    break;

                case ScriptExpressionType.Group:
                    CompareGroups(origNext, modNext);
                    break;

                default:
                    _output.WriteLine($"[Critical] Unrecognized Expression Type {origNext.Type}");
                    break;
                }

                if ((origNext.NextExpression == null && modNext.NextExpression != null) ||
                    (origNext.NextExpression != null && modNext.NextExpression == null))
                {
                    _output.WriteLine("[CRITICAL] Unequal Next Datums. "
                                      + $"Original Index: \"{origNext.Index.Index.ToString("X4")}\" "
                                      + $"Modified Index: \"{modNext.Index.Index.ToString("X4")}\"");
                    _output.WriteLine();
                    return;
                }

                origNext = origNext.NextExpression;
                modNext  = modNext.NextExpression;
            }
        }
Пример #17
0
 private ScriptExpression ExpectAndParseExpression(ScriptNode parentNode, ScriptExpression parentExpression = null, int newPrecedence = 0, string message = null)
 {
     if (StartAsExpression())
     {
         return(ParseExpression(parentNode, parentExpression, newPrecedence));
     }
     LogError(parentNode, CurrentSpan, message ?? $"Expecting <expression> instead of [{Current.Type}]");
     return(null);
 }
Пример #18
0
        public void Literal_Int(string value)
        {
            int i = value.StartsWith("0x")
        ? Int32.Parse(value.Substring(2), NumberStyles.HexNumber)
        : Int32.Parse(value, NumberStyles.Number);
            ScriptExpression expr = new ScriptExpression(value);

            Assert.That((int)expr.Evaluate(null), Is.EqualTo(i));
        }
Пример #19
0
        private bool IsScriptParameter(string expectedReturnType, ParserRuleContext context)
        {
            // This script doesn't have parameters.
            if (_parameterLookup.Count == 0)
            {
                return false;
            }

            string text = context.GetTextSanitized();

            // The script doesn't have a parameter with this name.
            if (!_parameterLookup.TryGetValue(text, out ParameterInfo info))
            {
                return false;
            }

            string returnType = DetermineReturnType(info, expectedReturnType, context);

            if(returnType is null)
            {
                throw new CompilerException($"Failed to determine the return type of the parameter {text}.", context);
            }

            ushort returnTypeOpcode = _opcodes.GetTypeInfo(returnType).Opcode;
            ushort opcode = returnTypeOpcode;

            // (In)Equality functions are special
            if(context.Parent.Parent is HS_Gen1Parser.CallContext grandparent)
            {
                string funcName = grandparent.callID().GetTextSanitized();
                var funcInfo = _opcodes.GetFunctionInfo(funcName);
                if(funcInfo != null)
                {
                    if (funcInfo[0].Group == "Equality" || funcInfo[0].Group == "Inequality")
                    {
                        opcode = GetEqualityArgumentOP(returnTypeOpcode);
                    }
                }
            }

            var expression = new ScriptExpression
            {
                Index = _currentIndex,
                Opcode = opcode,
                ReturnType = returnTypeOpcode,
                Type = ScriptExpressionType.ParameterReference,
                Next = DatumIndex.Null,
                StringOffset = _strings.Cache(info.Name),
                Value = new LongExpressionValue(info.Opcode),
                LineNumber = GetLineNumber(context)
            };
            EqualityPush(info.ReturnType);
            OpenDatumAddExpressionIncrement(expression);

            return true;
        }
Пример #20
0
 private ScriptExpression ExpectAndParseExpression(ScriptNode parentNode, ScriptExpression parentExpression = null, int newPrecedence = 0, string message = null,
                                                   ParseExpressionMode mode = ParseExpressionMode.Default, bool allowAssignment       = true)
 {
     if (StartAsExpression())
     {
         return(ParseExpression(parentNode, parentExpression, newPrecedence, mode, allowAssignment));
     }
     LogError(parentNode, CurrentSpan, message ?? $"Expecting <expression> instead of `{Current.Type}`");
     return(null);
 }
Пример #21
0
        private ScriptExpression ExpectAndParseExpression(ScriptNode parentNode, ScriptExpression parentExpression = null, int newPrecedence = 0, string message = null,
                                                          ParseExpressionMode mode = ParseExpressionMode.Default)
        {
            if (StartAsExpression())
            {
                return(ParseExpression(parentNode, parentExpression, newPrecedence, mode));
            }

            LogError(parentNode, CurrentSpan, message ?? string.Format(RS.ExpectToken, "expression", Current.Type));
            return(null);
        }
Пример #22
0
        internal static ScriptTriggerAction Parse(string expression)
        {
            ScriptExpression script = ScriptExpression.Parse(expression);

            if (script != null)
            {
                return(new ScriptTriggerAction(script));
            }

            return(null);
        }
Пример #23
0
        private ScriptExpression DeNestExpression(ScriptExpression expr)
        {
            if (_flags.HasFlags(ScriptFormatterFlags.MinimizeParenthesisNesting))
            {
                while (expr is ScriptNestedExpression nested)
                {
                    expr = nested.Expression;
                    nested.Expression = null;
                }
            }

            return(expr);
        }
Пример #24
0
        /// <summary>
        /// Pushes the current Datum index to the Datum stack and adds the expression to the table.
        /// </summary>
        /// <param name="expression"></param>
        private void OpenDatumAddExpressionIncrement(ScriptExpression expression)
        {
            int openIndex = _expressions.Count;

            if (_debug)
            {
                _logger.Datum(openIndex, CompilerDatumAction.Open);
            }

            _currentIndex.Increment();
            _openDatums.Push(openIndex);
            _expressions.Add(expression);
        }
Пример #25
0
 /// <summary>
 /// Sets the target expression with the specified value.
 /// </summary>
 /// <param name="target">The target expression.</param>
 /// <param name="value">The value.</param>
 /// <exception cref="System.ArgumentNullException">If target is null</exception>
 public void SetValue(ScriptExpression target, object value)
 {
     if (target == null)
     {
         throw new ArgumentNullException(nameof(target));
     }
     _getOrSetValueLevel++;
     try {
         GetOrSetValue(target, value, true);
     } finally {
         _getOrSetValueLevel--;
     }
 }
        private static bool HasImplicitBinaryExpression(ScriptExpression expression)
        {
            if (expression is ScriptBinaryExpression binaryExpression)
            {
                if (binaryExpression.OperatorToken == null && binaryExpression.Operator == ScriptBinaryOperator.Multiply)
                {
                    return(true);
                }

                return(HasImplicitBinaryExpression(binaryExpression.Left) || HasImplicitBinaryExpression(binaryExpression.Right));
            }
            return(false);
        }
Пример #27
0
        public object Invoke(TemplateContext context, ScriptNode callerContext, ScriptArray arguments, ScriptBlockStatement blockStatement)
        {
            if (arguments == null || arguments.Count == 0)
            {
                throw new ScriptRuntimeException(callerContext.Span, string.Format(RS.BadFunctionInvokeArgEmpty, "where"));
            }

            if (arguments.Count == 1)
            {
                return(arguments[0]);
            }

            // the second argument is a condition
            ScriptFunctionCall funcCall  = (ScriptFunctionCall)callerContext;
            ScriptExpression   condition = funcCall.Arguments[0];

            // hold the result in an array
            ScriptArray result = new ScriptArray();

            // the first argument doesn't have to be an array
            try
            {
                // string is enumerable but we want to treat it as a whole
                if (arguments[0] is string)
                {
                    throw new InvalidCastException();
                }

                IEnumerable array = (IEnumerable)arguments[0];
                foreach (object item in array)
                {
                    if ((bool)ReplaceCondition(condition, item).Evaluate(context))
                    {
                        result.Add(item);
                    }
                }
            }
            catch (InvalidCastException)
            {
                if ((bool)ReplaceCondition(condition, arguments[0]).Evaluate(context))
                {
                    return(arguments[0]);
                }
            }
            catch (Exception ex)
            {
                throw new ScriptParserRuntimeException(callerContext.Span, string.Format("RS.FilterFunctionException"), null, ex);
            }

            return(result);
        }
Пример #28
0
        public void Operation_Compare()
        {
            ScriptExpression expr;
            Random           r = new Random();

            List <Pair <string, Val> > test = new List <Pair <string, Val> >();

            for (int i = 0; i < 8; i++)
            {
                int   i0 = r.Next(-10000, 10000);
                int   i1 = r.Next(-10000, 10000);
                float f0 = ((float)r.NextDouble() - 0.5f) * 20000;
                float f1 = ((float)r.NextDouble() - 0.5f) * 20000;

#pragma warning disable CS1718 // Comparison made to same variable
                test.Add(new Pair <string, Val>("{0} == {1}".F(i0, i0), new Val(i0 == i0)));
                test.Add(new Pair <string, Val>("{0} == {1}".F(i0, i1), new Val(i0 == i1)));
                test.Add(new Pair <string, Val>("{0} != {1}".F(i1, i1), new Val(i1 != i1)));
                test.Add(new Pair <string, Val>("{0} != {1}".F(i0, i1), new Val(i0 != i1)));

                test.Add(new Pair <string, Val>("{0} > {1}".F(i0, i1), new Val(i0 > i1)));
                test.Add(new Pair <string, Val>("{0} < {1}".F(i0, i1), new Val(i0 < i1)));
                test.Add(new Pair <string, Val>("{0} >= {1}".F(i0, i1), new Val(i0 >= i1)));
                test.Add(new Pair <string, Val>("{0} <= {1}".F(i0, i1), new Val(i0 <= i1)));
                test.Add(new Pair <string, Val>("{0} >= {1}".F(i0, i0), new Val(i0 >= i0)));
                test.Add(new Pair <string, Val>("{0} <= {1}".F(i1, i1), new Val(i1 <= i1)));

                test.Add(new Pair <string, Val>("{0} == {1}".F(f0, f0), new Val(f0 == f0)));
                test.Add(new Pair <string, Val>("{0} == {1}".F(f0, f1), new Val(f0 == f1)));
                test.Add(new Pair <string, Val>("{0} != {1}".F(f1, f1), new Val(f1 != f1)));
                test.Add(new Pair <string, Val>("{0} != {1}".F(f0, f1), new Val(f0 != f1)));

                test.Add(new Pair <string, Val>("{0} > {1}".F(f0, f1), new Val(f0 > f1)));
                test.Add(new Pair <string, Val>("{0} < {1}".F(f0, f1), new Val(f0 < f1)));
                test.Add(new Pair <string, Val>("{0} >= {1}".F(f0, f1), new Val(f0 >= f1)));
                test.Add(new Pair <string, Val>("{0} <= {1}".F(f0, f1), new Val(f0 <= f1)));
                test.Add(new Pair <string, Val>("{0} >= {1}".F(f0, f0), new Val(f0 >= f0)));
                test.Add(new Pair <string, Val>("{0} <= {1}".F(f1, f1), new Val(f1 <= f1)));
#pragma warning restore CS1718 // Comparison made to same variable
            }

            foreach (Pair <string, Val> p in test)
            {
                expr = new ScriptExpression(p.t);
                Console.Write("{0} = {1}".F(p.t, p.u.Value));
                Assert.AreEqual(p.u, expr.Evaluate(null));
                Console.WriteLine(" ... OK!");
            }
        }
Пример #29
0
    public string ExecuteLineOrSaveLine(string line)
    {
        ScriptValue result = ScriptValue.NULL;

        if (ScriptExpression.Execute(line, method, out result))
        {
            return(result.ToString());
        }

        if (!string.IsNullOrEmpty(line))
        {
            _src += (line + '\n');
        }
        return("");
    }
Пример #30
0
        public static bool Execute(string ifSrc, ScriptMethod space, out bool result)
        {
            result = false;

            var tempSrc = ifSrc.Trim();

            int fpbPos = tempSrc.IndexOf(Grammar.FPB);
            int fpePos = tool.GrammarTool.ReadPairSignPos(tempSrc, fpbPos + 1, Grammar.FPB, Grammar.FPE);

            if (fpbPos == -1 || fpbPos >= fpePos)
            {
                Logger.Error(tempSrc);
                return(false);
            }
            var nameSrc = tempSrc.Substring(0, fpbPos).Trim();

            if (nameSrc != Grammar.IF)
            {
                Logger.Error(tempSrc);
                return(false);
            }

            var srcCondition = tempSrc.Substring(fpbPos + 1, fpePos - fpbPos - 1).Trim();

            if (string.IsNullOrEmpty(srcCondition))
            {
                Logger.Error(tempSrc);
                return(false);
            }

            ScriptValue tempValue = null;

            if (!ScriptMethodCall.Execute(srcCondition, space, out tempValue))
            {
                if (!ScriptExpression.Execute(srcCondition, space, out tempValue))
                {
                    Logger.Error(tempSrc);
                    return(false);
                }
            }
            if (tempValue.GetValueType() != ScriptValueType.Bool)
            {
                Logger.Error(tempSrc);
                return(false);
            }
            result = (bool)tempValue.GetValue();
            return(true);
        }
Пример #31
0
        private ScriptExpression ParseExpression(ScriptNode parentNode, ref bool hasAnonymousFunction, ScriptExpression parentExpression = null, int precedence = 0)
        {
            int expressionCount = 0;
            expressionLevel++;
            try
            {
                ScriptFunctionCall functionCall = null;
                parseExpression:
                expressionCount++;
                ScriptExpression leftOperand = null;
                switch (Current.Type)
                {
                    case TokenType.Identifier:
                    case TokenType.IdentifierSpecial:
                        leftOperand = ParseVariableOrLiteral();

                        // Special handle of the $$ block delegate variable
                        if (ScriptVariable.BlockDelegate.Equals(leftOperand))
                        {
                            if (expressionCount != 1 || expressionLevel > 1)
                            {
                                LogError("Cannot use block delegate $$ in a nested expression");
                            }

                            if (!(parentNode is ScriptExpressionStatement))
                            {
                                LogError(parentNode, "Cannot use block delegate $$ outside an expression statement");
                            }

                            return leftOperand;
                        }
                        break;
                    case TokenType.Integer:
                        leftOperand = ParseInteger();
                        break;
                    case TokenType.Float:
                        leftOperand = ParseFloat();
                        break;
                    case TokenType.String:
                        leftOperand = ParseString();
                        break;
                    case TokenType.OpenParent:
                        leftOperand = ParseParenthesis(ref hasAnonymousFunction);
                        break;
                    case TokenType.OpenBrace:
                        leftOperand = ParseObjectInitializer();
                        break;
                    case TokenType.OpenBracket:
                        leftOperand = ParseArrayInitializer();
                        break;
                    case TokenType.Not:
                    case TokenType.Minus:
                    case TokenType.Arroba:
                    case TokenType.Plus:
                    case TokenType.Caret:
                        leftOperand = ParseUnaryExpression(ref hasAnonymousFunction);
                        break;
                }

                // Should not happen but in case
                if (leftOperand == null)
                {
                    LogError($"Unexpected token [{Current.Type}] for expression");
                    return null;
                }

                if (leftOperand is ScriptAnonymousFunction)
                {
                    hasAnonymousFunction = true;
                }

                while (!hasAnonymousFunction)
                {
                    // Parse Member expression are expected to be followed only by an identifier
                    if (Current.Type == TokenType.Dot)
                    {
                        var nextToken = PeekToken();
                        if (nextToken.Type == TokenType.Identifier)
                        {
                            NextToken();
                            var memberExpression = Open<ScriptMemberExpression>();
                            memberExpression.Target = leftOperand;
                            var member = ParseVariableOrLiteral();
                            if (!(member is ScriptVariable))
                            {
                                LogError("Unexpected literal member [{member}]");
                                return null;
                            }
                            memberExpression.Member = (ScriptVariable) member;
                            leftOperand = Close(memberExpression);
                        }
                        else
                        {
                            LogError(nextToken,
                                $"Invalid token [{nextToken.Type}]. The dot operator is expected to be followed by a plain identifier");
                            return null;
                        }
                        continue;
                    }

                    // If we have a bracket but left operand is a (variable || member || indexer), then we consider next as an indexer
                    // unit test: 130-indexer-accessor-accept1.txt
                    if (Current.Type == TokenType.OpenBracket && leftOperand is ScriptVariablePath && !IsPreviousCharWhitespace())
                    {
                        NextToken();
                        var indexerExpression = Open<ScriptIndexerExpression>();
                        indexerExpression.Target = leftOperand;
                        // unit test: 130-indexer-accessor-error5.txt
                        indexerExpression.Index = ExpectAndParseExpression(indexerExpression, ref hasAnonymousFunction, functionCall, 0, $"Expecting <index_expression> instead of [{Current.Type}]");

                        if (Current.Type != TokenType.CloseBracket)
                        {
                            LogError($"Unexpected [{Current.Type}]. Expecting ']'");
                        }
                        else
                        {
                            NextToken();
                        }

                        leftOperand = Close(indexerExpression);
                        continue;
                    }

                    if (Current.Type == TokenType.Equal)
                    {
                        var assignExpression = Open<ScriptAssignExpression>();

                        if (expressionLevel > 1)
                        {
                            // unit test: 101-assign-complex-error1.txt
                            LogError(assignExpression, $"Expression is only allowed for a top level assignment");
                        }

                        NextToken();
                        assignExpression.Target = leftOperand;

                        // unit test: 105-assign-error3.txt
                        assignExpression.Value = ExpectAndParseExpression(assignExpression, ref hasAnonymousFunction, parentExpression);

                        leftOperand = Close(assignExpression);
                        continue;
                    }

                    // Handle binary operators here
                    ScriptBinaryOperator binaryOperatorType;
                    if (BinaryOperators.TryGetValue(Current.Type, out binaryOperatorType))
                    {
                        var newPrecedence = GetOperatorPrecedence(binaryOperatorType);

                        // Check precedence to see if we should "take" this operator here (Thanks TimJones for the tip code! ;)
                        if (newPrecedence < precedence)
                            break;

                        var binaryExpression = Open<ScriptBinaryExpression>();
                        binaryExpression.Left = leftOperand;
                        binaryExpression.Operator = binaryOperatorType;

                        NextToken(); // skip the operator

                        // unit test: 110-binary-simple-error1.txt
                        binaryExpression.Right = ExpectAndParseExpression(binaryExpression, ref hasAnonymousFunction,
                            functionCall ?? parentExpression, newPrecedence,
                            $"Expecting an <expression> to the right of the operator instead of [{Current.Type}]");
                        leftOperand = Close(binaryExpression);

                        continue;
                    }

                    if (precedence > 0)
                    {
                        break;
                    }

                    // If we can parse a statement, we have a method call
                    if (StartAsExpression())
                    {
                        if (parentExpression != null)
                        {
                            break;
                        }

                        if (functionCall == null)
                        {
                            functionCall = Open<ScriptFunctionCall>();
                            functionCall.Target = leftOperand;
                        }
                        else
                        {
                            functionCall.Arguments.Add(leftOperand);
                        }
                        goto parseExpression;
                    }

                    if (Current.Type == TokenType.Pipe)
                    {
                        if (functionCall != null)
                        {
                            functionCall.Arguments.Add(leftOperand);
                            leftOperand = functionCall;
                        }

                        var pipeCall = Open<ScriptPipeCall>();
                        pipeCall.From = leftOperand;
                        NextToken(); // skip |

                        // unit test: 310-func-pipe-error1.txt
                        pipeCall.To = ExpectAndParseExpression(pipeCall, ref hasAnonymousFunction);
                        return Close(pipeCall);
                    }

                    break;
                }

                if (functionCall != null)
                {
                    functionCall.Arguments.Add(leftOperand);
                    return functionCall;
                }
                return Close(leftOperand);
            }
            finally
            {
                expressionLevel--;
            }
        }
Пример #32
0
 private ScriptExpression ParseExpression(ScriptNode parentNode, ScriptExpression parentExpression = null, int precedence = 0)
 {
     bool hasAnonymousFunction = false;
     return ParseExpression(parentNode, ref hasAnonymousFunction, parentExpression, precedence);
 }
Пример #33
0
        private object GetOrSetValue(ScriptExpression targetExpression, object valueToSet, bool setter, int level)
        {
            object value = null;

            var nextVariable = targetExpression as ScriptVariable;
            if (nextVariable != null)
            {
                if (setter)
                {
                    SetValue(nextVariable, valueToSet, false);
                }
                else
                {
                    value = GetValueInternal(nextVariable);
                }
            }
            else
            {
                var nextDot = targetExpression as ScriptMemberExpression;
                if (nextDot != null)
                {
                    var targetObject = GetOrSetValue(nextDot.Target, valueToSet, false, level + 1);

                    if (targetObject == null)
                    {
                        throw new ScriptRuntimeException(nextDot.Span, $"Object [{nextDot.Target}] is null. Cannot access member: {nextDot}"); // unit test: 131-member-accessor-error1.txt
                    }

                    if (targetObject is string || targetObject.GetType().GetTypeInfo().IsPrimitive)
                    {
                        throw new ScriptRuntimeException(nextDot.Span, $"Cannot get or set a member on the primitive [{targetObject}/{targetObject.GetType()}] when accessing member: {nextDot}"); // unit test: 132-member-accessor-error2.txt
                    }

                    var accessor = GetMemberAccessor(targetObject);

                    var memberName = nextDot.Member.Name;

                    if (setter)
                    {
                        if (!accessor.TrySetValue(targetObject, memberName, valueToSet))
                        {
                            throw new ScriptRuntimeException(nextDot.Member.Span, $"Cannot set a value for the readonly member: {nextDot}"); // unit test: 132-member-accessor-error3.txt
                        }
                    }
                    else
                    {
                        value = accessor.GetValue(targetObject, memberName);
                    }
                }
                else
                {
                    var nextIndexer = targetExpression as ScriptIndexerExpression;
                    if (nextIndexer != null)
                    {
                        var targetObject = GetOrSetValue(nextIndexer.Target, valueToSet, false, level + 1);
                        if (targetObject == null)
                        {
                            throw new ScriptRuntimeException(nextIndexer.Target.Span, $"Object [{nextIndexer.Target}] is null. Cannot access indexer: {nextIndexer}"); // unit test: 130-indexer-accessor-error1.txt
                        }
                        else
                        {
                            var index = this.Evaluate(nextIndexer.Index);
                            if (index == null)
                            {
                                throw new ScriptRuntimeException(nextIndexer.Index.Span, $"Cannot access target [{nextIndexer.Target}] with a null indexer: {nextIndexer}"); // unit test: 130-indexer-accessor-error2.txt
                            }
                            else
                            {
                                if (targetObject is IDictionary || targetObject is ScriptObject)
                                {
                                    var accessor = GetMemberAccessor(targetObject);
                                    var indexAsString = ScriptValueConverter.ToString(nextIndexer.Index.Span, index);

                                    if (setter)
                                    {
                                        if (!accessor.TrySetValue(targetObject, indexAsString, valueToSet))
                                        {
                                            throw new ScriptRuntimeException(nextIndexer.Index.Span, $"Cannot set a value for the readonly member [{indexAsString}] in the indexer: {nextIndexer.Target}['{indexAsString}']"); // unit test: 130-indexer-accessor-error3.txt
                                        }
                                    }
                                    else
                                    {
                                        value = accessor.GetValue(targetObject, indexAsString);
                                    }
                                }
                                else
                                {
                                    var accessor = GetListAccessor(targetObject);
                                    if (accessor == null)
                                    {
                                        throw new ScriptRuntimeException(nextIndexer.Target.Span, $"Expecting a list. Invalid value [{targetObject}/{targetObject?.GetType().Name}] for the target [{nextIndexer.Target}] for the indexer: {nextIndexer}"); // unit test: 130-indexer-accessor-error4.txt
                                    }
                                    else
                                    {
                                        int i = ScriptValueConverter.ToInt(nextIndexer.Index.Span, index);

                                        // Allow negative index from the end of the array
                                        if (i < 0)
                                        {
                                            i = accessor.GetLength(targetObject) + i;
                                        }

                                        if (i >= 0)
                                        {
                                            if (setter)
                                            {
                                                accessor.SetValue(targetObject, i, valueToSet);
                                            }
                                            else
                                            {
                                                value = accessor.GetValue(targetObject, i);
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                    else if (!setter)
                    {
                        targetExpression.Evaluate(this);
                        value = this.Result;
                        this.Result = null;
                    }
                    else
                    {
                        throw new ScriptRuntimeException(targetExpression.Span, $"Unsupported expression for target for assignment: {targetExpression} = ..."); // unit test: 105-assign-error1.txt
                    }
                }
            }

            // If the variable being returned is a function, we need to evaluate it
            // If function call is disabled, it will be only when returning the final object (level 0 of recursion)
            if ((!isFunctionCallDisabled || level > 0) && ScriptFunctionCall.IsFunction(value))
            {
                value = ScriptFunctionCall.Call(this, targetExpression, value);
            }

            return value;
        }
Пример #34
0
 /// <summary>
 /// Gets the value from the specified expression using the current <see cref="ScriptObject"/> bound to the model context.
 /// </summary>
 /// <param name="target">The expression</param>
 /// <returns>The value of the expression</returns>
 public object GetValue(ScriptExpression target)
 {
     return GetOrSetValue(target, null, false, 0);
 }
Пример #35
0
 /// <summary>
 /// Sets the target expression with the specified value.
 /// </summary>
 /// <param name="target">The target expression.</param>
 /// <param name="value">The value.</param>
 /// <exception cref="System.ArgumentNullException">If target is null</exception>
 public void SetValue(ScriptExpression target, object value)
 {
     if (target == null) throw new ArgumentNullException(nameof(target));
     GetOrSetValue(target, value, true, 0);
 }