Home modules.gotpike.org
Username: Password: [Create Account]
[Forgot Password?]

Modules

ADT
Database
GTK2
GUI
IP
PiJAX
Public
Sql
Stdio
Subversion
System
Tools
Xosd
lua
v4l2
wx

Recent Changes

Public.Parser.XML2 1.50
Public.ZeroMQ 1.1
Public.Template.Mustache 1.0
Public.Protocols.XMPP 1.4
Sql.Provider.jdbc 1.0

Popular Downloads

Public.Parser.JSON2 1.0
Public.Parser.JSON 0.2
GTK2 2.23
Public.Web.FCGI 1.8
Public.Parser.XML2 1.48


Module Information
GTK2
Viewing contents of GTK2-2.19/build_pgtk.pike

#!/usr/bin/env pike


string destination_dir = "";

/* Start of relevant parts of program_id.h */
#define PROG_IMAGE_IMAGE_ID             100
#define PROG_IMAGE_COLORTABLE_ID        101
#define PROG_IMAGE_LAYER_ID             102
#define PROG_IMAGE_FONT_ID              103
#define PROG_IMAGE_COLOR_COLOR_ID       200
#define PROG_STDIO_FD_ID                  1

/* End of it */

#define COMPOSE(X) Parser.Pike.reconstitute_with_line_numbers( X )
#define SPLIT(X,FN) Parser.Pike.group(Parser.Pike.hide_whitespaces(Parser.Pike.tokenize(Parser.Pike.split(X),FN)))
#define GOBBLE() ((sizeof(t)>i)?t[i++]:0)
#define PEEK()    ((sizeof(t)>i)?t[i]:0)
#define SYNTAX(X,T) do{werror("\n%s:%d:\tSyntax error:\t"+replace((X),"%","%%")+"\n",T->file,T->line);exit(1);}while(0)
#define ERROR(X,T) do{werror("\n%s:%d:\tError:\t"+replace((X),"%","%%")+"\n",T->file,T->line);exit(1);}while(0)
#define WARN(X,T) ERROR(X,T)
#define SEMICOLON(X)  if(GOBBLE()!=";") SYNTAX("Missing ; after "+(X),token);
#define NEED_CLASS(X) if(!current_class) current_class = get_class_define("_global",file,(tk?tk:token)->line);

#define IDENTIFIER(X) do{                                               \
  tk=GOBBLE();                                                          \
  if(!tk||arrayp(tk)||!is_identifier(tk))                               \
    SYNTAX("Expected identifier after '"+(X)+"', got "+sprintf("%O",(arrayp(tk)?tk[0]:tk||token)->text),(arrayp(tk)?tk[0]:tk||token));                \
} while(0);

#define TYPE(X) do{                                                        \
  tk=GOBBLE();                                                             \
  if(!tk||arrayp(tk))                                                      \
    SYNTAX("Expected type after "+(X),(tk?tk[0]:token));                   \
  if( tk == "?" || tk == "*" || tk == "&" || tk == "+" )                   \
      tk = ({ tk, GOBBLE() });                                             \
  while( 1 )                                                               \
  {                                                                        \
    mixed tk2 = PEEK();                                                    \
    if(arrayp(tk2)&&tk2[0]=="(")                                           \
      tk=({tk,GOBBLE()});                                                  \
    if( tk2 == "." )                                                       \
      tk = ({ tk, GOBBLE(), GOBBLE() });                                   \
    else                                                                   \
      break;                                                               \
  }                                                                        \
  tk=parse_type(tk);                                                       \
} while(0);

string indent( string what, int amnt )
{
  if( !sizeof(what) ) return "";
  string q = (" "*amnt);
  return q+((what/"\n")*("\n"+q));
}

string make_c_string( string from )
{
  string line = "\"";
  string res = "";
  int c;
  for( int i=0; i 75 )
    {
      res += line+"\"\n";
      line="\"";
    }
  }
  return res+line+"\"";
}

string get_string_data()
{
  return "const char __pgtk_string_data[] =\n"+
         make_c_string(gbl_data)+";\n\n\n";
}

string gbl_data = "";
mapping ocache = ([]);

int data_offset( string what )
{
  int off;

  if(ocache[what])
    return ocache[what];

  if((off=search(gbl_data,what))!=-1)
  {
    if( gbl_data[ off..off+sizeof(what)-1 ] != what )
      werror( "Search returned illegal string match! %O != %O\n",
	      gbl_data[ off..off+sizeof(what)-1 ], what );
    return ocache[what]=off;
  }

  gbl_data += what;

  return data_offset( what );
}

string S( string what, int|void nul, int|void nq, int|void ind )
{
  if( nq == 2 )
    return sprintf("(PSTR+0x%x)",data_offset(what+(nul?"\0":"")));
  return sprintf("/* %O */\n%s(PSTR+0x%x)",
                 (nq?what:replace(what,"%","%%")),
                 " "*ind,
                 (data_offset(what+(nul?"\0":"")))
                );
}

string unsillycaps(string what)
{
  string res=upper_case(what[0..0]);
  if( what[0] == '_' )
  {
    res += what[1..1];
    what = what[1..];
  }
  foreach(what[1..]/"", string q)
    if(lower_case(q)==q)
      res += q;
    else
      res += "_"+lower_case(q);
  return res;
}

