input option parsing

generics
cosmonaut 2021-04-28 15:21:51 -07:00
parent ca1835f98d
commit 272c809c69
13 changed files with 3303 additions and 28 deletions

View File

@ -23,8 +23,8 @@ find_package(LLVM)
include_directories(${CMAKE_SOURCE_DIR})
BISON_TARGET(Parser wraith.y ${CMAKE_CURRENT_BINARY_DIR}/y.tab.c COMPILE_FLAGS "-d -v -t")
FLEX_TARGET(Scanner wraith.lex ${CMAKE_CURRENT_BINARY_DIR}/lex.yy.c)
BISON_TARGET(Parser generators/wraith.y ${CMAKE_CURRENT_BINARY_DIR}/y.tab.c COMPILE_FLAGS "-d -v -t")
FLEX_TARGET(Scanner generators/wraith.lex ${CMAKE_CURRENT_BINARY_DIR}/lex.yy.c)
ADD_FLEX_BISON_DEPENDENCY(Scanner Parser)
@ -32,11 +32,21 @@ include_directories(${CMAKE_CURRENT_BINARY_DIR})
add_executable(
wraith
ast.c
stack.c
# Libs
lib/dropt/dropt.h
lib/dropt/dropt_string.h
lib/dropt/dropt.c
lib/dropt/dropt_string.c
lib/dropt/dropt_handlers.c
# Source
src/ast.h
src/stack.h
src/ast.c
src/stack.c
src/compiler.c
# Generated code
${BISON_Parser_OUTPUTS}
${FLEX_Scanner_OUTPUTS}
compiler.c
)
if(NOT MSVC)

View File

