Skip to content

Commit

Permalink
Enum (#213)
Browse files Browse the repository at this point in the history
  • Loading branch information
Akuli authored Feb 11, 2023
1 parent 2ee5eb4 commit ef849db
Show file tree
Hide file tree
Showing 20 changed files with 324 additions and 45 deletions.
47 changes: 41 additions & 6 deletions src/build_cfg.c
Original file line number Diff line number Diff line change
Expand Up @@ -125,13 +125,32 @@ static const LocalVariable *build_cast(
add_unary_op(st, location, CF_PTR_CAST, obj, result);
return result;
}

if (is_number_type(obj->type) && is_number_type(to)) {
const LocalVariable *result = add_local_var(st, to);
add_unary_op(st, location, CF_NUM_CAST, obj, result);
return result;
}

if (is_integer_type(obj->type) || to->kind == TYPE_ENUM) {
const LocalVariable *i32var = add_local_var(st, intType);
const LocalVariable *result = add_local_var(st, to);
add_unary_op(st, location, CF_NUM_CAST, obj, i32var);
add_unary_op(st, location, CF_INT32_TO_ENUM, i32var, result);
return result;
}

if (obj->type->kind == TYPE_ENUM && is_integer_type(to)) {
const LocalVariable *i32var = add_local_var(st, intType);
const LocalVariable *result = add_local_var(st, to);
add_unary_op(st, location, CF_ENUM_TO_INT32, obj, i32var);
add_unary_op(st, location, CF_NUM_CAST, i32var, result);
return result;
}

if (obj->type == boolType && is_integer_type(to))
return build_bool_to_int_conversion(st, obj, location, to);

assert(0);
}

