#!/usr/bin/tks
// ---- file   : ying.tks
// ---- author : (c) 2004-2008 by Bastian Spiegel <bs@tkscript.de>
// ---- info   : YInG is the YAC Interface Generator.
// ----          YAC adds a portable (and VM independent) c++ object component model with reflectance
// -----         support to the C++ language.
// ----
// ---- license: distributed under terms of the GNU General Public License.
// ----
// ---- changed:  9-Feb-2004, 13-Feb-2004, 16-Feb-2004, 17-Feb-2004, 19-Feb-2004, 21-Feb-2004
// ----          23-Feb-2004,  5-Mar-2004, 23-Mar-2004, 19-Sep-2004,  9-Jan-2005, 11-Jan-2005
// ----          17-Jan-2005,  5-Feb-2005, 14-Feb-2005, 10-Mar-2005  25-Apr-2005, 25-May-2005
// ----          25-Jun-2005, 14-Jan-2006, 03-Feb-2006, 11-Nov-2007, 29-Dec-2007, 30-Jan-2008
// ----          04-Feb-2008, 11-Feb-2008, 13-Mar-2008, 09-May-2008
// ----
// ----
module Main;


//String s_srcfile="test.h";
boolean  b_group = true;
boolean  b_clid = true;
boolean  b_verbose = false;
boolean  b_allowintfloatmixup = false; // only works when cdecl (i.e. when all args are passed on the stack)

// Last seen line#
int line_nr;
String s_srcfile;


function ParseError(String _msg) {
   die("[---] YInG: "+s_srcfile+"("+line_nr+"): "+_msg+"\n");
}

function IP(String _msg) {
   trace "[...] YInG: "+_msg;
}


class YGroup
{
   public String name;
   public String source_h;                 // extern sUI clid_xxx; class xxx;
   public String source_cpp_templates;     // templates
   public String source_cpp_yac_init_cl;   // register classes
   public String source_cpp_yac_init_fun;  // register functions
   public String source_cpp_yac_exit;      // YAC_Exit_<group> inner statements
   public String source_cpp_fun_wrappers;  // function wrappers

   public method appendSources(
      String _h, String _cpp_templ, String _cpp_init_cl, String _cpp_init_fun, String _cpp_exit, String _fun_wrap) 
      {
         source_h                .append(_h);
         source_cpp_templates    .append(_cpp_templ);
         source_cpp_yac_init_cl  .append(_cpp_init_cl);
         source_cpp_yac_init_fun .append(_cpp_init_fun);
         source_cpp_yac_exit     .append(_cpp_exit);
         source_cpp_fun_wrappers .append(_fun_wrap);
      }
   
   
   public method writeGroupFiles() {
      String s_cpp;
      String s_h;
      
      s_cpp.alloc(16*1024); s_cpp.empty();
      s_cpp.append("// ---- auto generated by YInG - the YAC interface generator ("+
                   getCurrentDateString()+" "+getCurrentTimeString()+")\n");
      s_cpp.append("#ifndef __YAC__"+name+"_h__\n#define __YAC__"+name+"_h__\n\n");
      s_cpp.append(source_cpp_templates);
      s_cpp.append("\nvoid YAC_CALL YAC_Init_"+name+"(YAC_Host *_host) {\n\t// --------------------------------- classes ---------------------------------\n");
      s_cpp.append(source_cpp_yac_init_cl);
      s_cpp.append("\t// -------------------------------- functions --------------------------------\n");
      s_cpp.append(source_cpp_yac_init_fun);
      s_cpp.append("\n");
      s_cpp.append("}\n");
      s_cpp.append(source_cpp_fun_wrappers);
      
      s_cpp.append("\nvoid YAC_CALL YAC_Exit_"+name+"(YAC_Host *_host) {\n");
      s_cpp.append(source_cpp_yac_exit);
      s_cpp.append("}\n");
      s_cpp.append("\n#endif\n");
      
      
      s_h.alloc(16*1024); s_h.empty();
      s_h.append("// ---- ying_"+name+".h: auto generated by YInG - the YAC interface generator ("+
                 getCurrentDateString()+" "+getCurrentTimeString()+")\n");
      s_h.append(source_h);
      
      s_cpp .saveLocal("ying_"+name+".cpp");
      s_h   .saveLocal("ying_"+name+".h");
   }
}

HashTable groups;
String    s_group;
YGroup    cgroup;

function AddGroup() // name=s_group 
{
   if(!groups.exists(s_group))
   {
      cgroup<=new YGroup();
      cgroup.name=s_group;
      groups[s_group]=deref cgroup;
   }
   else
   {
      cgroup<=groups[s_group];
   }
}

int b_method_defreturn;
HashTable scanned_classes<=new HashTable;
YClass c_class;
String    s_class;
YMethod   c_method;

HashTable classes; classes.alloc(512);
String    ordered_classes[512];
YFunction functions[512];

String    yclass_typenames[]<=["YAC_Template ", "YAC_STemplate", "YAC_RTemplate"];





function UnMangle(String _s) {
   if(_s.startsWith("_yac_"))
   {
      _s.substring(5, _s.length-5);
   }
   else if(_s[0]=='_') 
   {
      _s.substring(1, _s.length-1);
   }
}

function UnMangleR(String _s) {
   if(scanned_classes.exists(_s))
   {
      return classes[_s].scriptname;
   }
   if(_s[0]=='_') 
   {
      String r;
      _s.substring(1, _s.length-1) => r;
      return r;
   }
   else
   {
      return _s;
   }
}


if(Arguments.numElements<1)
{
   print "Usage: ./ying.tks [-ng,--nogroup] [-nc,--noclid] inputfile.[cpp|h]";
   print " ";
   print "     -ng,--nogroup : Omit group header/cpp file output";
   print "     -nc,--noclid  : Omit YAC_C(<name>); statements, usually only ";
   print "                     for static class ID setups. (e.g. the TKS core API)";
   print "     -v, --verbose : Print parsed classes and methods to stdout.";
   print "";
   die("no input files.");
}

int total_num_functions=0;
int total_num_classes=0;
int total_num_methods=0;
int total_num_files=0;

StringArray inputfiles;
String ca; 
foreach ca in Arguments
{
   switch(ca)
	{
      case "-ng":
      case "--nogroup":
         b_group=false;
         break;
      case "-nc":
      case "--noclid":
         b_clid=false;
         break;
      case "-v":
      case "--verbose":
         b_verbose=true;
         break;
      default:
         inputfiles.add(ca);
         break;
	}
}

dtrace b_verbose;

enum {
   YCLASS_DYNAMIC   =0,
   YCLASS_STATIC    =1,
   YCLASS_RESTRICTED=2
}; 


function PadSpacesUntilColumn(String _s, int _col) {
   int l=_col-_s.length;
   loop(l) _s.append(" ");
}

function getCurrentDateString() {
   Time t; t.now();
   return 
      ((["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"])[t.weekday])+", "+t.monthday+"/"+
      ((["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"])[t.month])+"/"+t.year;
}

function getCurrentTimeString() {
   Time t;
   Integer io_h;
   Integer io_m;
   Integer io_s;
   t.now();
   io_h.value=t.hour;
   io_m.value=t.min;
   io_s.value=t.sec;
   return io_h.printf("%02i")+":"+io_m.printf("%02i")+":"+io_s.printf("%02i");
}

