package loop;

import loop.ast.script.Unit;
import loop.runtime.LoopInvocationHandler;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.StringReader;
import java.lang.reflect.Proxy;

/**
 * Converts parsed, type-solved, emitted code to Java classes.
 */
public class Loop {

  public static void main(String[] args) throws Exception {
    if (args.length == 0) {
      LoopShell.shell();
    }

    try {
      if (args.length > 1)
        run(args[0], args);
      else
        run(args[0]);
    } catch (RuntimeException e) {
      if (e.getCause() instanceof FileNotFoundException) {
        System.out.println("No such file: " + e.getCause().getMessage());
        System.out.println();
        System.exit(1);
      } else
        throw e;
    }
  }

  public static Object run(String file) {
    Executable unit = loopCompile(file);
    unit.runMain(true);

    return safeEval(unit, null);
  }

  public static Object run(String file, String[] args) {
    Executable unit = loopCompile(file);
    unit.runMain(true);

    return safeEval(unit, args);
  }

  public static Object evalClassOrFunction(String function,
                                           Unit shellScope) {
    Executable executable = new Executable(new StringReader(function));
    try {
      executable.compileClassOrFunction(shellScope);
    } catch (Exception e) {
      e.printStackTrace();
      return new LoopError("malformed function");
    }

    if (executable.hasErrors()) {
      executable.printStaticErrorsIfNecessary();
      return "";
    }

    return "ok";
  }

  static Object safeEval(Executable executable, String[] args) {
    if (executable.runMain()) {
      return executable.main(args);
    } else {
      executable.getCompiled();   // Forces class to be loaded & initialized.
      return null;
    }
  }

  /**
   * Compiles the specified file into a binary Java executable.
   */
  public static Class<?> compile(String file) {
    return loopCompile(file).getCompiled();
  }

  /**
   * Returns an executable that represents the compiled form of the Loop program.
   * <p/>
   * See {@link Executable} for more details on the compilation process.
   */
  private static Executable loopCompile(String file) {
    Executable executable;
    File script = new File(file);
    try {
      executable = new Executable(new FileReader(script), script.getName());
    } catch (FileNotFoundException e) {
      throw new RuntimeException(e);
    }
    executable.compile();
    if (executable.hasErrors()) {
      String errors = executable.printStaticErrorsIfNecessary();

      throw new LoopCompileException("Syntax errors exist:\n" + errors, executable);
    }
    return executable;
  }

  public static void error(String error) {
    throw new LoopExecutionException(error);
  }

  /**
   * Returns an implementation of the given Java interface that
   * is backed by the specified Loop module. The generated Java class is loaded into
   * the common runtime Loop class loader. See {@link LoopClassLoader} for details.
   *
   * @param iface A Java interface that you wish to implement using Loop
   * @param module The name of a Loop module minus the '.loop' extension. This name may
   *               contain a path-prefix from the current directory.
   */
  @SuppressWarnings("unchecked")
  public static <I> I implement(Class<I> iface, String module) {
    if (!iface.isInterface()) {
      throw new RuntimeException(iface + " is not an interface ");
    }

    return (I) Proxy.newProxyInstance(LoopClassLoader.CLASS_LOADER, new Class[]{ iface },
        new LoopInvocationHandler(iface, module));
  }
}

@@ -100,106 +100,9 @@
 .io.
-File;%0Aimport java.io.FileNotFoundException;%0Aimport java.io.FileReader;%0Aimport java.io.StringReader
+*
 ;%0Aim
@@ -701,24 +701,157 @@
 %0A    %7D%0A  %7D%0A%0A
+  /**%0A   * Executes a loop program stored in %7B@code file%7D and returns the%0A   * result of evaluating main()--which is optional.%0A   */%0A
   public sta
@@ -990,101 +990,729 @@
 %0A%0A  
-public static Object run(String file, String%5B%5D args) %7B%0A    Executable unit = loopCompile(file
+/**%0A   * Executes a loop program stored in %7B@code file%7D and returns the%0A   * result of evaluating main()--which is optional. Arguments can%0A   * be passed in which are sent to main() representing the command line.%0A   */%0A  public static Object run(String file, String%5B%5D args) %7B%0A    Executable unit = loopCompile(file);%0A    unit.runMain(true);%0A%0A    return safeEval(unit, args);%0A  %7D%0A%0A  /**%0A   * Executes a loop program read from %7B@code reader %7Dand returns the%0A   * result of evaluating main()--which is optional. Arguments can%0A   * be passed in which are sent to main() representing the command line.%0A   */%0A  public static Object run(String name, Reader reader, String%5B%5D args) %7B%0A    Executable unit = loopCompile(name, reader
 );%0A 
@@ -2690,32 +2690,242 @@
 ompiled();%0A  %7D%0A%0A
+  /**%0A   * Compiles the given script (fed by reader) into a binary Java executable.%0A   */%0A  public static Class%3C?%3E compile(String name, Reader reader) %7B%0A    return loopCompile(name, reader).getCompiled();%0A  %7D%0A%0A
   /**%0A   * Retur
@@ -3144,35 +3144,8 @@
 ) %7B%0A
-    Executable executable;%0A
     
@@ -3170,24 +3170,25 @@
 File(file);%0A
+%0A
     try %7B%0A  
@@ -3195,36 +3195,45 @@
     
-executable = new Executable(
+return loopCompile(script.getName(), 
 new 
@@ -3254,26 +3254,8 @@
 ipt)
-, script.getName()
 );%0A 
@@ -3332,24 +3332,157 @@
 n(e);%0A    %7D%0A
+  %7D%0A%0A  private static Executable loopCompile(String name, Reader reader) %7B%0A    Executable executable = new Executable(reader, name);%0A
     executab
@@ -4069,16 +4069,17 @@
 m iface 
+ 
 A Java i
@@ -4572,23 +4572,21 @@
 Class%5B%5D%7B
- 
 iface
- 
 %7D,%0A     

Allow loop programs to be executed from a given Reader