
/******************************************************************************
* MODULE     : fromtex.gen.cc
* DESCRIPTION: conversion of tex strings into texmacs trees
* 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_fromtex

tree upgrade_tex (tree t);

/******************************************************************************
* Transform parsed tex/latex trees into texmacs trees
******************************************************************************/

#define l2e parsed_latex_to_tree
#define t2e parsed_text_to_tree
#define m2e latex_modifier_to_tree
#define var_m2e var_latex_modifier_to_tree
tree l2e (tree);

tree
latex_symbol_to_tree (string s) {
  if (s == "") return "";
  if (s[0] == '\\') {
    if (command_type[s] == "command") {
      if (s == "\\ ") return " ";
      if (s == "\\-") return "";
      if (s == "\\/") return "";
      if (s == "\\i") return "\020";
      if (s == "\\j") return "\021";
      if (s == "\\oe") return "\367";
      if (s == "\\ae") return "\346";
      if (s == "\\ss") return "\377";
      if (s == "\\OE") return "\327";
      if (s == "\\AE") return "\306";
      if (s == "\\SS") return "\337";
      if (s == "\\\\") return tree (FORMAT, NEXT_LINE);
      if (s == "\\cr") return tree (FORMAT, NEXT_LINE);
      if (s == "\\nopagebreak")  return tree (FORMAT, NO_PAGE_BREAK_AFTER);
      if (s == "\\!")  return tree (HSPACE, "-0.25spc");
      if (s == "\\,")  return tree (HSPACE, "0.25spc");
      if (s == "\\:")  return tree (HSPACE, "0.5spc");
      if (s == "\\;")  return tree (HSPACE, "0.75spc");
      if (s == "\\*")  return "*";
      if (s == "\\quad")  return tree (HSPACE, "1fn");
      if (s == "\\par")  return tree (VSPACE_AFTER, "1fn");
      if (s == "\\smallskip")  return tree (VSPACE_AFTER, "0.5fn");
      if (s == "\\medskip")  return tree (VSPACE_AFTER, "1fn");
      if (s == "\\bigskip")  return tree (VSPACE_AFTER, "2fn");
    }

    if (command_type[s] == "modifier") {
      s= s(1,N(s));
      if (s == "rmfamily") return tree (SET, TEXT_FAMILY, "rm");
      if (s == "ttfamily") return tree (SET, TEXT_FAMILY, "tt");
      if (s == "sffamily") return tree (SET, TEXT_FAMILY, "sf");
      if (s == "mdseries") return tree (SET, TEXT_SERIES, "right");
      if (s == "bfseries") return tree (SET, TEXT_SERIES, "bold");
      if (s == "upshape")  return tree (SET, TEXT_SHAPE , "right");
      if (s == "itshape")  return tree (SET, TEXT_SHAPE , "italic");
      if (s == "slshape")  return tree (SET, TEXT_SHAPE , "slanted");
      if (s == "scshape")  return tree (SET, TEXT_SHAPE , "small-caps");
      if (s == "cal")      return tree (SET, MATH_FONT  , "cal");
      if (s == "frak")     return tree (SET, MATH_FONT  , "Euler");
      if (s == "Bbb")      return tree (SET, MATH_FONT  , "Bbb*");
      if (s == "displaystyle") return tree (SET, DISPLAY_STYLE, "true");
      
      if (s == "rm") return tree (SET, TEXT_FAMILY, "rm");
      if (s == "tt") return tree (SET, TEXT_FAMILY, "tt");
      if (s == "sf") return tree (SET, TEXT_FAMILY, "ss");
      if (s == "md") return tree (SET, TEXT_SERIES, "right");
      if (s == "bf") return tree (SET, TEXT_SERIES, "bold");
      if (s == "it") return tree (SET, TEXT_SHAPE, "italic");
      if (s == "sl") return tree (SET, TEXT_SHAPE, "slanted");
      if (s == "sc") return tree (SET, TEXT_SHAPE, "small-caps");
      if (s == "em") {
	if (command_type ["!em"] == "false") {
	  command_type ("!em")= "true";
	  return tree (SET, TEXT_SHAPE, "italic");
	}
	else {
	  command_type ("!em")= "false";
	  return tree (SET, TEXT_SHAPE, "right");
	}
      }

      if (s == "tiny") return tree (SET, FONT_SIZE, "0.59");
      if (s == "scriptsize") return tree (SET, FONT_SIZE, "0.71");
      if (s == "footnotesize") return tree (SET, FONT_SIZE, "0.71");
      if (s == "small") return tree (SET, FONT_SIZE, "0.84");
      if (s == "normalsize") return tree (SET, FONT_SIZE, "1");
      if (s == "large") return tree (SET, FONT_SIZE, "1.19");
      if (s == "Large") return tree (SET, FONT_SIZE, "1.41");
      if (s == "LARGE") return tree (SET, FONT_SIZE, "1.41");
      if (s == "huge") return tree (SET, FONT_SIZE, "1.68");
      if (s == "Huge") return tree (SET, FONT_SIZE, "2");
      
      if (s == "black") return tree (SET, COLOR, "black");
      if (s == "white") return tree (SET, COLOR, "white");
      if (s == "grey") return tree (SET, COLOR, "grey");
      if (s == "red") return tree (SET, COLOR, "red");
      if (s == "blue") return tree (SET, COLOR, "blue");
      if (s == "yellow") return tree (SET, COLOR, "yellow");
      if (s == "green") return tree (SET, COLOR, "green");
      if (s == "orange") return tree (SET, COLOR, "orange");
      if (s == "magenta") return tree (SET, COLOR, "magenta");
      if (s == "brown") return tree (SET, COLOR, "brown");
      if (s == "pink") return tree (SET, COLOR, "pink");

      cerr << "The symbol was " << s << "\n";
      fatal_error ("unexpected situation", "latex_symbol_to_tree");
    }
    if (command_type[s] == "operator")
      return s(1,N(s));
    if (command_type[s] == "control") return s(1,N(s));
    if (command_type[s] == "symbol")  return "<" * s(1,N(s)) * ">";
    if (command_type[s] == "big-symbol") {
      if (s(0,4)=="\\big") return tree (BIG, s(4,N(s)));
      else return tree (BIG, s(1,N(s)));
    }

    if ((N(s) > 7) && (s(0,7) == "\\begin-"))
      return tree (BEGIN, s(7,N(s)));
    if ((N(s) > 5) && (s(0,5) == "\\end-"))
      return tree (END, s(5,N(s)));

    return tree (APPLY, s(1,N(s)));
  }
  if ((N(s) == 2) && (s[0] == '#') && (s[1] >= '0') && (s[1] <= '9'))
    return tree (APPLY, s(1,2));
  if (s == "&") return tree (FORMAT, LINE_SEP);
  return copy (s);
}

