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; }
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); }
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)); }
public Branch(string branchName, uint textPosition, ScriptExpression condition, ScriptExpression targetScript) { BranchName = branchName; TextPosition = textPosition; Condition = condition; TargetScript = targetScript; }
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)); }
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); }
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); }
/// <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); }
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); }
public void Literal_String(string value) { string i = value.Trim('\"'); ScriptExpression expr = new ScriptExpression(value); Assert.That((string)expr.Evaluate(null), Is.EqualTo(i)); }
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!"); } }
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; } } } }
/// <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); }
/// <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--; } }
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; } }
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); }
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)); }
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; }
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); }
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); }
internal static ScriptTriggerAction Parse(string expression) { ScriptExpression script = ScriptExpression.Parse(expression); if (script != null) { return(new ScriptTriggerAction(script)); } return(null); }
private ScriptExpression DeNestExpression(ScriptExpression expr) { if (_flags.HasFlags(ScriptFormatterFlags.MinimizeParenthesisNesting)) { while (expr is ScriptNestedExpression nested) { expr = nested.Expression; nested.Expression = null; } } return(expr); }
/// <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); }
/// <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); }
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); }
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!"); } }
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(""); }
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); }
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--; } }
private ScriptExpression ParseExpression(ScriptNode parentNode, ScriptExpression parentExpression = null, int precedence = 0) { bool hasAnonymousFunction = false; return ParseExpression(parentNode, ref hasAnonymousFunction, parentExpression, precedence); }
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; }
/// <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); }
/// <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); }