示例#1
0
        public T GetVariable <T>(string name)
            where T : Value
        {
            SprakType type = SprakType.NetToSprakLookup[typeof(T)];

            return((T)GetVariable(name, type));
        }
示例#2
0
        public Value GetVariable(string name, SprakType targetType)
        {
            Value rawResult = GetVariable(name);

            _converter.TryConvertValue(rawResult, targetType, out Value result);
            return(result);
        }
示例#3
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);
        }
示例#4
0
        public bool CanAccept(OperatorTypeSignature other, AssignmentResolver resolver)
        {
            if (other.Inputs != Inputs)
            {
                return(false);
            }

            if (Inputs == InputSides.Both || Inputs == InputSides.Left)
            {
                SprakType destination = LeftParam;
                SprakType source      = other.LeftParam;
                if (!resolver.IsAssignable(source, destination))
                {
                    return(false);
                }
            }

            if (Inputs == InputSides.Both || Inputs == InputSides.Right)
            {
                SprakType destination = RightParam;
                SprakType source      = other.RightParam;
                if (!resolver.IsAssignable(source, destination))
                {
                    return(false);
                }
            }

            return(true);
        }
示例#5
0
 public OperatorTypeSignature(
     SprakType left, SprakType right, InputSides inputs)
 {
     LeftParam  = left;
     RightParam = right;
     Inputs     = inputs;
 }
示例#6
0
        private bool IsDirect(SprakType source, SprakType destination)
        {
            // Obviously if the destination accepts "any" then the current value is okay
            // The source being "any" is a problem - there is no way of checking.
            // Current the principle source of "any" is the array indexer, since arrays can contain any combination of values.

            // At somepoint, the assignment resolver should be modified so that
            // a source of any yields "IsDirect" as true, but "IsPerfect" as
            // false, perhaps? Or maybe this is a special case better handled
            // by the code using the resolution.

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

            if (destination == source)
            {
                return(true);
            }

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

            return(false);
        }
示例#7
0
        private void ParseReturnType(MethodInfo info, out SprakType returnType)
        {
            if (!SprakType.NetToSprakLookup.TryGetValue(info.ReturnType, out returnType))
            {
                string returnName = info.ReturnType.Name;
                string methodName = info.DeclaringType.Name + "." + info.Name;
                string message    = $"Unable to recognize return type {returnName} of {methodName} as a Sprak type";

                throw new ArgumentException(message);
            }
        }
示例#8
0
文件: Library.cs 项目: Seti-0/NSprak
        private void AddOperator(MethodInfo methodInfo, SprakOperatorAttribute meta)
        {
            BuiltInFunction function = new BuiltInFunction(methodInfo, this);

            if (!Operator.TryParse(out Operator op, name: meta.OperatorName))
            {
                string message = $"{MethodName(methodInfo)} is declared to be an unrecognized operator: \"{meta.OperatorName}\"";
                throw new ArgumentException(message);
            }

            int paramCount = function.Parameters.Count;

            bool binary        = meta.InputsHint == InputSides.Both;
            int  requiredCount = binary ? 2 : 1;

            if (paramCount != requiredCount)
            {
                string desc = binary ? "binary" : "unary";

                string message = $"{MethodName(methodInfo)} was declared to be the {desc} operator \"{meta.OperatorName}\", " +
                                 $"but has {function.Parameters.Count} Sprak arguments";

                throw new ArgumentException(message);
            }

            InputSides inputs = meta.InputsHint;

            SprakType left  = null;
            SprakType right = null;

            switch (inputs)
            {
            case InputSides.Both:
                left  = function.Parameters[0];
                right = function.Parameters[1];
                break;

            case InputSides.Left:
                left = function.Parameters[0];
                break;

            case InputSides.Right:
                right = function.Parameters[0];
                break;
            }

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

            OperatorSignature signature = new OperatorSignature(op.Name, typeSignature);

            _operators.Add(signature, function);
        }
示例#9
0
        public Value PopValue(SprakType type)
        {
            Value value = PopValue();

            if (!_converter.TryConvertValue(value, type, out Value result))
            {
                PushValue(value); // This is a recoverable runtime error, so restore the stack.
                throw new SprakRuntimeException(Messages.UnexpectedType, type, value.Type, value);
            }

            return(result);
        }