class Function(Class parent,
               string name,
               Type return_type,
               array(Type) arg_types,
               array(string) arg_names,
               mixed body,
               array require,
               string doc,
               string file,
               int line)
{
  static string _sprintf(int fmt)
  {
    return fmt=='O' && sprintf("Function( %O, %O )",name, return_type );
  }

  string pike_type( )
  {
    if(!return_type)
      return 0;
    string rt = return_type->pike_type( 1 );
    if( parent->name != "_global" && has_prefix(rt, "void" ) )
      rt = parent->pike_type( 1 );
    array res =  ({ });
    foreach( arg_types, Type t )
    {
      if( t->name != "null" )
	res += ({ t->pike_type( 0 ) });
    }
    return "function("+ res*"," +":"+rt+")";
  }

  string pike_name()
  {
    return name;
  }

  int is_static()
  {
    return (name=="create"||(name[0]=='_'));
  }

  string pike_add( )
  {
    if(!return_type) {
      if(parent->name == "_global")
	return "";
      else
	return sprintf("    set%s_callback(p%s);\n", name, c_name());
    }
    string type = function_type( pike_type( ) );
#ifdef EXTERMINATE_MIXED
    if( search( pike_type(), "mixed" ) != -1 )
    {
      write( "Hmm: "+parent->pike_name()+"->"+pike_name()+": "+pike_type()+
	     "\n" );
      write( "     "+(arg_types->pike_type()*",")+"\n");
      write( "     "+return_type->pike_type( 1 )+"\n");
      write( "     "+parent->pike_type( 1 )+"\n");
    }
#endif
    string res="";
    void low_do_emit( string name )
    {
      res += sprintf("    quick_add_function(%s,%d,p%s,%s,%d,\n                          "
                     "%s,OPT_EXTERNAL_DEPEND|OPT_SIDE_EFFECT);\n",
                     S(name,0,1,27), sizeof(name),
                     c_name(), S(type,0,2,27),
                     sizeof(type), (is_static()?"ID_STATIC":"0"));
    };
    array names = ({ name });
    switch( name )
    {
     case "union": names += ({ "`|" }); break;
     case "subtract": names += ({ "`-" }); break;
     case "intersect": names += ({ "`&" }); break;
     case "equal": names += ({ "`==" }); break;
     case "lt": names += ({ "`<" }); break;
     case "gt": names += ({ "`>" }); break;
     case "xor": names += ({ "`^" }); break;
     case "not": names += ({ "`~" }); break;
     case "_add": names += ({ "`+" }); break;
     case "_index": names += ({ "`[]", "`->" }); break;
    }


    foreach( names, string n ) low_do_emit( n );
    return res;
  }

  static string prefix( Class c )
  {
    if( c->c_name() == "" )
      return "pgtk";
    return "p"+c->c_name();
    
  }

  string c_name( )
  {
    string res;
    string base = parent->c_name();
    if( parent->c_name() == "" )
      res="gtk_"+name;
    else if( name == "create" )
      res=base+"_new";
    else
      res=base+"_"+name;
    return res;
  }

  string c_prototype()
  {
    return "void p"+c_name()+"( "+
      (return_type? "INT32 args" :
       (parent->name == "_global"? "" : "struct object *object"))+
      " );\n";
  }

  string c_definition()
  {
    if(!return_type)
      return c_prototype()-";"+"{\n" +
	COMPOSE( body )+"}\n";

    string res = "";
    void emit( string what )
    {
      res += what;
    };
    emit( "void p"+c_name()+"( INT32 args )\n" );
    if( body  && body != ";" )
    {
      emit( COMPOSE( body ) );
    } else {
      emit( sprintf("#line %d %O\n", line, file ) );
      emit("{\n");
      int a = 0;
      foreach( arg_types, Type t )
      {
        if( t->c_declare( a ) )
          emit( t->c_declare( a ) );
        a += t->c_stack_consumed( a );
      }

      a = 0;
      int required;
      foreach( arg_types, Type t )
      {
        a += t->c_stack_consumed( a );
        if( !t->opt ) required = a;
      }
      if( required )
        emit( "  if( args < "+required+" )\n"
              "    Pike_error("+S("Too few arguments, %d required, got %d\n",
                                  1,1,16)+",\n"
              "               "+required+", args);\n");

      a = 0;
      foreach( arg_types, Type t )
      {
        if( t->c_fetch_from_stack( a ) )
          emit( t->c_fetch_from_stack( a ) );
        a += t->c_stack_consumed( a );
      }


      /* Arguments now fetched. Time to call the function. :-) */
      int rv = a;
      if( name == "create" )
      {
        if( has_prefix( parent->name, "Gnome2" ) )
          emit("    pgtk_verify_gnome_setup();\n" );
        else
          emit("    pgtk_verify_setup();\n" );
        emit("  pgtk_verify_not_inited();\n");
        emit( "  THIS->obj =  (void *)");
      } else
        emit("  pgtk_verify_inited();\n");

      if( return_type->name != "void" )
      {
	return_type->pushed = 1;
        emit("  {\n");
        emit("  "+return_type->c_declare( rv = a+1, 1 ) );
        emit("    a"+rv+" = ");
        if( classes[ return_type->name ] )
          emit( " ("+classes[ return_type->name ]->c_type()+" *)" );
      }


      emit( "  "+c_name() + "( " );
      if( name != "create" )
        emit( parent->c_cast( "THIS->obj" ) );
      a = 0;
      array args = ({});

      foreach( arg_types, Type t )
      {
        if( t->c_pass_to_function( a ) )
          args += ({  t->c_pass_to_function( a ) });
        a += t->c_stack_consumed( a );
      }
      if( sizeof( args ) )
        emit( (name=="create"?"":", ")+(args*", ") );
      emit( " );\n");
      if( return_type->name != "void" )
      {
        emit("    my_pop_n_elems(args);\n");
        emit("  "+return_type->push( "a"+rv )+"\n");
        emit("  }\n");
      }
      else
      {
        if( name != "create" )
          emit("  RETURN_THIS();\n");
        else
        {
          emit( "  my_pop_n_elems( args );\n" );
          emit( "  push_int( 0 );\n" );
        }
      }
      a = 0;
      foreach( arg_types, Type t )
      {
        if( t->c_free( a ) )
          emit( t->c_free( a ) );
        a += t->c_stack_consumed( a );
      }
      if( name == "create" )
        emit( "  pgtk__init_object( Pike_fp->current_object );\n");
      emit("}\n\n");
    }
    return res;
  }
}

class Signal( string name )
{
  string doc = "";

  string pike_name( )
  {
    return replace( name, "-", "_" );
  }

  string pike_add( )
  {
    return "  add_string_constant( "+S("s_"+pike_name(),1,0,27)+", "+
           S(pike_name(),1,0,27)+", 0 );\n";
  }

  static string _sprintf(int fmt)
  {
    return fmt=='O' && sprintf("Signal( %O )",name );
  }
}

string function_type( string what )
{
  return __parse_pike_type( String.trim_all_whites( what ) );
}

class Member( string name, Type type, int set,
              string file, int line, Class parent )
{
  string doc = "";

  int is_static(){ return 0; }

  string pike_type( )
  {
    if( set )
      return "function("+type->pike_type(0)+":"+parent->pike_type(1)+")";
    else
      return "function(void:"+type->pike_type(1)+")";
  }

  string pike_add( )
  {
    string tp = function_type( pike_type( ) );
    if( set || classes[ type->name ] )
      return sprintf("    quick_add_function(%s,%d,p%s,\n                       %s,%d,"
                     "0,OPT_EXTERNAL_DEPEND);\n",
                     S(name,0,1,27), sizeof(name),
                     c_name(), S(tp,0,2,27), sizeof(tp));
    return sprintf("    quick_add_function(%s,%d,p%s,\n                       %s,%d,"
                   "0,OPT_EXTERNAL_DEPEND);\n",
                   S("get_"+name,0,1,27), sizeof("get_"+name),
                   c_name(), S(tp,0,2,27), sizeof(tp));
  }

  string pike_name()
  {
    return set ? "set_"+name : "get_"+name;
  }

  string c_name( )
  {
    if( parent->c_name() == "" )
      return "gtk_"+name;
    return set ? parent->c_name()+"_"+name : parent->c_name()+"_get_"+name;
  }

  string c_prototype()
  {
    return "void p"+c_name()+"( INT32 args );\n";
  }

  string c_definition()
  {
    if( set )
      return
	"void p"+c_name()+"( INT32 args )\n"
	"{\n"
	+ type->c_declare( 0 )+
	"  if( args != 1)\n"
	"    Pike_error("+S("Wrong number of arguments.\n",1,1,16)+");\n"
	+ type->c_fetch_from_stack( 0 ) +
	"  "+parent->c_cast( "THIS->obj" )+"->"+name[4..]+" = "+type->c_pass_to_function(0)+";\n"
	"  RETURN_THIS();\n"
	"}\n";
    else {
      type->pushed = 1;
      return
	"void p"+c_name()+#"( INT32 args )\n"
	"{\n"
	"  if( args )\n"
	"    Pike_error("+S("Too many arguments.\n",1,1,1)+");\n"
	+type->direct_push( parent->c_cast( "THIS->obj" ) +"->"+name )+
	"\n}\n\n";
    }
  }

  static string _sprintf(int fmt)
  {
    return fmt=='O' && sprintf("Member( %O /* %O */ )",name,type );
  }
}
  
