/* ----------------------------------------------------------------------------- 
 * s-exp.cxx
 *
 *     A parse tree represented as Lisp s-expressions.
 * 
 * Author(s) : Matthias Koeppe (mkoeppe@mail.math.uni-magdeburg.de)
 *
 * Copyright (C) 2002.  The University of Chicago
 * See the file LICENSE for information on usage and redistribution.	
 * ----------------------------------------------------------------------------- */

/* Derived from xml.cxx 1.1.2.2 */

char cvsroot_s_exp_cxx[] = "$Header: /cvsroot/swig/SWIG/Source/Modules/s-exp.cxx,v 1.16 2005/09/08 22:31:21 wsfulton Exp $";
static const char *usage = "\
S-Exp Options (available with -sexp)\n\
     -typemaplang <lang> - Typemap language\n\n";

#include "swigmod.h"
#include "dohint.h"

//static Node *view_top = 0;
static File *out = 0;
      
class Sexp : public Language {
public:
  int indent_level;
  Sexp() : indent_level( 0 ) {}
  virtual ~Sexp() {}
  virtual void main(int argc, char *argv[]) {
    SWIG_typemap_lang("sexp");
    for( int iX = 0; iX < argc; iX++ )
      {
	if( strcmp( argv[iX], "-typemaplang" ) == 0 )
	  {
	    Swig_mark_arg (iX);
	    iX++;
	    SWIG_typemap_lang(argv[iX]);
	    Swig_mark_arg (iX);
	    continue;
	  }
	if( strcmp( argv[iX], "-help" ) == 0 )
	  {
	    fputs( usage, stdout );
	  }
      }

      // Add a symbol to the parser for conditional compilation
      Preprocessor_define("SWIGSEXP 1",0);
  }

  DOHHash *print_circle_hash;
  int print_circle_count;
  int hanging_parens;
  bool need_whitespace;
  bool need_newline;

  /* Top of the parse tree */
  virtual int top(Node *n) 
  {
    if( out == 0 )
      {
	String *outfile = Getattr(n,"outfile");
	Replaceall(outfile,"_wrap.cxx", ".lisp");
	Replaceall(outfile,"_wrap.c", ".lisp");
	out = NewFile(outfile,"w");
	if (!out) 
	  {
	    FileErrorDisplay(outfile);
	    SWIG_exit(EXIT_FAILURE);
	  }
      }
    String *f_sink = NewString("");
    Swig_register_filebyname("header",f_sink);
    Swig_register_filebyname("wrapper",f_sink);
    Swig_register_filebyname("runtime",f_sink);
    Swig_register_filebyname("init",f_sink);
    
    Language::top(n);
    Printf( out, ";;; Lisp parse tree produced by SWIG\n" );
    print_circle_hash = DohNewHash();
    print_circle_count = 0;
    hanging_parens = 0;
    need_whitespace = 0;
    need_newline = 0;
    Sexp_print_node(n);
    flush_parens();
    return SWIG_OK;
  }

  void print_indent() 
  {
    int i;
    for (i = 0; i < indent_level; i++) 
      {
	Printf(out, " ");
      }
  }

  void open_paren(const String *oper)
  {
    flush_parens();
    Printf(out, "(");
    if (oper) Printf(out, "%s ", oper);
    indent_level += 2;
  }

  void close_paren(bool neednewline = false)
  {
    hanging_parens++;
    if (neednewline)
      print_lazy_whitespace();
    indent_level -= 2;
  }

  void flush_parens()
  {
    int i;
    if (hanging_parens) {
      for (i = 0; i<hanging_parens; i++)
	Printf(out, ")");
      hanging_parens = 0;
      need_newline = true;
      need_whitespace = true;
    }
    if (need_newline) {
      Printf(out, "\n");
      print_indent();
      need_newline = false;
      need_whitespace = false;
    }
    else if (need_whitespace) {
      Printf(out, " ");
      need_whitespace = false;
    }
  }

  void print_lazy_whitespace()
  {
    need_whitespace = 1;
  }

  void print_lazy_newline()
  {
    need_newline = 1;
  }