class YConst {
   int    type;
   String name;
   String expr;
}

int i_arginitcount;
int b_argvret;
class YArg {
   int    type;  // 0,1,2,3
   String otype;  // YAC_String/YAC_ListNode/YAC_TreeNode/YAC_IntArray/YAC_FloatArray/_MyClass
   String stype;  // String/ListNode/TreeNode/IntArray/FloatArray/MyClass
   int    isarray;
    
   getOrigArg() {
      switch(type)
      {
         case 0:
         case 1:
         case 2:
            return "";
         case 3:
            switch(otype)
            {
               default:
                  return "("+otype+"*)";
               case "":
                  return ""; // YAC_Object*
               case "YAC_String":
                  return "(YAC_String*)";
               case "YAC_Event":
                  return "(YAC_Event*)";
               case "YAC_ListNode":
                  return "(YAC_ListNode*)";
               case "YAC_TreeNode":
                  return "(YAC_TreeNode*)";
               case "YAC_Value":
                  return "(YAC_Value*)";
               case "YAC_IntArray":
                  return "(YAC_IntArray*)";
               case "YAC_FloatArray":
                  return "(YAC_FloatArray*)";
            }
            return "";
      }
   }

   init(String _s) {
      String tcl;
      isarray=false;
      otype="";
      _s.trim();  // "mangled" name, e.g. "_Pixmap"
      _s.replace("*","");
      tcl=_s; UnMangle(tcl); //tcl.replace("_",""); // unmangled name ("Pixmap")
      switch(_s) {
         case 0:
         case "":
         case "void":
            type=0;
            return 1;
         case "int":
         case "sSI":
         case "sBool":
         case "sUI":
         case "sS32":
            //case "sS64":
            type=1;
            i_arginitcount++;
            return 1;
         case "float":
            //case "double":
         case "sF32":
            //case "sF64":
            type=2;
            i_arginitcount++;
            return 1;
         case "YAC_Object":
            type=3;
            i_arginitcount++;
            return 1;
         case "YAC_String": 
            type=3; otype=_s; i_arginitcount++; stype="String";
            return 1;
         case "YAC_ValueObject": 
            type=3; otype=_s; i_arginitcount++; stype="Value";
            return 1;
         case "YAC_ListNode": 
            type=3; otype=_s; i_arginitcount++; stype="ListNode";
            return 1;
         case "YAC_TreeNode": 
            type=3; otype=_s; i_arginitcount++; stype="TreeNode";
            return 1;
         case "YAC_IntArray": 
            type=3; otype=_s; i_arginitcount++; stype="IntArray";
            return 1;
         case "YAC_FloatArray":
            type=3; otype=_s; i_arginitcount++; stype="FloatArray";
            return 1;
         case "YAC_Event": 
            type=3; otype=_s; i_arginitcount++; stype="Event";
            return 1;
         case "YAC_Value": 
            b_argvret=true;
            return 1;
         case "*":
            return 1;
         default:
            if(!scanned_classes.exists(tcl))
            {
               ParseError("cannot handle non-interface class \""+tcl+"\".");
            }
            type=3; otype=_s; stype=tcl;
            i_arginitcount++;
            return 1;
      }
   }
    
   implement(String _arg) {
      String r;
      switch(type)
      {
         default:
         case 0:
         case 1:
         case 2:
            return "";
         case 3:
            switch(otype)
            {
               default:
                  b_method_defreturn=true;
                  return "if(YAC_BCHK("+_arg+", clid_"+UnMangleR(otype)+"))";
               case "":
                  return "";
               case "YAC_String":
                  b_method_defreturn=true;
                  return "if(YAC_BCHK("+_arg+", YAC_CLID_STRING))";
               case "YAC_Value":
                  b_method_defreturn=true;
                  return "if(YAC_BCHK("+_arg+", YAC_CLID_VALUE))";
               case "YAC_ListNode":
                  b_method_defreturn=true;
                  return "if(YAC_BCHK("+_arg+", YAC_CLID_LISTNODE))\n";
               case "YAC_TreeNode":
                  b_method_defreturn=true;
                  return "if(YAC_BCHK("+_arg+", YAC_CLID_TREENODE))";
               case "YAC_IntArray":
                  b_method_defreturn=true;
                  return "if(YAC_BCHK("+_arg+", YAC_CLID_INTARRAY))";
               case "YAC_FloatArray":
                  b_method_defreturn=true;
                  return "if(YAC_BCHK("+_arg+", YAC_CLID_FLOATARRAY))";
            }
      }
   }
    
   getParam() {
      switch(type)
      {
         default:
         case 0: return "";
         case 1: return "sSI";
         case 2: return "sF32";
         case 3: return "YAC_Object *";
      }
   }
    
   getParam2() {
      switch(type)
      {
         default:
         case 0: ParseError("getParam2: cannot handle type="+type+".");
         case 1: return "si";
         case 2: return "f4";
         case 3: return "o";
      }
   }

}

class YProperty : YArg {
   String name;
      //int type;
    
   getTypeString() {
      switch(type)
      {
         default:
         case 0:
            ParseError("YProperty::getTypeString: <void>");
            break;
         case 1:
            return "sSI";
         case 2:
            return "sF32";
         case 3:
            return "YAC_Object *";
      }
   }
    
   implementGet2();
    
   implementSet2() {
      String r;
      String s_prop=name;
      s_prop[0]=ucchar(s_prop[0]);
      r="void YAC_CALL "+c_class.scriptname+"__set"+s_prop;
      PadSpacesUntilColumn(r, 37);
      r.append("(void *_o, yacmemptr _args");
      PadSpacesUntilColumn(r, 78);
      r.append(") {");
      switch(type)
      {
         case 0:
            ParseError("[---] YProperty::implementSet: type==void.");
         case 1: // int
            r.append("(("+c_class.name+"*)_o)->"+name+"=_args.mem[0].si;");
            break;
         case 2: // float
            r.append("(("+c_class.name+"*)_o)->"+name+"=_args.mem[0].f4;");
            break;
         case 3: // Object
            r.append("if((("+c_class.name+"*)_o)->"+name+")(("+c_class.name+"*)_o)->"+name+"->yacOperatorAssign(_args.mem[0].o);");
            break;
      }
      r.append("}\n");
      return r;
   }
}

class YFunction {
   String name;  // original C/C++ name, e.g. _Delete
   String mangled_name; // original C/C++ name or name of wrapper function (required for variable return type functions)
   String script_name;  // unmangled name, e.g. "Delete"
   YArg   return_type;
   int    return_new;
   YArg   args[];
    
   YFunction() { return_type.type=0; args.alloc(16); }  

   getNumArgs() {
      int r=0;
      YArg a; foreach a in args 
                 r+=(a.type!=0);
      return r;
   }

   needWrapper() {
      if(b_allowintfloatmixup)
      {
         // Note: Always wrap the function to be compatible with e.g. the fastcall calling convention (e.g. x86-64)
         //       Mixing sUI/sF32 will not work when arguments are passed in registers!
         if(return_type.type==4)
            return true; // return values are placed in an additional argument (YAC_Value *_r)
         else
         {
            // ---- need wrapper method ? (return value) ----
            // ---- or can we directly call the method (void method(int|float..))
            int r=0;
            YArg a; foreach a in args {
               r=r||((a.type>2)&&(!a.otype.isBlank()));
            }
            return r;
         }
      }
      else
      {
         return (return_type.type > 3) || (args.numElements > 0); // Wrap if arguments (fastcall!) or variable return type is used 
      }
   }
    
