package magic.compiler;

import static org.junit.Assert.*;

import org.junit.Test;

import magic.RT;
import magic.ast.Constant;
import magic.ast.Node;
import magic.ast.Lambda;
import magic.ast.Lookup;
import magic.compiler.Analyser;
import magic.compiler.Reader;
import magic.data.PersistentList;
import magic.data.Sets;
import magic.data.Symbol;
import magic.data.Tuple;
import magic.lang.Context;
import magic.lang.Symbols;

public class TestAnalyse {

	@SuppressWarnings("unchecked")
	public <T> Node<T> analyse(String t) {
		return (Node<T>) Analyser.analyse(Reader.read(t));
	}
	
	@Test 
	public void testLookup() {
		Context c=Context.createWith("foo",Constant.create(1));
		Node<?> e=analyse("foo");
		assertEquals(Integer.valueOf(1),e.compute(c));
		
		try {
			e.compute(Context.EMPTY);
			fail("Should not be able to lookup value in empty context");
		} catch (Throwable t) {
			// OK
		}
	}
	
	@Test public void testQuote() {
		Context c=RT.INITIAL_CONTEXT;
		EvalResult<?> r=Compiler.compile(c,"(def sym 'x)");
		Context c2=r.getContext();
		assertEquals(Symbol.create("x"),c2.getValue("sym"));
	}
	
	@Test public void testSyntaxQuote() {
		Context c=RT.INITIAL_CONTEXT;
		EvalResult<?> r=Compiler.compile(c,"(def sym `x)");
		Context c2=r.getContext();
		assertEquals(Symbol.create("x"),c2.getValue("sym"));
	}
	
	@Test public void testUnquote() {
		Context c=RT.INITIAL_CONTEXT;
		EvalResult<?> r=Compiler.compile(c,"(def a 1) (def v '[~a 2])");
		Context c2=r.getContext();
		// TODO: fix this!
		Object v=c2.getValue("v");
		assertNotNull(v);
		// assertEquals(Tuple.of(1L,2L),v);
	}
	
	@Test 
	public void testInfiniteRecursiveLookup() {
		Context c=Context.createWith("foo",Lookup.create("foo"));
		Node<?> e=analyse("foo");
		
		try {
			e.compute(c);
			fail("Should not be able to lookup with inifinite recursion");
		} catch (StackOverflowError t) {
			// OK
		}
	}
	
	@Test 
	public void testConstant() {
		Node<?> e=analyse("1");
		assertEquals(Long.valueOf(1),e.compute(Context.EMPTY));
	}
	
	@Test
	public void testDeps() {
		Symbol foo=Symbol.create("foo");
		Symbol bar=Symbol.create("bar");
		assertEquals(Sets.of(foo),Analyser.analyse(Reader.read("foo")).getDependencies());
		assertEquals(Sets.of(foo,bar),Analyser.analyse(Reader.read("[foo bar]")).getDependencies());
		assertEquals(Sets.of(),Analyser.analyse(Reader.read("(fn [bar] bar)")).getDependencies());
		assertEquals(Sets.of(foo),Analyser.analyse(Reader.read("(fn [bar] foo)")).getDependencies());

		assertEquals(Sets.of(Symbols.QUOTE),Analyser.analyse(Reader.read("'(foo bar)")).getDependencies());
	}
	
	@Test 
	public void testVector() {
		Context c=Context.createWith("foo",Constant.create(2L));
		Node<?> e=analyse("[1 foo]");
		assertEquals(Tuple.of(1L,2L),e.compute(c));
	}
	
	@Test 
	public void testEmptyVector() {
		Node<?> e=analyse("[]");
		assertEquals(Tuple.EMPTY,e.compute(Context.EMPTY));
	}
	
	@Test 
	public void testEmptyList() {
		Node<?> e=analyse("()");
		assertEquals(PersistentList.EMPTY,e.compute(Context.EMPTY));
	}
	
	@Test 
	public void testLambda() {
		Node<?> e=analyse("(fn [a] a)");
		assertEquals(Lambda.class,e.getClass());
		Context c=Context.createWith("identity",e);
		Node<?> app=Analyser.analyse(c,Reader.read("(identity 2)"));
		assertEquals(Long.valueOf(2),app.compute(c));

	}

}

@@ -418,16 +418,55 @@
 Symbols;
+%0Aimport magic.lang.UnresolvedException;
 %0A%0Apublic
@@ -1136,32 +1136,318 @@
 e(%22sym%22));%0A%09%7D%0A%09%0A
+%09@Test public void testUnexpanded() %7B%0A%09%09Context c=RT.INITIAL_CONTEXT;%0A%09%09EvalResult%3C?%3E r=Compiler.compile(c,%22(def x a)%22);%0A%09%09Context c2=r.getContext();%0A%09%09try %7B%0A%09%09%09c2.getValue(%22x%22);%0A%09%09%09fail();%0A%09%09%7D catch (UnresolvedException e) %7B%0A%09%09%09assertEquals(Symbol.create(%22a%22),e.getSymbol());%0A%09%09%7D%0A%09%7D%0A%09%0A
 %09@Test public vo

Test for unresolved symbol