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
Public.Parser.XML2
Viewing contents of Public_Parser_XML2-1.46/precompile.pike

#! /usr/bin/env pike

#define FUNC_OVERLOAD

#define PRECOMPILE_OVERLOAD_DEBUG

constant precompile_api_version = "2";

/*
 * This script is used to process *.cmod files into *.c files, it 
 * reads Pike style prototypes and converts them into C code.
 *
 * The input can look something like this:
 *
 * PIKECLASS fnord 
 *  attributes;
 * {
 *   INHERIT bar
 *     attributes;
 *
 *   CVAR int foo;
 *   PIKEVAR mapping m
 *     attributes;
 *
 *   DECLARE_STORAGE; // optional
 *
 *   PIKEFUN int function_name (int x, CTYPE char * foo)
 *    attribute;
 *    attribute value;
 *   {
 *     C code using 'x' and 'foo'.
 *
 *     RETURN x;
 *   }
 *
 *   INIT
 *   {
 *     // Object initialization code.
 *   }
 *
 *   EXIT
 *   {
 *     // Object cleanup code.
 *   }
 *
 *   GC_RECURSE
 *   {
 *     // Code to run under the gc recurse pass.
 *   }
 *
 *   GC_CHECK
 *   {
 *     // Code to run under the gc check pass.
 *   }
 *
 *   EXTRA
 *   {
 *     // Code for adding extra constants etc.
 *   }
 *
 *   OPTIMIZE
 *   {
 *     // Code for optimizing calls that clone the class.
 *     // The node for the call is available in the variable n.
 *   }
 * }
 *
 * All the begin_class/ADD_EFUN/ADD_FUNCTION calls will be inserted
 * instead of the word INIT in your code.
 *
 * The corresponding cleanup code will be inserted instead of the word EXIT.
 *
 * Currently, the following attributes are understood:
 *   efun;          makes this function a global constant (no value)
 *   flags;         ID_STATIC | ID_NOMASK etc.
 *   optflags;      OPT_TRY_OPTIMIZE | OPT_SIDE_EFFECT etc.
 *   optfunc;       Optimization function.
 *   type;          Override the pike type in the prototype with this type.
 *                  FIXME: this doesn't quite work
 *   rawtype;       Override the pike type in the prototype with this C-code
 *                  type, e.g. tInt, tMix etc.
 *   errname;       The name used when throwing errors.
 *   name;          The name used when doing add_function.
 *   prototype;     Ignore the function body, just add a prototype entry.
 *   program_flags; PROGRAM_USES_PARENT | PROGRAM_DESTRUCT_IMMEDIATE etc.
 *
 * POLYMORPHIC FUNCTION OVERLOADING
 *   You can define the same function several times with different
 *   types. This program will select the proper function whenever
 *   possible.
 *
 * AUTOMATIC ALLOCATION AND DEALLOCATION OF CONSTANT STRINGS
 *   You can use the syntax MK_STRING("my string") to refer to
 *   a struct pike_string with the content "my string", or the
 *   syntax MK_STRING_SVALUE("my string") for the same, but a
 *   full svalue. Note that this syntax can not be used in macros.
 *
 * BUGS/LIMITATIONS
 *  o Parenthesis must match, even within #if 0
 *  o Not all Pike types are supported yet.
 *  o No support for functions that take a variable number of arguments yet.
 *  o RETURN; (void) doesn't work yet
 *  o need a RETURN_NULL; or something.. RETURN 0; might work but may
 *    be confusing as RETURN x; will not work if x is zero.
 *  o Comments does not work inside prototypes (?)
 *
 * C ENVIRONMENT
 *  o The macro PRECOMPILE_API_VERSION is provided to be able
 *    to detect the version of precompile.pike at compile time.
 *  FIXME
 */

#define PC Parser.Pike

/* Strings declared with MK_STRING. */
mapping(string:string) strings = ([
  // From stralloc.h:
  "":"empty_pike_string",

  // From program.h:
  "this_program":"this_program_string",
  // lfuns:
  "__INIT":"lfun_strings[LFUN___INIT]",
  "create":"lfun_strings[LFUN_CREATE]",
  "destroy":"lfun_strings[LFUN_DESTROY]",
  "`+":"lfun_strings[LFUN_ADD]",
  "`-":"lfun_strings[LFUN_SUBTRACT]",
  "`&":"lfun_strings[LFUN_AND]",
  "`|":"lfun_strings[LFUN_OR]",
  "`^":"lfun_strings[LFUN_XOR]",
  "`<<":"lfun_strings[LFUN_LSH]",
  "`>>":"lfun_strings[LFUN_RSH]",
  "`*":"lfun_strings[LFUN_MULTIPLY]",
  "`/":"lfun_strings[LFUN_DIVIDE]",
  "`%":"lfun_strings[LFUN_MOD]",
  "`~":"lfun_strings[LFUN_COMPL]",
  "`==":"lfun_strings[LFUN_EQ]",
  "`<":"lfun_strings[LFUN_LT]",
  "`>":"lfun_strings[FLUN_GT]",
  "__hash":"lfun_strings[LFUN___HASH]",
  "cast":"lfun_strings[LFUN_CAST]",
  "`!":"lfun_strings[LFUN_NOT]",
  "`[]":"lfun_strings[LFUN_INDEX]",
  "`[]=":"lfun_strings[LFUN_ASSIGN_INDEX]",
  "`->":"lfun_strings[LFUN_ARROW]",
  "`->=":"lfun_strings[LFUN_ASSIGN_ARROW]",
  "_sizeof":"lfun_strings[LFUN__SIZEOF]",
  "_indices":"lfun_strings[LFUN__INDICES]",
  "_values":"lfun_strings[LFUN__VALUES]",
  "`()":"lfun_strings[LFUN_CALL]",
  "``+":"lfun_strings[LFUN_RADD]",
  "``-":"lfun_strings[LFUN_RSUBTRACT]",
  "``&":"lfun_strings[LFUN_RAND]",
  "``|":"lfun_strings[LFUN_ROR]",
  "``^":"lfun_strings[LFUN_RXOR]",
  "``<<":"lfun_strings[LFUN_RLSH]",
  "``>>":"lfun_strings[LFUN_RRSH]",
  "``*":"lfun_strings[LFUN_RMULTIPLY]",
  "``/":"lfun_strings[LFUN_RDIVIDE]",
  "``%":"lfun_strings[LFUN_RMOD]",
  "`+=":"lfun_strings[LFUN_ADD_EQ]",
  "_is_type":"lfun_strings[LFUN__IS_TYPE]",
  "_sprintf":"lfun_strings[LFUN__SPRINTF]",
  "_equal":"lfun_strings[LFUN__EQUAL]",
  "_m_delete":"lfun_strings[LFUN__M_DELETE]",
  "_get_iterator":"lfun_strings[LFUN__GET_ITERATOR]",
  "`[..]":"lfun_strings[LFUN_RANGE]",
  "_search":"lfun_strings[LFUN__SEARCH]",
]);
int last_str_id = 0;
array(string) stradd = ({});
int last_svalue_id = 0;
mapping(string:string) svalues = ([]);

string parse_string(string str)
{
  if (search(str, "\\") == -1) {
    return str[1..sizeof(str)-2];
  }
  error("String escapes not supported yet.\n");
}

string allocate_string(string orig_str)
{
  string str = parse_string(orig_str);
  string str_sym = strings[str];
  if (str_sym) return str_sym;

  if (String.width(str)>8) {
    error("Automatic allocation of wide strings with MK_STRING() or MK_STRING_SVALUE() not supported yet.\n");
  }
  int str_id = last_str_id++;
  stradd += ({
    sprintf("module_strings[%d] = \n"
	    "  make_shared_binary_string(%s,\n"
	    "                            CONSTANT_STRLEN(%s));\n",
	    str_id,
	    orig_str, orig_str),
  });
  str_sym = strings[str] = sprintf("module_strings[%d]", str_id);
  return str_sym;
}

string allocate_string_svalue(string orig_str)
{
  string str_sym = allocate_string(orig_str);
  string svalue_sym = svalues[str_sym];
  if (svalue_sym) return svalue_sym;
  int svalue_id = last_svalue_id++;
  stradd += ({
    sprintf("module_svalues[%d].type = PIKE_T_STRING;\n"
	    "module_svalues[%d].subtype = 0;\n"
	    "copy_shared_string(module_svalues[%d].u.string, %s);\n",
	    svalue_id, svalue_id, svalue_id, str_sym),
  });
  svalue_sym = svalues[str_sym] = sprintf("(module_svalues+%d)", svalue_id);
  return svalue_sym;
}

/*
 * This function takes an array of tokens containing a type
 * written in the 'array(int)' format. It returns position of
 * the first token that is not a part of the type.
 */
int parse_type(array t, int p)
{
  while(1)
  {
    while(1)
    {
      if(arrayp(t[p]))
      {
	p++;
      }else{
	switch( (string) t[p++])
	{
	  case "CTYPE":
	    p=sizeof(t)-2;
	    break;
	  case "0": case "1": case "2": case "3": case "4":
	  case "5": case "6": case "7": case "8": case "9":
	    if("=" != t[p]) break;
	  case "!":
	    continue;
	    
	  case "object":
	  case "program":
	  case "function":
	  case "mapping":
	  case "array":
	  case "multiset":
	  case "int":
	    if(arrayp(t[p])) p++;
	  case "string":
	  case "float":
	    break;
	}
      }
      break;
    }
    switch( arrayp(t[p]) ? "" : (string) t[p])
    {
      case "|":
      case "&":
	p++;
	continue;
    }
    break;
  }
  return p;
}

/*
 * This function takes an array of tokens and
 * reconstructs the string they came from. It does
 * not insert linenumbers.
 */
string merge(array x)
{
  return PC.simple_reconstitute(x);
}

/*
 * make a C identifier representation of 'n'
 */
string cquote(string|object(PC.Token) n)
{
  string ret="";
  while(sscanf((string)n,"%[a-zA-Z0-9]%c%s",string safe, int c, n)==3)
  {
    switch(c)
    {
      default: ret+=sprintf("%s_%02X",safe,c); break;
      case '+':	ret+=sprintf("%s_add",safe); break;
      case '`': ret+=sprintf("%s_backtick",safe);  break;
      case '_': ret+=sprintf("%s_",safe); break;
      case '=': ret+=sprintf("%s_eq",safe); break;
    }
  }
  ret+=n;
  if(ret[0]=='_' || (ret[0]>='0' && ret[0]<='9'))
    ret="cq_"+ret;
  return ret;
}


/*
 * This function maps C types to the approperiate
 * pike type. Input and outputs are arrays of tokens
 */
PikeType convert_ctype(array tokens)
{
  switch((string)tokens[0])
  {
    case "char": /* char* */
      if(sizeof(tokens) >1 && "*" == (string)tokens[1])
	return PikeType("string");

    case "short":
    case "int":
    case "long":
    case "size_t":
    case "ptrdiff_t":
    case "INT32":
      return PikeType("int");

    case "double":
    case "float":
      return PikeType("float");

    default:
      werror("Unknown C type.\n");
      exit(0);
  }
}

string stringify(string s)
{
  return sprintf("\"%{\\%o%}\"",(array)s);
//  return sprintf("%O",s);
}

class PikeType
{
  PC.Token t;
  array(PikeType|string|int) args=({});

  static string fiddle(string name, array(string) tmp)
    {
      while(sizeof(tmp) > 7)
      {
	int p=sizeof(tmp)-7;
	string z=name + "7(" + tmp[p..]*"," +")";
	tmp=tmp[..p];
	tmp[-1]=z;
      }
      switch(sizeof(tmp))
      {
	default:
	  return sprintf("%s%d(%s)",name,sizeof(tmp),tmp*",");
	case 2: return sprintf("%s(%s,%s)",name,tmp[0],tmp[1]);
	case 1: return tmp[0];
	case 0: return "";
      }
    }

  /*
   * return the 'one-word' description of this type
   */
  string basetype()
    {
      string ret=(string)t;
      switch(ret)
      {
	case "CTYPE":
	  return args[1]->basetype();

	case "|":
	  array(string) tmp=args->basetype() - ({"void"});
	  if(sizeof (tmp) == 1 || (sizeof (tmp) > 1 && `==(@tmp)))
	    return tmp[0];
	  return "mixed";

	case "=":
	case "&":
	  return args[-1]->basetype();

	case "!":
	case "any":
	case "0":
	case "1":
	case "2":
	case "3":
	case "4":
	case "5":
	case "6":
	case "7":
	case "8":
	case "9": return "mixed";

      case "int":
      case "float":
      case "string":
      case "object":
      case "function":
      case "array":
      case "mapping":
      case "multiset":
      case "type":
      case "program":
      case "mixed":
      case "void":
	return ret;

	default: return "object";
      }
    }

  /* Return an array of all possible basetypes for this type
   */
  array(string) basetypes()
    {
      string ret=(string)t;
      switch(ret)
      {
	case "CTYPE":
	  return args[1]->basetypes();

	case "|":
	  return `|(@args->basetypes());

	case "=":
	case "&":
	  return args[-1]->basetypes();

	case "!":
	case "any":
	case "0":
	case "1":
	case "2":
	case "3":
	case "4":
	case "5":
	case "6":
	case "7":
	case "8":
	case "9":

	case "mixed":
	  return ({
	    "int",
	    "float",
	    "string",
	    "object",
	    "program",
	    "function",
	    "array",
	    "mapping",
	    "multiset",
	    "type",
	  });
	case "program":
	  return ({
	    "object",
	    "program",
	    "function",
	  });

      case "int":
      case "float":
      case "string":
      case "object":
      case "function":
      case "array":
      case "mapping":
      case "multiset":
      case "type":
      case "void":
	return ({ ret });

	default:  return ({ "object" });
      }
    }


  /*
   * PIKE_T_INT, PIKE_T_STRING etc.
   */
  string type_number()
    {
      string btype=basetype();

      if (btype == "function") {
	btype = "mixed";
      }
      return "PIKE_T_"+upper_case(btype);
    }

  /*
   * Return 1 if this type is or matches 'void'
   */
  int may_be_void()
    {
      switch((string)t)
      {
	case "void": return 1;
	case "|":
	  for(int e=0;emay_be_void())
	      return 1;
	  return 0;

	case "=":
	case "&":
	  return args[-1]->may_be_void();
      }
    }

  string c_storage_type(int|void is_struct_entry)
    {
      string btype = /*may_be_void() ? "mixed" : */basetype();
      switch (btype)
      {
	case "void": return "void";
	case "int":
	  return may_be_void()?"struct svalue *":"INT_TYPE";
	case "float":
	  return may_be_void()?"struct svalue *":"FLOAT_TYPE";
	case "string": return "struct pike_string *";
	  
	case "array":
	case "multiset":
	case "mapping":

	case "object":
	case "program": return "struct "+btype+" *";
	  
	case "function":
	case "mixed":
	  if (is_struct_entry) {
	    return "struct svalue";
	  } else {
	    return "struct svalue *";
	  }
	default:
	  werror("Unknown type %s\n",btype);
	  exit(1);
      }
    }

  /*
   * Make a C representation, like 'tInt'
   */
  string output_c_type()
    {
      string ret=(string)t;
//      werror("FOO: %O %O\n",t,args);
      switch(ret)
      {
	case "CTYPE":
	  return (string)args[0]->t;

	case "&":
	  return fiddle("tAnd",args->output_c_type());
	  
	case "|":
	  return fiddle("tOr",args->output_c_type());

	case "!":
	  return sprintf("tNot(%s)",args[0]->output_c_type());

	case "mapping":
	  ret=sprintf("tMap(%s)",args->output_c_type()*",");
	  if(ret=="tMap(tMix,tMix)") return "tMapping";
	  return ret;

	case "multiset":
	  ret=sprintf("tSet(%s)",args[0]->output_c_type());
	  if(ret=="tSet(tMix)") return "tMultiset";
	  return ret;

	case "array":
	  ret=sprintf("tArr(%s)",args[0]->output_c_type());
	  if(ret=="tArr(tMix)") return "tArray";
	  return ret;

	case "function":
	  string t=args[..sizeof(args)-3]->output_c_type()*" ";
	  if(t == "") t="tNone";
	  if(args[-2]->output_pike_type(0) == "void")
	  {
	    return sprintf("tFunc(%s,%s)",
			   t,
			   args[-1]->output_c_type());
	  }else{
	    return sprintf("tFuncV(%s,%s,%s)",
			   t,
			   args[-2]->output_c_type(),
			   args[-1]->output_c_type());
	  }

	case "=":
	  return sprintf("tSetvar(%s,%s)",
			 (string)args[0]->t,
			 args[1]->output_c_type());

	case "0":
	case "1":
	case "2":
	case "3":
	case "4":
	case "5":
	case "6":
	case "7":
	case "8":
	case "9":       return sprintf("tVar(%s)",ret);

	case "zero":    return "tZero";
	case "void":    return "tVoid";
	case "float":   return "tFloat";
	case "string":  return "tString";
	case "program": return "tPrg(tObj)";
	case "any":     return "tAny";
	case "mixed":   return "tMix";
	case "int":
	  // NOTE! This piece of code KNOWS that PIKE_T_INT is 8!
	  return stringify(sprintf("\010%4c%4c",
				   (int)(string)(args[0]->t),
				   (int)(string)(args[1]->t)));

	case "object":  return "tObj";
	default:
	  return sprintf("tName(%O, tObjImpl_%s)",
			 ret, replace(upper_case(ret), ".", "_"));
      }
    }


  /*
   * Output pike type, such as array(int)
   */
  string output_pike_type(int pri)
    {
      string ret=(string)t;
      switch(ret)
      {
	case "CTYPE":
	  return args[1]->output_pike_type(pri);

	case "&":
	case "|":
	  ret=args->output_pike_type(1)*ret;
	  if(pri) ret="("+ret+")";
	  return ret;

	case "!":
	  return "!"+args[0]->output_pike_type(1);

	case "mapping":
	  ret=sprintf("mapping(%s:%s)",
		      args[0]->output_pike_type(0),
		      args[1]->output_pike_type(0));
	  if(ret=="mapping(mixed:mixed")
	    return "mapping";
	  return ret;

	case "multiset":
	case "array":
	{
	  string tmp=args[0]->output_pike_type(0);
	  if(tmp=="mixed") return ret;
	  return sprintf("%s(%s)",ret,tmp);
	}

	case "function":
	  array(string) tmp=args->output_pike_type(0);
	  ret=sprintf("function(%s:%s)",
		      tmp[..sizeof(tmp)-3]*","+
		      (tmp[-2]=="void"?"":tmp[-2] + "..."),
		      tmp[-1]);
	  if(ret=="function(mixed...:any)")
	    return "function";
	  return ret;

	case "=":
	  return sprintf("%s=%s",
			 args[0]->output_pike_type(0),
			 args[1]->output_pike_type(0));

	case "int":
	  ret=sprintf("int(%s..%s)",
		      args[0]->t == "-2147483648" ? "" : args[0]->t,
		      args[1]->t ==  "2147483647" ? "" : args[1]->t);
	  if(ret=="int(..)") return "int";
	  return ret;

	default:
	  return ret;
      }
    }


  /* Make a copy of this type */
  PikeType copy()
    {
      return PikeType(t, args->copy());
    }

  /* 
   * Copy and remove type assignments
   */
  PikeType copy_and_strip_type_assignments()
    {
      if("=" == (string)t)
	return args[1]->copy_and_strip_type_assignments();
      return PikeType(t, args && args->copy_and_strip_type_assignments());
    }

  string _sprintf(int how)
    {
      switch(how)
      {
	case 'O':
	  catch {
	    return sprintf("PikeType(%O)",output_pike_type(0));
	  };
	  return sprintf("PikeType(%O)",t);

	case 's':
	  return output_pike_type(0);
        case 't':
	  return "object";
      }
    }

  /*
   * Possible ways to initialize a PikeType:
   * PikeType("array(array(int))")
   * PikeType("CTYPE char *")
   * PikeType( ({ PC.Token("array"), ({ PC.Token("("),PC.Token("int"),PC.Token(")"), }) }) )
   *
   * And this way is for internal use only:
   * PikeType( PC.Token("array"), ({ PikeType("int") }) )
   */
  void create(string|array(PC.Token)|PC.Token|array(array) tok,
	      void|array(PikeType) a)
    {
      switch(sprintf("%t",tok))
      {
	case "object":
	  t=tok;
	  args=a;
	  break;

	case "string":
	  tok=convert_comments(PC.tokenize(PC.split(tok),"piketype"));
	  tok=PC.group(PC.hide_whitespaces(tok));
	  
	case "array":
	  /* strip parenthesis */
	  while(sizeof(tok) == 1 && arrayp(tok[0]))
	    tok=tok[0][1..sizeof(tok[0])-2];

	  array(array(PC.Token|array(PC.Token|array))) tmp;
	  tmp=tok/({"|"});

	  if(sizeof(tmp) >1)
	  {
	    t=PC.Token("|");
	    args=map(tmp,PikeType);
	    return;
	  }

	  tmp=tok/({"&"});
	  if(sizeof(tmp) >1)
	  {
	    t=PC.Token("&");
	    args=map(tmp,PikeType);
	    return;
	  }

	  tmp=tok/({"="});
	  if(sizeof(tmp) >1)
	  {
	    t=PC.Token("=");
	    args=map(tmp,PikeType);
	    return;
	  }

	  t=tok[0];

	  if(sizeof(tok) == 1)
	  {
	    switch((string)t)
	    {
	      case "CTYPE":
		args=({ PikeType(PC.Token(merge(tok[1..]))),
			  convert_ctype(tok[1..]) });
		break;

	      case "mapping":
		args=({PikeType("mixed"), PikeType("mixed")});
		break;
	      case "multiset":
	      case "array":
		args=({PikeType("mixed")});
		break;
	      case "int":
		string low = (string)(int)-0x80000000;
		string high = (string)0x7fffffff;
		args=({PikeType(PC.Token(low)),PikeType(PC.Token(high))});
		break;

	      case "function":
		args=({ PikeType("mixed"), PikeType("any") });
		break;
	    }
	  }else{
	    array q;
	    switch((string)t)
	    {
	      case "!":
		args=({ PikeType(tok[1..]) });
		break;

	      case "array":
	      case "multiset":
		args=({ PikeType(tok[1..]) });
		break;

	      case "mapping":
		if(arrayp(q=tok[1]))
		{
		  tmp=q[1..sizeof(q)-2]/({":"});
		  args=map(tmp,PikeType);
		}else{
		  args=({PikeType("mixed"),PikeType("mixed")});
		}
		break;
		
	      case "function":
		if(arrayp(q=tok[1]))
		{
		  tmp=q[1..sizeof(q)-2]/({":"});
		  if(sizeof(tmp)<2)
		  {
		    throw( ({
		      sprintf("%s:%d: Missing return type in function type.\n",
			      t->file||"-",
			      t->line),
			backtrace()
			}) );
		  }
		  array rettmp=tmp[1];
		  array argstmp=tmp[0]/({","});

		  int end=sizeof(argstmp)-1;
		  PikeType repeater;
		  if(sizeof(argstmp) &&
		     sizeof(argstmp[-1]) &&
		     argstmp[-1][-1]=="...")
		  {
		    repeater=PikeType(argstmp[-1][..sizeof(argstmp[-1])-2]);
		    end--;
		  }else{
		    repeater=PikeType("void");
		  }
		  args=map(argstmp[..end]-({({})}),PikeType)+
		    ({repeater, PikeType(rettmp) });
		}else{
		  args=({PikeType("mixed"),PikeType("any")});
		}
		return;
		
	      case "int":
		string low = (string)(int)-0x80000000;
		string high = (string)0x7fffffff;

		if(arrayp(q=tok[1]))
		{
		  tmp=q[1..sizeof(q)-2]/({".."});
		  /* Workaround for buggy Parser.Pike */
		  if(sizeof(tmp)==1) tmp=q[1..sizeof(q)-2]/({".. "});
		  if(sizeof(tmp[0])) low=(string)tmp[0][0];
		  if(sizeof(tmp[1])) high=(string)tmp[1][0];
		}
		args=({PikeType(PC.Token(low)),PikeType(PC.Token(high))});
		break;

	      case "object":
		if (arrayp(tok[1]) && sizeof(tok[1]) > 2) {
		  t = tok[1][1];
		}
	    }
	  }
      }
    }
};

class CType {
  inherit PikeType;
  string ctype;

  string output_c_type() { return ctype; }
  void create(string _ctype) {
    ctype = _ctype;
  }
}

/*
 * This class is used to represent one function argument
 */
class Argument
{
  /* internal */
  string _name;
  PikeType _type;
  string _c_type;
  string _basetype;
  int _is_c_type;
  int _line;
  string _file;
  string _typename;

  int is_c_type() { return _is_c_type; }
  int may_be_void() { return type()->may_be_void(); }
  int line() { return _line; }
  string name() { return _name; }
  PikeType type() { return _type; }

  string basetype()
    {
      if(_basetype) return _basetype;
      return _basetype = type()->basetype();
    }

  string c_type()
    {
      if(_c_type) return _c_type;
      return _c_type = type()->c_storage_type();
    }

  string typename()
    {
      if(_typename) return _typename;
      return _typename=
	type()->copy_and_strip_type_assignments()->output_pike_type(0);
    }

  void create(array x)
    {
      _name=(string)x[-1];
      _file=x[-1]->file;
      _line=x[-1]->line;
      _type=PikeType(x[..sizeof(x)-2]);
    }

  string _sprintf(int how)
    {
      return type()->output_pike_type(0)+" "+name();
    }
};

/*
 * This function takes a bunch of strings an makes
 * a unique C identifier with underscores in between.
 */
string mkname(string ... parts)
{
  return map(parts - ({"",0}), cquote) * "_";
}

mapping(string:int) names = ([]);

/*
 * Variant of mkname that always returns a unique name.
 */
string make_unique_name(string ... parts)
{
  string id = mkname(@parts);
  if (names[id]) {
    int i = 2;
    while (names[id + "_" + i]) i++;
    id += "_" + i;
  }
  names[id] = 1;
  return id;
}


/*
 * Create C code for popping 'howmany' arguments from
 * the stack. 'howmany' may be a constant or an expression.
 */
string make_pop(mixed howmany)
{
  switch(howmany)
  {
    default:
      return "pop_n_elems("+howmany+");";
      
    case "0": case 0: return "";
    case "1": case 1: return "pop_stack();";
  }
}

/* Fixme:
 * This routine inserts non-tokenized strings into the data, which
 * can confuse a later stage, we might need to do something about that.
 * However, I need a *simple* way of doing it first...
 * -Hubbe
 */
array fix_return(array body, PikeType rettype, mixed args)
{
  int pos=0;
  
  while( (pos=search(body,PC.Token("RETURN",0),pos)) != -1)
  {
    int pos2=search(body,PC.Token(";",0),pos+1);
    body[pos]=sprintf("do { %s ret_=(",rettype->c_storage_type());
    body[pos2]=sprintf("); %s push_%s(ret_); return; }while(0);",
		       make_pop(args),
		       rettype->basetype());
    pos=pos2+1;
  }

  pos=0;
  while( (pos=search(body,PC.Token("REF_RETURN",0),pos)) != -1)
  {
    int pos2=search(body,PC.Token(";",0),pos+1);
    body[pos]=sprintf("do { %s ret_=(",rettype->c_storage_type());
    body[pos2]=sprintf("); add_ref(ret_); %s push_%s(ret_); return; }while(0);",
		       make_pop(args),
		       rettype->basetype());
    pos=pos2+1;
  }
  return body;
}


// Workaround for bug in F_RECUR in some sub-versions of Pike7.1.34.
function(mixed,array,mixed ...:array) low_recursive = recursive;

array recursive(mixed func, array data, mixed ... args)
{
  array ret=({});

  foreach(data, mixed foo)
    {
      if(arrayp(foo))
      {
	ret+=({ low_recursive(func, foo, @args) });
      }else{
	ret+=({ foo });
      }
    }

  return func(ret, @args);
}

// FIXME: We could expand this to declare which attributes are valid
// where.
constant valid_attributes = (<
  "efun",
  "flags",
  "optflags",
  "optfunc",
  "type",
  "rawtype",
  "errname",
  "name",
  "prototype",
  "program_flags",
>);

/*
 * This function takes a list of tokens, containing attributes on the form:
 *   attributename foo bar gazonk ;
 *   attributename2 foo2 bar2 gazonk2 ;
 *
 * Returns a mapping like:
 * ([
 *   "attributename":"foo bar gazonk",
 *   "attributename2":"foo2 bar2 gazonk2",
 *   "attrflagname":1,
 * ])
 */
mapping parse_attributes(array attr, void|string location)
{
  mapping attributes=([]);
  foreach(attr/ ({";"}), attr)
    {
      while(sizeof(attr) && has_prefix((string)attr[0], "/*"))
	attr = attr[1..];
      switch(sizeof(attr))
      {
	case 0: continue;
	case 1:
	  if(arrayp(attr[0]))
	  {
	    werror("%s:%d: Expected attribute name!\n",
		   attr[0][0]->file,
		   attr[0][0]->line);
	    if(location)
	      werror("%s: This is where the attributes belong\n", location);
	    werror("This is what I got: %O\n", attr[0]);
	    exit(1);
	  }
	  attributes[(string)attr[0]]=1;
	  break;
	default:
	  array tmp=attr[1..];
	  if(sizeof(tmp) == 1 && arrayp(tmp[0]) && tmp[0][0]=="(")
	    tmp=tmp[0][1..sizeof(tmp[0])-2];
	  
	  attributes[(string)attr[0]]=merge(tmp);
      }

      if(!valid_attributes[(string)attr[0]]) {
	werror("%s:%d: Invalid attribute name %O.\n",
	       attr[0]->file, attr[0]->line, (string)attr[0]);
	exit(1);
      }
    }

  if(attributes->optfunc && !attributes->efun) {
    werror("Only efuns may have an optfunc.\n");
    exit(1);
  }

  return attributes;
}

/*
 * Generate an #ifdef/#else/#endif
 */
array IFDEF(string|array(string) define,
	    array yes,
	    void|array no)
{
  array ret=({});
  if(!arrayp(define)) define=({define});
  if(!yes || !sizeof(yes))
  {
    if(!no || !sizeof(no)) return ({"\n"});
    if(sizeof(define)==1)
    {
      ret+=({ sprintf("\n#ifndef %s\n",define[0]) });
    }else{
      ret+=({ sprintf("\n#if !defined(%s)\n",define*") && !defined(") });
    }
  }else{
    if(sizeof(define)==1)
    {
      ret+=({ sprintf("\n#ifdef %s\n",define[0]) });
    }else{
      ret+=({ sprintf("\n#if defined(%s)\n",define*") || defined(") });
    }
    ret+=yes;
    if(no && sizeof(no))
    {
      ret+=({sprintf("\n#else /* %s */\n",define*", ")});
      ret+=no;
    }
  }
  ret+=no || ({});
  ret+=({sprintf("\n#endif /* %s */\n",define*", ")});
  return ret;
}

/*
 * Generate a #define
 */
array DEFINE(string define, void|string as)
{
  string tmp=define;
  sscanf(tmp,"%s(",tmp);

  return ({
    sprintf("\n#undef %s\n",tmp),
    sprintf("#define %s%s\n",define, as?" "+as:"")
      });
}


#ifdef FUNC_OVERLOAD
class FuncData
{
  string name;
  string define;
  PikeType type;
  mapping attributes;
  int max_args;
  int min_args;
  array(Argument) args;

  string _sprintf()
    {
      return sprintf("FuncData(%s)",define);
    }
  
  int `==(mixed q)
    {
      return objectp(q) && q->name == name;
//      return 0;
    }
};

// Returns the number of non-empty equvivalence classes in q.
int evaluate_method(mixed q)
{
  int val=0;
  q=values(q) - ({ ({}) });
#ifdef PRECOMPILE_OVERLOAD_DEBUG
  werror("evaluate: %O\n",q);
#endif /* PRECOMPILE_OVERLOAD_DEBUG */
  for(int e=0;edefine, ({
      PC.Token(sprintf("%*n%s(args);\n",indent,mkname("f",d[0]->name))),
	PC.Token(sprintf("%*nreturn;\n",indent)),
	}))+
      ({ PC.Token(sprintf("%*nbreak;\n",indent)) });
  }

#ifdef PRECOMPILE_OVERLOAD_DEBUG
  werror("generate_overload_func_for(%O, %d, %d, %d, %O, %O)...\n",
	 d, indent, min_possible_arg, max_possible_arg, name, attributes);
#endif /* PRECOMPILE_OVERLOAD_DEBUG */

  array out=({});

  /* This part should be recursive */
  array(array(FuncData)) x=allocate(256,({}));
  int min_args=0x7fffffff;
  int max_args=0;
  foreach(d, FuncData q)
    {
#ifdef PRECOMPILE_OVERLOAD_DEBUG
      werror("LOOP: q:%O, min_args:%O, max_args:%O\n",
	     q, q->min_args, q->max_args);
#endif /* PRECOMPILE_OVERLOAD_DEBUG */
      int low = max(min_possible_arg, q->min_args);
      int high = min(max_possible_arg, q->max_args);
      for(int a = low; a <= min(high, 255); a++)
	x[a]+=({q});
      min_args=min(min_args, low);
      max_args=max(max_args, high);
    }

  min_args=max(min_args, min_possible_arg);
  max_args=min(max_args, max_possible_arg);

#ifdef PRECOMPILE_OVERLOAD_DEBUG
  werror("MIN: %O\n",min_args);
  werror("MAX: %O\n",max_args);
#endif /* PRECOMPILE_OVERLOAD_DEBUG */

  string argbase="-args";

  int best_method;
  int best_method_value;

  array(mapping(string:array(FuncData))) y;
  if(min_args>=0)
  {
    y=allocate(min(min_args,16), ([]));
    for(int a=0;aargs[a]->type()->basetypes()*"|");
#endif /* PRECOMPILE_OVERLOAD_DEBUG */
	  foreach(q->args[a]->type()->basetypes(), string t)
	  {
	    if (t != "void") {
	      if(!y[a][t]) y[a][t]=({});
	      y[a][t]+=({q});
	    }
	  }
	}
    }

    best_method=-1;
    best_method_value=evaluate_method(x);
#ifdef PRECOMPILE_OVERLOAD_DEBUG
    werror("Value X: %d\n",best_method_value);
#endif /* PRECOMPILE_OVERLOAD_DEBUG */
    
    for(int a=0;abest_method_value)
      {
	best_method=a;
	best_method_value=v;
      }
    }
  }

#ifdef PRECOMPILE_OVERLOAD_DEBUG
  werror("Best method=%d\n",best_method);
#endif /* PRECOMPILE_OVERLOAD_DEBUG */

  if(best_method == -1)
  {
    /* Switch on number of arguments */
    out+=({PC.Token(sprintf("%*nswitch(args) {\n",indent))});
    for(int a=min_args;a<=max_args;a++)
    {
      array tmp=x[a];
      if(tmp && sizeof(tmp))
      {
	int d;
	for(d=a;derrname || name,
			 best_method+1,
			 indices(m2)*"|")),
	PC.Token(sprintf("%*n}\n",indent)),
	});
  }
  return out;
}



#endif

// Parses a block of cmod code, separating it into declarations,
// functions to add, exit functions and other code.
class ParseBlock
{
  array code=({});
  array addfuncs=({});
  array exitfuncs=({});
  array declarations=({});

  void create(array(array|PC.Token) x, string base)
    {
      array(array|PC.Token) ret=({});
      array thestruct=({});

      // FIXME: Consider generating code in the order it appears in
      //        the source file.
      //	/grubba 2004-12-10

      int e;
      for(e = 0; e < sizeof(x); e++) {
	array|PC.Token t;
	if (arrayp(t = x[e])) {
	  ret += ({ t });
	  continue;
	}
	switch((string)t) {
	default:
	  ret += ({ t });
	  break;
	case "INHERIT":
	  {
	    int pos=search(x,PC.Token(";",0),e);
	    mixed name=x[e+1];
	    string define=make_unique_name("inherit",name,base,"defined");
	    mapping attributes = parse_attributes(x[e+2..pos]);
	    addfuncs +=
	      IFDEF(define,
		    ({
		      PC.Token(sprintf("  low_inherit(%s, NULL, -1, 0, %s, NULL);",
				       mkname((string)name, "program"),
				       attributes->flags || "0"),
			       x[e]->line),
		    }));
	    ret += DEFINE(define);
	    e = pos;
	    break;
	  }
	case "EXTRA":
	  {
	    string define = make_unique_name("extra",base,"defined");
	    addfuncs += IFDEF(define, x[e+1]);
	    ret += DEFINE(define);
	    e++;
	    break;
	  }
	case "PIKECLASS":
	  {
	    int p;
	    for(p=e+1;pdeclarations;
	    ret+=subclass->code;

	    addfuncs+=
	      IFDEF(define,
		    ({
		      IFDEF("PROG_"+upper_case(lname)+"_ID",
			    ({
			      PC.Token(sprintf("  START_NEW_PROGRAM_ID(%s);\n",
					       upper_case(lname)),
				       proto[0]->line),
			      "#else\n",
			      PC.Token("  start_new_program();\n",
				       proto[0]->line),
			    })),
		      IFDEF("tObjImpl_"+upper_case(lname),
			    0,
			    DEFINE("tObjImpl_"+upper_case(lname), "tObj")),
		    })+
		    subclass->addfuncs+
		    ({
		      attributes->program_flags?
		      PC.Token(sprintf("  Pike_compiler->new_program->flags |= %s;\n",
				     attributes->program_flags),
			       proto[1]->line):"",
		      PC.Token(sprintf("  %s=end_program();\n",program_var),
			       proto[0]->line),
		      PC.Token(sprintf("  %s_fun_num=add_program_constant(%O,%s,%s);\n",
				       program_var,
				       name,
				       program_var,
				       attributes->flags || "0"),
			       proto[0]->line),
		    })
		    );
	    exitfuncs+=
	      IFDEF(define,
		    subclass->exitfuncs+
		    ({
		      sprintf("  if(%s) {\n", program_var),
		      PC.Token(sprintf("    free_program(%s);\n", program_var),
			       proto[0]->line),
		      sprintf("    %s=0;\n"
			      "  }\n",
			      program_var),
		    }));
	    e = p;
	    break;
	  }

	case "PIKEVAR":
	  {
	    int pos = search(x, PC.Token(";",0), e);
	    int pos2 = parse_type(x, e+1);
	    mixed name = x[pos2];
	    PikeType type = PikeType(x[e+1..pos2-1]);
	    string define = make_unique_name("var",name,base,"defined");
	    mapping attributes = parse_attributes(x[pos2+1..pos]);

//    werror("type: %O\n",type);

	    thestruct+=
	      IFDEF(define,
		    ({ sprintf("  %s %s;\n",type->c_storage_type(1),name) }));
	    addfuncs+=
	      IFDEF(define,
		    ({
		      sprintf("  PIKE_MAP_VARIABLE(%O, %s_storage_offset + OFFSETOF(%s_struct, %s),\n"
			      "                    %s, %s, %s);",
			      (string)name, base, base, name,
			      type->output_c_type(), type->type_number(),
			      attributes->flags || "0"),
		    }));
	    ret+=DEFINE(define);
	    ret+=({ PC.Token("DECLARE_STORAGE") });
	    e = pos;
	    break;
	  }

	case "CVAR":
	  {
	    int pos = search(x,PC.Token(";",0),e);
	    int npos=pos-1;
	    while(arrayp(x[npos])) npos--;
	    mixed name=(string)x[npos];

	    string define=make_unique_name("var",name,base,"defined");
    
	    thestruct+=IFDEF(define,x[e+1..pos-1]+({";"}));
	    ret+=DEFINE(define);
	    ret+=({ PC.Token("DECLARE_STORAGE") });
	    e = pos;
	    break;
	  }

	case "INIT":
	case "EXIT":
	case "GC_RECURSE":
	case "GC_CHECK":
	case "OPTIMIZE":
	  {
	    ret += ({
	      PC.Token("PIKE_INTERNAL"),
	      PC.Token(lower_case((string)t)),
	    });
	    break;
	  }
	}
      }      

      x=ret/({"PIKE_INTERNAL"});
      ret=x[0];

      string ev_handler_define=make_unique_name(base,"event","handler","defined");
      string opt_callback_define=make_unique_name(base,"optimize","callback","defined");
      int opt_callback;
      array ev_handler=({});
      for(int f=1;fcurrent_storage))",structname);

	/* FIXME:
	 * Add runtime debug to these defines...
	 * Add defines for parents when possible
	 */
	declarations=
	  	  DEFINE("THIS",this)+   // FIXME: we should 'pop' this define later
	  DEFINE("THIS_"+upper_case(base),this)+
	  DEFINE("OBJ2_"+upper_case(base)+"(o)",
		 sprintf("((struct %s *)(o->storage+%s_storage_offset))",
			 structname, base))+
	  DEFINE("GET_"+upper_case(base)+"_STORAGE",
		 sprintf("((struct %s *)(o->storage+%s_storage_offset)",
			 structname, base))+
	    ({
	      sprintf("static ptrdiff_t %s_storage_offset;\n",base),
		sprintf("struct %s {\n",structname),
		})+thestruct+({
		  "};\n",
		    })
	  +declarations;
	addfuncs = ({
	  IFDEF("THIS_"+upper_case(base), ({
		  PC.Token(sprintf("  %s_storage_offset = "
				   "ADD_STORAGE(struct %s);",
				   base, structname)),
		})),
	}) + addfuncs;
      }

      x=ret/({"DECLARE_STORAGE"});
      ret=x[..sizeof(x)-2]*({})+declarations+x[-1];
      declarations=({});

      x=ret/({"PIKEFUN"});
      ret=x[0];

#ifdef FUNC_OVERLOAD
      mapping(string:array(FuncData)) name_data=([]);
      mapping(string:int) name_occurances=([]);

      for(int f=1;ffile||"-",
		 proto[p][0]->line);
	  exit(1);
	}
	string name=(string)proto[p];
	name_occurances[name]++;
      }
#endif

      for(int f=1;ffile||"-",
		 proto[p][0]->line);
	  exit(1);
	}
	string location=proto[p]->file+":"+proto[p]->line;
	string name=(string)proto[p];
	array args_tmp=proto[p+1];

	mapping attributes=parse_attributes(proto[p+2..],location);
#ifdef FUNC_OVERLOAD
	string common_name=name;
	if(!attributes->errname)
	  attributes->errname=name;
	if(name_occurances[common_name]>1)
	  name+="_"+(++name_occurances[common_name+".cnt"]);
#endif

	args_tmp=args_tmp[1..sizeof(args_tmp)-2];
	if(sizeof(args_tmp))
	{
	  args_tmp/=({","});
	}else{
	  args_tmp=({});
	}

	string funcname=mkname("f",base,name);
	string define=make_unique_name("f",base,name,"defined");
	string func_num=mkname("f", base,name,"fun_num");

//    werror("FIX RETURN: %O\n",body);
    
//    werror("name=%s\n",name);
//    werror("  rettype=%O\n",rettype);
//    werror("  args=%O\n",args);

	ret+=({
	  sprintf("#define %s\n", define),
	});

	if (!attributes->efun) {
	  ret += ({
	    sprintf("ptrdiff_t %s = 0;\n", func_num),
	  });
	}

	// werror("%O %O\n",proto,args);
	int last_argument_repeats;
	if(sizeof(args_tmp) && 
	   !arrayp(args_tmp[-1][-2]) &&
	   "..." == (string)args_tmp[-1][-2])
	{
	  last_argument_repeats++;
	  args_tmp[-1]=args_tmp[-1][..sizeof(args_tmp[-1])-3]+({
	    args_tmp[-1][-1]});
	}
	array(Argument) args=map(args_tmp,Argument);
	// werror("%O %O\n",proto,args);
	// werror("parsed args: %O\n", args);

	if((<"`<", "`>", "`==", "_equal">)[name])
	{
	  if(sizeof(args) != 1)
	  {
	    werror("%s must take one argument.\n");
	    exit(1);
	  }
	  if(sprintf("%s",args[0]->type()) != "mixed")
	  {
	    werror("%s:%s must take a mixed argument (was declared as %s)\n",
		   location, name, args[0]->type());
	    exit(1);
	  }
	}

	// FIXME: support ... types
	PikeType type;

	if(attributes->rawtype)
	  type = CType(attributes->rawtype);
	else if(attributes->type)
	{
	  mixed err=catch {
	    type=PikeType(attributes->type);
	  };
	  if(err)
	  {
	    werror("%s: Something went wrong when parsing type: %s\n",
		   location,
		   attributes->type);
	    throw(err);
	  }
	}else{
	  if(last_argument_repeats)
	  {
	    type = PikeType(PC.Token("function"),
			    args[..sizeof(args)-2]->type() +
			    ({ args[-1]->type(), rettype }) );
	  }else{
	    type = PikeType(PC.Token("function"),
			    args->type() + ({ PikeType("void"), rettype }) );
	  }
	}

//	werror("NAME: %O\n",name);
//	werror("type: %O\n", type);
//	werror("C type: %O\n", type->output_c_type());

	if (attributes->prototype) {
	  funcname = "NULL";
	} else {
	  ret+=({
	    sprintf("void %s(INT32 args) ",funcname),
	    "{","\n",
	  });

	  int min_args=sizeof(args);
	  int max_args=sizeof(args);
	  int repeat_arg = -1;

	  if(last_argument_repeats)
	  {
	    min_args--;
	    max_args=0x7fffffff;
	    repeat_arg = min_args;
	    args[-1]->_c_type = "struct svalue *";
	  }

	  while(min_args>0 && args[min_args-1]->may_be_void())
	    min_args--;

	  foreach(args, Argument arg)
	    ret+=({
	      PC.Token(sprintf("%s %s;\n",arg->c_type(), arg->name()),
		       arg->line()),
	    });


	  int argnum;

	  argnum=0;
	  string argbase;
	  string num_arguments;
	  if(min_args == max_args)
	  {
	    ret+=({
	      PC.Token(sprintf("if(args != %d) "
			       "wrong_number_of_args_error(%O,args,%d);\n",
			       sizeof(args),
			       attributes->errname || name,
			       sizeof(args)), proto[0]->line)
	    });
	    argbase=(string) (-sizeof(args));
	    num_arguments=(string)sizeof(args);
	  }else{
	    argbase="-args";
	    num_arguments="args";
	    if(min_args > 0) {
	      ret+=({
		PC.Token(sprintf("if(args < %d) "
				 "wrong_number_of_args_error(%O,args,%d);\n",
				 min_args,
				 name,
				 min_args), proto[0]->line)
	      });
	    }

	    if(max_args != 0x7fffffff && max_args != -1) {
	      ret+=({
		PC.Token(sprintf("if(args > %d) "
				 "wrong_number_of_args_error(%O,args,%d);\n",
				 max_args,
				 name,
				 max_args), proto[0]->line)
	      });
	    }
	  }

	  string check_argbase = argbase;

	  foreach(args, Argument arg)
	  {
	    if (argnum == repeat_arg) {
	      // Begin the argcnt loop.
	      check_argbase = "+argcnt"+argbase;
	      ret += ({ PC.Token(sprintf("if (args > %d) {\n"
					 "  INT32 argcnt = 0;\n"
					 "  do {\n"
					 "    dmalloc_touch_svalue(Pike_sp%+d%s);\n",
					 argnum, argnum, check_argbase),
				 arg->line()) });
	    }

	    else if(arg->may_be_void())
	    {
	      if ((arg->basetype() == "int") || (arg->basetype() == "mixed")) {
		ret+=({
		  PC.Token(sprintf("if (args > %d) {",argnum)),
		});
	      } else {
		ret+=({
		  PC.Token(sprintf("if ((args > %d) && \n"
				   "    ((Pike_sp[%d%s].type != PIKE_T_INT) ||\n"
				   "     (Pike_sp[%d%s].u.integer))) {",
				   argnum,
				   argnum, check_argbase,
				   argnum, check_argbase)),
		});
	      }
	    }

	    if(arg->is_c_type() && arg->basetype() == "string")
	    {
	      /* Special case for 'char *' */
	      /* This will have to be amended when we want to support
	       * wide strings
	       */
	      ret+=({
		PC.Token(sprintf("if(Pike_sp[%d%s].type != PIKE_T_STRING || Pike_sp[%d%s].ustring -> width)",
				 argnum,check_argbase,
				 argnum,check_argbase,
				 /*upper_case(arg->basetype())*/),arg->line())
	      });
	    } else {

	      switch(arg->basetype())
	      {
	      default:
		ret+=({
		  PC.Token(sprintf("if(Pike_sp[%d%s].type != PIKE_T_%s)",
				   argnum,check_argbase,
				   upper_case(arg->basetype())),arg->line())
		});
		break;

	      case "program":
		ret+=({
		  PC.Token(sprintf("if(!(%sprogram_from_svalue(Pike_sp%+d%s)))",
				   (arg->c_type() == "struct program *" ?
				    arg->name() + "=" : ""),
				   argnum,check_argbase),
			   arg->line())
		});
		break;

	      case "mixed":
	      }
	    }
	    switch(arg->basetype())
	    {
	    default:
	      ret+=({
		PC.Token(sprintf(" SIMPLE_BAD_ARG_ERROR(%O,%d%s,%O);\n",
				 attributes->errname || attributes->name || name,
				 argnum+1,
				 (argnum == repeat_arg)?"+argcnt":"",
				 arg->typename()),arg->line()),
	      });

	    case "mixed":
	    }

	    if (argnum == repeat_arg) {
	      // End the argcnt loop.
	      ret += ({ PC.Token(sprintf ("  } while (++argcnt < %s-%d);\n"
					  "  %s=Pike_sp%+d%s;\n"
					  "} else %s=0;\n",
					  num_arguments, argnum,
					  arg->name(), argnum, argbase,
					  arg->name()),
				 arg->line()) });
	    }

	    else {
	      switch(arg->c_type())
	      {
		case "INT_TYPE":
		  ret+=({
		    sprintf("%s=Pike_sp[%d%s].u.integer;\n",arg->name(),
			    argnum,argbase)
		  });
		  break;

		case "FLOAT_TYPE":
		  ret+=({
		    sprintf("%s=Pike_sp[%d%s].u.float_number;\n",
			    arg->name(),
			    argnum,argbase)
		  });
		  break;

		case "struct svalue *":
		  ret+=({
		    PC.Token(sprintf("%s=Pike_sp%+d%s; dmalloc_touch_svalue(Pike_sp%+d%s);\n",
				     arg->name(),
				     argnum,argbase,argnum,argbase),arg->line()),
		  });
		  break;

		case "struct program *":
		  // Program arguments are assigned directly in the check above.
		  break;

		default:
		  if(arg->is_c_type() && arg->basetype() == "string")
		  {
		    /* some sort of 'char *' */
		    /* This will have to be amended when we want to support
		     * wide strings
		     */
		    ret+=({
		      PC.Token(sprintf("%s=Pike_sp[%d%s].u.string->str; debug_malloc_touch(Pike_sp[%d%s].u.string)\n",
				       arg->name(),
				       argnum,argbase,
				       argnum,argbase),arg->line())
		    });
	      
		  }else{
		    ret+=({
		      PC.Token(sprintf("debug_malloc_pass(%s=Pike_sp[%d%s].u.%s);\n",
				       arg->name(),
				       argnum,argbase,
				       arg->basetype()),arg->line())
		    });
		  }

		case "program":
	      }

	      if(arg->may_be_void())
	      {
		ret+=({  PC.Token(sprintf("} else %s=0;\n", arg->name())) });
	      }
	    }

	    argnum++;
	  }
    
	  body=recursive(fix_return,body,rettype, num_arguments); 
	  if(sizeof(body))
	    ret+=({body});
	  ret+=({ "}\n" });

#ifdef FUNC_OVERLOAD
	  if(name_occurances[common_name] > 1)
	  {
	    FuncData d=FuncData();
	    d->name=name;
	    d->define=define;
	    d->type=type;
	    d->attributes=attributes;
	    d->max_args=max_args;
	    d->min_args=min_args;
	    d->args=args;
	    name_data[common_name]=( name_data[common_name] || ({}) ) + ({d});

	    if(name_occurances[common_name]!=name_occurances[common_name+".cnt"])
	    {
	      ret+=rest;
	      continue;
	    }
	    array(FuncData) tmp=map(name_data[common_name],
				    lambda(FuncData fun) {
				      fun->name = mkname(base, fun->name);
				      return fun;
				    });
	    /* Generate real funcname here */
	    name=common_name;
	    funcname=mkname("f",base,common_name);
	    define=make_unique_name("f",base,common_name,"defined");
	    func_num=mkname(base,funcname,"fun_num");
	    array(string) defines=({});
	  
	    type=PikeType(PC.Token("|"), tmp->type);
	    attributes=`|(@ tmp->attributes);

	    array out=generate_overload_func_for(tmp,
						 2,
						 0,
						 0x7fffffff,
						 common_name,
						 attributes);
	  

	    /* FIXME: This definition should be added
	     * somewhere outside of all #ifdefs really!
	     * -Hubbe
	     */
	    ret+=IFDEF(tmp->define, ({
	      sprintf("#define %s\n",define),
	      sprintf("ptrdiff_t %s = 0;\n", func_num),
	      sprintf("void %s(INT32 args) ",funcname),
	      "{\n",
	    })+out+({
	      "}\n",
	    }));
	  
	  }
#endif
	}
	ret+=rest;
	

	if (attributes->efun) {
	  if(attributes->optfunc)
	    addfuncs+=IFDEF(define,({
	      PC.Token(sprintf("  ADD_EFUN2(%O, %s, %s, %s, %s, 0);\n",
			       attributes->name || name,
			       funcname,
			       type->output_c_type(),
			       (attributes->optflags)|| "0",
			       attributes->optfunc
			       ), proto[0]->line),
	    }));
	  else
	    addfuncs+=IFDEF(define,({
	      PC.Token(sprintf("  ADD_EFUN(%O, %s, %s, %s);\n",
			       attributes->name || name,
			       funcname,
			       type->output_c_type(),
			       (attributes->optflags)|| "0"
			       ), proto[0]->line),
	    }));
	} else {
	  addfuncs+=IFDEF(define, ({
	    PC.Token(sprintf("  %s =\n", func_num)),
	    PC.Token(sprintf("    ADD_FUNCTION2(%O, %s, %s, %s, %s);\n",
			     attributes->name || name,
			     funcname,
			     type->output_c_type(),
			     attributes->flags || "0" ,
			     attributes->optflags ||
			     "OPT_EXTERNAL_DEPEND|OPT_SIDE_EFFECT"
			     ),proto[0]->line),
	  }));
	}
      }

      code=ret;
    }
}

array(PC.Token) convert_comments(array(PC.Token) tokens)
{
  // Filter AutoDoc mk II, and convert other C++ comments to C-style.
  array new = ({});
  array autodoc;

  foreach(tokens, PC.Token token) {
    if(has_prefix((string)token, "//!") ||
       has_prefix((string)token, "/*!")) {
      if(!autodoc)
	autodoc = ({});
      autodoc += ({ ((string)token)[3..] });
    }
    else {
      if(autodoc) {
	new += ({ PC.Token("/*!" + autodoc*"\n *!" ) });
	autodoc = 0;
      }
      if(has_prefix((string)token, "//"))
	new += ({ PC.Token("/*" + ((string)token)[2..] + " */") });
      else
	new += ({ token });
    }
  }

  return new;
}

array(PC.Token) allocate_strings(array(PC.Token) tokens)
{
  int i = -1;

  while ((i = search(tokens, PC.Token("MK_STRING"), i+1)) != -1) {
    // werror("MK_STRING found: %O\n", tokens[i..i+10]);
    if (arrayp(tokens[i+1]) && (sizeof(tokens[i+1]) == 3)) {
      tokens[i] = PC.Token(allocate_string((string)tokens[i+1][1]));
      tokens = tokens[..i] + tokens[i+2..];
    }
  }
  while ((i = search(tokens, PC.Token("MK_STRING_SVALUE"), i+1)) != -1) {
    // werror("MK_STRING_SVALUE found: %O\n", tokens[i..i+10]);
    if (arrayp(tokens[i+1]) && (sizeof(tokens[i+1]) == 3)) {
      tokens[i] = PC.Token(allocate_string_svalue((string)tokens[i+1][1]));
      tokens = tokens[..i] + tokens[i+2..];
    }
  }
  return tokens;
}

int main(int argc, array(string) argv)
{
  mixed x;

  string file = argv[1];
  x=Stdio.read_file(file);
  x=PC.split(x);
  x=PC.tokenize(x,file);
  x = convert_comments(x);
  x=PC.hide_whitespaces(x);
  x=PC.group(x);

  x = ({
    sprintf("/* Generated from %O by precompile.pike\n"
	    " *\n"
	    " * Do NOT edit this file.\n"
	    " */\n",
	    argv[1]),
  }) + DEFINE("PRECOMPILE_API_VERSION", (string)precompile_api_version) + ({
    "\n\n",
  }) + x;

//  werror("%O\n",x);

  x = recursive(allocate_strings, x);

  ParseBlock tmp=ParseBlock(x,"");

  if (last_str_id) {
    // Add code for allocation and deallocation of the strings.
    tmp->addfuncs = stradd + tmp->addfuncs;
    tmp->exitfuncs += ({
      sprintf("{\n"
	      "  int i;\n"
	      "  for(i=0; i < %d; i++) {\n"
	      "    if (module_strings[i]) free_string(module_strings[i]);\n"
	      "    module_strings[i] = NULL;\n"
	      "  }\n"
	      "}\n",
	      last_str_id),
    });
    tmp->declarations = ({
      sprintf("static struct pike_string *module_strings[%d] = {\n"
	      "%s};\n",
	      last_str_id, "  NULL,\n"*last_str_id),
    });

    // Same for svalues.
    // NOTE: This code needs changing in several aspects if
    //       support for other svalues than strings is added.
    if (last_svalue_id) {
      tmp->exitfuncs += ({
	sprintf("free_svalues(module_svalues, %d, BIT_STRING);\n",
		last_svalue_id),
      });
      tmp->declarations += ({
	sprintf("static struct svalue module_svalues[%d];\n",
		last_svalue_id),
      });
    }
  }

  x=tmp->code;
  x=recursive(replace,x,PC.Token("INIT",0),tmp->addfuncs);
  int need_init;
  if ((need_init = equal(x, tmp->code)))
  {
    // No INIT, add our own stuff..

    x+=({
      "PIKE_MODULE_INIT {\n",
      tmp->addfuncs,
      "}\n",
    });
  }
  tmp->code = x;
  x=recursive(replace,x,PC.Token("EXIT",0),tmp->exitfuncs);
  if(equal(x, tmp->code))
  {
    // No EXIT, add our own stuff..

    x+=({
      "PIKE_MODULE_EXIT {\n",
      tmp->exitfuncs,
      "}\n",
    });

    if (!need_init) {
      werror("Warning: INIT without EXIT. Added PIKE_MODULE_EXIT.\n");
    }
  } else if (need_init) {
    werror("Warning: EXIT without INIT. Added PIKE_MODULE_INIT.\n");
  }
  tmp->code = x;

  // FIXME: This line is fishy; there is no optfuncs in ParseBlock.
  x=recursive(replace,x,PC.Token("OPTIMIZE",0),tmp->optfuncs);

  x=recursive(replace,x,PC.Token("DECLARATIONS",0),tmp->declarations);

  if(equal(x,tmp->code))
  {
    // No OPTIMIZE / DECLARATIONS
    // FIXME: Add our own stuff...
    // NOTA BENE: DECLARATIONS are not handled automatically
    //            on the file level
  }
  if(getenv("PIKE_DEBUG_PRECOMPILER"))
    write(PC.simple_reconstitute(x));
  else
    write(PC.reconstitute_with_line_numbers(x));
}


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