示例#10
0
        public bool NextIsType(out SprakType type)
        {
            if (Next(out Token token) && token.Type == TokenType.Type)
            {
                type = token.AssertType();
                MoveNext();
                return(true);
            }

            type = null;
            return(false);
        }
示例#11
0
        public static SprakType AssertType(this Token token)
        {
            token.AssertType(TokenType.Type);

            if (SprakType.TryParse(token.Content, out SprakType result))
            {
                return(result);
            }

            else
            {
                throw new TokenCheckException(token, $"Failed to parse Sprak Type: {token.Type}");
            }
        }
示例#12
0
        public bool IsAssignable(SprakType source, SprakType destination)
        {
            if (IsDirect(source, destination))
            {
                return(true);
            }

            HashSet <SprakType> possibleSources;

            if (!_possibleConversions.TryGetValue(destination, out possibleSources))
            {
                return(false);
            }

            return(possibleSources.Contains(destination));
        }
示例#13
0
        public bool IsPerfect(SprakType source, SprakType destination)
        {
            if (IsDirect(source, destination))
            {
                return(true);
            }

            ConversionTypeSignature signature = new ConversionTypeSignature(source, destination);

            if (_informationLoss.TryGetValue(signature, out bool perfect))
            {
                return(perfect);
            }

            else
            {
                throw new ArgumentException($"Unrecognized conversion from {source} to {destination}");
            }
        }
示例#14
0
        public bool CanAccept(FunctionTypeSignature target, AssignmentResolver resolver)
        {
            if (target.Parameters.Count != Parameters.Count)
            {
                return(false);
            }

            for (int i = 0; i < Parameters.Count; i++)
            {
                SprakType destination = Parameters[i];
                SprakType source      = target.Parameters[i];

                if (!resolver.IsAssignable(source, destination))
                {
                    return(false);
                }
            }

            return(true);
        }
示例#15
0
        private void ParseParameters(MethodInfo info, out bool acceptsContext, out List <SprakType> types, out List <string> names)
        {
            types          = new List <SprakType>();
            names          = new List <string>();
            acceptsContext = false;

            ParameterInfo[] parameters = info.GetParameters();

            if (parameters.Length == 0)
            {
                return;
            }

            int index = 0;

            if (parameters[0].ParameterType == typeof(ExecutionContext))
            {
                acceptsContext = true;
                index++;
            }

            while (index < parameters.Length)
            {
                if (SprakType.TryGetSprak(parameters[index].ParameterType, out SprakType sprakType))
                {
                    types.Add(sprakType);
                    names.Add(parameters[index].Name);
                }
                else
                {
                    string paramName  = parameters[index].Name;
                    string typeName   = parameters[index].ParameterType.Name;
                    string methodName = info.DeclaringType.Name + "." + info.Name;
                    string message    = $"Unable to recogize the type of parameter [{index} ({paramName}) of {methodName}], {typeName}, as a Sprak type";

                    throw new ArgumentException(message);
                }

                index++;
            }
        }
示例#16
0
        public BuiltInFunction(MethodInfo methodInfo, Library parent)
        {
            _methodInfo = methodInfo;

            string name = methodInfo
                          .GetCustomAttribute <SprakNameOverride>()
                          ?.Name;

            name ??= methodInfo.Name;

            List <SprakType> types;
            List <string>    names;

            ParseParameters(methodInfo, out _acceptsContext, out types, out names);

            Signature      = new FunctionSignature(parent.UniqueID, name, new FunctionTypeSignature(types));
            ParameterNames = names;

            ParseReturnType(methodInfo, out SprakType returnType);
            ReturnType = returnType;
        }
示例#17
0
 public FunctionParameter(string name, SprakType type)
 {
     Name = name;
     Type = type;
 }
