Esempio n. 1
0
        protected override SucoExpression deduceTypes(SucoTypeEnvironment env, SucoContext context)
        {
            var newEnv       = env;
            var newClauses   = new List <SucoListClause>();
            var anySingleton = false;

            foreach (var clause in Clauses)
            {
                var newFromExpression = clause.FromExpression?.DeduceTypes(newEnv, context);

                // Deduce the type of the iterator variable
                SucoType collectionType;
                if (newFromExpression != null)
                {
                    collectionType = newFromExpression.Type;
                }
                else if (clause.HasDollar)
                {
                    collectionType = SucoType.Cell.List();
                }
                else
                {
                    try
                    {
                        collectionType = env.GetVariableType("cells");
                    }
                    catch (SucoTempCompileException tc)
                    {
                        throw new SucoCompileException(tc.Message, clause.StartIndex, clause.EndIndex);
                    }
                }

                if (collectionType is not SucoListType {
                    ElementType: SucoType elementType
                })
Esempio n. 2
0
        protected override SucoExpression deduceTypes(SucoTypeEnvironment env, SucoContext context)
        {
            var valueExpr = ValueExpression.DeduceTypes(env, context);
            var innerExpr = InnerExpression.DeduceTypes(env.DeclareVariable(VariableName, valueExpr.Type), context);

            return(new SucoLetExpression(StartIndex, EndIndex, VariableName, valueExpr, innerExpr, innerExpr.Type));
        }
Esempio n. 3
0
        protected override SucoExpression deduceTypes(SucoTypeEnvironment env, SucoContext context)
        {
            var condition = Condition.DeduceTypes(env, context);

            if (!(condition.Type is SucoBooleanType))
            {
                throw new SucoCompileException($"The condition in a ?: operator must be a boolean.", Condition.StartIndex, Condition.EndIndex);
            }

            var      trueExpr  = True.DeduceTypes(env, context);
            var      falseExpr = False.DeduceTypes(env, context);
            SucoType thisType;

            if (trueExpr.Type.Equals(falseExpr.Type))
            {
                thisType = trueExpr.Type;
            }
            else if (trueExpr.Type.ImplicitlyConvertibleTo(falseExpr.Type))
            {
                trueExpr = trueExpr.ImplicitlyConvertTo(falseExpr.Type);
                thisType = falseExpr.Type;
            }
            else if (falseExpr.Type.ImplicitlyConvertibleTo(trueExpr.Type))
            {
                falseExpr = falseExpr.ImplicitlyConvertTo(trueExpr.Type);
                thisType  = trueExpr.Type;
            }
            else
            {
                throw new SucoCompileException($"Types “{trueExpr.Type}” and “{falseExpr.Type}” are not compatible.", True.StartIndex, False.EndIndex);
            }

            return(new SucoConditionalExpression(StartIndex, EndIndex, condition, trueExpr, falseExpr, thisType));
        }
Esempio n. 4
0
        public override SucoListCondition DeduceTypes(SucoTypeEnvironment env, SucoContext context, SucoType elementType)
        {
            var innerExpression = Expression.DeduceTypes(env, context);

            if (!innerExpression.Type.ImplicitlyConvertibleTo(SucoType.Boolean))
            {
                throw new SucoCompileException($"A condition expression must be a boolean (or implicitly convertible to one).", StartIndex, EndIndex);
            }
            return(new SucoListExpressionCondition(StartIndex, EndIndex, innerExpression.ImplicitlyConvertTo(SucoType.Boolean)));
        }
Esempio n. 5
0
        public override SucoListCondition DeduceTypes(SucoTypeEnvironment env, SucoContext context, SucoType elementType)
        {
            switch (Name)
            {
            case "first":
            case "last":
            case "before":
            case "after":
            case "~":
                break;

            case "^":
            case ">":
            case "v":
            case "<":
            case "↑":
            case "→":
            case "↓":
            case "←":
            case "diagonal":
            case "adjacent":
            case "orthogonal":
            case "above":
            case "right":
            case "below":
            case "left":
            case "samerow":
            case "samecol":
            case "samebox":
            case "topleft":
            case "topright":
            case "bottomleft":
            case "bottomright":
            case "lefttop":
            case "righttop":
            case "leftbottom":
            case "rightbottom":
                if (!elementType.Equals(SucoType.Cell))
                {
                    throw new SucoCompileException($"“{Name}” can only be used on lists of cells.", StartIndex, EndIndex);
                }
                break;

            default:
                throw new SucoCompileException($"Unknown shortcut condition: “{Name}”.", StartIndex, EndIndex);
            }
            return(this);
        }
Esempio n. 6
0
 protected override SucoExpression deduceTypes(SucoTypeEnvironment env, SucoContext context)
 {
     try
     {
         var type = env.GetVariableType(Name);
         if (type == null)
         {
             throw new SucoCompileException($"Unknown variable “{Name}”.", StartIndex, EndIndex);
         }
         return(new SucoIdentifierExpression(StartIndex, EndIndex, Name, type));
     }
     catch (SucoTempCompileException tc)
     {
         throw new SucoCompileException(tc.Message, StartIndex, EndIndex);
     }
 }
Esempio n. 7
0
        protected override SucoExpression deduceTypes(SucoTypeEnvironment env, SucoContext context)
        {
            if (Elements.Count == 0)
            {
                throw new SucoCompileException("Empty array literals are not currently supported.", StartIndex, EndIndex);
            }
            var newElements = Elements.Select(e => e.DeduceTypes(env, context)).ToList();

            for (var i = 0; i < newElements.Count; i++)
            {
                if (newElements.All(e => e.Type.ImplicitlyConvertibleTo(newElements[i].Type)))
                {
                    return(new SucoArrayExpression(StartIndex, EndIndex, newElements.Select(e => e.ImplicitlyConvertTo(newElements[i].Type)).ToList(), newElements[i].Type.List()));
                }
            }
            throw new SucoCompileException("This array contains elements that are not compatible with one another.", StartIndex, EndIndex);
        }
Esempio n. 8
0
        protected override SucoExpression deduceTypes(SucoTypeEnvironment env, SucoContext context)
        {
            var newPieces = Pieces.Select(p =>
            {
                if (p is not SucoStringLiteralPieceExpression expr)
                {
                    return(p);
                }
                var typedExpr = expr.Expression.DeduceTypes(env, context);
                if (!typedExpr.Type.ImplicitlyConvertibleTo(SucoType.String))
                {
                    throw new SucoCompileException($"Expression interpolated into a string literal is of type “{typedExpr.Type}”, which not implicitly convertible to string.", expr.Expression.StartIndex, expr.Expression.EndIndex);
                }
                return(typedExpr.ImplicitlyConvertTo(SucoType.String));
            }).ToArray();

            return(new SucoStringLiteralExpression(StartIndex, EndIndex, newPieces, SucoType.String));
        }
Esempio n. 9
0
        protected override SucoExpression deduceTypes(SucoTypeEnvironment env, SucoContext context)
        {
            var op = Operand.DeduceTypes(env, context);

            try
            {
                var resultType = op.Type.GetUnaryOperatorType(Operator);
                if (resultType == null)
                {
                    throw new SucoCompileException($"Type “{op.Type}” does not support the “{Operator}” unary operator.", StartIndex, EndIndex);
                }
                return(new SucoUnaryOperatorExpression(StartIndex, EndIndex, op, Operator, resultType));
            }
            catch (SucoTempCompileException ce)
            {
                throw new SucoCompileException(ce.Message, StartIndex, EndIndex);
            }
        }
Esempio n. 10
0
        protected override SucoExpression deduceTypes(SucoTypeEnvironment env, SucoContext context)
        {
            var op = Operand.DeduceTypes(env, context);

            try
            {
                // Special case: the “.pos” member should work on any variable from a list comprehension
                if (MemberName == "pos" && op is SucoIdentifierExpression ident && env.IsVariableInListComprehension(ident.Name))
                {
                    return(new SucoPositionExpression(ident.StartIndex, ident.EndIndex, ident.Name, SucoType.Integer));
                }

                return(new SucoMemberAccessExpression(StartIndex, EndIndex, op, MemberName, op.Type.GetMemberType(MemberName, context)));
            }
            catch (SucoTempCompileException ce)
            {
                throw new SucoCompileException(ce.Message, StartIndex, EndIndex);
            }
        }
Esempio n. 11
0
        protected override SucoExpression deduceTypes(SucoTypeEnvironment env, SucoContext context)
        {
            var left  = Left.DeduceTypes(env, context);
            var right = Right.DeduceTypes(env, context);

            try
            {
                var resultType = left.Type.GetBinaryOperatorType(Operator, right.Type, context);
                if (resultType == null)
                {
                    throw new SucoCompileException($"Types “{left.Type}” and “{right.Type}” do not support the “{Operator}” operator.", left.StartIndex, right.EndIndex);
                }
                return(new SucoBinaryOperatorExpression(StartIndex, EndIndex, left, right, Operator, resultType));
            }
            catch (SucoTempCompileException ce)
            {
                throw new SucoCompileException(ce.Message, StartIndex, EndIndex);
            }
        }
Esempio n. 12
0
 public static bool IsValidCode(string source, SucoTypeEnvironment env, SucoContext context, SucoType expectedResultType, out string error)
 {
     try
     {
         ParseCode(source, env, context, expectedResultType);
         error = null;
         return(true);
     }
     catch (SucoCompileException sce)
     {
         error = sce.Message;
         return(false);
     }
     catch (SucoParseException spe)
     {
         error = spe.Message;
         return(false);
     }
 }
Esempio n. 13
0
        public SucoExpression DeduceTypes(SucoTypeEnvironment env, SucoContext context)
        {
            var result = deduceTypes(env, context);

            switch (context)
            {
            case SucoContext.Constraint:
                if (result.Type is SucoDecimalType)
                {
                    throw new SucoCompileException("You cannot use decimal numbers in a puzzle constraint.", StartIndex, EndIndex);
                }
                if (result.Type is SucoStringType)
                {
                    throw new SucoCompileException("You cannot use strings in a puzzle constraint.", StartIndex, EndIndex);
                }
                break;

            case SucoContext.Svg:
                break;
            }
            return(result);
        }
Esempio n. 14
0
        public SucoParser(string source, SucoContext context) : base(source, Ut.NewArray(

                                                                         // MAKE SURE that every multi-character token that has another token as its prefix comes before said prefix

                                                                         // Arithmetic
                                                                         "+", "-", "−", "*", "×", "^", "%", "/",
                                                                         // Relational
                                                                         "<=", "<", "≤", ">=", ">", "≥", "=", "!=", "≠",
                                                                         // Logical
                                                                         "&", "|", "?", ":", "!",
                                                                         // Structural
                                                                         "{", "}", ",", "(", ")", ".", "[", "]", ";",
                                                                         // Flags; "+" is listed in Arithmetic; "1" is parsed as a numeral
                                                                         "$",
                                                                         // String literals
                                                                         "\"",
                                                                         // Shortcut filters
                                                                         "~", "←", "→", "↑", "↓" // “v” parses as an identifier; “<”/“>” are in Relational; “^” is in Arithmetic
                                                                         ))
        {
            Context = context;
        }
Esempio n. 15
0
        protected override SucoExpression deduceTypes(SucoTypeEnvironment env, SucoContext context)
        {
            try
            {
                var operand = Operand.DeduceTypes(env, context);
                if (operand.Type is not SucoFunctionType fnc)
                {
                    throw new SucoCompileException($"“{operand.Type}” is not a function type.", operand.StartIndex, operand.EndIndex);
                }

                var newArguments = Arguments.Select(arg => arg.DeduceTypes(env, context)).ToArray();
                var(parameterTypes, returnType) = fnc.Resolve(newArguments.Select(a => a.Type).ToArray());
                for (var i = 0; i < newArguments.Length; i++)
                {
                    newArguments[i] = newArguments[i].ImplicitlyConvertTo(parameterTypes[i]);
                }
                return(new SucoCallExpression(StartIndex, EndIndex, operand, newArguments, returnType));
            }
            catch (SucoFunctionResolutionException re)
            {
                throw new SucoCompileException(re.Message, StartIndex, EndIndex);
            }
        }
Esempio n. 16
0
        public override SucoType GetBinaryOperatorType(BinaryOperator op, SucoType rightType, SucoContext context) => (op, rightType) switch
        {
            // Decimal op Decimal
            (BinaryOperator.Equal, SucoDecimalType) => SucoType.Boolean,
            (BinaryOperator.NotEqual, SucoDecimalType) => SucoType.Boolean,
            (BinaryOperator.LessThan, SucoDecimalType) => SucoType.Boolean,
            (BinaryOperator.LessThanOrEqual, SucoDecimalType) => SucoType.Boolean,
            (BinaryOperator.GreaterThan, SucoDecimalType) => SucoType.Boolean,
            (BinaryOperator.GreaterThanOrEqual, SucoDecimalType) => SucoType.Boolean,
            (BinaryOperator.Plus, SucoDecimalType) => SucoType.Decimal,
            (BinaryOperator.Minus, SucoDecimalType) => SucoType.Decimal,
            (BinaryOperator.Times, SucoDecimalType) => SucoType.Decimal,
            (BinaryOperator.Modulo, SucoDecimalType) => SucoType.Decimal,
            (BinaryOperator.Divide, SucoDecimalType) => SucoType.Decimal,
            (BinaryOperator.Power, SucoDecimalType) => SucoType.Decimal,

            // Decimal op Int
            (BinaryOperator.Equal, SucoIntegerType) => SucoType.Boolean,
            (BinaryOperator.NotEqual, SucoIntegerType) => SucoType.Boolean,
            (BinaryOperator.LessThan, SucoIntegerType) => SucoType.Boolean,
            (BinaryOperator.LessThanOrEqual, SucoIntegerType) => SucoType.Boolean,
            (BinaryOperator.GreaterThan, SucoIntegerType) => SucoType.Boolean,
            (BinaryOperator.GreaterThanOrEqual, SucoIntegerType) => SucoType.Boolean,
            (BinaryOperator.Plus, SucoIntegerType) => SucoType.Decimal,
            (BinaryOperator.Minus, SucoIntegerType) => SucoType.Decimal,
            (BinaryOperator.Times, SucoIntegerType) => SucoType.Decimal,
            (BinaryOperator.Modulo, SucoIntegerType) => SucoType.Decimal,
            (BinaryOperator.Divide, SucoIntegerType) => SucoType.Decimal,
            (BinaryOperator.Power, SucoIntegerType) => SucoType.Decimal,
            _ => base.GetBinaryOperatorType(op, rightType, context),
        };
Esempio n. 17
0
 protected abstract SucoExpression deduceTypes(SucoTypeEnvironment env, SucoContext context);
Esempio n. 18
0
        public static SucoExpression ParseCode(string source, SucoTypeEnvironment env, SucoContext context, SucoType expectedResultType = null)
        {
            if (source == null)
            {
                throw new ArgumentNullException(nameof(source));
            }

            try
            {
                // Try parsing as a standalone expression (e.g. “cells.unique”).
                var parser = new SucoParser(source, context);
                var ret    = parser.parseExpression();
                parser.EnforceEof();
                ret = ret.DeduceTypes(env, context);
                if (expectedResultType != null && !ret.Type.ImplicitlyConvertibleTo(expectedResultType))
                {
                    throw new SucoParseException($"The expression is of type “{ret.Type}”, which is not implicitly convertible to the required type, “{expectedResultType}”.", ret.StartIndex, ret.EndIndex);
                }
                return(expectedResultType == null ? ret : ret.ImplicitlyConvertTo(expectedResultType));
            }
            catch (SucoParseException pe1)
            {
                try
                {
                    // Try parsing as a list comprehension (e.g. “a: a.odd”).
                    var parser = new SucoParser(source, context);
                    var ret    = parser.parseListComprehension();
                    parser.EnforceEof();
                    ret = ret.DeduceTypes(env, context);
                    if (expectedResultType != null && !ret.Type.ImplicitlyConvertibleTo(expectedResultType))
                    {
                        throw new SucoParseException($"The expression is of type “{ret.Type}”, which is not implicitly convertible to the required type, “{expectedResultType}”.", ret.StartIndex, ret.EndIndex);
                    }
                    return(expectedResultType == null ? ret : ret.ImplicitlyConvertTo(expectedResultType));
                }
                catch (SucoParseException pe2) when(pe2.StartIndex <= pe1.StartIndex)
                {
                }
                throw;
            }
        }
Esempio n. 19
0
 public abstract SucoListCondition DeduceTypes(SucoTypeEnvironment env, SucoContext context, SucoType elementType);
Esempio n. 20
0
 public override SucoType GetBinaryOperatorType(BinaryOperator op, SucoType rightType, SucoContext context) => (op, rightType) switch
 {
     (BinaryOperator.Equal, SucoBooleanType) => SucoType.Boolean,
     (BinaryOperator.NotEqual, SucoBooleanType) => SucoType.Boolean,
     (BinaryOperator.And, SucoBooleanType) => SucoType.Boolean,
     (BinaryOperator.Or, SucoBooleanType) => SucoType.Boolean,
     _ => base.GetBinaryOperatorType(op, rightType, context)
 };
Esempio n. 21
0
 public override SucoType GetMemberType(string memberName, SucoContext context) => memberName switch
 {
     "hash" => SucoType.String,
Esempio n. 22
0
 protected override SucoExpression deduceTypes(SucoTypeEnvironment env, SucoContext context) => this;
Esempio n. 23
0
 public override SucoType GetMemberType(string memberName, SucoContext context) => memberName switch
 {
     //"value" => SucoType.Integer,
     "x" => SucoType.Integer,
Esempio n. 24
0
 public virtual SucoType GetBinaryOperatorType(BinaryOperator op, SucoType rightType, SucoContext context) => throw new SucoTempCompileException($"Binary operator “{op}” not defined on types “{this}” and “{rightType}”.");
Esempio n. 25
0
        public override SucoType GetBinaryOperatorType(BinaryOperator op, SucoType rightType, SucoContext context) => (op, rightType) switch
        {
            // Comparison with Int
            (BinaryOperator.Equal, SucoIntegerType) => SucoType.Boolean,
            (BinaryOperator.NotEqual, SucoIntegerType) => SucoType.Boolean,
            (BinaryOperator.LessThan, SucoIntegerType) => SucoType.Boolean,
            (BinaryOperator.LessThanOrEqual, SucoIntegerType) => SucoType.Boolean,
            (BinaryOperator.GreaterThan, SucoIntegerType) => SucoType.Boolean,
            (BinaryOperator.GreaterThanOrEqual, SucoIntegerType) => SucoType.Boolean,

            // Comparison with Decimal
            (BinaryOperator.Equal, SucoDecimalType) => SucoType.Boolean,
            (BinaryOperator.NotEqual, SucoDecimalType) => SucoType.Boolean,
            (BinaryOperator.LessThan, SucoDecimalType) => SucoType.Boolean,
            (BinaryOperator.LessThanOrEqual, SucoDecimalType) => SucoType.Boolean,
            (BinaryOperator.GreaterThan, SucoDecimalType) => SucoType.Boolean,
            (BinaryOperator.GreaterThanOrEqual, SucoDecimalType) => SucoType.Boolean,

            // Arithmetic with Int
            (BinaryOperator.Plus, SucoIntegerType) => SucoType.Integer,
            (BinaryOperator.Minus, SucoIntegerType) => SucoType.Integer,
            (BinaryOperator.Times, SucoIntegerType) => SucoType.Integer,
            (BinaryOperator.Modulo, SucoIntegerType) => SucoType.Integer,
            (BinaryOperator.Divide, SucoIntegerType) => context != SucoContext.Constraint ? SucoType.Decimal : throw new SucoTempCompileException("Suco does not allow the use of division in puzzle constraints. Rewrite the equation to use multiplication instead (for example: instead of a.value/b.value = 2, write a.value = 2*b.value)."),
                  (BinaryOperator.Power, SucoIntegerType) => SucoType.Integer,

                  // Arithmetic with Decimal
                  (BinaryOperator.Plus, SucoDecimalType) => SucoType.Decimal,
                  (BinaryOperator.Minus, SucoDecimalType) => SucoType.Decimal,
                  (BinaryOperator.Times, SucoDecimalType) => SucoType.Decimal,
                  (BinaryOperator.Modulo, SucoDecimalType) => SucoType.Decimal,
                  (BinaryOperator.Power, SucoDecimalType) => SucoType.Decimal,
                  _ => base.GetBinaryOperatorType(op, rightType, context),
        };
Esempio n. 26
0
 protected override SucoExpression deduceTypes(SucoTypeEnvironment env, SucoContext context) => new SucoBooleanLiteralExpression(StartIndex, EndIndex, LiteralValue, SucoType.Boolean);
Esempio n. 27
0
 public virtual SucoType GetMemberType(string memberName, SucoContext context) => throw new SucoTempCompileException($"Member “{memberName}” is not defined on type “{this}”.");