tree
t2e (tree t, bool flag= TRUE) {
  string old_mode= command_type ["!mode"];
  command_type ("!mode") = "text";
  tree r= l2e (t);
  command_type ("!mode") = old_mode;
  while (flag && (arity(r)>0)) r= r[0];
  return r;
}

bool
test_alpha_on_end (tree t) {
  if (is_atomic (t) && (N(t->label) >= 1))
    return is_alpha (t->label[N(t->label)-1]);
  if (is_concat (t) && (N(t)>=1))
    return test_alpha_on_end (t[N(t)-1]);
  return FALSE;
}

tree
latex_concat_to_tree (tree t, bool& new_flag) {
  int i, n=N(t);
  tree r (CONCAT), env (CONCAT);

  command_type ->extend ();
  command_arity->extend ();
  command_def  ->extend ();

  for (i=0; i<n; i++) {
    if (is_tuple (t[i]) && (N(t[i])==1)) {
      string s= t[i][0]->label;
      if (command_type[s] == "math-environment") {
	if (s(0,4)=="\\end") command_type ("!mode") = "text";
	else command_type ("!mode") = "math";
      }
      if (s == "\\begin-verbatim") command_type ("!verbatim") = "true";
      else if (s == "\\end-verbatim") command_type ("!verbatim") = "false";
    }
    if (is_atomic (t[i]) && (command_type["!verbatim"] == "true")) {
      r << tm_encode (t[i]->label);
      continue;
    }
    if (is_tuple (t[i], "\\text", 1) || is_tuple (t[i], "\\mbox", 1)) {
      r << tree (SET, MODE, "text");
      r << t2e (t[i][1], FALSE);
      r << tree (RESET, MODE);
      continue;
    }

    bool operator_flag=
      is_tuple (t[i]) && (N(t[i])==1) &&
      (command_type[t[i][0]->label]=="operator");
    bool cc_flag= is_concat (t[i]);
    tree u= (cc_flag? latex_concat_to_tree (t[i], new_flag): l2e (t[i]));
    if (is_atomic (u)) {
      if (u == " ") {
	if (command_type ["!mode"] == "math") {
	  if ((i==0) || (!is_tuple (t[i-1])) || (N(t[i-1])!=1) ||
	      (command_type[t[i-1][0]->label] != "operator"))
	    continue;
	}
	else {
	  if ((t[i] != tree (TUPLE, "\\ ")) && (i>0) && (is_tuple (t[i-1]))) {
	    string s= t[i-1][0]->label;
	    if ((s[0]=='\\') && (s!="\\end-math") && (s!="\\end-displaymath"))
	      if ((arity(t[i-1])==1) || (command_type[s]=="command"))
		continue;
	  }
	}
      }

      string s= u->label;
      bool old_flag= new_flag;
      if (!cc_flag) new_flag= ((N(s)==1) && is_alpha(s));
      if ((command_type ["!mode"] == "math") &&
	  (!cc_flag) && old_flag && (new_flag || operator_flag)) s= "*" * s;
      if ((N(r)>0) && is_atomic (r[N(r)-1])) r[N(r)-1]->label << s;
      else r << s;
    }
    else {
      if (is_func (u, SET, 2)) {
	env << u;
	if (((i+1)<n) && (t[i+1]==tree(" "))) i++;
      }
      r << u;
      if (!cc_flag) new_flag= FALSE;
    }
  }
  
  for (i=N(env)-1; i>=0; i--)
    r << tree (RESET, copy (env[i][0]));

  command_type ->shorten ();
  command_arity->shorten ();
  command_def  ->shorten ();

  if (N(r)==0) return "";
  if (N(r)==1) return r[0];
  return r;
}