class Property( string name, Type type, int set,
		string file, int line, Class parent )
{
  inherit Member;

  string c_definition()
  {
    string ret;
    if( set ) {
      ret="void p"+c_name()+"( INT32 args )\n{\n"
	+type->c_declare(0);
      ret+="  if( args != 1 )\n"
	"    Pike_error("+S("Wrong number of arguments.\n",1,1,16)+");\n"
	+ type->c_fetch_from_stack( 0 ) + "  ";
      ret=ret+"g_object_set(THIS->obj,\""+replace(name[4..],"_","-")+"\",";
      switch (type->name) {
	case "uint":
	case "int":
	case "double":
	case "float":
	  ret+="&";
	  break;
	default:
	  break;
      }     
      ret+=type->c_pass_to_function(0)+",NULL);\n  RETURN_THIS();\n}\n";
      return ret;
    } else {
      type->pushed = 1;
      ret="void p"+c_name()+"( INT32 args )\n{\n"
	+type->c_declare(0);
      ret=ret + "  if ( args )\n"
	"    Pike_error("+S("Too many arguments.\n",1,1,16)+");\n"
	"  g_object_get(G_OBJECT(THIS->obj),\""+replace(name,"_","-")+"\",&a0"
	",NULL);\n"
	+type->direct_push(type->c_pass_to_function(0))+
	"\n}\n\n";
      return ret;
    }
  }
}

class Type
{
  int star, amp, opt, copy, ref, plus;
  array(Type) subtypes;
  Type array_type;

  // Pushed variables aren't freed unless there's a "free" modifier.
  int pushed;
  
  string name;
  string modifiers;
  
  array _s_modifiers;

  string doc_type( )
  {
    if( subtypes )
      return subtypes->doc_type()*"|";
    string optp = "";
    if( opt )
      optp = "|void";
    switch( name )
    {
      case "uint":
	return "int"+optp;
      case "array":
	if( array_type )
	  return "array("+array_type->doc_type()+")"+optp;
	return name+optp;

      case "double": /* int can be used for better precision */
	return "int|float"+optp;

      case "callback":
	return "callback"+optp;

      case "function":
	if( has_value( get_modifiers(), "callback" ) )
	  return "callback"+optp;
	return "function"+optp;

      case "void": /* needed for return types */
	return "void";

/*
      case "GTK.CtreeNode":
	name = "GTK.CTreeNode";
*/

      default:
	if( classes[ name ] )
	  name = classes[ name ]->pike_name();
	return name+optp;
    }
  }

  string pike_type( int is, int|void nc )
  {
    if( subtypes )
      return subtypes->pike_type(is,1)*"|"+(opt?"|void":"");
    string optp = "";
    if( opt )
      optp = "|void";
    switch( name )
    {
      case "uint":
	return "int"+optp;
      case "array":
	if( !nc && !c_inited )
	  catch(c_init());
	if( array_type )
	  return "array("+array_type->pike_type(is,1)+")"+optp;
	/* fall-through */
      case "int":
      case "string":
      case "string...":
      case "mixed":
      case "mapping":
      case "float": 
      case "object":
	return name+optp;

      case "double": /* int can be used for better precision */
	return "int|float"+optp;

      case "Image.Image":
	return "object(implements "+PROG_IMAGE_IMAGE_ID+")"+optp;
      case "Stdio.File":
	return "object(implements "+PROG_STDIO_FD_ID+")"+optp;
      case "Image.Color.Color":
	return "object(implements "+PROG_IMAGE_COLOR_COLOR_ID+")"+optp;

      case "GDK2.Atom": // implemented in pike
	return "object"+optp;

      case "callback":
	return "function"+optp+",mixed"+optp;
      case "function":
	if( has_value( get_modifiers(), "callback" ) )
	  return "function"+optp+",mixed"+optp;
	return "function"+optp;
      case "void": /* needed for return types */
	return "void";

/*
      case "GTK.CtreeNode":
	name = "GTK.CTreeNode";
*/

      default:
	if( classes[ name ] )
	  if( nc || opt )
	    return classes[ name ]->pike_type(is) + optp;
	  else
	    return (is ? classes[ name ]->pike_type(1) :
		    (classes[ name ]->pike_type(0)+ "|zero"));
	throw(sprintf("Unknown type %O", this_object()));
	return "mixed"+optp;
    }
  }

  void create( string n )
  {
    array q = n/"|";
    if( sizeof(q) != 1 )
    {
      subtypes = map( q, Type );
      int ind;
      if( (has_value( subtypes->name, "void" )) )
      {
        opt = 1;
	ind=search(values(subtypes->name),"void");
        subtypes = subtypes[..ind-1] + subtypes[ind+1..];
      }
      name = subtypes[0]->name;
      modifiers = subtypes[0]->modifiers;
      star = subtypes[0]->star;
      amp = subtypes[0]->amp;
      if( sizeof( subtypes ) == 1 )
        subtypes = 0;
    }
    else
    {
      amp = sscanf( n, "&%s", n );
      star = sscanf( n, "*%s", n );
      opt = sscanf(n, "?%s", n );
      plus = sscanf(n, "+%s", n );
      name = n;
      if( sscanf( name, "%[^(](%s", name, modifiers ) == 2 )
        modifiers = modifiers[..sizeof(modifiers)-2];
      foreach( get_modifiers(), string modifier )
      {
        switch( modifier )
        {
         case "ref":
           ref = 1;
           _s_modifiers -= ({ "ref" });
           break;

         case "copy":
           copy = 1;
           _s_modifiers -= ({ "copy" });
           break;
        }
      }
    }
  }

  string _sprintf(int fmt)
  {
    if(fmt != 'O') return UNDEFINED;
    if( subtypes )
      return subtypes->_sprintf(fmt)*" | ";
    array q = get_modifiers();
    if( q == ({}) )
      return name;
    return (opt?"void|":"") + name+"( "+q*","+" )";
  }

