added "any" type
This commit is contained in:
@@ -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, },
|
||||
|
||||
@@ -16,3 +16,4 @@
|
||||
|
||||
#define TYPENAME_NULLABLE "NullableType"
|
||||
#define TYPENAME_SUM "SumType"
|
||||
#define TYPENAME_ANY "Any"
|
||||
@@ -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},
|
||||
|
||||
@@ -94,6 +94,7 @@ typedef enum {
|
||||
OPCODE_JUMPIFANDPOP,
|
||||
OPCODE_JUMPIFNOTANDPOP,
|
||||
OPCODE_JUMP,
|
||||
OPCODE_CHECKTYPE,
|
||||
} Opcode;
|
||||
|
||||
typedef struct xExecutable Executable;
|
||||
|
||||
@@ -195,8 +195,7 @@ typedef struct {
|
||||
typedef struct {
|
||||
Node base;
|
||||
char *name;
|
||||
int typec;
|
||||
Node *typev;
|
||||
Node *type;
|
||||
Node *value;
|
||||
} ArgumentNode;
|
||||
#endif
|
||||
+16
-110
@@ -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-value>
|
||||
* default_handled:
|
||||
* // Push the type of the argument
|
||||
* PUSHTYP;
|
||||
*
|
||||
* // Evaluate the type annotation and make
|
||||
* // sure that it evaluated to a type.
|
||||
* <arg-type-0>
|
||||
* 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;
|
||||
* <arg-type-1>
|
||||
* PUSHTYP;
|
||||
* PUSHTYPTYP;
|
||||
* EQL;
|
||||
* JUMPIFANDPOP argument_annotation_1_not_type;
|
||||
* EQL;
|
||||
* JUMPIFANDPOP argument_type_ok;
|
||||
*
|
||||
* PUSHTYP;
|
||||
* <arg-type-2>
|
||||
* 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 <arg-name>;
|
||||
* 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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
+24
-10
@@ -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 {
|
||||
|
||||
if(type->print == NULL)
|
||||
fprintf(fp, "<%s is unprintable>", Object_GetName(obj));
|
||||
else
|
||||
type->print(obj, fp);
|
||||
obj->flags |= Object_PRINT;
|
||||
|
||||
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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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; };
|
||||
};
|
||||
|
||||
@@ -40,7 +40,7 @@ struct Error {
|
||||
truncated;
|
||||
int length;
|
||||
char* message;
|
||||
char message2[256];
|
||||
char message2[512];
|
||||
const char *file,
|
||||
*func;
|
||||
int line;
|
||||
|
||||
Reference in New Issue
Block a user