示例#18
0
        private void Update(Expression expression, CompilationEnvironment env)
        {
            // Type hints can be dependent on child expressions.
            // (They are indie of parents, and neighbours)
            Update(expression.GetSubExpressions(), env);

            switch (expression)
            {
            case FunctionCall function:
                ResolveCallAndTypeHint(function, env);
                break;

            case Group group:
                group.TypeHint = group.Value.TypeHint;
                break;

            case LiteralArrayGet array:
                array.TypeHint = SprakType.Array;
                break;

            case Indexer indexer:

                if (!(indexer.SourceExpression is VariableReference))
                {
                    env.Messages.AtExpression(indexer.SourceExpression,
                                              Messages.IndexerNotSupported);
                }

                SprakType sourceType = indexer.SourceExpression.TypeHint;

                // The source should be an array
                if (sourceType != null && sourceType != SprakType.Array && sourceType != SprakType.Any)
                {
                    env.Messages.AtExpression(indexer.SourceExpression,
                                              Messages.CanOnlyIndexArrays, sourceType.Text);
                }

                // The index should be a number
                SprakType indexType = indexer.IndexExpression.TypeHint;
                if (indexType != null && indexType != SprakType.Number && indexType != SprakType.Any)
                {
                    env.Messages.AtExpression(indexer.IndexExpression,
                                              Messages.IndexShouldBeNumber, indexType.Text);
                }

                // This is unfortunate, and to be worked on
                // later.
                indexer.TypeHint = SprakType.Any;

                break;

            case LiteralGet literal:
                literal.TypeHint = literal.Value.Type;
                break;

            case OperatorCall op:
                ResolveCallAndTypeHint(op, env);
                break;

            case Return ret:

                if (ret.HasValue)
                {
                    ret.TypeHint = ret.Value.TypeHint;
                }

                Block parent = ret.ParentBlockHint;
                while (parent != null && !(parent.Header is FunctionHeader))
                {
                    parent = parent.ParentBlockHint;
                }

                if (parent == null)
                {
                    env.Messages.AtToken(ret.ReturnToken,
                                         Messages.ReturnOutsideFunction);
                }

                else
                {
                    SprakType src = ret.TypeHint;
                    SprakType dst = ((FunctionHeader)parent.Header).ReturnType;

                    if (ret.HasValue)
                    {
                        if (dst == SprakType.Unit)
                        {
                            env.Messages.AtExpression(ret.Value,
                                                      Messages.ReturnValueFromVoid);
                        }

                        else if (src != null)
                        {
                            if (!env.AssignmentLookup.IsAssignable(src, dst))
                            {
                                env.Messages.AtExpression(ret.Value,
                                                          Messages.IncompatibleReturnValue,
                                                          src.Text, dst.Text);
                            }
                        }
                    }
                    else
                    {
                        if (dst != SprakType.Unit)
                        {
                            env.Messages.AtToken(ret.ReturnToken,
                                                 Messages.MissingReturnValue);
                        }
                    }
                }

                break;

            case VariableAssignment assignment:
                ResolveAssignment(assignment, env);
                break;

            case VariableReference variable:

                if (variable.ParentBlockHint.TryGetVariableInfo(variable.Name, out VariableInfo info))
                {
                    Block refAncestor = variable.ParentBlockHint;
                    while (refAncestor != null && refAncestor != info.Source)
                    {
                        refAncestor = refAncestor.ParentBlockHint;
                    }

                    if (refAncestor == null)
                    {
                        env.Messages.AtToken(variable.Token,
                                             Messages.VariableFromDisconnectedBlock);
                    }

                    else if (variable.StartToken.Start < info.DeclarationEnd)
                    {
                        env.Messages.AtToken(variable.Token,
                                             Messages.ReferenceBeforeDefinition, variable.Name);
                    }

                    variable.TypeHint = info.DeclaredType;
                }
                else
                {
                    env.Messages.AtToken(variable.Token,
                                         Messages.UnrecognizedName, variable.Name);
                    variable.TypeHint = null;
                }

                break;
            }
        }
示例#19
0
        private void ResolveCallAndTypeHint(OperatorCall call, CompilationEnvironment env)
        {
            if (!Operator.TryParse(out Operator op, text: call.OperatorText))
            {
                env.Messages.AtToken(call.OperatorToken,
                                     Messages.UnrecognizedOperator, call.OperatorText);
                return;
            }

            if (op.IsAssignment)
            {
                env.Messages.AtToken(call.OperatorToken,
                                     Messages.IncorrectUseOfAssignment, op.Text);
                return;
            }

            call.TypeHint = null;

            if (call.LeftInput != null && call.LeftInput.TypeHint == null)
            {
                return;
            }

            if (call.RightInput != null && call.RightInput.TypeHint == null)
            {
                return;
            }

            SprakType  left  = call.LeftInput?.TypeHint;
            SprakType  right = call.RightInput?.TypeHint;
            InputSides inputs;

            if (left != null && right != null)
            {
                inputs = InputSides.Both;
            }

            else if (left != null)
            {
                inputs = InputSides.Left;
            }

            else
            {
                inputs = InputSides.Right;
            }

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

            SignatureLookupResult lookupResult;

            lookupResult = env.SignatureLookup
                           .TryFindMatch(op.Name, signature);

            if (lookupResult.Success)
            {
                call.BuiltInFunctionHint = lookupResult.BuiltInFunction;
                call.TypeHint            = lookupResult.FunctionInfo?.ReturnType;
            }
            else
            {
                string operation = $"({call.LeftInput?.TypeHint?.Text})"
                                   + $" {call.OperatorToken.Content}"
                                   + $" ({call.RightInput?.TypeHint?.Text})";

                env.Messages.AtToken(call.OperatorToken,
                                     Messages.UnresolvedOperation, operation);
            }
        }