   /*virtual*/ getWrapperDecl();    // ----
   /*virtual*/ implementWrapper();  // ---- global function 

   appendRegister(String _s);
   appendRegisterArgType(String _s);
   appendRegisterArgOType(String _s);
}

class YMethod : YFunction {
   getWrapperDecl();
   implementWrapper();
}

class YClass { 
   int       type; // 'D' 'R' 'S'
   String    name;
   String    scriptname;
   HashTable methods;
   HashTable properties;
   YConst    constants[];
   String    ordered_methods[];
   String    ordered_properties[];
   boolean   b_pooled;
      //String   mangled_name;
    
   YClass() {
      methods.alloc(512);
      properties.alloc(512);
      ordered_methods.alloc(512);
      ordered_properties.alloc(512);
      constants.alloc(8192);
      b_pooled = false;
   }
}

YFunction::appendRegisterArgType {
   String s="\tstatic const sUI   "+mangled_name+"_arg_types";
   PadSpacesUntilColumn(s, 64);
   s.append("[]={");
   if(args.numElements)
	{
      YArg a;
      int b_addsep=false;
      foreach a in args {
         if(b_addsep)
            s.append(", ");
         else
            b_addsep=true;
         s.append(String(a.type));
      }
      s.append(",};\n");
	}
   else
      s.append("0,};\n");
   _s.append(s);
}

YFunction::appendRegisterArgOType {
   String s="\tstatic const char *"+mangled_name+"_arg_otypes";
   PadSpacesUntilColumn(s, 64);
   s.append("[]={");
   if(args.numElements)
	{
      int b_addsep=false;
      YArg a;
      foreach a in args {
         if(b_addsep)
            s.append(", ");
         else
            b_addsep=true;
         s.append("\""+a.otype+"\"");
      }
      s.append(",};\n");
	}
   else
      s.append("0,};\n");
   _s.append(s);
}

YFunction::appendRegister {
   int i_callstyle;
   if(needWrapper())
	{
      // Parameters are passed through a yacmemptr that is decoded by a wrapper function
      if(return_type.type==4)
         i_callstyle=(args.numElements==0)+4;
      else
         i_callstyle=return_type.type;
	}
   else
   {
      // Parameters are directly passed to the native function
      int combi = 0; // int,float/ptr arg comb.  bit set == ptr  (64bit on 64bit systems)
      int argi = 0;
      loop(args.numElements)
      {
         YArg arg <= args[argi];
         if(arg.type > 2)
         {
            // Object pointers possibly require 64bit
            combi |= (1<<argi);
         }
         argi++;
      }
      ////i_callstyle = 8+(4*args.numElements)+return_type.type;
      
      i_callstyle = 8 + (4*combi) + return_type.type;
      argi = 0;
      loop(args.numElements)
      {
         i_callstyle += (1<<argi) *4;
         argi++;
      }
   }
   _s.append("\t_host->yacRegisterFunction((void*)"+mangled_name
             +", \""+script_name+"\", "
             +return_type.type
             +", \""+return_type.otype+"\", "
             +args.numElements
             +", "+mangled_name+"_arg_types, "+mangled_name+"_arg_otypes, "+i_callstyle+");\n");
}

YFunction::getWrapperDecl {
   if(needWrapper())
	{
      String r; r.alloc(1024);
      switch(return_type.type)
		{
         case 0: r="YAC_APIC void "; break;
         case 1: r="YAC_APIC sSI  "; break;
         case 2: r="YAC_APIC sF32 "; break;
         case 3: r="YAC_APIC void*"; break;
         case 4: r="YAC_APIC void "; break;
		}
      r.append(" YAC_CALL ");
      r.append(mangled_name);
      PadSpacesUntilColumn(r, 37);
      r.append("(");
	    
      if(args.numElements)
		{
         r.append("yacmemptr");
         if(return_type.type==4)
            r.append(", YAC_Value *");
		}
      else
         if(return_type.type==4)
            r.append("YAC_Value *");
      PadSpacesUntilColumn(r, 68);
      return r+");\n";
	}
   else
      return "";
}

YMethod::getWrapperDecl {
   String r; r.alloc(1024);
   switch(return_type.type)
	{
      case 0: r="void "; break;
      case 1: r="sSI  "; break;
      case 2: r="sF32 "; break;
      case 3: r="void*"; break;
      case 4: r="void "; break;
	}
   r.append(" YAC_CALL ");
   r.append(c_class.scriptname+"__"+name);
   PadSpacesUntilColumn(r, 37);
   r.append("(void *");
    
   if(args.numElements)
      r.append(", yacmemptr");
   if(return_type.type==4)
      r.append(", YAC_Value *");
   PadSpacesUntilColumn(r, 68);
   return r+");\n";
}

YFunction::implementWrapper() { 
   if(needWrapper())
	{
         //trace "YFunction::implementWrapper";
      String r; r.alloc(1024);
      String arglist="";
	    
      switch(return_type.type)
		{
         case 0: r="void "; break;
         case 1: r="sSI  "; break;
         case 2: r="sF32 "; break;
         case 3: r="void*"; break;
         case 4: r="void "; break;
		}
	    
      r.append(" YAC_CALL ");
      r.append(mangled_name);
      PadSpacesUntilColumn(r, 37);
      r.append("(");
      int i_arg=0;
      int b_addsep=false;
      YArg a;
      foreach a in args
         {
            if(a.type)
            {
               if(b_addsep)
                  arglist.append(", ");
               else
               {
                  r.append("yacmemptr _args");
                  b_addsep=true;
               }
				
               ////arglist.append(a.getOrigArg()+"_args."+a.getParam2()+"["+i_arg+"]");
               arglist.append(a.getOrigArg()+"_args.mem"+"["+i_arg+"]."+a.getParam2());
               i_arg++;
            }
         }
      if(return_type.type==4)
		{
         if(b_addsep)
			{
            r.append(", ");
            arglist.append(", ");
			}
         r.append("YAC_Value *_r");
         arglist.append("_r");
		}
      PadSpacesUntilColumn(r, 78);
      r.append(") {");
	    
      b_method_defreturn=false;
         // ---- decode parameters ----
      i_arg=0;
      foreach a in args
         {
            if(a.type)
            {
               ////r.append(a.implement("_args."+a.getParam2()+"["+i_arg+"]"));
               r.append(a.implement("_args.mem"+"["+i_arg+"]."+a.getParam2()));
               i_arg++;
            }
         }
      if(b_method_defreturn) r.append("{ ");
	    
         // ---- call wrapped method ----
      switch(return_type.type)
		{
         case 1: r.append("return (sSI)"); break;
         case 2: r.append("return (sF32)"); break;
         case 3: r.append("return (void*)"); break;
		}
      r.append(name+"("+arglist+");");
      if(b_method_defreturn) {
         r.append("}");
         switch(return_type.type)
         {
            case 1:
               r.append("return 0;");
               break;
            case 2:
               r.append("return 0.0f;");
               break;
            case 3:
               r.append("return (void*)0;");
               break;
         }
      }
      return r+"}\n";
	}
   else
	{
      return "";
	}
}