  bool internal_key_p(DOH *key)
  {
    return ((Cmp(key,"nodeType") == 0) 
	    || (Cmp(key,"firstChild") == 0) 
	    || (Cmp(key,"lastChild") == 0) 
	    || (Cmp(key,"parentNode") == 0) 
	    || (Cmp(key,"nextSibling") == 0) 
	    || (Cmp(key,"previousSibling") == 0)
	    || (Cmp(key,"csym:nextSibling") == 0)
	    || (Cmp(key,"csym:previousSibling") == 0)
	    || (Cmp(key,"typepass:visit") == 0)
	    || (Cmp(key,"allocate:visit") == 0)
	    || (*(Char(key)) == '$'));
  }

  bool boolean_key_p(DOH *key)
  {
    return ((Cmp(key,"allocate:default_constructor") == 0) 
	    || (Cmp(key,"allocate:default_destructor") == 0) 
	    || (Cmp(key,"allows_typedef") == 0) 
	    || (Cmp(key,"feature:immutable") == 0));
  }

  bool list_key_p(DOH *key)
  {
    return ((Cmp(key, "parms") == 0)
	    || (Cmp(key, "baselist") == 0));
  }

  bool plist_key_p(DOH *key)
    // true if KEY is the name of data that is a mapping from keys to
    // values, which should be printed as a plist.
  {
    return ((Cmp(key, "typescope") == 0));
  }

  bool maybe_plist_key_p(DOH *key)
  {
    return (Strncmp(key, "tmap:", 5) == 0);
  }
  
  bool print_circle(DOH *obj, bool list_p)
    // We have a complex object, which might be referenced several
    // times, or even recursively.  Use Lisp's reader notation for
    // circular structures (#n#, #n=).
    //
    // An object can be printed in list-mode or object-mode; LIST_P toggles.
    // return TRUE if OBJ still needs to be printed
  {
    flush_parens();
    // Following is a silly hack.  It works around the limitation of
    // DOH's hash tables that only work with string keys!
    char address[32];
    sprintf(address, "%p%c", obj, list_p ? 'L' : 'O');
    DOH *placeholder = Getattr(print_circle_hash, address);
    if (placeholder) {
      Printv(out, placeholder, NIL);
      return false;
    }
    else {
      String *placeholder = NewStringf("#%d#", ++print_circle_count);
      Setattr(print_circle_hash, address, placeholder);
      Printf(out, "#%d=", print_circle_count);
      return true;
    }
  }  

  void Sexp_print_value_of_key(DOH *value, DOH *key)
  {
    if ((Cmp(key, "parms") == 0) || (Cmp(key, "wrap:parms") == 0)
	|| (Cmp(key, "kwargs") == 0) || (Cmp(key, "pattern") == 0))
      Sexp_print_parms(value);
    else if (plist_key_p(key))
      Sexp_print_plist(value);
    else if (maybe_plist_key_p(key)) {
      if (DohIsMapping(value))
	Sexp_print_plist(value);
      else
	Sexp_print_doh(value);
    }
    else if (list_key_p(key))
      Sexp_print_list(value);
    else if (boolean_key_p(key))
      Sexp_print_boolean(value);
    else
      Sexp_print_doh(value);
  }
  
  void Sexp_print_boolean(DOH *obj)
  {
    flush_parens();
    /* See DOH/Doh/base.c, DohGetInt() */
    if (DohIsString(obj)) {
      if (atoi(Char(obj)) != 0)
	Printf(out, "t");
      else Printf(out, "nil");
    }
    else Printf(out, "nil");
  }
  
  void Sexp_print_list(DOH *obj)
  {
    if (print_circle(obj, true)) {
      open_paren(NIL);
      for (; obj; obj = nextSibling(obj)) {
	Sexp_print_doh(obj);
	print_lazy_whitespace();
      }
      close_paren(true);
    }
  }

