Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions .idea/comp394-type-modeling.iml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions .idea/misc.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions .idea/modules.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion java-type-checker/.idea/misc.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion java-type-checker/.idea/type-checker.iml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

121 changes: 115 additions & 6 deletions java-type-checker/java_type_checker/expressions.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-

from .types import Type
from .types import Type, NoSuchMethod
import types


class Expression(object):
Expand All @@ -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):
Expand Down
18 changes: 17 additions & 1 deletion java-type-checker/java_type_checker/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -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().
Expand Down Expand Up @@ -71,6 +85,8 @@ class NullType(Type):
"""
def __init__(self):
super().__init__("null")
self.name = "null"
self.is_instantiable = False


class NoSuchMethod(Exception):
Expand Down
32 changes: 22 additions & 10 deletions python-attr-lookup/src/plang/PythonObject.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
* The runtime state of an object in Python.
*/
public class PythonObject {
private final Map<String,PythonObject> attrs = new HashMap<>();
private final Map<String, PythonObject> attrs = new HashMap<>();
private final PythonType type;
private List<PythonObject> mro;

Expand All @@ -20,7 +20,7 @@ public class PythonObject {

/**
* The Python type (i.e. class) of this object.
*
* <p>
* 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.
Expand All @@ -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.
*
* <p>
* 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`.
* <p>
* Once again, hooray for not having to deal with that.
*/
public List<PythonObject> getMRO() {
if(mro == null)
if (mro == null)
mro = Collections.unmodifiableList(buildMRO());
return mro;
}
Expand All @@ -51,7 +51,11 @@ public List<PythonObject> getMRO() {
* result (i.e. it remembers the list buildMRO() returned and keeps returning it).
*/
protected List<PythonObject> 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");
}

/**
Expand All @@ -62,7 +66,14 @@ protected List<PythonObject> 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");
}

/**
Expand All @@ -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
Expand Down
10 changes: 8 additions & 2 deletions python-attr-lookup/src/plang/PythonType.java
Original file line number Diff line number Diff line change
Expand Up @@ -41,15 +41,21 @@ public PythonObject getBase() {

@Override
protected List<PythonObject> buildMRO() {
throw new UnsupportedOperationException("not implemented yet");
List<PythonObject> mro = new ArrayList<>();
mro.add(this);
if (base != null)
mro.add(base);
return mro;
// throw new UnsupportedOperationException("not implemented yet");
}

/**
* Creates and returns a new instance of this class, i.e. a PythonObject whose type is
* this PythonType.
*/
public PythonObject instantiate() {
throw new UnsupportedOperationException("not implemented yet");
return new PythonObject(this);
// throw new UnsupportedOperationException("not implemented yet");
}

@Override
Expand Down