YMethod::implementWrapper() { 
   String r; r.alloc(1024);
    
   String arglist="";
    
   switch(return_type.type)
   {
      case 0: r="void "; break;
      case 1: r="sSI  "; break;
      case 2: r="sF32 "; break;
      case 3: r="void*"; break;
      case 4: r="void "; break;
   }

   r.append(" YAC_CALL ");
   r.append(c_class.scriptname+"__"+name);
   PadSpacesUntilColumn(r, 37);
   r.append("(void *_o");
   int i_arg=0;
   int b_addsep=false;
   YArg a;
   foreach a in args
      {
         if(a.type)
         {
            if(b_addsep)
               arglist.append(", ");
            else
            {
               r.append(", yacmemptr _args");
               b_addsep=true;
            }
            ////arglist.append(a.getOrigArg()+"_args."+a.getParam2()+"["+i_arg+"]");
            arglist.append(a.getOrigArg()+"_args.mem"+"["+i_arg+"]."+a.getParam2());
            i_arg++;
         }
      }
   if(return_type.type==4)
	{
      r.append(", YAC_Value *_r");
      if(b_addsep)
         arglist.append(", ");
      arglist.append("_r");
	}
   PadSpacesUntilColumn(r, 78);
//      l=78-r.length;
//      if(l>0) loop(l) r.append(" ");
   r.append(") {");
    
   b_method_defreturn=false;
      // ---- decode parameters ----
   i_arg=0;
   foreach a in args
      {
         if(a.type)
         {
            ////r.append(a.implement("_args."+a.getParam2()+"["+i_arg+"]"));
            r.append(a.implement("_args.mem"+"["+i_arg+"]."+a.getParam2()));
            i_arg++;
         }
      }
   if(b_method_defreturn) r.append("{ ");
    
      // ---- call wrapped method ----
   switch(return_type.type)
   {
      case 1: r.append("return (sSI)"); break;
      case 2: r.append("return (sF32)"); break;
      case 3: r.append("return (void*)"); break;
   }
   r.append("(("+c_class.name+"*)_o)->"+name+"("+arglist+");");
   if(b_method_defreturn) {
      r.append("}");
      switch(return_type.type)
      {
         case 1:
            r.append("return 0;");
            break;
         case 2:
            r.append("return 0.0f;");
            break;
         case 3:
            r.append("return (void*)0;");
            break;
      }
   }
   return r+"}\n";
}

function AddClass(int _type) {
   trace "AddClass(\""+s_class+"\", _type="+_type+")";
   c_class<=new YClass;
   classes[s_class]=deref c_class;
   c_class.type=_type; //YCLASS_DYNAMIC, YCLASS_STATIC, YCLASS_RESTRICTED
   c_class.name=s_class;
   if(s_class=="YAC_Object")
      c_class.scriptname="Object";
   else
   {
      c_class.scriptname=s_class; 
      UnMangle(c_class.scriptname);
   }
      
   ordered_classes.add(s_class);
   total_num_classes++;
}

function AddMethod(String _name, String _return, ListNode _args) {
   _name.replace("*","");
   trace "AddMethod(\""+_name+"\")";
   c_method<=new YMethod;
   c_method.name=_name;
   String s_scriptname=_name;
   UnMangle(s_scriptname); //.replace("_","");
   c_method.script_name=s_scriptname; // e.g. "_delete" => "delete"
      //trace  "c_method.script_name="+c_method.script_name;
   HashTable h<=c_class.methods;
   h[_name]=deref c_method;
   c_class.ordered_methods.add(_name);
   _return.replace("*","");
   YArg c_return<=c_method.return_type;
   if(!c_return.init(_return))
   {
      ParseError("[---] cannot handle return type \""+_return+"\"\n");
   }
   i_arginitcount=0;
   b_argvret=false;
   Value v; foreach v in _args {
         //trace "AddMethod arg["+i_arginitcount+"] type="+v.type+" string="+v.string;
      if(v.type)
      {
         if(!c_method.args[i_arginitcount].init(v.stringValue))
         {
            ParseError("YMethod: cannot handle argument "+i_arginitcount+" type \""+v.stringValue+"\".");
         }
      }
   }
   if(b_argvret) c_return.type=4;
   c_method.args.setNumElements(i_arginitcount);
   total_num_methods++;
}

function AddFunction(String _name, String _return, ListNode _args) {
   trace "AddFunction(name=\""+_name+"\", return=\""+_return+"\" args="+_args+")";
   total_num_functions++;
   _name.replace("*","");
   YFunction f<=functions.nextFree;
   f.name=_name;
   f.script_name = _name;
   UnMangle(f.script_name);
   _return.replace("*","");
   YArg f_return<=f.return_type;
   if(!f_return.init(_return))
   {
      ParseError("YFunction: cannot handle return type \""+_return+"\"");
   }
   i_arginitcount=0;
   b_argvret=false;
   Value v; foreach v in _args {
         //trace "AddFunction arg["+i_arginitcount+"] type="+v.type+" string="+v.string;
      if(v.type)
      {
         if(!f.args[i_arginitcount].init(v.stringValue))
         {
            ParseError("YFunction: cannot handle argument "+i_arginitcount+" type \""+v.stringValue+"\".");
         }
      }
   }
   f.args.setNumElements(i_arginitcount);
   if(b_argvret) f_return.type=4;
   if(f.needWrapper()) 
      f.mangled_name="__APIC__"+_name;
   else 
      f.mangled_name=_name;
}

function AddProperty(String _name, String _type) {
   HashTable h<=c_class.properties;
  
   _name.replace("*", "");
   _name.trim();
   YProperty p<=new YProperty();
   h[_name]=deref p;
   c_class.ordered_properties.add(_name);
   p.name=_name;
   if(!p.init(_type))
   {
      ParseError("cannot handle property type \""+_type+"\".");
   }
}

function AddConstant(String _name, String _expr, int _type) {
//      trace "AddConstant(name=\""+_name+"\" expr=\""+_expr+"\" type="+_type+")";
   YConst c<=c_class.constants.nextFree;
      //trace "c="+c;
   UnMangle(_name); //_name.replace("_","");
   c.name=_name;
   c.type=_type;
   c.expr=_expr;
}

