totalspectrum / spin2cpp

Tool to convert Parallax Propeller Spin code to PASM, C++ or C
Other
46 stars 17 forks source link

"libflexspin" #285

Open Wuerfel21 opened 2 years ago

Wuerfel21 commented 2 years ago

This has been simmering in my head for ages, so I guess I'll write another burner issue so it can simmer here for another century.

Basically, it'd be real swell if you could call into flexspin as a library. Potential applications include:

Challenges:

Draft libflexspin.h API:

struct flexapi_compiler; // Opaque struct typedef struct flexapi_compiler flexapi_compiler;

/ Allocates new compiler object. / flexapi_compiler flexapi_create_compiler(); / Frees a compiler object. / void flexapi_free_compiler(flexapi_compiler gl); / Resets a compiler object. This is semantically identical to freeing one and creating another, but with less overhead. / void flexapi_reset_compiler(flexapi_compiler *gl);

enum flexapi_option_type { FLEXAPI_OPTION_INT = 1, FLEXAPI_OPTION_STRING, FLEXAPI_OPTION_STRING_MULTI, // Used for source files, preprocessor defines, etc... FLEXAPI_OPTION_ENUM, };

// Struct struct flexapi_option { const struct flexapi_option next; const char name; // Internally used option identifier const char long_name,description; // Long name / description for human consumption flexapi_option_type type; }; typedef struct flexapi_option flexapi_option;

/ List possible compiler options. / const flexapi_option * flexapi_list_options();

/ List possible values for an enum-typed option. This re-uses the same flexapi_option type, take care. / const flexapi_option flexapi_list_enum_vals(char opt);

/ Sets a compiler option to a given value. Do note that you can not "unset" an option without resetting the compiler. Returns negative value on failure. / int flexapi_set_option(flexapi_compiler gl,const char opt, const char value); / Same as flexapi_set_option, but for setting an integer value without having to create a string. / int flexapi_set_option_int(flexapi_compiler gl,const char *opt, int value);

/* Set callback used to read source files. Should read the entire file into memory and return it. The returned buffer is owned by libflexspin, so create a copy if neccessary.

If called with NULL or not called at all, a default method is used.

/ void flexapi_set_file_callback(flexapi_compiler gl, char(filecb)(const char name));

/ Run compiler. This can only be called once per compiler instance before it needs to be reset. If result is negative, it means an error has occurred. / int flexapi_do_compile(flexapi_compiler *gl);

/ Get compiled binary's size. / int flexapi_get_binary_size(flexapi_compiler gl); / Get compiled binary. / const char flexapi_get_binary(flexapi_compiler gl); / Get listing (zero terminated). / const char flexapi_get_listing(flexapi_compiler gl); / Get intermediate asm (zero terminated). / const char flexapi_get_asmgen(flexapi_compiler *gl);

enum flexapi_mesg_type { FLEXAPI_MESG_DEBUG = 1, FLEXAPI_MESG_INFO, FLEXAPI_MESG_WARNING, FLEXAPI_MESG_ERROR, };

struct flexapi_message { const struct flexapi_message next; // NULL means last message flexapi_mesg_type type; unsigned int line; // 0 means unknown line const char file; const char *text; }; typedef struct flexapi_message flexapi_message;

/ Get diagnostic messages from last compiler run. Returns NULL if there are no messages. Returned pointer only stays valid until the compiler is freed or reset. / const flexapi_message flexapi_get_messages(flexapi_compiler gl);


Simple example usage (option names are conjecture):
```c
flexapi_compiler *flex = flexapi_create_compiler();
flexapi_set_option(flex,"chip","P1");
flexapi_set_option(flex,"output","asm");
flexapi_set_option(flex,"optlevel","s");
flexapi_set_option(flex,"sourcefile","stuff.c");
flexapi_set_option(flex,"sourcefile","morestuff.c");
int status = flexapi_do_compile(flex);
if (status < 0) {
  printf("Failed to compile\n");
  // Print messages here, too lazy to write that right now.
} else {
  puts(flexapi_get_asmgen(flex));
}
flexapi_free_compiler(flex);
totalspectrum commented 1 year ago

Having a library (or at least being able to run in a browser via emscripten) would be really useful, I agree. I've started taking some baby steps in that direction by checking in a new pool memory allocator so that freeing all the memory we've used would be easy.

Wuerfel21 commented 1 year ago

Next step would be moving globals into a struct. For standalone builds, this can just be a single global instance and nothing changes, for a library build it should be using a thread-local pointer.