package org.cafebabepy.evaluter.Interpret;

import java.util.*;

/**
 * Created by yotchang4s on 2018/09/01.
 */
public abstract class Yielder<T> {
    private ThreadLocal<Context<T>> context = new ThreadLocal<>();

    public static <T> YielderIterable<T> newIterable(final Yielder<T> yielder) {
        return new YielderIterable<>(yielder);
    }

    protected final void yield(T returnValue) {
        this.context.get().yield(returnValue);
    }

    public abstract void run();

    private static class Context<T> {
        private final Queue<T> queue;
        private final Object lock = new Object();

        private boolean endReceived;
        private RuntimeException thrownException;

        Context() {
            this.queue = new ArrayDeque<>();
        }

        synchronized void yield(T returnValue) {
            try {
                if (this.endReceived) {
                    throw new IllegalStateException("yield() end has been called.");
                }

                this.queue.offer(returnValue);

                synchronized (this.lock) {
                    try {
                        this.lock.notifyAll();
                        this.lock.wait();

                    } catch (InterruptedException ignore) {
                    }
                }

            } catch (RuntimeException e) {
                this.thrownException = e;
            }
        }

        synchronized void yieldEnd() {
            synchronized (lock) {
                this.endReceived = true;
                lock.notifyAll();
            }
        }

        T pop() {
            return queue.poll();
        }
    }

    public static class YielderIterable<T> implements Iterable<T> {
        private final Context<T> context;
        private final Thread thread;

        private boolean iteratorCalled;

        private YielderIterable(Yielder<T> yielder) {
            this.context = new Context<>();
            this.thread = new Thread(() -> {
                try {
                    yielder.context.set(YielderIterable.this.context);
                    yielder.run();

                } catch (RuntimeException e) {
                    if (this.context.thrownException != null) {
                        e.addSuppressed(this.context.thrownException);
                    }

                    this.context.thrownException = e;

                } finally {
                    context.yieldEnd();
                    yielder.context.remove();
                }
            });

            this.thread.start();
        }

        @Override
        public synchronized Iterator<T> iterator() {
            if (iteratorCalled) {
                throw new IllegalStateException("iterator() has been called");
            }

            iteratorCalled = true;

            return new Iterator<T>() {
                T nextItem;

                @Override
                public boolean hasNext() {
                    if (!context.endReceived) {
                        synchronized (context.lock) {
                            if (!context.endReceived) {
                                try {
                                    context.lock.notifyAll();
                                    context.lock.wait();

                                } catch (InterruptedException ignore) {
                                }
                            }
                        }
                    }

                    if (nextItem != null) {
                        return true;
                    }

                    nextItem = context.pop();

                    return nextItem != null;
                }

                @Override
                public T next() {
                    if (!hasNext()) {
                        throw new NoSuchElementException();
                    }

                    T result = nextItem;
                    nextItem = null;

                    return result;
                }
            };
        }

        public final Optional<RuntimeException> thrownException() {
            RuntimeException e = this.context.thrownException;
            if (e == null) {
                return Optional.empty();
            }

            return Optional.of(e);
        }
    }
}

@@ -1796,32 +1796,41 @@
         private 
+volatile 
 boolean iterator
@@ -1836,16 +1836,64 @@
 rCalled;
+%0A        private volatile boolean threadStarted;
 %0A%0A      
@@ -2563,24 +2563,65 @@
        %7D);%0A%0A
+            this.iteratorCalled = false;%0A
             
@@ -2635,16 +2635,23 @@
 read
-.s
+S
 tart
-()
+ed = false
 ;%0A  
@@ -2746,16 +2746,21 @@
     if (
+this.
 iterator
@@ -2875,16 +2875,21 @@
         
+this.
 iterator
@@ -3229,32 +3229,246 @@
           try %7B%0A
+                                    if (!threadStarted) %7B%0A                                        thread.start();%0A                                        threadStarted = true;%0A                                    %7D%0A
                 
@@ -3635,32 +3635,82 @@
 ption ignore) %7B%0A
+                                    return false;%0A
                 
@@ -4326,32 +4326,32 @@
               %7D%0A
-
             %7D;%0A 
@@ -4352,32 +4352,185 @@
   %7D;%0A        %7D%0A%0A
+        @Override%0A        protected void finalize() %7B%0A            if (threadStarted) %7B%0A                this.thread.interrupt();%0A            %7D%0A        %7D%0A%0A
         public f

Fix yield