diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..e208459 --- /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..1ccf35d --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/java-type-checker/java_type_checker/expressions.py b/java-type-checker/java_type_checker/expressions.py index 27ed57e..e605798 100644 --- a/java-type-checker/java_type_checker/expressions.py +++ b/java-type-checker/java_type_checker/expressions.py @@ -31,6 +31,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,22 +45,52 @@ 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 + + def check_types(self): + pass + 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.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.declared_type.method_named(self.method_name).return_type + + def check_types(self): + + for arg in self.args: + arg.check_types() + + receiver_type = self.receiver.static_type() + if not receiver_type.is_subtype_of(Type.object): + raise JavaTypeError("Type {0} does not have methods".format(receiver_type.name)) + + method = receiver_type.method_named(self.method_name) # throws NoMethodError + expected_types = method.argument_types + actual_types = [arg.static_type() for arg in self.args] + call_name = "{0}.{1}()".format(self.receiver.static_type().name, self.method_name) + + check_arguments(expected_types, actual_types, call_name) + class ConstructorCall(Expression): """ @@ -64,6 +100,23 @@ 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 + + def check_types(self): + + for arg in self.args: + arg.check_types() + + if not self.instantiated_type.is_instantiable: + raise JavaTypeError("Type {0} is not instantiable".format(self.instantiated_type.name)) + + expected_types = self.instantiated_type.constructor.argument_types + actual_types = [arg.static_type() for arg in self.args] + call_name = "{0} constructor".format(self.instantiated_type.name) + + check_arguments(expected_types, actual_types, call_name) + class JavaTypeError(Exception): """ Indicates a compile-time type error in an expression. @@ -71,6 +124,25 @@ class JavaTypeError(Exception): pass +def check_arguments(expected_types, actual_types, call_name): + """" + Helper to check arguments. Raises JavaTypeError. + """ + if len(expected_types) != len(actual_types): + raise JavaTypeError( + "Wrong number of arguments for {0}: expected {1}, got {2}".format( + call_name, + len(expected_types), + len(actual_types))) + + for expected_type, actual_type in zip(expected_types, actual_types): + if not expected_type.is_supertype_of(actual_type): + raise JavaTypeError( + "{0} expects arguments of type {1}, but got {2}".format( + call_name, + names(expected_types), + names(actual_types))) + 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..1e7ac54 100644 --- a/java-type-checker/java_type_checker/types.py +++ b/java-type-checker/java_type_checker/types.py @@ -12,7 +12,12 @@ 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 is self: + return True + for direct_supertype in self.direct_supertypes: + if other is direct_supertype or direct_supertype.is_subtype_of(other): + return True + return False def is_supertype_of(self, other): """ Convenience counterpart to is_subtype_of(). @@ -72,6 +77,18 @@ class NullType(Type): def __init__(self): super().__init__("null") + def method_named(self, name): + """ + Raises a NoSuchMethod error since null has no method. + """ + raise NoSuchMethod("Cannot invoke method {1}() on null".format(self.name, name)) + + def is_subtype_of(self, other): + return type(other) == ClassOrInterface + + def is_supertype_of(self, other): + return other.is_subtype_of(self) + class NoSuchMethod(Exception): pass diff --git a/python-attr-lookup/python-attr-lookup.iml b/python-attr-lookup/python-attr-lookup.iml index 01e8e1b..498c02d 100644 --- a/python-attr-lookup/python-attr-lookup.iml +++ b/python-attr-lookup/python-attr-lookup.iml @@ -20,5 +20,15 @@ + + + + + + + + + + \ 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..2ced26b 100644 --- a/python-attr-lookup/src/plang/PythonObject.java +++ b/python-attr-lookup/src/plang/PythonObject.java @@ -51,7 +51,12 @@ 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"); + if (mro == null) { + mro = new ArrayList<>(); + mro.add(this); + mro.addAll(type.buildMRO()); + } + return mro; } /** @@ -62,7 +67,12 @@ 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 pythonObject : getMRO()) { + if (pythonObject.attrs.containsKey(attrName)) { + return pythonObject.attrs.get(attrName); + } + } + throw new PythonAttributeException(this, attrName); } /** @@ -74,7 +84,7 @@ public final PythonObject get(String attrName) throws PythonAttributeException { * @param value Its new value */ public final void set(String attrName, PythonObject value) { - throw new UnsupportedOperationException("not implemented yet"); + attrs.put(attrName, value); } @Override diff --git a/python-attr-lookup/src/plang/PythonType.java b/python-attr-lookup/src/plang/PythonType.java index 4e2be4a..1f1b15b 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 mroList = new ArrayList<>(); + mroList.add(this); + if (base != null) { + mroList.addAll(base.buildMRO()); + } + return mroList; } /** @@ -49,7 +54,7 @@ protected List buildMRO() { * this PythonType. */ public PythonObject instantiate() { - throw new UnsupportedOperationException("not implemented yet"); + return new PythonObject(this); } @Override