295 lines
9.2 KiB
C
295 lines
9.2 KiB
C
////////////////////////////////
|
|
// Functions: Command Line Parsing
|
|
|
|
MR4TH_SYMBOL MR4TH_READ_ONLY
|
|
CMDLN_Params cmdln__params_nil = {0};
|
|
|
|
#define cmdln_params_nil (CMDLN_Params*)(&cmdln__params_nil)
|
|
|
|
MR4TH_SYMBOL CMDLN*
|
|
cmdln_from_args(Arena *arena, String8List *args){
|
|
CMDLN *cmdln = push_array(arena, CMDLN, 1);
|
|
cmdln->raw = str8_list_copy(arena, args);
|
|
|
|
String8Node *node = cmdln->raw.first;
|
|
|
|
// first string is 'program'
|
|
if (node != 0){
|
|
cmdln->program = node->string;
|
|
node = node->next;
|
|
}
|
|
|
|
// consume string nodes
|
|
B32 forced_input = 0;
|
|
for (;node != 0;){
|
|
String8 string = str8_skip_chop_whitespace(node->string);
|
|
node = node->next;
|
|
|
|
// check if argument is a flag
|
|
B32 is_flag = 0;
|
|
if (!forced_input){
|
|
is_flag = (string.size != 0 && string.str[0] == '-');
|
|
}
|
|
|
|
// parse flag
|
|
if (is_flag){
|
|
|
|
// long flag
|
|
B32 is_long_flag = (string.size > 1 && string.str[1] == '-');
|
|
if (is_long_flag){
|
|
|
|
// end 'normal' mode
|
|
B32 double_dash = (string.size == 2);
|
|
if (double_dash){
|
|
forced_input = 1;
|
|
}
|
|
|
|
// parse long flag
|
|
if (!double_dash){
|
|
String8 flag_whole = str8_skip(string, 2);
|
|
|
|
// parameter delimter
|
|
U64 delim = flag_whole.size;
|
|
for (U8 *ptr = flag_whole.str, *opl = flag_whole.str + flag_whole.size;
|
|
ptr < opl; ptr += 1){
|
|
if (*ptr == '=' || *ptr == ':'){
|
|
delim = (U64)(ptr - flag_whole.str);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// split flag at delimiter
|
|
String8 flag_name = str8_prefix(flag_whole, delim);
|
|
String8 flag_param = str8_skip(flag_whole, delim + 1);
|
|
|
|
// if have a param delimiter at end
|
|
// then use the next argument as the flag_param
|
|
if (delim == flag_whole.size - 1){
|
|
if (node != 0){
|
|
flag_param = node->string;
|
|
node = node->next;
|
|
}
|
|
}
|
|
|
|
// parse parameters
|
|
CMDLN_Params *params = cmdln_params_from_string(arena, flag_param);
|
|
|
|
// store flag node
|
|
{
|
|
CMDLN_Node *cmdlnnode = push_array(arena, CMDLN_Node, 1);
|
|
SLLQueuePush(cmdln->first, cmdln->last, cmdlnnode);
|
|
cmdlnnode->string = flag_name;
|
|
cmdlnnode->params = params;
|
|
cmdln->flag_count += 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
// short flags
|
|
if (!is_long_flag){
|
|
String8 short_flags = str8_skip(string, 1);
|
|
|
|
U8 *flag = short_flags.str;
|
|
U8 *flagopl = short_flags.str + short_flags.size;
|
|
for (; flag < flagopl; flag += 1){
|
|
U8 *flagptr = flag;
|
|
|
|
// check for parameters
|
|
String8 flag_param = {0};
|
|
if (flag + 1 < flagopl &&
|
|
(flag[1] == '=' || flag[1] == ':')){
|
|
flag_param = str8_range(flag + 2, flagopl);
|
|
if (flag_param.size == 0){
|
|
if (node != 0){
|
|
flag_param = node->string;
|
|
node = node->next;
|
|
}
|
|
}
|
|
|
|
// kill the flag loop after finding parameters
|
|
flag = flagopl;
|
|
}
|
|
|
|
// parse parameters
|
|
CMDLN_Params *params = cmdln_params_from_string(arena, flag_param);
|
|
|
|
// store flag node
|
|
{
|
|
CMDLN_Node *cmdlnnode = push_array(arena, CMDLN_Node, 1);
|
|
SLLQueuePush(cmdln->first, cmdln->last, cmdlnnode);
|
|
cmdlnnode->string = CLiteral(String8){flagptr, 1};
|
|
cmdlnnode->params = params;
|
|
cmdln->flag_count += 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// parse input
|
|
if (!is_flag){
|
|
String8 input_string = string;
|
|
|
|
// store input node
|
|
{
|
|
CMDLN_Node *cmdlnnode = push_array(arena, CMDLN_Node, 1);
|
|
SLLQueuePush(cmdln->first, cmdln->last, cmdlnnode);
|
|
cmdlnnode->string = input_string;
|
|
cmdln->input_count += 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
// pointer arrays
|
|
cmdln->inputs = push_array(arena, CMDLN_Node*, cmdln->input_count);
|
|
cmdln->flags = push_array(arena, CMDLN_Node*, cmdln->flag_count);
|
|
{
|
|
CMDLN_Node **inputptr = cmdln->inputs;
|
|
CMDLN_Node **flagptr = cmdln->flags;
|
|
for (CMDLN_Node *node = cmdln->first;
|
|
node != 0;
|
|
node = node->next){
|
|
if (node->params == 0){
|
|
*inputptr = node;
|
|
inputptr += 1;
|
|
}
|
|
else{
|
|
*flagptr = node;
|
|
flagptr += 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
return(cmdln);
|
|
}
|
|
|
|
MR4TH_SYMBOL CMDLN_Params*
|
|
cmdln_params_from_string(Arena *arena, String8 flag_param){
|
|
CMDLN_Params *params = cmdln_params_nil;
|
|
if (flag_param.size > 0){
|
|
params = push_array(arena, CMDLN_Params, 1);
|
|
params->raw = flag_param;
|
|
params->list = str8_split(arena, flag_param, (U8*)",", 1);
|
|
}
|
|
return(params);
|
|
}
|
|
|
|
MR4TH_SYMBOL U64
|
|
cmdln_input_count(CMDLN *cmdln){
|
|
return(cmdln->input_count);
|
|
}
|
|
|
|
MR4TH_SYMBOL String8
|
|
cmdln_input_from_idx(CMDLN *cmdln, U64 idx){
|
|
String8 result = {0};
|
|
if (idx < cmdln->input_count){
|
|
result = cmdln->inputs[idx]->string;
|
|
}
|
|
return(result);
|
|
}
|
|
|
|
MR4TH_SYMBOL U64
|
|
cmdln_flag_count(CMDLN *cmdln){
|
|
return(cmdln->flag_count);
|
|
}
|
|
|
|
MR4TH_SYMBOL CMDLN_Flag*
|
|
cmdln_flag_from_idx(CMDLN *cmdln, U64 idx){
|
|
CMDLN_Flag *result = 0;
|
|
if (idx < cmdln->flag_count){
|
|
result = cmdln->flags[idx];
|
|
}
|
|
return(result);
|
|
}
|
|
|
|
MR4TH_SYMBOL CMDLN_Params*
|
|
cmdln_get_params(CMDLN *cmdln, String8 flagstr, char abbrev){
|
|
CMDLN_Params *result = 0;
|
|
for (CMDLN_Node *node = cmdln->first;
|
|
node != 0;
|
|
node = node->next){
|
|
if (node->params != 0){
|
|
if (str8_match(flagstr, node->string, 0) ||
|
|
(node->string.size == 1 && node->string.str[0] == (U8)abbrev)){
|
|
result = node->params;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return(result);
|
|
}
|
|
|
|
MR4TH_SYMBOL B32
|
|
cmdln_has_flag(CMDLN *cmdln, String8 flagstr, char abbrev){
|
|
CMDLN_Params *params = cmdln_get_params(cmdln, flagstr, abbrev);
|
|
B32 result = (params != 0);
|
|
return(result);
|
|
}
|
|
|
|
MR4TH_SYMBOL String8
|
|
cmdln_get_str8(CMDLN *cmdln, String8 flagstr, char abbrev){
|
|
CMDLN_Params *params = cmdln_get_params(cmdln, flagstr, abbrev);
|
|
String8 result = {0};
|
|
if (params != 0){
|
|
result = params->raw;
|
|
}
|
|
return(result);
|
|
}
|
|
|
|
MR4TH_SYMBOL S64
|
|
cmdln_get_s64(CMDLN *cmdln, String8 flagstr, char abbrev){
|
|
String8 str = cmdln_get_str8(cmdln, flagstr, abbrev);
|
|
S64 result = cmdln_s64_from_str8(str);
|
|
return(result);
|
|
}
|
|
|
|
MR4TH_SYMBOL F64
|
|
cmdln_get_f64(CMDLN *cmdln, String8 flagstr, char abbrev){
|
|
String8 str = cmdln_get_str8(cmdln, flagstr, abbrev);
|
|
F64 result = cmdln_f64_from_str8(str);
|
|
return(result);
|
|
}
|
|
|
|
MR4TH_SYMBOL S64
|
|
cmdln_s64_from_str8(String8 valstr){
|
|
S64 result = s64_from_str8_c_syntax(valstr);
|
|
return(result);
|
|
}
|
|
|
|
MR4TH_SYMBOL F64
|
|
cmdln_f64_from_str8(String8 valstr){
|
|
F64 result = f64_from_str8(valstr);
|
|
return(result);
|
|
}
|
|
|
|
MR4TH_SYMBOL void
|
|
cmdln_dump(Arena *arena, String8List *out, CMDLN *cmdln, U32 indent){
|
|
// raw
|
|
str8_list_pushf(arena, out, "%Nraw:\n", indent);
|
|
for (String8Node *node = cmdln->raw.first;
|
|
node != 0;
|
|
node = node->next){
|
|
str8_list_pushf(arena, out, "%N%S\n", indent + 1, node->string);
|
|
}
|
|
|
|
// program
|
|
str8_list_pushf(arena, out, "%Nprogram: %S\n", indent, cmdln->program);
|
|
|
|
// input nodes
|
|
str8_list_pushf(arena, out, "%Nnodes:\n", indent);
|
|
for (CMDLN_Node *cmdlnnode = cmdln->first;
|
|
cmdlnnode != 0;
|
|
cmdlnnode = cmdlnnode->next){
|
|
if (cmdlnnode->params == 0){
|
|
str8_list_pushf(arena, out, "%N[input] %S\n", indent + 1, cmdlnnode->string);
|
|
}
|
|
else{
|
|
str8_list_pushf(arena, out, "%N[flag ] %S\n", indent + 1, cmdlnnode->string);
|
|
for (String8Node *node = cmdlnnode->params->list.first;
|
|
node != 0;
|
|
node = node->next){
|
|
str8_list_pushf(arena, out, "%N[param] %S\n", indent + 2, node->string);
|
|
}
|
|
}
|
|
}
|
|
}
|