The notation of `eval ()` in C

I am trying to make an eval function in C for a while.

At the moment, my idea is to make a String -> function pointer hash with all the standard C library functions and all the functions that I create, so that I could handle function calls (for already defined functions).

However, defining functions with strings (i.e. calling eval("int fun(){return 1;}") ) is still a problem, I don't know how I could handle this at runtime, does anyone ideas?

Variable definitions do not seem to be too much of a problem, as I could just use a different hash var_name -> pointer and use that pointer whenever a variable is needed.

By the way, I don't care about performance, I want to make it work.

+8
c eval dynamic
source share
3 answers

It is possible, but pain is done. You need to write a parser that takes text as input and generates a syntax tree; then you need to simplify the constructs (for example, convert loops to goto statements and simplify expressions into single-static assignments that have only 1 operation). Then you need to map all the templates in your syntax tree to sequences of instructions on the target machine that perform the same tasks. Finally, you need to select the registers that will be used for each of these instructions, spilling them onto the stack if necessary.

In short, it is possible to write an implementation for eval in C, but a huge amount of work requiring a lot of experience and knowledge in several areas of computer science. The complexity of writing a compiler is the exact reason that most programming languages ​​either interpret or use a virtual machine with custom byte code. Tools like clang and llvm make this a lot easier, but they are written in C ++, not C.

+3
source share

Trying to make out C is a real pain in the back; but we already know how to parse C; call the C compiler! Here we collect the eval code into a dynamic library and load it.

You may have problems when your dynamic code cannot find other functions or variables in your own code; A simple solution for this is to compile your entire program, except for main () as a library and linking the dynamic code library to it. You can avoid the -fpic penalty by setting the library download address only a few kilograms above your main download address. On Linux, unresolved characters in a library can be resolved by an executable file if not separated, and glibc depends on this function; however, compiler optimization sometimes interferes, so a general library method may be required.

Sample code below for Linux. This can be adopted on other Unix, including Mac OSX, with little work. Trying Windows is possible, but harder because you do not have the C compiler guarantee unless you are ready to send it; and on Windows there is an obscene rule about several C sessions, so you must build with the one you are sending, and therefore must also build with the same compiler you are sending. In addition, here you must use a common library technique or characters in your main program simply will not be allowed in the library (the PE file format cannot express the need).

This code example does not allow eval () to save state; if you need it, you must make it either variables in the main program, or (preferred) passing in the state structure at the address.

If you are trying to do this in an embedded environment, do not do this. This is a bad idea in the embedded world.

In response to the comment rici; I have never seen a case where the argument types and the returned block type of eval () were not statically determined from the surrounding code; besides, what could you call it? The code sample below can reduce the extraction of the shared part, so the part of each type is just a couple of lines; exercise remains for the reader.

Unless you have a specific reason for dynamic C; try the built-in LUA instead with a well-defined interface.

 /* gcc -o dload dload.c -ldl */ #include <dlfcn.h> #include <stdio.h> typedef void (*fevalvd)(int arg); /* We need one of these per function signature */ /* Disclaimer: does not support currying; attempting to return functions -> undefined behavior */ /* The function to be called must be named fctn or this does not work. */ void evalvd(const char *function, int arg) { char buf1[50]; char buf2[50]; char buf3[100]; void *ctr; fevalvd fc; snprintf(buf1, 50, "/tmp/dl%dc", getpid()); snprintf(buf2, 50, "/tmp/libdl%d.so", getpid()); FILE *f = fopen(buf1, "w"); if (!f) { fprintf (stderr, "can't open temp file\n"); } fprintf(f, "%s", function); fclose(f); snprintf(buf3, 100, "gcc -shared -fpic -o %s %s", buf2, buf1); if (system(buf3)) { unlink(buf1); return ; /* oops */ } ctr = dlopen(buf2, RTLD_NOW | RTLD_LOCAL); if (!ctr) { fprintf(stderr, "can't open\n"); unlink(buf1); unlink(buf2); return ; } fc = (fevalvd)dlsym(ctr, "fctn"); if (fc) { fc(arg); } else { fprintf(stderr, "Can't find fctn in dynamic code\n"); } dlclose(ctr); unlink(buf2); unlink(buf1); } int main(int argc, char **argv) { evalvd("#include <stdio.h>\nvoid fctn(int a) { printf(\"%d\\n\", a); }\n", 10); } 
+3
source share

Given some limitations, using OpenCL may be a possible way to implement eval in C / C ++. Once your OpenCL implementation provides the ability to compile kernels and run them, no matter where on the processor or GPU (or some other accelerator device), this means that you can generate lines of kernel code in your C / R runtime C ++, compile them and enqueue to execute. In addition, the OpenCL API provides the ability to look for compilation, binding, and kernel errors. So, please take a look at OpenCL.

0
source share

All Articles