  array get_modifiers()
  {
    if( _s_modifiers )
      return _s_modifiers;
    if( !modifiers )
      return ({});
    _s_modifiers = SPLIT(modifiers,"-")/({","});
    for( int i = 0; itext*"";
    return _s_modifiers;
  }
  
  static string declare, fetch, pass, free, _push;
  static int consumed = 1;
  static int c_inited, c_declared;
  static string array_size;
  static string _dpush;
  string direct_push( string vv )
  {
    if(!c_inited) c_init();
    if( amp )  vv = "&("+vv+")";
    if( star ) vv = "*("+vv+")";

    switch( name )
    {
     case "array":
       if( !array_size )
         throw(sprintf("Cannot push array of unknown size (%O)",
                       this_object()));
       if( !array_type )   throw("Cannot push array(mixed)"); 
       return
         "  {\n"
         "    int i;\n"
         "    for( i = 0; i<"+array_size+"; i++ )\n"
         "    "+array_type->direct_push( "("+vv+"[i])" )+"\n"
         "    f_aggregate( i );\n"
         " }\n";

     default:
       if( !copy && !ref )
         return push( vv );

       pushed = 1;
       string res ="  {\n  "+c_declare(256);
       if( copy )
         res += "    a256 = (void *)xalloc( sizeof( a256[0] ) );\n"
                "    *a256=*("+vv+");\n";
       else
         res += "    a256 = ("+vv+");\n";

       if( ref )
       {
         Class c;
         if( !(c = classes[ name ]) )
           throw(sprintf("Cannot reference %O, it's not a class type!\n",
                         array_type));
         if( c->is_gobject() )
           res+="    g_object_ref( G_OBJECT( a256 ) );\n";
         else
           res+="    "+c->c_name()+"_ref( a256 );\n";
       }
       res += "    "+push("a256")+"\n  }\n";
       return res;
    }
  }

  string push( string vv )
  {
    if (!pushed && c_declared)
      error ("Pushed type not known at declaration.\n");

    if( _push )
      return sprintf( _push, vv );

    switch( name )
    {
     case "string":
       _push = "  PGTK_PUSH_GCHAR( %s );";
       if( has_value( get_modifiers(), "free"  ) )
         _push += "\n  g_free( %[0]s );";
       return sprintf( _push, vv );

     case "int":
     case "uint":
       return sprintf( (_push = "  PGTK_PUSH_INT( %s );"), vv );

     case "float":
     case "double":
       return sprintf( (_push="  push_float( (FLOAT_TYPE)%s );"), vv );

     default:
       if( plus )
	 return sprintf( (_push="  push_gobject( %s );"), vv );
       if( classes[name] )
         return classes[name]->push( vv );
       throw(sprintf("Cannot push %O, %s is not a class", this_object(),
                     name));
    }
  }

  
  static void c_init()
  {
    c_inited = 1;
    if( subtypes )
    {
      throw(sprintf("Complex types cannot be handled automatically (%O)\n",
                    this_object()));
    }
    if( Class c = classes[ name ] )
    {
      declare = classes[name]->c_declare( -1 );
      fetch = classes[name]->c_fetch_from_stack( -1 );
      pass = classes[name]->c_pass_to_function( -1 );
    }
    else switch( name )
    {
     case "null":
       declare = 0;
       fetch = 0;
       pass = "NULL";
       consumed = 0;
       break;

     case "function":
       if( modifiers != "callback" )
       {
         throw(sprintf("Complex types cannot be handled automatically (%O)\n",
                       this_object()));
       }

       /* Fallthrough */
     case "callback": /* actually 2 args */
       consumed = 2;
       declare = ("  struct signal_data *cb%[0]d = 0;\n");
       fetch =("  cb%[0]d = (void*)xalloc( sizeof( struct signal_data ) );\n"
               "  assign_svalue_no_free(&cb%[0]d->cb,  Pike_sp+%[0]d-args);\n"
               "  assign_svalue_no_free(&cb%[0]d->args,Pike_sp+%[0]d+1-args);\n");

       pass  = ("(void *)pgtk_buttonfuncwrapper, cb%[0]d");
       break;

     case "int":
     case "uint":
       declare = "  gint a%[0]d = 0;\n";
       if( name == "uint")
         declare = " guint a%[0]d = 0;\n";
       fetch = "  a%[0]d = (gint)PGTK_GETINT(&Pike_sp[%[0]d-args]);\n";
       pass =  "a%[0]d";
       break;

     case "float":
       declare = "  gfloat a%[0]d = 0;\n";
       fetch = "  a%[0]d = (gfloat)PGTK_GETFLT(&Pike_sp[%[0]d-args]);\n";
       pass =  "a%[0]d";
       break;

     case "double":
       declare = "  gdouble a%[0]d = 0;\n";
       fetch = "  a%[0]d = (gint)PGTK_GETFLT(&Pike_sp[%[0]d-args]);\n";
       pass =  "a%[0]d";
       break;

     case "string":
       declare = " CONST gchar *a%[0]d = 0;\n";
//       declare = "  gchar *a%[0]d = 0;\n";
       fetch =
             "  if( Pike_sp[%[0]d-args].type != PIKE_T_STRING )\n"
             "    Pike_error( "+S("Illegal argument %d, expected string\n",1,0,16)+",\n                %[0]d);\n"
             "  a%[0]d = PGTK_GETSTR( &Pike_sp[%[0]d-args] );\n";
       free = "  PGTK_FREESTR( a%[0]d );\n";
       pass = "a%[0]d";
       break;

     case "array": /* Oh my. */
       {
         string sub, pt, process, check, lfree, check_size;
         int end_null, nofree, do_loopfree;
         foreach( get_modifiers(), mixed opt )
         {
           switch( opt )
           {
            case "string":
              array_type = parse_type( SPLIT("string","type") );
              sub = "gchar **a%[0]d;";
              check = "PGTK_ISSTR(&_a%[0]d->item[_i%[0]d])";
              process = "PGTK_GETSTR(&_a%[0]d->item[_i%[0]d])";
              do_loopfree = 1;
              lfree = "  PGTK_FREESTR(a%[0]d[_i%[0]d])";
              break;
            case "int":
              array_type = parse_type( SPLIT("int","type") );
              sub = "gint *a%[0]d;";
              pt = 0;
              check = "PGTK_ISINT(&_a%[0]d->item[_i%[0]d])";
              process = "(gint)PGTK_GETINT(&_a%[0]d->item[_i%[0]d])";
              break;
            case "time_t":
              array_type = parse_type( SPLIT("int","type") );
              sub = "time_t *a%[0]d;";
              pt = 0;
              check = "PGTK_ISINT(&_a%[0]d->item[_i%[0]d])";
              process = "(time_t)PGTK_GETINT(&_a%[0]d->item[_i%[0]d])";
              break;
            case "uint":
              array_type = parse_type( SPLIT("uint","type") );
              sub = "guint *a%[0]d;";
              pt = 0;
              check = "PGTK_ISINT(&_a%[0]d->item[_i%[0]d])";
              process = "(guint)PGTK_GETINT(&_a%[0]d->item[_i%[0]d])";
              break;
            case "float":
              array_type = parse_type( SPLIT("float","type") );
              sub = "gfloat *a%[0]d;";
              pt = 0;
              check = "PGTK_ISFLT(&_a%[0]d->item[_i%[0]d])";
              process = "(gfloat)PGTK_GETFLT(&_a%[0]d->item[_i%[0]d])";
              break;
            case "double":
              array_type = parse_type( SPLIT("double","type") );
              sub = "gdouble *a%[0]d;";
              pt = 0;
              check = "PGTK_ISFLT(&_a%[0]d->item[_i%[0]d])";
              process = "(gdouble)PGTK_GETFLT(&_a%[0]d->item[_i%[0]d])";
              break;
            case "null":
            case "0":
              end_null = 1;
              break;
            case "nofree":
              nofree = 1;
              break;
            default:
              if( sscanf( opt, "size=%s", array_size ) )
              {
                check_size =
                     "  if( _a%[0]d->size != "+array_size+" )\n"
                     "    Pike_error("+S("Illegal array size, wanted %d, "
                                         "got %d\n", 1, 0, 16)+", "+
                           array_size+", _a%[0]d->size );\n";
              }
              else if( has_prefix(opt, "cast" )  )
                ;
              else
              {
                if( array_type ||
                    !(array_type = parse_type( SPLIT( opt, "type" ) ) ) )
                  throw( sprintf("Unknown array option %O\n", opt) );
              }
              break;
           }
         }
//          if(!sub && !array_type)
//          {
//            throw( sprintf("Cannot push %O", this_object()) );
//          }
         declare = "  int _i%[0]d;\n  struct array *_a%[0]d = 0;\n  " +
                 "CONST "+sub+"\n";
         pass = "a%d";
         fetch =
#"
  if( Pike_sp[%[0]d-args].type != PIKE_T_ARRAY )
    Pike_error("+S("Bad argument %d, expected array\n", 1,0,16)+#",\n                %[0]d);
  _a%[0]d = Pike_sp[%[0]d-args].u.array;
"+(check_size||"")+
"  a%[0]d = g_malloc0( sizeof( a%[0]d[0] ) * (_a%[0]d->size "+
             (end_null?"+1)":")")+#");
  for( _i%[0]d = 0; _i%[0]d < _a%[0]d->size; _i%[0]d++ )
  {
    if( !"+check+#" )
    {
      free( a%[0]d );
      Pike_error( "+S("Wrong type array argument (%d).\n", 1,0,20)+#",\n                    %[0]d);\n
    }
    a%[0]d[ _i%[0]d ] = "+process+#";
  }
";
         if( !nofree )
         {
           free="";
           if( do_loopfree )
             free =
                  "  for( _i%[0]d = 0; _i%[0]d < _a%[0]d->size; _i%[0]d++ )\n"+
                  "  "+lfree+";\n";
           free += "  g_free( a%[0]d );\n";
         }
       }
       break;
     default: /* Not as bad as an array, but still. :-) */
       if( !classes[ name ] )
         throw("Cannot handle the type "+name+"\n");
       return;
    }

    if( opt ) /* optional */
    {
      fetch = "  if( args > %[0]d ) {\n"+ indent(fetch,2) +" }\n";
      if( free )
        free = "  if( args > %[0]d ) {\n"+ indent(free,2) +" }\n";
    } else if( declare )
      declare = replace( declare, " = 0", "");
      
    if( star )  pass = "*"+pass;
    if( amp )   pass = "&"+pass;
  }

  int c_stack_consumed( int a )
  {
    if( !c_inited )c_init();
    return consumed;
  }
  string c_declare( int a, int|void const )
  {
    if( !c_inited )c_init();
    c_declared = 1;
    if(!declare) return 0;
    if( const &&
	(!free || (pushed && !has_value (get_modifiers(), "free"))) )
      return sprintf( replace( declare, "CONST", "const" ), a );
    return sprintf( replace( declare, "CONST", "" ), a );
           
  }

  string c_free( int a )
  {
    if( !c_inited )c_init();
    return free  && sprintf(free, a);
  }

  string c_fetch_from_stack( int a )
  {
    if( !c_inited )c_init();
    return fetch && sprintf(fetch,a);
  }

  string c_pass_to_function( int a )
  {
    if( !c_inited )c_init();
    if( pass )
    {
      foreach( get_modifiers(), string m )
        if( classes[m] )
          return classes[m]->c_cast( sprintf(pass,a) );
        else if( sscanf( m,"cast=%s", m ) )
          return replace(m,"_"," ")+sprintf(pass,a);

      return sprintf(pass,a);
    }
  }
}

int last_class_id = 2000;

class Class( string name, string file, int line )
{
  array(Class) inherits = ({});
  mapping(string:Function) functions = ([]);
  mapping(string:Signal)   signals   = ([]);
  mapping(string:Member)   members   = ([]);
  mapping(string:Property) properties= ([]);
  string doc = "";
  Class mixin_for;

