From 64280d46b6609e6b77dbb930a3a798bf3af62fb8 Mon Sep 17 00:00:00 2001 From: giangduong36 Date: Thu, 8 Feb 2018 21:34:22 -0600 Subject: [PATCH 1/4] Part 0 done --- .idea/comp394-type-modeling.iml | 24 ++++++++++++++ .idea/misc.xml | 6 ++++ .idea/modules.xml | 8 +++++ .../src/plang/PythonObject.java | 32 +++++++++++++------ python-attr-lookup/src/plang/PythonType.java | 10 ++++-- 5 files changed, 68 insertions(+), 12 deletions(-) create mode 100644 .idea/comp394-type-modeling.iml create mode 100644 .idea/misc.xml create mode 100644 .idea/modules.xml diff --git a/.idea/comp394-type-modeling.iml b/.idea/comp394-type-modeling.iml new file mode 100644 index 0000000..490de41 --- /dev/null +++ b/.idea/comp394-type-modeling.iml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..083d9fa --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..96bca08 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/python-attr-lookup/src/plang/PythonObject.java b/python-attr-lookup/src/plang/PythonObject.java index a8e311f..3a4fccb 100644 --- a/python-attr-lookup/src/plang/PythonObject.java +++ b/python-attr-lookup/src/plang/PythonObject.java @@ -10,7 +10,7 @@ * The runtime state of an object in Python. */ public class PythonObject { - private final Map attrs = new HashMap<>(); + private final Map attrs = new HashMap<>(); private final PythonType type; private List mro; @@ -20,7 +20,7 @@ public class PythonObject { /** * The Python type (i.e. class) of this object. - * + *

* May be null if this object is itself a class. This is a simplification for this assignment; * in actual Python, _all_ objects have types, and the type of a class is `type`. That is itself * a class, so the type of `type` is `type`. Phew! Thank goodness we’re ignoring that. @@ -32,16 +32,16 @@ public final PythonType getType() { /** * Return the list of objects we should search when asked for a given attribute, in the order * we should search them. - * + *

* The real Python implementation of the MRO is substantially more complicated, because * (1) Python supports multiple inheritance (i.e. classes can have multiple base classes), and * (2) the base of `type` is `object`, and the type of `object` is `type`, which creates a - * circular reference that Python resolves by special-casing both `object` and `type`. - * + * circular reference that Python resolves by special-casing both `object` and `type`. + *

* Once again, hooray for not having to deal with that. */ public List getMRO() { - if(mro == null) + if (mro == null) mro = Collections.unmodifiableList(buildMRO()); return mro; } @@ -51,7 +51,11 @@ public List getMRO() { * result (i.e. it remembers the list buildMRO() returned and keeps returning it). */ protected List buildMRO() { - throw new UnsupportedOperationException("not implemented yet"); + mro = new ArrayList<>(); + mro.add(this); + mro.addAll(type.getMRO()); + return mro; +// throw new UnsupportedOperationException("not implemented yet"); } /** @@ -62,7 +66,14 @@ protected List buildMRO() { * @throws PythonAttributeException When there is no attribute on this object with that name. */ public final PythonObject get(String attrName) throws PythonAttributeException { - throw new UnsupportedOperationException("not implemented yet"); + for (PythonObject i : this.getMRO()) { + if (i.attrs.containsKey(attrName)) { + return i.attrs.get(attrName); + } + } + throw new PythonAttributeException(this, attrName); + +// throw new UnsupportedOperationException("not implemented yet"); } /** @@ -71,10 +82,11 @@ public final PythonObject get(String attrName) throws PythonAttributeException { * resolution order. * * @param attrName The name of the attribute to set - * @param value Its new value + * @param value Its new value */ public final void set(String attrName, PythonObject value) { - throw new UnsupportedOperationException("not implemented yet"); + attrs.put(attrName, value); +// throw new UnsupportedOperationException("not implemented yet"); } @Override diff --git a/python-attr-lookup/src/plang/PythonType.java b/python-attr-lookup/src/plang/PythonType.java index 4e2be4a..ed16986 100644 --- a/python-attr-lookup/src/plang/PythonType.java +++ b/python-attr-lookup/src/plang/PythonType.java @@ -41,7 +41,12 @@ public PythonObject getBase() { @Override protected List buildMRO() { - throw new UnsupportedOperationException("not implemented yet"); + List mro = new ArrayList<>(); + mro.add(this); + if (base != null) + mro.add(base); + return mro; + // throw new UnsupportedOperationException("not implemented yet"); } /** @@ -49,7 +54,8 @@ protected List buildMRO() { * this PythonType. */ public PythonObject instantiate() { - throw new UnsupportedOperationException("not implemented yet"); + return new PythonObject(this); +// throw new UnsupportedOperationException("not implemented yet"); } @Override From 069ba274b92ac31dd3b1b0ca443cba1c99d68023 Mon Sep 17 00:00:00 2001 From: giangduong36 Date: Fri, 9 Feb 2018 15:16:10 -0600 Subject: [PATCH 2/4] Part 1 making progress --- java-type-checker/.idea/misc.xml | 2 +- java-type-checker/.idea/type-checker.iml | 2 +- .../java_type_checker/expressions.py | 60 +++++++++++++++++++ java-type-checker/java_type_checker/types.py | 14 ++++- 4 files changed, 75 insertions(+), 3 deletions(-) diff --git a/java-type-checker/.idea/misc.xml b/java-type-checker/.idea/misc.xml index c24fcb0..ba8ef12 100644 --- a/java-type-checker/.idea/misc.xml +++ b/java-type-checker/.idea/misc.xml @@ -12,5 +12,5 @@ - + \ No newline at end of file diff --git a/java-type-checker/.idea/type-checker.iml b/java-type-checker/.idea/type-checker.iml index 52e75c6..90500d6 100644 --- a/java-type-checker/.idea/type-checker.iml +++ b/java-type-checker/.idea/type-checker.iml @@ -2,7 +2,7 @@ - + diff --git a/java-type-checker/java_type_checker/expressions.py b/java-type-checker/java_type_checker/expressions.py index 27ed57e..04025d6 100644 --- a/java-type-checker/java_type_checker/expressions.py +++ b/java-type-checker/java_type_checker/expressions.py @@ -14,6 +14,15 @@ def static_type(self): Returns the compile-time type of this expression, i.e. the most specific type that describes all the possible values it could take on at runtime. Subclasses must implement this method. """ + # print(Type(self).is_subtype_of(Type(Variable))) + # # print() + # # print (Type(selfself).is_subtype_of(Variable)) + # # if hasattr(self, "declared_type"): + # # return self.declared_type + # # if hasattr(self, "type"): + # # return self.type + # # else: + # # return Type.null raise NotImplementedError(type(self).__name__ + " must implement static_type()") def check_types(self): @@ -21,6 +30,7 @@ def check_types(self): Validates the structure of this expression, checking for any logical inconsistencies in the child nodes and the operation this expression applies to them. """ + raise NotImplementedError(type(self).__name__ + " must implement check_types()") @@ -31,6 +41,12 @@ def __init__(self, name, declared_type): self.name = name #: The name of the variable self.declared_type = declared_type #: The declared type of the variable (Type) + def static_type(self): + return self.declared_type + + def check_types(self): + pass + class Literal(Expression): """ A literal value entered in the code, e.g. `5` in the expression `x + 5`. @@ -39,11 +55,20 @@ def __init__(self, value, type): self.value = value #: The literal value, as a string self.type = type #: The type of the literal (Type) + def static_type(self): + return self.type + + def check_types(self): + pass + class NullLiteral(Literal): def __init__(self): super().__init__("null", Type.null) + def static_type(self): + return Type.null + class MethodCall(Expression): """ @@ -55,6 +80,33 @@ def __init__(self, receiver, method_name, *args): self.method_name = method_name #: The name of the method to call (String) self.args = args #: The method arguments (list of Expressions) + def static_type(self): + return self.receiver.static_type().method_named(self.method_name).return_type + + def check_types(self): + + + print(self.receiver.static_type()) + # Nonexistent method + try: + self.receiver.static_type().method_named(self.method_name) + except KeyError: + raise NoSuchMethod( + "{0} has no method named {1}".format( + self.receiver.static_type().name, + self.method_name) + ) + + # Too many arguments + if len(self.args) != len(self.receiver.static_type().method_named(self.method_name).argument_types): + raise JavaTypeError( + "Wrong number of arguments for {0}.{1}(): expected {2}, got {3}".format( + self.receiver.static_type().name, + self.method_name, + len(self.receiver.static_type().method_named(self.method_name).argument_types), + len(self.args)) + ) + class ConstructorCall(Expression): """ @@ -64,6 +116,9 @@ def __init__(self, instantiated_type, *args): self.instantiated_type = instantiated_type #: The type to instantiate (Type) self.args = args #: Constructor arguments (list of Expressions) + def static_type(self): + return self.instantiated_type + class JavaTypeError(Exception): """ Indicates a compile-time type error in an expression. @@ -71,6 +126,11 @@ class JavaTypeError(Exception): pass +class NoSuchMethod(Exception): + """ Indicates a compile-time type error in an expression. + """ + pass + def names(named_things): """ Helper for formatting pretty error messages """ diff --git a/java-type-checker/java_type_checker/types.py b/java-type-checker/java_type_checker/types.py index 465f7f4..736a74b 100644 --- a/java-type-checker/java_type_checker/types.py +++ b/java-type-checker/java_type_checker/types.py @@ -12,7 +12,19 @@ def __init__(self, name, direct_supertypes=[]): def is_subtype_of(self, other): """ True if this type can be used where the other type is expected. """ - return True # TODO: implement + if other == self: + return True + + super = self.direct_supertypes + while len(super) > 0: + if other in super: + return True + newList = [] + for i in super: + newList.extend(i.direct_supertypes) + super = newList + return False + # TODO: implement def is_supertype_of(self, other): """ Convenience counterpart to is_subtype_of(). From 9c30bd84774b0e21f5db09a1286346c7a7a8dafa Mon Sep 17 00:00:00 2001 From: giangduong36 Date: Fri, 9 Feb 2018 17:26:54 -0600 Subject: [PATCH 3/4] Part 1.3 done --- .../java_type_checker/expressions.py | 95 +++++++++++++------ 1 file changed, 67 insertions(+), 28 deletions(-) diff --git a/java-type-checker/java_type_checker/expressions.py b/java-type-checker/java_type_checker/expressions.py index 04025d6..430f427 100644 --- a/java-type-checker/java_type_checker/expressions.py +++ b/java-type-checker/java_type_checker/expressions.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- -from .types import Type +from .types import Type, NoSuchMethod +import types class Expression(object): @@ -14,15 +15,6 @@ def static_type(self): Returns the compile-time type of this expression, i.e. the most specific type that describes all the possible values it could take on at runtime. Subclasses must implement this method. """ - # print(Type(self).is_subtype_of(Type(Variable))) - # # print() - # # print (Type(selfself).is_subtype_of(Variable)) - # # if hasattr(self, "declared_type"): - # # return self.declared_type - # # if hasattr(self, "type"): - # # return self.type - # # else: - # # return Type.null raise NotImplementedError(type(self).__name__ + " must implement static_type()") def check_types(self): @@ -37,8 +29,9 @@ def check_types(self): class Variable(Expression): """ An expression that reads the value of a variable, e.g. `x` in the expression `x + 5`. """ + def __init__(self, name, declared_type): - self.name = name #: The name of the variable + self.name = name #: The name of the variable self.declared_type = declared_type #: The declared type of the variable (Type) def static_type(self): @@ -51,9 +44,10 @@ def check_types(self): class Literal(Expression): """ A literal value entered in the code, e.g. `5` in the expression `x + 5`. """ + def __init__(self, value, type): self.value = value #: The literal value, as a string - self.type = type #: The type of the literal (Type) + self.type = type #: The type of the literal (Type) def static_type(self): return self.type @@ -74,63 +68,108 @@ class MethodCall(Expression): """ A Java method invocation, i.e. `foo.bar(0, 1, 2)`. """ + def __init__(self, receiver, method_name, *args): self.receiver = receiver - self.receiver = receiver #: The object whose method we are calling (Expression) + self.receiver = receiver #: The object whose method we are calling (Expression) self.method_name = method_name #: The name of the method to call (String) - self.args = args #: The method arguments (list of Expressions) + self.args = args #: The method arguments (list of Expressions) def static_type(self): return self.receiver.static_type().method_named(self.method_name).return_type def check_types(self): + # Nonexistent methods for special types (Avoid NoSuchMethod raised by method_named function) + excludes = [Type.void, Type.int, Type.double, Type.null] + if self.receiver.static_type() in excludes: + raise JavaTypeError("Type {0} does not have methods".format(self.receiver.static_type().name)) - - print(self.receiver.static_type()) - # Nonexistent method + # Nonexistent methods try: self.receiver.static_type().method_named(self.method_name) - except KeyError: + except NoSuchMethod: raise NoSuchMethod( "{0} has no method named {1}".format( self.receiver.static_type().name, self.method_name) ) - # Too many arguments - if len(self.args) != len(self.receiver.static_type().method_named(self.method_name).argument_types): + # Too many/few arguments + expected_types = self.receiver.static_type().method_named(self.method_name).argument_types + args = [x.static_type() for x in self.args] + if len(expected_types) != len(args): raise JavaTypeError( "Wrong number of arguments for {0}.{1}(): expected {2}, got {3}".format( self.receiver.static_type().name, self.method_name, - len(self.receiver.static_type().method_named(self.method_name).argument_types), - len(self.args)) + len(expected_types), + len(args)) ) + # Wrong argument type + for i in range(len(expected_types)): + if not args[i].is_subtype_of(expected_types[i]): + raise JavaTypeError( + "{0}.{1}() expects arguments of type {2}, but got {3}".format( + self.receiver.static_type().name, + self.method_name, + names(expected_types), + names(args)) + ) + + # Deep expression + for arg in self.args: + arg.check_types() + class ConstructorCall(Expression): """ A Java object instantiation, i.e. `new Foo(0, 1, 2)`. """ + def __init__(self, instantiated_type, *args): self.instantiated_type = instantiated_type #: The type to instantiate (Type) - self.args = args #: Constructor arguments (list of Expressions) + self.args = args #: Constructor arguments (list of Expressions) def static_type(self): return self.instantiated_type + def check_types(self): -class JavaTypeError(Exception): - """ Indicates a compile-time type error in an expression. - """ - pass + # Cannot instantiate primitives + excludes = [Type.void, Type.int, Type.double, Type.null] + if self.instantiated_type in excludes: + raise JavaTypeError("Type {0} is not instantiable".format(self.instantiated_type.name)) + + expected_types = self.instantiated_type.constructor.argument_types + args = [x.static_type() for x in self.args] + + # Wrong number of arguments + if len(args) != len(expected_types): + raise JavaTypeError( + "Wrong number of arguments for {0} constructor: expected {1}, got {2}".format( + self.instantiated_type.name, + len(expected_types), + len(args))) + # Wrong argument types + if args != expected_types: + raise JavaTypeError("{0} constructor expects arguments of type {1}, but got {2}".format( + self.instantiated_type.name, + names(expected_types), + names(args))) -class NoSuchMethod(Exception): + # Deep expression + for arg in self.args: + arg.check_types() + + +class JavaTypeError(Exception): """ Indicates a compile-time type error in an expression. """ pass + def names(named_things): """ Helper for formatting pretty error messages """ From 1805f9205389f3769750f8a24e7148b8f3e0a3a5 Mon Sep 17 00:00:00 2001 From: giangduong36 Date: Fri, 9 Feb 2018 18:06:38 -0600 Subject: [PATCH 4/4] Part 1.4 done --- .../java_type_checker/expressions.py | 26 +++++++++++++------ java-type-checker/java_type_checker/types.py | 6 ++++- 2 files changed, 23 insertions(+), 9 deletions(-) diff --git a/java-type-checker/java_type_checker/expressions.py b/java-type-checker/java_type_checker/expressions.py index 430f427..b13500c 100644 --- a/java-type-checker/java_type_checker/expressions.py +++ b/java-type-checker/java_type_checker/expressions.py @@ -79,8 +79,12 @@ def static_type(self): return self.receiver.static_type().method_named(self.method_name).return_type def check_types(self): + # Nonexistent methods for special types (Avoid NoSuchMethod raised by method_named function) - excludes = [Type.void, Type.int, Type.double, Type.null] + if self.receiver.static_type() == Type.null: + raise NoSuchMethod("Cannot invoke method {}() on null".format(self.method_name)) + + excludes = [Type.void, Type.int, Type.double] if self.receiver.static_type() in excludes: raise JavaTypeError("Type {0} does not have methods".format(self.receiver.static_type().name)) @@ -106,9 +110,10 @@ def check_types(self): len(args)) ) - # Wrong argument type + # Wrong argument type (also check for null) + for i in range(len(expected_types)): - if not args[i].is_subtype_of(expected_types[i]): + if (not args[i].is_subtype_of(expected_types[i])) or (args[i] == Type.null and expected_types[i] in excludes): raise JavaTypeError( "{0}.{1}() expects arguments of type {2}, but got {3}".format( self.receiver.static_type().name, @@ -122,6 +127,7 @@ def check_types(self): arg.check_types() + class ConstructorCall(Expression): """ A Java object instantiation, i.e. `new Foo(0, 1, 2)`. @@ -136,6 +142,10 @@ def static_type(self): def check_types(self): + # Deep expression + for arg in self.args: + arg.check_types() + # Cannot instantiate primitives excludes = [Type.void, Type.int, Type.double, Type.null] if self.instantiated_type in excludes: @@ -152,16 +162,16 @@ def check_types(self): len(expected_types), len(args))) - # Wrong argument types - if args != expected_types: + # Wrong argument types (also check for null) + checkList = [True for i, j in zip(args, expected_types) if + (i == j or (i == Type.null and j not in excludes))] + + if len(checkList) != len(args): raise JavaTypeError("{0} constructor expects arguments of type {1}, but got {2}".format( self.instantiated_type.name, names(expected_types), names(args))) - # Deep expression - for arg in self.args: - arg.check_types() class JavaTypeError(Exception): diff --git a/java-type-checker/java_type_checker/types.py b/java-type-checker/java_type_checker/types.py index 736a74b..8f63777 100644 --- a/java-type-checker/java_type_checker/types.py +++ b/java-type-checker/java_type_checker/types.py @@ -12,6 +12,9 @@ def __init__(self, name, direct_supertypes=[]): def is_subtype_of(self, other): """ True if this type can be used where the other type is expected. """ + if self.name == 'null': + return True + if other == self: return True @@ -24,7 +27,6 @@ def is_subtype_of(self, other): newList.extend(i.direct_supertypes) super = newList return False - # TODO: implement def is_supertype_of(self, other): """ Convenience counterpart to is_subtype_of(). @@ -83,6 +85,8 @@ class NullType(Type): """ def __init__(self): super().__init__("null") + self.name = "null" + self.is_instantiable = False class NoSuchMethod(Exception):