added argument type annotations and runtime checks. To do that new opcodes were created (PUSHTYP, PUSHTYPTYP, ERROR)

This commit is contained in:
cozis
2022-08-14 16:25:47 +02:00
parent bc516f470a
commit 6bd60bc84a
18 changed files with 571 additions and 68 deletions
+8 -6
View File
@@ -383,12 +383,13 @@ void bins_basic_init(StaticMapSlot slots[])
slots[0].as_type = Object_GetTypeType(); slots[0].as_type = Object_GetTypeType();
slots[1].as_type = Object_GetNoneType(); slots[1].as_type = Object_GetNoneType();
slots[2].as_type = Object_GetIntType(); slots[2].as_type = Object_GetIntType();
slots[3].as_type = Object_GetFloatType(); slots[3].as_type = Object_GetBoolType();
slots[4].as_type = Object_GetStringType(); slots[4].as_type = Object_GetFloatType();
slots[5].as_type = Object_GetListType(); slots[5].as_type = Object_GetStringType();
slots[6].as_type = Object_GetMapType(); slots[6].as_type = Object_GetListType();
slots[7].as_type = Object_GetFileType(); slots[7].as_type = Object_GetMapType();
slots[8].as_type = Object_GetDirType(); slots[8].as_type = Object_GetFileType();
slots[9].as_type = Object_GetDirType();
} }
StaticMapSlot bins_basic[] = { StaticMapSlot bins_basic[] = {
@@ -396,6 +397,7 @@ StaticMapSlot bins_basic[] = {
{ "Type", SM_TYPE, .as_type = NULL /* Until bins_basic_init is called */ }, { "Type", SM_TYPE, .as_type = NULL /* Until bins_basic_init is called */ },
{ "None", SM_TYPE, .as_type = NULL /* Until bins_basic_init is called */ }, { "None", SM_TYPE, .as_type = NULL /* Until bins_basic_init is called */ },
{ "int", SM_TYPE, .as_type = NULL /* Until bins_basic_init is called */ }, { "int", SM_TYPE, .as_type = NULL /* Until bins_basic_init is called */ },
{ "bool", SM_TYPE, .as_type = NULL /* Until bins_basic_init is called */ },
{ "float", SM_TYPE, .as_type = NULL /* Until bins_basic_init is called */ }, { "float", SM_TYPE, .as_type = NULL /* Until bins_basic_init is called */ },
{ "String", SM_TYPE, .as_type = NULL /* Until bins_basic_init is called */ }, { "String", SM_TYPE, .as_type = NULL /* Until bins_basic_init is called */ },
{ "List", SM_TYPE, .as_type = NULL /* Until bins_basic_init is called */ }, { "List", SM_TYPE, .as_type = NULL /* Until bins_basic_init is called */ },
+4
View File
@@ -105,9 +105,13 @@ static const InstrInfo instr_table[] = {
[OPCODE_PUSHFUN] = {"PUSHFUN", 2, (OperandType[]) {OPTP_INT, OPTP_INT}}, [OPCODE_PUSHFUN] = {"PUSHFUN", 2, (OperandType[]) {OPTP_INT, OPTP_INT}},
[OPCODE_PUSHLST] = {"PUSHLST", 1, (OperandType[]) {OPTP_INT}}, [OPCODE_PUSHLST] = {"PUSHLST", 1, (OperandType[]) {OPTP_INT}},
[OPCODE_PUSHMAP] = {"PUSHMAP", 1, (OperandType[]) {OPTP_INT}}, [OPCODE_PUSHMAP] = {"PUSHMAP", 1, (OperandType[]) {OPTP_INT}},
[OPCODE_PUSHTYP] = {"PUSHTYP", 0, NULL},
[OPCODE_PUSHTYPTYP] = {"PUSHTYPTYP", 0, NULL},
[OPCODE_RETURN] = {"RETURN", 1, (OperandType[]) {OPTP_INT}}, [OPCODE_RETURN] = {"RETURN", 1, (OperandType[]) {OPTP_INT}},
[OPCODE_ERROR] = {"ERROR", 1, (OperandType[]) {OPTP_STRING}},
[OPCODE_JUMPIFNOTANDPOP] = {"JUMPIFNOTANDPOP", 1, (OperandType[]) {OPTP_INT}}, [OPCODE_JUMPIFNOTANDPOP] = {"JUMPIFNOTANDPOP", 1, (OperandType[]) {OPTP_INT}},
[OPCODE_JUMPIFANDPOP] = {"JUMPIFANDPOP", 1, (OperandType[]) {OPTP_INT}}, [OPCODE_JUMPIFANDPOP] = {"JUMPIFANDPOP", 1, (OperandType[]) {OPTP_INT}},
[OPCODE_JUMP] = {"JUMP", 1, (OperandType[]) {OPTP_INT}}, [OPCODE_JUMP] = {"JUMP", 1, (OperandType[]) {OPTP_INT}},
+3
View File
@@ -84,7 +84,10 @@ typedef enum {
OPCODE_PUSHFUN, OPCODE_PUSHFUN,
OPCODE_PUSHLST, OPCODE_PUSHLST,
OPCODE_PUSHMAP, OPCODE_PUSHMAP,
OPCODE_PUSHTYP,
OPCODE_PUSHTYPTYP,
OPCODE_RETURN, OPCODE_RETURN,
OPCODE_ERROR,
OPCODE_JUMPIFANDPOP, OPCODE_JUMPIFANDPOP,
OPCODE_JUMPIFNOTANDPOP, OPCODE_JUMPIFNOTANDPOP,
OPCODE_JUMP, OPCODE_JUMP,
+2
View File
@@ -192,5 +192,7 @@ typedef struct {
typedef struct { typedef struct {
Node base; Node base;
char *name; char *name;
int typec;
Node *typev;
} ArgumentNode; } ArgumentNode;
#endif #endif
+133 -5
View File
@@ -124,6 +124,19 @@ static void emitInstr_JUMPIFANDPOP_2(CodegenContext *ctx,
CodegenContext_EmitInstr(ctx, OPCODE_JUMPIFANDPOP, opv, 1, off, len); CodegenContext_EmitInstr(ctx, OPCODE_JUMPIFANDPOP, opv, 1, off, len);
} }
static void emitInstr_EQL(CodegenContext *ctx, int off, int len)
{
CodegenContext_EmitInstr(ctx, OPCODE_EQL, NULL, 0, off, len);
}
static void emitInstr_ERROR(CodegenContext *ctx, const char *msg, int off, int len)
{
Operand opv[1] = {
{ .type = OPTP_STRING, .as_string = msg },
};
CodegenContext_EmitInstr(ctx, OPCODE_ERROR, opv, 1, off, len);
}
static void emitInstr_PUSHTRU(CodegenContext *ctx, int off, int len) static void emitInstr_PUSHTRU(CodegenContext *ctx, int off, int len)
{ {
CodegenContext_EmitInstr(ctx, OPCODE_PUSHTRU, NULL, 0, off, len); CodegenContext_EmitInstr(ctx, OPCODE_PUSHTRU, NULL, 0, off, len);
@@ -134,6 +147,16 @@ static void emitInstr_PUSHFLS(CodegenContext *ctx, int off, int len)
CodegenContext_EmitInstr(ctx, OPCODE_PUSHFLS, NULL, 0, off, len); CodegenContext_EmitInstr(ctx, OPCODE_PUSHFLS, NULL, 0, off, len);
} }
static void emitInstr_PUSHTYP(CodegenContext *ctx, int off, int len)
{
CodegenContext_EmitInstr(ctx, OPCODE_PUSHTYP, NULL, 0, off, len);
}
static void emitInstr_PUSHTYPTYP(CodegenContext *ctx, int off, int len)
{
CodegenContext_EmitInstr(ctx, OPCODE_PUSHTYPTYP, NULL, 0, off, len);
}
static void emitInstrForNode(CodegenContext *ctx, Node *node, Label *label_break); static void emitInstrForNode(CodegenContext *ctx, Node *node, Label *label_break);
static Opcode exprkind_to_opcode(ExprKind kind) static Opcode exprkind_to_opcode(ExprKind kind)
@@ -182,6 +205,103 @@ static void emitInstrForFuncCallNode(CodegenContext *ctx, CallExprNode *expr,
CodegenContext_EmitInstr(ctx, OPCODE_CALL, ops, 2, expr->base.base.offset, expr->base.base.length); CodegenContext_EmitInstr(ctx, OPCODE_CALL, ops, 2, expr->base.base.offset, expr->base.base.length);
} }
static void emitInstrForArgumentNode(CodegenContext *ctx, ArgumentNode *arg, int argidx)
{
/*
* PUSHTYP;
* <arg-type-0> // What if this isn't a type?
* PUSHTYP;
* PUSHTYPTYP;
* EQL;
* JUMPIFANDPOP argument_annotation_0_not_type;
* EQL;
* JUMPIFANDPOP argument_type_ok;
*
* 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:
* ASS <arg-name>;
* POP 1;
*/
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);
}
emitInstr_ASS(ctx, arg->name, arg->base.offset, arg->base.length);
emitInstr_POP1(ctx, arg->base.offset, arg->base.length);
}
static void emitInstrForFuncNode(CodegenContext *ctx, FunctionNode *func) static void emitInstrForFuncNode(CodegenContext *ctx, FunctionNode *func)
{ {
Label *label_func = Label_New(ctx); Label *label_func = Label_New(ctx);
@@ -204,11 +324,12 @@ static void emitInstrForFuncNode(CodegenContext *ctx, FunctionNode *func)
{ {
// Assign the arguments. // Assign the arguments.
ArgumentNode *arg = (ArgumentNode*) func->argv; ArgumentNode *arg = (ArgumentNode*) func->argv;
int argidx = func->argc-1;
while(arg) while(arg)
{ {
emitInstr_ASS(ctx, arg->name, arg->base.offset, arg->base.length); emitInstrForArgumentNode(ctx, arg, argidx);
emitInstr_POP1(ctx, arg->base.offset, arg->base.length);
arg = (ArgumentNode*) arg->base.next; arg = (ArgumentNode*) arg->base.next;
argidx -= 1;
} }
emitInstrForNode(ctx, func->body, NULL); emitInstrForNode(ctx, func->body, NULL);
@@ -539,7 +660,7 @@ static void emitInstrForWhileLoopNode(CodegenContext *ctx, WhileNode *loop, Labe
/* /*
* start: * start:
* <condition> * <condition>
* JUMPIFNOTANDPOP end * JUMPIFNOTANDPOP end
* <body> * <body>
* JUMP start * JUMP start
* end: * end:
@@ -673,19 +794,26 @@ Executable *codegen(AST *ast, BPAlloc *alloc, Error *error)
assert(ast != NULL); assert(ast != NULL);
assert(error != NULL); assert(error != NULL);
jmp_buf env;
CodegenContext *ctx = CodegenContext_New(error, alloc); CodegenContext *ctx = CodegenContext_New(error, alloc);
if(ctx == NULL) { if(ctx == NULL) {
Error_Report(error, 1, "No memory"); Error_Report(error, 1, "No memory");
return NULL; return NULL;
} }
bool jumped = CodegenContext_SetOrCatchJump(ctx); if(setjmp(env)) {
if(jumped) { assert(error->occurred == true);
CodegenContext_Free(ctx); CodegenContext_Free(ctx);
return NULL; return NULL;
} }
assert(error->occurred == false);
CodegenContext_SetJumpDest(ctx, &env);
emitInstrForNode(ctx, ast->root, NULL); emitInstrForNode(ctx, ast->root, NULL);
emitInstr_RETURN(ctx, 0, Source_GetSize(ast->src), 0); emitInstr_RETURN(ctx, 0, Source_GetSize(ast->src), 0);
assert(error->occurred == false);
return CodegenContext_MakeExecutableAndFree(ctx, ast->src); return CodegenContext_MakeExecutableAndFree(ctx, ast->src);
} }
+17 -8
View File
@@ -1,4 +1,3 @@
#include <setjmp.h>
#include <stdbool.h> #include <stdbool.h>
#include "../utils/defs.h" #include "../utils/defs.h"
#include "codegenctx.h" #include "codegenctx.h"
@@ -8,7 +7,8 @@ struct CodegenContext {
BPAlloc *alloc; BPAlloc *alloc;
ExeBuilder *builder; ExeBuilder *builder;
bool own_alloc; bool own_alloc;
jmp_buf env; bool env_set;
jmp_buf *env;
}; };
Label *Label_New(CodegenContext *ctx) Label *Label_New(CodegenContext *ctx)
@@ -48,7 +48,9 @@ Promise *Label_ToPromise(Label *label)
static void okNowJump(CodegenContext *ctx) static void okNowJump(CodegenContext *ctx)
{ {
longjmp(ctx->env, 1); assert(ctx->env_set == true);
longjmp(*ctx->env, 1);
UNREACHABLE;
} }
void CodegenContext_ReportErrorAndJump_(CodegenContext *ctx, const char *file, void CodegenContext_ReportErrorAndJump_(CodegenContext *ctx, const char *file,
@@ -61,12 +63,14 @@ void CodegenContext_ReportErrorAndJump_(CodegenContext *ctx, const char *file,
va_end(args); va_end(args);
okNowJump(ctx); okNowJump(ctx);
UNREACHABLE;
} }
_Bool CodegenContext_SetOrCatchJump(CodegenContext *ctx) void CodegenContext_SetJumpDest(CodegenContext *ctx, jmp_buf *env)
{ {
bool jumped = setjmp(ctx->env); assert(ctx->env_set == false);
return jumped; ctx->env = env;
ctx->env_set = true;
} }
CodegenContext *CodegenContext_New(Error *error, BPAlloc *alloc) CodegenContext *CodegenContext_New(Error *error, BPAlloc *alloc)
@@ -101,6 +105,7 @@ CodegenContext *CodegenContext_New(Error *error, BPAlloc *alloc)
ctx->error = error; ctx->error = error;
ctx->alloc = alloc; ctx->alloc = alloc;
ctx->builder = builder; ctx->builder = builder;
ctx->env_set = false;
ctx->own_alloc = own_alloc; ctx->own_alloc = own_alloc;
return ctx; return ctx;
} }
@@ -120,8 +125,10 @@ void CodegenContext_Free(CodegenContext *ctx)
Executable *CodegenContext_MakeExecutableAndFree(CodegenContext *ctx, Source *src) Executable *CodegenContext_MakeExecutableAndFree(CodegenContext *ctx, Source *src)
{ {
Executable *exe = ExeBuilder_Finalize(ctx->builder, ctx->error); Executable *exe = ExeBuilder_Finalize(ctx->builder, ctx->error);
if(exe == NULL) if(exe == NULL) {
okNowJump(ctx); okNowJump(ctx);
UNREACHABLE;
}
if(src != NULL) if(src != NULL)
Executable_SetSource(exe, src); Executable_SetSource(exe, src);
@@ -132,6 +139,8 @@ Executable *CodegenContext_MakeExecutableAndFree(CodegenContext *ctx, Source *sr
void CodegenContext_EmitInstr(CodegenContext *ctx, Opcode opcode, Operand *opv, int opc, int off, int len) void CodegenContext_EmitInstr(CodegenContext *ctx, Opcode opcode, Operand *opv, int opc, int off, int len)
{ {
if(!ExeBuilder_Append(ctx->builder, ctx->error, opcode, opv, opc, off, len)) if(!ExeBuilder_Append(ctx->builder, ctx->error, opcode, opv, opc, off, len)) {
okNowJump(ctx); okNowJump(ctx);
UNREACHABLE;
}
} }
+2 -1
View File
@@ -1,11 +1,12 @@
#ifndef CODEGENCTX_H #ifndef CODEGENCTX_H
#define CODEGENCTX_H #define CODEGENCTX_H
#include <setjmp.h>
#include "../common/executable.h" #include "../common/executable.h"
typedef struct CodegenContext CodegenContext; typedef struct CodegenContext CodegenContext;
CodegenContext *CodegenContext_New(Error *error, BPAlloc *alloc); CodegenContext *CodegenContext_New(Error *error, BPAlloc *alloc);
void CodegenContext_EmitInstr(CodegenContext *ctx, Opcode opcode, Operand *opv, int opc, int off, int len); void CodegenContext_EmitInstr(CodegenContext *ctx, Opcode opcode, Operand *opv, int opc, int off, int len);
_Bool CodegenContext_SetOrCatchJump(CodegenContext *ctx); void CodegenContext_SetJumpDest(CodegenContext *ctx, jmp_buf *env);
void CodegenContext_Free(CodegenContext *ctx); void CodegenContext_Free(CodegenContext *ctx);
Executable *CodegenContext_MakeExecutableAndFree(CodegenContext *ctx, Source *src); Executable *CodegenContext_MakeExecutableAndFree(CodegenContext *ctx, Source *src);
void CodegenContext_ReportErrorAndJump_(CodegenContext *ctx, const char *file, const char *func, int line, bool internal, const char *format, ...); void CodegenContext_ReportErrorAndJump_(CodegenContext *ctx, const char *file, const char *func, int line, bool internal, const char *format, ...);
+92 -38
View File
@@ -469,7 +469,6 @@ static inline TokenKind current(Context *ctx)
// Compile with -DDEBUG to get debugging messages printed to stderr. // Compile with -DDEBUG to get debugging messages printed to stderr.
#ifdef DEBUG #ifdef DEBUG
#include <stdio.h> #include <stdio.h>
static inline TokenKind next(Context *ctx, const char *file, int line) static inline TokenKind next(Context *ctx, const char *file, int line)
@@ -501,6 +500,22 @@ static inline TokenKind prev(Context *ctx)
#define next(ctx) next(ctx, __FILE__, __LINE__) #define next(ctx) next(ctx, __FILE__, __LINE__)
static void Error_Report_(Error *error, const char *file, const char *func, int line, _Bool internal, const char *fmt, ...)
{
fprintf(stderr, "Reporting error at %s:%d (in %s)\n", file, line, func);
va_list args;
va_start(args, fmt);
_Error_Report2(error, internal, file, func, line, fmt, args);
va_end(args);
}
#ifdef Error_Report
#undef Error_Report
#endif
#define Error_Report(error, internal, fmt, ...) Error_Report_(error, __FILE__, __func__, __LINE__, internal, fmt, ## __VA_ARGS__)
#else #else
static inline TokenKind next(Context *ctx) static inline TokenKind next(Context *ctx)
@@ -1850,40 +1865,9 @@ static Node *parse_compound_statement(Context *ctx, TokenKind end)
return (Node*) node; return (Node*) node;
} }
static Node *parse_function_definition(Context *ctx) static Node *parse_function_arguments(Context *ctx, int *argc_)
{ {
assert(ctx != NULL); #warning "What if the source ends here?"
if(done(ctx))
{
Error_Report(ctx->error, 0, "Source ended where a function definition was expected");
return NULL;
}
if(current(ctx) != TKWFUN)
{
Error_Report(ctx->error, 0, "Got unexpected token \"%.*s\" where a function definition was expected", ctx->token->length, ctx->src + ctx->token->offset);
return NULL;
}
int offset = current_token(ctx)->offset;
if(next(ctx) != TIDENT)
{
if(done(ctx))
Error_Report(ctx->error, 0, "Source ended where an identifier was expected as function name");
else
Error_Report(ctx->error, 0, "Got unexpected token \"%.*s\" where an identifier was expected as function name", ctx->token->length, ctx->src + ctx->token->offset);
return NULL;
}
char *name = copy_token_text(ctx);
if(name == NULL)
{
Error_Report(ctx->error, 1, "No memory");
return NULL;
}
if(next(ctx) != '(') if(next(ctx) != '(')
{ {
@@ -1923,6 +1907,30 @@ static Node *parse_function_definition(Context *ctx)
return NULL; return NULL;
} }
int typec;
Node *typev = NULL;
if(next(ctx) == ':') {
next(ctx); // Skip the ':'.
Node *type = parse_expression(ctx, 0);
if(type == NULL)
return NULL;
typev = type;
typec = 1;
Node *tail = type;
while(current(ctx) == '|') {
next(ctx);
type = parse_expression(ctx, 0);
if(type == NULL)
return NULL;
tail->next = type;
tail = type;
typec += 1;
}
} else
typec = 0;
ArgumentNode *arg; ArgumentNode *arg;
{ {
// Make argument node. // Make argument node.
@@ -1939,6 +1947,8 @@ static Node *parse_function_definition(Context *ctx)
arg->base.offset = current_token(ctx)->offset; arg->base.offset = current_token(ctx)->offset;
arg->base.length = current_token(ctx)->length; arg->base.length = current_token(ctx)->length;
arg->name = arg_name; arg->name = arg_name;
arg->typev = typev;
arg->typec = typec;
} }
// Add it to the list. // Add it to the list.
@@ -1946,10 +1956,7 @@ static Node *parse_function_definition(Context *ctx)
arg->base.next = argv; arg->base.next = argv;
argv = (Node*) arg; argv = (Node*) arg;
// Get either ',' or ')'. // Expect either ',' or ')'.
if(next(ctx) == ')')
break;
if(done(ctx)) if(done(ctx))
{ {
@@ -1957,6 +1964,9 @@ static Node *parse_function_definition(Context *ctx)
return NULL; return NULL;
} }
if(current(ctx) == ')')
break;
if(current(ctx) != ',') if(current(ctx) != ',')
{ {
Error_Report(ctx->error, 0, "Got unexpected token \"%.*s\" inside function argument list, where either ',' or ')' were expected", ctx->token->length, ctx->src + ctx->token->offset); Error_Report(ctx->error, 0, "Got unexpected token \"%.*s\" inside function argument list, where either ',' or ')' were expected", ctx->token->length, ctx->src + ctx->token->offset);
@@ -1970,6 +1980,50 @@ static Node *parse_function_definition(Context *ctx)
next(ctx); // Consume the ')'. next(ctx); // Consume the ')'.
if(argc_ != NULL)
*argc_ = argc;
return argv;
}
static Node *parse_function_definition(Context *ctx)
{
assert(ctx != NULL);
if(done(ctx))
{
Error_Report(ctx->error, 0, "Source ended where a function definition was expected");
return NULL;
}
if(current(ctx) != TKWFUN)
{
Error_Report(ctx->error, 0, "Got unexpected token \"%.*s\" where a function definition was expected", ctx->token->length, ctx->src + ctx->token->offset);
return NULL;
}
int offset = current_token(ctx)->offset;
if(next(ctx) != TIDENT)
{
if(done(ctx))
Error_Report(ctx->error, 0, "Source ended where an identifier was expected as function name");
else
Error_Report(ctx->error, 0, "Got unexpected token \"%.*s\" where an identifier was expected as function name", ctx->token->length, ctx->src + ctx->token->offset);
return NULL;
}
char *name = copy_token_text(ctx);
if(name == NULL)
{
Error_Report(ctx->error, 1, "No memory");
return NULL;
}
int argc = 0; // Initialization for the warning.
Node *argv = parse_function_arguments(ctx, &argc);
if(done(ctx)) if(done(ctx))
{ {
Error_Report(ctx->error, 0, "Source ended before function body"); Error_Report(ctx->error, 0, "Source ended before function body");
+8 -2
View File
@@ -139,7 +139,10 @@ static _Bool interpret_file(const char *file)
} }
Executable *exe = compile_source_and_print_error_on_failure(src); Executable *exe = compile_source_and_print_error_on_failure(src);
if(exe == NULL) return 0; if(exe == NULL) {
Source_Free(src);
return 0;
}
_Bool r = interpret(exe); _Bool r = interpret(exe);
@@ -164,7 +167,10 @@ static _Bool interpret_code(const char *code)
} }
Executable *exe = compile_source_and_print_error_on_failure(src); Executable *exe = compile_source_and_print_error_on_failure(src);
if(exe == NULL) return 0; if(exe == NULL) {
Source_Free(src);
return 0;
}
_Bool r = interpret(exe); _Bool r = interpret(exe);
+42
View File
@@ -1147,6 +1147,39 @@ static _Bool step(Runtime *runtime, Error *error)
return 1; return 1;
} }
case OPCODE_PUSHTYPTYP:
{
assert(opc == 0);
Object *obj = (Object*) Object_GetTypeType();
assert(obj != NULL);
if(!Runtime_Push(runtime, error, obj))
return 0;
return 1;
}
case OPCODE_PUSHTYP:
{
assert(opc == 0);
if(runtime->frame->used < 1)
{
Error_Report(error, 1, "Frame has not enough values on the stack to run PUSHTYP instruction");
return 0;
}
Object *top = Stack_Top(runtime->stack, 0);
assert(top != NULL);
Object *typ = (Object*) Object_GetType(top);
assert(typ != NULL);
if(!Runtime_Push(runtime, error, typ))
return 0;
return 1;
}
case OPCODE_RETURN: case OPCODE_RETURN:
{ {
assert(opc == 1); assert(opc == 1);
@@ -1157,6 +1190,15 @@ static _Bool step(Runtime *runtime, Error *error)
return 0; return 0;
} }
case OPCODE_ERROR:
{
assert(opc == 1);
assert(ops[0].type == OPTP_STRING);
const char *msg = ops[0].as_string;
Error_Report(error, 0, "%s", msg);
return 0;
}
case OPCODE_JUMP: case OPCODE_JUMP:
assert(opc == 1); assert(opc == 1);
assert(ops[0].type == OPTP_INT); assert(ops[0].type == OPTP_INT);
+6
View File
@@ -71,6 +71,12 @@ void _Error_Report2(Error *err, _Bool internal,
assert(func); assert(func);
assert(line > 0); assert(line > 0);
assert(fmt); assert(fmt);
#ifdef DEBUG
if(err->occurred != 0) {
fprintf(stderr, "Error previously reported at %s:%d (in %s) :: %s\n", err->file, err->line, err->func, err->message);
}
#endif
assert(err->occurred == 0); assert(err->occurred == 0);
err->occurred = 1; err->occurred = 1;
+4 -8
View File
@@ -1,24 +1,20 @@
#source #source
fun add(a, b) { fun nop(a) {
return a + b; return a;
} }
#bytecode #bytecode
PUSHFUN fun, 2; PUSHFUN fun, 1;
ASS "add"; ASS "nop";
POP 1; POP 1;
JUMP end; JUMP end;
fun: fun:
ASS "b";
POP 1;
ASS "a"; ASS "a";
POP 1; POP 1;
PUSHVAR "a"; PUSHVAR "a";
PUSHVAR "b";
ADD;
RETURN 1; RETURN 1;
RETURN 0; RETURN 0;
end: end:
+25
View File
@@ -0,0 +1,25 @@
#source
fun add(a, b) {
return a + b;
}
#bytecode
PUSHFUN fun, 2;
ASS "add";
POP 1;
JUMP end;
fun:
ASS "b";
POP 1;
ASS "a";
POP 1;
PUSHVAR "a";
PUSHVAR "b";
ADD;
RETURN 1;
RETURN 0;
end:
RETURN 0;
+33
View File
@@ -0,0 +1,33 @@
#source
fun nop(a: None) {
return a;
}
#bytecode
PUSHFUN fun, 1;
ASS "nop";
POP 1;
JUMP end;
fun:
PUSHTYP;
PUSHVAR "None";
PUSHTYP;
PUSHTYPTYP;
EQL;
JUMPIFNOTANDPOP arg_annot_not_type;
EQL;
JUMPIFANDPOP arg_ok;
ERROR "Bad type for argument 0";
arg_annot_not_type:
ERROR "Argument 0 type annotation 0 is not a type";
arg_ok:
ASS "a";
POP 1;
PUSHVAR "a";
RETURN 1;
RETURN 0;
end:
RETURN 0;
+37
View File
@@ -0,0 +1,37 @@
#source
fun add(a: int, b) {
return a + b;
}
#bytecode
PUSHFUN fun, 2;
ASS "add";
POP 1;
JUMP end;
fun:
ASS "b";
POP 1;
PUSHTYP;
PUSHVAR "int";
PUSHTYP;
PUSHTYPTYP;
EQL;
JUMPIFNOTANDPOP arg0_annot0_bad;
EQL;
JUMPIFANDPOP arg_ok;
ERROR "Bad type for argument 0";
arg0_annot0_bad:
ERROR "Argument 0 type annotation 0 is not a type";
arg_ok:
ASS "a";
POP 1;
PUSHVAR "a";
PUSHVAR "b";
ADD;
RETURN 1;
RETURN 0;
end:
RETURN 0;
+37
View File
@@ -0,0 +1,37 @@
#source
fun add(a, b: int) {
return a + b;
}
#bytecode
PUSHFUN fun, 2;
ASS "add";
POP 1;
JUMP end;
fun:
PUSHTYP;
PUSHVAR "int";
PUSHTYP;
PUSHTYPTYP;
EQL;
JUMPIFNOTANDPOP annot_bad;
EQL;
JUMPIFANDPOP arg_ok;
ERROR "Bad type for argument 1";
annot_bad:
ERROR "Argument 1 type annotation 0 is not a type";
arg_ok:
ASS "b";
POP 1;
ASS "a";
POP 1;
PUSHVAR "a";
PUSHVAR "b";
ADD;
RETURN 1;
RETURN 0;
end:
RETURN 0;
+49
View File
@@ -0,0 +1,49 @@
#source
fun add(a: int, b: int) {
return a + b;
}
#bytecode
PUSHFUN fun, 2;
ASS "add";
POP 1;
JUMP end;
fun:
PUSHTYP;
PUSHVAR "int";
PUSHTYP;
PUSHTYPTYP;
EQL;
JUMPIFNOTANDPOP arg1_annot0_not_type;
EQL;
JUMPIFANDPOP arg1_ok;
ERROR "Bad type for argument 1";
arg1_annot0_not_type:
ERROR "Argument 1 type annotation 0 is not a type";
arg1_ok:
ASS "b";
POP 1;
PUSHTYP;
PUSHVAR "int";
PUSHTYP;
PUSHTYPTYP;
EQL;
JUMPIFNOTANDPOP arg0_annot0_not_type;
EQL;
JUMPIFANDPOP arg0_ok;
ERROR "Bad type for argument 0";
arg0_annot0_not_type:
ERROR "Argument 0 type annotation 0 is not a type";
arg0_ok:
ASS "a";
POP 1;
PUSHVAR "a";
PUSHVAR "b";
ADD;
RETURN 1;
RETURN 0;
end:
RETURN 0;
+69
View File
@@ -0,0 +1,69 @@
#source
fun add(a: int | float, b: bool | Map) {
return a + b;
}
#bytecode
PUSHFUN fun, 2;
ASS "add";
POP 1;
JUMP end;
fun:
PUSHTYP;
PUSHVAR "bool";
PUSHTYP;
PUSHTYPTYP;
EQL;
JUMPIFNOTANDPOP arg1_annot0_bad;
EQL;
JUMPIFANDPOP arg1_ok;
PUSHTYP;
PUSHVAR "Map";
PUSHTYP;
PUSHTYPTYP;
EQL;
JUMPIFNOTANDPOP arg1_annot1_bad;
EQL;
JUMPIFANDPOP arg1_ok;
ERROR "Bad type for argument 1";
arg1_annot0_bad:
ERROR "Argument 1 type annotation 0 is not a type";
arg1_annot1_bad:
ERROR "Argument 1 type annotation 1 is not a type";
arg1_ok:
ASS "b";
POP 1;
PUSHTYP;
PUSHVAR "int";
PUSHTYP;
PUSHTYPTYP;
EQL;
JUMPIFNOTANDPOP arg0_annot0_bad;
EQL;
JUMPIFANDPOP arg0_ok;
PUSHTYP;
PUSHVAR "float";
PUSHTYP;
PUSHTYPTYP;
EQL;
JUMPIFNOTANDPOP arg0_annot1_bad;
EQL;
JUMPIFANDPOP arg0_ok;
ERROR "Bad type for argument 0";
arg0_annot0_bad:
ERROR "Argument 0 type annotation 0 is not a type";
arg0_annot1_bad:
ERROR "Argument 0 type annotation 1 is not a type";
arg0_ok:
ASS "a";
POP 1;
PUSHVAR "a";
PUSHVAR "b";
ADD;
RETURN 1;
RETURN 0;
end:
RETURN 0;