package org.metaborg.meta.lang.dynsem.interpreter.nodes.matching;

import org.metaborg.meta.lang.dynsem.interpreter.terms.IListTerm;
import org.metaborg.meta.lang.dynsem.interpreter.utils.SourceSectionUtil;
import org.spoofax.interpreter.core.Tools;
import org.spoofax.interpreter.terms.IStrategoAppl;

import com.oracle.truffle.api.dsl.Fallback;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.FrameDescriptor;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.ExplodeLoop;
import com.oracle.truffle.api.source.SourceSection;

public abstract class GenericListMatch extends MatchPattern {

	protected final int numHeadElems;
	@Child protected MatchPattern tailPattern;

	public GenericListMatch(SourceSection source, int numHeadElems, MatchPattern tailPattern) {
		super(source);
		this.numHeadElems = numHeadElems;
		this.tailPattern = tailPattern;
	}

	@Specialization(guards = { "tailPattern == null", "numHeadElems != other.size()" })
	public void matchNoTailEqual(VirtualFrame frame, IListTerm<?> other) {
		;
	}

	@Specialization(guards = "tailPattern == null")
	public void matchNoTailNotEqual(VirtualFrame frame, IListTerm<?> other) {
		throw PatternMatchFailure.INSTANCE;
	}

	@Specialization(guards = "numHeadElems > other.size()")
	public void matchTailNotEqual(VirtualFrame frame, IListTerm<?> other) {
		throw PatternMatchFailure.INSTANCE;
	}

	@Specialization
	@ExplodeLoop
	public void matchTail(VirtualFrame frame, IListTerm<?> other) {
		IListTerm<?> tail = other;
		for (int i = 0; i < numHeadElems; i++) {
			tail = other.tail();
		}
		tailPattern.executeMatch(frame, tail);
	}

	@Fallback
	public void doFail(VirtualFrame frame, Object t) {
		throw PatternMatchFailure.INSTANCE;
	}

	public static GenericListMatch create(IStrategoAppl t, FrameDescriptor fd) {
		assert Tools.hasConstructor(t, "List", 1) || Tools.hasConstructor(t, "ListTail", 2);

		final int numHeadElems = Tools.listAt(t, 0).size();

		MatchPattern tailPattern = null;
		if (Tools.hasConstructor(t, "ListTail", 2)) {
			tailPattern = MatchPattern.create(Tools.applAt(t, 1), fd);
		}

		return GenericListMatchNodeGen.create(SourceSectionUtil.fromStrategoTerm(t), numHeadElems, tailPattern);
	}

}

@@ -497,57 +497,8 @@
 me;%0A
-import com.oracle.truffle.api.nodes.ExplodeLoop;%0A
 impo
@@ -898,18 +898,16 @@
 guards =
- %7B
  %22tailPa
@@ -924,42 +924,8 @@
 ull%22
-, %22numHeadElems != other.size()%22 %7D
 )%0A%09p
@@ -939,24 +939,16 @@
 oid 
-match
+do
 NoTail
-Equal
 (Vir
@@ -981,149 +981,84 @@
 %3C?%3E 
-other
+list
 ) %7B%0A%09%09
-;%0A%09%7D%0A%0A%09@Specialization(guards = %22tailPattern == null%22)%0A%09public void matchNoTailNotEqual(VirtualFrame frame, IListTerm%3C?%3E other
+assert tailPattern == null;%0A%09%09if (numHeadElems != list.size()
 ) %7B%0A
+%09
 %09%09th
@@ -1083,32 +1083,36 @@
 ilure.INSTANCE;%0A
+%09%09%7D%0A
 %09%7D%0A%0A%09@Specializa
@@ -1130,183 +1130,29 @@
  = %22
-numHeadElems %3E other.size()%22)%0A%09public void matchTailNotEqual(VirtualFrame frame, IListTerm%3C?%3E other) %7B%0A%09%09throw PatternMatchFailure.INSTANCE;%0A%09%7D%0A%0A%09@Specialization%0A%09@ExplodeLoop
+tailPattern != null%22)
 %0A%09pu
@@ -1161,20 +1161,21 @@
 ic void 
-matc
+doWit
 hTail(Vi
@@ -1209,117 +1209,127 @@
 %3C?%3E 
-other
+list
 ) %7B%0A%09%09
-IListTerm%3C?%3E tail = other
+assert tailPattern != null
 ;%0A
+%0A
 %09%09
-for (int i = 0; i %3C numHeadElems; i++) %7B%0A%09%09%09tail = other.tail()
+if (numHeadElems %3E list.size()) %7B%0A%09%09%09throw PatternMatchFailure.INSTANCE
 ;%0A%09%09%7D%0A
+%0A
 %09%09ta
@@ -1358,20 +1358,39 @@
 (frame, 
-tail
+list.drop(numHeadElems)
 );%0A%09%7D%0A%0A%09

Fix implementation for generic list pattern matching (previous implementation had incorrect specializations)