package wyil.lang;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Set;

public abstract class RVal {
	public abstract Type type();
	
	public static abstract class LVal extends RVal {}
	
	public static void usedVariables(RVal r, Set<String> uses) {
		if (r instanceof Variable) {
			Variable v = (Variable) r;
			uses.add(v.name);
		} else if(r instanceof Register) {
			Register v = (Register) r;
			uses.add("%" + v.index);
		}
	}
	
	/**
	 * Substitute all occurrences of variable from with variable to.
	 * 
	 * @param c
	 * @param uses
	 */
	public static RVal substitute(HashMap<String,RVal> binding, RVal r) {
		if (r instanceof Variable) {
			Variable v = (Variable) r;
			RVal rv = binding.get(v.name);
			if (rv != null) {
				return rv;
			} 
		}
		return r;
	}
	
	public static Variable VAR(Type t, String v) {
		return get(new Variable(t,v));
	}
	
	public static Register REG(Type t, int index) {
		return get(new Register(t,index));
	}
	
	
	public static class Variable extends LVal {
		public final String name;
		public final Type type;
		
		Variable(Type type, String name) {
			this.name = name;
			this.type = type;
			if (this.name.contains("%")) {
				throw new IllegalArgumentException(
						"wyil.lang.RVal.Variable name cannot contain \"$\" --- reserved for registers");
			}
		}
		public Type type() {
			return type;
		}
		public int hashCode() {
			return type.hashCode() + name.hashCode();
		}
		public boolean equals(Object o) {
			if(o instanceof Variable) {
				Variable v = (Variable) o;
				return type.equals(v.type) && name.equals(v.name);
			}
			return false;
		}
		public String toString() {
			return "(" + type + ") " + name;
		}
	}
	
	public static class Register extends LVal {
		public final int index;
		public final Type type;
		
		Register(Type type, int index) {
			this.index = index;
			this.type = type;
		}
		public Type type() {
			return type;
		}
		public int hashCode() {
			return type.hashCode() + index;
		}
		public boolean equals(Object o) {
			if(o instanceof Register) {
				Register v = (Register) o;
				return type.equals(v.type) && index == v.index;
			}
			return false;
		}
		public String toString() {
			return "(" + type + ") %" + index;
		}
	}
	
	private static final ArrayList<RVal> values = new ArrayList<RVal>();
	private static final HashMap<RVal,Integer> cache = new HashMap<RVal,Integer>();
	
	private static <T extends RVal> T get(T type) {
		Integer idx = cache.get(type);
		if(idx != null) {
			return (T) values.get(idx);
		} else {					
			cache.put(type, values.size());
			values.add(type);
			return type;
		}
	}
}

@@ -804,16 +804,222 @@
 %0A%09%09%09%7D %0D%0A
+%09%09%7D else if (r instanceof Register) %7B%0D%0A%09%09%09Register v = (Register) r;%0D%0A%09%09%09// FIXME: should changing the type of binding%0D%0A%09%09%09RVal rv = binding.get(v.toString());%0D%0A%09%09%09if (rv != null) %7B%0D%0A%09%09%09%09return rv;%0D%0A%09%09%09%7D %0D%0A
 %09%09%7D%0D%0A%09%09r
@@ -1968,25 +1968,262 @@
 ;%0D%0A%09%09%7D%0D%0A%09%7D%0D%0A
-%09
+%0D%0A%09/**%0D%0A%09 * A register is different from a variable because it's explicitly%0D%0A%09 * designated as temporary. In contract, a Variable corresponds to a program%0D%0A%09 * variable from the original source language.%0D%0A%09 * %0D%0A%09 * @author djp%0D%0A%09 * %0D%0A%09 */
 %0D%0A%09public st

A couple of minor things.