public ForEachStatement( IExecutable declarationStatement, IValue loopVariable, IValueGetter containerExpression, IExecutable loopBody, KeywordToken keywordToken) { if (!typeof(IEnumerable).IsAssignableFrom(containerExpression.GetValueType())) { throw new ScriptParsingException( source: keywordToken, message: $"Collection of ForEach statement is not an Enumerable collection: {containerExpression.GetValueType().Name}"); } if (!loopVariable.GetValueType().AssignableFromType(containerExpression.GetValueType().GetGenericArguments()[0])) { throw new ScriptParsingException( source: keywordToken, message: $"Collection items of type " + $"({containerExpression.GetValueType().GetGenericArguments()[0].Name}) " + $"not assignable to declared item type: {loopVariable.GetValueType().Name}"); } this.loopVariable = loopVariable; this.declarationStatement = declarationStatement; this.containerExpression = containerExpression; this.loopBody = loopBody; }
public T GetAs <T>(RuntimeContext context) { Type returnType = typeof(T); if (!returnType.AssignableFromType(getType)) { throw new ScriptRuntimeException($"Tried to retrieve result of Indexing with type {getType.Name} as type {returnType.Name}"); } object index = indexArg.GetAs <object>(context); if (!indexerType.IsAssignableFrom(indexArg.GetValueType())) { index = Convert.ChangeType(index, indexerType); } object value = indexGetter.Invoke(valueArg.GetAs <object>(context), new object[] { index }); if (returnType.IsAssignableFrom(getType)) { return((T)value); } return((T)Convert.ChangeType(value, returnType)); }
public IsNaNMathFunction( IValueGetter arg, Token source) { if (!(arg.GetValueType() == typeof(double) || arg.GetValueType() == typeof(int))) { throw new ScriptParsingException( source: source, message: $"Argument of IsNaN is not numerical: type {arg.GetValueType().Name}"); } this.arg = arg; }
public static DeclarationAssignmentOperation CreateDelcaration( IdentifierToken identifierToken, Type valueType, IValueGetter initializer, bool isConstant, CompilationContext context) { //Check initializer type if (!valueType.AssignableFromType(initializer.GetValueType())) { throw new ScriptParsingException( source: identifierToken, message: $"Incompatible type in declaration. Expected type {valueType.Name}, Received {initializer.GetValueType().Name}"); } if (isConstant) { if (initializer is LiteralToken litToken) { object value = litToken.GetAs <object>(); if (!valueType.IsAssignableFrom(initializer.GetValueType())) { value = Convert.ChangeType(value, valueType); } context.DeclareConstant( identifierToken: identifierToken, type: valueType, value: value); //Const declaration can't have sideeffects, thus no statement is generated return(null); } throw new ScriptParsingException( source: identifierToken, message: $"Constants cannot be initialized with non-constant values."); } //Try to declare and throw exceptions if invalid context.DeclareVariable(identifierToken, valueType); return(new DeclarationAssignmentOperation( identifier: identifierToken.identifier, valueType: valueType, initializer: initializer)); }
public static IExpression CreateCastOperation( IValueGetter arg, OperatorToken operatorToken) { Type argType = arg.GetValueType(); if (!(argType == typeof(double) || argType == typeof(int))) { throw new ScriptParsingException( source: operatorToken, message: $"Argument of operator {operatorToken.operatorType} is not numerical: type {argType.Name}."); } if (arg is LiteralToken litArg) { switch (operatorToken.operatorType) { case Operator.CastDouble: return(new LiteralToken <double>(operatorToken, litArg.GetAs <double>())); case Operator.CastInteger: return(new LiteralToken <int>(operatorToken, (int)litArg.GetAs <double>())); default: throw new ArgumentException($"Unexpected Operator: {operatorToken.operatorType}"); } } return(new CastOperation(arg, operatorToken.operatorType)); }
public static IValueGetter[] ParseItems( IEnumerator <Token> tokens, Type itemType, CompilationContext context) { List <IValueGetter> items = new List <IValueGetter>(); tokens.AssertAndSkip(Separator.OpenCurlyBoi); if (!tokens.TestWithoutSkipping(Separator.CloseCurlyBoi)) { do { Token temp = tokens.Current; IValueGetter nextValue = ParseNextGetterExpression(tokens, context); if (!itemType.AssignableFromType(nextValue.GetValueType())) { throw new ScriptParsingException( source: temp, message: $"Initializer List Argument must be of appropriate value type: Expected: {itemType.Name}, Found: {nextValue.GetValueType().Name}"); } items.Add(nextValue); }while (tokens.TestAndConditionallySkip(Separator.Comma)); } tokens.AssertAndSkip(Separator.CloseCurlyBoi); return(items.ToArray()); }
public static IExpression CreateNegationOperation( IValueGetter arg, Token source) { Type argType = arg.GetValueType(); if (!(argType == typeof(double) || argType == typeof(int))) { throw new ScriptParsingException( source: source, message: $"Cannot negate a non-numerical value {arg} of type {argType.Name}"); } if (arg is LiteralToken litArg) { if (argType == typeof(int)) { return(new LiteralToken <int>(source, -litArg.GetAs <int>())); } else { return(new LiteralToken <double>(source, -litArg.GetAs <double>())); } } return(new NegationOperation(arg, argType)); }
public static IExpression CreateBinaryBoolOperator( IValueGetter arg1, IValueGetter arg2, OperatorToken operatorToken) { if (arg1.GetValueType() != typeof(bool)) { throw new ScriptParsingException( source: operatorToken, message: $"Left side of operator {operatorToken.operatorType} not of expected type bool: {arg1.GetValueType().Name}"); } if (arg2.GetValueType() != typeof(bool)) { throw new ScriptParsingException( source: operatorToken, message: $"Right side of operator {operatorToken.operatorType} not of expected type bool: {arg2.GetValueType().Name}"); } //Constant case if (arg1 is LiteralToken litArg1 && arg2 is LiteralToken litArg2) { switch (operatorToken.operatorType) { case Operator.And: return(new LiteralToken <bool>(operatorToken, litArg1.GetAs <bool>() && litArg2.GetAs <bool>())); case Operator.Or: return(new LiteralToken <bool>(operatorToken, litArg1.GetAs <bool>() || litArg2.GetAs <bool>())); default: throw new ArgumentException($"Unexpected Operator {operatorToken.operatorType}"); } } return(new BinaryBoolOperation(arg1, arg2, operatorToken.operatorType)); }
public BooleanInPlaceOperation( IValue assignee, IValueGetter value, Operator operatorType, Token source) { if (assignee.GetValueType() != typeof(bool)) { throw new ScriptParsingException( source: source, message: $"Left Argument {assignee} of Operator {source} is not a bool: type {assignee.GetValueType().Name}"); } if (value.GetValueType() != typeof(bool)) { throw new ScriptParsingException( source: source, message: $"Right Argument {value} of Operator {source} is not a bool."); } this.assignee = assignee; this.value = value; this.operatorType = operatorType; switch (operatorType) { case Operator.AndEquals: case Operator.OrEquals: //Acceptable break; default: throw new ArgumentException($"Unexpected Operator: {operatorType}"); } }
public static IExpression CreateEqualityComparisonOperator( IValueGetter arg1, IValueGetter arg2, OperatorToken operatorToken) { Type argType; Type arg1Type = arg1.GetValueType(); Type arg2Type = arg2.GetValueType(); if (arg1Type == arg2Type) { argType = arg1Type; } else if (arg1Type.AssignableFromType(arg2Type)) { argType = arg1Type; } else if (arg2Type.AssignableFromType(arg1Type)) { argType = arg2Type; } else { throw new ScriptParsingException( source: operatorToken, message: $"Incompatible Types for {operatorToken.operatorType} operator: {arg1Type.Name} and {arg2Type.Name}"); } //Constant case if (arg1 is LiteralToken litArg1 && arg2 is LiteralToken litArg2) { object value1 = litArg1.GetAs <object>(); object value2 = litArg2.GetAs <object>(); if (argType != arg1Type) { value1 = Convert.ChangeType(value1, argType); } if (argType != arg2Type) { value2 = Convert.ChangeType(value2, argType); } switch (operatorToken.operatorType) { case Operator.IsEqualTo: return(new LiteralToken <bool>(operatorToken, value1.Equals(value2))); case Operator.IsNotEqualTo: return(new LiteralToken <bool>(operatorToken, !value1.Equals(value2))); default: throw new ArgumentException($"Unexpected Operator: {operatorToken.operatorType}"); } } return(new EqualityCompairsonOperation(arg1, arg2, argType, operatorToken.operatorType)); }
public ReturnStatement( KeywordToken keywordToken, IValueGetter returnValue, CompilationContext context) { this.returnValue = returnValue; context.ValidateReturn( returnKeyword: keywordToken, returnType: returnValue?.GetValueType() ?? typeof(void)); returnType = context.GetReturnType(); }
public HasDataOperation( IValueGetter keyArg, Token source) { if (keyArg.GetValueType() != typeof(string)) { throw new ScriptParsingException( source: source, message: $"Key argument of User.HasData must be a string: type {keyArg.GetValueType().Name}"); } this.keyArg = keyArg; }
public static IExpression CreateComparisonOperation( IValueGetter arg1, IValueGetter arg2, OperatorToken operatorToken) { if (!(arg1.GetValueType() == typeof(double) || arg1.GetValueType() == typeof(int))) { throw new ScriptParsingException( source: operatorToken, message: $"Left side of operator {operatorToken.operatorType} has incompatible type: {arg1.GetValueType().Name}"); } if (!(arg2.GetValueType() == typeof(double) || arg2.GetValueType() == typeof(int))) { throw new ScriptParsingException( source: operatorToken, message: $"Right side of operator {operatorToken.operatorType} has incompatible type: {arg2.GetValueType().Name}"); } //Constant case if (arg1 is LiteralToken litArg1 && arg2 is LiteralToken litArg2) { switch (operatorToken.operatorType) { case Operator.IsGreaterThan: return(new LiteralToken <bool>(operatorToken, litArg1.GetAs <double>() > litArg2.GetAs <double>())); case Operator.IsGreaterThanOrEqualTo: return(new LiteralToken <bool>(operatorToken, litArg1.GetAs <double>() >= litArg2.GetAs <double>())); case Operator.IsLessThan: return(new LiteralToken <bool>(operatorToken, litArg1.GetAs <double>() < litArg2.GetAs <double>())); case Operator.IsLessThanOrEqualTo: return(new LiteralToken <bool>(operatorToken, litArg1.GetAs <double>() <= litArg2.GetAs <double>())); default: throw new ArgumentException($"Unexpected Operator {operatorToken.operatorType}"); } } return(new ComparisonOperation(arg1, arg2, operatorToken.operatorType)); }
public static IExpression CreateBinaryNumericalOperation( IValueGetter arg1, IValueGetter arg2, OperatorToken operatorToken) { Type arg1Type = arg1.GetValueType(); Type arg2Type = arg2.GetValueType(); Type valueType; if (!(arg1Type == typeof(double) || arg1Type == typeof(int))) { throw new ScriptParsingException( source: operatorToken, message: $"Left side of operator {operatorToken.operatorType} not of expected type int or bool: {arg1.GetValueType().Name}"); } if (!(arg2Type == typeof(double) || arg2Type == typeof(int))) { throw new ScriptParsingException( source: operatorToken, message: $"Right side of operator {operatorToken.operatorType} not of expected type int or bool: {arg2.GetValueType().Name}"); } if (arg1Type == arg2Type) { valueType = arg1Type; } else { valueType = typeof(double); } //Constant case if (arg1 is LiteralToken litArg1 && arg2 is LiteralToken litArg2) { if (valueType == typeof(int)) { return(new LiteralToken <int>( operatorToken, IntOperator <int>(litArg1.GetAs <int>(), litArg2.GetAs <int>(), operatorToken.operatorType, valueType))); } else { return(new LiteralToken <double>( operatorToken, DoubleOperator <double>(litArg1.GetAs <double>(), litArg2.GetAs <double>(), operatorToken.operatorType, valueType))); } } return(new BinaryNumericalOperation(arg1, arg2, valueType, operatorToken.operatorType)); }
public SingleArgumentMathFunction( IValueGetter arg, MathMethod mathMethod, Token source) { this.arg = arg; this.mathMethod = mathMethod; Type argType = arg.GetValueType(); if (!(argType == typeof(double) || argType == typeof(int))) { throw new ScriptParsingException( source: source, message: $"Cannot perform Math.{mathMethod} on non-numerical value {arg} of type {argType.Name}"); } switch (mathMethod) { case MathMethod.Floor: case MathMethod.Ceiling: case MathMethod.Round: case MathMethod.Abs: valueType = argType; break; case MathMethod.Sign: valueType = typeof(int); break; case MathMethod.Ln: case MathMethod.Log10: case MathMethod.Exp: case MathMethod.Sqrt: case MathMethod.Sin: case MathMethod.Cos: case MathMethod.Tan: case MathMethod.Asin: case MathMethod.Acos: case MathMethod.Atan: case MathMethod.Sinh: case MathMethod.Cosh: case MathMethod.Tanh: //Only return doubles. valueType = typeof(double); break; default: throw new ArgumentException($"Unexpected Operator: {mathMethod}"); } }
public SetUserValueMethod( IValueGetter keyArg, IValueGetter valueArg, UserMethod userMethod, Token source) { switch (userMethod) { case UserMethod.SetString: valueType = typeof(string); break; case UserMethod.SetBool: valueType = typeof(bool); break; case UserMethod.SetDouble: valueType = typeof(double); break; case UserMethod.SetInt: valueType = typeof(int); break; case UserMethod.SetList: valueType = typeof(IList); break; default: throw new Exception($"Unexpected UserMethod: {userMethod}"); } if (keyArg.GetValueType() != typeof(string)) { throw new ScriptParsingException( source: source, message: $"Key Argument {keyArg} of User.{userMethod} is not a string: type {keyArg.GetValueType().Name}"); } if (!valueType.AssignableFromType(valueArg.GetValueType())) { throw new ScriptParsingException( source: source, message: $"Value Argument {valueArg} of User.{userMethod} is type {valueType.Name} and not assignable to type {valueArg.GetValueType().Name}"); } this.keyArg = keyArg; this.valueArg = valueArg; }
public MemberArgumentValueExecutableOperation( IValueGetter value, Func <TInput, RuntimeContext, TResult> operation, Token source) { this.value = value; this.operation = operation; if (!typeof(TInput).AssignableFromType(value.GetValueType())) { throw new ScriptParsingException( source: source, message: $"Incorrect value type. Expected: {typeof(TInput).Name}. Received: {value.GetValueType().Name}. "); } }
public MemberStatementOperation( IValueGetter value, Action <TInput> operation, Token source) { this.value = value; this.operation = operation; if (!typeof(TInput).AssignableFromType(value.GetValueType())) { throw new ScriptParsingException( source: source, message: $"Incorrect value type. Expected: {typeof(TInput).Name}. Received: {value.GetValueType().Name}. "); } }
public WhileStatement( IValueGetter continueExpression, IExecutable loopBody, KeywordToken keywordToken) { if (continueExpression.GetValueType() != typeof(bool)) { throw new ScriptParsingException( source: keywordToken, message: $"ContinueExpression of {keywordToken} statement is not a boolean value: type {continueExpression.GetValueType().Name}"); } this.continueExpression = continueExpression; this.loopBody = loopBody; }
public NumericalInPlaceOperation( IValue assignee, IValueGetter value, Operator operatorType, Token source) { assigneeType = assignee.GetValueType(); Type valueType = value.GetValueType(); if (!(assigneeType == typeof(int) || assigneeType == typeof(double))) { throw new ScriptParsingException( source: source, message: $"Assignee {assignee} for Operator {source} is not a numerical value: type {assigneeType.Name}"); } if (!(valueType == typeof(int) || valueType == typeof(double))) { throw new ScriptParsingException( source: source, message: $"Value {value} for Operator {source} is not a numerical value: type {valueType.Name}"); } if (assigneeType == typeof(int) && valueType == typeof(double)) { throw new ScriptParsingException( source: source, message: $"Unable to implicity cast Value {value} to type int for assignment to assignee {assignee}."); } this.assignee = assignee; this.value = value; this.operatorType = operatorType; switch (operatorType) { case Operator.PlusEquals: case Operator.MinusEquals: case Operator.TimesEquals: case Operator.DivideEquals: case Operator.PowerEquals: case Operator.ModuloEquals: //Acceptable break; default: throw new ArgumentException($"Unexpected Operator: {operatorType}"); } }
public IfStatement( IValueGetter condition, IExecutable trueBlock, IExecutable falseBlock, KeywordToken keywordToken) { if (condition.GetValueType() != typeof(bool)) { throw new ScriptParsingException( source: keywordToken, message: $"Condition of {keywordToken} statement is not a boolean value: type {condition.GetValueType().Name}"); } this.condition = condition; this.trueBlock = trueBlock; this.falseBlock = falseBlock; }
public SettablePropertyValueOperation( IValueGetter value, Func <TInput, TResult> getOperation, Action <TInput, TResult> setOperation, Token source) { this.value = value; this.getOperation = getOperation; this.setOperation = setOperation; if (!typeof(TInput).AssignableFromType(value.GetValueType())) { throw new ScriptParsingException( source: source, message: $"Incorrect value type. Expected: {typeof(TInput).Name}. Received: {value.GetValueType().Name}. "); } }
public ClampMathFunction( IValueGetter value, IValueGetter lowerbound, IValueGetter upperbound, Token source) { Type inputValueType = value.GetValueType(); Type lowerboundType = lowerbound.GetValueType(); Type upperboundType = upperbound.GetValueType(); if (!(inputValueType == typeof(int) || inputValueType == typeof(double))) { throw new ScriptParsingException( source: source, message: $"Tried to apply function Clamp to value {value} of type {inputValueType.Name}"); } if (!(lowerboundType == typeof(int) || lowerboundType == typeof(double))) { throw new ScriptParsingException( source: source, message: $"Tried to apply function Clamp with lowerbound value {lowerbound} of type {lowerboundType.Name}"); } if (!(upperboundType == typeof(int) || upperboundType == typeof(double))) { throw new ScriptParsingException( source: source, message: $"Tried to apply function Clamp with upperbound value {upperbound} of type {upperboundType.Name}"); } if (inputValueType == typeof(int) && lowerboundType == typeof(int) && upperboundType == typeof(int)) { valueType = inputValueType; } else { valueType = typeof(double); } this.value = value; this.lowerbound = lowerbound; this.upperbound = upperbound; }
public GetUserValueFunction( IValueGetter keyArg, IValueGetter defaultArg, UserMethod userMethod, Token source) { this.keyArg = keyArg; this.defaultArg = defaultArg; this.userMethod = userMethod; if (keyArg.GetValueType() != typeof(string)) { throw new ScriptParsingException( source: source, message: $"Key argument of User.{userMethod} must be a string: type {keyArg.GetValueType().Name}"); } switch (userMethod) { case UserMethod.GetInt: getType = typeof(int); break; case UserMethod.GetDouble: getType = typeof(double); break; case UserMethod.GetBool: getType = typeof(bool); break; case UserMethod.GetString: getType = typeof(string); break; default: throw new Exception($"Unsupported UserMethod: {userMethod}"); } if (defaultArg != null && !getType.AssignableFromType(defaultArg.GetValueType())) { throw new ScriptParsingException( source: source, message: $"Default Value argument of User.{userMethod} must be of type {getType.Name}: type {defaultArg.GetValueType().Name}"); } }
public DoubleArgumentMathFunction( IValueGetter arg1, IValueGetter arg2, MathMethod mathMethod, Token source) { Type arg1Type = arg1.GetValueType(); Type arg2Type = arg2.GetValueType(); this.arg1 = arg1; this.arg2 = arg2; this.mathMethod = mathMethod; if (!(arg1Type == typeof(double) || arg1Type == typeof(int))) { throw new ScriptParsingException( source: source, message: $"First Argument of Math.{mathMethod} not of expected type int or bool: {arg1.GetValueType().Name}"); } if (!(arg2Type == typeof(double) || arg2Type == typeof(int))) { throw new ScriptParsingException( source: source, message: $"Second Argument of Math.{mathMethod} not of expected type int or bool: {arg2.GetValueType().Name}"); } if (arg1Type == arg2Type) { valueType = arg1Type; } else { valueType = typeof(double); } switch (mathMethod) { case MathMethod.Min: case MathMethod.Max: //Acceptable break; default: throw new ArgumentException($"Unexpected MathMethod: {mathMethod}"); } }
public IndexerOperation( IValueGetter valueArg, IValueGetter indexArg, Token source) { Type valueType = valueArg.GetValueType(); indexerType = valueType.GetIndexingType(); if (indexerType == null) { throw new ScriptParsingException( source: source, message: $"Indexer used on non-Indexable type: type {valueType.Name}"); } getType = valueType.GetIndexingReturnType(); if (!indexerType.AssignableFromType(indexArg.GetValueType())) { throw new ScriptParsingException( source: source, message: $"Indexer value must be of type {indexerType.Name}: type {indexArg.GetValueType().Name}"); } this.valueArg = valueArg; this.indexArg = indexArg; indexGetter = valueType.GetMethod("get_Item", new Type[] { indexerType }); indexSetter = valueType.GetMethod("set_Item", new Type[] { indexerType, getType }); if (indexGetter == null) { throw new ScriptParsingException( source: source, message: $"Failed to get indexGetter for type: type {valueType.Name}"); } if (indexSetter == null) { throw new ScriptParsingException( source: source, message: $"Failed to get indexSetter for type: type {valueType.Name}"); } }
public static IExpression CreateNotOperation( IValueGetter arg, OperatorToken operatorToken) { if (arg.GetValueType() != typeof(bool)) { throw new ScriptParsingException( source: operatorToken, message: $"Argument of Operator {operatorToken} is not boolean: type {arg.GetValueType().Name}."); } if (arg is LiteralToken litArg) { return(new LiteralToken <bool>(operatorToken, !litArg.GetAs <bool>())); } return(new NotOperation(arg)); }
public AssignmentOperation( IValue assignee, IValueGetter value, Token source) { this.assignee = assignee; this.value = value; assigneeType = assignee.GetValueType(); valueType = value.GetValueType(); if (!assigneeType.AssignableFromType(valueType)) { throw new ScriptParsingException( source: source, message: $"Assignment operator has an incompatible type for {assigneeType.Name} variable: type {valueType.Name}"); } }
public ScriptDeclaration( IdentifierToken identifierToken, Type valueType, IValueGetter initializer, CompilationContext context) { identifier = identifierToken.identifier; this.valueType = valueType; this.initializer = initializer; //Try to declare and throw exceptions if invalid context.DeclareVariable(identifierToken, valueType); //Check initializer type if (initializer != null && !valueType.AssignableFromType(initializer.GetValueType())) { throw new ScriptParsingException( source: identifierToken, message: $"Incompatible type in declaration. Expected type {valueType.Name}, Received {initializer.GetValueType().Name}"); } }
public override FlowState Execute(ScopeRuntimeContext context) { if (returnType == typeof(void)) { context.PushReturnValue(null); } else { if (returnType == returnValue.GetValueType()) { context.PushReturnValue(returnValue.GetAs <object>(context)); } else { context.PushReturnValue( Convert.ChangeType(returnValue.GetAs <object>(context), returnType)); } } return(FlowState.Return); }