  void Sexp_print_parms(DOH *obj)
    // print it as a list of plists
  {
    if (print_circle(obj, true)) {
      open_paren(NIL);
      for (; obj; obj = nextSibling(obj)) {
	if (DohIsMapping(obj)) {
	  Iterator k;
	  open_paren(NIL);
	  for (k = First(obj); k.key; k = Next(k)) {
	    if (!internal_key_p(k.key)) {
	      DOH *value = Getattr(obj, k.key);
	      Sexp_print_as_keyword(k.key);
	      Sexp_print_value_of_key(value, k.key);
	      print_lazy_whitespace();
	    }
	  }	
	  close_paren(true);
	}
	else Sexp_print_doh(obj);
	print_lazy_whitespace();
      }
      close_paren(true);
    }
  }

  void Sexp_print_doh(DOH *obj)
  {
    flush_parens();
    if (DohIsString(obj)) {
      String *o = Str(obj);
      Replaceall( o, "\\", "\\\\" );
      Replaceall( o, "\"", "\\\"" );
      Printf(out,"\"%s\"", o);
      Delete(o);
    }
    else {
      if (print_circle(obj, false)) {
	// Dispatch type
	if (nodeType(obj)) {
	  Sexp_print_node(obj);
	}
	
	else if (DohIsMapping(obj)) {
	  Iterator k;
	  open_paren(NIL);
	  for (k = First(obj); k.key; k = Next(k)) {
	    if (!internal_key_p(k.key)) {
	      DOH *value = Getattr(obj, k.key);
	      flush_parens();
	      open_paren(NIL);
	      Sexp_print_doh(k.key);
	      Printf(out, " . ");
	      Sexp_print_value_of_key(value, k.key);
	      close_paren();
	    }
	  }
	  close_paren();
	}
	else if (strcmp(ObjType(obj)->objname, "List") == 0) {
	  int i;
	  open_paren(NIL);
	  for (i = 0; i<Len(obj); i++) {
	    DOH *item = Getitem(obj, i);
	    Sexp_print_doh(item);
	  }
	  close_paren();
	}
	else {
	  // What is it?
	  Printf(out,"#<DOH %s %x>", ObjType(obj)->objname, obj);
	}
      }
    }
  }

  void Sexp_print_as_keyword(const DOH *k)
  {
    /* Print key, replacing ":" with "-" because : is CL's package prefix */
    flush_parens();
    String *key = NewString(k);
    Replaceall(key, ":", "-");
    Replaceall(key, "_", "-");
    Printf(out,":%s ", key);
    Delete(key);
  }

  void Sexp_print_plist_noparens(DOH *obj)
  {
    /* attributes map names to objects */
    Iterator k;
    bool first;
    for (k = First(obj), first = true; k.key; k = Next(k), first=false) {
      if (!internal_key_p(k.key)) {
	DOH *value = Getattr(obj, k.key);
	flush_parens();
	if (!first) {
	  Printf(out, " ");
	}
	Sexp_print_as_keyword(k.key);
	/* Print value */
	Sexp_print_value_of_key(value, k.key);
      }
    }
  }
  
  void Sexp_print_plist(DOH *obj)
  {
    flush_parens();
    if (print_circle(obj, true)) {
      open_paren(NIL);
      Sexp_print_plist_noparens(obj);
      close_paren();
    }
  }

  void Sexp_print_attributes(Node * obj)
  {
    Sexp_print_plist_noparens(obj);
  }
  
  void Sexp_print_node(Node *obj) 
  {
    Node   *cobj;
    open_paren(nodeType(obj));
    /* A node has an attribute list... */
    Sexp_print_attributes(obj);
    /* ... and child nodes. */
    cobj = firstChild(obj);
    if (cobj) {
      print_lazy_newline();
      flush_parens();
      Sexp_print_as_keyword("children");
      open_paren(NIL);
      for (; cobj; cobj = nextSibling(cobj)) {
	Sexp_print_node(cobj);
      }
      close_paren();
    }
    close_paren();
  }


  virtual int functionWrapper(Node *n)
  {
    ParmList *l = Getattr(n,"parms");
    Wrapper *f = NewWrapper();
    emit_attach_parmmaps(l,f);
    Setattr(n,"wrap:parms",l);
    return SWIG_OK;
  }
  
};


static Language * new_swig_sexp() {
  return new Sexp();
}
extern "C" Language * swig_sexp( void ) {
  return new_swig_sexp();
}
