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/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..b13500c 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): @@ -21,48 +22,156 @@ 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()") 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): + 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`. """ + 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 + + 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): """ 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) + 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)) + + # Nonexistent methods + try: + self.receiver.static_type().method_named(self.method_name) + except NoSuchMethod: + raise NoSuchMethod( + "{0} has no method named {1}".format( + self.receiver.static_type().name, + self.method_name) + ) + + # 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(expected_types), + len(args)) + ) + + # Wrong argument type (also check for null) + + for i in range(len(expected_types)): + 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, + 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): + + # 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: + 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 (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))) + class JavaTypeError(Exception): diff --git a/java-type-checker/java_type_checker/types.py b/java-type-checker/java_type_checker/types.py index 465f7f4..8f63777 100644 --- a/java-type-checker/java_type_checker/types.py +++ b/java-type-checker/java_type_checker/types.py @@ -12,7 +12,21 @@ 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 self.name == 'null': + return True + + 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 def is_supertype_of(self, other): """ Convenience counterpart to is_subtype_of(). @@ -71,6 +85,8 @@ class NullType(Type): """ def __init__(self): super().__init__("null") + self.name = "null" + self.is_instantiable = False class NoSuchMethod(Exception): 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