tree
m2e (tree t, string var, string val) {
  return tree (CONCAT,
	       tree (SET, copy (var), copy (val)),
	       l2e (t[1]),
	       tree (RESET, copy (var)));
}

tree
var_m2e (tree t, string var, string val) {
  return tree (CONCAT,
	       tree (SET, copy (var), copy (val)),
	       t2e (t[1], FALSE),
	       tree (RESET, copy (var)));
}

tree
latex_command_to_tree (tree t) {
  if (is_tuple (t, "\\def", 2) && is_tuple (t[1])) {
    string var= t[1][0]->label;
    if ((N(var)>0) && (var[0]=='\\')) var= var (1, N(var));
    return tree (ASSIGN, var, tree (FUNCTION, l2e (t[2])));
  }
  if (is_tuple (t, "\\def*", 3) && is_tuple (t[1])) {
    string var= t[1][0]->label;
    if ((N(var)>0) && (var[0]=='\\')) var= var (1, N(var));
    int i, arity= as_int (l2e(t[2]));
    tree f (FUNCTION);
    for (i=1; i<=arity; i++) f << as_string (i);
    f << l2e (t[3]);
    return tree (ASSIGN, var, f);
  }

  if (is_tuple (t, "\\newenvironment", 3)) {
    string var= l2e(t[1])->label;
    return tree (ASSIGN, var, tree (ENVIRONMENT, l2e (t[2]), l2e (t[3])));
  }
  if (is_tuple (t, "\\newenvironment*", 4)) {
    string var= l2e(t[1])->label;
    int i, arity= as_int (l2e(t[2])->label);
    tree e (ENVIRONMENT);
    for (i=1; i<=arity; i++) e << as_string (i);
    e << l2e (t[3]);
    e << l2e (t[4]);
    return tree (ASSIGN, var, e);
  }

  if (is_tuple (t, "\\textrm", 1)) return m2e (t, TEXT_FAMILY, "rm");
  if (is_tuple (t, "\\texttt", 1)) return m2e (t, TEXT_FAMILY, "tt");
  if (is_tuple (t, "\\textsf", 1)) return m2e (t, TEXT_FAMILY, "ss");
  if (is_tuple (t, "\\textmd", 1)) return m2e (t, TEXT_SERIES, "medium");
  if (is_tuple (t, "\\textbf", 1)) return m2e (t, TEXT_SERIES, "bold");
  if (is_tuple (t, "\\textup", 1)) return m2e (t, TEXT_SHAPE, "right");
  if (is_tuple (t, "\\textit", 1)) return m2e (t, TEXT_SHAPE, "italic");
  if (is_tuple (t, "\\textsl", 1)) return m2e (t, TEXT_SHAPE, "slanted");
  if (is_tuple (t, "\\textsc", 1)) return m2e (t, TEXT_SHAPE, "small-caps");
  if (is_tuple (t, "\\emph", 1))   return m2e (t, TEXT_SHAPE, "italic");
  if (is_tuple (t, "\\mathnormal", 1)) return m2e (t, MATH_FAMILY, "mr");
  if (is_tuple (t, "\\mathrm", 1)) return var_m2e (t, MATH_FAMILY, "rm");
  if (is_tuple (t, "\\mathtt", 1)) return var_m2e (t, MATH_FAMILY, "tt");
  if (is_tuple (t, "\\mathsf", 1)) return var_m2e (t, MATH_FAMILY, "ss");
  if (is_tuple (t, "\\mathbf", 1)) return var_m2e (t, MATH_FAMILY, "bf");
  if (is_tuple (t, "\\mathit", 1)) return var_m2e (t, MATH_FAMILY, "it");
  if (is_tuple (t, "\\mathsl", 1)) return var_m2e (t, MATH_FAMILY, "sl");
  if (is_tuple (t, "\\mathup", 1)) return var_m2e (t, MATH_FAMILY, "up");
  if (is_tuple (t, "\\mathcal", 1))  return m2e (t, MATH_FONT, "cal");
  if (is_tuple (t, "\\mathfrak", 1)) return m2e (t, MATH_FONT, "Euler");
  if (is_tuple (t, "\\mathbb", 1))   return m2e (t, MATH_FONT, "Bbb*");

  if (is_tuple (t, "\\prime", 1))  return tree (RIGHT_PRIME, as_string (t[1]));
  if (is_tuple (t, "\\frac", 2))  return tree (FRAC, l2e (t[1]), l2e (t[2]));
  if (is_tuple (t, "\\sqrt", 1))  return tree (SQRT, l2e (t[1]));
  if (is_tuple (t, "\\sqrt*", 2)) return tree (SQRT, l2e (t[2]), l2e (t[1]));
  if (is_tuple (t, "\\<sub>", 1)) return tree (RIGHT_SUB, l2e (t[1]));
  if (is_tuple (t, "\\<sup>", 1)) return tree (RIGHT_SUP, l2e (t[1]));
  if (is_tuple (t, "\\not", 1))   return tree (NEG, l2e (t[1]));
  if (is_tuple (t, "\\hat", 1))  return tree (WIDE, l2e (t[1]), "^");
  if (is_tuple (t, "\\tilde", 1))  return tree (WIDE, l2e (t[1]), "~");
  if (is_tuple (t, "\\check", 1))  return tree (WIDE, l2e (t[1]), "<check>");
  if (is_tuple (t, "\\grave", 1))  return tree (WIDE, l2e (t[1]), "<grave>");
  if (is_tuple (t, "\\acute", 1))  return tree (WIDE, l2e (t[1]), "<acute>");
  if (is_tuple (t, "\\vec", 1))  return tree (WIDE, l2e (t[1]), "<vect>");
  if (is_tuple (t, "\\hspace", 1)) return tree (HSPACE, t2e (t[1]));
  if (is_tuple (t, "\\vspace", 1)) return tree (VSPACE_AFTER, t2e (t[1]));
  if (is_tuple (t, "\\label", 1)) return tree (LABEL, t2e (t[1]));
  if (is_tuple (t, "\\ref", 1))   return tree (REFERENCE, t2e (t[1]));
  if (is_tuple (t, "\\mathop", 1)) return t2e (t[1]);

  if (is_tuple (t, "\\left", 1)) {
    string s= t[1]->label;
    if ((N(s)>1) && (s[0]=='\\')) s=s(1,N(s));
    return tree (LEFT, s);
  }
  if (is_tuple (t, "\\right", 1)) {
    string s= t[1]->label;
    if ((N(s)>1) && (s[0]=='\\')) s=s(1,N(s));
    return tree (RIGHT, s);
  }

  if (is_tuple (t, "\\cite", 1) || is_tuple (t, "\\nocite", 1)) {
    string s= t2e(t[1])->label;
    tree u (CONCAT);
    int i, last;
    for (last=0, i=0; i<N(s); i++) {
      while ((i<N(s)) && (s[i]!=',')) i++;
      if (is_tuple (t, "\\cite", 1) && (last>0)) u << ", ";
      u << tree (APPLY, t[0]->label (1, N(t[0]->label)), s(last,i));
      if (i<N(s)) i++;
      while ((i<N(s)) && (s[i]==' ')) i++;
      last= i;
    }
    if (N(u)==0) return "";
    if (N(u)==1) return u[0];
    return u;
  }

  int i;
  string s= t[0]->label;
  tree r (APPLY, s(1,N(s)));
  if ((N(s)>7) && (s(0,7)=="\\begin-")) r= tree (BEGIN, s(7,N(s)));
  for (i=1; i<N(t); i++) r << l2e(t[i]);
  return r;
}

