|  |  | @ -7,6 +7,8 @@ class SemanticAnalyzer(parser: Parser) extends Reporter with BugReporter { | 
			
		
	
		
		
			
				
					
					|  |  |  |    * Primitive functions that do not need to be defined or declared. |  |  |  |    * Primitive functions that do not need to be defined or declared. | 
			
		
	
		
		
			
				
					
					|  |  |  |    */ |  |  |  |    */ | 
			
		
	
		
		
			
				
					
					|  |  |  |   val primitives = Map[String,(Boolean,Type)]( |  |  |  |   val primitives = Map[String,(Boolean,Type)]( | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |       "toInt" -> (false, FunType(List(("", CharType)), IntType)), | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |       "toChar" -> (false, FunType(List(("", IntType)), CharType)), | 
			
		
	
		
		
			
				
					
					|  |  |  |       "getchar" -> (false, FunType(List(), IntType)), |  |  |  |       "getchar" -> (false, FunType(List(), IntType)), | 
			
		
	
		
		
			
				
					
					|  |  |  |       "putchar" -> (false, FunType(List(("", IntType)), UnitType)) |  |  |  |       "putchar" -> (false, FunType(List(("", IntType)), UnitType)) | 
			
		
	
		
		
			
				
					
					|  |  |  |     ) |  |  |  |     ) | 
			
		
	
	
		
		
			
				
					|  |  | @ -163,7 +165,10 @@ class SemanticAnalyzer(parser: Parser) extends Reporter with BugReporter { | 
			
		
	
		
		
			
				
					
					|  |  |  |    * TODO: implement the remaining binary operators for typeBinOperator |  |  |  |    * TODO: implement the remaining binary operators for typeBinOperator | 
			
		
	
		
		
			
				
					
					|  |  |  |    */ |  |  |  |    */ | 
			
		
	
		
		
			
				
					
					|  |  |  |   def typeBinOperator(op: String)(pos: Position) = op match { |  |  |  |   def typeBinOperator(op: String)(pos: Position) = op match { | 
			
		
	
		
		
			
				
					
					|  |  |  |     case "+" => FunType(List(("", IntType), ("", IntType)), IntType) |  |  |  |     case "+" | "-" | "*" | "/" =>  | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |       FunType(List(("", IntType), ("", IntType)), IntType) | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     case ">=" | "<=" | ">" | "<" | "==" | "!=" =>  | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |       FunType(List(("", IntType), ("", IntType)), BooleanType) | 
			
		
	
		
		
			
				
					
					|  |  |  |     case _ => |  |  |  |     case _ => | 
			
		
	
		
		
			
				
					
					|  |  |  |       error("undefined binary operator", pos) |  |  |  |       error("undefined binary operator", pos) | 
			
		
	
		
		
			
				
					
					|  |  |  |       UnknownType |  |  |  |       UnknownType | 
			
		
	
	
		
		
			
				
					|  |  | @ -177,6 +182,8 @@ class SemanticAnalyzer(parser: Parser) extends Reporter with BugReporter { | 
			
		
	
		
		
			
				
					
					|  |  |  |    * TODO: implement typeUnOperator |  |  |  |    * TODO: implement typeUnOperator | 
			
		
	
		
		
			
				
					
					|  |  |  |    */ |  |  |  |    */ | 
			
		
	
		
		
			
				
					
					|  |  |  |   def typeUnOperator(op: String)(pos: Position) = op match { |  |  |  |   def typeUnOperator(op: String)(pos: Position) = op match { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     case "+" | "-" => FunType(List(("", IntType)), IntType) | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | //    case "!" => FunType(List(("", BooleanType)), BooleanType) | 
			
		
	
		
		
			
				
					
					|  |  |  |     case _ => |  |  |  |     case _ => | 
			
		
	
		
		
			
				
					
					|  |  |  |       error(s"undefined unary operator", pos) |  |  |  |       error(s"undefined unary operator", pos) | 
			
		
	
		
		
			
				
					
					|  |  |  |       UnknownType |  |  |  |       UnknownType | 
			
		
	
	
		
		
			
				
					|  |  | @ -188,6 +195,8 @@ class SemanticAnalyzer(parser: Parser) extends Reporter with BugReporter { | 
			
		
	
		
		
			
				
					
					|  |  |  |    * operators: block-set |  |  |  |    * operators: block-set | 
			
		
	
		
		
			
				
					
					|  |  |  |    */ |  |  |  |    */ | 
			
		
	
		
		
			
				
					
					|  |  |  |   def typeTerOperator(op: String)(pos: Position) = op match { |  |  |  |   def typeTerOperator(op: String)(pos: Position) = op match { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     case "block-set" =>  | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |       FunType(List(("", ArrayType(IntType)), ("", IntType)), IntType) | 
			
		
	
		
		
			
				
					
					|  |  |  |     case _ => |  |  |  |     case _ => | 
			
		
	
		
		
			
				
					
					|  |  |  |       error(s"undefined ternary operator", pos) |  |  |  |       error(s"undefined ternary operator", pos) | 
			
		
	
		
		
			
				
					
					|  |  |  |       UnknownType |  |  |  |       UnknownType | 
			
		
	
	
		
		
			
				
					|  |  | @ -215,7 +224,8 @@ class SemanticAnalyzer(parser: Parser) extends Reporter with BugReporter { | 
			
		
	
		
		
			
				
					
					|  |  |  |     case (_, UnknownType) => typeWellFormed(tp)(env, pos)  // tp <: Any |  |  |  |     case (_, UnknownType) => typeWellFormed(tp)(env, pos)  // tp <: Any | 
			
		
	
		
		
			
				
					
					|  |  |  |     case (UnknownType, _) => typeWellFormed(pt)(env, pos)  // for function arguments |  |  |  |     case (UnknownType, _) => typeWellFormed(pt)(env, pos)  // for function arguments | 
			
		
	
		
		
			
				
					
					|  |  |  |     case (FunType(args1, rtp1), FunType(args2, rtp2)) if args1.length == args2.length =>  |  |  |  |     case (FunType(args1, rtp1), FunType(args2, rtp2)) if args1.length == args2.length =>  | 
			
		
	
		
		
			
				
					
					|  |  |  |       ??? // TODO: Function type conformity |  |  |  |       FunType(typeConform(args1, args2)(env, pos), typeConforms(rtp1, rtp2)(env, pos)) | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |       // Done: Function type conformity | 
			
		
	
		
		
			
				
					
					|  |  |  |     case (ArrayType(tp), ArrayType(pt)) => ArrayType(typeConforms(tp, pt)(env, pos)) |  |  |  |     case (ArrayType(tp), ArrayType(pt)) => ArrayType(typeConforms(tp, pt)(env, pos)) | 
			
		
	
		
		
			
				
					
					|  |  |  |     case _ => error(s"type mismatch;\nfound   : $tp\nexpected: $pt", pos); pt |  |  |  |     case _ => error(s"type mismatch;\nfound   : $tp\nexpected: $pt", pos); pt | 
			
		
	
		
		
			
				
					
					|  |  |  |   } |  |  |  |   } | 
			
		
	
	
		
		
			
				
					|  |  | @ -281,12 +291,22 @@ class SemanticAnalyzer(parser: Parser) extends Reporter with BugReporter { | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |   def typeInfer(exp: Exp, pt: Type)(env: TypeEnv): Exp = exp match { |  |  |  |   def typeInfer(exp: Exp, pt: Type)(env: TypeEnv): Exp = exp match { | 
			
		
	
		
		
			
				
					
					|  |  |  |     case Lit(_: Int) => exp.withType(IntType) |  |  |  |     case Lit(_: Int) => exp.withType(IntType) | 
			
		
	
		
		
			
				
					
					|  |  |  |     case Lit(_: Boolean) => ??? |  |  |  |     case Lit(_: Boolean) => exp.withType(BooleanType) | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |     case Lit(_: Unit) => ??? |  |  |  |     case Lit(_: Char) => exp.withType(CharType) | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |     case Prim("block-set", args) => ??? |  |  |  |     case Lit(_: Unit) => exp.withType(UnitType) | 
			
				
				
			
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     case Prim("block-set", args) =>  | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |       Prim("block-set", List( | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         typeCheck(args(0), ArrayType(pt))(env),  | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         typeCheck(args(1), IntType)(env),  | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         typeCheck(args(2), pt)(env) | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |       )).withType(UnitType) | 
			
		
	
		
		
			
				
					
					|  |  |  |     case Prim(op, args) => |  |  |  |     case Prim(op, args) => | 
			
		
	
		
		
			
				
					
					|  |  |  |       typeOperator(op, args.length)(exp.pos) match { |  |  |  |       typeOperator(op, args.length)(exp.pos) match { | 
			
		
	
		
		
			
				
					
					|  |  |  |         case FunType(atps, rtp) => ??? |  |  |  |         case FunType(atps, rtp) =>  | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |           val args_checked = (args zip atps) map { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |             case (arg, (_, param)) => typeCheck(arg, param)(env) | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |           } | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |           Prim(op, args_checked).withType(rtp) | 
			
		
	
		
		
			
				
					
					|  |  |  |         case UnknownType => exp.withType(UnknownType) |  |  |  |         case UnknownType => exp.withType(UnknownType) | 
			
		
	
		
		
			
				
					
					|  |  |  |         case _ => BUG("operator's type needs to be FunType") |  |  |  |         case _ => BUG("operator's type needs to be FunType") | 
			
		
	
		
		
			
				
					
					|  |  |  |       } |  |  |  |       } | 
			
		
	
	
		
		
			
				
					|  |  | @ -298,18 +318,28 @@ class SemanticAnalyzer(parser: Parser) extends Reporter with BugReporter { | 
			
		
	
		
		
			
				
					
					|  |  |  |       Let(x, nrhs.tp, nrhs, nbody).withType(nbody.tp) |  |  |  |       Let(x, nrhs.tp, nrhs, nbody).withType(nbody.tp) | 
			
		
	
		
		
			
				
					
					|  |  |  |     case Ref(x) => |  |  |  |     case Ref(x) => | 
			
		
	
		
		
			
				
					
					|  |  |  |       env(x) match { |  |  |  |       env(x) match { | 
			
		
	
		
		
			
				
					
					|  |  |  |         case Some(tp) => ??? // Remember to check that the type taken from the environment is welformed |  |  |  |         case Some(tp) =>  | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |           exp.withType(typeWellFormed(tp)(env, exp.pos)) | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |           // Remember to check that the type taken from the environment is wellformed | 
			
		
	
		
		
			
				
					
					|  |  |  |         case _ => |  |  |  |         case _ => | 
			
		
	
		
		
			
				
					
					|  |  |  |           error("undefined identifier", exp.pos) |  |  |  |           error("undefined identifier", exp.pos) | 
			
		
	
		
		
			
				
					
					|  |  |  |           ??? |  |  |  |           exp.withType(UnknownType) | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  |       } |  |  |  |       } | 
			
		
	
		
		
			
				
					
					|  |  |  |     case If(cond, tBranch, eBranch) => |  |  |  |     case If(cond, tBranch, eBranch) => | 
			
		
	
		
		
			
				
					
					|  |  |  |       // Hint: type check the else branch before the then branch. |  |  |  |       // Hint: type check the else branch before the then branch. | 
			
		
	
		
		
			
				
					
					|  |  |  |       ??? |  |  |  |       val condChecked = typeCheck(cond, BooleanType)(env)  | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |       val elseChecked = typeCheck(eBranch, pt)(env) | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |       val thenChecked = typeCheck(tBranch, elseChecked.tp)(env) | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |       If(condChecked, thenChecked, elseChecked).withType(elseChecked.tp) | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |     case VarDec(x, tp, rhs, body) => |  |  |  |     case VarDec(x, tp, rhs, body) => | 
			
		
	
		
		
			
				
					
					|  |  |  |       if (env.isDefined(x)) |  |  |  |       if (env.isDefined(x)) | 
			
		
	
		
		
			
				
					
					|  |  |  |         warn("reuse of variable name", exp.pos) |  |  |  |         warn("reuse of variable name", exp.pos) | 
			
		
	
		
		
			
				
					
					|  |  |  |       ??? |  |  |  |       val rhsChecked = typeCheck(rhs, tp)(env) | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |       val _env = env.withVar(x, rhsChecked.tp) | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |       val bodyChecked = typeCheck(body, pt)(_env) | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |       VarDec(x, rhsChecked.tp, rhsChecked, bodyChecked).withType(bodyChecked.tp) | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |     case VarAssign(x, rhs) => |  |  |  |     case VarAssign(x, rhs) => | 
			
		
	
		
		
			
				
					
					|  |  |  |       val xtp = if (!env.isDefined(x)) { |  |  |  |       val xtp = if (!env.isDefined(x)) { | 
			
		
	
		
		
			
				
					
					|  |  |  |         error("undefined identifier", exp.pos) |  |  |  |         error("undefined identifier", exp.pos) | 
			
		
	
	
		
		
			
				
					|  |  | @ -320,7 +350,7 @@ class SemanticAnalyzer(parser: Parser) extends Reporter with BugReporter { | 
			
		
	
		
		
			
				
					
					|  |  |  |         env(x).get |  |  |  |         env(x).get | 
			
		
	
		
		
			
				
					
					|  |  |  |       } |  |  |  |       } | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |       ??? |  |  |  |       val rhsChecked = typeCheck(rhs, xtp)(env) | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |       /* Because of syntactic sugar, a variable assignment  |  |  |  |       /* Because of syntactic sugar, a variable assignment  | 
			
		
	
		
		
			
				
					
					|  |  |  |        * statement can be accepted as an expression |  |  |  |        * statement can be accepted as an expression | 
			
		
	
	
		
		
			
				
					|  |  | @ -338,18 +368,35 @@ class SemanticAnalyzer(parser: Parser) extends Reporter with BugReporter { | 
			
		
	
		
		
			
				
					
					|  |  |  |        * Without changing the semantics! |  |  |  |        * Without changing the semantics! | 
			
		
	
		
		
			
				
					
					|  |  |  |        */ |  |  |  |        */ | 
			
		
	
		
		
			
				
					
					|  |  |  |       pt match { |  |  |  |       pt match { | 
			
		
	
		
		
			
				
					
					|  |  |  |         case UnitType => ??? |  |  |  |         case UnitType | IntType | BooleanType | CharType => VarAssign(x, rhsChecked).withType(rhsChecked.tp) | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |         case _ => ??? |  |  |  |         case _ => exp.withType(UnknownType) | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |       } |  |  |  |       } | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |     case While(cond, lbody, body) => ??? |  |  |  |     case While(cond, lbody, body) =>  | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |     case FunDef(fname, args, rtp, fbody) => ??? |  |  |  |       val condChecked = typeCheck(cond, BooleanType)(env) | 
			
				
				
			
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |       val lbodyChecked = typeCheck(lbody, UnitType)(env) | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |       val bodyChecked = typeCheck(body, pt)(env) | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |       While(condChecked, lbodyChecked, bodyChecked).withType(bodyChecked.tp) | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     case FunDef(fname, args, rtp, fbody) =>  | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |       checkDuplicateNames(args) | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |       val argsWType = args map { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         a => (a.name, typeWellFormed(a.tp)(env, a.pos)) | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |       } | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |       val bodyChecked = typeCheck(fbody, rtp)(env.withVals(argsWType)) // check rtp w/ pt? | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |       FunDef(fname, args, bodyChecked.tp, bodyChecked).withType( | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         FunType(argsWType, bodyChecked.tp) | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |       ) | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |     case LetRec(funs, body) => |  |  |  |     case LetRec(funs, body) => | 
			
		
	
		
		
			
				
					
					|  |  |  |       // TODO modify to handle general case |  |  |  |       // TODO modify to handle general case | 
			
		
	
		
		
			
				
					
					|  |  |  |       val nbody = typeCheck(body, pt)(env) |  |  |  |       val _env = env.withVals(funs.map {case FunDef(name, _, rtp, _) => (name, rtp)}) | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |       LetRec(Nil, nbody).withType(nbody.tp) |  |  |  |       val funsChecked = funs map {case f@FunDef(_, _, rtp, _) => typeCheck(f, rtp)(_env) } | 
			
				
				
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |       val bodyChecked = typeCheck(body, pt)( env.withVals(funsChecked map {case FunDef(name, _, rtp, _) => (name, rtp)})) | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |       LetRec(funsChecked, bodyChecked).withType(bodyChecked.tp) | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |     case App(fun, args) => |  |  |  |     case App(fun, args) => | 
			
		
	
		
		
			
				
					
					|  |  |  |       // TODO Check fun type |  |  |  |       // TODO Check fun type | 
			
		
	
		
		
			
				
					
					|  |  |  |       val nFun: Exp = ??? |  |  |  |       val nFun: Exp = typeCheck(fun, fun.tp)(env) | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |       // Handling some errors |  |  |  |       // Handling some errors | 
			
		
	
		
		
			
				
					
					|  |  |  |       val ftp = nFun.tp match { |  |  |  |       val ftp = nFun.tp match { | 
			
		
	
	
		
		
			
				
					|  |  | @ -369,7 +416,9 @@ class SemanticAnalyzer(parser: Parser) extends Reporter with BugReporter { | 
			
		
	
		
		
			
				
					
					|  |  |  |       } |  |  |  |       } | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |       // TODO: Check arguments type |  |  |  |       // TODO: Check arguments type | 
			
		
	
		
		
			
				
					
					|  |  |  |       val nargs: List[Exp] = ??? |  |  |  |       val nargs: List[Exp] = (args zip ftp.args) map { | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         case (arg, (_, param)) => typeCheck(arg, param)(env) | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |       } | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |       // Transform some function applications into primitives on arrays. |  |  |  |       // Transform some function applications into primitives on arrays. | 
			
		
	
		
		
			
				
					
					|  |  |  |       nFun.tp match { |  |  |  |       nFun.tp match { | 
			
		
	
	
		
		
			
				
					|  |  | @ -380,7 +429,7 @@ class SemanticAnalyzer(parser: Parser) extends Reporter with BugReporter { | 
			
		
	
		
		
			
				
					
					|  |  |  |     case ArrayDec(size: Exp, etp: Type) =>  |  |  |  |     case ArrayDec(size: Exp, etp: Type) =>  | 
			
		
	
		
		
			
				
					
					|  |  |  |       // TODO: Check array declaration |  |  |  |       // TODO: Check array declaration | 
			
		
	
		
		
			
				
					
					|  |  |  |       // Note that etp is the type of elements |  |  |  |       // Note that etp is the type of elements | 
			
		
	
		
		
			
				
					
					|  |  |  |       ??? |  |  |  |       ArrayDec(typeCheck(size, IntType)(env), etp).withType(etp) | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  |     case _ => BUG(s"malformed expresstion $exp") |  |  |  |     case _ => BUG(s"malformed expresstion $exp") | 
			
		
	
		
		
			
				
					
					|  |  |  |   } |  |  |  |   } | 
			
		
	
		
		
			
				
					
					|  |  |  | } |  |  |  | } | 
			
		
	
	
		
		
			
				
					|  |  | 
 |