mirror of
https://git.planet-casio.com/Lephenixnoir/fxsdk.git
synced 2024-12-29 13:03:37 +01:00
157 lines
6.4 KiB
C
157 lines
6.4 KiB
C
//---------------------------------------------------------------------------//
|
|
// ==>/[_]\ fxlink: A community communication tool for CASIO calculators. //
|
|
// |:::| Made by Lephe' as part of the fxSDK. //
|
|
// \___/ License: MIT <https://opensource.org/licenses/MIT> //
|
|
//---------------------------------------------------------------------------//
|
|
// fxlink.tui.command-util: Preprocessor black magic for command definition
|
|
//
|
|
// This header provides the following method for declaring TUI commands that
|
|
// are automatically registered at startup, and are invoked with arguments
|
|
// pre-parsed:
|
|
//
|
|
// FXLINK_COMMAND("<NAME>", <TYPE>(<NAME>), <TYPE>(<NAME>), ...) {
|
|
// /* normal code... */
|
|
// return <STATUS>;
|
|
// }
|
|
//
|
|
// The command name is a string. It can have multiple space-separated words as
|
|
// in "gintctl test", in which case it is matched against argv[0], argv[1], etc
|
|
// and the prefix ("gintctl") is automatically made into a sub-category command
|
|
// with relevant error messages.
|
|
//
|
|
// Each argument has a type and a name, as in INT(x). The type carries
|
|
// information on the parsing method, the acceptable range, and of course the
|
|
// actual runtime type of the argument. Available types are:
|
|
//
|
|
// Name Runtime type Meaning and range
|
|
// --------------------------------------------------------------------------
|
|
// INT int Any integer
|
|
// STRING char const * Any string argument from argv[]
|
|
// VARIADIC char const ** End of the argv array (NULL-terminated)
|
|
// --------------------------------------------------------------------------
|
|
// DEVICE struct fxlink_device * Selected device (implicit; never NULL)
|
|
// --------------------------------------------------------------------------
|
|
//
|
|
// The function returns a status code, which is an integer. The entire command
|
|
// declaration might look like:
|
|
//
|
|
// FXLINK_COMMAND("gintctl test", INT(lower_bound), INT(upper_bound)) {
|
|
// int avg = (lower_bound + upper_bound) / 2;
|
|
// return 0;
|
|
// }
|
|
//
|
|
// I considered doing the entire thing in C++, but absolute preprocessor abuse
|
|
// is fun once in a while.
|
|
//---
|
|
|
|
#include <fxlink/defs.h>
|
|
|
|
//---
|
|
// Shell-like command parsing (without the features)
|
|
//---
|
|
|
|
struct fxlink_tui_cmd {
|
|
int argc;
|
|
char const **argv;
|
|
char *data;
|
|
};
|
|
|
|
/* Parse a string into an argument vector */
|
|
struct fxlink_tui_cmd fxlink_tui_cmd_parse(char const *input);
|
|
|
|
/* Dump a command to TUI console for debugging */
|
|
void fxlink_tui_cmd_dump(struct fxlink_tui_cmd const *cmd);
|
|
|
|
/* Free a command */
|
|
void fxlink_tui_cmd_free(struct fxlink_tui_cmd const *cmd);
|
|
|
|
//---
|
|
// Command registration and argument scanning
|
|
//---
|
|
|
|
/* Parse a list of arguments into structured data. The format is a string of
|
|
argument specifiers, each of which can be:
|
|
s String (char *)
|
|
d Integer (int)
|
|
* Other variadic arguments (char **)
|
|
(-- will probably be expanded later.)
|
|
Returns true if parsing succeeded, false otherwise (including if arguments
|
|
are missing) after printing an error message. */
|
|
bool fxlink_tui_parse_args(int argc, char const **argv, char const *fmt, ...);
|
|
|
|
/* Register a command with the specified name and invocation function. This can
|
|
be called manually or generated (along with the parser) using the macro
|
|
FXLINK_COMMAND. */
|
|
void fxlink_tui_register_cmd(char const *name,
|
|
int (*func)(int argc, char const **argv));
|
|
|
|
/* Apply a macro to every variadic argument. _M1 is applied to the first
|
|
argument and _Mn is applied to all subsequent arguments. */
|
|
#define MAPn(_M1,_Mn,...) __VA_OPT__(MAP_1(_M1,_Mn,__VA_ARGS__))
|
|
#define MAP_1(_M1,_Mn,_X,...) _M1(_X) __VA_OPT__(MAP_2(_M1,_Mn,__VA_ARGS__))
|
|
#define MAP_2(_M1,_Mn,_X,...) _Mn(_X) __VA_OPT__(MAP_3(_M1,_Mn,__VA_ARGS__))
|
|
#define MAP_3(_M1,_Mn,_X,...) _Mn(_X) __VA_OPT__(MAP_4(_M1,_Mn,__VA_ARGS__))
|
|
#define MAP_4(_M1,_Mn,_X,...) _Mn(_X) __VA_OPT__(MAP_5(_M1,_Mn,__VA_ARGS__))
|
|
#define MAP_5(_M1,_Mn,_X,...) _Mn(_X) __VA_OPT__(MAP_6(_M1,_Mn,__VA_ARGS__))
|
|
#define MAP_6(_M1,_Mn,_X,...) _Mn(_X) __VA_OPT__(MAP_7(_M1,_Mn,__VA_ARGS__))
|
|
#define MAP_7(_M1,_Mn,_X,...) _Mn(_X) __VA_OPT__(MAP_8(_M1,_Mn,__VA_ARGS__))
|
|
#define MAP_8(_M1,_Mn,_X,...) _Mn(_X) __VA_OPT__(MAP_MAX_8_ARGS(_))
|
|
#define MAP_MAX_8_ARGS()
|
|
/* Simpler version where the same macro is applied to all arguments */
|
|
#define MAP(_M, ...) MAPn(_M, _M, ##__VA_ARGS__)
|
|
|
|
/* Command declaration macro. Builds an invocation function and a registration
|
|
function so the command name doesn't have to be repeated. */
|
|
#define FXLINK_COMMAND(_NAME, ...) DO_COMMAND1(_NAME, __COUNTER__, __VA_ARGS__)
|
|
/* This call forces __COUNTER__ to expand */
|
|
#define DO_COMMAND1(...) DO_COMMAND(__VA_ARGS__)
|
|
|
|
#define DO_COMMAND(_NAME, _COUNTER, ...) \
|
|
static int ___command_ ## _COUNTER(); \
|
|
static int ___invoke_command_ ## _COUNTER \
|
|
(int ___argc, char const **___argv) { \
|
|
MAP(MKVAR, ##__VA_ARGS__) \
|
|
if(!fxlink_tui_parse_args(___argc, ___argv, \
|
|
"" MAP(MKFMT, ##__VA_ARGS__) \
|
|
MAP(MKPTR, ##__VA_ARGS__))) return 1; \
|
|
return ___command_ ## _COUNTER( \
|
|
MAPn(MKCALL_1, MKCALL_n, ##__VA_ARGS__)); \
|
|
} \
|
|
__attribute__((constructor)) \
|
|
static void ___declare_command_ ## _COUNTER (void) { \
|
|
fxlink_tui_register_cmd(_NAME, ___invoke_command_ ## _COUNTER); \
|
|
} \
|
|
static int ___command_ ## _COUNTER(MAPn(MKFML_1, MKFML_n, ##__VA_ARGS__))
|
|
|
|
/* Make the format string for an argument */
|
|
#define MKFMT(_TV) MKFMT_ ## _TV
|
|
#define MKFMT_INT(_X) "i"
|
|
#define MKFMT_STRING(_X) "s"
|
|
#define MKFMT_VARIADIC(_X) "*"
|
|
#define MKFMT_DEVICE(_X) "d"
|
|
|
|
/* Make the formal function parameter for an argument */
|
|
#define MKFML_1(_TV) MKFML_ ## _TV
|
|
#define MKFML_n(_TV) , MKFML_1(_TV)
|
|
#define MKFML_INT(_X) int _X
|
|
#define MKFML_STRING(_X) char const * _X
|
|
#define MKFML_VARIADIC(_X) char const ** _X
|
|
#define MKFML_DEVICE(_X) struct fxlink_device * _X
|
|
|
|
/* Create a variable */
|
|
#define MKVAR(_TV) MKFML_1(_TV);
|
|
|
|
/* Make a pointer to an argument (sadly we can't get the name directly) */
|
|
#define MKPTR(_TV) , MKPTR_ ## _TV
|
|
#define MKPTR_INT(_X) &_X
|
|
#define MKPTR_STRING(_X) &_X
|
|
#define MKPTR_VARIADIC(_X) &_X
|
|
#define MKPTR_DEVICE(_X) &_X
|
|
|
|
/* Pass a variable as a function argument */
|
|
#define MKCALL_1(_TV) MKCALL_ ## _TV
|
|
#define MKCALL_n(_TV) , MKCALL_1(_TV)
|
|
#define MKCALL_INT(_X) _X
|
|
#define MKCALL_STRING(_X) _X
|
|
#define MKCALL_VARIADIC(_X) _X
|
|
#define MKCALL_DEVICE(_X) _X
|