tree
l2e (tree t) {
  if (is_atomic (t)) return latex_symbol_to_tree (t->label);
  if (is_func (t, CONCAT)) {
    bool new_flag= FALSE;
    return latex_concat_to_tree (t, new_flag);
  }
  if (is_tuple (t) && (N(t)==1)) return latex_symbol_to_tree (t[0]->label);
  return latex_command_to_tree (t);
}

#undef var_m2e
#undef m2e
#undef t2e
#undef l2e

/******************************************************************************
* Final modifications to the converted tree
******************************************************************************/

static tree
finalize_returns (tree t) {
  if (is_atomic (t)) return t;
  int  i, n= N(t);
  tree u (t, n);
  for (i=0; i<n; i++) u[i]= finalize_returns (t[i]);
  if (is_func (u, CONCAT)) {
    tree r (CONCAT);
    for (i=0; i<n; i++) {
      if (is_func (u[i], CONCAT)) r << A(u[i]);
      else if (is_compound (u[i])) r << u[i];
      else {
	int j= 0;
	string s= u[i]->label;
	while (j<N(s)) {
	  int start= j;
	    while ((j<N(s)) && (s[j]!='\n')) j++;
	    if (j>start) r << s(start,j);
	    if (j<N(s)) { r << tree (FORMAT, NEW_LINE); j++; }
	}
      }
    }
    if (N(r)==0) return "";
    if (N(r)==1) return r[0];
    return r;
  }
  else return u;
}

