From a22c4cb90da21dedaaecabe4b8cc155d0e0e51f5 Mon Sep 17 00:00:00 2001 From: Thakee Nathees Date: Thu, 24 Jun 2021 23:08:44 +0530 Subject: [PATCH] CLI argparse implementation. (#136) * argparse library added to third parth. We're using argparse (https://github.com/cofyc/argparse) repo to parse cli args. * parsed arguments applied to the cli. Co-authored-by: Derick Alangi --- build.bat | 33 ++- cli/main.c | 127 +++++++--- cli/repl.c | 3 - cli/thirdparty.c | 6 +- cli/thirdparty/argparse/LICENSE | 21 ++ cli/thirdparty/argparse/argparse.c | 388 +++++++++++++++++++++++++++++ cli/thirdparty/argparse/argparse.h | 130 ++++++++++ docs/TODO.txt | 7 +- 8 files changed, 654 insertions(+), 61 deletions(-) create mode 100644 cli/thirdparty/argparse/LICENSE create mode 100644 cli/thirdparty/argparse/argparse.c create mode 100644 cli/thirdparty/argparse/argparse.h diff --git a/build.bat b/build.bat index 518a38f..d55854c 100644 --- a/build.bat +++ b/build.bat @@ -3,6 +3,7 @@ :: Distributed Under The MIT License @echo off +Pushd "%~dp0" :: ---------------------------------------------------------------------------- :: PARSE COMMAND LINE ARGS @@ -20,16 +21,16 @@ shift :PARSE_ARGS if (%1)==(-clean) goto :CLEAN -if (%1)==(-d) set debug_build=%2&& goto :SHIFT_ARG_2 -if (%1)==(-s) set shared_lib=%~2&& goto :SHIFT_ARG_2 +if (%1)==(-r) set debug_build=false&& goto :SHIFT_ARG_1 +if (%1)==(-s) set shared_lib=true&& goto :SHIFT_ARG_1 if (%1)==() goto :CHECK_MSVC :PRINT_USAGE echo Usage: call build.bat [options ...] echo options: -echo -d trut/false Set false to build release binary. Default is true. -echo -s true/false Set true to build shared/dynamic library. Default is false. -echo -clean Clean all compiled/generated intermediate binary. +echo -r Compile the release version of pocketlang (default = debug) +echo -s Link the pocket as shared library (default = static link). +echo -clean Clean all compiled/generated intermediate binary. goto :END :: ---------------------------------------------------------------------------- @@ -116,9 +117,19 @@ cl /nologo /c %addnl_cdefines% %addnl_cflags% %root_dir%src\*.c if errorlevel 1 goto :FAIL :: If compiling shared lib, jump pass the lib/cli binaries. -if "%shared_lib%"=="true" goto :BUILD_DLL +if "%shared_lib%"=="true" ( + set pklib=..\bin\pocket-dll.lib +) else ( + set pklib=..\bin\pocket.lib +) -lib /nologo %addnl_linkflags% /OUT:..\bin\pocket.lib *.obj +:: If compiling shared lib, jump pass the lib/cli binaries. +if "%shared_lib%"=="true" goto :SHARED +lib /nologo %addnl_linkflags% /OUT:%pklib% *.obj +goto :SRC_END +:SHARED +link /nologo /dll /out:..\bin\pocket.dll /implib:%pklib% *.obj +:SRC_END if errorlevel 1 goto :FAIL :: Go inside cli\ from src\ build all cli files. @@ -127,7 +138,7 @@ cl /nologo /c %addnl_cdefines% %addnl_cflags% /I%root_dir%src\include\ %root_dir if errorlevel 1 goto :FAIL :: Compile the cli executable. -cl /nologo %addnl_cdefines% *.obj ..\bin\pocket.lib /Fe..\bin\pocket.exe +cl /nologo %addnl_cdefines% *.obj %pklib% /Fe..\bin\pocket.exe if errorlevel 1 goto :FAIL :: Navigate to the build directory. @@ -135,9 +146,8 @@ cd ..\..\ goto :SUCCESS :BUILD_DLL -link /nologo /dll /out:..\bin\pocket.dll /implib:..\bin\pocket-dll.lib *.obj -if errorlevel 1 goto :FAIL -cd ..\..\ + + goto :SUCCESS :CLEAN @@ -164,6 +174,7 @@ exit /b 1 goto :END :END +popd endlocal goto :eof diff --git a/cli/main.c b/cli/main.c index 8b6d575..c27f704 100644 --- a/cli/main.c +++ b/cli/main.c @@ -4,12 +4,13 @@ */ #include "internal.h" - #include "modules.h" +#include "thirdparty/argparse/argparse.h" + // FIXME: Everything below here is temporary and for testing. -void repl(PKVM* vm, const PkCompileOptions* options); +int repl(PKVM* vm, const PkCompileOptions* options); const char* read_line(uint32_t* length); // --------------------------------------- @@ -121,17 +122,8 @@ PkStringPtr loadScript(PKVM* vm, const char* path) { return result; } -int main(int argc, char** argv) { - - //const char* usage = "usage: pocket [-c cmd | file]\n"; - - // TODO: implement arg parse, REPL. - - //if (argc < 2) { - // printf("%s\n%s", notice, help); - // return 0; - //} - +// Create new pocket VM and set it's configuration. +static PKVM* intializePocketVM() { PkConfiguration config = pkNewConfiguration(); config.error_fn = errorFunction; config.write_fn = writeFunction; @@ -143,47 +135,100 @@ int main(int argc, char** argv) { config.load_script_fn = loadScript; config.resolve_path_fn = resolvePath; - PkCompileOptions options = pkNewCompilerOptions(); - options.debug = true; // TODO: update this with cli args. (tco disabled). + return pkNewVM(&config); +} - PKVM* vm = pkNewVM(&config); +int main(int argc, char** argv) { + // Parse command line arguments. + + const char* usage[] = { + "pocket ... [-c cmd | file] ...", + NULL, + }; + + const char* cmd = NULL; + int debug = false, help = false, quiet = false, version = false; + struct argparse_option cli_opts[] = { + OPT_STRING('c', "cmd", (void*)&cmd, + "Evaluate and run the passed string.", NULL, 0, 0), + + OPT_BOOLEAN('d', "debug", (void*)&debug, + "Compile and run the debug version.", NULL, 0, 0), + + OPT_BOOLEAN('h', "help", (void*)&help, + "Prints this help message and exit.", NULL, 0, 0), + + OPT_BOOLEAN('q', "quiet", (void*)&quiet, + "Don't print version and copyright statement on REPL startup.", + NULL, 0, 0), + + OPT_BOOLEAN('v', "version", &version, + "Prints the pocketlang version and exit.", NULL, 0, 0), + OPT_END(), + }; + + // Parse the options. + struct argparse argparse; + argparse_init(&argparse, cli_opts, usage, 0); + argc = argparse_parse(&argparse, argc, argv); + + if (help) { // pocket --help. + argparse_usage(&argparse); + return 0; + } + + if (version) { // pocket --version + fprintf(stdout, "pocketlang %s\n", PK_VERSION_STRING); + return 0; + } + + int exitcode = 0; + + // Create and initialize pocket VM. + PKVM* vm = intializePocketVM(); VmUserData user_data; user_data.repl_mode = false; pkSetUserData(vm, &user_data); registerModules(vm); - // FIXME: this is temp till arg parse implemented. - PkResult result; + PkCompileOptions options = pkNewCompilerOptions(); + options.debug = debug; - if (argc == 1) { - options.repl_mode = true; - repl(vm, &options); + if (cmd != NULL) { // pocket -c "print('foo')" - } if (argc >= 3 && strcmp(argv[1], "-c") == 0) { - - PkStringPtr source = { argv[2], NULL, NULL }; + PkStringPtr source = { cmd, NULL, NULL }; PkStringPtr path = { "$(Source)", NULL, NULL }; + PkResult result = pkInterpretSource(vm, source, path, NULL); + exitcode = (int)result; - result = pkInterpretSource(vm, source, path, NULL); - pkFreeVM(vm); - return result; - } - - PkStringPtr resolved = resolvePath(vm, ".", argv[1]); - PkStringPtr source = loadScript(vm, resolved.string); - - if (source.string != NULL) { - result = pkInterpretSource(vm, source, resolved, &options); - - } else { - result = PK_RESULT_COMPILE_ERROR; - fprintf(stderr, "Error: cannot open file at \"%s\"\n", resolved.string); - if (resolved.on_done != NULL) resolved.on_done(vm, resolved); - if (source.on_done != NULL) source.on_done(vm, source); + } if (argc == 0) { // Run on REPL mode. + + // Print the copyright and license notice, if --quiet not set. + if (!quiet) { + printf("%s", CLI_NOTICE); + } + + options.repl_mode = true; + exitcode = repl(vm, &options); + + } else { // pocket file.pk ... + + PkStringPtr resolved = resolvePath(vm, ".", argv[0]); + PkStringPtr source = loadScript(vm, resolved.string); + + if (source.string != NULL) { + PkResult result = pkInterpretSource(vm, source, resolved, &options); + exitcode = (int)result; + } else { + fprintf(stderr, "Error: cannot open file at \"%s\"\n", resolved.string); + if (resolved.on_done != NULL) resolved.on_done(vm, resolved); + if (source.on_done != NULL) source.on_done(vm, source); + } } + // Cleanup the VM and exit. pkFreeVM(vm); - return result; + return exitcode; } diff --git a/cli/repl.c b/cli/repl.c index d21fe71..4efec16 100644 --- a/cli/repl.c +++ b/cli/repl.c @@ -62,9 +62,6 @@ int repl(PKVM* vm, const PkCompileOptions* options) { VmUserData* user_data = (VmUserData*)pkGetUserData(vm); user_data->repl_mode = true; - // Print the copyright and license notice. - printf("%s", CLI_NOTICE); - // The main module that'll be used to compile and execute the input source. PkHandle* module = pkNewModule(vm, "$(REPL)"); diff --git a/cli/thirdparty.c b/cli/thirdparty.c index 3a5e8c9..36d87ea 100644 --- a/cli/thirdparty.c +++ b/cli/thirdparty.c @@ -9,9 +9,13 @@ // directory to the list of source directory. // Library : cwalk -// Source : https://github.com/likle/cwalk +// Source : https://github.com/likle/cwalk/ // Doc : https://likle.github.io/cwalk/ // About : Path library for C/C++. Cross-Platform for Windows, MacOS and // Linux. Supports UNIX and Windows path styles on those platforms. #include "thirdparty/cwalk/cwalk.c" +// Library : argparse +// Source : https://github.com/cofyc/argparse/ +// About : Command-line arguments parsing library. +#include "thirdparty/argparse/argparse.c" diff --git a/cli/thirdparty/argparse/LICENSE b/cli/thirdparty/argparse/LICENSE new file mode 100644 index 0000000..6c6cffd --- /dev/null +++ b/cli/thirdparty/argparse/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2012-2013 Yecheng Fu + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. \ No newline at end of file diff --git a/cli/thirdparty/argparse/argparse.c b/cli/thirdparty/argparse/argparse.c new file mode 100644 index 0000000..66691e3 --- /dev/null +++ b/cli/thirdparty/argparse/argparse.c @@ -0,0 +1,388 @@ +/** + * Copyright (C) 2012-2015 Yecheng Fu + * All rights reserved. + * + * Use of this source code is governed by a MIT-style license that can be found + * in the LICENSE file. + */ +#include +#include +#include +#include +#include +#include "argparse.h" + +#define OPT_UNSET 1 +#define OPT_LONG (1 << 1) + +static const char * +prefix_skip(const char *str, const char *prefix) +{ + size_t len = strlen(prefix); + return strncmp(str, prefix, len) ? NULL : str + len; +} + +static int +prefix_cmp(const char *str, const char *prefix) +{ + for (;; str++, prefix++) + if (!*prefix) { + return 0; + } else if (*str != *prefix) { + return (unsigned char)*prefix - (unsigned char)*str; + } +} + +static void +argparse_error(struct argparse *self, const struct argparse_option *opt, + const char *reason, int flags) +{ + (void)self; + if (flags & OPT_LONG) { + fprintf(stderr, "error: option `--%s` %s\n", opt->long_name, reason); + } else { + fprintf(stderr, "error: option `-%c` %s\n", opt->short_name, reason); + } + exit(EXIT_FAILURE); +} + +static int +argparse_getvalue(struct argparse *self, const struct argparse_option *opt, + int flags) +{ + const char *s = NULL; + if (!opt->value) + goto skipped; + switch (opt->type) { + case ARGPARSE_OPT_BOOLEAN: + if (flags & OPT_UNSET) { + *(int *)opt->value = *(int *)opt->value - 1; + } else { + *(int *)opt->value = *(int *)opt->value + 1; + } + if (*(int *)opt->value < 0) { + *(int *)opt->value = 0; + } + break; + case ARGPARSE_OPT_BIT: + if (flags & OPT_UNSET) { + *(int *)opt->value &= ~opt->data; + } else { + *(int *)opt->value |= opt->data; + } + break; + case ARGPARSE_OPT_STRING: + if (self->optvalue) { + *(const char **)opt->value = self->optvalue; + self->optvalue = NULL; + } else if (self->argc > 1) { + self->argc--; + *(const char **)opt->value = *++self->argv; + } else { + argparse_error(self, opt, "requires a value", flags); + } + break; + case ARGPARSE_OPT_INTEGER: + errno = 0; + if (self->optvalue) { + *(int *)opt->value = strtol(self->optvalue, (char **)&s, 0); + self->optvalue = NULL; + } else if (self->argc > 1) { + self->argc--; + *(int *)opt->value = strtol(*++self->argv, (char **)&s, 0); + } else { + argparse_error(self, opt, "requires a value", flags); + } + if (errno == ERANGE) + argparse_error(self, opt, "numerical result out of range", flags); + if (s[0] != '\0') // no digits or contains invalid characters + argparse_error(self, opt, "expects an integer value", flags); + break; + case ARGPARSE_OPT_FLOAT: + errno = 0; + if (self->optvalue) { + *(float *)opt->value = strtof(self->optvalue, (char **)&s); + self->optvalue = NULL; + } else if (self->argc > 1) { + self->argc--; + *(float *)opt->value = strtof(*++self->argv, (char **)&s); + } else { + argparse_error(self, opt, "requires a value", flags); + } + if (errno == ERANGE) + argparse_error(self, opt, "numerical result out of range", flags); + if (s[0] != '\0') // no digits or contains invalid characters + argparse_error(self, opt, "expects a numerical value", flags); + break; + default: + assert(0); + } + +skipped: + if (opt->callback) { + return opt->callback(self, opt); + } + + return 0; +} + +static void +argparse_options_check(const struct argparse_option *options) +{ + for (; options->type != ARGPARSE_OPT_END; options++) { + switch (options->type) { + case ARGPARSE_OPT_END: + case ARGPARSE_OPT_BOOLEAN: + case ARGPARSE_OPT_BIT: + case ARGPARSE_OPT_INTEGER: + case ARGPARSE_OPT_FLOAT: + case ARGPARSE_OPT_STRING: + case ARGPARSE_OPT_GROUP: + continue; + default: + fprintf(stderr, "wrong option type: %d", options->type); + break; + } + } +} + +static int +argparse_short_opt(struct argparse *self, const struct argparse_option *options) +{ + for (; options->type != ARGPARSE_OPT_END; options++) { + if (options->short_name == *self->optvalue) { + self->optvalue = self->optvalue[1] ? self->optvalue + 1 : NULL; + return argparse_getvalue(self, options, 0); + } + } + return -2; +} + +static int +argparse_long_opt(struct argparse *self, const struct argparse_option *options) +{ + for (; options->type != ARGPARSE_OPT_END; options++) { + const char *rest; + int opt_flags = 0; + if (!options->long_name) + continue; + + rest = prefix_skip(self->argv[0] + 2, options->long_name); + if (!rest) { + // negation disabled? + if (options->flags & OPT_NONEG) { + continue; + } + // only OPT_BOOLEAN/OPT_BIT supports negation + if (options->type != ARGPARSE_OPT_BOOLEAN && options->type != + ARGPARSE_OPT_BIT) { + continue; + } + + if (prefix_cmp(self->argv[0] + 2, "no-")) { + continue; + } + rest = prefix_skip(self->argv[0] + 2 + 3, options->long_name); + if (!rest) + continue; + opt_flags |= OPT_UNSET; + } + if (*rest) { + if (*rest != '=') + continue; + self->optvalue = rest + 1; + } + return argparse_getvalue(self, options, opt_flags | OPT_LONG); + } + return -2; +} + +int +argparse_init(struct argparse *self, struct argparse_option *options, + const char *const *usages, int flags) +{ + memset(self, 0, sizeof(*self)); + self->options = options; + self->usages = usages; + self->flags = flags; + self->description = NULL; + self->epilog = NULL; + return 0; +} + +void +argparse_describe(struct argparse *self, const char *description, + const char *epilog) +{ + self->description = description; + self->epilog = epilog; +} + +int +argparse_parse(struct argparse *self, int argc, const char **argv) +{ + self->argc = argc - 1; + self->argv = argv + 1; + self->out = argv; + + argparse_options_check(self->options); + + for (; self->argc; self->argc--, self->argv++) { + const char *arg = self->argv[0]; + if (arg[0] != '-' || !arg[1]) { + if (self->flags & ARGPARSE_STOP_AT_NON_OPTION) { + goto end; + } + // if it's not option or is a single char '-', copy verbatim + self->out[self->cpidx++] = self->argv[0]; + continue; + } + // short option + if (arg[1] != '-') { + self->optvalue = arg + 1; + switch (argparse_short_opt(self, self->options)) { + case -1: + break; + case -2: + goto unknown; + } + while (self->optvalue) { + switch (argparse_short_opt(self, self->options)) { + case -1: + break; + case -2: + goto unknown; + } + } + continue; + } + // if '--' presents + if (!arg[2]) { + self->argc--; + self->argv++; + break; + } + // long option + switch (argparse_long_opt(self, self->options)) { + case -1: + break; + case -2: + goto unknown; + } + continue; + +unknown: + fprintf(stderr, "error: unknown option `%s`\n", self->argv[0]); + argparse_usage(self); + exit(EXIT_FAILURE); + } + +end: +/* Modified By : https://www.github.com/ThakeeNathees */ + memmove((void*)(self->out + self->cpidx), (void*)(self->argv), + self->argc * sizeof(*self->out)); +/* -------------------------------------------------- */ + self->out[self->cpidx + self->argc] = NULL; + + return self->cpidx + self->argc; +} + +void +argparse_usage(struct argparse *self) +{ + if (self->usages) { + fprintf(stdout, "Usage: %s\n", *self->usages++); + while (*self->usages && **self->usages) + fprintf(stdout, " or: %s\n", *self->usages++); + } else { + fprintf(stdout, "Usage:\n"); + } + + // print description + if (self->description) + fprintf(stdout, "%s\n", self->description); + +/* Modified By : https://www.github.com/ThakeeNathees */ +// fputc('\n', stdout); +/* -------------------------------------------------- */ + + const struct argparse_option *options; + + // figure out best width + size_t usage_opts_width = 0; + size_t len; + options = self->options; + for (; options->type != ARGPARSE_OPT_END; options++) { + len = 0; + if ((options)->short_name) { + len += 2; + } + if ((options)->short_name && (options)->long_name) { + len += 2; // separator ", " + } + if ((options)->long_name) { + len += strlen((options)->long_name) + 2; + } + if (options->type == ARGPARSE_OPT_INTEGER) { + len += strlen("="); + } + if (options->type == ARGPARSE_OPT_FLOAT) { + len += strlen("="); + } else if (options->type == ARGPARSE_OPT_STRING) { + len += strlen("="); + } + len = (len + 3) - ((len + 3) & 3); + if (usage_opts_width < len) { + usage_opts_width = len; + } + } + usage_opts_width += 4; // 4 spaces prefix + + options = self->options; + for (; options->type != ARGPARSE_OPT_END; options++) { + size_t pos = 0; + size_t pad = 0; + if (options->type == ARGPARSE_OPT_GROUP) { + fputc('\n', stdout); + fprintf(stdout, "%s", options->help); + fputc('\n', stdout); + continue; + } + pos = fprintf(stdout, " "); + if (options->short_name) { + pos += fprintf(stdout, "-%c", options->short_name); + } + if (options->long_name && options->short_name) { + pos += fprintf(stdout, ", "); + } + if (options->long_name) { + pos += fprintf(stdout, "--%s", options->long_name); + } + if (options->type == ARGPARSE_OPT_INTEGER) { + pos += fprintf(stdout, "="); + } else if (options->type == ARGPARSE_OPT_FLOAT) { + pos += fprintf(stdout, "="); + } else if (options->type == ARGPARSE_OPT_STRING) { + pos += fprintf(stdout, "="); + } + if (pos <= usage_opts_width) { + pad = usage_opts_width - pos; + } else { + fputc('\n', stdout); + pad = usage_opts_width; + } + fprintf(stdout, "%*s%s\n", (int)pad + 2, "", options->help); + } + + // print epilog + if (self->epilog) + fprintf(stdout, "%s\n", self->epilog); +} + +int +argparse_help_cb(struct argparse *self, const struct argparse_option *option) +{ + (void)option; + argparse_usage(self); + exit(EXIT_SUCCESS); +} \ No newline at end of file diff --git a/cli/thirdparty/argparse/argparse.h b/cli/thirdparty/argparse/argparse.h new file mode 100644 index 0000000..4933329 --- /dev/null +++ b/cli/thirdparty/argparse/argparse.h @@ -0,0 +1,130 @@ +/** + * Copyright (C) 2012-2015 Yecheng Fu + * All rights reserved. + * + * Use of this source code is governed by a MIT-style license that can be found + * in the LICENSE file. + */ +#ifndef ARGPARSE_H +#define ARGPARSE_H + +/* For c++ compatibility */ +#ifdef __cplusplus +extern "C" { +#endif + +#include + +struct argparse; +struct argparse_option; + +typedef int argparse_callback (struct argparse *self, + const struct argparse_option *option); + +enum argparse_flag { + ARGPARSE_STOP_AT_NON_OPTION = 1, +}; + +enum argparse_option_type { + /* special */ + ARGPARSE_OPT_END, + ARGPARSE_OPT_GROUP, + /* options with no arguments */ + ARGPARSE_OPT_BOOLEAN, + ARGPARSE_OPT_BIT, + /* options with arguments (optional or required) */ + ARGPARSE_OPT_INTEGER, + ARGPARSE_OPT_FLOAT, + ARGPARSE_OPT_STRING, +}; + +enum argparse_option_flags { + OPT_NONEG = 1, /* disable negation */ +}; + +/** + * argparse option + * + * `type`: + * holds the type of the option, you must have an ARGPARSE_OPT_END last in your + * array. + * + * `short_name`: + * the character to use as a short option name, '\0' if none. + * + * `long_name`: + * the long option name, without the leading dash, NULL if none. + * + * `value`: + * stores pointer to the value to be filled. + * + * `help`: + * the short help message associated to what the option does. + * Must never be NULL (except for ARGPARSE_OPT_END). + * + * `callback`: + * function is called when corresponding argument is parsed. + * + * `data`: + * associated data. Callbacks can use it like they want. + * + * `flags`: + * option flags. + */ +struct argparse_option { + enum argparse_option_type type; + const char short_name; + const char *long_name; + void *value; + const char *help; + argparse_callback *callback; + intptr_t data; + int flags; +}; + +/** + * argpparse + */ +struct argparse { + // user supplied + const struct argparse_option *options; + const char *const *usages; + int flags; + const char *description; // a description after usage + const char *epilog; // a description at the end + // internal context + int argc; + const char **argv; + const char **out; + int cpidx; + const char *optvalue; // current option value +}; + +// built-in callbacks +int argparse_help_cb(struct argparse *self, + const struct argparse_option *option); + +// built-in option macros +#define OPT_END() { ARGPARSE_OPT_END, 0, NULL, NULL, 0, NULL, 0, 0 } +#define OPT_BOOLEAN(...) { ARGPARSE_OPT_BOOLEAN, __VA_ARGS__ } +#define OPT_BIT(...) { ARGPARSE_OPT_BIT, __VA_ARGS__ } +#define OPT_INTEGER(...) { ARGPARSE_OPT_INTEGER, __VA_ARGS__ } +#define OPT_FLOAT(...) { ARGPARSE_OPT_FLOAT, __VA_ARGS__ } +#define OPT_STRING(...) { ARGPARSE_OPT_STRING, __VA_ARGS__ } +#define OPT_GROUP(h) { ARGPARSE_OPT_GROUP, 0, NULL, NULL, h, NULL, 0, 0 } +#define OPT_HELP() OPT_BOOLEAN('h', "help", NULL, \ + "show this help message and exit", \ + argparse_help_cb, 0, OPT_NONEG) + +int argparse_init(struct argparse *self, struct argparse_option *options, + const char *const *usages, int flags); +void argparse_describe(struct argparse *self, const char *description, + const char *epilog); +int argparse_parse(struct argparse *self, int argc, const char **argv); +void argparse_usage(struct argparse *self); + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/docs/TODO.txt b/docs/TODO.txt index a23b42c..d3463bc 100644 --- a/docs/TODO.txt +++ b/docs/TODO.txt @@ -1,10 +1,10 @@ // In good first issue -- Refactor is_done as an attribute. -- Implement argparse. // To implement. +- refactor the build.bat script. + - no __file__ for core modules. - def f(x) @@ -27,9 +27,6 @@ fn(a, b) // May be closure support? end -- In keyword. -- Structs (maybe enums). -- Implement file IO (require structs). - Implement utf8 support. - Implement gdb like debugger (add color print for readability). - Initialize imported scripts (require fiber based vm).