private IPythonType TryDetermineReturnValue() { var annotationType = Eval.GetTypeFromAnnotation(FunctionDefinition.ReturnAnnotation); if (!annotationType.IsUnknown()) { // Annotations are typically types while actually functions return // instances unless specifically annotated to a type such as Type[T]. // TODO: try constructing argument set from types. Consider Tuple[_T1, _T2] where _T1 = TypeVar('_T1', str, bytes) var t = annotationType.CreateInstance(ArgumentSet.Empty(FunctionDefinition.ReturnAnnotation, Eval)); // If instance could not be created, such as when return type is List[T] and // type of T is not yet known, just use the type. var instance = t.IsUnknown() ? (IMember)annotationType : t; _overload.SetReturnValue(instance, true); _overload.SetReturnValue(instance, true); } else { // Check if function is a generator var suite = FunctionDefinition.Body as SuiteStatement; var yieldExpr = suite?.Statements.OfType <ExpressionStatement>().Select(s => s.Expression as YieldExpression).ExcludeDefault().FirstOrDefault(); if (yieldExpr != null) { // Function return is an iterator var yieldValue = Eval.GetValueFromExpression(yieldExpr.Expression) ?? Eval.UnknownType; var returnValue = new PythonGenerator(Eval.Interpreter, yieldValue); _overload.SetReturnValue(returnValue, true); } } return(annotationType); }
private void Assign(SequenceExpression seq, ValueEnumerator valueEnum) { foreach (var item in seq.Items) { switch (item) { case StarredExpression stx when stx.Expression is NameExpression nex && !string.IsNullOrEmpty(nex.Name): AssignVariable(nex, valueEnum.Next()); break; case ParenthesisExpression pex when pex.Expression is NameExpression nex && !string.IsNullOrEmpty(nex.Name): AssignVariable(nex, valueEnum.Next()); break; case NameExpression nex when !string.IsNullOrEmpty(nex.Name): AssignVariable(nex, valueEnum.Next()); break; // Nested sequence expression in sequence, Tuple[Tuple[int, str], int], List[Tuple[int], str] // TODO: Because of bug with how collection types are constructed, they don't make nested collection types // into instances, meaning we have to create it here case SequenceExpression se when valueEnum.Peek is IPythonCollection || valueEnum.Peek is IPythonCollectionType: var collection = valueEnum.Next(); var pc = collection as IPythonCollection; var pct = collection as IPythonCollectionType; Assign(se, pc ?? pct.CreateInstance(ArgumentSet.Empty(se, Eval))); break; case SequenceExpression se: Assign(se, valueEnum); break; } } }
public IMember GetValueFromCallable(CallExpression expr, LookupOptions lookupOptions = LookupOptions.Normal) { if (expr?.Target == null) { return(null); } var target = GetValueFromExpression(expr.Target, lookupOptions); target?.AddReference(GetLocationOfName(expr.Target)); var result = GetValueFromGeneric(target, expr, lookupOptions); if (result != null) { return(result); } // Should only be two types of returns here. First, an bound type // so we can invoke Call over the instance. Second, an type info // so we can create an instance of the type (as in C() where C is class). IMember value = null; var args = ArgumentSet.Empty(expr, this); switch (target) { case IPythonBoundType bt: // Bound property, method or an iterator. value = GetValueFromBound(bt, expr); break; case IPythonInstance pi: value = GetValueFromInstanceCall(pi, expr); break; case IPythonFunctionType ft: // Standalone function or a class method call. var instance = ft.DeclaringType?.CreateInstance(args) as IPythonInstance; value = GetValueFromFunctionType(ft, instance, expr); break; case IPythonClassType cls: value = GetValueFromClassCtor(cls, expr); break; case IPythonType t: // Target is type (info), the call creates instance. // For example, 'x = C; y = x()' or 'x = C()' where C is class value = t.CreateInstance(args); break; } if (value == null) { Log?.Log(TraceEventType.Verbose, $"Unknown callable: {expr.Target.ToCodeString(Ast).Trim()}"); } return(value); }
private void DeclareParameter(Parameter p, ParameterInfo pi) { IPythonType paramType; // If type is known from annotation, use it. if (pi != null && !pi.Type.IsUnknown() && !pi.Type.IsGenericParameter()) { // TODO: technically generics may have constraints. Should we consider them? paramType = pi.Type; } else { paramType = pi?.DefaultValue?.GetPythonType() ?? UnknownType; } DeclareVariable(p.Name, paramType.CreateInstance(ArgumentSet.Empty(p.NameExpression, this)), VariableSource.Declaration, p.NameExpression); }
public IMember GetValueFromIndex(IndexExpression expr, LookupOptions lookupOptions = LookupOptions.Normal) { if (expr?.Target == null) { return(null); } var target = GetValueFromExpression(expr.Target, lookupOptions); // Try generics first since this may be an expression like Dict[int, str] var result = GetValueFromGeneric(target, expr, lookupOptions); if (result != null) { return(result); } if (expr.Index is SliceExpression || expr.Index is TupleExpression) { // When slicing, assume result is the same type return(target); } var type = target.GetPythonType(); if (type != null) { if (!(target is IPythonInstance instance)) { instance = type.CreateInstance(ArgumentSet.Empty(expr, this)) as IPythonInstance; } if (instance != null) { var index = GetValueFromExpression(expr.Index, lookupOptions); if (index != null) { return(type.Index(instance, new ArgumentSet(new[] { index }, expr, this))); } } } return(UnknownType); }
public IMember GetValueFromClassCtor(IPythonClassType cls, CallExpression expr) { SymbolTable.Evaluate(cls.ClassDefinition); // Determine argument types var args = ArgumentSet.Empty(expr, this); var init = cls.GetMember <IPythonFunctionType>(@"__init__"); if (init != null) { using (OpenScope(cls.DeclaringModule, cls.ClassDefinition, out _)) { var a = new ArgumentSet(init, 0, cls, expr, this); if (a.Errors.Count > 0) { // AddDiagnostics(Module.Uri, a.Errors); } args = a.Evaluate(); } } return(cls.CreateInstance(args)); }
private void HandleTypedVariable(IPythonType variableType, IMember value, Expression expr) { // Check value type for compatibility IMember instance = null; if (value != null) { var valueType = value.GetPythonType(); if (!variableType.IsUnknown() && !valueType.Equals(variableType)) { // TODO: warn incompatible value type. // TODO: verify values. Value may be list() while variable type is List[str]. // Leave it as variable type. } else { instance = value; } } var args = ArgumentSet.Empty(expr, Eval); instance = instance ?? variableType?.CreateInstance(args) ?? Eval.UnknownType.CreateInstance(ArgumentSet.WithoutContext); if (expr is NameExpression ne) { Eval.DeclareVariable(ne.Name, instance, VariableSource.Declaration, ne); return; } if (expr is MemberExpression m) { // self.x : int = 42 var self = Eval.LookupNameInScopes("self", out var scope); var argType = self?.GetPythonType(); if (argType is PythonClassType cls && scope != null) { cls.AddMember(m.Name, instance, true); } } }
private IMember GetValueFromUnaryOp(UnaryExpression expr, string op, LookupOptions lookupOptions) { var target = GetValueFromExpression(expr.Expression, lookupOptions); if (target is IPythonInstance instance) { var fn = instance.GetPythonType()?.GetMember <IPythonFunctionType>(op); // Process functions declared in code modules. Scraped/compiled/stub modules do not actually perform any operations. if (fn?.DeclaringModule != null && (fn.DeclaringModule.ModuleType == ModuleType.User || fn.DeclaringModule.ModuleType == ModuleType.Library)) { var result = fn.Call(instance, op, ArgumentSet.Empty(expr, this)); if (!result.IsUnknown()) { return(result); } } return(instance is IPythonConstant c && instance.TryGetConstant <int>(out var value) ? new PythonConstant(-value, c.Type) : instance); } return(UnknownType); }
public static IMember GetReturnValueFromAnnotation(ExpressionEval eval, Expression annotation) { if (eval == null || annotation == null) { return(null); } var annotationType = eval.GetTypeFromAnnotation(annotation, LookupOptions.All); if (annotationType.IsUnknown()) { return(null); } // Annotations are typically types while actually functions return // instances unless specifically annotated to a type such as Type[T]. // TODO: try constructing argument set from types. Consider Tuple[_T1, _T2] where _T1 = TypeVar('_T1', str, bytes) var t = annotationType.CreateInstance(ArgumentSet.Empty(annotation, eval)); // If instance could not be created, such as when return type is List[T] and // type of T is not yet known, just use the type. var instance = t.IsUnknown() ? (IMember)annotationType : t; return(instance); }
public IMember GetConstantFromLiteral(Expression expr) { if (expr is ConstantExpression ce) { switch (ce.Value) { case string s: return(new PythonUnicodeString(s, Interpreter)); case AsciiString b: return(new PythonAsciiString(b, Interpreter)); case int integer: return(new PythonConstant(integer, Interpreter.GetBuiltinType(BuiltinTypeId.Int))); case bool b: return(new PythonConstant(b, Interpreter.GetBuiltinType(BuiltinTypeId.Bool))); } } var t = SuppressBuiltinLookup ? UnknownType : (GetTypeFromLiteral(expr) ?? UnknownType); return(t.CreateInstance(ArgumentSet.Empty(expr, this))); }
public override IMember Index(IArgumentSet args) => _collectionType.Index(this, args).GetPythonType().CreateInstance(ArgumentSet.Empty(args.Expression, args.Eval));
public IReadOnlyList <IParameterInfo> CreateFunctionParameters( IPythonClassType self, IPythonClassMember function, FunctionDefinition fd, bool declareVariables) { // For class method no need to add extra parameters, but first parameter type should be the class. // For static and unbound methods do not add or set anything. // For regular bound methods add first parameter and set it to the class. var parameters = new List <ParameterInfo>(); var skip = 0; if (self != null && function.HasClassFirstArgument()) { var p0 = fd.Parameters.FirstOrDefault(); if (p0 != null && !string.IsNullOrEmpty(p0.Name)) { var annType = GetTypeFromAnnotation(p0.Annotation, out var isGeneric); // Actual parameter type will be determined when method is invoked. // The reason is that if method might be called on a derived class. // Declare self or cls in this scope. if (declareVariables) { DeclareVariable(p0.Name, self.CreateInstance(ArgumentSet.Empty(p0.NameExpression, this)), VariableSource.Declaration, p0.NameExpression); } // Set parameter info, declare type as annotation type for generic self // e.g def test(self: T) var pi = new ParameterInfo(Ast, p0, isGeneric ? annType : self, null, false); parameters.Add(pi); skip++; } } // Declare parameters in scope for (var i = skip; i < fd.Parameters.Length; i++) { var p = fd.Parameters[i]; if (!string.IsNullOrEmpty(p.Name)) { var defaultValue = GetValueFromExpression(p.DefaultValue); var paramType = GetTypeFromAnnotation(p.Annotation, out var isGeneric) ?? UnknownType; if (paramType.IsUnknown()) { // If parameter has default value, look for the annotation locally first // since outer type may be getting redefined. Consider 's = None; def f(s: s = 123): ... paramType = GetTypeFromAnnotation(p.Annotation, out isGeneric, LookupOptions.Local | LookupOptions.Builtins); // Default value of None does not mean the parameter is None, just says it can be missing. defaultValue = defaultValue.IsUnknown() || defaultValue.IsOfType(BuiltinTypeId.None) ? null : defaultValue; if (paramType == null && defaultValue != null) { paramType = defaultValue.GetPythonType(); } } // If all else fails, look up globally. var pi = new ParameterInfo(Ast, p, paramType, defaultValue, isGeneric | paramType.IsGeneric()); if (declareVariables) { DeclareParameter(p, pi); } parameters.Add(pi); } else if (p.IsList || p.IsDictionary) { parameters.Add(new ParameterInfo(Ast, p, null, null, false)); } } return(parameters); }
private IMember GetValueFromProperty(IPythonPropertyType p, IPythonInstance instance, CallExpression expr) { // Function may not have been walked yet. Do it now. SymbolTable.Evaluate(p.FunctionDefinition); return(instance.Call(p.Name, ArgumentSet.Empty(expr, this))); }
private IMember GetValueFromMember(MemberExpression expr) { if (expr?.Target == null || string.IsNullOrEmpty(expr.Name)) { return(null); } IPythonInstance instance = null; var m = GetValueFromExpression(expr.Target); if (m is IPythonType typeInfo) { var member = typeInfo.GetMember(expr.Name); // If container is class/type info rather than the instance, then the method is an unbound function. // Example: C.f where f is a method of C. Compare to C().f where f is bound to the instance of C. if (member is PythonFunctionType f && !f.IsStatic && !f.IsClassMethod) { f.AddReference(GetLocationOfName(expr)); return(f.ToUnbound()); } instance = new PythonInstance(typeInfo); } instance = instance ?? m as IPythonInstance; var type = m?.GetPythonType(); // Try inner type var value = type?.GetMember(expr.Name); type?.AddMemberReference(expr.Name, this, GetLocationOfName(expr)); if (type is IPythonModule) { return(value); } // Class type GetMember returns a type. However, class members are // mostly instances (consider self.x = 1, x is an instance of int). // However, it is indeed possible to have them as types, like in // class X ... // class C: ... // self.x = X // which is somewhat rare as compared to self.x = X() but does happen. switch (value) { case IPythonClassType _: return(value); case IPythonPropertyType prop: return(prop.Call(instance, prop.Name, ArgumentSet.Empty(expr, this))); case IPythonType p: return(new PythonBoundType(p, instance)); case null: Log?.Log(TraceEventType.Verbose, $"Unknown member {expr.ToCodeString(Ast).Trim()}"); return(UnknownType); default: return(value); } }
public static Field Simple(string name) { return(new Field(string.Empty, name, ArgumentSet.Empty(), DirectiveSet.Empty(), SelectionSet.Empty())); }