static void
parse_pmatrix (tree& r, tree t, int& i, bool bracket) {
  if (bracket) r << tree (LEFT, "(");

  int rows=0, cols=0;
  tree V (CONCAT);
  tree L (CONCAT);
  tree E (CONCAT);
  for (i++; i<N(t); i++) {
    tree v= t[i];
    if (v == tree (FORMAT, LINE_SEP)) {
      L << simplify_concat (E);
      E= tree (CONCAT);
      continue;
    }
    else if (v == tree (FORMAT, NEXT_LINE)) {
      L << simplify_concat (E);
      V << L;
      cols= max (cols, N(L));
      L= tree (CONCAT);
      E= tree (CONCAT);
      continue;
    }
    else if (is_func (v, BEGIN) && (v[0] == "array")) {
      parse_pmatrix (E, t, i, FALSE);
      if (i<N(t)) continue;
      break;
    }
    else if (v == tree (BEGIN, "matrix")) {
      parse_pmatrix (E, t, i, FALSE);
      if (i<N(t)) continue;
      break;
    }
    else if (v == tree (BEGIN, "pmatrix")) {
      parse_pmatrix (E, t, i, TRUE);
      if (i<N(t)) continue;
      break;
    }
    else if (v == tree (END, "array")) break;
    else if (v == tree (END, "matrix")) break;
    else if (v == tree (END, "pmatrix")) break;
    else E << v;
  }
  if ((N(L)>0) || (N(E)>0)) {
    L << simplify_concat (E);
    V << L;
  }
  cols= max (cols, N(L));
  rows= N(V);

  int x, y;
  tree M (OLD_MATRIX);
  for (y=0; y<rows; y++)
    for (x=0; x<cols; x++)
      if (x<N(V[y])) M << V[y][x];
      else M << "";
  M << as_string (cols) << as_string (rows);
  r << M;
  if (bracket) r << tree (RIGHT, ")");
}

