Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion cleaf_backlog.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,13 @@ x 2026-06-09 2026-06-09 Add "asm" function for inline assembly in the code base
(B) Add "sizeof" compiler function @global
(C) Add "type" keyword to create types at compile time @global
(A) Import workflow for multiple file and std compilation @global
(D) Add "char" type @global
x 2026-06-11 2026-06-11 Add "char" type @global
(B) Add Array type @global
(B) Add signed type @global
x 2026-06-10 2026-06-10 Choose between mutable or const for var by default and change compiler behavior depending on this choice @global
(A) Enhance error reporting @thirdparty
(B) choose if we pass struct var by value or pointer and implement both methods
(C) make language documentation @docs
(D) add defer keyword @global
(A) add type casting @global
(B) add way to get var address @global
38 changes: 30 additions & 8 deletions src/frontend/ast.c
Original file line number Diff line number Diff line change
Expand Up @@ -580,8 +580,9 @@ expression_t* ast_parse_expr_call(parser_t* p)
int l = 0;
while (!check_next(p, ')', l)) {
if ((size_t) p->pos + l > p->count) {
error_report_at_token(p->error_ctx, peek_next(p, l - 1), ERROR_SEVERITY_ERROR,
"unexpected EOF, looking for ')' token");
error_report_at_token(
p->error_ctx, peek_next(p, l - 1), ERROR_SEVERITY_ERROR,
"unexpected EOF, looking for ')' token");
}
++l;
}
Expand Down Expand Up @@ -679,8 +680,9 @@ expression_t* ast_parse_expr_unary(parser_t* p)
}

if (!operand) {
error_report_at_token(p->error_ctx, peek(p), ERROR_SEVERITY_ERROR,
"expected identifier or literal before postfix operator");
error_report_at_token(
p->error_ctx, peek(p), ERROR_SEVERITY_ERROR,
"expected identifier or literal before postfix operator");
free_expression(e);
return NULL;
}
Expand Down Expand Up @@ -723,11 +725,29 @@ expression_t* parse_primary(parser_t* p)
return NULL;
}

expression_t* ast_parse_expr_char_lit(parser_t* p)
{
expression_t* expr = calloc(1, sizeof(expression_t));
if (!expr) {
error_report_general(ERROR_SEVERITY_ERROR, "out of memory");
return NULL;
}
expr->type = EXPRESSION_CHAR_LIT;

token_t* char_tok = advance(p);

expr->char_lit.value = (unsigned char) char_tok->int_value;
return expr;
}

