mirror of
https://github.com/c3lang/c3c.git
synced 2026-02-27 12:01:16 +00:00
Adding feature flags.
This commit is contained in:
@@ -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); }
|
||||
|
||||
@@ -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
|
||||
;
|
||||
|
||||
@@ -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(¤t_arg[2], name) == 0;
|
||||
return str_eq(¤t_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"))
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 },
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
#define COMPILER_VERSION "0.4.582"
|
||||
#define COMPILER_VERSION "0.4.583"
|
||||
Reference in New Issue
Block a user