@ -1,11 +1,11 @@
%code requires {
#include "stack.h"
#include "../src/stack.h"
}
%{
#include <stdio.h>
#include "ast.h"
#include "stack.h"
#include "../src/ast.h"
#include "../src/stack.h"
void yyerror(FILE *fp, Stack *stack, char *s)
{

1627
lib/dropt/dropt.c Normal file

File diff suppressed because it is too large Load Diff

272
lib/dropt/dropt.h Normal file
View File

@ -0,0 +1,272 @@
/** dropt.h
*
* A deliberately rudimentary command-line option parser.
*
* Version 2.0.0
*
* Copyright (C) 2006-2018 James D. Lin <jamesdlin@berkeley.edu>
*
* The latest version of this file can be downloaded from:
* <http://www.taenarum.com/software/dropt/>
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
*
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
*
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef DROPT_H
#define DROPT_H
#include <stdio.h>
#include <wchar.h>
#if __STDC_VERSION__ >= 199901L
#include <stdint.h>
typedef uintptr_t dropt_uintptr;
#else
typedef size_t dropt_uintptr;
#endif
#ifdef __cplusplus
extern "C" {
#endif
#ifndef DROPT_USE_WCHAR
#if defined _UNICODE && (defined _MSC_VER || defined DROPT_NO_STRING_BUFFERS)
#define DROPT_USE_WCHAR 1
#endif
#endif
#ifdef DROPT_USE_WCHAR
/* This may be used for both char and string literals. */
#define DROPT_TEXT_LITERAL(s) L ## s
typedef wchar_t dropt_char;
#else
#define DROPT_TEXT_LITERAL(s) s
typedef char dropt_char;
#endif
enum
{
/* Errors in the range [0x00, 0x7F] are reserved for dropt. */
dropt_error_none,
dropt_error_unknown,
dropt_error_bad_configuration,
dropt_error_insufficient_memory,
dropt_error_invalid_option,
dropt_error_insufficient_arguments,
dropt_error_mismatch,
dropt_error_overflow,
dropt_error_underflow,
/* Errors in the range [0x80, 0xFFFF] are free for clients to use. */
dropt_error_custom_start = 0x80,
dropt_error_custom_last = 0xFFFF
};
typedef unsigned int dropt_error;
typedef unsigned char dropt_bool;
/* Opaque. */
typedef struct dropt_context dropt_context;
/* Forward declarations. */
typedef struct dropt_option dropt_option;
/** `dropt_option_handler_func` callbacks are responsible for parsing
* individual options and storing the parsed value.
*
* `dropt_option_handler_decl` may be used for declaring the callback
* functions (see the stock option handlers below for examples).
* `dropt_option_handler_func` is the actual function pointer type.
*
* `option` points to the `dropt_option` entry that matched the option
* supplied by the user. This will never be `NULL` when dropt invokes the
* handler.
*
* `optionArgument` will be `NULL` if no argument is specified for an option.
* It will be the empty string if the user explicitly passed an empty string
* as the argument (e.g. `--option=""`).
*
* An option that doesn't expect an argument still can receive a non-null
* value for `optionArgument` if the user explicitly specified one (e.g.
* `--option=arg`).
*
* If the option's argument is optional, the handler might be called twice:
* once with a candidate argument, and if that argument is rejected by the
* handler, again with no argument. Handlers should be aware of this if they
* have side-effects.
*
* `dest` is the client-specified pointer to a variable for the handler to
* modify.
*/
typedef dropt_error dropt_option_handler_decl(dropt_context* context,
const dropt_option* option,
const dropt_char* optionArgument,
void* dest);
typedef dropt_option_handler_decl* dropt_option_handler_func;
/** `dropt_error_handler_func` callbacks are responsible for generating error
* messages. The returned string must be allocated on the heap and must be
* freeable with `free()`.
*/
typedef dropt_char* (*dropt_error_handler_func)(dropt_error error,
const dropt_char* optionName,
const dropt_char* optionArgument,
void* handlerData);
/** `dropt_strncmp_func` callbacks allow callers to provide their own (possibly
* case-insensitive) string comparison function.
*/
typedef int (*dropt_strncmp_func)(const dropt_char* s, const dropt_char* t,
size_t n);
/** Properties defining each option:
*
* short_name:
* The option's short name (e.g. the 'h' in `-h`).
* Use '\0' if the option has no short name.
*
* long_name:
* The option's long name (e.g. "help" in `--help`).
* Use `NULL` if the option has no long name.
*
* description:
* The description shown when generating help.
* May be `NULL` for undocumented options.
*
* arg_description:
* The description for the option's argument (e.g. `--option=argument` or
* `--option argument`), printed when generating help.
* Use `NULL` if the option does not take an argument.
*
* handler:
* The handler callback and data invoked in response to encountering the
* option.
*
* dest:
* The address of a variable for the handler to modify, if necessary.
*
* attr:
* Miscellaneous attributes. See below.
*
* extra_data:
* Additional callback data for the handler.
*/
struct dropt_option
{
dropt_char short_name;
const dropt_char* long_name;
const dropt_char* description;
const dropt_char* arg_description;
dropt_option_handler_func handler;
void* dest;
unsigned int attr;
dropt_uintptr extra_data;
};
/** Bitwise flags for option attributes:
*
* dropt_attr_halt:
* Stop processing when this option is encountered.
*
* dropt_attr_hidden:
* Don't list the option when generating help. Use this for undocumented
* options.
*
* dropt_attr_optional_val:
* The option's argument is optional. If an option has this attribute,
* the handler callback may be invoked twice (once with a potential
* argument, and if that fails, again with a `NULL` argument).
*/
enum
{
dropt_attr_halt = (1 << 0),
dropt_attr_hidden = (1 << 1),
dropt_attr_optional_val = (1 << 2)
};
typedef struct dropt_help_params
{
unsigned int indent;
unsigned int description_start_column;
dropt_bool blank_lines_between_options;
} dropt_help_params;
dropt_context* dropt_new_context(const dropt_option* options);
void dropt_free_context(dropt_context* context);
const dropt_option* dropt_get_options(const dropt_context* context);
void dropt_set_error_handler(dropt_context* context,
dropt_error_handler_func handler,
void* handlerData);
void dropt_set_strncmp(dropt_context* context, dropt_strncmp_func cmp);
/* Use this only for backward compatibility purposes. */
void dropt_allow_concatenated_arguments(dropt_context* context,
dropt_bool allow);
dropt_char** dropt_parse(dropt_context* context, int argc, dropt_char** argv);
dropt_error dropt_get_error(const dropt_context* context);
void dropt_get_error_details(const dropt_context* context,
dropt_char** optionName,
dropt_char** optionArgument);
const dropt_char* dropt_get_error_message(dropt_context* context);
void dropt_clear_error(dropt_context* context);
#ifndef DROPT_NO_STRING_BUFFERS
dropt_char* dropt_default_error_handler(dropt_error error,
const dropt_char* optionName,
const dropt_char* optionArgument);
void dropt_init_help_params(dropt_help_params* helpParams);
dropt_char* dropt_get_help(const dropt_context* context,
const dropt_help_params* helpParams);
void dropt_print_help(FILE* f, const dropt_context* context,
const dropt_help_params* helpParams);
#endif
/* Stock option handlers for common types. */
dropt_option_handler_decl dropt_handle_bool;
dropt_option_handler_decl dropt_handle_verbose_bool;
dropt_option_handler_decl dropt_handle_int;
dropt_option_handler_decl dropt_handle_uint;
dropt_option_handler_decl dropt_handle_double;
dropt_option_handler_decl dropt_handle_string;
dropt_option_handler_decl dropt_handle_const;
#define DROPT_MISUSE(message) dropt_misuse(message, __FILE__, __LINE__)
void dropt_misuse(const char* message, const char* filename, int line);
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* DROPT_H */

520
lib/dropt/dropt_handlers.c Normal file
View File

@ -0,0 +1,520 @@
/** dropt_handlers.c
*
* Default type handlers for dropt.
*
* Copyright (C) 2006-2018 James D. Lin <jamesdlin@berkeley.edu>
*
* The latest version of this file can be downloaded from:
* <http://www.taenarum.com/software/dropt/>
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
*
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
*
* 3. This notice may not be removed or altered from any source distribution.
*/
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <float.h>
#include <errno.h>
#include <assert.h>
#include "dropt.h"
#include "dropt_string.h"
#define CONCAT(s, t) s ## t
#define XCONCAT(s, t) CONCAT(s, t)
#define STATIC_ASSERT(cond) \
enum { XCONCAT(static_assert_line_, __LINE__) = 1 / ((cond) != 0) }
#define ABS(x) (((x) < 0) ? -(x) : (x))
typedef enum { false, true } bool;
/** dropt_handle_bool
*
* Stores a boolean value parsed from the given string if possible.
*
* PARAMETERS:
* IN/OUT context : The options context.
* IN option : The matched option. For more information, see
* `dropt_option_handler_decl`.
* IN optionArgument : A string representing a boolean value (0 or 1).
* If `NULL`, the boolean value is assumed to be true.
* OUT dest : A `dropt_bool*`.
* On success, set to the interpreted boolean value.
* On error, left untouched.
*
* RETURNS:
* dropt_error_none
* dropt_error_unknown
* dropt_error_bad_configuration
* dropt_error_mismatch
*/
dropt_error
dropt_handle_bool(dropt_context* context,
const dropt_option* option,
const dropt_char* optionArgument,
void* dest)
{
dropt_error err = dropt_error_none;
bool val = false;
dropt_bool* out = dest;
if (out == NULL)
{
DROPT_MISUSE("No handler destination specified.");
err = dropt_error_bad_configuration;
}
else if (optionArgument == NULL)
{
/* No explicit argument implies that the option is being turned on. */
val = true;
}
else if (optionArgument[0] == DROPT_TEXT_LITERAL('\0'))
{
err = dropt_error_mismatch;
}
else
{
unsigned int i = 0;
err = dropt_handle_uint(context, option, optionArgument, &i);
if (err == dropt_error_none)
{
switch (i)
{
case 0:
val = false;
break;
case 1:
val = true;
break;
default:
err = dropt_error_mismatch;
break;
}
}
else if (err == dropt_error_overflow)
{
err = dropt_error_mismatch;
}
}
if (err == dropt_error_none) { *out = val; }
return err;
}
/** dropt_handle_verbose_bool
*
* Like `dropt_handle_bool` but accepts "true" and "false" string values.
*
* PARAMETERS:
* IN/OUT context : The options context.
* IN option : The matched option. For more information, see
* `dropt_option_handler_decl`.
* IN optionArgument : A string representing a boolean value.
* If `NULL`, the boolean value is assumed to be true.
* OUT dest : A `dropt_bool*`.
* On success, set to the interpreted boolean value.
* On error, left untouched.
*
* RETURNS:
* See dropt_handle_bool.
*/
dropt_error
dropt_handle_verbose_bool(dropt_context* context,
const dropt_option* option,
const dropt_char* optionArgument,
void* dest)
{
dropt_error err = dropt_handle_bool(context, option, optionArgument, dest);
if (err == dropt_error_mismatch)
{
bool val = false;
dropt_bool* out = dest;
/* `dropt_handle_bool` already checks for this. */
assert(out != NULL);
if (dropt_stricmp(optionArgument, DROPT_TEXT_LITERAL("false")) == 0)
{
val = false;
err = dropt_error_none;
}
else if (dropt_stricmp(optionArgument, DROPT_TEXT_LITERAL("true")) == 0)
{
val = true;
err = dropt_error_none;
}
if (err == dropt_error_none) { *out = val; }
}
return err;
}
/** dropt_handle_int
*
* Stores an integer parsed from the given string.
*
* PARAMETERS:
* IN/OUT context : The options context.
* IN option : The matched option. For more information, see
* `dropt_option_handler_decl`.
* IN optionArgument : A string representing a base-10 integer.
* If `NULL`, returns
* `dropt_error_insufficient_arguments`.
* OUT dest : An `int*`.
* On success, set to the interpreted integer.
* On error, left untouched.
*
* RETURNS:
* dropt_error_none
* dropt_error_unknown
* dropt_error_bad_configuration
* dropt_error_insufficient_arguments
* dropt_error_mismatch
* dropt_error_overflow
*/
dropt_error
dropt_handle_int(dropt_context* context,
const dropt_option* option,
const dropt_char* optionArgument,
void* dest)
{
dropt_error err = dropt_error_none;
int val = 0;
int* out = dest;
if (out == NULL)
{
DROPT_MISUSE("No handler destination specified.");
err = dropt_error_bad_configuration;
}
else if ( optionArgument == NULL
|| optionArgument[0] == DROPT_TEXT_LITERAL('\0'))
{
err = dropt_error_insufficient_arguments;
}
else
{
dropt_char* end;
long n;
errno = 0;
n = dropt_strtol(optionArgument, &end, 10);
/* Check that we matched at least one digit.
* (`strtol`/`strtoul` will return 0 if fed a string with no digits.)
*/
if (*end == DROPT_TEXT_LITERAL('\0') && end > optionArgument)
{
if (errno == ERANGE || n < INT_MIN || n > INT_MAX)
{
err = dropt_error_overflow;
val = (n < 0) ? INT_MIN : INT_MAX;
}
else if (errno == 0)
{
val = (int) n;
}
else
{
err = dropt_error_unknown;
}
}
else
{
err = dropt_error_mismatch;
}
}
if (err == dropt_error_none) { *out = val; }
return err;
}
/** dropt_handle_uint
*
* Stores an unsigned integer parsed from the given string.
*
* PARAMETERS:
* IN/OUT context : The options context.
* IN option : The matched option. For more information, see
* `dropt_option_handler_decl`.
* IN optionArgument : A string representing an unsigned base-10 integer.
* If `NULL`, returns
* `dropt_error_insufficient_arguments`.
* IN option : The matched option. For more information, see
* dropt_option_handler_decl.
* OUT dest : An `unsigned int*`.
* On success, set to the interpreted integer.
* On error, left untouched.
*
* RETURNS:
* dropt_error_none
* dropt_error_unknown
* dropt_error_bad_configuration
* dropt_error_insufficient_arguments
* dropt_error_mismatch
* dropt_error_overflow
*/
dropt_error
dropt_handle_uint(dropt_context* context,
const dropt_option* option,
const dropt_char* optionArgument,
void* dest)
{
dropt_error err = dropt_error_none;
int val = 0;
unsigned int* out = dest;
if (out == NULL)
{
DROPT_MISUSE("No handler destination specified.");
err = dropt_error_bad_configuration;
}
else if ( optionArgument == NULL
|| optionArgument[0] == DROPT_TEXT_LITERAL('\0'))
{
err = dropt_error_insufficient_arguments;
}
else if (optionArgument[0] == DROPT_TEXT_LITERAL('-'))
{
err = dropt_error_mismatch;
}
else
{
dropt_char* end;
unsigned long n;
errno = 0;
n = dropt_strtoul(optionArgument, &end, 10);
/* Check that we matched at least one digit.
* (`strtol`/`strtoul` will return 0 if fed a string with no digits.)
*/
if (*end == DROPT_TEXT_LITERAL('\0') && end > optionArgument)
{
if (errno == ERANGE || n > UINT_MAX)
{
err = dropt_error_overflow;
val = UINT_MAX;
}
else if (errno == 0)
{
val = (unsigned int) n;
}
else
{
err = dropt_error_unknown;
}
}
else
{
err = dropt_error_mismatch;
}
}
if (err == dropt_error_none) { *out = val; }
return err;
}
/** dropt_handle_double
*
* Stores a `double` parsed from the given string.
*
* PARAMETERS:
* IN/OUT context : The options context.
* IN option : The matched option. For more information, see
* `dropt_option_handler_decl`.
* IN optionArgument : A string representing a base-10 floating-point
* number.
* If `NULL`, returns
* `dropt_error_insufficient_arguments`.
* OUT dest : A `double*`.
* On success, set to the interpreted `double`.
* On error, left untouched.
*
* RETURNS:
* dropt_error_none
* dropt_error_unknown
* dropt_error_bad_configuration
* dropt_error_insufficient_arguments
* dropt_error_mismatch
* dropt_error_overflow
* dropt_error_underflow
*/
dropt_error
dropt_handle_double(dropt_context* context,
const dropt_option* option,
const dropt_char* optionArgument,
void* dest)
{
dropt_error err = dropt_error_none;
double val = 0.0;
double* out = dest;
if (out == NULL)
{
DROPT_MISUSE("No handler destination specified.");
err = dropt_error_bad_configuration;
}
else if ( optionArgument == NULL
|| optionArgument[0] == DROPT_TEXT_LITERAL('\0'))
{
err = dropt_error_insufficient_arguments;
}
else
{
dropt_char* end;
errno = 0;
val = dropt_strtod(optionArgument, &end);
/* Check that we matched at least one digit.
* (`strtod` will return 0 if fed a string with no digits.)
*/
if (*end == DROPT_TEXT_LITERAL('\0') && end > optionArgument)
{
if (errno == ERANGE)
{
/* Note that setting `errno` to `ERANGE` for underflow errors
* is implementation-defined behavior, but glibc, BSD's
* libc, and Microsoft's CRT all have implementations of
* `strtod` documented to return 0 and to set `errno` to
* `ERANGE` for such cases.
*/
err = (ABS(val) <= DBL_MIN)
? dropt_error_underflow
: dropt_error_overflow;
}
else if (errno != 0)
{
err = dropt_error_unknown;
}
}
else
{
err = dropt_error_mismatch;
}
}
if (err == dropt_error_none) { *out = val; }
return err;
}
/** dropt_handle_string
*
* Stores a string.
*
* PARAMETERS:
* IN/OUT context : The options context.
* IN option : The matched option. For more information, see
* `dropt_option_handler_decl`.
* IN optionArgument : A string.
* If `NULL`, returns
* `dropt_error_insufficient_arguments`.
* OUT dest : A `dropt_char**`.
* On success, set to the input string. The string is
* NOT copied from the original `argv` array, so do
* not free it.
* On error, left untouched.
*
* RETURNS:
* dropt_error_none
* dropt_error_bad_configuration
* dropt_error_insufficient_arguments
*/
dropt_error
dropt_handle_string(dropt_context* context,
const dropt_option* option,
const dropt_char* optionArgument,
void* dest)
{
dropt_error err = dropt_error_none;
const dropt_char** out = dest;
if (out == NULL)
{
DROPT_MISUSE("No handler destination specified.");
err = dropt_error_bad_configuration;
}
else if (optionArgument == NULL)
{
err = dropt_error_insufficient_arguments;
}
if (err == dropt_error_none) { *out = optionArgument; }
return err;
}
/** dropt_handle_const
*
* Stores a predefined value. This can be used to set a single variable
* to different values in response to different boolean-like command-line
* options.
*
* PARAMETERS:
* IN/OUT context : The options context.
* IN option : The matched option. For more information, see
* `dropt_option_handler_decl`.
* IN optionArgument : Must be `NULL`.
* OUT dest : A `dropt_uintptr*`.
* On success, set to the constant value specified by
* `option->extra_data`.
* On error, left untouched.
*
* RETURNS:
* dropt_error_none
* dropt_error_bad_configuration
* dropt_error_mismatch
*/
dropt_error
dropt_handle_const(dropt_context* context,
const dropt_option* option,
const dropt_char* optionArgument,
void* dest)
{
dropt_error err = dropt_error_none;
dropt_uintptr* out = dest;
STATIC_ASSERT(sizeof (dropt_uintptr) >= sizeof (void*));
if (out == NULL)
{
DROPT_MISUSE("No handler destination specified.");
err = dropt_error_bad_configuration;
}
else if (option == NULL)
{
DROPT_MISUSE("No option entry given.");
err = dropt_error_bad_configuration;
}
else if (optionArgument != NULL)
{
err = dropt_error_mismatch;
}
if (err == dropt_error_none) { *out = option->extra_data; }
return err;
}

692
lib/dropt/dropt_string.c Normal file
View File

@ -0,0 +1,692 @@
/** dropt_string.c
*
* String routines for dropt.
*
* Copyright (C) 2006-2018 James D. Lin <jamesdlin@berkeley.edu>
*
* The latest version of this file can be downloaded from:
* <http://www.taenarum.com/software/dropt/>
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
*
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
*
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifdef _MSC_VER
#include <tchar.h>
#endif
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <ctype.h>
#include <wctype.h>
#include <stdio.h>
#include <assert.h>
#if __STDC_VERSION__ >= 199901L
#include <stdint.h>
#else
/* Compatibility junk for things that don't yet support ISO C99. */
#if defined _MSC_VER || defined __BORLANDC__
#ifndef va_copy
#define va_copy(dest, src) (dest = (src))
#endif
#else
#ifndef va_copy
#error Unsupported platform. va_copy is not defined.
#endif
#endif
#ifndef SIZE_MAX
#define SIZE_MAX ((size_t) -1)
#endif
#endif
#include "dropt_string.h"
#ifndef MAX
#define MAX(x, y) (((x) > (y)) ? (x) : (y))
#endif
#ifndef MIN
#define MIN(x, y) (((x) < (y)) ? (x) : (y))
#endif
#ifdef DROPT_DEBUG_STRING_BUFFERS
enum { default_stringstream_buffer_size = 1 };
#define GROWN_STRINGSTREAM_BUFFER_SIZE(oldSize, minAmount) \
((oldSize) + (minAmount))
#else
enum { default_stringstream_buffer_size = 256 };
#define GROWN_STRINGSTREAM_BUFFER_SIZE(oldSize, minAmount) \
MAX((oldSize) * 2, (oldSize) + (minAmount))
#endif
#ifndef DROPT_NO_STRING_BUFFERS
struct dropt_stringstream
{
/* The string buffer. */
dropt_char* string;
/* Size of the string buffer, in `dropt_char`s, including space for `NUL`.
*/
size_t maxSize;
/* Number of elements used in the string buffer, excluding `NUL`. */
size_t used;
};
#endif
/** dropt_safe_malloc
*
* A version of `malloc` that checks for integer overflow.
*
* PARAMETERS:
* IN numElements : The number of elements to allocate.
* IN elementSize : The size of each element, in bytes.
*
* RETURNS:
* A pointer to the allocated memory.
* Returns `NULL` if `numElements` is 0.
* Returns `NULL` on error.
*/
void*
dropt_safe_malloc(size_t numElements, size_t elementSize)
{
return dropt_safe_realloc(NULL, numElements, elementSize);
}
/** dropt_safe_realloc
*
* Wrapper around `realloc` to check for integer overflow.
*
* PARAMETERS:
* IN/OUT p : A pointer to the memory block to resize.
* If `NULL`, a new memory block of the specified size
* will be allocated.
* IN numElements : The number of elements to allocate.
* If 0, frees `p`.
* IN elementSize : The size of each element, in bytes.
*
* RETURNS:
* A pointer to the allocated memory.
* Returns `NULL` if `numElements` is 0.
* Returns `NULL` on error.
*/
void*
dropt_safe_realloc(void* p, size_t numElements, size_t elementSize)
{
size_t numBytes;
/* `elementSize` shouldn't legally be 0, but we check for it in case a
* caller got the argument order wrong.
*/
if (numElements == 0 || elementSize == 0)
{
/* The behavior of `realloc(p, 0)` is implementation-defined. Let's
* enforce a particular behavior.
*/
free(p);
assert(elementSize != 0);
return NULL;
}
numBytes = numElements * elementSize;
if (numBytes / elementSize != numElements)
{
/* Overflow. */
return NULL;
}
return realloc(p, numBytes);
}
/** dropt_strdup
*
* Duplicates a string.
*
* PARAMETERS:
* IN s : A `NUL`-terminated string to duplicate.
*
* RETURNS:
* The duplicated string. The caller is responsible for calling `free()`
* on it when no longer needed.
* Returns `NULL` on error.
*/
dropt_char*
dropt_strdup(const dropt_char* s)
{
return dropt_strndup(s, SIZE_MAX);
}
/** dropt_strndup
*
* Duplicates the first `n` characters of a string.
*
* PARAMETERS:
* IN s : The string to duplicate.
* IN n : The maximum number of `dropt_char`s to copy, excluding the
* `NUL`-terminator.
*
* RETURNS:
* The duplicated string, which is always `NUL`-terminated. The caller is
* responsible for calling `free()` on it when no longer needed.
* Returns `NULL` on error.
*/
dropt_char*
dropt_strndup(const dropt_char* s, size_t n)
{
dropt_char* copy;
size_t len = 0;
assert(s != NULL);
while (len < n && s[len] != DROPT_TEXT_LITERAL('\0'))
{
len++;
}
if (len + 1 < len)
{
/* This overflow check shouldn't be strictly necessary. `len` can be at
* most `SIZE_MAX`, so `SIZE_MAX + 1` can wrap around to 0, but
* `dropt_safe_malloc` will return `NULL` for a 0-sized allocation.
* However, favor defensive paranoia.
*/
return NULL;
}
copy = dropt_safe_malloc(len + 1 /* NUL */, sizeof *copy);
if (copy != NULL)
{
memcpy(copy, s, len * sizeof *copy);
copy[len] = DROPT_TEXT_LITERAL('\0');
}
return copy;
}
/** dropt_stricmp
*
* Compares two `NUL`-terminated strings ignoring case differences. Not
* recommended for non-ASCII strings.
*
* PARAMETERS:
* IN s, t : The strings to compare.
*
* RETURNS:
* 0 if the strings are equivalent,
* < 0 if `s` should precede `t`,
* > 0 if `s` should follow `t`.
*/
int
dropt_stricmp(const dropt_char* s, const dropt_char* t)
{
assert(s != NULL);
assert(t != NULL);
return dropt_strnicmp(s, t, SIZE_MAX);
}
/** dropt_strnicmp
*
* Compares the first `n` characters of two strings, ignoring case
* differences. Not recommended for non-ASCII strings.
*
* PARAMETERS:
* IN s, t : The strings to compare.
* IN n : The maximum number of `dropt_char`s to compare.
*
* RETURNS:
* 0 if the strings are equivalent,
* < 0 if `s` should precede `t`,
* > 0 if `s` should follow `t`.
*/
int
dropt_strnicmp(const dropt_char* s, const dropt_char* t, size_t n)
{
assert(s != NULL);
assert(t != NULL);
if (s == t) { return 0; }
while (n--)
{
if (*s == DROPT_TEXT_LITERAL('\0') && *t == DROPT_TEXT_LITERAL('\0'))
{
break;
}
else if (*s == *t || dropt_tolower(*s) == dropt_tolower(*t))
{
s++;
t++;
}
else
{
return (dropt_tolower(*s) < dropt_tolower(*t))
? -1
: +1;
}
}
return 0;
}
#ifndef DROPT_NO_STRING_BUFFERS
/** dropt_vsnprintf
*
* `vsnprintf` wrapper to provide ISO C99-compliant behavior.
*
* PARAMETERS:
* OUT s : The destination buffer. May be `NULL` if `n` is 0.
* If non-`NULL`, always `NUL`-terminated.
* IN n : The size of the destination buffer, measured in
* `dropt_char`s.
* IN format : `printf`-style format specifier. Must not be `NULL`.
* IN args : Arguments to insert into the formatted string.
*
* RETURNS:
* The number of characters that would be written to the destination
* buffer if it's sufficiently large, excluding the `NUL`-terminator.
* Returns -1 on error.
*/
int
dropt_vsnprintf(dropt_char* s, size_t n, const dropt_char* format, va_list args)
{
#if __STDC_VERSION__ >= 199901L || __GNUC__
/* ISO C99-compliant.
*
* As far as I can tell, gcc's implementation of `vsnprintf` has always
* matched the behavior required by the C99 standard (which is to return
* the necessary buffer size).
*
* Note that this won't work with `wchar_t` because there is no true,
* standard `wchar_t` equivalent of `snprintf`. `swprintf` comes close but
* doesn't return the necessary buffer size (and the standard does not
* provide a guaranteed way to test if truncation occurred), and its
* format string can't be used interchangeably with `snprintf`.
*
* It's simpler not to support `wchar_t` on non-Windows platforms.
*/
assert(format != NULL);
return vsnprintf(s, n, format, args);
#elif defined __BORLANDC__
/* Borland's compiler neglects to `NUL`-terminate. */
int ret;
assert(format != NULL);
ret = vsnprintf(s, n, format, args);
if (n != 0) { s[n - 1] = DROPT_TEXT_LITERAL('\0'); }
return ret;
#elif defined _MSC_VER
/* `_vsntprintf` and `_vsnprintf_s` on Windows don't have C99 semantics;
* they return -1 if truncation occurs.
*/
va_list argsCopy;
int ret;
assert(format != NULL);
va_copy(argsCopy, args);
ret = _vsctprintf(format, argsCopy);
va_end(argsCopy);
if (n != 0)
{
assert(s != NULL);
#if _MSC_VER >= 1400
(void) _vsntprintf_s(s, n, _TRUNCATE, format, args);
#else
/* This version doesn't necessarily `NUL`-terminate. Sigh. */
(void) _vsnprintf(s, n, format, args);
s[n - 1] = DROPT_TEXT_LITERAL('\0');
#endif
}
return ret;
#else
#error Unsupported platform. dropt_vsnprintf unimplemented.
return -1;
#endif
}
/** See `dropt_vsnprintf`. */
int
dropt_snprintf(dropt_char* s, size_t n, const dropt_char* format, ...)
{
int ret;
va_list args;
va_start(args, format);
ret = dropt_vsnprintf(s, n, format, args);
va_end(args);
return ret;
}
/** dropt_vasprintf
*
* Allocates a formatted string with `vprintf` semantics.
*
* PARAMETERS:
* IN format : `printf`-style format specifier. Must not be `NULL`.
* IN args : Arguments to insert into the formatted string.
*
* RETURNS:
* The formatted string, which is always NUL-terminated. The caller is
* responsible for calling `free()` on it when no longer needed.
* Returns `NULL` on error.
*/
dropt_char*
dropt_vasprintf(const dropt_char* format, va_list args)
{
dropt_char* s = NULL;
int len;
va_list argsCopy;
assert(format != NULL);
va_copy(argsCopy, args);
len = dropt_vsnprintf(NULL, 0, format, argsCopy);
va_end(argsCopy);
if (len >= 0)
{
size_t n = len + 1 /* NUL */;
s = dropt_safe_malloc(n, sizeof *s);
if (s != NULL)
{
dropt_vsnprintf(s, n, format, args);
}
}
return s;
}
/** See `dropt_vasprintf`. */
dropt_char*
dropt_asprintf(const dropt_char* format, ...)
{
dropt_char* s;
va_list args;
va_start(args, format);
s = dropt_vasprintf(format, args);
va_end(args);
return s;
}
/** dropt_ssopen
*
* Constructs a new `dropt_stringstream`.
*
* RETURNS:
* An initialized `dropt_stringstream`. The caller is responsible for
* calling either `dropt_ssclose()` or `dropt_ssfinalize()` on it when
* no longer needed.
* Returns `NULL` on error.
*/
dropt_stringstream*
dropt_ssopen(void)
{
dropt_stringstream* ss = malloc(sizeof *ss);
if (ss != NULL)
{
ss->used = 0;
ss->maxSize = default_stringstream_buffer_size;
ss->string = dropt_safe_malloc(ss->maxSize, sizeof *ss->string);
if (ss->string == NULL)
{
free(ss);
ss = NULL;
}
else
{
ss->string[0] = DROPT_TEXT_LITERAL('\0');
}
}
return ss;
}
/** dropt_ssclose
*
* Destroys a `dropt_stringstream`.
*
* PARAMETERS:
* IN/OUT ss : The `dropt_stringstream`.
*/
void
dropt_ssclose(dropt_stringstream* ss)
{
if (ss != NULL)
{
free(ss->string);
free(ss);
}
}
/** dropt_ssgetfreespace
*
* RETURNS:
* The amount of free space in the `dropt_stringstream`'s internal buffer,
* measured in `dropt_char`s. Space used for the `NUL`-terminator is
* considered free. (The amount of free space therefore is always
* positive.)
*/
static size_t
dropt_ssgetfreespace(const dropt_stringstream* ss)
{
assert(ss != NULL);
assert(ss->maxSize > 0);
assert(ss->maxSize > ss->used);
return ss->maxSize - ss->used;
}
/** dropt_ssresize
*
* Resizes a `dropt_stringstream`'s internal buffer. If the requested
* size is less than the amount of buffer already in use, the buffer will
* be shrunk to the minimum size necessary.
*
* PARAMETERS:
* IN/OUT ss : The `dropt_stringstream`.
* IN n : The desired buffer size, in `dropt_char`s.
*
* RETURNS:
* The new size of the `dropt_stringstream`'s buffer in `dropt_char`s,
* including space for a terminating `NUL`.
*/
static size_t
dropt_ssresize(dropt_stringstream* ss, size_t n)
{
assert(ss != NULL);
/* Don't allow shrinking if it will truncate the string. */
if (n < ss->maxSize) { n = MAX(n, ss->used + 1 /* NUL */); }
/* There should always be a buffer to point to. */
assert(n > 0);
if (n != ss->maxSize)
{
dropt_char* p = dropt_safe_realloc(ss->string, n, sizeof *ss->string);
if (p != NULL)
{
ss->string = p;
ss->maxSize = n;
assert(ss->maxSize > 0);
}
}
return ss->maxSize;
}
/** dropt_ssclear
*
* Clears and re-initializes a `dropt_stringstream`.
*
* PARAMETERS:
* IN/OUT ss : The `dropt_stringstream`.
*/
void
dropt_ssclear(dropt_stringstream* ss)
{
assert(ss != NULL);
ss->string[0] = DROPT_TEXT_LITERAL('\0');
ss->used = 0;
dropt_ssresize(ss, default_stringstream_buffer_size);
}
/** dropt_ssfinalize
*
* Finalizes a `dropt_stringstream`; returns the contained string and
* destroys the `dropt_stringstream`.
*
* PARAMETERS:
* IN/OUT ss : The `dropt_stringstream`.
*
* RETURNS:
* The `dropt_stringstream`'s string, which is always `NUL`-terminated.
* Note that the caller assumes ownership of the returned string and is
* responsible for calling `free()` on it when no longer needed.
*/
dropt_char*
dropt_ssfinalize(dropt_stringstream* ss)
{
dropt_char* s;
assert(ss != NULL);
/* Shrink to fit. */
dropt_ssresize(ss, 0);
s = ss->string;
ss->string = NULL;
dropt_ssclose(ss);
return s;
}
/** dropt_ssgetstring
*
* PARAMETERS:
* IN ss : The `dropt_stringstream`.
*
* RETURNS:
* The `dropt_stringstream`'s string, which is always `NUL`-terminated.
* The returned string will no longer be valid if further operations are
* performed on the `dropt_stringstream` or if the `dropt_stringstream`
* is closed.
*/
const dropt_char*
dropt_ssgetstring(const dropt_stringstream* ss)
{
assert(ss != NULL);
return ss->string;
}
/** dropt_vssprintf
*
* Appends a formatted string with `vprintf` semantics to a
* `dropt_stringstream`.
*
* PARAMETERS:
* IN/OUT ss : The `dropt_stringstream`.
* IN format : `printf`-style format specifier. Must not be `NULL`.
* IN args : Arguments to insert into the formatted string.
*
* RETURNS:
* The number of characters written to the `dropt_stringstream`, excluding
* the `NUL`-terminator.
* Returns a negative value on error.
*/
int
dropt_vssprintf(dropt_stringstream* ss, const dropt_char* format, va_list args)
{
int n;
va_list argsCopy;
assert(ss != NULL);
assert(format != NULL);
va_copy(argsCopy, args);
n = dropt_vsnprintf(NULL, 0, format, argsCopy);
va_end(argsCopy);
if (n > 0)
{
size_t available = dropt_ssgetfreespace(ss);
if ((unsigned int) n >= available)
{
/* It's possible that `newSize < ss->maxSize` if
* `GROWN_STRINGSTREAM_BUFFER_SIZE()` overflows, but it should be
* safe since we'll recompute the available space.
*/
size_t newSize = GROWN_STRINGSTREAM_BUFFER_SIZE(ss->maxSize, n);
dropt_ssresize(ss, newSize);
available = dropt_ssgetfreespace(ss);
}
assert(available > 0); /* Space always is reserved for NUL. */
/* `snprintf`'s family of functions return the number of characters
* that would be output with a sufficiently large buffer, excluding
* `NUL`.
*/
n = dropt_vsnprintf(ss->string + ss->used, available, format, args);
/* We couldn't allocate enough space. */
if ((unsigned int) n >= available) { n = -1; }
if (n > 0) { ss->used += n; }
}
return n;
}
/** See `dropt_vssprintf`. */
int
dropt_ssprintf(dropt_stringstream* ss, const dropt_char* format, ...)
{
int n;
va_list args;
va_start(args, format);
n = dropt_vssprintf(ss, format, args);
va_end(args);
return n;
}
#endif /* DROPT_NO_STRING_BUFFERS */

96
lib/dropt/dropt_string.h Normal file
View File

@ -0,0 +1,96 @@
/** dropt_string.h
*
* String routines for dropt.
*
* Copyright (C) 2006-2010 James D. Lin <jamesdlin@berkeley.edu>
*
* The latest version of this file can be downloaded from:
* <http://www.taenarum.com/software/dropt/>
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
*
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
*
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef DROPT_STRING_H
#define DROPT_STRING_H
#include <stdarg.h>
#include "dropt.h"
#ifdef DROPT_USE_WCHAR
#define dropt_strlen wcslen
#define dropt_strcmp wcscmp
#define dropt_strncmp wcsncmp
#define dropt_strchr wcschr
#define dropt_strtol wcstol
#define dropt_strtoul wcstoul
#define dropt_strtod wcstod
#define dropt_tolower towlower
#define dropt_fputs fputws
#else
#define dropt_strlen strlen
#define dropt_strcmp strcmp
#define dropt_strncmp strncmp
#define dropt_strchr strchr
#define dropt_strtol strtol
#define dropt_strtoul strtoul
#define dropt_strtod strtod
#define dropt_tolower tolower
#define dropt_fputs fputs
#endif
#ifdef __cplusplus
extern "C" {
#endif
void* dropt_safe_malloc(size_t numElements, size_t elementSize);
void* dropt_safe_realloc(void* p, size_t numElements, size_t elementSize);
dropt_char* dropt_strdup(const dropt_char* s);
dropt_char* dropt_strndup(const dropt_char* s, size_t n);
int dropt_stricmp(const dropt_char* s, const dropt_char* t);
int dropt_strnicmp(const dropt_char* s, const dropt_char* t, size_t n);
#ifndef DROPT_NO_STRING_BUFFERS
typedef struct dropt_stringstream dropt_stringstream;
int dropt_vsnprintf(dropt_char* s, size_t n, const dropt_char* format, va_list args);
int dropt_snprintf(dropt_char* s, size_t n, const dropt_char* format, ...);
dropt_char* dropt_vasprintf(const dropt_char* format, va_list args);
dropt_char* dropt_asprintf(const dropt_char* format, ...);
dropt_stringstream* dropt_ssopen(void);
void dropt_ssclose(dropt_stringstream* ss);
void dropt_ssclear(dropt_stringstream* ss);
dropt_char* dropt_ssfinalize(dropt_stringstream* ss);
const dropt_char* dropt_ssgetstring(const dropt_stringstream* ss);
int dropt_vssprintf(dropt_stringstream* ss, const dropt_char* format, va_list args);
int dropt_ssprintf(dropt_stringstream* ss, const dropt_char* format, ...);
#endif /* DROPT_NO_STRING_BUFFERS */
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* DROPT_STRING_H */

View File

View File

View File

@ -16,6 +16,7 @@
#include "y.tab.h"
#include "ast.h"
#include "stack.h"
#include "../lib/dropt/dropt.h"
extern FILE *yyin;
Stack *stack;
@ -819,17 +820,8 @@ static void Compile(LLVMModuleRef module, LLVMContextRef context, Node *node)
}
}
int main(int argc, char *argv[])
static int Build(char *inputFilename, uint32_t optimizationLevel)
{
if (argc < 2)
{
printf("Please provide a file.\n");
return 1;
}
extern int yydebug;
yydebug = 1;
scope = CreateScope();
structTypeDeclarations = NULL;
@ -837,7 +829,7 @@ int main(int argc, char *argv[])
stack = CreateStack();
FILE *fp = fopen(argv[1], "r");
FILE *fp = fopen(inputFilename, "r");
yyin = fp;
yyparse(fp, stack);
fclose(fp);
@ -866,7 +858,13 @@ int main(int argc, char *argv[])
/* verify */
char *error = NULL;
LLVMVerifyModule(module, LLVMAbortProcessAction, &error);
if (LLVMVerifyModule(module, LLVMAbortProcessAction, &error) != 0)
{
fprintf(stderr, "%s\n", error);
LLVMDisposeMessage(error);
return EXIT_FAILURE;
}
/* prepare to emit assembly */
LLVMInitializeNativeTarget();
@ -883,7 +881,7 @@ int main(int argc, char *argv[])
fprintf(stderr, "Failed to get target!\n");
fprintf(stderr, "%s\n", error);
LLVMDisposeMessage(error);
return 1;
return EXIT_FAILURE;
}
LLVMPassManagerRef passManager = LLVMCreatePassManager();
@ -894,13 +892,14 @@ int main(int argc, char *argv[])
// LLVMAddPromoteMemoryToRegisterPass(passManager);
LLVMPassManagerBuilderRef passManagerBuilder = LLVMPassManagerBuilderCreate();
LLVMPassManagerBuilderSetOptLevel(passManagerBuilder, 0);
LLVMPassManagerBuilderSetOptLevel(passManagerBuilder, optimizationLevel);
LLVMPassManagerBuilderPopulateModulePassManager(passManagerBuilder, passManager);
LLVMRunPassManager(passManager, module);
if (LLVMWriteBitcodeToFile(module, "test.bc") != 0) {
fprintf(stderr, "error writing bitcode to file\n");
return EXIT_FAILURE;
}
char *cpu = "generic";
@ -921,7 +920,7 @@ int main(int argc, char *argv[])
fprintf(stderr, "Failed to emit machine code!\n");
fprintf(stderr, "%s\n", error);
LLVMDisposeMessage(error);
return 1;
return EXIT_FAILURE;
}
LLVMDisposeMessage(error);
@ -929,5 +928,64 @@ int main(int argc, char *argv[])
LLVMPassManagerBuilderDispose(passManagerBuilder);
LLVMDisposePassManager(passManager);
LLVMDisposeModule(module);
return 0;
return EXIT_SUCCESS;
}
int main(int argc, char *argv[])
{
dropt_bool showHelp;
uint32_t optimizationLevel = 0;
char *inputFilename;
extern int yydebug;
int exitCode = EXIT_SUCCESS;
dropt_option options[] = {
{ 'h', "help", "Shows help.", NULL, dropt_handle_bool, &showHelp, dropt_attr_halt },
{ 'O', "optimize", "Sets optimization level of the output IR. Must be a value between 0 and 3.", "number", dropt_handle_uint, &optimizationLevel },
{ 0 } /* Required sentinel value. */
};
dropt_context *droptContext = dropt_new_context(options);
if (droptContext == NULL)
{
exitCode = EXIT_FAILURE;
}
else if (argc == 0)
{
printf("Must supply an input file.");
exitCode = EXIT_FAILURE;
}
else
{
char** rest = dropt_parse(droptContext, -1, &argv[1]);
if (dropt_get_error(droptContext) != dropt_error_none)
{
fprintf(stderr, "wraith: %s\n", dropt_get_error_message(droptContext));
exitCode = EXIT_FAILURE;
}
else if (showHelp)
{
printf("Usage: wraith [options] [--] [input_file]\n\n"
"Options:\n");
dropt_print_help(stdout, droptContext, NULL);
}
else
{
yydebug = 1; /* FIXME: make this an option */
inputFilename = *rest;
if (inputFilename == NULL)
{
fprintf(stderr, "ERROR: Must provide an input file.\n");
exitCode = EXIT_FAILURE;
}
else
{
exitCode = Build(inputFilename, optimizationLevel);
}
}
}
return exitCode;
}