  array pre  = ({});
  string post = "";
  array init = ({});
  array exit = ({});

  string _pass;
  string _fetch;
  string _cname;
  string _cdcl;


  int _class_id;
  
  int class_id()
  {
    if( _class_id ) return _class_id;
    return (_class_id = last_class_id++);
  }

  string pike_type(int is)
  {
    if( name == "_global" )
      return "mixed";
//     return "object("+(is?"is ":"implements ")+class_id()+")";
    return "object(implements "+class_id()+")";
  }
  
  string doc_name()
  {
    return name;
  }

  string pike_name()
  {
    if( sscanf(name, "GTK2.%s", string pn ) )
      return pn;
    return (replace(replace(name,"GDK2","Gdk"),"PANGO","Pango")-".");
  }


  void create_default_sprintf( )
  {
    if( name == "_global" || mixin_for ) return 0;
    add_function( Function(this_object(),
                           "_sprintf",
                           Type("string"), ({
                             Type("int"),
                             Type("mapping")
                           }), ({
                             "flags",
                             "options",
                           }),
                           SPLIT(
                             "{\n  pgtk_default__sprintf( args, "+
                             data_offset( name )+","+sizeof(name)+
                             " );\n}\n",
                             file),
                           ({}),
                           "Not normally called directly, used by sprintf()",
                           file, line ) );
  }
  
  void create_init_exit( )
  {
    if( !sizeof(inherits) && name != "_global" )
      init = SPLIT((mixin_for?
		    "{\n  pgtk_setup_mixin( object, p"+
		    mixin_for->c_name()+"_program);\n}\n"
		    : "{\n  pgtk_clear_obj_struct( object );\n}\n"),
		   file) + init;

    if( sizeof(init) )
      add_function( Function(this_object(), "_init", 0, ({}), ({}),
			     init, ({}), "", file, line ) );
    if( sizeof(exit) )
      add_function( Function(this_object(), "_exit", 0, ({}), ({}),
			     exit, ({}), "", file, line ) );
  }

  string c_type_define()
  {
    array q = c_name()/"_";
    return upper_case( q[0]+"_TYPE_"+(q[1..]*"_") );
  }

  int is_gobject()
  {
    if( name == "G.Object" )
      return 1;
    if( sizeof(inherits) )
      return max(@inherits->is_gobject());
    return 0;
  }

  string c_name( )
  {
    if( _cname )
      return _cname;
    if( name == "_global" )  return "";
    string mn = (name/".")[0];
    string cn = (name/".")[-1];
    if( mn == cn )
      return mn;
    //    if( mn == "Gnome2" && (has_prefix( cn, "Applet" ) ) )
    //      return lower_case(unsillycaps(cn));
/*
    cn = replace( cn, ({ "GL", "GC","XML","CList","CTree" }),
                      ({ "Gl","Gc","Xml","Clist","Ctree"}) );
*/
    cn = replace( cn, ({ "GL", "GC","XML" }),
		      ({ "Gl","Gc","Xml" }) );
    _cname=(lower_case(mn)+"_"+lower_case(unsillycaps(cn)));
    _cname=replace(_cname,"gtk2","gtk");
    _cname=replace(_cname,"gdk2","gdk");
    _cname=replace(_cname,"gnome2","gnome");
    //    return _cname = (lower_case(mn)+"_"+lower_case(unsillycaps(cn)));
    return _cname;
  }

  string c_fetch_from_stack( int i )
  {
    if( !_fetch )
    {
      string n = c_name();
      if( n[1] != 'd' )
      {
        _fetch=
#"
  if( Pike_sp[ %[0]d - args ].type != PIKE_T_OBJECT )
    a%[0]d = NULL;
  else
    a%[0]d = "+c_cast("get_pgobject(Pike_sp[%[0]d-args].u.object, p"+
                      n+"_program)")+";\n";
      }
      else
      {
        string s = (name/".")[-1];
        _fetch=
#"
  if( Pike_sp[ %[0]d - args ].type != PIKE_T_OBJECT )
    a%[0]d = NULL;
  else
    a%[0]d = get_gdkobject(Pike_sp[%[0]d-args].u.object,"+lower_case(s)+");\n";
      }
    }
    if( i < 0 ) return _fetch;
    return sprintf( _fetch, i );
  }