static tree
finalize_pmatrix (tree t) {
  if (is_atomic (t)) return t;
  int  i, n= N(t);
  tree u (t, n);
  for (i=0; i<n; i++) u[i]= finalize_pmatrix (t[i]);
  if (is_func (u, CONCAT)) {
    tree r (CONCAT);
    for (i=0; i<n; i++)
      if (is_func (u[i], BEGIN)) {
	if (u[i][0] == "array") parse_pmatrix (r, u, i, FALSE);
	else if (u[i][0] == "matrix") parse_pmatrix (r, u, i, FALSE);
	else if (u[i][0] == "pmatrix") parse_pmatrix (r, u, i, TRUE);
	else r << u[i];
      }
      else r << u[i];
    return r;
  }
  else return u;
}

static void
remove_space (tree& t) {
  if (arity (t) == 0) return;
  if (is_compound (t[N(t)-1])) return;
  string s= t[N(t)-1]->label;
  if ((N(s)>0) && (s[N(s)-1]==' '))
    t[N(t)-1]= s(0,N(s)-1);
}

static void
insert_return (tree& t) {
  remove_space (t);
  if ((arity(t)>0) && (t[N(t)-1]==tree (FORMAT, NEW_LINE))) return;
  t << tree (FORMAT, NEW_LINE);
}

static bool
space_eater (tree t) {
  return is_func (t, FORMAT, 1) && (t[0]->label != NEW_LINE);
}

static bool
admissible_env (tree t) {
  string s= t[0]->label;
  if (command_type["\\begin-" * s] == "list") return TRUE;
  if (command_type["\\begin-" * s] == "environment") return TRUE;
  if (command_type["\\begin-" * s] == "math-environment") return TRUE;
  return FALSE;
}

