diff --git a/src/lib/builtins/basic.c b/src/lib/builtins/basic.c index dae12d7..de0115b 100644 --- a/src/lib/builtins/basic.c +++ b/src/lib/builtins/basic.c @@ -331,6 +331,7 @@ void bins_basic_init(StaticMapSlot slots[]) slots[9].as_type = Object_GetDirType(); slots[10].as_type = Object_GetNullableType(); slots[11].as_type = Object_GetSumType(); + slots[12].as_object = Object_NewAny(); } StaticMapSlot bins_basic[] = { @@ -346,7 +347,7 @@ StaticMapSlot bins_basic[] = { { TYPENAME_DIRECTORY, SM_TYPE, .as_type = NULL /* Until bins_basic_init is called */ }, { TYPENAME_NULLABLE, SM_TYPE, .as_type = NULL }, { TYPENAME_SUM, SM_TYPE, .as_type = NULL }, - + { "any", SM_OBJECT, .as_object = NULL }, { "math", SM_SMAP, .as_smap = bins_math, }, { "files", SM_SMAP, .as_smap = bins_files, }, { "buffer", SM_SMAP, .as_smap = bins_buffer, }, diff --git a/src/lib/common/defs.h b/src/lib/common/defs.h index d8407cb..bb726f7 100644 --- a/src/lib/common/defs.h +++ b/src/lib/common/defs.h @@ -15,4 +15,5 @@ #define TYPENAME_DIRECTORY "Directory" #define TYPENAME_NULLABLE "NullableType" -#define TYPENAME_SUM "SumType" \ No newline at end of file +#define TYPENAME_SUM "SumType" +#define TYPENAME_ANY "Any" \ No newline at end of file diff --git a/src/lib/common/executable.c b/src/lib/common/executable.c index 830d8f7..cebb6fe 100644 --- a/src/lib/common/executable.c +++ b/src/lib/common/executable.c @@ -86,6 +86,7 @@ static const InstrInfo instr_table[] = { [OPCODE_STP] = {"STP", 0, NULL}, [OPCODE_ASS] = {"ASS", 1, (OperandType[]) {OPTP_STRING}}, [OPCODE_POP] = {"POP", 1, (OperandType[]) {OPTP_INT}}, + [OPCODE_CHECKTYPE] = {"CHECKTYPE", 2, (OperandType[]) {OPTP_INT, OPTP_STRING}}, [OPCODE_CALL] = {"CALL", 2, (OperandType[]) {OPTP_INT, OPTP_INT}}, [OPCODE_SELECT] = {"SELECT", 0, NULL}, [OPCODE_INSERT] = {"INSERT", 0, NULL}, diff --git a/src/lib/common/executable.h b/src/lib/common/executable.h index 2fe24e2..b8bbd22 100644 --- a/src/lib/common/executable.h +++ b/src/lib/common/executable.h @@ -94,6 +94,7 @@ typedef enum { OPCODE_JUMPIFANDPOP, OPCODE_JUMPIFNOTANDPOP, OPCODE_JUMP, + OPCODE_CHECKTYPE, } Opcode; typedef struct xExecutable Executable; diff --git a/src/lib/compiler/ASTi.h b/src/lib/compiler/ASTi.h index aedcd6d..3174566 100644 --- a/src/lib/compiler/ASTi.h +++ b/src/lib/compiler/ASTi.h @@ -195,8 +195,7 @@ typedef struct { typedef struct { Node base; char *name; - int typec; - Node *typev; + Node *type; Node *value; } ArgumentNode; #endif \ No newline at end of file diff --git a/src/lib/compiler/codegen.c b/src/lib/compiler/codegen.c index 240c39e..1c18c6f 100644 --- a/src/lib/compiler/codegen.c +++ b/src/lib/compiler/codegen.c @@ -143,6 +143,15 @@ static void emitInstr_ERROR(CodegenContext *ctx, const char *msg, int off, int l CodegenContext_EmitInstr(ctx, OPCODE_ERROR, opv, 1, off, len); } +static void emitInstr_CHECKTYPE(CodegenContext *ctx, int arg_index, const char *arg_name, int off, int len) +{ + Operand opv[2] = { + { .type = OPTP_INT, .as_int = arg_index }, + { .type = OPTP_STRING, .as_string = arg_name }, + }; + CodegenContext_EmitInstr(ctx, OPCODE_CHECKTYPE, opv, 2, off, len); +} + static void emitInstr_PUSHTRU(CodegenContext *ctx, int off, int len) { CodegenContext_EmitInstr(ctx, OPCODE_PUSHTRU, NULL, 0, off, len); @@ -216,64 +225,11 @@ static void emitInstrForFuncCallNode(CodegenContext *ctx, CallExprNode *expr, CodegenContext_EmitInstr(ctx, OPCODE_CALL, ops, 2, expr->base.base.offset, expr->base.base.length); } +static void emitInstrForExprNode(CodegenContext *ctx, ExprNode *expr, + Label *label_break); + static void emitInstrForArgumentNode(CodegenContext *ctx, ArgumentNode *arg, int argidx) { - /* - * // If a default value for the argument was specified - * PUSHTYP; - * PUSHNNETYP; - * EQL; - * JUMPIFNOTANDPOP default_handled; - * POP 1; - * - * default_handled: - * // Push the type of the argument - * PUSHTYP; - * - * // Evaluate the type annotation and make - * // sure that it evaluated to a type. - * - * PUSHTYP; - * PUSHTYPTYP; - * EQL; - * JUMPIFANDPOP argument_annotation_0_not_type; - * - * // If the annotated type and the argument's - * // type match, jump to the argument assignment. - * EQL; - * JUMPIFANDPOP argument_type_ok; - * - * // To the same for the next annotation. - * - * PUSHTYP; - * - * PUSHTYP; - * PUSHTYPTYP; - * EQL; - * JUMPIFANDPOP argument_annotation_1_not_type; - * EQL; - * JUMPIFANDPOP argument_type_ok; - * - * PUSHTYP; - * - * PUSHTYP; - * PUSHTYPTYP; - * EQL; - * JUMPIFANDPOP argument_annotation_2_not_type; - * EQL; - * JUMPIFANDPOP argument_type_ok; - * - * ERROR "Bad type of argument N"; - * argument_annotation_0_not_a_type: - * ERROR "Argument N annotation M isn't a type"; - * argument_type_ok: - * - * // At this point we know that the argument has - * // a valid type. - * ASS ; - * POP 1; - */ - if(arg->value != NULL) { /* Emit bytecode for the default argument */ Label *label_default_handled = Label_New(ctx); @@ -287,61 +243,11 @@ static void emitInstrForArgumentNode(CodegenContext *ctx, ArgumentNode *arg, int Label_Free(label_default_handled); } - if(arg->typev != NULL) { - - /* Emit checks for the argument type */ - - Node *typev = arg->typev; - int typec = arg->typec; - assert(typec > 0); - - Label *maybe[8]; - Label **label_annotation_not_a_type; - if((size_t) typec > sizeof(maybe)/sizeof(maybe[0])) { - label_annotation_not_a_type = malloc(typec * sizeof(Label*)); - if(label_annotation_not_a_type == NULL) - CodegenContext_ReportErrorAndJump(ctx, 1, "No memory"); - } else - label_annotation_not_a_type = maybe; - - for(int i = 0; i < typec; i += 1) - label_annotation_not_a_type[i] = Label_New(ctx); - - Label *label_argument_type_ok = Label_New(ctx); - - Node *type = typev; - for(int i = 0; i < typec; i += 1) { - assert(type != NULL); - emitInstr_PUSHTYP(ctx, arg->base.offset, arg->base.length); // The source slice should refer only to the name label, not the whole operand - emitInstrForNode(ctx, type, NULL); - { - emitInstr_PUSHTYP(ctx, type->offset, type->length); - emitInstr_PUSHTYPTYP(ctx, type->offset, type->length); - emitInstr_EQL(ctx, type->offset, type->length); - emitInstr_JUMPIFNOTANDPOP(ctx, label_annotation_not_a_type[i], type->offset, type->length); - } - emitInstr_EQL(ctx, type->offset, type->length); - emitInstr_JUMPIFANDPOP_2(ctx, label_argument_type_ok, type->offset, type->length); - type = type->next; - } - - char msg[256]; - snprintf(msg, sizeof(msg), "Bad type for argument %d", argidx); - emitInstr_ERROR(ctx, msg, arg->base.offset, arg->base.length); - for(int i = 0; i < typec; i += 1) { - Label_SetHere(label_annotation_not_a_type[i], ctx); - snprintf(msg, sizeof(msg), "Argument %d type annotation %d is not a type", argidx, i); - emitInstr_ERROR(ctx, msg, arg->base.offset, arg->base.length); - } - - Label_SetHere(label_argument_type_ok, ctx); - - Label_Free(label_argument_type_ok); - for(int i = 0; i < typec; i += 1) - Label_Free(label_annotation_not_a_type[i]); - if(label_annotation_not_a_type != maybe) - free(label_annotation_not_a_type); + if (arg->type != NULL) { + emitInstrForNode(ctx, arg->type, NULL); + emitInstr_CHECKTYPE(ctx, argidx, arg->name, arg->type->offset, arg->type->length); } + emitInstr_ASS(ctx, arg->name, arg->base.offset, arg->base.length); emitInstr_POP1(ctx, arg->base.offset, arg->base.length); } diff --git a/src/lib/compiler/parse.c b/src/lib/compiler/parse.c index 6a2d623..1607f3f 100644 --- a/src/lib/compiler/parse.c +++ b/src/lib/compiler/parse.c @@ -1919,29 +1919,14 @@ static _Bool parse_function_arguments(Context *ctx, int *argc_, Node **argv_) return 0; } - int typec; - Node *typev = NULL; - + Node *type; if(next(ctx) == ':') { next(ctx); // Skip the ':'. - - Node *type = parse_expression(ctx, 0, 0); + type = parse_expression(ctx, 0, 0); if(type == NULL) return 0; - typev = type; - typec = 1; - Node *tail = type; - while(current(ctx) == '|') { - next(ctx); - type = parse_expression(ctx, 0, 0); - if(type == NULL) - return 0; - tail->next = type; - tail = type; - typec += 1; - } } else - typec = 0; + type = NULL; Node *defarg; // Default argument (or NULL if there isn't one) if(current(ctx) == '=') { @@ -1968,8 +1953,7 @@ static _Bool parse_function_arguments(Context *ctx, int *argc_, Node **argv_) arg->base.offset = current_token(ctx)->offset; arg->base.length = current_token(ctx)->length; arg->name = arg_name; - arg->typev = typev; - arg->typec = typec; + arg->type = type; arg->value = defarg; } diff --git a/src/lib/objects/o_any.c b/src/lib/objects/o_any.c new file mode 100644 index 0000000..6a25f7e --- /dev/null +++ b/src/lib/objects/o_any.c @@ -0,0 +1,56 @@ +#include "../common/defs.h" +#include "../utils/defs.h" +#include "objects.h" + +static bool istypeof(Object *self, Object *other, Heap *heap, Error *error); + +static TypeObject t_any = { + .base = (Object) { .type = &t_type, .flags = Object_STATIC }, + .name = TYPENAME_ANY, + .size = sizeof(Object), + .init = NULL, + .free = NULL, + .hash = NULL, + .copy = NULL, + .call = NULL, + .print = NULL, + + .select = NULL, + .delete = NULL, + .insert = NULL, + .count = NULL, + + .istypeof = istypeof, + + .op_eql = NULL, + .walk = NULL, + .walkexts = NULL, +}; + +static Object the_any_object = { + .type = &t_any, + .flags = Object_STATIC, +}; + +TypeObject *Object_GetAnyType() +{ + return &t_any; +} + +Object *Object_NewAny() +{ + return &the_any_object; +} + +static bool +istypeof(Object *self, + Object *other, + Heap *heap, + Error *error) +{ + UNUSED(self); + UNUSED(other); + UNUSED(heap); + UNUSED(error); + return true; +} diff --git a/src/lib/objects/o_nullable.c b/src/lib/objects/o_nullable.c index 1bd6853..bf9df4e 100644 --- a/src/lib/objects/o_nullable.c +++ b/src/lib/objects/o_nullable.c @@ -2,6 +2,7 @@ #include "objects.h" static void walk(Object *self, void (*callback)(Object **referer, void *userp), void *userp); +static void print(Object *obj, FILE *fp); static bool istypeof(Object *self, Object *other, Heap *heap, Error *error); typedef struct { @@ -18,7 +19,7 @@ static TypeObject t_nullable = { .hash = NULL, .copy = NULL, .call = NULL, - .print = NULL, + .print = print, .select = NULL, .delete = NULL, @@ -62,3 +63,11 @@ static void walk(Object *self, void (*callback)(Object **referer, void *userp), NullableObject *nullable = (NullableObject*) self; callback(&nullable->item, userp); } + +static void print(Object *obj, FILE *fp) +{ + NullableObject *nullable = (NullableObject*) obj; + + fprintf(fp, "?"); + Object_Print(nullable->item, fp); +} \ No newline at end of file diff --git a/src/lib/objects/objects.c b/src/lib/objects/objects.c index 958c73b..2c77dfc 100644 --- a/src/lib/objects/objects.c +++ b/src/lib/objects/objects.c @@ -34,15 +34,23 @@ static _Bool op_eql(Object *self, Object *other); static bool istypeof(Object *self, Object *other, Heap *heap, Error *error); +static void print(Object *self, FILE *fp); TypeObject t_type = { .base = (Object) { .type = &t_type, .flags = Object_STATIC }, .name = TYPENAME_TYPE, .size = sizeof(TypeObject), + .print = print, .istypeof = istypeof, .op_eql = op_eql, }; +static void print(Object *self, FILE *fp) +{ + ASSERT(self->type == &t_type); + fprintf(fp, "%s", ((TypeObject*) self)->name); +} + static bool istypeof(Object *self, Object *other, Heap *heap, Error *error) { UNUSED(heap); @@ -80,10 +88,7 @@ const TypeObject *Object_GetType(const Object *obj) return obj->type; } -bool Object_IsTypeOf(Object *typ, - Object *obj, - Heap *heap, - Error *error) +bool Object_IsTypeOf(Object *typ, Object *obj, Heap *heap, Error *error) { if (typ->type->istypeof == NULL) return false; @@ -135,13 +140,22 @@ void Object_Print(Object *obj, FILE *fp) { ASSERT(obj != NULL); - const TypeObject *type = Object_GetType(obj); - ASSERT(type); + if (obj->flags & Object_PRINT) { + fprintf(fp, "..."); + } else { + + obj->flags |= Object_PRINT; - if(type->print == NULL) - fprintf(fp, "<%s is unprintable>", Object_GetName(obj)); - else - type->print(obj, fp); + const TypeObject *type = Object_GetType(obj); + ASSERT(type); + + if(type->print == NULL) + fprintf(fp, "<%s is unprintable>", Object_GetName(obj)); + else + type->print(obj, fp); + + obj->flags &= ~Object_PRINT; + } } Object *Object_Select(Object *coll, Object *key, Heap *heap, Error *err) @@ -275,4 +289,4 @@ void Object_WalkExtensions(Object *parent, void (*callback)(void **referer, unsi ASSERT(parent != NULL); if(parent->type->walkexts != NULL) parent->type->walkexts(parent, callback, userp); -} +} \ No newline at end of file diff --git a/src/lib/objects/objects.h b/src/lib/objects/objects.h index 1012841..45d3844 100644 --- a/src/lib/objects/objects.h +++ b/src/lib/objects/objects.h @@ -81,8 +81,9 @@ struct TypeObject { }; enum { - Object_STATIC = 1, - Object_MOVED = 2, + Object_STATIC = 1 << 0, + Object_MOVED = 1 << 1, + Object_PRINT = 1 << 2, }; Heap* Heap_New(int size); @@ -120,6 +121,7 @@ Object* Object_NewClosure(Object *parent, Object *new_map, Heap *heap, Error * Object* Object_SliceBuffer(Object *obj, size_t offset, size_t length, Heap *heap, Error *error); Object* Object_NewNullable(Object *item, Heap *heap, Error *error); Object* Object_NewSum(Object *item0, Object *item1, Heap *heap, Error *error); +Object* Object_NewAny(); Object* Object_FromInt (long long int val, Heap *heap, Error *error); Object* Object_FromBool (bool val, Heap *heap, Error *error); @@ -141,6 +143,7 @@ TypeObject *Object_GetFileType(); TypeObject *Object_GetDirType(); TypeObject *Object_GetNullableType(); TypeObject *Object_GetSumType(); +TypeObject *Object_GetAnyType(); bool Object_IsNone(Object *obj); bool Object_IsInt(Object *obj); diff --git a/src/lib/runtime/o_staticmap.c b/src/lib/runtime/o_staticmap.c index f90bee0..0f79d35 100644 --- a/src/lib/runtime/o_staticmap.c +++ b/src/lib/runtime/o_staticmap.c @@ -68,6 +68,7 @@ static TypeObject t_staticmap = { .copy = copy, .hash = hash, .select = select_, +#warning "Need to walk the references" }; static Object *copy(Object *self, Heap *heap, Error *err) @@ -137,6 +138,7 @@ static Object *select_(Object *self, Object *key, Heap *heap, Error *error) case SM_SMAP: return Object_NewStaticMap(slot.as_smap, NULL, map->runt, error); case SM_NONE: return Object_NewNone(heap, error); case SM_TYPE: return (Object*) slot.as_type; + case SM_OBJECT: return slot.as_object; default: assert(0); break; } return obj; diff --git a/src/lib/runtime/runtime.c b/src/lib/runtime/runtime.c index 4257e78..d4dba55 100644 --- a/src/lib/runtime/runtime.c +++ b/src/lib/runtime/runtime.c @@ -826,6 +826,50 @@ static _Bool step(Runtime *runtime, Error *error) return 1; } + case OPCODE_CHECKTYPE: + { + ASSERT(opc == 2); + ASSERT(ops[0].type == OPTP_INT); + ASSERT(ops[1].type == OPTP_STRING); + + const char *arg_name; + int arg_index; + + arg_index = ops[0].as_int; + arg_name = ops[1].as_string; + + if(runtime->frame->used < 2) + { + Error_Report(error, 1, "Frame doesn't own enough objects to execute CHECKTYPE"); + return 0; + } + + Object *typ = Stack_Top(runtime->stack, 0); + Object *arg = Stack_Top(runtime->stack, -1); + ASSERT(typ != NULL); + ASSERT(arg != NULL); + + // Pop type + if(!Runtime_Pop(runtime, error, 1)) + return 0; + + if (!Object_IsTypeOf(typ, arg, runtime->heap, error)) { + char provided[512]; + char allowed[512]; + FILE *provided_fp = fmemopen(provided, sizeof(provided), "wb"); + FILE *allowed_fp = fmemopen(allowed, sizeof(allowed), "wb"); + // TODO: Check for errors from [fmemopen] + Object_Print(typ, allowed_fp); + Object_Print(arg, provided_fp); + fclose(allowed_fp); + fclose(provided_fp); + Error_Report(error, 0, "Argument %d \"%s\" has an unallowed type. Was expected something with type %s but was provided %s", + arg_index+1, arg_name, allowed, provided); + return 0; + } + return 1; + } + case OPCODE_CALL: { ASSERT(opc == 2); diff --git a/src/lib/runtime/runtime.h b/src/lib/runtime/runtime.h index f65ca45..8294215 100644 --- a/src/lib/runtime/runtime.h +++ b/src/lib/runtime/runtime.h @@ -68,6 +68,7 @@ typedef enum { SM_SMAP, SM_NONE, SM_TYPE, + SM_OBJECT, } StaticMapSlotKind; typedef struct StaticMapSlot StaticMapSlot; @@ -83,6 +84,7 @@ struct StaticMapSlot { double as_float; int (*as_funct)(Runtime*, Object**, unsigned int, Object*[static MAX_RETS], Error*); TypeObject *as_type; + Object *as_object; }; union { int argc; int length; }; }; diff --git a/src/lib/utils/error.h b/src/lib/utils/error.h index 61eb07a..e86981c 100644 --- a/src/lib/utils/error.h +++ b/src/lib/utils/error.h @@ -40,7 +40,7 @@ struct Error { truncated; int length; char* message; - char message2[256]; + char message2[512]; const char *file, *func; int line;