  string c_pass_to_function( int i )
  {
    if( !_pass ) _pass = c_cast( "a%[0]d" );
    if( i < 0 ) return _pass;
    return sprintf( _pass, i );
  }

  string c_type( )
  {

    string bt = name-".";
    bt = replace(bt, "GTK2", "Gtk" );
    bt = replace(bt, "GDK2", "Gdk" );
    bt = replace(bt, "GNOME2", "Gnome" );
    bt = replace(bt, "Gnome2", "Gnome" );
    bt = replace(bt, "PANGO", "Pango" );
    bt = replace(bt, "ATK", "Atk" );
    return bt;
  }

  string c_declare( int n )
  {
    if( !_cdcl )
      _cdcl = "  "+c_type()+" *a%[0]d = 0;\n";
    if( n < 0 )  return _cdcl;
    return sprintf( _cdcl, n );
  }

  string c_cast( string x )
  {
    string f = upper_case(c_name())+"("+x+")";
    if( has_prefix(f,"GDK_") ) {
      if (search(f,"PixbufAnimation")!=-1 && search(f,"iter")==-1) {
	return "GDK_PIXBUF_ANIMATION("+x+")";
      } else if (search(f,"Pixbuf")!=-1 && search(f,"iter")==-1) {
	return "GDK_PIXBUF("+x+")";
      } else if (search(f,"Image")!=-1) {
	return "GDK_IMAGE("+x+")";
      } else if (search(f,"Pixmap")!=-1) {
	return "GDK_PIXMAP("+x+")";
      } else if (search(f,"Window")!=-1) {
	return "GDK_WINDOW("+x+")";
      } else if (search(f,"Drawable")!=-1) {
	return "GDK_DRAWABLE("+x+")";
      } else if (search(f,"Gc")!=-1) {
	return "GDK_GC("+x+")";
      } else if (search(f,"Display")!=-1) {
	return "GDK_DISPLAY_OBJECT("+x+")";
      }
      return "((Gdk"+((name-"_")/".")[1]+"*)"+x+")";
    }
    return f;
  }

  string _push;
  string push( string vv )
  {
    if( _push ) return sprintf( _push, vv );
    string mn = (name/".")[0];
    string nn = (name/".")[-1];
    switch( mn )
    {
     case "GDK2":
       _push="  push_gdkobject( %s, "+lower_case(nn)+" );";
       break;
     case "GTK2":
     case "Gnome2":
     case "Pango":
     case "ATK":
       _push="  push_gobjectclass( %s, p"+c_name()+"_program );";
       break;
    }
    return sprintf( _push, vv );
  }

  string direct_push( string x, int amp, int star )
  {
    if( amp )  x = "&("+x+")";
    if( star)  x = "*("+x+")";
    return push( x );
  }
  

  static string _sprintf(int fmt)
  {
    return fmt=='O' && sprintf("Class( %O /* %d funcs. */ )",name,
			       sizeof(functions)+sizeof(members)+
			       sizeof(properties));
	
  }

  class Ref( string file, int line, Class c ) {  }

  array(Ref) references = ({ });

  Class add_function( object f )
  {
    functions[ f->name ] = f;
    return this_object();
  }

  Class add_signal( Signal s )
  {
    signals[ s->name ] = s;
    return this_object();
  }
  
  Class add_member( Member m )
  {
    members[ m->name ] = m;
    return this_object();
  }
  
  Class add_property( Property p )
  {
    properties[ p->name ] = p;
    return this_object();
  }

  Class add_ref( string f, int l, Class c )
  {
    references += ({ Ref( f, l, c ) });
    return this_object();
  }

  Class add_init( array code )
  {
    init += code;
    return this_object();
  }

  Class add_exit( array code )
  {
    exit += code;
    return this_object();
  }
}

mapping(string:Class) classes = ([]);
mapping(string:Constant) constants = ([]);
array global_pre = ({});

class Constant( string name, Type type, string file, int line )
{
  string doc = "";

  string pike_name( )
  {
    string pn;
    if( sscanf( name, "GTK_%s", pn ) )
      return pn;
    return name;
  }

  string pike_add( )
  {
    switch( type->name )
    {
     case "string":
       return "  add_string_constant( "+S(pike_name(),1,0,30)+", "+name+", 0 );\n";
     case "int":
       return "  add_integer_constant( "+S(pike_name(),1,0,30)+", "+name+", 0 );\n";
     case "float":
       return "  add_float_constant( "+S(pike_name(),1,0,30)+", "+name+", 0 );\n";
     default:
       werror(file+":"+line+":\tCannot add constant of type %O\n",type);
       exit(1);
    }
  }

