/* ----------------------------------------------------------------------------- 
 * contract.cxx
 *
 *     Experimental support for contracts
 * 
 * Author(s) : Aquinas Hobor (aahobor@cs.uchicago.edu)
 *
 * Copyright (C) 1999-2000.  The University of Chicago
 * See the file LICENSE for information on usage and redistribution.	
 * ----------------------------------------------------------------------------- */

char cvsroot_contract_cxx[] = "$Header: /cvs/projects/SWIG/Source/Modules/contract.cxx,v 1.1 2002/12/03 20:33:32 beazley Exp $";

#include "swigmod.h"

class Contracts : public Dispatcher {

public:
  virtual int top(Node *n) {
    emit_children(n);
    return SWIG_OK;
  }

  virtual int importDirective(Node *n) { return emit_children(n); }
  virtual int includeDirective(Node *n) { return emit_children(n); }  // ?
  virtual int externDeclaration(Node *n) { return emit_children(n); }

  String * strParms(ParmList *l) {
    int comma = 0;
    int i = 0;
    Parm *p = l;
    SwigType *pt;
    String * returns = NewString("");
    while(p) {
      String *pname;
      pt = Getattr(p,"type");
      if ((SwigType_type(pt) != T_VOID)) {
	if (comma) Printf(returns,",");
	pname = Swig_cparm_name(p,i);
	Printf(returns,"%s",SwigType_rcaststr(pt,pname));
	comma = 1;
	i++;
      }
      p = nextSibling(p);
    }
    return returns;
  }

  virtual int cDeclaration(Node *n) {
    String *name = Getattr(n,"name");
    String *k = Getattr(n,"feature:contract");

    if(k)
    {
      /* make the names */
      ParmList *l = Getmeta(k,"parms");
      String *params = ParmList_str(l);
      String *transformed = strParms(l);
      if(DohStrcmp(params,"")==0) {
	DohDelete(params);
	params = DohNewString("void");
      }
      String *contractName = DohNewStringf("__SWIG_precontract_%s",name);

      /* make the contract */
      String *contract = DohNewStringf("int %s(%s,int rt[2])\n{\n",contractName,params);
      SwigScanner * ss = NewSwigScanner();
      SwigScanner_clear(ss);
      SwigScanner_push(ss,Copy(k));
      SwigScanner_token(ss);  // Get rid of the '{' at the begining

      /* loop over the clauses */
      int clauseNum = 1;
      int token = -1;
      while(1) {
	String *clause = DohNewString(""); /*BUG -- should free*/
	while((token=SwigScanner_token(ss))) {
	  if ((token==SWIG_TOKEN_SEMI)||(token==SWIG_TOKEN_RBRACE))
	    break;
	  //	  if (token != SWIG_TOKEN_ENDLINE)
	    Printf(clause,"%s",SwigScanner_text(ss));
	}
	if (DohStrcmp(clause,"\n") != 0) {
	  Printf(contract,"if (!(%s",clause);
	  Printf(contract,")) {\nrt[0]=__LINE__;\nrt[1]=%i;\nreturn 1;\n}\n",clauseNum);
	}
	if(token==SWIG_TOKEN_RBRACE) break;
	clauseNum++;
      }

      /* finish it off and attach it to the main tree */
      Printf(contract,"return 0;\n}\n");
      Setattr(n,"wrap:code",contract); /*BUG -- WHAT IF SOMETHING IS ALREADY THERE*/
      
      /*  Generate the calling code */
      String * calling = DohNewString("{\nint cfail[2];\nchar message[255];\n");
      Printf(calling,"if (%s(%s,cfail)) {\n",contractName,transformed);
      Printf(calling,"sprintf(message,\"Contract %s failed on clause %%i (line %%i)!\",cfail[1],cfail[0]);\n",contractName);
      Printf(calling,"PyErr_SetString(PyExc_Exception,message);return NULL;\n}\n");
      Printf(calling,"}\n");
      /*      Setattr(n,"feature:preassert",calling); */
    }
    /*There are two attributes "feature:preassert" and "feature:postassert".*/

    
    return SWIG_OK;
  }

};

void Swig_contracts(Node *n) {
  Printf(stdout,"Applying contracts (experimental v0.09)\n");

  Contracts *a = new Contracts;
  a->top(n);
  delete a;

}