String s_cpp_templ, s_cpp_init_cl, s_cpp_init_fun, s_cpp_exit, s_cpp_funwrap;
//  String s_h;
String s_group_h;
function CPPOut() {
   String s_clid_def="";
   String tcl;
   String s_classfdecl="";
   s_cpp_templ   .alloc(8*1024); s_cpp_templ   .empty();
   s_cpp_init_cl .alloc(8*1024); s_cpp_init_cl .empty();
   s_cpp_init_fun.alloc(8*1024); s_cpp_init_fun.empty();
   s_cpp_exit    .alloc(4*1024); s_cpp_exit    .empty();
   s_cpp_funwrap .alloc(4*1024); s_cpp_funwrap .empty();

      //    s_cpp.append("// ---- auto generated by YInG - the YAC interface generator ("+
//                 getCurrentDateString()+" "+getCurrentTimeString()+")\n");
//    s_cpp.append("#ifndef __YAC__"+s_group+"_h__\n#define __YAC__"+s_group+"_h__\n\n");

   s_group_h.alloc(16*1024); s_group_h.empty();
//    s_group_h.append("// ---- ying_"+s_group+".h: auto generated by YInG - the YAC interface generator ("+
//                     getCurrentDateString()+" "+getCurrentTimeString()+")\n");

   String scl;
   YClass c;
   foreach scl in ordered_classes {
      c<=classes[scl];
      c_class<=c;
      tcl=c.name; //"__YAC_"+scl;
    
      String s_cl; 
//      String s_decl; s_decl.alloc(4*1024); s_decl.empty();
      String s_cdecl; s_cdecl.alloc(4*1024); s_cdecl.empty();
//      String s_def; s_def.alloc(4*1024); s_def.empty();
      String s_cdef; s_cdef.alloc(4*1024); s_cdef.empty();

      s_cl.alloc(16*1024); s_cl.empty();
      s_cdecl.append("// ---- ying_"+s_group+"_"+c.scriptname+".cpp: auto generated by YInG - the YAC interface generator ("+
                     getCurrentDateString()+" "+getCurrentTimeString()+")\n\n");

         // ---- write method declarations/exports ----
      s_classfdecl.append("class "+tcl+";\n");
      int i_method=1;
      int i_member=0;
      String s_method_pfx;
      String s_method;
      int i_maxmethodlen=1;
      foreach s_method in c.ordered_methods {
         c_method<=c.methods[s_method];
         if(c_method.name.length>i_maxmethodlen)
            i_maxmethodlen=c_method.name.length;
      }
      foreach s_method in c.ordered_methods {
         c_method<=c.methods[s_method];
         s_cdecl.append(c_method.getWrapperDecl());
      }
      String s_property;

      YProperty p;
      String s_prop_impl; s_prop_impl.alloc(4*1024); s_prop_impl.empty();
      foreach s_property in c.ordered_properties {
         p<=c.properties[s_property];
         String s_prop=p.name;
         s_prop[0]=ucchar(s_prop[0]);
         int l;
         if(!c.methods.exists("get"+s_prop))
         {
            i_method++;
            String s_get_decl;
            switch(p.type)
            {
               case 0: s_get_decl="void "; break;
               case 1: s_get_decl="sSI  "; break;
               case 2: s_get_decl="sF32 "; break;
               case 3: s_get_decl="void*"; break;
            }
            s_get_decl.append(" YAC_CALL ");
            s_get_decl.append(tcl+"__get"+s_prop);
            PadSpacesUntilColumn(s_get_decl, 37);
            s_get_decl.append("(void *");
            PadSpacesUntilColumn(s_get_decl, 68);
            s_cdecl.append(s_get_decl+");\n");
               // ---- implement property get method ----
            s_prop_impl.append(p.implementGet2());
         }
         if(!c.methods.exists("set"+s_prop))
         {
            i_method++;
            String s_set_decl="void YAC_CALL "+tcl+"__set"+s_prop;
            PadSpacesUntilColumn(s_set_decl, 37);
            s_set_decl.append("(void *, yacmemptr");
            PadSpacesUntilColumn(s_set_decl, 68);
            s_cdecl.append(s_set_decl+");\n");
               // ---- implement property set method ----	  
            s_prop_impl.append(p.implementSet2());
         }
         i_member++;
      }
      if(b_clid)
      {
         if(c.b_pooled)
         {
            s_cdef.append("YAC_C_POOLED("+tcl+", \""+c.scriptname+"\");\n\n");
         }
         else
         {
            s_cdef.append("YAC_C("+tcl+", \""+c.scriptname+"\");\n\n");
         }
      }
      s_group_h.append("extern sUI clid_"+c.scriptname+";\n");
      s_cpp_templ.append("sUI                                          clid_"+c.scriptname+";\n");
      String s_template=yclass_typenames[c.type]+"<"+tcl+">";
      PadSpacesUntilColumn(s_template, 48);
      s_cpp_templ.append(s_template+"*t_"+c.scriptname+";\n");

      s_cl.append("// ------------------ YAC class \""+tcl+"\" reflection map (implementation) ------------------\n");
      if(i_member)
      {
         String s_cl_member_name="";
         String s_cl_member_type="";
         String s_cl_member_objecttype="";
         String s_cl_member_offset="";
         int b_addsep=false;
         foreach s_property in c.ordered_properties {
            p<=c.properties[s_property];
            if(b_addsep) 
            {
               s_cl_member_name.append(", ");
               s_cl_member_type.append(", ");
               s_cl_member_objecttype.append(", ");
               s_cl_member_offset.append(", ");
            }
            else b_addsep=true;
            s_cl_member_name.append("\""+p.name+"\"");
            s_cl_member_type.append(String(p.type));
            if(!p.stype.isBlank())
               s_cl_member_objecttype.append("\""+p.stype+"\"");
            else
               s_cl_member_objecttype.append("(const char*)0");
		    
            //s_cl_member_offset.append("(((sSI)&"+p.name+")-((sSI)this))");
            s_cl_member_offset.append("((sU8*)((((sU8*)&"+p.name+")-((sU8*)this))))");
         }
         s_cl.append("sUI          YAC_VCALL "+tcl+"::yacMemberGetNum                  (void) {return "+i_member+";}\n");
         s_cl.append("const char **YAC_VCALL "+tcl+"::yacMemberGetNames                (void) {static const char*r[]={"+s_cl_member_name+"};return r; }\n");
         s_cl.append("const sUI   *YAC_VCALL "+tcl+"::yacMemberGetTypes                (void) {static const sUI r[]={"+s_cl_member_type+"}; return r; }\n");
         s_cl.append("const char **YAC_VCALL "+tcl+"::yacMemberGetObjectTypes          (void) {static const char*r[]={"+s_cl_member_objecttype+"};return r; }\n");
         s_cl.append("const sU8  **YAC_VCALL "+tcl+"::yacMemberGetOffsets              (void) {static const sU8*r[]={"+s_cl_member_offset+"};return r;}\n");
      }
      else
      {
         s_cl.append("sUI          YAC_VCALL "+tcl+"::yacMemberGetNum                  (void) {return 0;}\n");
         s_cl.append("const char **YAC_VCALL "+tcl+"::yacMemberGetNames                (void) {return 0;}\n");
         s_cl.append("const sUI   *YAC_VCALL "+tcl+"::yacMemberGetTypes                (void) {return 0;}\n");
         s_cl.append("const char **YAC_VCALL "+tcl+"::yacMemberGetObjectTypes          (void) {return 0;}\n");
         s_cl.append("const sU8  **YAC_VCALL "+tcl+"::yacMemberGetOffsets              (void) {return (const sU8**)0;}\n");
      }


      if(i_method||c.ordered_methods.numElements)
      {
         String s_cl_method_name="\"operator\"";
         String s_cl_method_numparam="2";
         String s_cl_method_param_types="";
         String s_cl_method_param_typesd="";
         String s_cl_method_param_otypes="";
         String s_cl_method_param_otypesd="";
         String s_cl_method_return_type="4";
         String s_cl_method_return_otype="\"\"";
         String s_cl_method_adr="(void*)Object__operator";
         String s_cl_method_adr2="(void*)0";
         int i_typesd=1;

         s_cl_method_param_typesd.append("static const sUI rt0[]={1,3};");
         s_cl_method_param_types.append("rt0");
         s_cl_method_param_otypesd.append("static const char *rs0[]={0,\"\",};");
         s_cl_method_param_otypes.append("rs0");

         foreach s_method in c.ordered_methods {
            i_method++;
            c_method<=c.methods[s_method];
            s_cl_method_name.append(", ");
            s_cl_method_numparam.append(", ");
            s_cl_method_param_types.append(", ");
            s_cl_method_param_otypes.append(", ");
            s_cl_method_return_type.append(", ");
            s_cl_method_return_otype.append(", ");
            s_cl_method_param_typesd.append("static const sUI rt"+i_typesd+"[]=");
            s_cl_method_param_otypesd.append("static const char *rs"+i_typesd+"[]=");
            s_cl_method_numparam.append(String(c_method.args.numElements));
            s_cl_method_name.append("\""+c_method.script_name+"\"");
            s_cl_method_return_type.append(String(c_method.return_type.type));
            if(!c_method.return_type.stype.isBlank())
               s_cl_method_return_otype.append("\""+c_method.return_type.stype+"\"");
            else
               s_cl_method_return_otype.append("(const char*)0");
            s_cl_method_adr.append(", (void*)"+c.scriptname+"__"+c_method.name);
            s_cl_method_adr2.append(", (void*)__JIT__"+c.scriptname+"__"+c_method.name);
            int b_addsep3=false;
            if(c_method.args.numElements)
            {
               s_cl_method_param_typesd.append("{");
               s_cl_method_param_otypesd.append("{");
               YArg a; foreach a in c_method.args {
                  if(b_addsep3)
                  {
                     s_cl_method_param_typesd.append(", ");
                     s_cl_method_param_otypesd.append(", ");
                  }
                  else
                     b_addsep3=true;
                  s_cl_method_param_typesd.append(String(a.type));
                  if(!a.stype.isBlank())
                     s_cl_method_param_otypesd.append("\""+a.stype+"\"");
                  else
                     s_cl_method_param_otypesd.append("(const char*)0");
               }
               s_cl_method_param_typesd.append(",};");
               s_cl_method_param_otypesd.append(",};");
            }
            else
            {
               s_cl_method_param_typesd.append("{0,};");//
               s_cl_method_param_otypesd.append("{(const char*)0,};");
            }
            s_cl_method_param_types.append("rt"+(i_typesd));
            s_cl_method_param_otypes.append("rs"+(i_typesd++));
         }
         foreach s_property in c.ordered_properties {
            p<=c.properties[s_property];
            s_prop=p.name;
            s_prop[0]=ucchar(s_prop[0]);
            if(!c.methods.exists("get"+s_prop))
            {
               s_cl_method_name.append(", \"get"+s_prop+"\"");
               s_cl_method_numparam.append(", 0");
               s_cl_method_param_typesd.append("static const sUI rt"+i_typesd+"[]={0,};");
               s_cl_method_param_otypesd.append("static const char *rs"+i_typesd+"[]={\"\", };");
               s_cl_method_param_types.append(", rt"+(i_typesd));
               s_cl_method_param_otypes.append(", rs"+(i_typesd++));
               s_cl_method_return_type.append(", "+p.type);
               s_cl_method_return_otype.append(", \""+p.stype+"\"");
               s_cl_method_adr.append(", (void*)"+tcl+"__get"+s_prop);
                  //i_method++;
            }
            if(!c.methods.exists("set"+s_prop))
            {
               s_cl_method_name.append(", \"set"+s_prop+"\"");
               s_cl_method_numparam.append(", 1");
               s_cl_method_param_typesd.append("static const sUI rt"+i_typesd+"[]={"+p.type+",};");
               s_cl_method_param_otypesd.append("static const char *rs"+i_typesd+"[]={\""+p.stype+"\", };");
               s_cl_method_param_types.append(", rt"+(i_typesd));
               s_cl_method_param_otypes.append(", rs"+(i_typesd++));
               s_cl_method_return_type.append(", 0");
               s_cl_method_return_otype.append(", \"\"");
               s_cl_method_adr.append(", (void*)"+tcl+"__set"+s_prop);
                  //i_method++;
            }
         }

         s_cl.append("sUI          YAC_VCALL "+tcl+"::yacMethodGetNum                  (void) {return "+i_method+";}\n");
         s_cl.append("const char **YAC_VCALL "+tcl+"::yacMethodGetNames                (void) {static const char *r[]={"+s_cl_method_name+"}; return r;}\n");
         s_cl.append("const sUI   *YAC_VCALL "+tcl+"::yacMethodGetNumParameters        (void) {static const sUI r[]={"+s_cl_method_numparam+"}; return r;}\n");
         s_cl.append("const sUI  **YAC_VCALL "+tcl+"::yacMethodGetParameterTypes       (void) {"+s_cl_method_param_typesd+"static const sUI *r[]={"+s_cl_method_param_types+"}; return r;}\n");
         s_cl.append("const char***YAC_VCALL "+tcl+"::yacMethodGetParameterObjectTypes (void) {"+s_cl_method_param_otypesd+"static const char**r[]={"+s_cl_method_param_otypes+"}; return r;}\n");
         s_cl.append("const sUI   *YAC_VCALL "+tcl+"::yacMethodGetReturnTypes          (void) {static const sUI r[]={"+s_cl_method_return_type+"}; return r;}\n");
         s_cl.append("const char **YAC_VCALL "+tcl+"::yacMethodGetReturnObjectTypes    (void) {static const char *r[]={"+s_cl_method_return_otype+"}; return r;}\n");
         s_cl.append("const void **YAC_VCALL "+tcl+"::yacMethodGetAdr                  (void) {static const void *r[]={"+s_cl_method_adr+"}; return r;}\n");
      }
      else
      {
         s_cl.append("sUI          YAC_VCALL "+tcl+"::yacMethodGetNum                  (void) {return 1;}\n");
         s_cl.append("const char **YAC_VCALL "+tcl+"::yacMethodGetNames                (void) {static const char *r[]={\"operator\",}; return r;}\n");
         s_cl.append("const sUI   *YAC_VCALL "+tcl+"::yacMethodGetNumParameters        (void) {return {2,};\n");
         s_cl.append("const sUI  **YAC_VCALL "+tcl+"::yacMethodGetParameterTypes       (void) {static const sUI r0[]={1,3,}; return {r0,};}\n");
         s_cl.append("const char***YAC_VCALL "+tcl+"::yacMethodGetParameterObjectTypes (void) {static const char *r0[]={\"\",}; return {r0,};}\n");
         s_cl.append("const sUI   *YAC_VCALL "+tcl+"::yacMethodGetReturnTypes          (void) {return {4,};}\n");
         s_cl.append("const char **YAC_VCALL "+tcl+"::yacMethodGetReturnObjectTypes    (void) {return {\"\",};}\n");
         s_cl.append("const void **YAC_VCALL "+tcl+"::yacMethodGetAdr                  (void) {static const void*r[]={(void*)Object__operator,}; return r;}\n");
      }
	    

      if(c.constants.numElements)
      {
         String s_const;
         String s_const_name="";
         String s_const_type="";
         String s_const_value="";
         b_addsep3=false;
         int i_const=0;
         YConst co; foreach co in c.constants {
            if(b_addsep3)
            {
               s_const_name.append(", ");
               s_const_type.append(", ");
            }
            else
               b_addsep3=true;
            s_const_name.append("\""+co.name+"\"");
            s_const_type.append(String(co.type));
            if(co.type==1)
               s_const_value.append("m.si["+i_const+"]=(sSI)"+co.expr+";");
            else
               s_const_value.append("m.f4["+i_const+"]=(sF32)"+co.expr+";");
            i_const++;
         }
         s_cl.append("sUI          YAC_VCALL "+tcl+"::yacConstantGetNum                (void) {return "+i_const+";}\n");
         s_cl.append("const char **YAC_VCALL "+tcl+"::yacConstantGetNames              (void) {static const char*r[]={"+s_const_name+"}; return r;}\n");
         s_cl.append("const sUI   *YAC_VCALL "+tcl+"::yacConstantGetTypes              (void) {static const sUI r[]={"+s_const_type+"}; return r;}\n");
         s_cl.append("yacmemptr    YAC_VCALL "+tcl+"::yacConstantGetValues             (void) {static sUI r["+i_const+"]; yacmemptr m; m.ui=r; "+s_const_value+"; return m;}\n");
      }
      else
      {
         s_cl.append("sUI          YAC_VCALL "+tcl+"::yacConstantGetNum                (void) {return 0;}\n");
         s_cl.append("const char **YAC_VCALL "+tcl+"::yacConstantGetNames              (void) {static const char*r[]={\"\"}; return r;}\n");
         s_cl.append("const sUI   *YAC_VCALL "+tcl+"::yacConstantGetTypes              (void) {static const sUI r[]={0}; return r;}\n");
         s_cl.append("yacmemptr    YAC_VCALL "+tcl+"::yacConstantGetValues             (void) {static sUI r[]={0,};yacmemptr m; m.ui=(sUI*)r; return m;}\n");
      }
//      s_h.append(s_decl+"\n");
//      s_h.append(s_def);
      s_cl=s_cdecl+"\n"+s_cl+"\n"+s_cdef;

         // ---- implement wrapper methods ----
      foreach s_method in c.ordered_methods {
         c_method<=c.methods[s_method];
         s_cl.append(c_method.implementWrapper());
      }
   
      s_cl.append(s_prop_impl);

      s_cl.saveLocal("ying_"+s_group+"_"+c.scriptname+".cpp");
   }

   s_group_h.append(s_classfdecl);
   s_group_h.append(s_clid_def);

//    s_cpp.append("\nvoid YAC_Init_"+s_group+"(YAC_Host *_host) {\n\t// --------------------------------- classes ---------------------------------\n");
//    s_cpp_init.append("\n\t// --------------------------------- classes ---------------------------------\n");
   foreach scl in ordered_classes {
      c<=classes[scl];
      tcl=c.name;
         //tcl="__YAC_"+scl;
      String s_newtemp="\t   t_"+c.scriptname;
      PadSpacesUntilColumn(s_newtemp, 32);
      s_cpp_init_cl.append(s_newtemp+"=new "+yclass_typenames[c.type]+"<"+tcl+">(_host);\n");
      s_newtemp="\tclid_"+c.scriptname;
      PadSpacesUntilColumn(s_newtemp, 32);
      s_cpp_init_cl.append(s_newtemp+"=                t_"+c.scriptname+"->ctemplate.class_ID;\n");
   }
      //s_cpp_init.append("\t// -------------------------------- functions --------------------------------\n$(YAC_REGISTER_FUN)}\n");

//    s_cpp.append("\nvoid YAC_Exit_"+s_group+"(YAC_Host *_host) {\n");
      // ---- delete in reverse order ---
   int i=ordered_classes.numElements;
   while(--i>=0) {
      scl=ordered_classes[i];
      c<=classes[scl];
      s_cpp_exit.append("\tdelete t_"+c.scriptname+";\n");
   }
//    s_cpp.append("}\n");


      // ---- process "C" function bindings ----
   String s_fun="";
   String s_fun_arg_types="";
   String s_fun_arg_otypes="";
   YFunction f;
   foreach f in functions {
      s_group_h.append(f.getWrapperDecl());
      s_cpp_funwrap.append(f.implementWrapper());
      f.appendRegister(s_fun);
      f.appendRegisterArgType(s_fun_arg_types);
      f.appendRegisterArgOType(s_fun_arg_otypes);
   }
      //s_cpp_init.replace("$(YAC_REGISTER_FUN)", s_fun_arg_types+s_fun_arg_otypes+s_fun);
   s_cpp_init_fun.append(s_fun_arg_types);
   s_cpp_init_fun.append(s_fun_arg_otypes);
   s_cpp_init_fun.append(s_fun);
   if(cgroup)
   {
      cgroup.appendSources(s_group_h, s_cpp_templ, s_cpp_init_cl, s_cpp_init_fun, s_cpp_exit, s_cpp_funwrap);
   }
   else
   {
      die "[---] ying: the current group has not been set. (YG(\"mygroup\");).\n";
   }
   
   
//    s_cpp.append("\n\n#endif\n");
//    if(b_group)
//    {
//       s_cpp.saveLocal("ying_"+s_group+".cpp");
//       s_group_h.saveLocal("ying_"+s_group+".h");
//    }

   trace "\n-------------------------------------\n";//+s_cpp;

}
		  