示例#20
0
 public FunctionInfo(FunctionHeader header)
 {
     Signature  = header.Signature;
     ReturnType = header.ReturnType;
 }
示例#21
0
 public FunctionInfo(FunctionSignature signature, SprakType returnType)
 {
     Signature  = signature;
     ReturnType = returnType;
 }
示例#22
0
 private void RenderType(SprakType type)
 {
     Write(type?.InternalName, Theme.Expressions.Type);
 }
示例#23
0
 public VariableInfo(SprakType type, int offset, Block source)
 {
     DeclaredType   = type;
     DeclarationEnd = offset;
     Source         = source;
 }
示例#24
0
 public void AssertType(out SprakType type)
 {
     Assert(out Token current);
     type = current.AssertType();
 }
示例#25
0
        private void UpdateBlock(Block block,
                                 Messenger messenger, Dictionary <FunctionSignature, FunctionInfo> functions)
        {
            if (block.ScopeHint == null)
            {
                block.ScopeHint = new Scope();
            }

            if (block.Header is IfHeader ifHeader)
            {
                ApplyCombinedScope(ifHeader);
            }

            Scope scope = block.ScopeHint;

            // Three steps

            // First: check if the header declares any variables - function arguments, for example

            switch (block.Header)
            {
            case FunctionHeader function:

                for (int i = 0; i < function.ParameterCount; i++)
                {
                    string    name = function.ParameterNames[i];
                    SprakType type = function.ParameterTypes[i];
                    scope.VariableDeclarations
                    .Add(name, new VariableInfo(type, -1, block));
                }

                break;

            case LoopHeader loop:

                string loopVarName = null;

                if (loop.HasName)
                {
                    loopVarName = loop.Name;
                }

                else if (loop.IsForeach)
                {
                    loopVarName = "@";
                }

                if (loopVarName != null)
                {
                    SprakType loopVarType;
                    if (loop.IsRange)
                    {
                        loopVarType = SprakType.Number;
                    }
                    else
                    {
                        loopVarType = SprakType.Any;
                    }

                    // we need an "undetermined" type here
                    scope.VariableDeclarations
                    .Add(loopVarName, new VariableInfo(loopVarType, -1, block));
                }

                break;
            }

            // Second: check if variables or functions have been declared in the statements of the block.
            // If there is a subblock, apply this update to that block as well.

            // Note that a subblock can a function declaration, but any other declarations in that subblock belong to its hints,
            // not the the ones for the current block.

            foreach (Expression expression in block.Statements)
            {
                switch (expression)
                {
                case VariableAssignment assignment:

                    if (assignment.IsDeclaration)
                    {
                        if (scope.VariableDeclarations.ContainsKey(assignment.Name))
                        {
                            messenger.AtToken(assignment.NameToken,
                                              Messages.DuplicateVariable, assignment.Name);
                        }

                        else
                        {
                            scope.VariableDeclarations.Add(assignment.Name,
                                                           new VariableInfo(assignment.DeclarationType,
                                                                            assignment.EndToken.End, block));
                        }
                    }

                    break;

                case Block subBlock:

                    if (subBlock.Header is FunctionHeader function)
                    {
                        if (functions.ContainsKey(function.Signature))
                        {
                            messenger.AtToken(function.NameToken,
                                              Messages.DuplicateFunction, function.Name);
                        }

                        else
                        {
                            functions.Add(function.Signature,
                                          new FunctionInfo(function));
                        }
                    }

                    UpdateBlock(subBlock, messenger, functions);

                    break;
                }
            }

            block.ScopeHint = scope;
        }
示例#26
0
        public T PopValue <T>() where T : Value
        {
            SprakType target = SprakType.NetToSprakLookup[typeof(T)];

            return(PopValue(target) as T);
        }