# -*- Mode: Python -*- # simple lambda language for the purpose of exercising the type inference engine import lisp_reader is_a = isinstance class node: pass class vardef (node): def __init__ (self, name): self.name = name self.alpha = 0 class varref (node): def __init__ (self, name): self.name = name class literal (node): def __init__ (self, kind, value): self.kind = kind self.value = value class function (node): def __init__ (self, formals, body): self.formals = formals self.body = body class application (node): def __init__ (self, rator, rands): self.rator = rator self.rands = rands class let (node): def __init__ (self, names, inits, body): self.names = names self.inits = inits self.body = body class walker: def go (self, exp): return self.walk (exp) def walk (self, exp): # Note: for simplicity, no real syntax checking here if is_a (exp, str): return varref (exp) elif is_a (exp, lisp_reader.atom): return literal (exp.kind, exp.value) elif is_a (exp, list): rator = exp[0] if is_a (rator, str): if rator == 'lambda': # ['lambda', [formal0, formal1, ...], body] ignore, formals, body = exp return function ([vardef (x) for x in formals], self.walk (body)) elif rator == 'let': # ['let', [[name0, init0], [name1, init1], ...], body] names = [vardef (x[0]) for x in exp[1]] inits = [self.walk (x[1]) for x in exp[1]] body = self.walk (exp[2]) return let (names, inits, body) else: return application (self.walk (rator), [self.walk (x) for x in exp[1:]]) else: return application (self.walk (rator), [self.walk (x) for x in exp[1:]]) def rename_variables (exp): # alpha convert vars = [] def lookup (name, lenv): while lenv: rib, lenv = lenv for i in range (len(rib)): x = rib[i] if x.name == name: return x raise ValueError ("unbound variable: %r" % (name,)) def rename (exp, lenv): if is_a (exp, let) or is_a (exp, function): if is_a (exp, let): defs = exp.names else: defs = exp.formals for vd in defs: vd.alpha = len(vars) vars.append (vd) if is_a (exp, let): for i in range (len (defs)): rename (exp.inits[i], lenv) lenv = (defs, lenv) rename (exp.body, lenv) else: lenv = (defs, lenv) rename (exp.body, lenv) elif is_a (exp, varref): if exp.name.startswith ('%'): # primitive pass else: exp.var = lookup (exp.name, lenv) exp.name = '%s_%d' % (exp.name, exp.var.alpha) elif is_a (exp, application): rename (exp.rator, lenv) for rand in exp.rands: rename (rand, lenv) elif is_a (exp, literal): pass else: raise ValueError ("unknown node type") rename (exp, None) # now go back and change the names of the vardefs for vd in vars: vd.name = '%s_%d' % (vd.name, vd.alpha) return