  static string _sprintf(int fmt)
  {
    return fmt=='O' && sprintf("Constant( %O /* %O */ )",name,type );
  }
}

Constant get_constant_def( string name, Type t, string file, int line )
{
  Constant res;
  if( res = constants[ name ] )
  {
    write( file+":"+line+":\tError:\tRedefining constant "+name+"\n");
    write( file+":"+line+":\tError:\tPreviously defined in "+
           res->file+":"+res->line+"\n");
    exit(1);
  }
  return constants[name] = Constant( name, t, file, line );
}

Class get_class_ref( string name, string file, int line,
                     Class p )
{
  Class res = classes[ name ];
  if(!res)
    res = classes[ name ] = Class( name, 0, 0 );
  if( p )
    return res->add_ref( file, line,p );
  return res;
}

Class get_class_define( string name, string file, int line )
{
  Class res = classes[name];
  if( res )
  {
    if( res->file )
    {
      werror("\n");
      werror(file+":"+line+"\tError: "+name+" redefined\n" );
      werror(res->file+":"+res->line+"\t       Previous definition\n" );
      exit(1);
    }
    res->file = file;
    res->line = line;
  }
  else
    res = classes[name] = Class(name,file,line);
  return res;
}

mapping(string:Type) types = ([]);
Type parse_type( mixed t )
{
  string tt;
  if(!sizeof(t))
    return 0;
  if( arrayp(t) )
  {
    t = Array.flatten(t);
    tt = t->text*"";
    t = t[0]; // only used for line-number in error messages
  }
  else
    tt = t->text;
  if(!sizeof(tt))
    SYNTAX("Expected type",t); 
  if( tt[0] == '"' ) // No types are strings...
    SYNTAX("Expected type",t); 
  if( types[tt] ) return types[tt];
  Type ty = Type( tt );
  types[tt] = ty;
  switch( ty->name )
  {
   case "int":
   case "uint":
   case "mapping":
   case "object":
   case "mixed":
   case "float":
   case "double":
   case "string":
   case "string...":
   case "null":
   case "void":
   case "function":
   case "callback":
   case "array":
   case "Stdio.File":
   case "Image.Image":
   case "Image.Color.Color":
     break;
   default:
     if( ty->name[0..0] != "G" && ty->name[0..0] != "P" && ty->name[0..0] != "A" )
       SYNTAX(sprintf("%O is not a valid type",tt),t);
  }
  return ty;
}

int is_identifier( object t )
{
  if( arrayp(t) )
    return 0;
  if( sizeof(t->text/""-
     "ab_cde1234567890fghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"/"" ) )
    return 0;
  if( t->text[0] >= '0' && t->text[0] <= '9' )
    return 0;
  return 1;
}

multiset options;
int verify_required( array r )
{
  if(!options)
    options = mkmultiset( ((Stdio.read_bytes( destination_dir+"options" )||"")
                           -" ")/"\n" );
  foreach( r, string opt )
    if( !options[ opt- " " ] )
      return 0;
  return 1;
}

array parse_args( array tokens )
{
  if(!sizeof(tokens))
    return ({ ({}), ({}) });
  array(array(string)) q = tokens / ({","});
  array types = ({});
  array names = ({});
  foreach( q, array one )
  {
    if( !sizeof( one ) )
      SYNTAX("Illegal argument definition ''", tokens[0]);
    if( one[0] == "null"  && sizeof(one) == 1 )
    {
      types += ({ parse_type( one[0] ) });
      names += ({ "undef" });
    } else {
      if( sizeof(one) < 2 )
        SYNTAX("Illegal argument definition '"+(one->text*" ")+"'", one[0] );
      types += ({ parse_type( one[..sizeof(one)-2] ) });
      if( !is_identifier( one[-1] ) )
        SYNTAX( "Expected identifier after type", (arrayp(one[-1])?one[-1][0]:one[-1]) );
      names += ({ one[-1]->text });
    }
  }
  return ({ types, names });
}

mapping(string:string) strings = ([]);

array(string) make_strings(array tokens)
{
  array ret = ({});
  foreach(tokens; int num; array|object token)
    if( arrayp(token) )
      ret += make_strings( token );
    else if( token->text == "_STR" )
    {
      if( sizeof(tokens)>num+1 && arrayp(tokens[num+1]) &&
          sizeof(tokens[num+1])==3 )
      {
        string str = tokens[num+1][1]->text;
        if( str[0] != '\"' ||
            str[-1] != '\"' )
            SYNTAX( "_STR needs a string argument.", tokens[num+1][1] );
        sscanf(str, "%O", str);
        string tok = "pstr_" + str;

        if( !strings[tok] )
          strings[tok] = str;

        ret += ({ tok });
        tokens[num] = Parser.Pike.Token(tok);
        tokens[num+1] = Parser.Pike.Token("");
      }
      else
        SYNTAX( "_STR statement malformed.", token );
    }
  return Array.uniq(ret);
}

string parse_pre_file( string file )
{
  array current_require = ({});
  array current_unrequire = ({});
  Class current_class;
  mixed current_scope;

  if( file[0] != '/' )
    file = combine_path( getcwd(), file );

  array t = SPLIT(Stdio.read_file(file),file);
  // pass 1: Find valid preprocessor macros.
  int i, have_pp;
  for( int i = 0; itext) &&
        pp->text[0] == '#' &&
        sscanf( pp->text, "#include \"%s.inc\"", fname ) )
    {
      fname = combine_path( file, "../"+fname+".inc" );
      have_pp = 1;
      string data = Stdio.read_bytes( fname );
      if(!data)
        ERROR("Failed to read "+fname, pp );
      t[i] = SPLIT( data, fname );
    }
  }
  if( have_pp )
    t = SPLIT(COMPOSE(t),file);

  i = 0;
  do
  {
    mixed type, name, args, body; // used in several cases below
    array(Type) arg_types;
    array(string) arg_names;
    mixed tk,token = GOBBLE();
    string doc = "";

    if( objectp( token ) )
    {
      switch( token->text )
      {
       case "require":
         array q = ({});
         while( (tk = GOBBLE() ) != ";" )
           q += ({ tk });
         current_require += ({ (q->text*" ") });
         continue;
	case "not":
	 q = ({});
         while( (tk = GOBBLE() ) != ";" )
           q += ({ tk });
         current_unrequire += ({ (q->text*" ") });
         continue;
       case "endrequire":
         current_require = current_require[ .. sizeof(current_require)-2 ];
         SEMICOLON("endrequire");
         continue;
       case "endnot":
         current_unrequire = current_unrequire[ .. sizeof(current_unrequire)-2 ];
         SEMICOLON("endnot");
         continue;
      }
    }
    if( !verify_required( current_require ) ||
	(sizeof( current_unrequire ) && verify_required(current_unrequire)))
      continue;

    if( objectp( token ) )
    {
      switch( token->text )
      {
       case "class":
         tk = GOBBLE();
         while( PEEK() != ";" )
           tk = ({ tk, GOBBLE() });
         if(!arrayp(tk)) tk = ({ tk });
         current_scope=current_class=
           get_class_define(Array.flatten(tk)->text*"",file,tk[-1]->line);
         SEMICOLON("class");
         break;
       case "property":
	 NEED_CLASS("property");
	 TYPE("property");  type = tk;
	 IDENTIFIER("property"); name = tk;
	 SEMICOLON("property");
	 current_class->add_property( current_scope =
				    Property( name->text, type, 0,
					    file, tk->line, current_class) );
	 break;
       case "setproperty":
	 NEED_CLASS("property");
	 TYPE("property");  type = tk;
	 IDENTIFIER("property"); name = tk;
	 SEMICOLON("setproperty");
	 current_class->add_property( current_scope =
				    Property( "set_"+name->text,type, 1,
					    file, tk->line, current_class) );
	 break;
       case "member":
         NEED_CLASS("member");
         TYPE("member");  type = tk;
         IDENTIFIER("member"); name = tk;
         SEMICOLON("member");
         current_class->add_member( current_scope =
                                    Member( name->text, type, 0,
                                            file, tk->line, current_class) );
         break;
       case "setmember":
         NEED_CLASS("member");
         TYPE("member");  type = tk;
         IDENTIFIER("member"); name = tk;
         SEMICOLON("setmember");
         current_class->add_member( current_scope =
                                    Member( "set_"+name->text,type, 1,
                                            file, tk->line, current_class) );
         break;

       case "signal":
         NEED_CLASS("signal");
         IDENTIFIER("member"); name = tk;
         SEMICOLON("signal");
         current_class->add_signal( current_scope = Signal( name->text ) );
         break;

       case "constant":
         TYPE("constant");  type = tk;
         IDENTIFIER("constant"); name = tk;
         SEMICOLON("constant");
         current_scope = get_constant_def( name->text,type,file,name->line );
//         werror("Got constant: %O %O\n",type,name);
         break;

       case "DISABLED":
         GOBBLE();
         SEMICOLON("DISABLED");
         break;
         
       case "add_global":
         tk = GOBBLE();
         if( !arrayp(tk) || tk[0] != "{" || tk[-1] != "}" )
           SYNTAX("Expected add_global { code };",token);
         SEMICOLON("add_global");
         global_pre += tk[1..sizeof(tk)-2];
         break;
         
       case "inherit":
         NEED_CLASS("inherit");
         tk = GOBBLE();
         while( PEEK() != ";" )
           tk = ({ tk, GOBBLE() });
         if(!arrayp(tk)) tk = ({ tk });
         current_class->inherits
           +=  ({ get_class_ref(Array.flatten(tk)->text*"",
                            file,
                            tk[-1]->line,
			    current_class) });
         SEMICOLON("inherit");
         break;

       case "mixin_for":
         NEED_CLASS("mixin_for");
         tk = GOBBLE();
         while( PEEK() != ";" )
           tk = ({ tk, GOBBLE() });
         if(!arrayp(tk)) tk = ({ tk });
         current_class->mixin_for
           =  get_class_ref(Array.flatten(tk)->text*"",
                            file,
                            tk[-1]->line,
			    current_class);
         SEMICOLON("mixin_for");
         break;

       case "INIT":
	 NEED_CLASS("INIT");
         tk = GOBBLE();
         if( !arrayp(tk) || tk[0] != "{" || tk[-1] != "}" )
           SYNTAX("Expected INIT { code }",token);
         current_class->add_init( tk );
	 break;

       case "EXIT":
	 NEED_CLASS("EXIT");
         tk = GOBBLE();
         if( !arrayp(tk) || tk[0] != "{" || tk[-1] != "}" )
           SYNTAX("Expected EXIT { code }",token);
         current_class->add_exit( tk );
	 break;

       case "%":
         NEED_CLASS("%{ %}");
         if( !arrayp( token = GOBBLE() ) )
           SYNTAX("Missing '{' after '%'",token);
         if( sizeof( token ) < 4 )
           SYNTAX( "Expected %{ code %}", token[0] );
         if( token[0] != "{" )
           SYNTAX( "Expected '{' after '%'", token[0] );
         if( token[-2] != "%" || token[-1] != "}" )
           SYNTAX( "Expected '%}' after '%{'", token[-1] );
         token = token[1..sizeof(token)-3];
         if( current_class )
           current_class->pre += token;
         break;

       default:
         if( token->text[..1] == "/*" ) // comment
           continue;

         if( !sizeof(token->text) )
           continue;

         if( token->text[0] == '#' )
         {
           if( has_prefix( token->text, "#line" ) )
           {
             token->text="";
             continue;
           }
           else
             SYNTAX("Unexpected preprocessor code. Use %{ %}",token );
         }
         if( sscanf( token->text, "//!%s", string doc ) )
         {
           NEED_CLASS("documentation");
           current_scope->doc += doc+"\n";
           continue;
         }

         /*
          * type identifier( type identifier, ... )
          * ;  || { code }
          * makes a function definition.
          */
         if(!current_class )
           SYNTAX("Need class before function definition\n",  token);
         i--;
         NEED_CLASS("function definition");
         TYPE("function definition");       type = tk;
         IDENTIFIER("function definition"); name = tk;
         args = GOBBLE();
         if(!arrayp(args) || args[0] != "(" || args[-1] != ")")
           SYNTAX("Expected argument array",args);
         string doc = "";
         for(;;)
         {
           body = GOBBLE();
           if(!body || arrayp(body) )
             break;
           if( body->text[..2] == "//!" )
             doc += body->text[2..]+"\n";
           else
             break;
         }

         if( !body || (arrayp(body) && (body[0] != "{" || body[-1] != "}"))
             || (objectp(body) && body!=";" ))
           SYNTAX("Expected ; or function block",
                  (arrayp(body)?body[0]:body||token));
         [arg_types,arg_names] = parse_args( args[1..sizeof(args)-2] );
         if( arrayp(body) ) {
           array strs = make_strings(body);
           if(sizeof(strs))
           {
             foreach( strs, string str )
               current_class->pre +=
                 ({ Parser.Pike.Token("extern struct pike_string * "+
                                      str+";\n") });
           }
         }
         Function f = Function( current_class, name->text, type,
                                arg_types, arg_names, body, 
                                current_require, doc, file, token->line );
         current_class->add_function( f );
         current_scope = f;
      }
    }
    else /* token is an array (group) */
    {
      SYNTAX(token[0]->text+" not expected here",token[0]);
    }
  } while( i < sizeof(t) );
}


