github.com/Konstantin8105/c4go@v0.0.0-20240505174241-768bb1c65a51/program/definition_function.go (about) 1 package program 2 3 import ( 4 "fmt" 5 "strings" 6 7 "github.com/Konstantin8105/c4go/util" 8 ) 9 10 // DefinitionFunction contains the prototype definition for a function. 11 type DefinitionFunction struct { 12 // The name of the function, like "printf". 13 Name string 14 15 // The C return type, like "int". 16 ReturnType string 17 18 // The C argument types, like ["bool", "int"]. There is currently no way 19 // to represent a varargs. 20 ArgumentTypes []string 21 22 // Each function from some source. For example: "stdio.h" 23 IncludeFile string 24 // If function called, then true. 25 IsCalled bool 26 27 // If function without body, then true 28 HaveBody bool 29 30 // If function from some C standard library, then true. 31 IsCstdFunction bool 32 pntCstd *stdFunction 33 34 // If this is not empty then this function name should be used instead 35 // of the Name. Many low level functions have an exact match with a Go 36 // function. For example, "sin()". 37 Substitution string 38 39 // Can be overridden with the substitution to rearrange the return variables 40 // and parameters. When either of these are nil the behavior is to keep the 41 // single return value and parameters the same. 42 ReturnParameters []int 43 Parameters []int 44 } 45 46 // Each of the predefined function have a syntax that allows them to be easy to 47 // read (and maintain). For example: 48 // 49 // double __builtin_fabs(double) -> noarch.Fabs 50 // 51 // Declares the prototype of __builtin_fabs (a low level function implemented 52 // only on Mac) with a specific substitution provided. This means that it should 53 // replace any instance of __builtin_fabs with: 54 // 55 // github.com/Konstantin8105/c4go/noarch.Fabs 56 // 57 // The substitution is optional. 58 // 59 // The substituted function can also move the parameters and return value 60 // positions. This is called a transformation. For example: 61 // 62 // size_t fread(void*, size_t, size_t, FILE*) -> $0, $1 = noarch.Fread($2, $3, $4) 63 // 64 // Where $0 represents the C return value and $1 and above are for each of the 65 // parameters. 66 // 67 // Transformations can also be used to specify variable that need to be passed 68 // by reference by using the prefix "&" instead of "$": 69 // 70 // size_t fread(void*, size_t, size_t, FILE*) -> $0 = noarch.Fread(&1, $2, $3, $4) 71 var builtInFunctionDefinitions = map[string][]string{ 72 "signal.h": { 73 // signal.h 74 "void (*signal(int , void (*)(int)))(int) -> noarch.Signal", 75 "int raise(int ) -> noarch.Raise", 76 }, 77 "errno.h": { 78 // errno.h 79 "int * __errno_location(void ) -> noarch.ErrnoLocation", 80 }, 81 "math.h": { 82 // math.h 83 "double acos(double) -> math.Acos", 84 "double asin(double) -> math.Asin", 85 "double atan(double) -> math.Atan", 86 "double atan2(double, double) -> math.Atan2", 87 "double ceil(double) -> math.Ceil", 88 "double cos(double) -> math.Cos", 89 "double cosh(double) -> math.Cosh", 90 "double fabs(double) -> math.Abs", 91 "double floor(double) -> math.Floor", 92 "double fmod(double, double) -> math.Mod", 93 "double remainder(double, double) -> math.Remainder", 94 "double ldexp(double, int) -> math.Ldexp", 95 "double log(double) -> math.Log", 96 "double log10(double) -> math.Log10", 97 "double pow(double, double) -> math.Pow", 98 "double sin(double) -> math.Sin", 99 "double sinh(double) -> math.Sinh", 100 "double sqrt(double) -> math.Sqrt", 101 "double tan(double) -> math.Tan", 102 "double tanh(double) -> math.Tanh", 103 104 "double copysign(double, double) -> math.Copysign", 105 "long double copysignl(long double, long double) -> math.Copysign", 106 107 "double expm1(double) -> math.Expm1", 108 "long double expm1l(long double) -> math.Expm1", 109 110 "double exp2(double) -> math.Exp2", 111 "long double exp2l(long double) -> math.Exp2", 112 113 "double exp(double) -> math.Exp", 114 "long double expl(long double) -> math.Exp", 115 116 "double erf(double) -> math.Erf", 117 "long double erfl(long double) -> math.Erf", 118 119 "double erfc(double) -> math.Erfc", 120 "long double erfcl(long double) -> math.Erfc", 121 122 "double log2(double) -> math.Log2", 123 "long double log2l(long double) -> math.Log2", 124 125 "double log1p(double) -> math.Log1p", 126 "long double log1pl(long double) -> math.Log1p", 127 128 "double asinh(double) -> math.Asinh", 129 "float asinhf(float) -> noarch.Asinhf", 130 "long double asinhl(long double) -> math.Asinh", 131 132 "double acosh(double) -> math.Acosh", 133 "float acoshf(float) -> noarch.Acoshf", 134 "long double acoshl(long double) -> math.Acosh", 135 136 "double atanh(double) -> math.Atanh", 137 "float atanhf(float) -> noarch.Atanhf", 138 "long double atanhl(long double) -> math.Atanh", 139 140 "double sinh(double) -> math.Sinh", 141 "long double sinhl(long double) -> math.Sinh", 142 143 "double cosh(double) -> math.Cosh", 144 "long double coshl(long double) -> math.Cosh", 145 146 "double tanh(double) -> math.Tanh", 147 "long double tanhl(long double) -> math.Tanh", 148 149 "double cbrt(double) -> math.Cbrt", 150 "long double cbrtl(long double) -> math.Cbrt", 151 152 "double hypot(double, double) -> math.Hypot", 153 "long double hypotl(long double, long double) -> math.Hypot", 154 }, 155 "stdio.h": { 156 157 // linux/stdio.h 158 "int _IO_getc(FILE*) -> noarch.Fgetc", 159 "int _IO_putc(int, FILE*) -> noarch.Fputc", 160 161 // stdio.h 162 "int printf(const char*, ...) -> noarch.Printf", 163 "int scanf(const char*, ...) -> noarch.Scanf", 164 "int putchar(int) -> noarch.Putchar", 165 "int puts(const char *) -> noarch.Puts", 166 "FILE* fopen(const char *, const char *) -> noarch.Fopen", 167 "int fclose(FILE*) -> noarch.Fclose", 168 "int remove(const char*) -> noarch.Remove", 169 "int rename(const char*, const char*) -> noarch.Rename", 170 "int fputs(const char*, FILE*) -> noarch.Fputs", 171 "FILE* tmpfile() -> noarch.Tmpfile", 172 "char* fgets(char*, int, FILE*) -> noarch.Fgets", 173 "void rewind(FILE*) -> noarch.Rewind", 174 "int feof(FILE*) -> noarch.Feof", 175 "char* tmpnam(char*) -> noarch.Tmpnam", 176 "int fflush(FILE*) -> noarch.Fflush", 177 "int fprintf(FILE*, const char*, ...) -> noarch.Fprintf", 178 "int fscanf(FILE*, const char*, ...) -> noarch.Fscanf", 179 "int fgetc(FILE*) -> noarch.Fgetc", 180 "int fputc(int, FILE*) -> noarch.Fputc", 181 "int getc(FILE*) -> noarch.Fgetc", 182 "char * gets(char*) -> noarch.Gets", 183 "int getchar() -> noarch.Getchar", 184 "int putc(int, FILE*) -> noarch.Fputc", 185 "int fseek(FILE*, long int, int) -> noarch.Fseek", 186 "long ftell(FILE*) -> noarch.Ftell", 187 "int fread(void*, int, int, FILE*) -> $0 = noarch.Fread(&1, $2, $3, $4)", 188 "int fwrite(char*, int, int, FILE*) -> noarch.Fwrite", 189 "int fgetpos(FILE*, int*) -> noarch.Fgetpos", 190 "int fsetpos(FILE*, int*) -> noarch.Fsetpos", 191 "int sprintf(char*, const char *, ...) -> noarch.Sprintf", 192 "int snprintf(char*, int, const char *, ...) -> noarch.Snprintf", 193 "int vsprintf(char*, const char *, ...) -> noarch.Vsprintf", 194 "int vprintf(const char *, ...) -> noarch.Vprintf", 195 "int vfprintf(FILE *, const char *, ...) -> noarch.Vfprintf", 196 "int vsnprintf(char*, int, const char *, ...) -> noarch.Vsnprintf", 197 "void perror( const char *) -> noarch.Perror", 198 "ssize_t getline(char **, size_t *, FILE *) -> noarch.Getline", 199 "int sscanf( const char *, const char *, ...) -> noarch.Sscanf", 200 }, 201 "wchar.h": { 202 // wchar.h 203 "wchar_t * wcscpy(wchar_t*, const wchar_t*) -> noarch.Wcscpy", 204 "int wcscmp(const wchar_t*, const wchar_t*) -> noarch.Wcscmp", 205 "size_t wcslen(const wchar_t*) -> noarch.Wcslen", 206 }, 207 "string.h": { 208 // string.h 209 "char* strcat(char *, const char *) -> noarch.Strcat", 210 "char* strncat(char *, const char *, int) -> noarch.Strncat", 211 "int strcmp(const char *, const char *) -> noarch.Strcmp", 212 "char * strchr(char *, int) -> noarch.Strchr", 213 "char * strstr(const char *, const char *) -> noarch.Strstr", 214 215 "char* strcpy(const char*, char*) -> noarch.Strcpy", 216 // should be: "char* strncpy(const char*, char*, size_t) -> noarch.Strncpy", 217 "char* strncpy(const char*, char*, int) -> noarch.Strncpy", 218 219 // real return type is "size_t", but it is changed to "int" 220 // in according to noarch.Strlen 221 "int strlen(const char*) -> noarch.Strlen", 222 223 "char* __inline_strcat_chk(char *, const char *) -> noarch.Strcat", 224 225 "char * memset(char *, char, unsigned int) -> noarch.Memset", 226 "char * memmove(char *, char *, unsigned int) -> noarch.Memmove", 227 "int memcmp(const char *, const char *, unsigned int) -> noarch.Memcmp", 228 "const char * strrchr( const char *, int) -> noarch.Strrchr", 229 "char * strdup(const char *) -> noarch.Strdup", 230 "char * strerror(int ) -> noarch.Strerror", 231 }, 232 "stdlib.h": { 233 // stdlib.h 234 "int abs(int) -> noarch.Abs", 235 "double atof(const char *) -> noarch.Atof", 236 "int atoi(const char*) -> noarch.Atoi", 237 "long int atol(const char*) -> noarch.Atol", 238 "long long int atoll(const char*) -> noarch.Atoll", 239 "div_t div(int, int) -> noarch.Div", 240 "void exit(int) -> noarch.Exit", 241 "void free(void*) -> noarch.Free", 242 "char* getenv(const char *) -> noarch.Getenv", 243 "long int labs(long int) -> noarch.Labs", 244 "ldiv_t ldiv(long int, long int) -> noarch.Ldiv", 245 "long long int llabs(long long int) -> noarch.Llabs", 246 "lldiv_t lldiv(long long int, long long int) -> noarch.Lldiv", 247 "int rand() -> noarch.Int32", 248 // The real definition is srand(unsigned int) however the type would be 249 // different. It's easier to change the definition than create a proxy 250 // function in stdlib.go. 251 "void srand(long long) -> math/rand.Seed", 252 "double strtod(const char *, char **) -> noarch.Strtod", 253 "float strtof(const char *, char **) -> noarch.Strtof", 254 "long strtol(const char *, char **, int) -> noarch.Strtol", 255 "long double strtold(const char *, char **) -> noarch.Strtold", 256 "long long strtoll(const char *, char **, int) -> noarch.Strtoll", 257 "long unsigned int strtoul(const char *, char **, int) -> noarch.Strtoul", 258 "long long unsigned int strtoull(const char *, char **, int) -> noarch.Strtoull", 259 "int system(const char *) -> noarch.System", 260 "void free(void*) -> _", 261 "int atexit(void*) -> noarch.Atexit", 262 }, 263 "time.h": { 264 // time.h 265 "time_t time(time_t *) -> noarch.Time", 266 "char* ctime(const time_t *) -> noarch.Ctime", 267 "struct tm * localtime(const time_t *) -> noarch.LocalTime", 268 "struct tm * gmtime(const time_t *) -> noarch.Gmtime", 269 "time_t mktime(struct tm *) -> noarch.Mktime", 270 "char * asctime(struct tm *) -> noarch.Asctime", 271 "clock_t clock(void) -> noarch.Clock", 272 "double difftime(time_t , time_t ) -> noarch.Difftime", 273 }, 274 "locale.h": { 275 "struct lconv * localeconv(void) -> noarch.Localeconv", 276 "char * setlocale(int , const char * ) -> noarch.Setlocale", 277 }, 278 "termios.h": { 279 // termios.h 280 "int tcsetattr(int , int , const struct termios *) -> noarch.Tcsetattr", 281 "int tcgetattr(int , struct termios *) -> noarch.Tcgetattr", 282 "int tcsendbreak(int , int ) -> noarch.Tcsendbreak", 283 "int tcdrain(int ) -> noarch.Tcdrain", 284 "int tcflush(int , int ) -> noarch.Tcflush", 285 "int tcflow(int , int ) -> noarch.Tcflow", 286 "void cfmakeraw(struct termios *) -> noarch.Cfmakeraw", 287 "speed_t cfgetispeed(const struct termios *) -> noarch.Cfgetispeed", 288 "speed_t cfgetospeed(const struct termios *) -> noarch.Cfgetospeed", 289 "int cfsetispeed(struct termios *, speed_t ) -> noarch.Cfsetispeed", 290 "int cfsetospeed(struct termios *, speed_t ) -> noarch.Cfsetospeed", 291 "int cfsetspeed(struct termios *, speed_t ) -> noarch.Cfsetspeed", 292 }, 293 "sys/ioctl.h": { 294 "int ioctl(int , int , ... ) -> noarch.Ioctl", 295 }, 296 "sys/time.h": { 297 "int gettimeofday(struct timeval *, struct timezone *) -> noarch.Gettimeofday", 298 }, 299 "fcntl.h": { 300 "int open(const char *, int , ...) -> noarch.Open", 301 }, 302 "unistd.h": { 303 "int pipe(int *) -> noarch.Pipe", 304 "void exit(int) -> golang.org/x/sys/unix.Exit", 305 "ssize_t write(int, const void *, size_t) -> noarch.Write", 306 "ssize_t read(int, void *, size_t) -> noarch.Read", 307 "int close(int) -> noarch.CloseOnExec", 308 "int isatty(int) -> noarch.Isatty", 309 "int unlink(const char *) -> noarch.Unlink", 310 "int ftruncate(int , off_t ) -> noarch.Ftruncate", 311 }, 312 "sys/stat.h": { 313 "int fstat(int , struct stat *) -> noarch.Fstat", 314 "int stat(const char * , struct stat * ) -> noarch.Stat", 315 "int lstat(const char * , struct stat * ) -> noarch.Lstat", 316 }, 317 } 318 319 // GetIncludeFileNameByFunctionSignature - return name of C include header 320 // in according to function name and type signature 321 func (p *Program) GetIncludeFileNameByFunctionSignature( 322 functionName, cType string) (includeFileName string, err error) { 323 324 for k, functionList := range builtInFunctionDefinitions { 325 for i := range functionList { 326 if !strings.Contains(functionList[i], functionName) { 327 continue 328 } 329 // find function name 330 baseFunction := strings.Split(functionList[i], " -> ")[0] 331 332 // separate baseFunction to function name and type 333 counter := 1 334 var pos int 335 // var err error 336 for i := len(baseFunction) - 2; i >= 0; i-- { 337 if baseFunction[i] == ')' { 338 counter++ 339 } 340 if baseFunction[i] == '(' { 341 counter-- 342 } 343 if counter == 0 { 344 pos = i 345 break 346 } 347 } 348 leftPart := strings.TrimSpace(baseFunction[:pos]) 349 rightPart := strings.TrimSpace(baseFunction[pos:]) 350 index := strings.LastIndex(leftPart, " ") 351 if index < 0 { 352 err = fmt.Errorf("cannot found space ` ` in %v", leftPart) 353 return 354 } 355 if strings.Replace(functionName, " ", "", -1) != 356 strings.Replace(leftPart[index+1:], " ", "", -1) { 357 continue 358 } 359 if strings.Replace(cType, " ", "", -1) != 360 strings.Replace(leftPart[:index]+rightPart, " ", "", -1) { 361 continue 362 } 363 return k, nil 364 } 365 } 366 367 return 368 } 369 370 // GetFunctionDefinition will return nil if the function does not exist (is not 371 // registered). 372 func (p *Program) GetFunctionDefinition(functionName string) *DefinitionFunction { 373 p.loadFunctionDefinitions() 374 375 if f, ok := p.functionDefinitions[functionName]; ok { 376 return &f 377 } 378 379 return nil 380 } 381 382 // AddFunctionDefinition registers a function definition. If the definition 383 // already exists it will be replaced. 384 func (p *Program) AddFunctionDefinition(f DefinitionFunction) { 385 p.loadFunctionDefinitions() 386 387 f.ReturnType = strings.TrimSpace(f.ReturnType) 388 p.functionDefinitions[f.Name] = f 389 } 390 391 // dollarArgumentsToIntSlice converts a list of dollar arguments, like "$1, &2" 392 // into a slice of integers; [1, -2]. 393 // 394 // This function requires at least one argument in s, but only arguments upto 395 // $9 or &9. 396 func dollarArgumentsToIntSlice(s string) []int { 397 r := []int{} 398 multiplier := 1 399 400 for _, c := range s { 401 if c == '$' { 402 multiplier = 1 403 } 404 if c == '&' { 405 multiplier = -1 406 } 407 408 if c >= '0' && c <= '9' { 409 r = append(r, multiplier*(int(c)-'0')) 410 } 411 } 412 413 return r 414 } 415 416 func (p *Program) loadFunctionDefinitions() { 417 if p.builtInFunctionDefinitionsHaveBeenLoaded { 418 return 419 } 420 421 p.functionDefinitions = map[string]DefinitionFunction{} 422 p.builtInFunctionDefinitionsHaveBeenLoaded = true 423 424 for k, v := range builtInFunctionDefinitions { 425 if !p.IncludeHeaderIsExists(k) { 426 continue 427 } 428 429 for _, f := range v { 430 index := strings.Index(f, "->") 431 _, a, w, e, err := util.ParseFunction(f[:index]) 432 if err != nil { 433 panic(err) 434 } 435 436 // Defaults for transformations. 437 var returnParameters, parameters []int 438 439 // Substitution rules. 440 substitution := strings.TrimSpace(f[index+2:]) 441 if substitution != "" { 442 substitution = strings.TrimLeft(substitution, " ->") 443 444 // The substitution might also rearrange the parameters (return and 445 // parameter transformation). 446 subMatch := util.GetRegex(`^(.*?) = (.*)\((.*)\)$`). 447 FindStringSubmatch(substitution) 448 if len(subMatch) > 0 { 449 returnParameters = dollarArgumentsToIntSlice(subMatch[1]) 450 parameters = dollarArgumentsToIntSlice(subMatch[3]) 451 substitution = subMatch[2] 452 } 453 } 454 455 if strings.HasPrefix(substitution, "noarch.") { 456 substitution = "github.com/Konstantin8105/c4go/" + substitution 457 } 458 459 p.AddFunctionDefinition(DefinitionFunction{ 460 Name: a, 461 ReturnType: e[0], 462 ArgumentTypes: w, 463 Substitution: substitution, 464 ReturnParameters: returnParameters, 465 Parameters: parameters, 466 IsCstdFunction: false, 467 }) 468 } 469 } 470 471 // initialization CSTD 472 for i := range std { 473 _, a, w, e, err := util.ParseFunction(std[i].cFunc) 474 if err != nil { 475 panic(err) 476 } 477 478 p.AddFunctionDefinition(DefinitionFunction{ 479 Name: a, 480 ReturnType: e[0], 481 ArgumentTypes: w, 482 IsCstdFunction: true, 483 pntCstd: &std[i], 484 }) 485 } 486 } 487 488 func (p *Program) SetCalled(name string) { 489 f, ok := p.functionDefinitions[name] 490 if ok { 491 f.IsCalled = true 492 p.functionDefinitions[name] = f 493 } 494 } 495 496 func (p *Program) SetHaveBody(name string) { 497 f, ok := p.functionDefinitions[name] 498 if ok { 499 f.HaveBody = true 500 p.functionDefinitions[name] = f 501 } 502 } 503 504 func (p *Program) GetCstdFunction() (src string) { 505 for i := range p.functionDefinitions { 506 if !p.functionDefinitions[i].IsCalled { 507 continue 508 } 509 if !p.functionDefinitions[i].IsCstdFunction { 510 continue 511 } 512 if p.functionDefinitions[i].pntCstd == nil { 513 continue 514 } 515 // add dependencies of packages 516 p.AddImports(p.functionDefinitions[i].pntCstd.dependPackages...) 517 518 // add dependencies of functions 519 for _, funcName := range p.functionDefinitions[i].pntCstd.dependFuncStd { 520 for j := range p.functionDefinitions { 521 if p.functionDefinitions[j].Name != funcName { 522 continue 523 } 524 def := p.functionDefinitions[j] 525 def.IsCalled = true 526 p.functionDefinitions[j] = def 527 break 528 } 529 } 530 } 531 532 for _, v := range p.functionDefinitions { 533 if !v.IsCstdFunction { 534 continue 535 } 536 if !v.IsCalled { 537 continue 538 } 539 src += v.pntCstd.functionBody 540 } 541 return 542 } 543 544 func (p *Program) GetOutsideCalledFunctions() (ds []DefinitionFunction) { 545 for _, v := range p.functionDefinitions { 546 if v.IncludeFile == "" { 547 continue 548 } 549 if !v.IsCalled { 550 continue 551 } 552 if !v.HaveBody { 553 ds = append(ds, v) 554 continue 555 } 556 if v.IsCstdFunction { 557 continue 558 } 559 if p.PreprocessorFile.IsUserSource(v.IncludeFile) { 560 continue 561 } 562 ds = append(ds, v) 563 } 564 return 565 }