Adding feature flags.

This commit is contained in:
Christoffer Lerno
2023-07-26 23:28:11 +02:00
parent c18526f10a
commit 7b0408f79d
15 changed files with 304 additions and 80 deletions

View File

@@ -54,6 +54,7 @@ int comment_level = 0;
"$eval" { count(); return(CT_EVAL); }
"$evaltype" { count(); return(CT_EVALTYPE); }
"$extnameof" { count(); return(CT_EXTNAMEOF); }
"$feature" { count(); return(CT_FEATURE); }
"$for" { count(); return(CT_FOR); }
"$foreach" { count(); return(CT_FOREACH); }
"$if" { count(); return(CT_IF); }

View File

@@ -30,7 +30,7 @@ void yyerror(char *s);
%token CT_ENDFOR CT_ENDSWITCH BUILTIN IMPLIES INITIALIZE FINALIZE CT_ECHO CT_ASSERT CT_EVALTYPE CT_VATYPE
%token TRY CATCH SCOPE DEFER LVEC RVEC OPTELSE CT_TYPEFROM CT_TYPEOF TLOCAL
%token CT_VASPLAT INLINE DISTINCT CT_VACONST CT_NAMEOF CT_VAREF CT_VACOUNT CT_VAARG
%token CT_SIZEOF CT_STRINGIFY CT_QNAMEOF CT_OFFSETOF CT_VAEXPR
%token CT_SIZEOF CT_STRINGIFY CT_QNAMEOF CT_OFFSETOF CT_VAEXPR CT_FEATURE
%token CT_EXTNAMEOF CT_EVAL CT_DEFINED CT_CHECKS CT_ALIGNOF ASSERT
%token ASM CHAR_LITERAL REAL TRUE FALSE CT_CONST_IDENT
%token LBRAPIPE RBRAPIPE HASH_CONST_IDENT
@@ -139,6 +139,7 @@ base_expr
| ct_arg '(' expr ')'
| ct_analyse '(' expr ')'
| CT_VACOUNT
| CT_FEATURE '(' CONST_IDENT ')'
| CT_CHECKS '(' expression_list ')'
| lambda_decl compound_statement
;

View File

