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:
Christoffer Lerno
2022-08-05 00:42:22 +02:00
parent 046469843c
commit 398e19d727
15 changed files with 129 additions and 35 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View 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);
}

View File

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

View File

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

View File

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

View File

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

View File

@@ -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(&macro_context, param)) goto EXIT_FAIL;
}
AstId assert_first = 0;
AstId* next = &assert_first;
if (!sema_analyse_contracts(&macro_context, decl->macro_decl.docs, &next)) return false;
sema_append_contract_asserts(assert_first, body);
if (!sema_analyse_statement(&macro_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]);

View File

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

View File

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

View File

@@ -1 +1 @@
#define COMPILER_VERSION "0.3.3"
#define COMPILER_VERSION "0.3.4"