static string
translate_list (string s) {
  if (s == "itemizeminus") return "itemize-minus";
  if (s == "itemizedot") return "itemize-dot";
  if (s == "itemizearrow") return "itemize-arrow";
  if (s == "enumeratenumeric") return "enumerate-numeric";
  if (s == "enumerateroman") return "enumerate-roman";
  if (s == "enumerateromancap") return "enumerate-romancap";
  if (s == "enumeratealpha") return "enumerate-alpha";
  if (s == "enumeratealphacap") return "enumerate-alphacap";
  return s;
}

static tree
finalize_layout (tree t) {
  if (is_atomic (t)) return t;
  int  i, n= N(t);
  tree u (t, n);
  for (i=0; i<n; i++) u[i]= finalize_layout (t[i]);
  if (is_func (u, CONCAT)) {
    bool spc_flag =FALSE;
    bool item_flag=FALSE;
    tree r (CONCAT);
    for (i=0; i<n; i++) {
      tree v= u[i];

      if (space_eater (v)) {
	remove_space (r);
	r << v;
	spc_flag = TRUE;
	item_flag= FALSE;
	continue;
      }

      if (is_func (v, BEGIN, 1) && admissible_env (v)) {
	if (v == tree (BEGIN, "verbatim")) {
	  r << v; i++;
	  if ((i<n) && (t[i] == tree (FORMAT, NEW_LINE))) {
	    remove_space (r);
	    r << t[i]; i++;
	  }
	  while ((i<n) && (t[i] != tree (END, "verbatim"))) {
	    if ((t[i] == tree (FORMAT, NEW_LINE)) &&
		(((i+1) == n) || (t[i+1] != tree (END, "verbatim"))))
	      r << tree (FORMAT, NEXT_LINE);
	    else r << t[i];
	    i++;
	  }
	  if (i<n) r << t[i];
	  spc_flag = (t[i-1] == tree (FORMAT, NEW_LINE));
	  item_flag= FALSE;
	  continue;
	}
	
	if (v == tree (BEGIN, "displaymath"))
	  v= tree (BEGIN, "equation*");
	if (v == tree (BEGIN, "math")) {
	  r << tree (SET, MODE, "math");
	  spc_flag = item_flag= FALSE;
	  continue;
	}
	
	insert_return (r);
	r << tree (BEGIN, translate_list (v[0]->label));
	spc_flag = TRUE;
	item_flag= (command_type ["\\begin-" * v[0]->label] == "list");
	continue;
      }

      if (is_func (v, END, 1) && admissible_env (v)) {
	if (v == tree (END, "displaymath"))
	  v= tree (END, "equation*");
	if (v == tree (END, "math")) {
	  r << tree (RESET, MODE);
	  spc_flag = item_flag= FALSE;
	  continue;
	}
	
	remove_space (r);
	r << tree (END, translate_list (v[0]->label));
	if (((i+1) == N(t)) || (t[i+1] != tree (FORMAT, NEW_LINE)))
	  insert_return (r);
	spc_flag = TRUE;
	item_flag= FALSE;
	continue;
      }
      
      if ((v == tree (APPLY, "item")) ||
	  (is_func (v, APPLY, 2) && (v[0]->label == "item*"))) {
	if (!item_flag) insert_return (r);
	r << v;
	spc_flag = TRUE;
	item_flag= FALSE;
	continue;
      }

      if ((is_atomic (v)) && spc_flag &&
	  (N(v->label)>0) && (v->label[0]==' '))
	{
	  if (N(v->label)==1) continue;
	  r << v->label (1, N(v->label));
	}
      else r << v;
      spc_flag = FALSE;
      item_flag= FALSE;
    }
    return r;
  }
  else return u;
}

