
/******************************************************************************
* MODULE     : totm.gen.cc
* DESCRIPTION: conversion of TeXmacs trees to the TeXmacs file format
* COPYRIGHT  : (C) 1999  Joris van der Hoeven
*******************************************************************************
* This software falls under the GNU general public license and comes WITHOUT
* ANY WARRANTY WHATSOEVER. See the file $TEXMACS_PATH/LICENSE for more details.
* If you don't have this file, write to the Free Software Foundation, Inc.,
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
******************************************************************************/

#module code_totm

/******************************************************************************
* Conversion of TeXmacs trees to the present TeXmacs string format
******************************************************************************/

struct tm_writer {
  string  buf;       // the resulting string
  string  spc;       // "" or " "
  string  tmp;       // not yet flushed characters
  int     mode;      // normal: 0, verbatim: 1, mathematics: 2

  int     tab;       // number of tabs after CR
  int     xpos;      // current horizontal position in buf
  bool    spc_flag;  // TRUE if last printed character was a space or CR
  bool    ret_flag;  // TRUE if last printed character was a CR

  tm_writer ():
    buf (""), spc (""), tmp (""), mode (0),
    tab (0), xpos (0), spc_flag (TRUE), ret_flag (TRUE) {}

  void cr ();
  void flush ();
  void write_space ();
  void write_return ();
  void write (string s, bool flag= TRUE);
  void br (int indent= 0);
  void tag (string before, string s, string after);
  void apply (string func, array<tree> args);
  void write (tree t);
};

void
tm_writer::cr () {
  int i, n= N(buf);
  for (i=n-1; i>=0; i--)
    if (buf[i] != ' ') break;
  if (i<n-1) {
    buf= buf (0, i+1);
    n  = n- N(buf);
    for (i=0; i<n; i++) buf << "\\ ";
  }
  buf << '\n';
  for (i=0; i<min(tab,20); i++) buf << ' ';
  xpos= tab;
}

void
tm_writer::flush () {
  int i, m= N(spc), n= N(tmp);
  if ((m+n)==0) return;
  if ((xpos+m+n) < 78) {
    buf << spc << tmp;
    xpos += m+n;
  }
  else {
    if (spc == " ") {
      if (xpos > 40) cr ();
      else {
	buf << " ";
	xpos++;
      }
    }
    if ((xpos+n) < 78) {
      buf << tmp;
      xpos += n;
    }
    else for (i=0; i<n; ) {
      if (((i+1) < n) && (tmp[i] == '\\') && (tmp[i+1] == ' ')) {
	if (xpos >= 76) {
	  buf << "\\";
	  cr ();
	}
	buf << "\\ ";
	xpos += 2;
	i += 2;
      }
      else {
	if (xpos >= 77) {
	  buf << "\\";
	  cr ();
	}
	buf << tmp[i];
	xpos++;
	i++;
      }
    }
  }
  spc= "";
  tmp= "";
}

void
tm_writer::write_space () {
  if (spc_flag) tmp << "\\ ";
  else {
    flush ();
    spc= " ";
  }
  spc_flag= TRUE;
  ret_flag= FALSE;
}

void
tm_writer::write_return () {
  if (ret_flag) {
    buf << "\\;\n";
    cr ();
  }
  else {
    if ((spc == " ") && (tmp == "")) {
      spc= "";
      tmp= "\\ ";
    }
    flush ();
    buf << "\n";
    cr ();
  }
  spc_flag= TRUE;
  ret_flag= TRUE;
}

void
tm_writer::write (string s, bool flag) {
  if (flag) {
    int i, n=N(s);
    for (i=0; i<n; i++) {
      char c= s[i];
      if (c == ' ') write_space ();
      else {
	if (c == '\n') tmp << "\\n";
	else if (c == '\t') tmp << "\\t";
	else if (c == '\0') tmp << "\\0";
	else if (c == '\\') tmp << "\\\\";
	else if (c == '<') tmp << "\\<";
	else if (c == '|') tmp << "\\|";
	else if (c == '>') tmp << "\\>";
	else tmp << c;
	spc_flag= FALSE;
	ret_flag= FALSE;
      }
    }
  }
  else {
    tmp << s;
    if (N(s) != 0) {
      spc_flag= FALSE;
      ret_flag= FALSE;
    }
  }
}

