spin2cpp icon indicating copy to clipboard operation
spin2cpp copied to clipboard

"libflexspin"

Open Wuerfel21 opened this issue 2 years ago • 2 comments

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);

Wuerfel21 avatar Jun 24 '22 01:06 Wuerfel21

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.

totalspectrum avatar Jan 22 '23 03:01 totalspectrum

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.

Wuerfel21 avatar Jan 22 '23 15:01 Wuerfel21