expression_t* parse_expression(parser_t* p)
{
if (check(p, '{'))
return ast_parse_expr_composite_literal(p);

if (check(p, LEXER_token_charlit))
return ast_parse_expr_char_lit(p);

if (check(p, LEXER_token_plusplus) ||
check(p, LEXER_token_minusminus) ||
check(p, '-') ||
Expand Down Expand Up @@ -830,8 +850,9 @@ declaration_t* ast_parse_function(parser_t* p)

if (strcmp(type_tok->string_value, "var") == 0) {
if (p->error_ctx) {
error_report_at_token(p->error_ctx, type_tok, ERROR_SEVERITY_ERROR,
"`var` cannot be use for functions parameters. Use an explicit type instead");
error_report_at_token(
p->error_ctx, type_tok, ERROR_SEVERITY_ERROR,
"`var` cannot be use for functions parameters. Use an explicit type instead");
}
free_declaration(decl);
return NULL;
Expand Down Expand Up @@ -1200,8 +1221,9 @@ declaration_t* ast_parse_struct_decl(parser_t* p)

if (strcmp(type_tok->string_value, "var") == 0) {
if (p->error_ctx) {
error_report_at_token(p->error_ctx, type_tok, ERROR_SEVERITY_ERROR,
"`var` cannot be use for struct members. Use an explicit type instead");
error_report_at_token(
p->error_ctx, type_tok, ERROR_SEVERITY_ERROR,
"`var` cannot be use for struct members. Use an explicit type instead");
}
free_declaration(decl);
return NULL;
Expand Down
1 change: 1 addition & 0 deletions src/frontend/ast.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ expression_t* ast_parse_expr_comparison_binary(parser_t* p);
expression_t* ast_parse_expr_call(parser_t* p);
expression_t* ast_parse_expr_unary(parser_t* p);
expression_t* ast_parse_expr_composite_literal(parser_t* p);
expression_t* ast_parse_expr_char_lit(parser_t* p);

void populate_parser_known_type(known_type_array* types);

Expand Down
2 changes: 2 additions & 0 deletions src/frontend/ast_definition.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ typedef enum
typedef enum
{
EXPRESSION_INT_LIT,
EXPRESSION_CHAR_LIT,
EXPRESSION_VAR,
EXPRESSION_BINARY,
EXPRESSION_CALL,
Expand Down Expand Up @@ -193,6 +194,7 @@ struct expression_t

union {
struct { int value; } int_lit;
struct { char value; } char_lit;
struct {
typed_identifier_t ident;
expression_t* member;
Expand Down
4 changes: 4 additions & 0 deletions src/frontend/ast_printer.c
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,10 @@ static void print_expression(expression_t* e, const char* prefix, bool is_last)
printf(CLR_LIT "IntegerLiteral" CLR_RESET " %d\n", e->int_lit.value);
break;

case EXPRESSION_CHAR_LIT:
printf(CLR_LIT "CharLiteral" CLR_RESET " %c\n", e->char_lit.value);
break;

case EXPRESSION_VAR:
printf(CLR_LIT "VarRef" CLR_RESET " '%s': ",
e->var.ident.ident_name ?
Expand Down
1 change: 1 addition & 0 deletions src/frontend/lexer.h
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,7 @@ static token_t lexer_copy_token(lexer_t* lex)
}
break;
case LEXER_token_intlit:
case LEXER_token_charlit:
token.int_value = lex->int_value;
break;
default:
Expand Down
7 changes: 6 additions & 1 deletion src/frontend/semantic.c
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,10 @@ known_type_t semantic_check_expression(
.size = types_description[kind].size,
};
}

if (expr->type == EXPRESSION_CHAR_LIT) {
return expr->var.ident.type;
}

if (expr->type == EXPRESSION_VAR) {
variable_symbol_t* vs =
Expand Down Expand Up @@ -616,7 +620,8 @@ void semantic_check_var_declaration(
analyzer, decl->source_pos -1,
"can't store value. Exceeding max type accetping value");
}
if (expected_type.kind >= actual_type.kind)
if (expected_type.kind >= actual_type.kind &&
actual_type.kind != TYPE_CHAR)
goto var_def_put;

if (actual_type.kind != TYPE_ERROR) {
Expand Down
14 changes: 8 additions & 6 deletions src/frontend/types.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

typedef enum {
TYPE_U8 = 0,
TYPE_CHAR,
TYPE_U16,
TYPE_U32,
TYPE_INT,
Expand All @@ -22,12 +23,13 @@ typedef struct {

// we store sizes as bytes while the syntax use bits for convinience
static const types_ident types_description[TYPE_COUNT] = {
{"u8", 1},
{"u16", 2},
{"u32", 4},
{"int", 4},
{"u64", 8},
{"var", -1},
{"u8", 1},
{"char", 1},
{"u16", 2},
{"u32", 4},
{"int", 4},
{"u64", 8},
{"var", -1},
{"untype", -1},
};

Expand Down
16 changes: 15 additions & 1 deletion src/middleend/hir.c
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ int IR_lower_composite_literal_expression(
mov_offset->kind = IR_MOV_OFFSET;
mov_offset->offset.timing = IR_PRE_OFFSET;
mov_offset->dest.id = save;
mov_offset->dest.size = decl->var_decl.ident.type.kind;
mov_offset->dest.size = decl->var_decl.ident.type.size;
mov_offset->src.id = func->next_temp_id;

size_t j = 0;
Expand Down Expand Up @@ -424,6 +424,20 @@ int IR_lower_expression(HIR_parser_t* hir,
return 0;
}

if (expr->type == EXPRESSION_CHAR_LIT) {
IR_instruction_t* instr = calloc(1, sizeof(IR_instruction_t));
if (!instr) {
error_report_general(ERROR_SEVERITY_ERROR, "out of memory");
return -1;
}

instr->kind = IR_INT_CONST;
instr->dest.id = ++(func->next_temp_id);
instr->int_value = expr->char_lit.value;
da_append(func->code, instr);
return 0;
}

if (expr->type == EXPRESSION_VAR) {
IR_instruction_t* instr = calloc(1, sizeof(IR_instruction_t));
if (!instr) {
Expand Down
15 changes: 15 additions & 0 deletions test/ast_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -588,6 +588,21 @@ ct_test(ast, const_in_func_parameter, "fn main(int! a) { }") {
ct_assert_eq(decl->func.params.count, 1, "function should have one parameter");
ct_assert_eq(decl->func.params.items[0].is_constant, true, "first function parameter should be marked as constant");

free_declaration(decl);
da_free(&parser);
}

ct_test(ast, char_var_decl, "char a = 'a';") {
declaration_t* decl = parse_declaration(&parser);
ct_assert_not_null(decl, "char var declaration should not be null");
ct_assert_eq(decl->var_decl.ident.type.name, "char", "var type should be char");
ct_assert_eq(decl->var_decl.ident.ident_name, "a", "Variable name should be 'a'");
ct_assert_eq(decl->var_decl.ident.type.kind, TYPE_CHAR, "Variable type should be TYPE_CHAR");
ct_assert_not_null(decl->var_decl.init, "Variable init expression should not be NULL");
ct_assert_eq(decl->var_decl.init->type, EXPRESSION_CHAR_LIT, "Init expression should be char literal");
ct_assert_eq(decl->var_decl.init->char_lit.value, 'a', "Init char value should be 'a'");


free_declaration(decl);
da_free(&parser);
}
10 changes: 10 additions & 0 deletions test/codegen_case/char_var_declaration.asm
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
section .text
global _start
_start:
push rbp
mov rbp, rsp
sub rsp, 2
mov r11, 97
mov [rbp - 8], r11
mov r12, 12
mov [rbp - 16], r12
4 changes: 4 additions & 0 deletions test/codegen_case/char_var_declaration.clf
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
fn main() {
char a = 'a';
char b = 12;
}
4 changes: 4 additions & 0 deletions test/codegen_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -253,3 +253,7 @@ ct_test(codegen_test, asm_no_args, "test/codegen_case/asm_no_args.clf", "test/co
ct_test(codegen_test, asm_with_arg, "test/codegen_case/asm_with_arg.clf", "test/codegen_case/asm_with_arg.asm") {
ct_assert_eq(result, 0, "codegen gives right output for asm with variable interpolation");
}

ct_test(codegen_test, char_var_declaration, "test/codegen_case/char_var_declaration.clf", "test/codegen_case/char_var_declaration.asm") {
ct_assert_eq(result, 0, "codegen gives right output for char var declaration");
}
4 changes: 4 additions & 0 deletions test/hir_case/char_var_declaration.clf
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
fn main() {
char a = 'a';
char b = 12;
}
5 changes: 5 additions & 0 deletions test/hir_case/char_var_declaration.res
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Function main
0: t1 = INT_CONST 97
1: STR slot(a), b1
2: t2 = INT_CONST 12
3: STR slot(b), b2
4 changes: 4 additions & 0 deletions test/hir_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -246,3 +246,7 @@ ct_test(hir_test, asm_no_args, "test/hir_case/asm_no_args.clf", "test/hir_case/a
ct_test(hir_test, asm_with_arg, "test/hir_case/asm_with_arg.clf", "test/hir_case/asm_with_arg.res") {
ct_assert_eq(result, 0, "hir gives right output for asm with variable interpolation");
}

ct_test(hir_test, char_var_declaration, "test/hir_case/char_var_declaration.clf", "test/hir_case/char_var_declaration.res") {
ct_assert_eq(result, 0, "hir gives right output for char var declaration");
}
11 changes: 11 additions & 0 deletions test/semantic_case/char_declaration.clf
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
fn main() {
char a = 'q';
char b = 256;
char c = 'a';
char d = 34;
char e = foo('e');
}

fn foo(char a): char {
return a;
}
5 changes: 5 additions & 0 deletions test/semantic_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -690,3 +690,8 @@ ct_test(semantic_case, const_in_func_parameter_error, "test/semantic_case/const_

free_analyzer(&analyzer);
}

ct_test(semantic_case, char_declaration, "test/semantic_case/char_declaration.clf") {
ct_assert_eq(analyzer.error_count, 2, "Should have 2 errors");
free_analyzer(&analyzer);
}
Loading