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

File Contents

Contents of /GTK2-2.23/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<sizeof( from ); i++ )
  {
    switch( (c=from[i]) )
    {
     case '.':
     case 'a'..'z':
     case 'A'..'Z':
     case 0300..0376: // 377 breaks some versions of gcc.
     case '_': case ' ':
       line += from[i..i];
       break;
     default:
       line += sprintf("\\%o", c );
       break;
    }
    if( sizeof( line ) > 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; i<sizeof(_s_modifiers); i++ )
      _s_modifiers[i] = Array.flatten(_s_modifiers[i])->text*"";
    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 });
}

// String literal : index number
mapping(string:int) strings = ([]);

void make_strings(array tokens)
{
  foreach(tokens; int num; array|object token)
    if( arrayp(token) )
      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);

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

        tokens[num] = Parser.Pike.Token("(pstr_vector["+strings[str]+"])");
        tokens[num+1] = Parser.Pike.Token("");
      }
      else
        SYNTAX( "_STR statement malformed.", token );
    }
}

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; i<sizeof(t); i++ )
  {
    object pp = t[i];
    string fname;
    if( sizeof(pp->text) &&
        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) )
           make_strings(body);
         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 - 2011 | Pike is a trademark of Department of Computer and Information Science, Linköping University