@@ -108,6 +108,8 @@ static void usage(void)
OUTPUT(" -O3+ - Aggressive optimization, single module.");
OUTPUT(" -Os+ - Optimize for size, single module.");
OUTPUT(" -Oz+ - Optimize for tiny size, single module.");
OUTPUT(" -D <name> - Add feature flag <name>.");
OUTPUT(" -U <name> - Remove feature flag <name>.");
OUTPUT(" --build-dir <dir> - Override build output directory.");
OUTPUT(" --obj-out <dir> - Override object file output directory.");
OUTPUT(" --llvm-out <dir> - Override llvm output directory for '--emit-llvm'.");
@@ -203,9 +205,9 @@ static inline bool next_is_opt()
return args[arg_index + 1][0] == '-';
}
static inline bool match_longopt(const char* name)
INLINE bool match_longopt(const char* name)
{
return strcmp(&current_arg[2], name) == 0;
return str_eq(&current_arg[2], name);
}
static inline const char *match_argopt(const char* name)
@@ -385,6 +387,29 @@ static void add_linker_arg(BuildOptions *options, const char *arg)
options->linker_args[options->linker_arg_count++] = arg;
}
void update_feature_flags(const char ***flags, const char ***removed_flags, const char *arg, bool add)
{
const char **to_remove_from = add ? *removed_flags : *flags;
unsigned len = vec_size(to_remove_from);
// Remove if it's in there.
for (unsigned i = 0; i < len; i++)
{
if (str_eq(to_remove_from[i], arg))
{
vec_erase_ptr_at(to_remove_from, i);
break;
}
}
const char ***to_add_to_ref = add ? flags : removed_flags;
unsigned add_len = vec_size(*to_add_to_ref);
for (unsigned i = 0; i < add_len; i++)
{
if (str_eq((*to_add_to_ref)[i], arg)) return;
}
vec_add(*to_add_to_ref, arg);
}
static int parse_multi_option(const char *start, unsigned count, const char** elements)
{
const char *arg = current_arg;
@@ -435,18 +460,42 @@ static void parse_option(BuildOptions *options)
case 'h':
break;
case 'z':
if (at_end()) error_exit("error: -z needs a value");
if (match_shortopt("U"))
{
if (at_end()) error_exit("error: -z needs a value.");
add_linker_arg(options, next_arg());
return;
}
break;
case 'o':
if (at_end()) error_exit("error: -o needs a name");
if (match_shortopt("o"))
{
if (at_end()) error_exit("error: -o needs a name.");
options->output_name = next_arg();
return;
case 'O':
if (options->optimization_setting_override != OPT_SETTING_NOT_SET)
{
FAIL_WITH_ERR("Multiple optimization levels were set.");
}
break;
case 'D':
if (match_shortopt("D"))
{
if (at_end()) error_exit("error: -D needs a feature name.");
const char *arg = next_arg();
if (!str_is_valid_constant(arg)) error_exit("Invalid feature name '%s', expected an all-uppercase constant name.", arg);
update_feature_flags(&options->feature_names, &options->removed_feature_names, arg, true);
return;
}
break;
case 'U':
if (match_shortopt("U"))
{
if (at_end()) error_exit("error: -U needs a feature name.");
const char *arg = next_arg();
if (!str_is_valid_constant(arg)) error_exit("Invalid feature name '%s', expected an all-uppercase constant name.", arg);
update_feature_flags(&options->feature_names, &options->removed_feature_names, arg, false);
return;
}
break;
case 'O':
if (match_shortopt("O0+"))
{
options->optimization_setting_override = OPT_SETTING_O0_PLUS;
@@ -501,34 +550,54 @@ static void parse_option(BuildOptions *options)
}
return;
case 'E':
if (match_shortopt("E"))
{
if (options->compile_option != COMPILE_NORMAL)
{
FAIL_WITH_ERR("Illegal combination of compile options.");
}
options->compile_option = COMPILE_LEX_ONLY;
return;
}
break;
case 'L':
if (match_shortopt("L"))
{
if (at_end() || next_is_opt()) error_exit("error: -L needs a directory.");
options->linker_lib_dir[options->linker_lib_dir_count++] = check_dir(next_arg());
return;
}
break;
case 'l':
if (match_shortopt("l"))
{
if (at_end() || next_is_opt()) error_exit("error: -l needs a library name.");
options->linker_libs[options->linker_lib_count++] = next_arg();
return;
}
break;
case 'P':
if (match_shortopt("P"))
{
if (options->compile_option != COMPILE_NORMAL)
{
FAIL_WITH_ERR("Illegal combination of compile options.");
}
options->compile_option = COMPILE_LEX_PARSE_ONLY;
return;
}
break;
case 'C':
if (match_shortopt("C"))
{
if (options->compile_option != COMPILE_NORMAL)
{
FAIL_WITH_ERR("Illegal combination of compile options.");
}
options->compile_option = COMPILE_LEX_PARSE_CHECK_ONLY;
return;
}
break;
case '-':
if (match_longopt("tb"))
{

View File

@@ -12,6 +12,7 @@
#define MAX_INCLUDES 2048
#define MAX_THREADS 0xFFFF
void update_feature_flags(const char ***flags, const char ***removed_flag, const char *arg, bool add);
typedef enum
{
@@ -305,6 +306,8 @@ typedef struct BuildOptions_
int build_threads;
const char **libraries_to_fetch;
const char **files;
const char **feature_names;
const char **removed_feature_names;
const char *output_name;
const char *project_name;
const char *target_select;
@@ -441,6 +444,7 @@ typedef struct
const char *cflags;
const char **csource_dirs;
const char **csources;
const char **feature_list;
struct
{
SoftFloat soft_float : 3;

View File

@@ -192,6 +192,27 @@ static void update_build_target_from_options(BuildTarget *target, BuildOptions *
target->backend = options->backend;
// Remove feature flags
FOREACH_BEGIN(const char *remove_feature, options->removed_feature_names)
FOREACH_BEGIN_IDX(i, const char *feature, target->feature_list)
if (str_eq(feature, remove_feature))
{
vec_erase_ptr_at(target->feature_list, i);
break;
}
FOREACH_END();
FOREACH_END();
// Add feature flags
FOREACH_BEGIN(const char *add_feature, options->feature_names)
FOREACH_BEGIN_IDX(i, const char *feature, target->feature_list)
if (str_eq(feature, add_feature)) goto NEXT;
FOREACH_END();
vec_add(target->feature_list, add_feature);
NEXT:;
FOREACH_END();
update_build_target_with_opt_level(target, options->optimization_setting_override);
if (options->cc) target->cc = options->cc;

View File

@@ -45,6 +45,7 @@ void compiler_init(const char *std_lib_dir)
decltable_init(&global_context.symbols, INITIAL_SYMBOL_MAP);
decltable_init(&global_context.generic_symbols, INITIAL_GENERIC_SYMBOL_MAP);
htable_init(&global_context.features, 1024);
htable_init(&global_context.compiler_defines, 16 * 1024);
global_context.module_list = NULL;
global_context.generic_module_list = NULL;
@@ -820,6 +821,12 @@ void compile()
target_setup(&active_target);
resolve_libraries();
TokenType type = TOKEN_CONST_IDENT;
FOREACH_BEGIN(const char *feature_flag, active_target.feature_list)
feature_flag = symtab_preset(feature_flag, TOKEN_CONST_IDENT);
htable_set(&global_context.features, (void *)feature_flag, (void *)feature_flag);
FOREACH_END();
setup_int_define("C_SHORT_SIZE", platform_target.width_c_short, type_long);
setup_int_define("C_INT_SIZE", platform_target.width_c_int, type_long);
setup_int_define("C_LONG_SIZE", platform_target.width_c_long, type_long);

View File

@@ -1703,6 +1703,7 @@ typedef struct
bool suppress_errors;
Decl ***locals_list;
HTable compiler_defines;
HTable features;
Module std_module;
DeclTable symbols;
DeclTable generic_symbols;

View File

@@ -568,6 +568,7 @@ typedef enum
TOKEN_CT_EVALTYPE, // $evaltype
TOKEN_CT_ERROR, // $error
TOKEN_CT_EXTNAMEOF, // $extnameof
TOKEN_CT_FEATURE, // $feature
TOKEN_CT_FOR, // $for
TOKEN_CT_FOREACH, // $foreach
TOKEN_CT_IF, // $if

View File

@@ -1017,6 +1017,7 @@ static Expr *parse_hash_ident(ParseContext *c, Expr *left)
return expr;
}
/**
* ct_eval ::= CT_EVAL '(' expr ')'
*/
@@ -1104,7 +1105,7 @@ static Expr *parse_ct_embed(ParseContext *c, Expr *left)
}
/**
* ct_call ::= (ALIGNOF | EXTNAMEOF | OFFSETOF | NAMEOF | QNAMEOF) '(' flat_path ')'
* ct_call ::= (CT_ALIGNOF | CT_FEATURE | CT_EXTNAMEOF | CT_OFFSETOF | CT_NAMEOF | CT_QNAMEOF) '(' flat_path ')'
* flat_path ::= expr ('.' primary) | '[' expr ']')*
*/
static Expr *parse_ct_call(ParseContext *c, Expr *left)
@@ -1909,6 +1910,7 @@ ParseRule rules[TOKEN_EOF + 1] = {
[TOKEN_CT_CHECKS] = { parse_ct_checks, NULL, PREC_NONE },
[TOKEN_CT_EMBED] = { parse_ct_embed, NULL, PREC_NONE },
[TOKEN_CT_EVAL] = { parse_ct_eval, NULL, PREC_NONE },
[TOKEN_CT_FEATURE] = { parse_ct_call, NULL, PREC_NONE },
[TOKEN_CT_EXTNAMEOF] = { parse_ct_call, NULL, PREC_NONE },
[TOKEN_CT_OFFSETOF] = { parse_ct_call, NULL, PREC_NONE },
[TOKEN_CT_NAMEOF] = { parse_ct_call, NULL, PREC_NONE },

View File

@@ -1208,50 +1208,51 @@ Ast *parse_stmt(ParseContext *c)
return parse_ct_foreach_stmt(c);
case TOKEN_CT_FOR:
return parse_ct_for_stmt(c);
case TOKEN_STAR:
case TOKEN_AMP:
case TOKEN_INTEGER:
case TOKEN_CHAR_LITERAL:
case TOKEN_AT:
case TOKEN_AT_CONST_IDENT:
case TOKEN_AT_IDENT:
case TOKEN_AT_TYPE_IDENT:
case TOKEN_BANG:
case TOKEN_BIT_NOT:
case TOKEN_BIT_OR:
case TOKEN_BIT_XOR:
case TOKEN_BUILTIN:
case TOKEN_BYTES:
case TOKEN_CHAR_LITERAL:
case TOKEN_CT_ALIGNOF:
case TOKEN_CT_CHECKS:
case TOKEN_CT_CONST_IDENT:
case TOKEN_CT_DEFINED:
case TOKEN_CT_EMBED:
case TOKEN_CT_EVAL:
case TOKEN_CT_EXTNAMEOF:
case TOKEN_CT_FEATURE:
case TOKEN_CT_IDENT:
case TOKEN_CT_NAMEOF:
case TOKEN_CT_OFFSETOF:
case TOKEN_CT_QNAMEOF:
case TOKEN_CT_SIZEOF:
case TOKEN_CT_STRINGIFY:
case TOKEN_CT_VAARG:
case TOKEN_CT_VACONST:
case TOKEN_CT_VACOUNT:
case TOKEN_CT_VAEXPR:
case TOKEN_CT_VAREF:
case TOKEN_FALSE:
case TOKEN_INTEGER:
case TOKEN_LBRAPIPE:
case TOKEN_LPAREN:
case TOKEN_MINUS:
case TOKEN_BANG:
case TOKEN_MINUSMINUS:
case TOKEN_NULL:
case TOKEN_OR:
case TOKEN_PLUS:
case TOKEN_MINUSMINUS:
case TOKEN_PLUSPLUS:
case TOKEN_CT_CONST_IDENT:
case TOKEN_CT_IDENT:
case TOKEN_STRING:
case TOKEN_REAL:
case TOKEN_FALSE:
case TOKEN_NULL:
case TOKEN_STAR:
case TOKEN_STRING:
case TOKEN_TRUE:
case TOKEN_LBRAPIPE:
case TOKEN_CT_OFFSETOF:
case TOKEN_CT_ALIGNOF:
case TOKEN_CT_EXTNAMEOF:
case TOKEN_CT_SIZEOF:
case TOKEN_CT_QNAMEOF:
case TOKEN_CT_NAMEOF:
case TOKEN_CT_DEFINED:
case TOKEN_CT_CHECKS:
case TOKEN_CT_STRINGIFY:
case TOKEN_CT_EVAL:
case TOKEN_BYTES:
case TOKEN_BUILTIN:
case TOKEN_CT_VACOUNT:
case TOKEN_CT_VAARG:
case TOKEN_CT_VAEXPR:
case TOKEN_CT_VACONST:
case TOKEN_CT_VAREF:
case TOKEN_AT_TYPE_IDENT:
case TOKEN_AT_CONST_IDENT:
case TOKEN_AT:
case TOKEN_AT_IDENT:
case TOKEN_CT_EMBED:
return parse_expr_stmt(c);
case TOKEN_ASSERT:
return parse_assert_stmt(c);

View File

@@ -7224,6 +7224,25 @@ FAIL_NO_INFER:
return false;
}
static inline bool sema_expr_analyse_ct_feature(SemaContext *context, Expr *expr)
{
if (expr->resolve_status == RESOLVE_DONE) return expr_ok(expr);
Expr *inner = expr->ct_call_expr.main_var;
if (expr->ct_call_expr.flat_path) goto ERROR;
if (inner->expr_kind != EXPR_IDENTIFIER) goto ERROR;
if (inner->resolve_status != RESOLVE_NOT_DONE) goto ERROR;
if (!inner->identifier_expr.is_const) goto ERROR;
const char *name = inner->identifier_expr.ident;
void *value = htable_get(&global_context.features, (void *)name);
assert(!value || value == name);
expr_rewrite_const_bool(expr, type_bool, value != NULL);
return true;
ERROR:
RETURN_SEMA_ERROR(inner, "Expected a feature name here, e.g. $feature(MY_FEATURE).");
}
static inline bool sema_expr_analyse_ct_defined(SemaContext *context, Expr *expr)
{
if (expr->resolve_status == RESOLVE_DONE) return expr_ok(expr);
@@ -7491,6 +7510,8 @@ static inline bool sema_expr_analyse_ct_call(SemaContext *context, Expr *expr)
case TOKEN_CT_NAMEOF:
case TOKEN_CT_EXTNAMEOF:
return sema_expr_analyse_ct_nameof(context, expr);
case TOKEN_CT_FEATURE:
return sema_expr_analyse_ct_feature(context, expr);
default:
UNREACHABLE
}

View File

@@ -342,6 +342,8 @@ const char *token_type_to_string(TokenType type)
return "$evaltype";
case TOKEN_CT_ERROR:
return "$error";
case TOKEN_CT_FEATURE:
return "$feature";
case TOKEN_CT_FOR:
return "$for";
case TOKEN_CT_FOREACH:

View File

@@ -132,6 +132,11 @@ char *str_vprintf(const char *var, va_list list);
void str_ellide_in_place(char *string, size_t max_size_shown);
bool str_is_valid_lowercase_name(const char *string);
bool str_is_valid_constant(const char *string);
const char *str_unescape(char *string);
bool str_is_identifier(const char *string);
bool str_eq(const char *str1, const char *str2);
bool str_is_type(const char *string);
bool str_is_integer(const char *string);
bool str_has_no_uppercase(const char *string);
char *str_copy(const char *start, size_t str_len);
@@ -601,6 +606,19 @@ static inline bool char_is_lower_alphanum_(char c)
}
}
static inline bool char_is_upper_alphanum_(char c)
{
switch (c)
{
case UPPER_CHAR_CASE:
case NUMBER_CHAR_CASE:
case '_':
return true;
default:
return false;
}
}
static inline bool char_is_letter(char c)
{
switch (c)

View File

@@ -43,14 +43,89 @@ bool str_is_valid_lowercase_name(const char *string)
return true;
}
bool str_is_valid_constant(const char *string)
static const char *scan_past_underscore(const char *string)
{
while (string[0] == '_') string++;
if (string[0] == '\0') return NULL;
return string;
}
const char *str_unescape(char *string)
{
assert(string[0] == '"');
char c;
// Must start with a lower case
int length = 0;
size_t index = 0;
while ((c = string++[0]) != '"')
{
if (c == 0) return NULL;
if (c == '\\')
{
c = string++[0];
}
string[index++] = c;
}
string[index] = '\0';
return string;
}
bool str_is_type(const char *string)
{
string = scan_past_underscore(string);
if (!string) return false;
char c = string++[0];
if (!char_is_upper(c)) return false;
bool found_lower = false;
while ((c = *(string++)) != '\0')
{
if (!char_is_upper(c) && c != '_') return false;
if (char_is_lower(c))
{
found_lower = true;
continue;
}
if (!char_is_alphanum_(c)) return false;
}
return found_lower;
}
bool str_is_identifier(const char *string)
{
string = scan_past_underscore(string);
if (!string) return false;
char c = string++[0];
if (!char_is_lower(c)) return false;
while ((c = *(string++)) != '\0')
{
if (!char_is_alphanum_(c)) return false;
}
return true;
}
bool str_eq(const char *str1, const char *str2)
{
return str1 == str2 || strcmp(str1, str2) == 0;
}
bool str_is_integer(const char *string)
{
if (string[0] == '-') string++;
if (!string[0]) return false;
char c;
// Must start with a lower case
while ((c = *(string++)) != '\0')
{
if (!char_is_digit(c)) return false;
}
return true;
}
bool str_is_valid_constant(const char *string)
{
string = scan_past_underscore(string);
if (!string) return false;
char c = string++[0];
if (!char_is_upper(c)) return false;
while ((c = *(string++)) != '\0') {
if (!char_is_upper_alphanum_(c)) return false;
}
return true;
}

View File

@@ -1 +1 @@
#define COMPILER_VERSION "0.4.582"
#define COMPILER_VERSION "0.4.583"