def _Call(self, t): self._dispatch(t.func) # Check that t.func is actually a name func_node_class = t.func.__class__.__name__ is_attribute = False if func_node_class == "AttributeRefNode": is_attribute = True name_node = t.func.attribute elif func_node_class in ("NameNode", "TypeNode"): name_node = t.func else: raise InvalidNameError("{1}: Cannot call a {0}".format(func_node_class, t.lineno)) # Check that this name is actually callable # Also, if t.func is a type, then this is a constructor call func_sym = self._symbol_table.get(name_node.value, namespace=name_node.namespace) func_sym_class = func_sym.__class__.__name__ if func_sym_class == "TypeSymbol": t.is_constructor = True elif func_sym_class != "FunctionSymbol": raise InvalidNameError("{1}: Cannot call an instance of {0}".format(func_sym_class, t.lineno)) self._dispatch(t.args) if t.is_constructor: type_sym = func_sym # Use the class's constructor method symbol instead func_sym = self._resolve_attribute(type_sym.full_name, util.CONSTRUCTOR_NAME) t.type = type_sym.full_name # Replace NameNode with TypeNode for the type being constructed if func_node_class == "NameNode": new_node = nodes.TypeNode(type_sym.name, type_sym.namespace) new_node.type = type_sym.full_name new_node.lineno = t.lineno if is_attribute: t.func.attribute = new_node else: t.func = new_node if func_sym is None: # There's no init method to check, so we're done here return else: t.type = func_sym.return_type # Check that arg types match param_types in function symbol if len(func_sym.param_types) != len(t.args): raise ParameterCountError( "{0}: expected {1} argument(s) for function {2}, " "found {3}".format( t.lineno, len(func_sym.param_types), symbols.stringify_full_name(func_sym.full_name), len(t.args) ) ) for i, (param_type, arg) in enumerate(zip(func_sym.param_types, t.args)): if param_type not in self._get_ancestor_types(arg.type): raise InconsistentTypeError( "{3}: expected type {0} for function argument {1}, " "found {2}".format( symbols.stringify_full_name(param_type), i + 1, symbols.stringify_full_name(arg.type), t.lineno ) )
def _BinaryOp(self, t): self._dispatch(t.left) self._dispatch(t.right) boolean_ops = ("or", "and") comparison_ops = ("<", ">", "<=", ">=", "==", "!=", "is") arithmetic_ops = ("+", "-", "*", "/", "%") bool_sym = self._symbol_table.get("bool", symbol_type=symbols.TypeSymbol) bool_type = bool_sym.full_name int_type = self._symbol_table.get("int", symbol_type=symbols.TypeSymbol).full_name float_sym = self._symbol_table.get("float", symbol_type=symbols.TypeSymbol) float_type = float_sym.full_name str_type = self._symbol_table.get("str", symbol_type=symbols.TypeSymbol).full_name numeric_types = set([int_type, float_type]) op = t.operator if op in boolean_ops: t.type = bool_type # Coerce each side to bool if numeric or string bool_node = nodes.NameNode(bool_sym.name, bool_sym.namespace) if t.left.type in (int_type, float_type, str_type): new_node = nodes.CallNode(func=bool_node, args=[t.left], is_constructor=True) new_node.type = bool_type t.left = new_node if t.right.type in (int_type, float_type, str_type): new_node = nodes.CallNode(func=bool_node, args=[t.right], is_constructor=True) new_node.type = bool_type t.right = new_node elif op in comparison_ops: t.type = bool_type if op not in ("==", "!=", "is") and not (t.left.type in numeric_types and t.right.type in numeric_types): raise InvalidTypeError( "{0}: either {1} or {2} types cannot take part in an " "arithmetic operation".format( t.lineno, symbols.stringify_full_name(t.left.type), symbols.stringify_full_name(t.right.type) ) ) elif op in arithmetic_ops: if not (t.left.type in numeric_types and t.right.type in numeric_types): raise InvalidTypeError( "{0}: either {1} or {2} types cannot take part in an " "arithmetic operation".format( t.lineno, symbols.stringify_full_name(t.left.type), symbols.stringify_full_name(t.right.type) ) ) if t.left.type != t.right.type: t.type = float_type # Coerce int to float (we only have 2 numeric types) float_node = nodes.NameNode(float_sym.name, float_sym.namespace) if t.left.type == int_type: new_node = nodes.CallNode(func=float_node, args=[t.left], is_constructor=True) new_node.type = float_type t.left = new_node else: new_node = nodes.CallNode(func=float_node, args=[t.right], is_constructor=True) new_node.type = float_type t.right = new_node else: t.type = t.left.type else: raise Error("{1}: Unknown binary operation: {0}".format(op, t.lineno))
def _Assignment(self, t): self._dispatch(t.target) self._dispatch(t.value) ancestor_types = self._get_ancestor_types(t.value.type) if t.target.type not in ancestor_types: raise InconsistentTypeError( "{2}: Target type {0} not compatible with " "value type {1}".format( symbols.stringify_full_name(t.target.type), symbols.stringify_full_name(t.value.type), t.target.lineno, ) )
def _BinaryOp(self, t): op = t.operator if op in ('==', '!='): # value equality if op == '!=': self.write('!') self.write('(') self.dispatch(t.left) self.write(').equals(') self.dispatch(t.right) self.write(')') return if op == 'is': # object equality op = '==' if op == 'and': op = '&&' elif op == 'or': op = '||' expr_type = symbols.stringify_full_name(t.type) self.write('(({0}) ('.format(convert_type(expr_type))) self.dispatch(t.left) self.write(' {0} '.format(op)) self.dispatch(t.right) self.write('))')
def _Subscript(self, t): self._dispatch(t.value) # Check that we can actually index into t.value if ((), "list") not in self._get_ancestor_types(t.value.type): raise InvalidTypeError( "{1}: Type {0} is not subscriptable -- only type list".format( symbols.stringify_full_name(t.value.type), t.lineno ) ) self._dispatch(t.index) # Check that the index is actually an integer if ((), "int") not in self._get_ancestor_types(t.index.type): raise InvalidTypeError( "{2}: Invalid subscript type {0} for index {1}".format( symbols.stringify_full_name(t.index.type), t.index, t.lineno ) ) # An iterable can contain elements of any type t.type = ((), "object")
def _ClassDef(self, t): self._dispatch(t.name) if t.base: self._dispatch(t.base) self._dispatch(t.body) for stmt in t.body: if ( stmt.__class__.__name__ == "FunctionDefNode" and stmt.name.value == util.CONSTRUCTOR_NAME and stmt.return_type.type != t.name.type ): raise InconsistentTypeError( "{0}: constructor return type {1} does not match " "class name {2}".format( t.lineno, symbols.stringify_full_name(stmt.return_type.type), symbols.stringify_full_name(t.name.type), ) )
def _For(self, t): # "enhanced for" loop self._dispatch(t.target) self._dispatch(t.iterable) # Check that t.iterable is an instance of list or set if not (set(self._get_ancestor_types(t.iterable.type)) & set([((), "list"), ((), "set")])): raise InvalidTypeError( "{1}: expected an iterable (list, set), found {0}".format( symbols.stringify_full_name(t.iterable.type), t.lineno ) ) for item in getattr(t.iterable, "elts", []): ancestor_types = self._get_ancestor_types(item.type) if t.target.type not in ancestor_types: raise InconsistentTypeError( "{0}: expected type {1}, found type {2}".format( item.lineno, symbols.stringify_full_name(t.target.type), symbols.stringify_full_name(item.type) ) ) self._dispatch(t.body)
def _FunctionDef(self, t): self._dispatch(t.name) sym = self._symbol_table.get_by_qualified_name((t.name.namespace, t.name.value)) self._dispatch(t.return_type) sym.return_type = (t.return_type.namespace, t.return_type.value) self._dispatch(t.params) self._dispatch(t.body) # Check that the return types matches the function declaration. # Look for return statement(s) in 'body'. return_types = self._get_return_types(t.body) if not (return_types or sym.return_type == ((), "void") or t.name.value == util.CONSTRUCTOR_NAME): raise InconsistentTypeError( "{0}: found no return type(s) for function {1}".format( t.lineno, symbols.stringify_full_name(sym.full_name) ) ) if sym.return_type == ((), "void") and return_types: raise InconsistentTypeError( "{0}: found return type(s) for function {1}, " "expected none".format(t.lineno, symbols.stringify_full_name(sym.full_name)) ) for return_type in return_types: if return_type == util.NULL: continue ancestor_types = self._get_ancestor_types(return_type) if sym.return_type not in ancestor_types: raise InconsistentTypeError( "{0}: expected return type {1} in definition of " "function {2}, found return type {3}".format( t.lineno, symbols.stringify_full_name(sym.return_type), t.name.value, symbols.stringify_full_name(return_type), ) )
def _Type(self, t): sym = self._symbol_table.get(t.value, namespace=t.namespace, symbol_type=symbols.TypeSymbol) # Now that we've resolved this type symbol, we can update the namespace # and type for it and any type parameters t.namespace = sym.namespace t.type = sym.full_name self._dispatch(t.params) expected_param_count = util.COLLECTION_BASE_TYPES.get(t.type) if expected_param_count and expected_param_count != len(t.params): raise InvalidTypeError( "{0}: expected {1} parameter(s) for type {2}, " "found {3}".format(t.lineno, expected_param_count, symbols.stringify_full_name(t.type), len(t.params)) ) for param in t.params: param_sym = self._symbol_table.get(param.value, namespace=param.namespace, symbol_type=symbols.TypeSymbol) param.namespace = param_sym.namespace param.type = param_sym.full_name
def _UnaryOp(self, t): self._dispatch(t.operand) boolean_ops = ("not",) arithmetic_ops = ("+", "-") bool_sym = self._symbol_table.get("bool", symbol_type=symbols.TypeSymbol) bool_type = bool_sym.full_name int_type = self._symbol_table.get("int", symbol_type=symbols.TypeSymbol).full_name float_sym = self._symbol_table.get("float", symbol_type=symbols.TypeSymbol) float_type = float_sym.full_name str_type = self._symbol_table.get("str", symbol_type=symbols.TypeSymbol).full_name numeric_types = set([int_type, float_type]) op = t.operator if op in boolean_ops: t.type = bool_type # Coerce operand to bool if numeric or string bool_node = nodes.NameNode(bool_sym.name, bool_sym.namespace) if t.operand.type in (int_type, float_type, str_type): new_node = nodes.CallNode(func=bool_node, args=[t.operand], is_constructor=True) new_node.type = bool_type t.operand = new_node elif op in arithmetic_ops: if t.operand.type not in numeric_types: raise InvalidTypeError( "{0}: {1} type cannot take part in an " "arithmetic operation".format(t.lineno, symbols.stringify_full_name(t.operand.type)) ) t.type = t.operand.type elif op.__class__.__name__ == "TypeNode": # Type cast self._dispatch(op) # op_ancestor_types = self._get_ancestor_types(op.type) # operand_ancestor_types = self._get_ancestor_types(t.operand.type) # if (op.type not in operand_ancestor_types and # t.operand.type not in op_ancestor_types): # raise InconsistentTypeError( # '{0}: operator type {1} not incompatible with ' # 'operand type {2}'.format( # t.lineno, symbols.stringify_full_name(op.type), # symbols.stringify_full_name(t.operand.type))) t.type = t.operand.type = op.type else: raise Error("{1}: Unknown unary operation: {0}".format(op, t.lineno))
def _AttributeRef(self, t): self._dispatch(t.value) self._dispatch(t.attribute) # check that t.attribute is actually an attribute of t.value attr_sym = self._resolve_attribute(t.value.type, t.attribute.value) if attr_sym is None: raise symbols.UnknownSymbolError( "{2}: {0} is not an attribute of type {1} or any of its " "ancestors".format(t.attribute.value, symbols.stringify_full_name(t.value.type), t.lineno) ) # We skipped setting the attribute's namespace and type in _Name() so # that we could set it here t.attribute.namespace = attr_sym.namespace if attr_sym.__class__.__name__ == "FunctionSymbol": # If the attribute isn't a VariableSymbol or TypeSymbol, # type means nothing t.attribute.type = None else: t.attribute.type = attr_sym.var_type t.type = t.attribute.type
def _Call(self, t): # Translate qualified function name to Java name # - type casting: special handling # - builtin function: prepend GraphUtil # - constructor: prepend "new " # - attribute ref: map attr (namespace, name) to Java attr func_name_node = getattr(t.func, 'attribute', t.func) func_full_name = symbols.stringify_full_name( (func_name_node.namespace, func_name_node.value)) # Special handling for casting if func_full_name == 'bool': self.write('Boolean.valueOf(') self.write('(') self.dispatch(t.args[0]) self.write(') != 0 ? "true" : "false"') self.write(')') return if func_full_name == 'str': self.write('String.valueOf(') self.dispatch(t.args[0]) self.write(')') return if func_full_name == 'int': self.write('Integer.valueOf(') self.write('(') self.dispatch(t.args[0]) self.write(').toString()') self.write(')') return if func_full_name == 'float': self.write('Double.valueOf(') self.write('(') self.dispatch(t.args[0]) self.write(').toString()') self.write(')') return if func_full_name == '__builtins.isinstance': self.write('(') self.dispatch(t.args[0]) type_node = t.args[1] if type_node.__class__.__name__ == 'StringNode': value = eval(type_node.value) else: value = symbols.stringify_full_name(type_node.type) new_type = convert_type(value) self.write(' instanceof {0}'.format(new_type)) self.write(')') return if t.is_constructor: self.write('(') self.write('new ') self.dispatch(func_name_node) self.write('(') interleave(lambda: self.write(', '), self.dispatch, t.args) self.write(')') self.write(')') return java_name = JAVA_NAME_MAP.get(func_full_name, func_name_node.value) if func_name_node.namespace == (util.BUILTINS_CLASS_NAME,): # Built-in functions have special handling self.write(java_name) elif func_name_node.namespace == (): # Top-level functions need to be qualified with the wrapper class self.write('{0}.{1}'.format(TARGET_PROGRAM_NAME, java_name)) else: # If called function is an object attribute, generate object first if getattr(t.func, 'attribute', False): self.dispatch(t.func.value) self.write('.') self.write(java_name) self.write('(') interleave(lambda: self.write(', '), self.dispatch, t.args) self.write(')')