package jazz.circuit.esterel;

//////////////////////////////////////////////////////////////////////////////
//
//                   Parsing of Pure Esterel programs
//
//  The Syntax is that of "A Hardware Implementation of Pure Esterel" by
//  Gérard Berry, except for brackets which are used to parenthesize the
//  parallel construct
//
//           [ stmt ; ... ; stmt || stmt ; ... ; stmt ]
//
//  instead of being the grouping symbols for statements.
//
//  Since parsing is, in essence, a sequential operation, we make heavy use of
//  explicit serialization of the evaluation of expressions
// 
//        (e1 ; e2 ; ... ; en)
// 
//  which evaluates, in sequence, e1, ..., and en to head-normal form. This
//  explicit serialization is required when the ei's have side effects. When
//  the values vi of ei is used in ej (j > i) the recommended programming
//  style, used below, is the following
// 
//       v = (v1 ; v2 ; ... ; vn) {
//         v1 = e1;
//         v2 = e2;
//         ...
//         vn = en;
//       }
// 
//  which is equivalent to the following C or Java statement
// 
//      {
//        T1 v1 = e1;
//        T2 v2 = e2;
//        ...
//        Tn vn = e2;
// 
//        v = vn;
//      }
//
//  where Ti is the type of ei.
//
//////////////////////////////////////////////////////////////////////////////

import jazz.circuit.*;
import jazz.io.FileReader;
import jazz.util.List;
import jazz.unsafe.Hashtable;

Module.parse(fileName: String) = ( table.put(POS, 0)
                                 ; nextToken()
                                 ; module() )
{
  POS = 0;
  TOKEN = 1;
  
  table = Hashtable.create();
  file = FileReader.open(fileName).readString();
  len = file.length();
  
  fun syntax() = error("syntax error");
  
  fun beg() = ( char
              ; table.put(POS, pos + 1)
              ; spc ? beg() : pos )
  {
    pos = table.get(POS, 0);
    char = (pos >= len) ? -1 : file.charAt(pos);
    spc = (char == 32 || char == 10 || char == 13);
  }
  
  fun end() = done ? pos : (table.put(POS, pos + 1) ; end())
  {
    pos = table.get(POS, 0);
    char = (pos >= len) ? -1 : file.charAt(pos);
    done = (char == -1 || char == 32 || char == 10 || char == 13 ||
            char == 58 || char == 124 || char == 59 || char == 91 ||
            char == 93 || char == 44);
  }
  
  fun nextToken() = ( tbeg
                    ; tend
                    ; table.put(TOKEN, token)
                    ; token )
  {
    tbeg = beg();
    tend = end();
    token = (tbeg < len && tend <= len ? file.substring(tbeg, tend) : "");
  }

  fun currentToken() = (String) table.get(TOKEN, "");
  
  fun stmt() = (s1; (t2 == ";" ? seq : s1))
  {
    t1 = currentToken();
    if (t1 == "halt") {
      s1 = new HaltStmt();
      t2 = nextToken();
    } else if (t1 == "nothing") {
      s1 = new NothingStmt();
      t2 = nextToken();
    } else if (t1 == "emit") {
      s1 = (sig; s2) {
        sig = nextToken();
        s2 = new EmitStmt(sig = sig);
      }
      t2 = nextToken();
    } else if (t1 == "exit") {
      s1 = (sig; s2) {
        sig = nextToken();
        s2 = new ExitStmt(sig = sig);
      }
      t2 = nextToken();
    } else if (t1 == "loop") {
      s1 = (nextToken(); s2; t2; s3) {
        s2 = stmt();
        t2 = currentToken() == "end" ? nextToken() : syntax();
        s3 = new LoopStmt(stmt = s2);
      }
      t2 = currentToken();
    } else if (t1 == "present") {
      s1 = (sig; t2; s2; t3; t4; s3; s5) {
        sig = nextToken();
        t2 = nextToken() == "then" ? nextToken() : syntax();
        s2 = stmt();
        t3 = currentToken();
        t4 = (t3 == "end" || t3 == "else") ? nextToken() : syntax();
        s3 = t3 == "end" ? new NothingStmt() : (s4; t5; s4);
        s4 = stmt();
        t5 = currentToken() == "end" ? nextToken() : syntax();
        s5 = new PresentStmt(sig = sig, stmt1 = s2, stmt2 = s3);
      }
      t2 = currentToken();
     } else if (t1 == "trap") {
      s1 = (sig; t2; s2; t3; s3) {
        sig = nextToken();
        t2 = nextToken() == "in" ? nextToken() : syntax();
        s2 = stmt();
        t3 = currentToken() == "end" ? nextToken() : syntax();
        s3 = new TrapStmt(sig = sig, stmt = s2);
      }
      t2 = currentToken();
     } else if (t1 == "signal") {
      s1 = (sig; t2; s2; t3; s3) {
        sig = nextToken();
        t2 = nextToken() == "in" ? nextToken() : syntax();
        s2 = stmt();
        t3 = currentToken() == "end" ? nextToken() : syntax();
        s3 = new SignalStmt(sig = sig, stmt = s2);
      }
      t2 = currentToken();
   } else if (t1 == "do") {
      s1 = (nextToken(); s2; sig; s3) {
        s2 = stmt();
        sig = currentToken() == "watching" ? nextToken() : syntax();
        s3 = new WatchStmt(sig = sig, stmt = s2);
      }
      t2 = nextToken();
    } else if (t1 == "[") {
      s1 = (nextToken(); s2; t2; t3; s3; t4; s4) {
        s2 = stmt();
        t2 = currentToken() == "|" ? nextToken() : syntax();
        t3 = currentToken() == "|" ? nextToken() : syntax();
        s3 = stmt();
        t4 = currentToken() == "]" ? nextToken() : syntax();
        s4 = new ParallelStmt(stmt1 = s2, stmt2 = s3);
      }
      t2 = currentToken();
    } else {
      s1 = syntax();
    }
    seq = ( nextToken()
          ; s2
          ; new SequenceStmt(stmt1 = s1, stmt2 = s2)) {
      s2 = stmt();
    }
  }

  fun ios(keyword) = currentToken() == keyword ? l : List.nil
  {
    l = (nextToken(); ios(List.nil));
    fun ios(l) = t1 == ";" ? l1 : l2
    {
      t1 = currentToken();
      l1 = (nextToken(); l);
      l2 = (nextToken(); t1 == "," ? ios(l) : ios(l.cons(t1)));
    }
  }
  
  fun module() = (t1; t2; i; o; s; d; mod)
  {
    t1 = currentToken() == "module" ? nextToken() : syntax();
    t2 = nextToken() == ":" ? nextToken() : syntax();
    i = ios("input").reverse();
    o = ios("output").reverse();
    s = stmt();
    d = currentToken() == "." ? () : syntax();
    mod = new Module(name = t1, inputs = i, outputs = o, stmt = s);
  }
}