예제 #1
0
        public bool TryConvertValue(Value source, SprakType destinationType, out Value destination)
        {
            if (source.Type == destinationType)
            {
                destination = source;
                return(true);
            }

            if (destinationType == SprakType.Any)
            {
                destination = source;
                return(true);
            }

            if (destinationType == SprakType.String)
            {
                destination = source.ToSprakString();
                return(true);
            }

            ConversionTypeSignature signature = new ConversionTypeSignature(source.Type, destinationType);

            SignatureLookupResult result = _resolver.TryFindMatch(signature);

            if (!result.Success)
            {
                destination = null;
                return(false);
            }

            destination = result.BuiltInFunction.Call(new Value[] { source }, _context);
            return(true);
        }
예제 #2
0
        private void ResolveAssignment(VariableAssignment assignment, CompilationEnvironment env)
        {
            if (assignment.Indices.Count > 1)
            {
                env.Messages.AtExpression(assignment.Indices.Last().Index,
                                          Messages.MultipleIndices);
            }

            if (!assignment.ParentBlockHint
                .TryGetVariableInfo(assignment.Name, out VariableInfo nameInfo))
            {
                env.Messages.AtToken(assignment.NameToken,
                                     Messages.UnrecognizedName, assignment.NameToken.Content);
                return;
            }

            if (!assignment.IsDeclaration)
            {
                Block ancestor = assignment.ParentBlockHint;
                while (ancestor != null && ancestor != nameInfo.Source)
                {
                    ancestor = ancestor.ParentBlockHint;
                }

                if (ancestor == null)
                {
                    env.Messages.AtToken(assignment.NameToken,
                                         Messages.VariableFromDisconnectedBlock);
                }
            }

            if (nameInfo.DeclaredType == null)
            {
                // I don't think this should be possible, but this is all
                // very convoluted. I wish C# was more nullable-aware.
                throw new Exception("Declaration with Declaration Type");
            }

            SprakType dstType;

            if (assignment.Indices.Count == 0)
            {
                dstType = nameInfo.DeclaredType;
            }

            else
            {
                if (nameInfo.DeclaredType != SprakType.Array)
                {
                    env.Messages.AtToken(assignment.NameToken,
                                         Messages.CanOnlyIndexArrays, nameInfo.DeclaredType);
                }

                // Best we can do with array assignments until generic
                // type tracking is introduced.
                dstType = SprakType.Any;
            }

            SprakType srcType;

            if (assignment.Value == null)
            {
                srcType = nameInfo.DeclaredType;
            }

            else if (assignment.Value.TypeHint != null)
            {
                srcType = assignment.Value.TypeHint;
            }

            else
            {
                srcType = SprakType.Any;
            }

            if (assignment.IsDeclaration)
            {
                if (assignment.Operator != Operator.Set)
                {
                    env.Messages.AtToken(assignment.OperatorToken,
                                         Messages.InvalidDeclarationOperator);
                }

                if (assignment.Indices.Count > 0)
                {
                    env.Messages.AtExpression(assignment.Indices.First().Index,
                                              Messages.InvalidIndexDeclaration);
                }
            }
            else if (!assignment.Operator.IsAssignment)
            {
                env.Messages.AtToken(assignment.OperatorToken,
                                     Messages.ExpectedAssignmentOperator, assignment.Operator.Text);
                return;
            }
            else if (assignment.Operator.AssignmentOperation != null)
            {
                // I doubt I'll bother implementing right-only inputs.
                // (Like --i) They don't show up much.

                InputSides inputs;
                SprakType  left;
                SprakType  right;

                if (!assignment.HasValue)
                {
                    inputs = InputSides.Left;
                    left   = srcType;
                    right  = null;
                }
                else
                {
                    inputs = InputSides.Both;
                    left   = srcType;
                    right  = dstType;
                }

                // We need the name of the function to called before assignment.
                // That may or may not be the same name as that of the operator.
                string name = assignment.Operator.AssignmentOperation;

                OperatorTypeSignature signature
                    = new OperatorTypeSignature(left, right, inputs);

                SignatureLookupResult lookupResult = env.SignatureLookup
                                                     .TryFindMatch(name, signature);

                if (lookupResult.Success)
                {
                    assignment.BuiltInFunctionHint = lookupResult.BuiltInFunction;
                    assignment.OpHint = lookupResult.OpBuilder;
                    srcType           = lookupResult.BuiltInFunction.ReturnType;
                }
                else
                {
                    string operation = assignment.ToString();
                    env.Messages.AtExpression(assignment,
                                              Messages.UnresolvedOperation, operation);

                    srcType = SprakType.Any;
                }
            }

            if (!env.AssignmentLookup.IsAssignable(srcType, dstType))
            {
                env.Messages.AtExpression(
                    assignment, Messages.AssignmentTypeMismatch, srcType, dstType);
            }
        }