Expand Down Expand Up @@ -343,17 +362,17 @@ static const LocalVariable *build_address_of_expression(struct State *st, const
case AST_EXPR_DEREF_AND_GET_FIELD:
{
// &obj->field aka &(obj->field)
const LocalVariable *obj = build_expression(st, address_of_what->data.field.obj);
const LocalVariable *obj = build_expression(st, address_of_what->data.structfield.obj);
assert(obj->type->kind == TYPE_POINTER);
assert(obj->type->data.valuetype->kind == TYPE_STRUCT);
return build_struct_field_pointer(st, obj, address_of_what->data.field.fieldname, address_of_what->location);
return build_struct_field_pointer(st, obj, address_of_what->data.structfield.fieldname, address_of_what->location);
}
case AST_EXPR_GET_FIELD:
{
// &obj.field aka &(obj.field), evaluate as &(&obj)->field
const LocalVariable *obj = build_address_of_expression(st, address_of_what->data.field.obj);
const LocalVariable *obj = build_address_of_expression(st, address_of_what->data.structfield.obj);
assert(obj->type->kind == TYPE_POINTER);
return build_struct_field_pointer(st, obj, address_of_what->data.field.fieldname, address_of_what->location);
return build_struct_field_pointer(st, obj, address_of_what->data.structfield.fieldname, address_of_what->location);
}
case AST_EXPR_INDEXING:
{
Expand Down Expand Up @@ -437,6 +456,14 @@ static const LocalVariable *build_struct_init(struct State *st, const Type *type
return instance;
}

static int find_enum_member(const Type *enumtype, const char *name)
{
for (int i = 0; i < enumtype->data.enummembers.count; i++)
if (!strcmp(enumtype->data.enummembers.names[i], name))
return i;
assert(0);
}

static const LocalVariable *build_expression(struct State *st, const AstExpression *expr)
{
const ExpressionTypes *types = get_expr_types(st, expr);
Expand All @@ -453,8 +480,16 @@ static const LocalVariable *build_expression(struct State *st, const AstExpressi
result = build_struct_init(st, types->type, &expr->data.call, expr->location);
break;
case AST_EXPR_GET_FIELD:
temp = build_expression(st, expr->data.field.obj);
result = build_struct_field(st, temp, expr->data.field.fieldname, expr->location);
temp = build_expression(st, expr->data.structfield.obj);
result = build_struct_field(st, temp, expr->data.structfield.fieldname, expr->location);
break;
case AST_EXPR_GET_ENUM_MEMBER:
result = add_local_var(st, types->type);
Constant c = { CONSTANT_ENUM_MEMBER, {
.enum_member.enumtype = types->type,
.enum_member.memberidx = find_enum_member(types->type, expr->data.enummember.membername),
}};
add_constant(st, expr->location, c, result);
break;
case AST_EXPR_GET_VARIABLE:
if ((temp = find_local_var(st, expr->data.varname))) {
Expand Down
12 changes: 11 additions & 1 deletion src/codegen.c
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ static LLVMTypeRef codegen_type(const Type *type)
free(elems);
return result;
}
case TYPE_ENUM:
return LLVMInt32Type();
}
assert(0);
}
Expand Down Expand Up @@ -152,6 +154,8 @@ static LLVMValueRef codegen_constant(const struct State *st, const Constant *c)
return LLVMConstNull(codegen_type(voidPtrType));
case CONSTANT_STRING:
return make_a_string_constant(st, c->data.str);
case CONSTANT_ENUM_MEMBER:
return LLVMConstInt(LLVMInt32Type(), c->data.enum_member.memberidx, false);
}
assert(0);
}
Expand Down Expand Up @@ -302,7 +306,13 @@ static void codegen_instruction(const struct State *st, const CfInstruction *ins

case CF_BOOL_NEGATE: setdest(LLVMBuildXor(st->builder, getop(0), LLVMConstInt(LLVMInt1Type(), 1, false), "bool_negate")); break;
case CF_PTR_CAST: setdest(LLVMBuildBitCast(st->builder, getop(0), codegen_type(ins->destvar->type), "ptr_cast")); break;
case CF_VARCPY: setdest(getop(0)); break;

// various no-ops
case CF_VARCPY:
case CF_INT32_TO_ENUM:
case CF_ENUM_TO_INT32:
setdest(getop(0));
break;

case CF_NUM_ADD: setdest(build_num_operation(st->builder, getop(0), getop(1), ins->operands[0]->type, LLVMBuildAdd, LLVMBuildAdd, LLVMBuildFAdd)); break;
case CF_NUM_SUB: setdest(build_num_operation(st->builder, getop(0), getop(1), ins->operands[0]->type, LLVMBuildSub, LLVMBuildSub, LLVMBuildFSub)); break;
Expand Down
12 changes: 8 additions & 4 deletions src/free.c
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,8 @@ static void free_expression(const AstExpression *expr)
break;
case AST_EXPR_GET_FIELD:
case AST_EXPR_DEREF_AND_GET_FIELD:
free_expression(expr->data.field.obj);
free(expr->data.field.obj);
free_expression(expr->data.structfield.obj);
free(expr->data.structfield.obj);
break;
case AST_EXPR_INDEXING:
case AST_EXPR_ADD:
Expand Down Expand Up @@ -114,6 +114,7 @@ static void free_expression(const AstExpression *expr)
free_constant(&expr->data.constant);
break;
case AST_EXPR_GET_VARIABLE:
case AST_EXPR_GET_ENUM_MEMBER:
break;
}
}
Expand Down Expand Up @@ -193,6 +194,9 @@ void free_ast(AstToplevelNode *topnodelist)
free_name_type_value(ntv);
free(t->data.structdef.fields.ptr);
break;
case AST_TOPLEVEL_DEFINE_ENUM:
free(t->data.enumdef.membernames);
break;
case AST_TOPLEVEL_IMPORT:
free(t->data.import.path);
break;
Expand Down Expand Up @@ -220,15 +224,15 @@ void free_type_context(const TypeContext *ctx)
{
for (GlobalVariable **g = ctx->globals.ptr; g < End(ctx->globals); g++)
free(*g);
for (Type **t = ctx->structs.ptr; t < End(ctx->structs); t++)
for (Type **t = ctx->owned_types.ptr; t < End(ctx->owned_types); t++)
free_type(*t);
for (Signature *s = ctx->function_signatures.ptr; s < End(ctx->function_signatures); s++)
free_signature(s);
free(ctx->expr_types.ptr);
free(ctx->globals.ptr);
free(ctx->locals.ptr);
free(ctx->types.ptr);
free(ctx->structs.ptr);
free(ctx->owned_types.ptr);
free(ctx->function_signatures.ptr);
}

Expand Down
26 changes: 23 additions & 3 deletions src/jou_compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ typedef struct AstStatement AstStatement;
typedef struct AstToplevelNode AstToplevelNode;
typedef struct AstFunctionDef AstFunctionDef;
typedef struct AstStructDef AstStructDef;
typedef struct AstEnumDef AstEnumDef;
typedef struct AstImport AstImport;

