diff --git a/.gitignore b/.gitignore index 3b554e7..0c13b8e 100644 --- a/.gitignore +++ b/.gitignore @@ -9,4 +9,6 @@ test vgcore.* *.gcov -lib*.a \ No newline at end of file +lib*.a + +*.noja \ No newline at end of file diff --git a/src/lib/builtins/basic.c b/src/lib/builtins/basic.c index a6ae85a..a9259fe 100644 --- a/src/lib/builtins/basic.c +++ b/src/lib/builtins/basic.c @@ -299,6 +299,25 @@ static int bin_error(Runtime *runtime, Object **argv, unsigned int argc, Object return -1; } +static int bin_istypeof(Runtime *runtime, Object **argv, unsigned int argc, Object *rets[static MAX_RETS], Error *error) +{ + UNUSED(argc); + UNUSED(rets); + UNUSED(runtime); + + ASSERT(argc == 2); + + bool yes = Object_IsTypeOf(argv[0], argv[1]); + + Heap *heap = Runtime_GetHeap(runtime); + Object *o_yes = Object_FromBool(yes, heap, error); + if (o_yes == NULL) + return -1; + + rets[0] = o_yes; + return 1; +} + void bins_basic_init(StaticMapSlot slots[]) { slots[0].as_type = Object_GetTypeType(); @@ -311,6 +330,7 @@ void bins_basic_init(StaticMapSlot slots[]) slots[7].as_type = Object_GetMapType(); slots[8].as_type = Object_GetFileType(); slots[9].as_type = Object_GetDirType(); + slots[10].as_type = Object_GetNullableType(); } StaticMapSlot bins_basic[] = { @@ -324,6 +344,7 @@ StaticMapSlot bins_basic[] = { { TYPENAME_MAP, SM_TYPE, .as_type = NULL /* Until bins_basic_init is called */ }, { TYPENAME_FILE, SM_TYPE, .as_type = NULL /* Until bins_basic_init is called */ }, { TYPENAME_DIRECTORY, SM_TYPE, .as_type = NULL /* Until bins_basic_init is called */ }, + { TYPENAME_NULLABLE, SM_TYPE, .as_type = NULL }, { "math", SM_SMAP, .as_smap = bins_math, }, { "files", SM_SMAP, .as_smap = bins_files, }, @@ -331,11 +352,12 @@ StaticMapSlot bins_basic[] = { { "string", SM_SMAP, .as_smap = bins_string, }, { "import", SM_FUNCT, .as_funct = bin_import, .argc = 1, }, - { "type", SM_FUNCT, .as_funct = bin_type, .argc = 1 }, - { "print", SM_FUNCT, .as_funct = bin_print, .argc = -1 }, - { "input", SM_FUNCT, .as_funct = bin_input, .argc = 0 }, - { "count", SM_FUNCT, .as_funct = bin_count, .argc = 1 }, - { "error", SM_FUNCT, .as_funct = bin_error, .argc = 1 }, + { "type", SM_FUNCT, .as_funct = bin_type, .argc = 1 }, + { "print", SM_FUNCT, .as_funct = bin_print, .argc = -1 }, + { "input", SM_FUNCT, .as_funct = bin_input, .argc = 0 }, + { "count", SM_FUNCT, .as_funct = bin_count, .argc = 1 }, + { "error", SM_FUNCT, .as_funct = bin_error, .argc = 1 }, { "assert", SM_FUNCT, .as_funct = bin_assert, .argc = -1 }, + { "istypeof", SM_FUNCT, .as_funct = bin_istypeof, .argc = 2, }, { NULL, SM_END, {}, {} }, }; diff --git a/src/lib/common/defs.h b/src/lib/common/defs.h index 5a5fa99..1af4e87 100644 --- a/src/lib/common/defs.h +++ b/src/lib/common/defs.h @@ -12,4 +12,6 @@ #define TYPENAME_BUFFER "Buffer" #define TYPENAME_FILE "File" -#define TYPENAME_DIRECTORY "Directory" \ No newline at end of file +#define TYPENAME_DIRECTORY "Directory" + +#define TYPENAME_NULLABLE "NullableType" \ No newline at end of file diff --git a/src/lib/common/executable.c b/src/lib/common/executable.c index bed2561..397945b 100644 --- a/src/lib/common/executable.c +++ b/src/lib/common/executable.c @@ -82,6 +82,7 @@ static const InstrInfo instr_table[] = { [OPCODE_GRT] = {"GRT", 0, NULL}, [OPCODE_LEQ] = {"LEQ", 0, NULL}, [OPCODE_GEQ] = {"GEQ", 0, NULL}, + [OPCODE_NLB] = {"NLB", 0, NULL}, [OPCODE_ASS] = {"ASS", 1, (OperandType[]) {OPTP_STRING}}, [OPCODE_POP] = {"POP", 1, (OperandType[]) {OPTP_INT}}, [OPCODE_CALL] = {"CALL", 2, (OperandType[]) {OPTP_INT, OPTP_INT}}, diff --git a/src/lib/common/executable.h b/src/lib/common/executable.h index 600dd26..3b79738 100644 --- a/src/lib/common/executable.h +++ b/src/lib/common/executable.h @@ -67,6 +67,7 @@ typedef enum { OPCODE_GRT, OPCODE_LEQ, OPCODE_GEQ, + OPCODE_NLB, OPCODE_ASS, OPCODE_POP, OPCODE_CALL, diff --git a/src/lib/compiler/ASTi.h b/src/lib/compiler/ASTi.h index 42bfb38..aedcd6d 100644 --- a/src/lib/compiler/ASTi.h +++ b/src/lib/compiler/ASTi.h @@ -77,6 +77,9 @@ typedef enum { EXPR_STRING, EXPR_IDENT, EXPR_SELECT, + + EXPR_NULLABLETYPE, + EXPR_SUMTYPE, } ExprKind; typedef struct Node Node; diff --git a/src/lib/compiler/codegen.c b/src/lib/compiler/codegen.c index 57fdc7f..e4885c6 100644 --- a/src/lib/compiler/codegen.c +++ b/src/lib/compiler/codegen.c @@ -174,6 +174,7 @@ static Opcode exprkind_to_opcode(ExprKind kind) { switch(kind) { + case EXPR_NULLABLETYPE: return OPCODE_NLB; case EXPR_NOT: return OPCODE_NOT; case EXPR_POS: return OPCODE_POS; case EXPR_NEG: return OPCODE_NEG; @@ -475,10 +476,13 @@ static void emitInstrForExprNode(CodegenContext *ctx, ExprNode *expr, switch(expr->kind) { case EXPR_PAIR: - CodegenContext_ReportErrorAndJump(ctx, 0, "Tuple outside of assignment or return statement"); + CodegenContext_ReportErrorAndJump( + ctx, 0, "Tuple outside of " + "assignment or return statement"); UNREACHABLE; return; // For the compiler warning. + case EXPR_NULLABLETYPE: case EXPR_NOT: case EXPR_POS: case EXPR_NEG: case EXPR_ADD: case EXPR_SUB: diff --git a/src/lib/compiler/parse.c b/src/lib/compiler/parse.c index 45bd649..0456b33 100644 --- a/src/lib/compiler/parse.c +++ b/src/lib/compiler/parse.c @@ -63,6 +63,7 @@ typedef enum { + TOPT = '?', TPOS = '+', TNEG = '-', @@ -556,6 +557,7 @@ static Node *parse_statement(Context *ctx) case '[': case '+': case '-': + case '?': case TINT: case TFLOAT: case TSTRING: @@ -1544,6 +1546,7 @@ static Node *parse_prefix_expression(Context *ctx) case '+': case '-': + case '?': case TKWNOT: { Token *unary_operator = current_token(ctx); @@ -1569,6 +1572,7 @@ static Node *parse_prefix_expression(Context *ctx) switch(unary_operator->kind) { + case '?': temp->base.kind = EXPR_NULLABLETYPE; break; case '+': temp->base.kind = EXPR_POS; break; case '-': temp->base.kind = EXPR_NEG; break; case TKWNOT: temp->base.kind = EXPR_NOT; break; @@ -2217,4 +2221,4 @@ static Node *parse_dowhile_statement(Context *ctx) } return (Node*) dowhl; -} +} \ No newline at end of file diff --git a/src/lib/objects/o_nullable.c b/src/lib/objects/o_nullable.c new file mode 100644 index 0000000..b7715a8 --- /dev/null +++ b/src/lib/objects/o_nullable.c @@ -0,0 +1,62 @@ +#include "../common/defs.h" +#include "objects.h" + +static void walk(Object *self, void (*callback)(Object **referer, void *userp), void *userp); +static bool istypeof(Object *self, Object *other); + +typedef struct { + Object base; + Object *item; +} NullableObject; + +static TypeObject t_nullable = { + .base = (Object) { .type = &t_type, .flags = Object_STATIC }, + .name = TYPENAME_NULLABLE, + .size = sizeof(NullableObject), + .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 = walk, + .walkexts = NULL, +}; + +TypeObject *Object_GetNullableType() +{ + return &t_nullable; +} + +Object *Object_NewNullable(Object *item, Heap *heap, Error *error) +{ + NullableObject *nullable = (NullableObject*) Heap_Malloc(heap, &t_nullable, error); + if (nullable != NULL) + nullable->item = item; + return (Object*) nullable; +} + +static bool +istypeof(Object *self, + Object *other) +{ + NullableObject *nullable = (NullableObject*) self; + if (Object_IsNone(other)) + return true; + return Object_IsTypeOf(nullable->item, other); +} + +static void walk(Object *self, void (*callback)(Object **referer, void *userp), void *userp) +{ + NullableObject *nullable = (NullableObject*) self; + callback(&nullable->item, userp); +} \ No newline at end of file diff --git a/src/lib/objects/objects.c b/src/lib/objects/objects.c index 4ec6869..189593d 100644 --- a/src/lib/objects/objects.c +++ b/src/lib/objects/objects.c @@ -33,14 +33,21 @@ #include "../common/defs.h" static _Bool op_eql(Object *self, Object *other); +static bool istypeof(Object *self, Object *other); TypeObject t_type = { .base = (Object) { .type = &t_type, .flags = Object_STATIC }, .name = TYPENAME_TYPE, .size = sizeof(TypeObject), + .istypeof = istypeof, .op_eql = op_eql, }; +static bool istypeof(Object *self, Object *other) +{ + return other->type == (TypeObject*) self; +} + TypeObject *Object_GetTypeType() { return &t_type; @@ -71,6 +78,13 @@ const TypeObject *Object_GetType(const Object *obj) return obj->type; } +bool Object_IsTypeOf(Object *typ, Object *obj) +{ + if (typ->type->istypeof == NULL) + return false; + return typ->type->istypeof(typ, obj); +} + int Object_Hash(Object *obj) { ASSERT(obj != NULL); diff --git a/src/lib/objects/objects.h b/src/lib/objects/objects.h index 94b5a02..b3f8d42 100644 --- a/src/lib/objects/objects.h +++ b/src/lib/objects/objects.h @@ -72,6 +72,9 @@ struct TypeObject { bool (*insert)(Object *self, Object *key, Object *val, Heap *heap, Error *err); int (*count)(Object *self); + // Types. + bool (*istypeof)(Object *self, Object *other); + bool (*op_eql)(Object *self, Object *other); void (*walk) (Object *self, void (*callback)(Object **referer, void *userp), void *userp); void (*walkexts)(Object *self, void (*callback)(void **referer, unsigned int size, void *userp), void *userp); @@ -100,6 +103,7 @@ int Object_Hash (Object *obj); Object* Object_Copy (Object *obj, Heap *heap, Error *err); int Object_Call (Object *obj, Object **argv, unsigned int argc, Object *rets[static MAX_RETS], Heap *heap, Error *err); void Object_Print(Object *obj, FILE *fp); +bool Object_IsTypeOf(Object *typ, Object *obj); Object* Object_Select(Object *coll, Object *key, Heap *heap, Error *err); Object* Object_Delete(Object *coll, Object *key, Heap *heap, Error *err); bool Object_Insert(Object *coll, Object *key, Object *val, Heap *heap, Error *err); @@ -114,6 +118,7 @@ Object* Object_NewNone(Heap *heap, Error *error); Object* Object_NewBuffer(size_t size, Heap *heap, Error *error); Object* Object_NewClosure(Object *parent, Object *new_map, Heap *heap, Error *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_FromInt (long long int val, Heap *heap, Error *error); Object* Object_FromBool (bool val, Heap *heap, Error *error); @@ -133,6 +138,7 @@ TypeObject *Object_GetMapType(); TypeObject *Object_GetBufferType(); TypeObject *Object_GetFileType(); TypeObject *Object_GetDirType(); +TypeObject *Object_GetNullableType(); bool Object_IsNone(Object *obj); bool Object_IsInt(Object *obj); diff --git a/src/lib/runtime/runtime.c b/src/lib/runtime/runtime.c index 3340851..60f208b 100644 --- a/src/lib/runtime/runtime.c +++ b/src/lib/runtime/runtime.c @@ -653,6 +653,32 @@ static _Bool step(Runtime *runtime, Error *error) return 1; } + case OPCODE_NLB: + { + ASSERT(opc == 0); + + if(runtime->frame->used == 0) + { + Error_Report(error, 1, "Frame doesn't have enough items on the stack to execute NLB"); + return 0; + } + + Object *top = Stack_Top(runtime->stack, 0); + + if(!Runtime_Pop(runtime, error, 1)) + return 0; + + ASSERT(top != NULL); + + Object *nullable = Object_NewNullable(top, runtime->heap, error); + if(nullable == NULL) + return 0; + + if(!Runtime_Push(runtime, error, nullable)) + return 0; + return 1; + } + case OPCODE_ADD: case OPCODE_SUB: case OPCODE_MUL: