diff --git a/lib/modules/Tools.pmod/Standalone.pmod/precompile.pike b/lib/modules/Tools.pmod/Standalone.pmod/precompile.pike new file mode 100644 index 0000000000000000000000000000000000000000..86634411e9861d9d2055903f5bb74f182bebb37d --- /dev/null +++ b/lib/modules/Tools.pmod/Standalone.pmod/precompile.pike @@ -0,0 +1,2667 @@ +#! /usr/bin/env pike + +#define FUNC_OVERLOAD + +constant precompile_api_version = "2"; + +constant want_args = 1; + +constant description = "Converts .pmod-files to .c files"; +string usage = #"[options] <from> > <to> + + + 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 + gc_trivial; + { + // 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. + + Argument count checks are added both for too many (unless the last + arg is varargs) and too args. The too many args check can be + disabled by putting \", ...\" last in the argument list. + + Magic types in function argument lists: + + bignum A native int or bignum object. The C storage type is + struct svalue. + + longest A native int or bignum object which is range checked and stored + in a LONGEST. + + zero If used like \"zero|Something\" then a zero value is allowed in + addition to Something (which is how it works by default in pike + code). This will not cause an svalue to be constructed on the C + level, rather 0/NULL is assigned to the C type. + + Note that this is almost the same as how void works in + \"void|Something\", except that the argument still is required. + + \"zero\" interacts with \"void\" in a special way: If you write + \"void|int\" then the C storage will be an svalue to make it + possible to tell a missing argument from a zero integer. If you + write \"void|zero|int\" then it's assumed that you don't care + about that, and so the C storage will be an INT_TYPE which will + be assigned zero both if the argument is zero and when it's left + out. This applies to the int and float runtime types, but not + for the special case \"void|zero\". + + Note: If a function argument accepts more than one runtime type, e.g. + \"int|string\", then no type checking at all is performed on the svalue. That + is not a bug - the user code have to do a type check later anyway, and it's + more efficient to add the error handling there. + + 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. + gc_trivial; Only valid for EXIT functions. This instructs the gc that + the EXIT function is trivial and that it's ok to destruct + objects of this program in any order. In general, if there + is an EXIT function, the gc has to take the same care as if + there is a destroy function. But if the EXIT function + doesn't mind that arbitrary referenced pike objects gets + destructed before this one (which is very common), then + this attribute can be added to make the gc work a little + easier. + + 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 at compile time. +"; + + +#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 "bignum": + case "longest": + 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 ""; + } + } + + static array(PikeType) strip_zero_alt() + /* Assumes this is an '|' node. Returns args without any 'zero' + * alternatives. */ + { + if (!args) return 0; + array(PikeType) a = ({}); + foreach (args, PikeType arg) + if (arg->basetype() != "zero") a += ({arg}); + if (!sizeof (a) && sizeof (args)) + // Make sure we don't strip away the entire type. + a = ({args[0]}); + return a; + } + + /* + * return the 'one-word' description of this type + */ + string basetype() + { + string ret=(string)t; + switch(ret) + { + case "CTYPE": + return args[1]->basetype(); + + case "|": { + // If there's only one type except "void" and "zero" then return it, + // else return "mixed". Plus some special cases if we only got "void" + // and "zero" in there. + int got_zero; + string single_type; + foreach (args, PikeType arg) { + string type = arg->basetype(); + if (type == "zero") + got_zero = 1; + else if (type != "void") { + if (single_type && single_type != type) + return "mixed"; + else + single_type = type; + } + } + if (single_type) return single_type; + else if (got_zero) return "zero"; + else return "void"; + } + + 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": + case "bignum": + return "mixed"; + + // FIXME: Guess the special "longest" type ought to be + // "CTYPE LONGEST" instead, but the CTYPE stuff seems to be + // broken and I don't want to dig into it.. /mast + case "longest": return "LONGEST"; + + case "int": + case "float": + case "string": + case "object": + case "function": + case "array": + case "mapping": + case "multiset": + case "type": + case "program": + case "mixed": + case "void": + case "zero": + return ret; + + default: return "object"; + } + } + + string realtype() + /* Similar to basetype, but returns the source description if the + * base type. */ + { + string ts = (string) t; + switch (ts) { + case "bignum": + case "longest": + return ts; + default: + return basetype(); + } + } + + /* 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": + case "zero": + return ({ ret }); + + case "bignum": + case "longest": + return ({"int", "object"}); + + 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); + } + + /* + * Check if this type matches 'void' and/or 'zero'. + */ + int may_be_void_or_zero (int(0..) may_be_void, int(0..) may_be_zero) + { + switch((string)t) + { + case "zero": return may_be_zero; + case "void": return may_be_void; + case "|": + return max (0, @args->may_be_void_or_zero (may_be_void, may_be_zero)); + + case "=": + case "&": + return args[-1]->may_be_void_or_zero (may_be_void, may_be_zero); + } + return 0; + } + + int may_be_void() // Compat wrapper. + { + return may_be_void_or_zero (1, 0); + } + + string c_storage_type(int|void is_struct_entry) + { + string btype = realtype(); + switch (btype) + { + case "void": return "void"; + case "zero": + return may_be_void()?"struct svalue *":"INT_TYPE"; + case "int": + return (may_be_void_or_zero (1, 2) == 1 ? + "struct svalue *" : "INT_TYPE"); + case "float": + return (may_be_void_or_zero (1, 2) == 1 ? + "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": + case "bignum": + if (is_struct_entry) { + return "struct svalue"; + } else { + return "struct svalue *"; + } + + case "longest": + return "LONGEST"; + + default: + werror("Unknown type %s\n",btype); + exit(1); + } + } + + /* + * Make a C representation, like 'tInt'. Can also strip off an + * "|zero" alternative on the top level. + */ + string output_c_type (void|int ignore_zero_alt) + { + 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 "|": + array(PikeType) a = strip_zero_alt(); + if (sizeof (a) == 1) return a[0]->output_c_type(); + return fiddle("tOr",a->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 (ignore_zero_alt)); + + 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 "bignum": + case "longest": + return "tInt"; + + 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; + + case "bignum": + case "longest": + return "int"; + + default: + return ret; + } + } + + + /* Make a copy of this type */ + PikeType copy() + { + return PikeType(t, args->copy()); + } + + /* + * Copy and remove type assignments. Can also strip off an "|zero" + * alternative on the top level. + */ + PikeType copy_and_strip_type_assignments (void|int ignore_zero_alt) + { + if("=" == (string)t) + return args[1]->copy_and_strip_type_assignments (ignore_zero_alt); + array(PikeType) a; + if (args && ignore_zero_alt && (string) t == "|") + a = strip_zero_alt(); + else + a = args; + return PikeType(t, a && a->copy_and_strip_type_assignments (0)); + } + + 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; + string _realtype; + + int is_c_type() { return _is_c_type; } + int may_be_void_or_zero (int may_be_void, int may_be_zero) + { return type()->may_be_void_or_zero (may_be_void, may_be_zero); } + 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 realtype() + { + if (_realtype) return _realtype; + return _realtype = type()->realtype(); + } + + 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 (1)->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, + void|multiset really_valid_attributes) +{ + 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(!(really_valid_attributes || 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;e<sizeof(q);e++) + { + if(q[e] && sizeof(q[e])) + { + val++; + for(int d=e+1;d<sizeof(q);d++) + { + if(!sizeof(q[e] ^ q[d])) /* equal is broken in some Pikes */ + { +#ifdef PRECOMPILE_OVERLOAD_DEBUG + werror("EQ, %d %d\n",e,d); +#endif /* PRECOMPILE_OVERLOAD_DEBUG */ + q[d]=0; + } + } + } + } + return val; +} + +array generate_overload_func_for(array(FuncData) d, + int indent, + int min_possible_arg, + int max_possible_arg, + string name, + mapping attributes) +{ + if(sizeof(d)==1) + { + return IFDEF(d[0]->define, ({ + 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) + { + y=allocate(min(min_args,16), ([])); + for(int a=0;a<sizeof(y);a++) + { + foreach(d, FuncData q) + { +#ifdef PRECOMPILE_OVERLOAD_DEBUG + werror("BT: %s\n",q->args[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;a<sizeof(y);a++) + { + int v=evaluate_method(y[a]); +#ifdef PRECOMPILE_OVERLOAD_DEBUG + werror("Value %d: %d\n",a,v); +#endif /* PRECOMPILE_OVERLOAD_DEBUG */ + if(v>best_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;d<sizeof(x);d++) + { + if(equal(tmp, x[d]) && !sizeof(tmp ^ x[d])) + { + out+=({sprintf("%*n case %d:\n",indent,d) }); + x[d]=0; + }else{ + break; + } + } +#ifdef PRECOMPILE_OVERLOAD_DEBUG + werror("Generating code for %d..%d arguments.\n", a, d-1); +#endif /* PRECOMPILE_OVERLOAD_DEBUG */ + out+=generate_overload_func_for(tmp, + indent+2, + a, + d-1, + name, + attributes); + } + } + out+=({ + PC.Token(sprintf("%*n default:\n",indent)), + PC.Token(sprintf("%*n wrong_number_of_args_error(%O,args,%d);\n", + indent, + name, + min_args)), + PC.Token(sprintf("%*n}\n",indent)), + }); + }else{ + /* Switch on an argument */ + /* First check that we have at least that many arguments (if needed) */ + + if(min_possible_arg < best_method+1) + { + out+=({ + PC.Token(sprintf("%*nif(args < %d) wrong_number_of_args_error(%O,args,%d);\n", + indent, + best_method+1, + name, + min_args)) + }); + min_possible_arg=best_method; + } + + if(min_possible_arg == max_possible_arg) + argbase=(string) (-min_possible_arg); + + out+=({PC.Token(sprintf("%*nswitch(Pike_sp[%d%s].type) {\n", + indent, + best_method,argbase)) }); + + mapping m=y[best_method]; + mapping m2=m+([]); + foreach(indices(m), string type) + { + array tmp=m[type]; + if(tmp && sizeof(tmp)) + { + foreach(indices(m), string t2) + { + if(equal(tmp, m[t2]) && !sizeof(tmp ^ m[t2])) + { + m_delete(m,t2); + out+=({PC.Token(sprintf("%*n case PIKE_T_%s:\n", + indent, + upper_case(t2)))}); + } + } + out+=generate_overload_func_for(tmp, + indent+2, + min_possible_arg, + max_possible_arg, + name, + attributes); + } + } + out+=({ + PC.Token(sprintf("%*n default:\n",indent)), + PC.Token(sprintf("%*n SIMPLE_BAD_ARG_ERROR(%O,%d,%O);\n", + indent, + attributes->errname || 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 + + string gc_live_obj_define = make_unique_name (base, "gc", "live", "obj"); + + 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;p<sizeof(x);p++) + if(arrayp(x[p]) && x[p][0]=="{") + break; + + array proto = x[e+1..p-1]; + array body = x[p]; + + string name=(string)proto[0]; + string lname = mkname(base, name); + mapping attributes=parse_attributes(proto[1..]); + + ParseBlock subclass = ParseBlock(body[1..sizeof(body)-2], + mkname(base, name)); + string program_var = mkname(base, name, "program"); + + string define = make_unique_name("class", base, name, "defined"); + + ret+=DEFINE(define); + // FIXME: The struct program variable should probably default + // to being static. + // /grubba 2004-10-23 + ret+=({sprintf("struct program *%s=NULL;\n" + "static int %s_fun_num=-1;\n", + program_var, program_var)}); + ret+=subclass->declarations; + 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 "EXIT": { + int p; + for(p=e+1;p<sizeof(x);p++) + if(arrayp(x[p]) && x[p][0]=="{") + break; + + mapping attributes = + parse_attributes (x[e+1..p-1], 0, (<"gc_trivial">)); + + if (!attributes->gc_trivial) + ret += DEFINE (gc_live_obj_define); + } + // Fall through. + + case "INIT": + 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;f<sizeof(x);f++) + { + array func=x[f]; + int p; + for(p=0;p<sizeof(func);p++) + if(arrayp(func[p]) && func[p][0]=="{") + break; + + array body=func[p]; + array rest=func[p+1..]; +// werror("%O\n",func); + string name=(string)func[0]; + string funcname=mkname(name,base,"struct"); + string define=make_unique_name("internal",name,base,"defined"); + ret+=DEFINE(define); + if (name == "optimize") { + opt_callback = 1; + ret += DEFINE(opt_callback_define); + ret += ({ PC.Token(sprintf("static node *%s(node *n)\n", + funcname)) }); + } else { + ret+=DEFINE(ev_handler_define); + ret+=({ PC.Token(sprintf("static void %s(void)\n",funcname)) }); + ev_handler+=IFDEF(define,({ + sprintf(" case PROG_EVENT_%s: %s(); break;\n", + upper_case(name), + funcname) + })); + } + ret+=body; + ret+=rest; + } + if(sizeof(ev_handler)) + { + string funcname=mkname(base,"event","handler"); + ret+=IFDEF(ev_handler_define, + ({ + sprintf("static void %s(int ev) {\n",funcname), + " switch(ev) {\n" + })+ + ev_handler+ + ({ " default: break; \n" + " }\n}\n" })); + + addfuncs+= + IFDEF(ev_handler_define, + ({ PC.Token(sprintf(" pike_set_prog_event_callback(%s);\n",funcname)) }) + + IFDEF (gc_live_obj_define, + 0, + ({PC.Token (" Pike_compiler->new_program->flags &= ~PROGRAM_LIVE_OBJ;\n")})) + ); + } + if (opt_callback) { + addfuncs += + IFDEF(opt_callback_define, + ({ PC.Token(sprintf(" pike_set_prog_optimize_callback(%s);\n", + mkname("optimize",base,"struct"))) })); + } + + if(sizeof(thestruct)) + { + /* We insert the struct definition after the last + * variable definition + */ + string structname = base+"_struct"; + string this=sprintf("((struct %s *)(Pike_interpreter.frame_pointer->current_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", + sprintf (#"\ +#ifdef PIKE_DEBUG +/* Ensure the struct is used in a variable declaration, or else gdb might not see it. */ +struct %s *%s_gdb_dummy_ptr; +#endif\n", structname, base), + }) + +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;f<sizeof(x);f++) + { + array func=x[f]; + int p; + for(p=0;p<sizeof(func);p++) + if(arrayp(func[p]) && func[p][0]=="{") + break; + + array proto=func[..p-1]; + p=parse_type(proto,0); + if(arrayp(proto[p])) + { + werror("%s:%d: Missing return type?\n", + proto[p][0]->file||"-", + proto[p][0]->line); + exit(1); + } + string name=(string)proto[p]; + name_occurances[name]++; + } +#endif + + for(int f=1;f<sizeof(x);f++) + { + array func=x[f]; + int p; + for(p=0;p<sizeof(func);p++) + if(arrayp(func[p]) && func[p][0]=="{") + break; + + array proto=func[..p-1]; + array body=func[p]; + array rest=func[p+1..]; + + // Not all versions of Pike allow Token objects + // to be indexed. + catch { + foreach(Array.flatten(proto), string t) + if(has_prefix((string)t, "/*!")) + body = ({t})+body; + }; + + p=parse_type(proto,0); + PikeType rettype=PikeType(proto[..p-1]); + + if(arrayp(proto[p])) + { + werror("%s:%d: Missing type?\n", + proto[p][0]->file||"-", + 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, ignore_more_args; + if (sizeof(args_tmp)) { + array last_arg = args_tmp[-1]; + if (sizeof (last_arg) > 1) { + if (!arrayp(last_arg[-2]) && "..." == (string)last_arg[-2]) { + last_argument_repeats = 1; + args_tmp[-1] = last_arg[..sizeof(last_arg)-3] + ({last_arg[-1]}); + } + } + else + if (!arrayp(last_arg[0]) && "..." == (string)last_arg[0]) { + ignore_more_args = 1; + args_tmp = args_tmp[..sizeof (args_tmp) - 2]; + } + } + 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 *"; + } + else if (ignore_more_args) + max_args = 0x7fffffff; + + 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) + { + int got_void_or_zero_check = 0; + + 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 { + int void_or_zero = arg->may_be_void_or_zero (2, 1); + if (void_or_zero == 2) { + if (!(<"int","mixed">)[arg->basetype()]) { + ret+=({ + PC.Token(sprintf("if (args > %d &&" + " (Pike_sp[%d%s].type != PIKE_T_INT ||" + " Pike_sp[%d%s].u.integer)) {\n", + argnum, + argnum, check_argbase, + argnum, check_argbase), arg->line()), + }); + } else { + ret+=({ + PC.Token(sprintf("if (args > %d) {\n",argnum), arg->line()), + }); + } + got_void_or_zero_check = 1; + } + + else if (void_or_zero == 1) { + if (!(<"int", "mixed">)[arg->basetype()]) { + ret += ({ + PC.Token (sprintf ("if (Pike_sp[%d%s].type != PIKE_T_INT ||" + " Pike_sp[%d%s].u.integer) {\n", + argnum, check_argbase, + argnum, check_argbase), arg->line()), + }); + got_void_or_zero_check = 1; + } + } + } + + check_arg: { + 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].u.string->shift_size)", + argnum,check_argbase, + argnum,check_argbase), arg->line()) + }); + } else { + + switch(arg->realtype()) + { + 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 "longest": + case "bignum": + // Note that for "longest" we always accept a bignum + // object, even if LONGEST isn't longer than INT_TYPE. + // That way we get a more natural error below if the + // bignum is too large (i.e. "Integer too large" + // instead of "Expected int, got object"). + ret += ({ + PC.Token ( + sprintf ("if (Pike_sp[%d%s].type != PIKE_T_INT", + argnum, check_argbase), + arg->line()), + "\n#ifdef AUTO_BIGNUM\n", + PC.Token ( + sprintf (" && !is_bignum_object_in_svalue (Pike_sp%+d%s)", + argnum, check_argbase), + arg->line()), + "\n#endif\n", + PC.Token (")", arg->line()), + }); + break; + + case "mixed": + break check_arg; + } + } + + ret+=({ + PC.Token(sprintf(" SIMPLE_ARG_TYPE_ERROR(%O,%d%s,%O);\n", + attributes->errname || attributes->name || name, + argnum+1, + (argnum == repeat_arg)?"+argcnt":"", + arg->typename()),arg->line()), + }); + } + + 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+=({ + PC.Token (sprintf("%s=Pike_sp[%d%s].u.integer;\n", + arg->name(), argnum,argbase), + arg->line()) + }); + break; + + case "FLOAT_TYPE": + ret+=({ + PC.Token (sprintf("%s=Pike_sp[%d%s].u.float_number;\n", + arg->name(), argnum,argbase), + arg->line()) + }); + 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; + + case "LONGEST": + ret += ({ + "\n#ifdef AUTO_BIGNUM\n", + PC.Token ( + sprintf ("if (Pike_sp[%d%s].type == PIKE_T_INT)\n", + argnum, argbase), + arg->line()), + PC.Token ( + sprintf (" %s = Pike_sp[%d%s].u.integer;\n", + arg->name(), argnum, argbase), + arg->line()), + PC.Token ("else", arg->line()), + "\n#if SIZEOF_LONGEST > SIZEOF_INT_TYPE\n", + PC.Token ( + sprintf ("if (!int64_from_bignum (&(%s), " + "Pike_sp[%d%s].u.object))\n", + arg->name(), argnum, argbase), + arg->line()), + "\n#endif\n", + PC.Token ( + sprintf (" SIMPLE_ARG_ERROR (%O, %d," + " \"Integer too large.\");\n", + attributes->errname || attributes->name || name, + argnum+1), + arg->line()), + "\n#else\n", + PC.Token ( + sprintf ("%s = Pike_sp[%d%s].u.integer;\n", + arg->name(), argnum, argbase), + arg->line()), + "\n#endif\n", + }); + 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 (got_void_or_zero_check) + { + string zero_val; + switch (arg->c_type()) { + case "INT_TYPE": zero_val = "0"; break; + case "FLOAT_TYPE": zero_val = "0.0"; break; + default: zero_val = "NULL"; break; + } + ret+=({ PC.Token(sprintf("} else %s = %s;\n", + arg->name(), zero_val), + arg->line()) }); + } + } + + 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|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]->text[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; + + if( has_suffix( lower_case(dirname( argv[0] )), "standalone.pmod" ) ) + usage = "pike -x " + basename( argv[0] )-".pike" + " " + usage; + foreach(Getopt.find_all_options( argv, ({ + ({ "help", Getopt.NO_ARG, "-h,--help"/"," }) })), array opt ) + switch(opt[0]) { + case "version": + write( "Precompile format version "+precompile_api_version+"\n"); + return 0; + case "help": + write( usage ); + return 0; + } + argv = Getopt.get_args(argv); + if( sizeof( argv ) < 2 ) + { + write((usage/"\n")[0]+"\n"); + return 0; + } + string file = argv[1]; + + x=Stdio.read_file(file)-"\r"; + 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)); +}