YProperty::implementGet2() {
   String r;
   String s_prop=name;
   s_prop[0]=ucchar(s_prop[0]);
   switch(type)
   {
      case 0: r="void "; break;
      case 1: r="sSI  "; break;
      case 2: r="sF32 "; break;
      case 3: r="void*"; break;
   }
   r.append(c_class.scriptname+"__get"+s_prop);
   PadSpacesUntilColumn(r, 37);
   r.append("(void *_o");
   PadSpacesUntilColumn(r, 78);
   r.append(") {");
   switch(type)
   {
      case 1: r.append("return (sSI)"); break;
      case 2: r.append("return (sF32)"); break;
      case 3: r.append("return (void*)"); break;
   }
   r.append("(("+c_class.name+"*)_o)->"+name+";");
   r.append("}\n");
   return r;
}


// ---------------------------------------------------------------------------------------------------------

function parseInterface(String s_srcfile) {
   String s_in;
   int found_something=false;

   if(!s_in.loadLocal(s_srcfile, true))
      die "[---] ying: failed to load input file \""+s_srcfile+"\".";

   classes.free(); classes.alloc(512);
   ordered_classes.empty();
   functions.empty();
   s_group="UNTITLED";
   cgroup<=null;
   c_class<=null;
   
   b_method_defreturn=false;
    

   s_in.split('\n');
   String cl;
   int i_cl=1;
      //int b_class=false;
   int i,j;
   String st,st2,st3,st4;
    

      // ---- scan classes ----
   foreach cl in s_in {
      int nameOff;
      i = cl.indexOf("YC class",0);
      if(-1 == i)
      {
         i = cl.indexOf("YCR class", 0);

         if(-1 == i)
         {
            i = cl.indexOf("YCS class", 0);
            nameOff = 9;
         }
         else
         {
            nameOff = 9;
         }
      }
      else
      {
         nameOff = 8;
      }

      if(i != -1)
      {
            // ---- extract class name ----
         cl.substring(i+nameOff, cl.length-(i+nameOff)) => st;
         String tcl = st.splitSpace(0).getCopy(0);
         tcl.replace(":",""); // "class MyClass: public Bla"
         UnMangle(tcl); //.replace("_","");
         scanned_classes[tcl]=true;
         IP("scanned class \""+tcl+"\"");
      }
   }


   line_nr = 1;
   foreach cl in s_in {
      ListNode l,lc;
      String s_arg;
      int class_type=0;

         //Integer io; io.value=i_cl;
         //trace "["+io.printf("%3i")+"]: "+cl;
      i_cl++;
      i=cl.indexOf("YG(\"",0);
      if(i!=-1)
      {
            // ---- extract group name ----
         j=cl.indexOf("\")", i+4);
         if(j!=-1)
         {
            cl.substring(i+4, j-(i+4)) => s_group;
            trace "[...] current group set to \""+s_group+"\".";
            AddGroup();
         }
         else 
            die("[---] ying: error parsing YG(\"\") group name definition.");
      }
      else
      {
         i=cl.indexOf("YC class",0);
         if(i==-1)
         {
            i=cl.indexOf("YCS class", 0);
            if(i!=-1)
               class_type=YCLASS_STATIC; // found "YCS class" definition
            else
            {
               i=cl.indexOf("YCR class", 0);
               if(i!=-1)
               {
                  class_type=YCLASS_RESTRICTED; // found "YCR class" definition
               }
               else
               {
                  // ---- no class?
                  i=cl.indexOf("class", 0);
                  if(i!=-1)
                  {
                     String chars_before_class; 
                     cl.substring(0, i) => chars_before_class;
                     if(chars_before_class.isBlank())
                     {
                        c_class <= null;
                     }
                  }
                  i=-1; 
               }
            }
         }
         else
         {
            class_type = YCLASS_DYNAMIC; // found "YC class" definition
         }

         if(i != -1)
         {
               // ---- extract class name ----
            cl.replace("YAC_API","");
            cl.substring(i+8+(class_type!=YCLASS_DYNAMIC), cl.length-(i+8)) => st;
            s_class = st.splitSpace(1).getCopy(0);
            s_class.replace(":","");
            AddClass(class_type);
               //b_class=true;
            found_something = true;
         }
         else
         {
            cl.trim() => st;

            if(st.startsWith("YAC_POOLED"))
            {
               if(c_class != null)
               {
                  c_class.b_pooled = true;
               }
            }
            else if(st.startsWith("YF "))
            {
               c_class<=null;

               // ---- parse function ----
               // YF void YAC_CALL test(void);
               // YF int YAC_CALL test(void);
               // YF void YAC_CALL test(int _i, int _j, int _k)
               st.replace("//","");
               st.words(false);
               String s_yaccall = st.getWord(2);
               if( (s_yaccall != "YAC_CALL") && (s_yaccall != "*YAC_CALL") )
               {
                  ParseError("expected YAC_CALL in function export \"YF " +st.getWord(1) +" " + s_yaccall+"\".");
               }
               st2=st.getWord(3); 
               i=st2.indexOf("(", 0); // void name(void)
               if(i==-1)
               {
                  st2.append(st.getWord(4)); // void name (void)
                  i=st2.indexOf("(", 0);
               }
               st2.substring(0, i) => st3; //get name ("test")
               i = st.indexOf("(", 0);
               j = st.indexOf(")", 0);
               st.substring(i+1, j-(i+1)) => st4; // st4=args ("void","int")
               l <= new ListNode;
               lc <= l;
               foreach s_arg in st4.splitChar(',')
               {
                  lc <= lc.appendValue(#(String(s_arg.splitSpace(0).getCopy(0))));
               }
               AddFunction(st3, st.getWord(1), l);
               l <= 0;
               st.freeStack();
               found_something=true;
            }

            if
               (
                  c_class!=null
                  )
            {
               if(st.startsWith("YM "))
               {
                  // ---- parse method ----
                  // YM void test(void);
                  // YM int test(void);
                  // YM void test(int _i, int _j, int _k)
                  // YM YAC_ListNode * test(void)
                  st.replace("//","");
                  st.replace("YM virtual","YM ");
                  st.words(false);
                  st2=st.getWord(2); 
                  int i_name;
                  if(st2=="*")
                  {
                     st2=st.getWord(3);
                     i_name=4;
                  }
                  else
                     i_name=3;
                  i=st2.indexOf("(", 0); // void name(void)
                  if(i==-1)
                  {
                     st2.append(st.getWord(i_name)); // void name (void)
                     i=st2.indexOf("(", 0);
                  }
                  st2.substring(0, i) => st3; //get name ("test")
                  i = st.indexOf("(", 0);
                  j = st.indexOf(")", 0);
                  st.substring(i+1, j-(i+1)) => st4; // st4=args ("void","int")
                  l <= new ListNode;
                  lc <= l;
                  if(st4.isBlank())
                  {
                     ParseError("empty argument list in method "+c_class.scriptname+"::"+st3);
                  }
                  foreach s_arg in st4.splitChar(',')
                  {
                     lc <= lc.appendValue(#(String(s_arg.splitSpace(0).getCopy(0))));
                  }
                  AddMethod(st3, st.getWord(1), l);
                  l<=0;
                  st.freeStack();
               }
               else
               {
                  if(st.startsWith("YP "))
                  {
                     // ---- parse property ----
                     // YP sSI myint;
                     // YP sF32 myfloat;
                     // 
                     st.replace("//","");
                     st.words(false);
                     st2=st.getWord(2);
                     i=st2.indexOf(";", 0);
                     st2.substring(0, i) => st3;
                     AddProperty(st3, st.getWord(1));
                     st.freeStack();
                  }
                  else
                  {
                     if(st.startsWith("#define")||st.startsWith("//#define"))
                     {
                        // ---- parse constant ----
                        // #define YCI TEST_INT_CONSTANT 42
                        // //#define YCI EXPORT_INT_CONSTANT 42
                        // #define YCF TEST_INT_CONSTANT 42.42
                        // //#define YCF EXPORT_INT_CONSTANT 42.42
                        // 
                        st.words(false);
                        if(st.getWord(2)=="YCI")
                           AddConstant(st.getWord(1), st.getWord(3),1);
                        else if(st.getWord(2)=="YCF")
                           AddConstant(st.getWord(1), st.getWord(3),2);
                        st.freeStack();
                     }
                  }
               }
            } // c_class!=null
         }
      }

      // Process next line
      line_nr++;

   } // foreach cl in s_in
   s_in.freeStack();
    
   if(found_something)
	{
      CPPOut();
	    
      trace "[...] found "+classes.numElements+" classes.";
      trace ".";
      total_num_files++;
	}
}


// ---- parse all input files ----
trace "Starting to process "+inputfiles.numElements+" input file(s).";

foreach s_srcfile in inputfiles {
   trace "s_srcfile="+s_srcfile;
   parseInterface(s_srcfile);
}

// ---- write group .h and .cpp files
if(b_group)
{
   String gkey;
   foreach gkey in groups {
      YGroup g<=groups[gkey];
      g.writeGroupFiles();
   }
}


print "Found "+total_num_functions+" functions and "+total_num_methods+" method(s) in "+total_num_classes+" class(es) in "+groups.numElements+" groups in "+total_num_files+"/"+inputfiles.numElements+" input file(s).";