Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

"libflexspin" #285

Open
Wuerfel21 opened this issue Jun 24, 2022 · 2 comments
Open

"libflexspin" #285

Wuerfel21 opened this issue Jun 24, 2022 · 2 comments

Comments

@Wuerfel21
Copy link
Contributor

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);
@totalspectrum
Copy link
Owner

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
Copy link
Contributor Author

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants