mirror of
https://github.com/c3lang/c3c.git
synced 2026-02-27 20:11:17 +00:00
Fix constant typeid comparisons. Allow methods to use & and * and constants. Improved error messages. Updated String type with generic append.
This commit is contained in:
committed by
Christoffer Lerno
parent
c1de3f059e
commit
7e0a29ef40
@@ -41,14 +41,12 @@ fn String new(char[] c)
|
||||
return (String)data;
|
||||
}
|
||||
|
||||
fn ZString String.zstr(String* str)
|
||||
fn ZString String.zstr(String str)
|
||||
{
|
||||
if (!str) return (ZString)"";
|
||||
StringData* data = str.data();
|
||||
if (!data) return (ZString)"";
|
||||
if (data.capacity == data.len)
|
||||
{
|
||||
libc::printf("feofk\n");
|
||||
str.reserve(1);
|
||||
data.chars[data.len] = 0;
|
||||
}
|
||||
@@ -59,24 +57,24 @@ fn ZString String.zstr(String* str)
|
||||
return (ZString)&data.chars[0];
|
||||
}
|
||||
|
||||
fn usize String.len(String* this)
|
||||
fn usize String.len(String this)
|
||||
{
|
||||
if (!*this) return 0;
|
||||
if (!this) return 0;
|
||||
return this.data().len;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require new_size <= this.len()
|
||||
*/
|
||||
fn void String.chop(String* this, usize new_size)
|
||||
fn void String.chop(String this, usize new_size)
|
||||
{
|
||||
if (!*this) return;
|
||||
if (!this) return;
|
||||
this.data().len = new_size;
|
||||
}
|
||||
|
||||
fn char[] String.str(String* str)
|
||||
fn char[] String.str(String str)
|
||||
{
|
||||
StringData* data = (StringData*)*str;
|
||||
StringData* data = (StringData*)str;
|
||||
return data.chars[0..data.len - 1];
|
||||
}
|
||||
|
||||
@@ -92,7 +90,7 @@ fn void String.append_utf32(String* str, Char32[] chars)
|
||||
/**
|
||||
* @require index < str.len()
|
||||
**/
|
||||
fn void String.set(String* str, usize index, char c)
|
||||
fn void String.set(String str, usize index, char c)
|
||||
{
|
||||
str.data().chars[index] = c;
|
||||
}
|
||||
@@ -147,7 +145,7 @@ fn void String.append_char32(String* str, Char32 c)
|
||||
|
||||
fn String String.copy(String* str, Allocator allocator = { null, null })
|
||||
{
|
||||
if (!*str)
|
||||
if (!str)
|
||||
{
|
||||
if (allocator.function) return new_with_capacity(0, allocator);
|
||||
return (String)null;
|
||||
@@ -175,7 +173,7 @@ fn ZString String.copy_zstr(String* str, Allocator allocator = { null, null })
|
||||
return (ZString)zstr;
|
||||
}
|
||||
|
||||
fn bool String.equals(String* str, String other_string)
|
||||
fn bool String.equals(String str, String other_string)
|
||||
{
|
||||
StringData *str1 = str.data();
|
||||
StringData *str2 = other_string.data();
|
||||
@@ -200,7 +198,7 @@ fn void String.destroy(String* str)
|
||||
*str = (String)null;
|
||||
}
|
||||
|
||||
fn bool String.less(String* str, String other_string)
|
||||
fn bool String.less(String str, String other_string)
|
||||
{
|
||||
StringData* str1 = str.data();
|
||||
StringData* str2 = other_string.data();
|
||||
@@ -217,7 +215,7 @@ fn bool String.less(String* str, String other_string)
|
||||
return true;
|
||||
}
|
||||
|
||||
fn void String.append(String* this, char[] str)
|
||||
fn void String.append_chars(String* this, char[] str)
|
||||
{
|
||||
usize other_len = str.len;
|
||||
if (!other_len) return;
|
||||
@@ -255,29 +253,50 @@ fn void String.append_char(String* str, char c)
|
||||
data.chars[data.len++] = c;
|
||||
}
|
||||
|
||||
macro void String.@append(String &str, value)
|
||||
macro bool is_pointer_char_array($Type)
|
||||
{
|
||||
$switch ($typeof(value)):
|
||||
var $Type2 = $Type;
|
||||
$if ($Type.typeid.kind != TypeKind.POINTER):
|
||||
return false;
|
||||
$elif ($Type.typeid.inner.kind != TypeKind.ARRAY):
|
||||
return false;
|
||||
$elif ($Type.typeid.inner.inner != char.typeid):
|
||||
return false;
|
||||
$else:
|
||||
return true;
|
||||
$endif;
|
||||
}
|
||||
|
||||
macro void String.append(String* str, value)
|
||||
{
|
||||
var $Type = $typeof(value);
|
||||
$switch ($Type):
|
||||
$case char:
|
||||
$case ichar:
|
||||
str.append_char(value);
|
||||
$case String:
|
||||
str.append_string(value);
|
||||
$case char[]:
|
||||
str.append(value);
|
||||
$case char32:
|
||||
str.append_chars(value);
|
||||
$case Char32:
|
||||
str.append_char32(value);
|
||||
$default:
|
||||
$assert("Unsupported type for appending");
|
||||
$if ($Type.typeid.kind == TypeKind.SIGNED_INT || $Type.typeid.kind == TypeKind.UNSIGNED_INT):
|
||||
str.append_char32((Char32)value);
|
||||
$elif (is_pointer_char_array($Type)):
|
||||
str.append_chars(value);
|
||||
$else:
|
||||
$assert("Unsupported type for appending");
|
||||
$endif;
|
||||
$endswitch;
|
||||
}
|
||||
|
||||
|
||||
private fn StringData* String.data(String* str) @inline
|
||||
private fn StringData* String.data(String str) @inline
|
||||
{
|
||||
return (StringData*)*str;
|
||||
return (StringData*)str;
|
||||
}
|
||||
|
||||
|
||||
private fn void String.reserve(String* str, usize addition)
|
||||
{
|
||||
StringData* data = str.data();
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
module std::math;
|
||||
module std::math::matrix;
|
||||
|
||||
fault MatrixError {
|
||||
MATRIX_INVERSE_DOESNT_EXIST,
|
||||
@@ -360,7 +360,7 @@ fn Matrix4x4 Matrix4x4.scale(Matrix4x4* m, float[<3>] v) {
|
||||
}
|
||||
|
||||
|
||||
fn Matrix4x4 Matrix4x4.ortho(float left, float right, float top, float bottom, float near, float far) {
|
||||
fn Matrix4x4 ortho(float left, float right, float top, float bottom, float near, float far) {
|
||||
float width = right - left;
|
||||
float height = top - bottom;
|
||||
float depth = far - near;
|
||||
@@ -375,7 +375,7 @@ fn Matrix4x4 Matrix4x4.ortho(float left, float right, float top, float bottom, f
|
||||
}
|
||||
|
||||
// fov in radians
|
||||
fn Matrix4x4 Matrix4x4.perspective(float fov, float aspect_ratio, float near, float far) {
|
||||
fn Matrix4x4 perspective(float fov, float aspect_ratio, float near, float far) {
|
||||
float top = ((float)math::sin(fov / 2) / (float)math::cos(fov / 2)) * near;
|
||||
float right = top * aspect_ratio;
|
||||
float depth = far - near;
|
||||
|
||||
@@ -1913,6 +1913,7 @@ void expr_const_set_float(ExprConst *expr, Real d, TypeKind kind);
|
||||
void expr_const_set_bool(ExprConst *expr, bool b);
|
||||
void expr_const_set_null(ExprConst *expr);
|
||||
|
||||
bool expr_const_in_range(const ExprConst *left, const ExprConst *right, const ExprConst *right_to);
|
||||
bool expr_const_compare(const ExprConst *left, const ExprConst *right, BinaryOp op);
|
||||
bool expr_const_will_overflow(const ExprConst *expr, TypeKind kind);
|
||||
|
||||
|
||||
@@ -102,7 +102,8 @@ bool expr_const_compare(const ExprConst *left, const ExprConst *right, BinaryOp
|
||||
is_eq = !strncmp(left->string.chars, right->string.chars, left->string.len);
|
||||
break;
|
||||
case CONST_TYPEID:
|
||||
return left->typeid == right->typeid;
|
||||
is_eq = left->typeid == right->typeid;
|
||||
break;
|
||||
case CONST_ERR:
|
||||
case CONST_ENUM:
|
||||
{
|
||||
@@ -151,6 +152,15 @@ bool expr_const_compare(const ExprConst *left, const ExprConst *right, BinaryOp
|
||||
return (op == BINARYOP_EQ) && is_eq;
|
||||
}
|
||||
|
||||
bool expr_const_in_range(const ExprConst *left, const ExprConst *right, const ExprConst *right_to)
|
||||
{
|
||||
if (right == right_to)
|
||||
{
|
||||
return expr_const_compare(left, right, BINARYOP_EQ);
|
||||
}
|
||||
return expr_const_compare(left, right, BINARYOP_GE) && expr_const_compare(left, right_to, BINARYOP_LE);
|
||||
}
|
||||
|
||||
bool float_const_fits_type(const ExprConst *expr_const, TypeKind kind)
|
||||
{
|
||||
Real hi_limit;
|
||||
|
||||
@@ -585,7 +585,7 @@ static inline Ast* parse_ct_else_stmt(ParseContext *c)
|
||||
{
|
||||
Ast *ast = new_ast(AST_CT_ELSE_STMT, c->span);
|
||||
advance_and_verify(c, TOKEN_CT_ELSE);
|
||||
TRY_CONSUME(TOKEN_COLON, "$else needs a ':', did you forget it?");
|
||||
TRY_CONSUME_AFTER(TOKEN_COLON, "$else needs a ':', did you forget it?", poisoned_ast);
|
||||
if (!parse_ct_compound_stmt(c, &ast->ct_else_stmt)) return poisoned_ast;
|
||||
return ast;
|
||||
}
|
||||
@@ -604,11 +604,11 @@ static inline Ast* parse_ct_if_stmt(ParseContext *c, bool is_elif)
|
||||
ASSIGN_EXPR_OR_RET(ast->ct_if_stmt.expr, parse_const_paren_expr(c), poisoned_ast);
|
||||
if (is_elif)
|
||||
{
|
||||
TRY_CONSUME(TOKEN_COLON, "$elif needs a ':' after the expression, did you forget it?");
|
||||
TRY_CONSUME_AFTER(TOKEN_COLON, "$elif needs a ':' after the expression, did you forget it?", poisoned_ast);
|
||||
}
|
||||
else
|
||||
{
|
||||
TRY_CONSUME(TOKEN_COLON, "$if needs a ':' after the expression, did you forget it?");
|
||||
TRY_CONSUME_AFTER(TOKEN_COLON, "$if needs a ':' after the expression, did you forget it?", poisoned_ast);
|
||||
}
|
||||
if (!parse_ct_compound_stmt(c, &ast->ct_if_stmt.then)) return poisoned_ast;
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#define TRY_EXPECT_OR_RET(_tok, _message, _type) do { if (!tok_is(c, _tok)) { SEMA_ERROR_HERE(_message); return _type; } } while(0)
|
||||
#define TRY_CONSUME_OR_RET(_tok, _message, _type) do { if (!consume(c, _tok, _message)) return _type; } while(0)
|
||||
#define TRY_CONSUME(_tok, _message) TRY_CONSUME_OR_RET(_tok, _message, poisoned_ast)
|
||||
#define TRY_CONSUME_AFTER(_tok, _message, _type) do { if (!try_consume(c, _tok)) { sema_error_at_after(c->prev_span, _message); return _type; } } while(0)
|
||||
#define CHECK_EXPR_OR_RET(_expr) do { if (!expr_ok(_expr)) return _expr; } while(0)
|
||||
|
||||
Decl *parse_top_level_statement(ParseContext *c);
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "sema_internal.h"
|
||||
|
||||
|
||||
static inline bool sema_is_valid_method_param(SemaContext *context, Decl *param, Type *parent_type);
|
||||
static bool sema_analyse_struct_union(SemaContext *context, Decl *decl);
|
||||
static bool sema_analyse_attributes(SemaContext *context, Decl *decl, Attr** attrs, AttributeDomain domain);
|
||||
static bool sema_analyse_attributes_for_var(SemaContext *context, Decl *decl);
|
||||
@@ -1116,6 +1117,14 @@ static inline bool sema_analyse_method(SemaContext *context, Decl *decl)
|
||||
type_to_error_string(parent_type->type));
|
||||
return false;
|
||||
}
|
||||
Decl **params = decl->func_decl.function_signature.params;
|
||||
if (!vec_size(params))
|
||||
{
|
||||
SEMA_ERROR(decl, "A method must start with a parameter of type %s or %s.",
|
||||
type_quoted_error_string(parent_type->type), type_quoted_error_string(type_get_ptr(parent_type->type)));
|
||||
return false;
|
||||
}
|
||||
if (!sema_is_valid_method_param(context, params[0], type)) return false;
|
||||
return unit_add_method_like(context->unit, type, decl);
|
||||
}
|
||||
|
||||
@@ -1755,6 +1764,24 @@ static inline bool sema_analyse_func(SemaContext *context, Decl *decl)
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool sema_is_valid_method_param(SemaContext *context, Decl *param, Type *parent_type)
|
||||
{
|
||||
assert(parent_type->canonical == parent_type && "Expected already the canonical version.");
|
||||
Type *param_type = param->type;
|
||||
|
||||
if (!param_type) goto ERROR;
|
||||
param_type = param_type->canonical;
|
||||
// 1. Same type ok!
|
||||
if (param_type == parent_type) return true;
|
||||
// 2. A pointer is ok!
|
||||
if (param_type->type_kind == TYPE_POINTER && param_type->pointer == parent_type) return true;
|
||||
|
||||
ERROR:
|
||||
SEMA_ERROR(param, "The first parameter must be of type %s or %s.", type_quoted_error_string(parent_type),
|
||||
type_quoted_error_string(type_get_ptr(parent_type)));
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool sema_analyse_macro_method(SemaContext *context, Decl *decl)
|
||||
{
|
||||
TypeInfo *parent_type_info = type_infoptr(decl->macro_decl.type_parent);
|
||||
@@ -1769,18 +1796,16 @@ static bool sema_analyse_macro_method(SemaContext *context, Decl *decl)
|
||||
}
|
||||
if (!vec_size(decl->macro_decl.parameters))
|
||||
{
|
||||
SEMA_ERROR(decl, "Expected at least one parameter - of type &%s.", type_to_error_string(parent_type));
|
||||
SEMA_ERROR(decl, "Expected at least one parameter - of type %s.", type_to_error_string(parent_type));
|
||||
return false;
|
||||
}
|
||||
Decl *first_param = decl->macro_decl.parameters[0];
|
||||
if (!first_param->type || first_param->type->canonical != parent_type->canonical)
|
||||
|
||||
if (!sema_is_valid_method_param(context, first_param, parent_type->canonical)) return false;
|
||||
|
||||
if (first_param->var.kind != VARDECL_PARAM_REF && first_param->var.kind != VARDECL_PARAM)
|
||||
{
|
||||
SEMA_ERROR(first_param, "The first parameter must be &%s.", type_to_error_string(parent_type));
|
||||
return false;
|
||||
}
|
||||
if (first_param->var.kind != VARDECL_PARAM_REF)
|
||||
{
|
||||
SEMA_ERROR(first_param, "The first parameter must be a ref (&) parameter.");
|
||||
SEMA_ERROR(first_param, "The first parameter must be a regular or ref (&) type.");
|
||||
return false;
|
||||
}
|
||||
return unit_add_method_like(context->unit, parent_type, decl);
|
||||
|
||||
@@ -2488,10 +2488,24 @@ static inline bool sema_expr_analyse_call(SemaContext *context, Expr *expr)
|
||||
break;
|
||||
case EXPR_ACCESS:
|
||||
decl = func_expr->access_expr.ref;
|
||||
if (decl->decl_kind == DECL_FUNC || decl->decl_kind == DECL_MACRO)
|
||||
switch (decl->decl_kind)
|
||||
{
|
||||
if (decl->decl_kind != DECL_MACRO) expr_insert_addr(func_expr->access_expr.parent);
|
||||
struct_var = func_expr->access_expr.parent;
|
||||
case DECL_MACRO:
|
||||
if (decl->macro_decl.parameters[0]->type->type_kind == TYPE_POINTER)
|
||||
{
|
||||
expr_insert_addr(func_expr->access_expr.parent);
|
||||
}
|
||||
struct_var = func_expr->access_expr.parent;
|
||||
break;
|
||||
case DECL_FUNC:
|
||||
if (decl->func_decl.function_signature.params[0]->type->type_kind == TYPE_POINTER)
|
||||
{
|
||||
expr_insert_addr(func_expr->access_expr.parent);
|
||||
}
|
||||
struct_var = func_expr->access_expr.parent;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case EXPR_TYPEINFO:
|
||||
|
||||
@@ -242,16 +242,14 @@ static inline bool sema_analyse_top_level_switch(SemaContext *context, Decl *ct_
|
||||
ExprConst *other_const = &other_case->ct_case_decl.expr->const_expr;
|
||||
ExprConst *other_const_to = other_case->ct_case_decl.to_expr
|
||||
? &other_case->ct_case_decl.to_expr->const_expr : other_const;
|
||||
if (expr_const_compare(const_expr, other_const_to, BINARYOP_LE) &&
|
||||
expr_const_compare(const_to_expr, other_const, BINARYOP_GE))
|
||||
if (expr_const_in_range(const_expr, other_const, other_const_to))
|
||||
{
|
||||
SEMA_ERROR(kase, "'%s' appears more than once.", expr_const_to_error_string(const_expr));
|
||||
SEMA_PREV(cases[j]->ct_case_decl.expr, "The previous $case was here.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (expr_const_compare(switch_expr_const, const_expr, BINARYOP_GE) &&
|
||||
expr_const_compare(switch_expr_const, const_to_expr, BINARYOP_LE))
|
||||
if (expr_const_in_range(switch_expr_const, const_expr, const_to_expr))
|
||||
{
|
||||
matched_case = (int)i;
|
||||
}
|
||||
|
||||
@@ -1531,8 +1531,7 @@ static bool sema_analyse_nextcase_stmt(SemaContext *context, Ast *statement)
|
||||
}
|
||||
ExprConst *const_expr = &case_stmt->case_stmt.expr->const_expr;
|
||||
ExprConst *to_const_expr = case_stmt->case_stmt.to_expr ? &case_stmt->case_stmt.to_expr->const_expr : const_expr;
|
||||
if (expr_const_compare(&target->const_expr, const_expr, BINARYOP_GE) &&
|
||||
expr_const_compare(&target->const_expr, to_const_expr, BINARYOP_LE))
|
||||
if (expr_const_in_range(&target->const_expr, const_expr, to_const_expr))
|
||||
{
|
||||
statement->nextcase_stmt.case_switch_stmt = astid(case_stmt);
|
||||
return true;
|
||||
@@ -1753,7 +1752,7 @@ static inline bool sema_check_value_case(SemaContext *context, Type *switch_type
|
||||
if (other->ast_kind != AST_CASE_STMT) continue;
|
||||
ExprConst *other_const = &other->case_stmt.expr->const_expr;
|
||||
ExprConst *other_to_const = other->case_stmt.to_expr ? &other->case_stmt.to_expr->const_expr : other_const;
|
||||
if (expr_const_compare(const_expr, other_to_const, BINARYOP_LE) && expr_const_compare(to_const_expr, other_const, BINARYOP_GE))
|
||||
if (expr_const_in_range(const_expr, other_const, other_to_const))
|
||||
{
|
||||
SEMA_ERROR(case_stmt, "The same case value appears more than once.");
|
||||
SEMA_PREV(other, "Here is the previous use of that value.");
|
||||
@@ -1937,14 +1936,14 @@ static bool sema_analyse_ct_switch_body(SemaContext *context, Ast *statement)
|
||||
if (other_stmt->ast_kind == AST_DEFAULT_STMT) continue;
|
||||
ExprConst *other_const = &other_stmt->case_stmt.expr->const_expr;
|
||||
ExprConst *other_const_to = other_stmt->case_stmt.to_expr ? &other_stmt->case_stmt.to_expr->const_expr : other_const;
|
||||
if (expr_const_compare(const_expr, other_const_to, BINARYOP_LE) && expr_const_compare(const_to_expr, other_const, BINARYOP_GE))
|
||||
if (expr_const_in_range(const_expr, other_const, other_const_to))
|
||||
{
|
||||
SEMA_ERROR(stmt, "'%s' appears more than once.", expr_const_to_error_string(const_expr));
|
||||
SEMA_PREV(cases[j]->case_stmt.expr, "The previous $case was here.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (expr_const_compare(switch_expr_const, const_expr, BINARYOP_GE) && expr_const_compare(switch_expr_const, const_to_expr, BINARYOP_LE))
|
||||
if (expr_const_in_range(switch_expr_const, const_expr, const_to_expr))
|
||||
{
|
||||
matched_case = (int) i;
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
#define COMPILER_VERSION "0.2.21"
|
||||
#define COMPILER_VERSION "0.2.22"
|
||||
Reference in New Issue
Block a user