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  }