"""G02: typed, pure, finite expressions. No eval or host-language calls.AST: int, bool, variable str, ('+',a,b), ('*',a,b), ('<',a,b),('if',condition,then,otherwise), ('let',name,value,body)."""definfer(expr,env=None,depth=0):env={}ifenvisNoneelseenvifdepth>48:raiseValueError("expression depth exceeds 48")iftype(expr)isbool:return"bool"iftype(expr)isint:return"int"iftype(expr)isstr:ifexprnotinenv:raiseValueError("unbound name: "+expr)returnenv[expr]iftype(expr)isnottupleornotexpr:raiseValueError("malformed expression")tag=expr[0]iftagin("+","*","<")andlen(expr)==3:ifany(infer(x,env,depth+1)!="int"forxinexpr[1:]):raiseTypeError("arithmetic/comparison requires integers")return"bool"iftag=="<"else"int"iftag=="if"andlen(expr)==4:ifinfer(expr[1],env,depth+1)!="bool":raiseTypeError("condition must be Boolean")left=infer(expr[2],env,depth+1)right=infer(expr[3],env,depth+1)ifleft!=right:raiseTypeError("branches must have identical types")returnleftiftag=="let"andlen(expr)==4andtype(expr[1])isstr:typ=infer(expr[2],env,depth+1)child=dict(env)child[expr[1]]=typreturninfer(expr[3],child,depth+1)raiseValueError("unknown node or wrong arity")defevaluate(expr,env=None):values={}ifenvisNoneelsedict(env)ifany(type(x)notin(int,bool)forxinvalues.values()):raiseTypeError("environment values must be integers or Booleans")infer(expr,{k:"bool"iftype(v)isboolelse"int"fork,vinvalues.items()})defgo(e,scope):iftype(e)in(int,bool):returneiftype(e)isstr:returnscope[e]tag=e[0]iftag=="let":value=go(e[2],scope)# value uses the outer scopereturngo(e[3],{**scope,e[1]:value})iftag=="if":returngo(e[2]ifgo(e[1],scope)elsee[3],scope)a,b=go(e[1],scope),go(e[2],scope)iftag=="+":returna+biftag=="*":returna*breturna<breturngo(expr,values)if__name__=="__main__":e=("let","x",3,("if",("<","x",4),("*","x",2),0))print(infer(e),evaluate(e))