static tree
finalize_document (tree t) {
  if (is_atomic (t)) t= tree (CONCAT, t);
  t= finalize_returns (t);
  t= finalize_pmatrix (t);
  t= finalize_layout (t);
  if (!is_func (t, CONCAT)) return tree (DOCUMENT, t);

  int i;
  tree r (DOCUMENT);
  for (i=0; i<N(t); i++) {
    int start= i;
    while ((i<N(t)) && (t[i]!=tree (FORMAT, NEW_LINE))) i++;
    if (i==start) r << "";
    else if (i==(start+1)) r << t[start];
    else r << t(start,i);
    if (i==(N(t)-1)) r << "";
  }
  return r;
}

bool
is_preamble_command (tree t, tree& doc, string& style) {
  (void) doc;
  if (is_func (t, APPLY, 2)) {
    if (t[0] == "usepackage") return TRUE;
    if ((t[0] == "documentstyle") ||
	(t[0] == "documentclass")) {
      style= t[1]->label;
      return TRUE;
    }
  }
  if (is_func (t, APPLY, 3)) {
    if (t[0] == "usepackage*") return TRUE;
    if ((t[0] == "documentstyle*") ||
	(t[0] == "documentclass*")) {
      style= t[2]->label;
      return TRUE;
    }
  }
  if (is_func (t, BEGIN, 1) && (t[0] == "document")) return TRUE;
  if (is_func (t, END, 1) && (t[0] == "document")) return TRUE;
  return FALSE;
}

bool
is_bibliography_command (tree t, tree& doc, string& bib_style) {
  if (is_func (t, APPLY, 2)) {
    if (t[0] == "bibliographystyle") {
      bib_style= t[1]->label;
      return TRUE;
    }
    if (t[0] == "bibliography") {
      tree begin (BEGIN, "bibliography");
      tree end (END, "bibliography");
      begin << "bib" << bib_style << t[1]->label;
      doc << begin << end;
      return TRUE;
    }
  }
  return FALSE;
}

tree
finalize_preamble (tree t, string& style) {
  int i, j;
  tree u (DOCUMENT);
  style= "letter";
  string bib_style= "plain";
  for (i=0; i<N(t); i++) {
    if (is_concat (t[i])) {
      tree v (CONCAT);
      for (j=0; j<N(t[i]); j++)
	if (is_preamble_command (t[i][j], v, style));
	else if (is_bibliography_command (t[i][j], v, bib_style));
	else v << t[i][j];
      if (N(v)==1) u << v[0];
      if (N(v)>=2) u << v;
    }
    else if (is_preamble_command (t[i], u, style));
    else if (is_bibliography_command (t[i], u, bib_style));
    else u << t[i];
  }
  return u;
}

/******************************************************************************
* Interface
******************************************************************************/

tree
latex_to_tree (string s, string mode) {
  // cout << "\n\ns = " << s  << "\n\n";
  tree t1= parse_latex (s, mode);
  // cout << "\n\nt1= " << t1 << "\n\n";
  tree t2= parsed_latex_to_tree (t1);
  // cout << "\n\nt2= " << t2 << "\n\n";
  tree t3= finalize_document (t2);
  // cout << "\n\nt3= " << t3 << "\n\n";
  tree t4= upgrade_tex (t3);
  // cout << "\n\nt4= " << t4 << "\n\n";
  if (N(t4)==0) return "";
  if (N(t4)==1) return t4[0];
  return t4;
}

tree
latex_document_to_tree (string s, string& style) {
  // cout << "\n\ns = " << s  << "\n\n";
  tree t1= parse_latex (s, "text");
  // cout << "\n\nt1= " << t1 << "\n\n";
  tree t2= parsed_latex_to_tree (t1);
  // cout << "\n\nt2= " << t2 << "\n\n";
  tree t3= finalize_document (t2);
  // cout << "\n\nt3= " << t3 << "\n\n";
  tree t4= finalize_preamble (t3, style);
  // cout << "\n\nt4= " << t4 << "\n\n";
  tree t5= upgrade_tex (t4);
  // cout << "\n\nt5= " << t5 << "\n\n";
  return t5;
}

#endmodule // code_fromtex