typedef struct GlobalVariable GlobalVariable;
Expand Down Expand Up @@ -95,6 +96,7 @@ struct Token {
// Constants can appear in AST and also compilation steps after AST.
struct Constant {
enum ConstantKind {
CONSTANT_ENUM_MEMBER,
CONSTANT_INTEGER,
CONSTANT_FLOAT,
CONSTANT_DOUBLE,
Expand All @@ -107,6 +109,7 @@ struct Constant {
char *str;
char double_or_float_text[100]; // convenient because LLVM wants a string anyway
bool boolean;
struct { const Type *enumtype; int memberidx; } enum_member;
} data;
};
#define copy_constant(c) ( (c)->kind==CONSTANT_STRING ? (Constant){ CONSTANT_STRING, {.str=strdup((c)->data.str)} } : *(c) )
Expand Down Expand Up @@ -162,6 +165,7 @@ struct AstExpression {

enum AstExpressionKind {
AST_EXPR_CONSTANT,
AST_EXPR_GET_ENUM_MEMBER, // Cannot be just a Constant because ast doesn't know about Types.
AST_EXPR_FUNCTION_CALL,
AST_EXPR_BRACE_INIT,
AST_EXPR_GET_FIELD, // foo.bar
Expand Down Expand Up @@ -198,7 +202,8 @@ struct AstExpression {
Constant constant; // AST_EXPR_CONSTANT
char varname[100]; // AST_EXPR_GET_VARIABLE
AstCall call; // AST_EXPR_CALL, AST_EXPR_INSTANTIATE
struct { AstExpression *obj; char fieldname[100]; } field; // AST_EXPR_GET_FIELD, AST_EXPR_DEREF_AND_GET_FIELD
struct { AstExpression *obj; char fieldname[100]; } structfield; // AST_EXPR_GET_FIELD, AST_EXPR_DEREF_AND_GET_FIELD
struct { char enumname[100]; char membername[100]; } enummember;
struct { AstExpression *obj; AstType type; } as;
/*
The "operands" pointer is an array of 1 to 2 expressions.
Expand Down Expand Up @@ -289,6 +294,12 @@ struct AstStructDef {
List(AstNameTypeValue) fields;
};

struct AstEnumDef {
char name[100];
char (*membernames)[100];
int nmembers;
};

struct AstImport {
char *path; // Relative to current working directory, so e.g. "blah/stdlib/io.jou"
char symbolname[100];
Expand All @@ -305,12 +316,14 @@ struct AstToplevelNode {
AST_TOPLEVEL_DEFINE_FUNCTION,
AST_TOPLEVEL_DEFINE_GLOBAL_VARIABLE,
AST_TOPLEVEL_DEFINE_STRUCT,
AST_TOPLEVEL_DEFINE_ENUM,
AST_TOPLEVEL_IMPORT,
} kind;
union {
AstNameTypeValue globalvar; // AST_TOPLEVEL_DECLARE_GLOBAL_VARIABLE
AstFunctionDef funcdef; // AST_TOPLEVEL_DECLARE_FUNCTION, AST_TOPLEVEL_DEFINE_FUNCTION (body is empty for declaring)
AstStructDef structdef; // AST_TOPLEVEL_DEFINE_STRUCT
AstEnumDef enumdef; // AST_TOPLEVEL_DEFINE_ENUM
AstImport import; // AST_TOPLEVEL_IMPORT
} data;
};
Expand All @@ -328,12 +341,14 @@ struct Type {
TYPE_ARRAY,
TYPE_STRUCT,
TYPE_OPAQUE_STRUCT, // struct with unknown members
TYPE_ENUM,
} kind;
union {
int width_in_bits; // TYPE_SIGNED_INTEGER, TYPE_UNSIGNED_INTEGER, TYPE_FLOATING_POINT
const Type *valuetype; // TYPE_POINTER
struct { const Type *membertype; int len; } array; // TYPE_ARRAY
struct { int count; char (*names)[100]; const Type **types; } structfields; // TYPE_STRUCT
struct { int count; char (*names)[100]; } enummembers;
} data;
};

Expand Down Expand Up @@ -364,6 +379,7 @@ const Type *get_pointer_type(const Type *t); // result lives as long as t
const Type *get_array_type(const Type *t, int len); // result lives as long as t
const Type *type_of_constant(const Constant *c);
Type *create_opaque_struct(const char *name);
Type *create_enum(const char *name, int membercount, char (*membernames)[100]);
void set_struct_fields(
Type *structtype, // must be opaque struct, becomes non-opaque
int fieldcount,
Expand Down Expand Up @@ -423,15 +439,17 @@ struct TypeContext {
List(ExpressionTypes *) expr_types;
List(GlobalVariable *) globals; // TODO: probably doesn't need to has pointers
List(LocalVariable *) locals;
List(Type *) structs; // These will be freed later
List(Type *) owned_types; // These will be freed later
List(const Type *) types;
List(Signature) function_signatures;
};

/*
Type checking is split into several stages:
1. Create types. After this, structs defined in Jou exist, but
they are opaque and contain no members.
they are opaque and contain no members. Enums exist and contain
their members (although it doesn't really matter whether enum
members are handled in step 1 or 2).
2. Check signatures, global variables and struct bodies. This step
assumes that all types exist, but doesn't need to know what
fields each struct has.
Expand Down Expand Up @@ -489,6 +507,8 @@ struct CfInstruction {
CF_NUM_EQ,
CF_NUM_LT,
CF_NUM_CAST,
CF_ENUM_TO_INT32,
CF_INT32_TO_ENUM,
CF_BOOL_NEGATE, // TODO: get rid of this?
CF_VARCPY, // similar to assignment statements: var1 = var2
} kind;
Expand Down
2 changes: 2 additions & 0 deletions src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,8 @@ static bool astnode_conflicts_with_an_import(const AstToplevelNode *astnode, con
return import->kind == EXPSYM_GLOBAL_VAR && !strcmp(import->name, astnode->data.globalvar.name);
case AST_TOPLEVEL_DEFINE_STRUCT:
return import->kind == EXPSYM_TYPE && !strcmp(import->name, astnode->data.structdef.name);
case AST_TOPLEVEL_DEFINE_ENUM:
return import->kind == EXPSYM_TYPE && !strcmp(import->name, astnode->data.enumdef.name);
case AST_TOPLEVEL_IMPORT:
return false;
case AST_TOPLEVEL_END_OF_FILE:
Expand Down
46 changes: 43 additions & 3 deletions src/parse.c
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,11 @@ static AstExpression parse_elementary_expression(const Token **tokens)
} else if (is_operator(&(*tokens)[1], "{")) {
expr.kind = AST_EXPR_BRACE_INIT;
expr.data.call = parse_call(tokens, '{', '}', true);
} else if (is_operator(&(*tokens)[1], "::") && (*tokens)[2].type == TOKEN_NAME) {
expr.kind = AST_EXPR_GET_ENUM_MEMBER;
safe_strcpy(expr.data.enummember.enumname, (*tokens)[0].data.name);
safe_strcpy(expr.data.enummember.membername, (*tokens)[2].data.name);
*tokens += 3;
} else {
expr.kind = AST_EXPR_GET_VARIABLE;
safe_strcpy(expr.data.varname, (*tokens)->data.name);
Expand Down Expand Up @@ -422,12 +427,12 @@ static AstExpression parse_expression_with_fields_and_indexing(const Token **tok
.location = startop->location,
.kind = (is_operator(startop, "->") ? AST_EXPR_DEREF_AND_GET_FIELD : AST_EXPR_GET_FIELD),
};
result2.data.field.obj = malloc(sizeof *result2.data.field.obj);
*result2.data.field.obj = result;
result2.data.structfield.obj = malloc(sizeof *result2.data.structfield.obj);
*result2.data.structfield.obj = result;

if ((*tokens)->type != TOKEN_NAME)
fail_with_parse_error(*tokens, "a field name");
safe_strcpy(result2.data.field.fieldname, (*tokens)->data.name);
safe_strcpy(result2.data.structfield.fieldname, (*tokens)->data.name);
++*tokens;

result = result2;
Expand Down Expand Up @@ -767,6 +772,37 @@ static AstStructDef parse_structdef(const Token **tokens)
return result;
}

static AstEnumDef parse_enumdef(const Token **tokens)
{
AstEnumDef result = {0};
if ((*tokens)->type != TOKEN_NAME)
fail_with_parse_error(*tokens, "a name for the enum");
safe_strcpy(result.name, (*tokens)->data.name);
++*tokens;

parse_start_of_body(tokens);
List(const char*) membernames = {0};

while ((*tokens)->type != TOKEN_DEDENT) {
for (const char **old = membernames.ptr; old < End(membernames); old++)
if (!strcmp(*old, (*tokens)->data.name))
fail_with_error((*tokens)->location, "the enum has two members named '%s'", (*tokens)->data.name);

Append(&membernames, (*tokens)->data.name);
++*tokens;
eat_newline(tokens);
}

result.nmembers = membernames.len;
result.membernames = malloc(sizeof(result.membernames[0]) * result.nmembers);
for (int i = 0; i < result.nmembers; i++)
strcpy(result.membernames[i], membernames.ptr[i]);

free(membernames.ptr);
++*tokens;
return result;
}

static char *get_actual_import_path(const Token *pathtoken, const char *stdlib_path)
{
if (pathtoken->type != TOKEN_STRING)
Expand Down Expand Up @@ -890,6 +926,10 @@ static AstToplevelNode parse_toplevel_node(const Token **tokens)
++*tokens;
result.kind = AST_TOPLEVEL_DEFINE_STRUCT;
result.data.structdef = parse_structdef(tokens);
} else if (is_keyword(*tokens, "enum")) {
++*tokens;
result.kind = AST_TOPLEVEL_DEFINE_ENUM;
result.data.enumdef = parse_enumdef(tokens);
} else {
fail_with_parse_error(*tokens, "a definition or declaration");
}
Expand Down
Loading

0 comments on commit ef849db

Please sign in to comment.