void
tm_writer::br (int indent) {
  int i;
  flush ();
  tab += indent;
  for (i=N(buf)-1; i>=0; i--) {
    if (buf[i] == '\n') return;
    if (buf[i] != ' ') {
      cr ();
      spc_flag= TRUE;
      ret_flag= FALSE;
      return;
    }
  }
}

void
tm_writer::tag (string before, string s, string after) {
  write (before, FALSE);
  write (s);
  write (after, FALSE);
}

static bool
is_empty (tree t) {
  if (is_atomic (t)) return t == "";
  if (is_func (t, DOCUMENT, 0)) return TRUE;
  if (is_func (t, DOCUMENT, 1)) return is_empty (t[0]);
  if (is_func (t, CONCAT, 0)) return TRUE;
  if (is_func (t, CONCAT, 1)) return is_empty (t[0]);
  return FALSE;
}

void
tm_writer::apply (string func, array<tree> args) {
  int i, last, n=N(args);
  for (i=n-1; i>=0; i--)
    if (is_document (args[i]) || is_func (args[i], COLLECTION))
      break;
  last= i;

  if (last >= 0) {
    /*
    tag ("<\\", func, ">");
    for (i=0; i<n; i++) {
      if (is_empty (args[i])) br ();
      else {
	br (2);
	write (args[i]);
	br (-2);
      }
      if (i<(n-1)) tag ("<|", func, ">");
    }
    tag ("</", func, ">");
    */
    
    for (i=0; i<=n; i++) {
      bool flag=
	(i<n) && (is_document (args[i]) || is_func (args[i], COLLECTION));
      if (i==0) { write ("<\\", FALSE); write (func); }
      else if (i==last+1) {write ("</", FALSE); write (func); }
      else if (is_document (args[i-1]) || is_func (args[i-1], COLLECTION)) {
	write ("<|", FALSE); write (func); }
      if (i==n) {
	write (">", FALSE);
	break;
      }

      if (flag) {
	write (">", FALSE);
	br (2);
	write (args[i]);
	br (-2);	
      }
      else {
	write ("|", FALSE);
	write (args[i]);
      }
    }
  }
  else {
    write ("<", FALSE);
    write (func);
    for (i=0; i<n; i++) {
      write ("|", FALSE);
      write (args[i]);
    }
    write (">", FALSE);
  }
}

void
tm_writer::write (tree t) {
  if (is_atomic (t)) {
    write (t->label);
    return;
  }

  int i, n= N(t);
  switch (L(t)) {
  case DOCUMENT:
    spc_flag= TRUE;
    ret_flag= TRUE;
    for (i=0; i<n; i++) {
      write (t[i]);
      if (i<(n-1)) write_return ();
      else if (ret_flag) write ("\\;", FALSE);
    }
    break;
  case CONCAT:
    for (i=0; i<n; i++) write (t[i]);
    break;
  case EXPAND:
    if ((n>=1) && is_atomic (t[0])) {
      string s= t[0]->label;
      if (CONSTRUCTOR_CODE->contains (s));
      else if ((N(s)>0) && (!is_iso_alpha (s)));
      else {
	apply (s, A(t(1,n)));
	break;
      }
    }
    apply (CONSTRUCTOR_NAME [EXPAND], A(t));
    break;
  case COLLECTION:
    tag ("<\\", CONSTRUCTOR_NAME [COLLECTION], ">");
    if (n==0) br ();
    else {
      br (2);
      for (i=0; i<n; i++) {
	write (t[i]);
	if (i<(n-1)) br ();
      }
      br (-2);
    }
    tag ("</", CONSTRUCTOR_NAME [COLLECTION], ">");
    break;
  default:
    apply (CONSTRUCTOR_NAME [L(t)], A(t));
    break;
  }
}

/******************************************************************************
* Conversion of TeXmacs trees to TeXmacs strings
******************************************************************************/

string
tree_to_texmacs (tree t) {
  tm_writer tmw;
  tmw.write (t);
  tmw.flush ();
  return tmw.buf;
}

string
tree_to_texmacs_document (tree doc) {
  int i, n= arity (doc);
  for (i=0; i<n; i++)
    if ((is_func (doc[i], EXPAND, 2) || is_func (doc[i], APPLY, 2)) &&
	(doc[i][0] == "style"))
      {
	tree style= doc[i][1];
	if (is_func (style, TUPLE, 1)) style= style[0];
	doc[i][1]= style;
      }
  return tree_to_texmacs (doc) * "\n";
}

#endmodule // code_totm
