def check_BinaryOperator(node, env): check(node.left, env) check(node.right, env) node.type = check_binop(node.left.type, node.operator, node.right.type) if node.type is None and (node.left.type and node.right.type): # if left and right have types then we are *not* in a cascading case error(f'Invalid binop: {node.left.type} {node.operator} {node.right.type}')
def check_NamedLocation(node, env): declaration = env.get(node.name) if declaration is None: error(f'Location {node} was not declared') return # cannot do further checking node.type = declaration.type node.mutable = not isinstance(declaration, (Constant, Function))
def check_Function(node, env): if node.name in env.maps[0]: error(f'Duplicate definition of {node.name}.') # and do NOT overwrite it ... originally defined function stays else: env[node.name] = node local_env = env.new_child() check(node.parameters, local_env) check(node.statements, local_env)
def check_Assignment(node, env): check(node.location, env) check(node.expression, env) # What I expect if node.location.type != node.expression.type: error(f'Type error on assignment: {node.location.type} != {node.expression.type}') # Mutability: let's make assignment responsible for this if not node.location.mutable: # Wishful Thinking Programming! error(f"Cannot assign to immutable location: {node.location}")
def _checkVariableOrConst(node, env): if node.value is not None: check(node.value, env) if node.type_specified_when_declared: if node.value.type != node.type: error(f'Variable defined as type {node.type} does not match {node.value.type}') else: # infer type from value node.type = node.value.type if node.name in env: error(f'Duplicate definition of {node.name}') env[node.name] = node
def check_FunctionCall(node, env): # Check function arguments check(node.args, env) # Check that the function is defined func = env.get(node.function_name) if func is None: error(f'Function {node.function_name} is not defined.') # Check that the function is a function if not isinstance(func, Function): error(f'Cannot call {node.name} as a function') return # ... and no further checking is possible # Check that the function has all of the arguments that it needs if len(func.parameters) != len(node.args): error(f'Function is missing required arguments. Needs {func.parameters} got {node.args}') # Check that the function parameter types match the supplied argument types for n, (arg, param) in enumerate(zip(node.args, func.parameters), 1): if param.type != arg.type: error(f'Type error in argument {n}: {param.type} != {arg.type}') node.type = func.return_type
def check_UnaryOperator(node, env): check(node.operand, env) node.type = check_unop(node.operator, node.operand.type) if node.type is None: error(f'Invalid unary operation: {node.operator}{node.operand}')
def check_While(node, env): check(node.test, env) if node.test.type != Bool.type: error('If test did not evaluate to a Boolean!') check(node.consequence, env.new_child())
def check_If(node, env): check(node.test, env) if node.test.type != Bool.type: error('If test did not evaluate to a Boolean!') check(node.consequence, env.new_child()) # Make a new scope (from ChainMap) check(node.alternative, env.new_child())
def check_FunctionParameter(node, env): if node.name in env.maps[0]: error(f'Duplicate definition of {node.name}') env[node.name] = node