void main(int argc, array argv)
{
  string source_dir = "source/";
  string output;
  foreach( argv[1..], string option )
  {
    if( sscanf( option, "--source=%s", option ) )
      source_dir = option;
    else if( sscanf( option, "--destination=%s", option ) )
      destination_dir = option;
    else if( option == "--help" )
    {
      write(
#" %s [--help] [--sources=directory] plugin.pike

   --help: This help
   --source: Specify another directory for the source files.
              The default is source/
   --destination: Specify another directory for the destination files.
                  The default is the current directory
", argv[0] );
      exit(1);
    }
    else
      output = option;
  }
  if( source_dir[0] != '/' )
    source_dir = combine_path( getcwd(), source_dir );
  if (source_dir[-1] != '/')
    source_dir += "/";
  if( !sizeof(destination_dir) || destination_dir[0] != '/' )
    destination_dir = combine_path( getcwd(), destination_dir );
  if (destination_dir[-1] != '/')
    destination_dir += "/";

  if(!output)
  {
    werror("You must specify an output plugin\n");
    exit(1);
  }

  add_constant( "Class", Class );
  add_constant( "unsillycaps", unsillycaps );
  add_constant( "Type", Type );
  add_constant( "S", S );
  add_constant( "data_offset", data_offset );
  add_constant( "get_string_data", get_string_data );

  object|array(object) q;
  add_constant( "Node", typeof( q ) );
  add_constant( "Function", Function );
  add_constant( "Constant", Constant );
  add_constant( "COMPOSE", Parser.Pike.reconstitute_with_line_numbers );
  object output_plugin = ((program)output)( source_dir, destination_dir,
                                            this_object() );

  if (string files = Stdio.read_file (destination_dir + "/files_to_compile")) {
    int ok = 1;
    foreach (files / " ", string file)
      if (!file_stat (destination_dir + "/" + file)) {ok = 0; break;}
    if( ok && output_plugin->up_to_date() )
    {
      exit(0);
    }
  }
  werror("Parsing input files...     ");
  float t1 = gauge {
  foreach( glob( "*.pre",get_dir( source_dir )), string f) {
    parse_pre_file( source_dir + f );
  }
  };
  Class c = get_class_ref("_global", "*.c", 0, 0);
  foreach( glob( "*.c",get_dir( source_dir )), string f)
    c->pre += SPLIT( Stdio.read_bytes( source_dir+f ),source_dir+f);

  werror("%4.1fs\n", t1);
  werror("Checking inherits ...\n");

  string print_refs( array c, string name  )
  {
    foreach( c, object r )
      werror( r->file+":"+r->line+"   "+name+" not defined\n");
  };


  int err;
  foreach( indices( classes ), string c ) {
    foreach( classes[c]->inherits, Class d )
      if( !d->line )
      {
	err++;
	print_refs( d->references, d->name );
      }
    if( classes[c]->mixin_for )
      if( !classes[c]->mixin_for->line )
      {
	err++;
	print_refs( classes[c]->mixin_for->references,
		    classes[c]->mixin_for->name );
      }
      else if( sizeof(classes[c]->inherits) )
      {
	err++;
	werror( classes[c]->file+":"+classes[c]->line+"   "+classes[c]->name+
		" is both subclass and mixin at the same time\n");
      }
      else if( sizeof(classes[c]->mixin_for->inherits) )
      {
	err++;
	werror( classes[c]->file+":"+classes[c]->line+"   mixin base class "+
		classes[c]->mixin_for->name+" is not a root class\n");
      }
  }
  if( err )
    exit(1);
  werror("Outputting result files...\n");
  float t2 = gauge {
    array files =
          output_plugin->output( classes, constants, global_pre, strings );
    if( files )
      Stdio.write_file( destination_dir+"/files_to_compile",
                        replace(files*" ",".c",".o") );
  };
  werror("Total time spent...        %4.1fs\n",t1+t2);
}


gotpike.org | Copyright © 2004 - 2019 | Pike is a trademark of Department of Computer and Information Science, Linköping University