Exemple #1
0
        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 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 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 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 TernaryOperation(
            IValueGetter condition,
            IValueGetter arg1,
            IValueGetter arg2,
            OperatorToken operatorToken)
        {
            Type arg1Type = arg1.GetValueType();
            Type arg2Type = arg2.GetValueType();

            if (condition.GetValueType() != typeof(bool))
            {
                throw new ScriptParsingException(
                          source: operatorToken,
                          message: $"Condition of Ternary Operator must be a boolean value: type {condition.GetValueType()}.");
            }

            if (arg1Type == arg2Type)
            {
                valueType = arg1Type;
            }
            else if ((arg1Type == typeof(int) || arg1Type == typeof(double)) &&
                     (arg2Type == typeof(int) || arg2Type == typeof(double)))
            {
                valueType = typeof(double);
            }
            else
            {
                throw new ScriptParsingException(
                          source: operatorToken,
                          message: $"Incompatible argments in Ternary operator: {arg1Type.Name} vs {arg2Type.Name}");
            }

            this.condition = condition;
            this.arg1      = arg1;
            this.arg2      = arg2;
        }
        private static void ReduceAssignmentOperators(List <ParsingUnit> units)
        {
            for (int i = 0; i < units.Count; i++)
            {
                if (units[i].OperatorType == Operator.Assignment ||
                    units[i].OperatorType == Operator.PlusEquals ||
                    units[i].OperatorType == Operator.MinusEquals ||
                    units[i].OperatorType == Operator.TimesEquals ||
                    units[i].OperatorType == Operator.DivideEquals ||
                    units[i].OperatorType == Operator.PowerEquals ||
                    units[i].OperatorType == Operator.ModuloEquals ||
                    units[i].OperatorType == Operator.AndEquals ||
                    units[i].OperatorType == Operator.OrEquals)
                {
                    if (i == 0)
                    {
                        throw new ScriptParsingException(
                                  source: units[i].FirstToken,
                                  message: $"Expression began with a {units[i].OperatorType} operator: {units[i].FirstToken}");
                    }

                    if (i == units.Count - 1)
                    {
                        throw new ScriptParsingException(
                                  source: units[i].FirstToken,
                                  message: $"Expression ended with a {units[i].OperatorType} operator: {units[i].FirstToken}");
                    }

                    if (units[i - 1].AsValue == null)
                    {
                        throw new ScriptParsingException(
                                  source: units[i - 1].FirstToken,
                                  message: $"Expression before {units[i].OperatorType} operator must be modifiable: {units[i - 1].FirstToken}");
                    }

                    if (units[i + 1].AsValueGetter == null)
                    {
                        throw new ScriptParsingException(
                                  source: units[i + 1].FirstToken,
                                  message: $"Expression after {units[i].OperatorType} operator must have a value: {units[i + 1].FirstToken}");
                    }

                    OperatorToken operatorToken = units[i].FirstToken as OperatorToken;

                    //Cache Value and remove Right value and operator
                    IValueGetter value = units[i + 1].AsValueGetter;
                    units.RemoveAt(i + 1);
                    units.RemoveAt(i);

                    //adjust our current position
                    i--;

                    //Swap the ParsingUnit for the calculated value

                    switch (operatorToken.operatorType)
                    {
                    case Operator.Assignment:
                        units[i] = new ParsedValuedUnit(
                            value: new AssignmentOperation(
                                assignee: units[i].AsValue,
                                value: value,
                                source: operatorToken),
                            firstToken: units[i].FirstToken);
                        break;

                    case Operator.PlusEquals:
                        units[i] = new ParsedValuedUnit(
                            value: new PlusEqualsOperation(
                                assignee: units[i].AsValue,
                                value: value,
                                source: operatorToken),
                            firstToken: units[i].FirstToken);
                        break;

                    case Operator.MinusEquals:
                    case Operator.TimesEquals:
                    case Operator.DivideEquals:
                    case Operator.PowerEquals:
                    case Operator.ModuloEquals:
                        //Handle Numerical Operators
                        units[i] = new ParsedValuedUnit(
                            value: new NumericalInPlaceOperation(
                                assignee: units[i].AsValue,
                                value: value,
                                operatorType: operatorToken.operatorType,
                                source: operatorToken),
                            firstToken: units[i].FirstToken);
                        break;

                    case Operator.AndEquals:
                    case Operator.OrEquals:
                        units[i] = new ParsedValuedUnit(
                            value: new BooleanInPlaceOperation(
                                assignee: units[i].AsValue,
                                value: value,
                                operatorType: operatorToken.operatorType,
                                source: operatorToken),
                            firstToken: units[i].FirstToken);
                        break;

                    default:
                        throw new ArgumentException($"Unexpected Operator: {operatorToken.operatorType}");
                    }
                }
            }
        }
        private static void ReduceTernaryOperator(List <ParsingUnit> units)
        {
            for (int i = 0; i < units.Count; i++)
            {
                if (units[i].OperatorType == Operator.Ternary)
                {
                    if (i == 0)
                    {
                        throw new ScriptParsingException(
                                  source: units[i].FirstToken,
                                  message: $"Expression began with a {units[i].OperatorType} operator: {units[i].FirstToken}");
                    }

                    if (i >= units.Count - 2)
                    {
                        throw new ScriptParsingException(
                                  source: units[i].FirstToken,
                                  message: $"Expression with a {units[i].OperatorType} operator didn't contain enought arguments: {units[i].FirstToken}");
                    }

                    if (units[i - 1].AsValueGetter == null)
                    {
                        throw new ScriptParsingException(
                                  source: units[i - 1].FirstToken,
                                  message: $"Unexpected token before {units[i].OperatorType} operator: {units[i - 1].FirstToken}");
                    }

                    if (units[i + 1].AsValueGetter == null)
                    {
                        throw new ScriptParsingException(
                                  source: units[i + 1].FirstToken,
                                  message: $"Unexpected token after {units[i].OperatorType} operator: {units[i + 1].FirstToken}");
                    }

                    if (units[i + 2].AsValueGetter == null)
                    {
                        throw new ScriptParsingException(
                                  source: units[i + 1].FirstToken,
                                  message: $"Unexpected token for second arguemnt of {units[i].OperatorType} operator: {units[i + 2].FirstToken}");
                    }

                    OperatorToken operatorToken = units[i].FirstToken as OperatorToken;

                    //Cache Value and remove Right value and operator
                    IValueGetter arg1Value = units[i + 1].AsValueGetter;
                    IValueGetter arg2Value = units[i + 2].AsValueGetter;
                    units.RemoveAt(i + 2);
                    units.RemoveAt(i + 1);
                    units.RemoveAt(i);

                    //adjust our current position
                    i--;

                    //Swap the ParsingUnit for the calculated value
                    units[i] = new ParsedValuedUnit(
                        value: new TernaryOperation(
                            condition: units[i].AsValueGetter,
                            arg1: arg1Value,
                            arg2: arg2Value,
                            operatorToken: operatorToken),
                        firstToken: units[i].FirstToken);
                }
            }
        }
        private static void ReduceBinaryOperator(
            Operator op,
            List <ParsingUnit> units)
        {
            for (int i = 0; i < units.Count; i++)
            {
                if (units[i].OperatorType == op)
                {
                    if (i == 0)
                    {
                        throw new ScriptParsingException(
                                  source: units[i].FirstToken,
                                  message: $"Expression began with a {units[i].OperatorType} operator: {units[i].FirstToken}");
                    }

                    if (i == units.Count - 1)
                    {
                        throw new ScriptParsingException(
                                  source: units[i].FirstToken,
                                  message: $"Expression ended with a {units[i].OperatorType} operator: {units[i].FirstToken}");
                    }

                    if (units[i - 1].AsValueGetter == null)
                    {
                        throw new ScriptParsingException(
                                  source: units[i - 1].FirstToken,
                                  message: $"Unexpected token before {units[i].OperatorType} operator: {units[i - 1].FirstToken}");
                    }

                    if (units[i + 1].AsValueGetter == null)
                    {
                        throw new ScriptParsingException(
                                  source: units[i + 1].FirstToken,
                                  message: $"Unexpected token after {units[i].OperatorType} operator: {units[i + 1].FirstToken}");
                    }

                    OperatorToken operatorToken = units[i].FirstToken as OperatorToken;

                    //Cache Value and remove Right value and operator
                    IValueGetter arg2Value = units[i + 1].AsValueGetter;
                    units.RemoveAt(i + 1);
                    units.RemoveAt(i);

                    //adjust our current position
                    i--;

                    //Swap the ParsingUnit for the calculated value

                    switch (op)
                    {
                    case Operator.Plus:
                        if (units[i].AsValueGetter.GetValueType() == typeof(string) ||
                            arg2Value.GetValueType() == typeof(string))
                        {
                            //Handle String Concatenation
                            units[i] = new ParsedValuedUnit(
                                value: ConcatenateOperator.CreateConcatenateOperator(
                                    arg1: units[i].AsValueGetter,
                                    arg2: arg2Value),
                                firstToken: units[i].FirstToken);
                            break;
                        }
                        //Handle like addition
                        goto case Operator.Minus;

                    case Operator.Minus:
                    case Operator.Times:
                    case Operator.Divide:
                    case Operator.Power:
                    case Operator.Modulo:
                        //Handle Numerical Operators
                        units[i] = new ParsedValuedUnit(
                            value: BinaryNumericalOperation.CreateBinaryNumericalOperation(
                                arg1: units[i].AsValueGetter,
                                arg2: arg2Value,
                                operatorToken: operatorToken),
                            firstToken: units[i].FirstToken);
                        break;

                    case Operator.IsEqualTo:
                    case Operator.IsNotEqualTo:
                        units[i] = new ParsedValuedUnit(
                            value: EqualityCompairsonOperation.CreateEqualityComparisonOperator(
                                arg1: units[i].AsValueGetter,
                                arg2: arg2Value,
                                operatorToken: operatorToken),
                            firstToken: units[i].FirstToken);
                        break;

                    case Operator.IsGreaterThan:
                    case Operator.IsGreaterThanOrEqualTo:
                    case Operator.IsLessThan:
                    case Operator.IsLessThanOrEqualTo:
                        units[i] = new ParsedValuedUnit(
                            value: ComparisonOperation.CreateComparisonOperation(
                                arg1: units[i].AsValueGetter,
                                arg2: arg2Value,
                                operatorToken: operatorToken),
                            firstToken: units[i].FirstToken);
                        break;

                    case Operator.And:
                    case Operator.Or:
                        units[i] = new ParsedValuedUnit(
                            value: BinaryBoolOperation.CreateBinaryBoolOperator(
                                arg1: units[i].AsValueGetter,
                                arg2: arg2Value,
                                operatorToken: operatorToken),
                            firstToken: units[i].FirstToken);
                        break;

                    default:
                        throw new ArgumentException($"Unexpected Operator: {op}");
                    }
                }
            }
        }
        private static void HandleNextOperator(
            OperatorToken opToken,
            List <ParsingUnit> units,
            IEnumerator <Token> tokens,
            CompilationContext context)
        {
            switch (opToken.operatorType)
            {
            case Operator.Assignment:
            case Operator.PlusEquals:
            case Operator.MinusEquals:
            case Operator.TimesEquals:
            case Operator.DivideEquals:
            case Operator.PowerEquals:
            case Operator.ModuloEquals:
            case Operator.AndEquals:
            case Operator.OrEquals:
            case Operator.Plus:
            case Operator.Minus:
            case Operator.Times:
            case Operator.Divide:
            case Operator.Power:
            case Operator.Modulo:
            case Operator.CastDouble:
            case Operator.CastInteger:
            case Operator.Increment:
            case Operator.Decrement:
            case Operator.Not:
            case Operator.IsEqualTo:
            case Operator.IsNotEqualTo:
            case Operator.IsGreaterThan:
            case Operator.IsGreaterThanOrEqualTo:
            case Operator.IsLessThan:
            case Operator.IsLessThanOrEqualTo:
            case Operator.And:
            case Operator.Or:
            case Operator.Negate:
                //Add and continue
                units.Add(new TokenUnit(opToken));
                tokens.CautiousAdvance();
                break;

            case Operator.Ternary:
                //Add the ternary Operator
                units.Add(new TokenUnit(opToken));
                tokens.CautiousAdvance();

                //Add the first expected expression
                Token firstToken = tokens.Current;
                units.Add(new ParsedValuedUnit(
                              value: ParseNextExpression(tokens, context),
                              firstToken: firstToken));

                //Colon
                tokens.AssertAndSkip(Separator.Colon);

                //Add the second expected expression
                firstToken = tokens.Current;
                units.Add(new ParsedValuedUnit(
                              value: ParseNextExpression(tokens, context),
                              firstToken: firstToken));
                break;

            case Operator.MemberAccess:
                tokens.CautiousAdvance();
                IdentifierToken nameToken = tokens.GetTokenAndAdvance <IdentifierToken>();
                if (tokens.TestWithoutSkipping(Separator.OpenParen))
                {
                    //Method
                    units.Add(new MemberAccessUnit(
                                  identifier: nameToken.identifier,
                                  args: ParseArguments(tokens, context),
                                  firstToken: opToken));
                }
                else
                {
                    //Value
                    units.Add(new MemberAccessUnit(
                                  identifier: nameToken.identifier,
                                  args: null,
                                  firstToken: opToken));
                }
                break;


            case Operator.AmbiguousMinus:
            default:
                throw new ArgumentException($"Unsupported Operator: {opToken.operatorType}");
            }
        }