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 <alangiderick@gmail.com>
This commit is contained in:
Thakee Nathees 2021-06-24 23:08:44 +05:30 committed by GitHub
parent a6b20787f8
commit a22c4cb90d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 654 additions and 61 deletions

View File

@ -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

View File

@ -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;
}

View File

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

View File

@ -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"

21
cli/thirdparty/argparse/LICENSE vendored Normal file
View File

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2012-2013 Yecheng Fu <cofyc.jackson@gmail.com>
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.

388
cli/thirdparty/argparse/argparse.c vendored Normal file
View File

@ -0,0 +1,388 @@
/**
* Copyright (C) 2012-2015 Yecheng Fu <cofyc.jackson at gmail dot com>
* All rights reserved.
*
* Use of this source code is governed by a MIT-style license that can be found
* in the LICENSE file.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <errno.h>
#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("=<int>");
}
if (options->type == ARGPARSE_OPT_FLOAT) {
len += strlen("=<flt>");
} else if (options->type == ARGPARSE_OPT_STRING) {
len += strlen("=<str>");
}
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, "=<int>");
} else if (options->type == ARGPARSE_OPT_FLOAT) {
pos += fprintf(stdout, "=<flt>");
} else if (options->type == ARGPARSE_OPT_STRING) {
pos += fprintf(stdout, "=<str>");
}
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);
}

130
cli/thirdparty/argparse/argparse.h vendored Normal file
View File

@ -0,0 +1,130 @@
/**
* Copyright (C) 2012-2015 Yecheng Fu <cofyc.jackson at gmail dot com>
* 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 <stdint.h>
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

View File

@ -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).