
// JSimple.java
// Andrew Davison, ad@coe.psu.ac.th, July 2023

/* Converts code like that found in
   JEP 445 ("Unnamed Classes and Instance Main Methods")
   into a complete Java class with a main().

   The code is in a text file, and can consist of
   one-line declarations, functions enclosed between
   "func" and "end" keywords, and a main code body
   that starts after the keyword "main". 

   There must be a main body, but the declarations 
   and functions are optional.

   The generated Java code is saved in <text-fnm>.java

   Info on JEP 445:
     * https://openjdk.org/jeps/445
     * https://www.infoq.com/news/2023/05/beginner-friendly-java/

   Usage:
    > compile JSimple.java
    > run JSimple <simplified code file>
   e.g.
    > run JSimple JSimp1.txt
        // generates JSimp1.java

    You can then compile and run the code:
       > javac JSimp1.java
       > java JSimp1
*/

import java.io.*;
import java.util.*;

import org.apache.velocity.app.*;
import org.apache.velocity.*;
import org.apache.velocity.tools.generic.*;


public class JSimple
{
  private static final String TEMPLATE_FNM = "jsimple.vm";

  private static List<String> decls = new ArrayList<>();
  private static List<String> funcs = new ArrayList<>();
  private static String mainBody;


  public static void main(String args[]) throws Exception
  {
    if (args.length < 1) {
      System.out.println("Usage: run JSimple <simplified code file>");
      return;
    }

    loadSimple(args[0]);
    String className = stripExt(args[0]);

    Velocity.init();
    System.out.println("Loading template: " + TEMPLATE_FNM);
    Template template = Velocity.getTemplate(TEMPLATE_FNM);
     
    VelocityContext vc = new VelocityContext();
    vc.put("dateTool", new ComparisonDateTool());  // load tool

    vc.put("className", className);
    if (!decls.isEmpty())
      vc.put("decls", decls);
    if (!funcs.isEmpty())
      vc.put("funcs", funcs);

    if (mainBody.length() == 0)
      System.out.println("No main() found");
    else
      vc.put("mainBody", mainBody);

    String fnm = className + ".java";
    System.out.println("\nGenerating " + fnm);

    Writer w = new FileWriter(new File(fnm));
    template.merge(vc, w);
    w.flush();
    w.close();
  } /* main */



  private static void loadSimple(String fnm)
  /* The format of the input lines are:
     and blank lines and comment lines.
     Keywords are:
         'main', 'func', and 'end'
  */
  { 
    boolean inMain = false;
    boolean inFunc = false;
    StringBuilder func = new StringBuilder();

    try {
      System.out.println("Loading file: " + fnm);
      BufferedReader br = new BufferedReader( new FileReader(fnm));
      String line, trimmed;
      StringBuilder sb = new StringBuilder();
      while((line = br.readLine()) != null) {
        trimmed = line.trim();
        if (trimmed.length() == 0)  // blank line
          continue;
        if (trimmed.startsWith("//"))   // comment
          continue;

        if (trimmed.startsWith("main")) {  // start of main
          inMain = true;
          continue;
        }
        if (trimmed.startsWith("func")) {  // start of a function
          inFunc = true;
          func = new StringBuilder();
          continue;
        }
        if (trimmed.startsWith("end")) {  // end of a function
          inFunc = false;
          funcs.add( func.toString());
          continue;
        }

        if (inFunc)
          func.append(line + "\n");
        else if (inMain)
          sb.append("  " + line + "\n");
        else // one-line declarations, so add immediately
          decls.add(trimmed);
      }
      br.close();
      mainBody = sb.toString();
    } 
    catch (IOException e) 
    { System.out.println("  " + e);
      System.exit(1);
    }
  }  // end of loadSimple()



  private static String stripExt(String fnm)
  {
    int dotPos = fnm.lastIndexOf('.');
    if (dotPos == -1)
      return fnm;
    else
      return fnm.substring(0, dotPos);
  }  // end of stripExt()


}  // end of JSimple class
