L3.uniqify
1from collections.abc import Callable, Mapping 2from functools import partial 3 4from util.sequential_name_generator import SequentialNameGenerator 5 6from .syntax import ( 7 Abstract, 8 Allocate, 9 Apply, 10 Begin, 11 Branch, 12 Identifier, 13 Immediate, 14 Let, 15 LetRec, 16 Load, 17 Primitive, 18 Program, 19 Reference, 20 Store, 21 Term, 22) 23 24type Context = Mapping[Identifier, Identifier] 25 26 27# This pass is responsible for renaming all identifiers in a program to be unique. This is necessary for the later stages of the compiler, which rely on the fact that all identifiers are unique. 28def uniqify_term( 29 term: Term, 30 context: Context, 31 fresh: Callable[[str], str], 32) -> Term: 33 _term = partial(uniqify_term, context=context, fresh=fresh) 34 35 match term: 36 case Let(bindings=bindings, body=body): 37 new_bindings: list[tuple[Identifier, Term]] = [] 38 new_context = context 39 for name, value in bindings: 40 new_name = fresh(name) 41 new_bindings.append((new_name, _term(value))) 42 new_context = {**new_context, name: new_name} 43 44 return Let( 45 bindings=new_bindings, 46 body=uniqify_term(body, new_context, fresh), 47 ) 48 49 case LetRec(bindings=bindings, body=body): 50 new_bindings: list[tuple[Identifier, Term]] = [] 51 new_context = context 52 for name, value in bindings: 53 new_name = fresh(name) 54 new_bindings.append((new_name, value)) 55 new_context = {**new_context, name: new_name} 56 57 _new_term = partial(uniqify_term, context=new_context, fresh=fresh) 58 59 return LetRec( 60 bindings=[(new_name, _new_term(value)) for new_name, value in new_bindings], 61 body=_new_term(body), 62 ) 63 64 case Reference(name=name): 65 if name in context: 66 return Reference(name=context[name]) 67 return term 68 69 case Abstract(parameters=parameters, body=body): 70 new_parameters = [fresh(parameter) for parameter in parameters] 71 new_context = { 72 **context, 73 **{parameter: new_parameter for parameter, new_parameter in zip(parameters, new_parameters)}, 74 } 75 return Abstract( 76 parameters=new_parameters, 77 body=uniqify_term(body, new_context, fresh), 78 ) 79 80 case Apply(target=target, arguments=arguments): 81 return Apply( 82 target=_term(target), 83 arguments=[_term(argument) for argument in arguments], 84 ) 85 86 case Immediate(): 87 return term 88 89 case Primitive(operator=operator, left=left, right=right): 90 return Primitive( 91 operator=operator, 92 left=_term(left), 93 right=_term(right), 94 ) 95 96 case Branch(operator=operator, left=left, right=right, consequent=consequent, otherwise=otherwise): 97 return Branch( 98 operator=operator, 99 left=_term(left), 100 right=_term(right), 101 consequent=_term(consequent), 102 otherwise=_term(otherwise), 103 ) 104 105 case Allocate(): 106 return term 107 108 case Load(base=base, index=index): 109 return Load( 110 base=_term(base), 111 index=index, 112 ) 113 114 case Store(base=base, index=index, value=value): 115 return Store( 116 base=_term(base), 117 index=index, 118 value=_term(value), 119 ) 120 121 case Begin(effects=effects, value=value): # pragma: no branch 122 return Begin( 123 effects=[_term(effect) for effect in effects], 124 value=_term(value), 125 ) 126 127 128def uniqify_program( 129 program: Program, 130) -> tuple[Callable[[str], str], Program]: 131 fresh = SequentialNameGenerator() 132 133 _term = partial(uniqify_term, fresh=fresh) 134 135 match program: 136 case Program(parameters=parameters, body=body): # pragma: no branch 137 local = {parameter: fresh(parameter) for parameter in parameters} 138 return ( 139 fresh, 140 Program( 141 parameters=[local[parameter] for parameter in parameters], 142 body=_term(body, local), 143 ), 144 )
type Context =
Mapping[Identifier, Identifier]
def
uniqify_term(term: Term, context: Context, fresh: Callable[[str], str]) -> Term:
29def uniqify_term( 30 term: Term, 31 context: Context, 32 fresh: Callable[[str], str], 33) -> Term: 34 _term = partial(uniqify_term, context=context, fresh=fresh) 35 36 match term: 37 case Let(bindings=bindings, body=body): 38 new_bindings: list[tuple[Identifier, Term]] = [] 39 new_context = context 40 for name, value in bindings: 41 new_name = fresh(name) 42 new_bindings.append((new_name, _term(value))) 43 new_context = {**new_context, name: new_name} 44 45 return Let( 46 bindings=new_bindings, 47 body=uniqify_term(body, new_context, fresh), 48 ) 49 50 case LetRec(bindings=bindings, body=body): 51 new_bindings: list[tuple[Identifier, Term]] = [] 52 new_context = context 53 for name, value in bindings: 54 new_name = fresh(name) 55 new_bindings.append((new_name, value)) 56 new_context = {**new_context, name: new_name} 57 58 _new_term = partial(uniqify_term, context=new_context, fresh=fresh) 59 60 return LetRec( 61 bindings=[(new_name, _new_term(value)) for new_name, value in new_bindings], 62 body=_new_term(body), 63 ) 64 65 case Reference(name=name): 66 if name in context: 67 return Reference(name=context[name]) 68 return term 69 70 case Abstract(parameters=parameters, body=body): 71 new_parameters = [fresh(parameter) for parameter in parameters] 72 new_context = { 73 **context, 74 **{parameter: new_parameter for parameter, new_parameter in zip(parameters, new_parameters)}, 75 } 76 return Abstract( 77 parameters=new_parameters, 78 body=uniqify_term(body, new_context, fresh), 79 ) 80 81 case Apply(target=target, arguments=arguments): 82 return Apply( 83 target=_term(target), 84 arguments=[_term(argument) for argument in arguments], 85 ) 86 87 case Immediate(): 88 return term 89 90 case Primitive(operator=operator, left=left, right=right): 91 return Primitive( 92 operator=operator, 93 left=_term(left), 94 right=_term(right), 95 ) 96 97 case Branch(operator=operator, left=left, right=right, consequent=consequent, otherwise=otherwise): 98 return Branch( 99 operator=operator, 100 left=_term(left), 101 right=_term(right), 102 consequent=_term(consequent), 103 otherwise=_term(otherwise), 104 ) 105 106 case Allocate(): 107 return term 108 109 case Load(base=base, index=index): 110 return Load( 111 base=_term(base), 112 index=index, 113 ) 114 115 case Store(base=base, index=index, value=value): 116 return Store( 117 base=_term(base), 118 index=index, 119 value=_term(value), 120 ) 121 122 case Begin(effects=effects, value=value): # pragma: no branch 123 return Begin( 124 effects=[_term(effect) for effect in effects], 125 value=_term(value), 126 )
129def uniqify_program( 130 program: Program, 131) -> tuple[Callable[[str], str], Program]: 132 fresh = SequentialNameGenerator() 133 134 _term = partial(uniqify_term, fresh=fresh) 135 136 match program: 137 case Program(parameters=parameters, body=body): # pragma: no branch 138 local = {parameter: fresh(parameter) for parameter in parameters} 139 return ( 140 fresh, 141 Program( 142 parameters=[local[parameter] for parameter in parameters], 143 body=_term(body, local), 144 ), 145 )