spin2cpp
spin2cpp copied to clipboard
"libflexspin"
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:
- Flexspin in browser (emscripten)
- Quick compilation of many files from a script (avoid process spawn overhead)
- Integration into PropTool (yeah I wish)
Challenges:
- Make everything thread-safe and easily resettable (move globals into struct)
- Write custom allocator to deallocate internal structures when done
- Refactor options system for programmatic interface
- Refactor usage of temporary files
- Finalize external API
Draft libflexspin.h
API:
-
flexapi_set_file_callback
might need to be slightly more complex to deal with library files. Is dealing with library paths domain of the compiler or of the file callback?
/*
Returns version string.
Optionally fills int pointers with version number.
*/
const char *
flexapi_get_version(int *major, int *minor, int *patch);
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):
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);
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.
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.