mirror of
https://github.com/c3lang/c3c.git
synced 2026-02-27 03:51:18 +00:00
Corrected default alignment on temp alloc. Added str_index_of. Added simple getline. Added a simple calculator. Allow [1..] to create a zero length slice. Added some initial macro contracts. Fix accessing enum functions. Support for @checked. Bump to 0.3.4
This commit is contained in:
@@ -4,7 +4,7 @@
|
||||
module std::core::builtin;
|
||||
|
||||
/**
|
||||
* @require is_comparable_value(a) && is_comparable_value(b)
|
||||
* @require types::is_comparable_value(a) && types::is_comparable_value(b)
|
||||
**/
|
||||
macro less(a, b) @builtin
|
||||
{
|
||||
@@ -18,7 +18,7 @@ macro less(a, b) @builtin
|
||||
}
|
||||
|
||||
/**
|
||||
* @require is_comparable_value(a) && is_comparable_value(b)
|
||||
* @require types::is_comparable_value(a) && types::is_comparable_value(b)
|
||||
**/
|
||||
macro less_eq(a, b) @builtin
|
||||
{
|
||||
@@ -32,7 +32,7 @@ macro less_eq(a, b) @builtin
|
||||
}
|
||||
|
||||
/**
|
||||
* @require is_comparable_value(a) && is_comparable_value(b)
|
||||
* @require types::is_comparable_value(a) && types::is_comparable_value(b)
|
||||
**/
|
||||
macro greater(a, b) @builtin
|
||||
{
|
||||
@@ -46,7 +46,7 @@ macro greater(a, b) @builtin
|
||||
}
|
||||
|
||||
/**
|
||||
* @require is_comparable_value(a) && is_comparable_value(b)
|
||||
* @require types::is_comparable_value(a) && types::is_comparable_value(b)
|
||||
**/
|
||||
macro greater_eq(a, b) @builtin
|
||||
{
|
||||
@@ -60,7 +60,7 @@ macro greater_eq(a, b) @builtin
|
||||
}
|
||||
|
||||
/**
|
||||
* @require is_equatable_value(a) && is_equatable_value(b) `values must be equatable`
|
||||
* @require types::is_equatable_value(a) && types::is_equatable_value(b) `values must be equatable`
|
||||
**/
|
||||
macro bool equals(a, b) @builtin
|
||||
{
|
||||
|
||||
@@ -166,17 +166,17 @@ macro talloc($Type) @builtin
|
||||
return temp_allocator().alloc_aligned($Type.sizeof, $alignof($Type))!!;
|
||||
}
|
||||
|
||||
fn void* tmalloc(usize size, usize alignment = 0) @builtin @inline
|
||||
fn void* tmalloc(usize size, usize alignment = allocator::DEFAULT_MEM_ALIGNMENT) @builtin @inline
|
||||
{
|
||||
return temp_allocator().alloc_aligned(size, alignment)!!;
|
||||
}
|
||||
|
||||
fn void* tcalloc(usize size, usize alignment = 0) @builtin @inline
|
||||
fn void* tcalloc(usize size, usize alignment = allocator::DEFAULT_MEM_ALIGNMENT) @builtin @inline
|
||||
{
|
||||
return temp_allocator().calloc_aligned(size, alignment)!!;
|
||||
}
|
||||
|
||||
fn void* trealloc(void* ptr, usize size, usize alignment = 0) @builtin @inline
|
||||
fn void* trealloc(void* ptr, usize size, usize alignment = allocator::DEFAULT_MEM_ALIGNMENT) @builtin @inline
|
||||
{
|
||||
return temp_allocator().realloc_aligned(ptr, size, alignment)!!;
|
||||
}
|
||||
|
||||
@@ -29,6 +29,32 @@ fn String join(char[][] s, char[] joiner)
|
||||
return res;
|
||||
}
|
||||
|
||||
fn usize! str_index_of(char[] s, char[] needle)
|
||||
{
|
||||
usize match = 0;
|
||||
usize needed = needle.len;
|
||||
if (!needed) return SearchResult.MISSING!;
|
||||
usize index_start = 0;
|
||||
char search = needle[0];
|
||||
foreach (usize i, char c : s)
|
||||
{
|
||||
if (c == search)
|
||||
{
|
||||
if (!match) index_start = i;
|
||||
match++;
|
||||
if (match == needed) return i;
|
||||
search = needle[match];
|
||||
continue;
|
||||
}
|
||||
if (match)
|
||||
{
|
||||
match = 0;
|
||||
search = needle[0];
|
||||
}
|
||||
}
|
||||
return SearchResult.MISSING!;
|
||||
}
|
||||
|
||||
fn ZString copy_zstring(char[] s)
|
||||
{
|
||||
usize len = s.len;
|
||||
|
||||
@@ -8,7 +8,7 @@ fault ConversionResult
|
||||
VALUE_OUT_OF_UNSIGNED_RANGE,
|
||||
}
|
||||
/**
|
||||
* @require type.kind == SIGNED_INT || type.kind == UNSIGNED_INT || type.kind == ENUM, "Argument was not an integer"
|
||||
* @require $Type.kind.is_int() || $Type.kind == TypeKind.ENUM "Argument was not an integer"
|
||||
**/
|
||||
macro variant_to_int(variant v, $Type)
|
||||
{
|
||||
|
||||
@@ -169,6 +169,22 @@ fn usize! File.println(File* file, char[] string)
|
||||
return len + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param [&in] file
|
||||
* @require file.file `File must be initialized`
|
||||
*/
|
||||
fn String File.getline(File* file, Allocator* allocator = mem::current_allocator())
|
||||
{
|
||||
String s = string::new_with_capacity(120, allocator);
|
||||
while (!file.eof())
|
||||
{
|
||||
int c = libc::fgetc(file.file);
|
||||
if (c == '\n') break;
|
||||
s.append_char((char)c);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param [&in] file
|
||||
* @require file.file `File must be initialized`
|
||||
|
||||
@@ -186,6 +186,7 @@ extern fn int putchar(int c);
|
||||
extern fn int puts(char* str);
|
||||
extern fn int ungetc(int c, CFile stream);
|
||||
extern fn void perror(char* str);
|
||||
extern fn isize getline(char** linep, usize* linecapp, CFile stream);
|
||||
|
||||
// vsprintf vprintf not supported
|
||||
|
||||
|
||||
41
resources/examples/plus_minus.c3
Normal file
41
resources/examples/plus_minus.c3
Normal file
@@ -0,0 +1,41 @@
|
||||
module test;
|
||||
import std::io;
|
||||
import libc;
|
||||
|
||||
fault TokenResult
|
||||
{
|
||||
NO_MORE_TOKENS
|
||||
}
|
||||
|
||||
fn char[]! read_next(char[]* remaining)
|
||||
{
|
||||
// Skip spaces.
|
||||
while (remaining.len > 0 && (*remaining)[0] == ' ') *remaining = (*remaining)[1..];
|
||||
char* ptr_start = remaining.ptr;
|
||||
usize len = 0;
|
||||
while (remaining.len > 0 && (*remaining)[0] != ' ')
|
||||
{
|
||||
len++;
|
||||
*remaining = (*remaining)[1..];
|
||||
}
|
||||
if (!len) return TokenResult.NO_MORE_TOKENS!;
|
||||
return ptr_start[:len];
|
||||
}
|
||||
|
||||
fn void main(char[][] args)
|
||||
{
|
||||
String s = io::stdin().getline();
|
||||
defer s.destroy();
|
||||
char[] numbers = s.str();
|
||||
int val = 0;
|
||||
bool add = true;
|
||||
while (try char[] token = read_next(&numbers))
|
||||
{
|
||||
int i = libc::atoi(token.ptr);
|
||||
val = add ? val + i : val - i;
|
||||
char[]! op = read_next(&numbers);
|
||||
if (catch op) break;
|
||||
add = op[0] == '+';
|
||||
}
|
||||
io::printfln("%d", val);
|
||||
}
|
||||
@@ -1482,6 +1482,7 @@ typedef struct
|
||||
bool in_test_mode : 1;
|
||||
unsigned errors_found;
|
||||
unsigned warnings_found;
|
||||
bool suppress_errors;
|
||||
Decl ***locals_list;
|
||||
HTable compiler_defines;
|
||||
Module std_module;
|
||||
@@ -1907,8 +1908,6 @@ static inline Decl *decl_flatten(Decl *decl)
|
||||
// --- Diag functions
|
||||
|
||||
|
||||
void diag_verror_range(SourceSpan location, const char *message, va_list args);
|
||||
|
||||
#define EXPR_NEW_EXPR(kind_, expr_) expr_new(kind_, (expr_)->span)
|
||||
#define EXPR_NEW_TOKEN(kind_) expr_new(kind_, c->span)
|
||||
|
||||
|
||||
@@ -152,17 +152,9 @@ static void vprint_error(SourceSpan location, const char *message, va_list args)
|
||||
}
|
||||
|
||||
|
||||
void diag_verror_range(SourceSpan location, const char *message, va_list args)
|
||||
{
|
||||
if (global_context.in_panic_mode) return;
|
||||
global_context.in_panic_mode = true;
|
||||
vprint_error(location, message, args);
|
||||
global_context.errors_found++;
|
||||
}
|
||||
|
||||
|
||||
void sema_verror_range(SourceSpan location, const char *message, va_list args)
|
||||
{
|
||||
if (global_context.suppress_errors) return;
|
||||
vprint_error(location, message, args);
|
||||
global_context.errors_found++;
|
||||
}
|
||||
@@ -208,6 +200,7 @@ void sema_error_prev_at(SourceSpan loc, const char *message, ...)
|
||||
|
||||
void sema_error(ParseContext *context, const char *message, ...)
|
||||
{
|
||||
if (global_context.suppress_errors) return;
|
||||
global_context.errors_found++;
|
||||
File *file = context->unit->file;
|
||||
va_list list;
|
||||
|
||||
@@ -2342,7 +2342,7 @@ static void llvm_emit_slice_values(GenContext *c, Expr *slice, BEValue *parent_r
|
||||
{
|
||||
assert(len.value);
|
||||
BEValue exceeds_size;
|
||||
llvm_emit_int_comparison(c, &exceeds_size, &start_index, &len, BINARYOP_GE);
|
||||
llvm_emit_int_comparison(c, &exceeds_size, &start_index, &len, BINARYOP_GT);
|
||||
llvm_emit_panic_if_true(c, &exceeds_size, "Index exceeds array length.", slice->span);
|
||||
}
|
||||
|
||||
|
||||
@@ -1778,7 +1778,7 @@ static inline bool parse_func_macro_header(ParseContext *c, bool is_macro,
|
||||
/**
|
||||
* macro ::= macro_header '(' macro_params ')' compound_statement
|
||||
*/
|
||||
static inline Decl *parse_macro_declaration(ParseContext *c, Visibility visibility)
|
||||
static inline Decl *parse_macro_declaration(ParseContext *c, Visibility visibility, AstId docs)
|
||||
{
|
||||
DeclKind kind = try_consume(c, TOKEN_MACRO) ? DECL_MACRO : DECL_GENERIC;
|
||||
if (kind == DECL_GENERIC) advance_and_verify(c, TOKEN_GENERIC);
|
||||
@@ -1786,6 +1786,7 @@ static inline Decl *parse_macro_declaration(ParseContext *c, Visibility visibili
|
||||
Decl *decl = decl_calloc();
|
||||
decl->decl_kind = kind;
|
||||
decl->visibility = visibility;
|
||||
decl->macro_decl.docs = docs;
|
||||
TypeInfoId *rtype_ref = &decl->macro_decl.rtype;
|
||||
TypeInfoId *method_type_ref = &decl->macro_decl.type_parent;
|
||||
if (!parse_func_macro_header(c, true, rtype_ref, method_type_ref, &decl->name, &decl->span)) return poisoned_decl;
|
||||
@@ -2426,7 +2427,7 @@ Decl *parse_top_level_statement(ParseContext *c)
|
||||
case TOKEN_GENERIC:
|
||||
case TOKEN_MACRO:
|
||||
{
|
||||
ASSIGN_DECL_OR_RET(decl, parse_macro_declaration(c, visibility), poisoned_decl);
|
||||
ASSIGN_DECL_OR_RET(decl, parse_macro_declaration(c, visibility, docs), poisoned_decl);
|
||||
break;
|
||||
}
|
||||
case TOKEN_ENUM:
|
||||
|
||||
@@ -1420,7 +1420,7 @@ FAIL_MISSING:
|
||||
|
||||
static inline bool sema_expr_analyse_call_invocation(SemaContext *context, Expr *call, CalledDecl callee, bool *failable)
|
||||
{
|
||||
// 1. Check body arguments.
|
||||
// 1. Check body arguments (for macro calls, or possibly broken )
|
||||
if (!sema_check_invalid_body_arguments(context, call, &callee)) return false;
|
||||
|
||||
// 2. Pick out all the arguments and parameters.
|
||||
@@ -1945,6 +1945,12 @@ bool sema_expr_analyse_macro_call(SemaContext *context, Expr *call_expr, Expr *s
|
||||
if (!sema_add_local(¯o_context, param)) goto EXIT_FAIL;
|
||||
}
|
||||
|
||||
AstId assert_first = 0;
|
||||
AstId* next = &assert_first;
|
||||
|
||||
if (!sema_analyse_contracts(¯o_context, decl->macro_decl.docs, &next)) return false;
|
||||
sema_append_contract_asserts(assert_first, body);
|
||||
|
||||
if (!sema_analyse_statement(¯o_context, body)) goto EXIT_FAIL;
|
||||
|
||||
bool is_no_return = decl->macro_decl.attr_noreturn;
|
||||
@@ -3786,7 +3792,7 @@ CHECK_DEEPER:
|
||||
member = sema_resolve_symbol_in_current_dynamic_scope(context, kw);
|
||||
SCOPE_END;
|
||||
|
||||
if (member && decl_is_enum_kind(decl) && parent->expr_kind == EXPR_CONST)
|
||||
if (member && decl_is_enum_kind(decl) && member->decl_kind == DECL_VAR && parent->expr_kind == EXPR_CONST)
|
||||
{
|
||||
assert(parent->const_expr.const_kind == CONST_ENUM);
|
||||
Expr *copy_init = expr_macro_copy(current_parent->const_expr.enum_val->enum_constant.args[member->var.index]);
|
||||
|
||||
@@ -58,7 +58,8 @@ bool splitpathref(const char *string, ArraySize len, Path **path_ref, const char
|
||||
|
||||
bool expr_is_ltype(Expr *expr);
|
||||
bool sema_expr_check_assign(SemaContext *c, Expr *expr);
|
||||
|
||||
bool sema_analyse_contracts(SemaContext *context, AstId doc, AstId **asserts);
|
||||
void sema_append_contract_asserts(AstId assert_first, Ast* compound_stmt);
|
||||
void sema_context_init(SemaContext *context, CompilationUnit *unit);
|
||||
void sema_context_destroy(SemaContext *context);
|
||||
Decl **global_context_acquire_locals_list(void);
|
||||
|
||||
@@ -2446,15 +2446,20 @@ static bool sema_analyse_checked(SemaContext *context, Ast *directive, AstId **a
|
||||
{
|
||||
Expr *declexpr = directive->doc_stmt.contract.decl_exprs;
|
||||
bool success = true;
|
||||
bool suppress_error = global_context.suppress_errors;
|
||||
global_context.suppress_errors = true;
|
||||
SCOPE_START
|
||||
VECEACH(declexpr->cond_expr, j)
|
||||
{
|
||||
Expr *expr = declexpr->cond_expr[j];
|
||||
|
||||
const char *comment = directive->doc_stmt.contract.comment;
|
||||
global_context.suppress_errors = comment != NULL;
|
||||
if (!sema_analyse_cond_expr(context, expr))
|
||||
{
|
||||
const char *comment = directive->doc_stmt.contract.comment;
|
||||
if (comment)
|
||||
{
|
||||
global_context.suppress_errors = false;
|
||||
SEMA_ERROR(expr, comment);
|
||||
}
|
||||
success = false;
|
||||
@@ -2463,10 +2468,20 @@ static bool sema_analyse_checked(SemaContext *context, Ast *directive, AstId **a
|
||||
}
|
||||
END:
|
||||
SCOPE_END;
|
||||
global_context.suppress_errors = suppress_error;
|
||||
return success;
|
||||
}
|
||||
|
||||
static bool sema_analyse_contracts(SemaContext *context, AstId doc, AstId **asserts)
|
||||
void sema_append_contract_asserts(AstId assert_first, Ast* compound_stmt)
|
||||
{
|
||||
assert(compound_stmt->ast_kind == AST_COMPOUND_STMT);
|
||||
if (!assert_first) return;
|
||||
Ast *ast = new_ast(AST_COMPOUND_STMT, compound_stmt->span);
|
||||
ast->compound_stmt.first_stmt = assert_first;
|
||||
ast_prepend(&compound_stmt->compound_stmt.first_stmt, ast);
|
||||
}
|
||||
|
||||
bool sema_analyse_contracts(SemaContext *context, AstId doc, AstId **asserts)
|
||||
{
|
||||
while (doc)
|
||||
{
|
||||
@@ -2548,12 +2563,7 @@ bool sema_analyse_function_body(SemaContext *context, Decl *func)
|
||||
}
|
||||
else
|
||||
{
|
||||
if (assert_first)
|
||||
{
|
||||
Ast *ast = new_ast(AST_COMPOUND_STMT, body->span);
|
||||
ast->compound_stmt.first_stmt = assert_first;
|
||||
ast_prepend(&body->compound_stmt.first_stmt, ast);
|
||||
}
|
||||
sema_append_contract_asserts(assert_first, body);
|
||||
Type *canonical_rtype = type_no_fail(prototype->rtype)->canonical;
|
||||
// Insert an implicit return
|
||||
if (canonical_rtype == type_void)
|
||||
|
||||
@@ -1 +1 @@
|
||||
#define COMPILER_VERSION "0.3.3"
|
||||
#define COMPILER_VERSION "0.3.4"
|
||||
Reference in New Issue
Block a user