added argument type annotations and runtime checks. To do that new opcodes were created (PUSHTYP, PUSHTYPTYP, ERROR)
This commit is contained in:
@@ -383,12 +383,13 @@ void bins_basic_init(StaticMapSlot slots[])
|
||||
slots[0].as_type = Object_GetTypeType();
|
||||
slots[1].as_type = Object_GetNoneType();
|
||||
slots[2].as_type = Object_GetIntType();
|
||||
slots[3].as_type = Object_GetFloatType();
|
||||
slots[4].as_type = Object_GetStringType();
|
||||
slots[5].as_type = Object_GetListType();
|
||||
slots[6].as_type = Object_GetMapType();
|
||||
slots[7].as_type = Object_GetFileType();
|
||||
slots[8].as_type = Object_GetDirType();
|
||||
slots[3].as_type = Object_GetBoolType();
|
||||
slots[4].as_type = Object_GetFloatType();
|
||||
slots[5].as_type = Object_GetStringType();
|
||||
slots[6].as_type = Object_GetListType();
|
||||
slots[7].as_type = Object_GetMapType();
|
||||
slots[8].as_type = Object_GetFileType();
|
||||
slots[9].as_type = Object_GetDirType();
|
||||
}
|
||||
|
||||
StaticMapSlot bins_basic[] = {
|
||||
@@ -396,6 +397,7 @@ StaticMapSlot bins_basic[] = {
|
||||
{ "Type", 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 */ },
|
||||
{ "bool", 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 */ },
|
||||
{ "List", SM_TYPE, .as_type = NULL /* Until bins_basic_init is called */ },
|
||||
|
||||
@@ -105,9 +105,13 @@ static const InstrInfo instr_table[] = {
|
||||
[OPCODE_PUSHFUN] = {"PUSHFUN", 2, (OperandType[]) {OPTP_INT, OPTP_INT}},
|
||||
[OPCODE_PUSHLST] = {"PUSHLST", 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_ERROR] = {"ERROR", 1, (OperandType[]) {OPTP_STRING}},
|
||||
|
||||
[OPCODE_JUMPIFNOTANDPOP] = {"JUMPIFNOTANDPOP", 1, (OperandType[]) {OPTP_INT}},
|
||||
[OPCODE_JUMPIFANDPOP] = {"JUMPIFANDPOP", 1, (OperandType[]) {OPTP_INT}},
|
||||
[OPCODE_JUMP] = {"JUMP", 1, (OperandType[]) {OPTP_INT}},
|
||||
|
||||
@@ -84,7 +84,10 @@ typedef enum {
|
||||
OPCODE_PUSHFUN,
|
||||
OPCODE_PUSHLST,
|
||||
OPCODE_PUSHMAP,
|
||||
OPCODE_PUSHTYP,
|
||||
OPCODE_PUSHTYPTYP,
|
||||
OPCODE_RETURN,
|
||||
OPCODE_ERROR,
|
||||
OPCODE_JUMPIFANDPOP,
|
||||
OPCODE_JUMPIFNOTANDPOP,
|
||||
OPCODE_JUMP,
|
||||
|
||||
@@ -192,5 +192,7 @@ typedef struct {
|
||||
typedef struct {
|
||||
Node base;
|
||||
char *name;
|
||||
int typec;
|
||||
Node *typev;
|
||||
} ArgumentNode;
|
||||
#endif
|
||||
+132
-4
@@ -124,6 +124,19 @@ static void emitInstr_JUMPIFANDPOP_2(CodegenContext *ctx,
|
||||
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)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
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 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);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
Label *label_func = Label_New(ctx);
|
||||
@@ -204,11 +324,12 @@ static void emitInstrForFuncNode(CodegenContext *ctx, FunctionNode *func)
|
||||
{
|
||||
// Assign the arguments.
|
||||
ArgumentNode *arg = (ArgumentNode*) func->argv;
|
||||
int argidx = func->argc-1;
|
||||
while(arg)
|
||||
{
|
||||
emitInstr_ASS(ctx, arg->name, arg->base.offset, arg->base.length);
|
||||
emitInstr_POP1(ctx, arg->base.offset, arg->base.length);
|
||||
emitInstrForArgumentNode(ctx, arg, argidx);
|
||||
arg = (ArgumentNode*) arg->base.next;
|
||||
argidx -= 1;
|
||||
}
|
||||
|
||||
emitInstrForNode(ctx, func->body, NULL);
|
||||
@@ -673,19 +794,26 @@ Executable *codegen(AST *ast, BPAlloc *alloc, Error *error)
|
||||
assert(ast != NULL);
|
||||
assert(error != NULL);
|
||||
|
||||
jmp_buf env;
|
||||
|
||||
CodegenContext *ctx = CodegenContext_New(error, alloc);
|
||||
if(ctx == NULL) {
|
||||
Error_Report(error, 1, "No memory");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool jumped = CodegenContext_SetOrCatchJump(ctx);
|
||||
if(jumped) {
|
||||
if(setjmp(env)) {
|
||||
assert(error->occurred == true);
|
||||
CodegenContext_Free(ctx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
assert(error->occurred == false);
|
||||
CodegenContext_SetJumpDest(ctx, &env);
|
||||
|
||||
emitInstrForNode(ctx, ast->root, NULL);
|
||||
emitInstr_RETURN(ctx, 0, Source_GetSize(ast->src), 0);
|
||||
assert(error->occurred == false);
|
||||
|
||||
return CodegenContext_MakeExecutableAndFree(ctx, ast->src);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
#include <setjmp.h>
|
||||
#include <stdbool.h>
|
||||
#include "../utils/defs.h"
|
||||
#include "codegenctx.h"
|
||||
@@ -8,7 +7,8 @@ struct CodegenContext {
|
||||
BPAlloc *alloc;
|
||||
ExeBuilder *builder;
|
||||
bool own_alloc;
|
||||
jmp_buf env;
|
||||
bool env_set;
|
||||
jmp_buf *env;
|
||||
};
|
||||
|
||||
Label *Label_New(CodegenContext *ctx)
|
||||
@@ -48,7 +48,9 @@ Promise *Label_ToPromise(Label *label)
|
||||
|
||||
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,
|
||||
@@ -61,12 +63,14 @@ void CodegenContext_ReportErrorAndJump_(CodegenContext *ctx, const char *file,
|
||||
va_end(args);
|
||||
|
||||
okNowJump(ctx);
|
||||
UNREACHABLE;
|
||||
}
|
||||
|
||||
_Bool CodegenContext_SetOrCatchJump(CodegenContext *ctx)
|
||||
void CodegenContext_SetJumpDest(CodegenContext *ctx, jmp_buf *env)
|
||||
{
|
||||
bool jumped = setjmp(ctx->env);
|
||||
return jumped;
|
||||
assert(ctx->env_set == false);
|
||||
ctx->env = env;
|
||||
ctx->env_set = true;
|
||||
}
|
||||
|
||||
CodegenContext *CodegenContext_New(Error *error, BPAlloc *alloc)
|
||||
@@ -101,6 +105,7 @@ CodegenContext *CodegenContext_New(Error *error, BPAlloc *alloc)
|
||||
ctx->error = error;
|
||||
ctx->alloc = alloc;
|
||||
ctx->builder = builder;
|
||||
ctx->env_set = false;
|
||||
ctx->own_alloc = own_alloc;
|
||||
return ctx;
|
||||
}
|
||||
@@ -120,8 +125,10 @@ void CodegenContext_Free(CodegenContext *ctx)
|
||||
Executable *CodegenContext_MakeExecutableAndFree(CodegenContext *ctx, Source *src)
|
||||
{
|
||||
Executable *exe = ExeBuilder_Finalize(ctx->builder, ctx->error);
|
||||
if(exe == NULL)
|
||||
if(exe == NULL) {
|
||||
okNowJump(ctx);
|
||||
UNREACHABLE;
|
||||
}
|
||||
|
||||
if(src != NULL)
|
||||
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)
|
||||
{
|
||||
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);
|
||||
UNREACHABLE;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
#ifndef CODEGENCTX_H
|
||||
#define CODEGENCTX_H
|
||||
#include <setjmp.h>
|
||||
#include "../common/executable.h"
|
||||
|
||||
typedef struct CodegenContext CodegenContext;
|
||||
CodegenContext *CodegenContext_New(Error *error, BPAlloc *alloc);
|
||||
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);
|
||||
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, ...);
|
||||
|
||||
+92
-38
@@ -469,7 +469,6 @@ static inline TokenKind current(Context *ctx)
|
||||
|
||||
// Compile with -DDEBUG to get debugging messages printed to stderr.
|
||||
#ifdef DEBUG
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
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__)
|
||||
|
||||
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
|
||||
|
||||
static inline TokenKind next(Context *ctx)
|
||||
@@ -1850,40 +1865,9 @@ static Node *parse_compound_statement(Context *ctx, TokenKind end)
|
||||
return (Node*) node;
|
||||
}
|
||||
|
||||
static Node *parse_function_definition(Context *ctx)
|
||||
static Node *parse_function_arguments(Context *ctx, int *argc_)
|
||||
{
|
||||
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;
|
||||
}
|
||||
#warning "What if the source ends here?"
|
||||
|
||||
if(next(ctx) != '(')
|
||||
{
|
||||
@@ -1923,6 +1907,30 @@ static Node *parse_function_definition(Context *ctx)
|
||||
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;
|
||||
{
|
||||
// Make argument node.
|
||||
@@ -1939,6 +1947,8 @@ static Node *parse_function_definition(Context *ctx)
|
||||
arg->base.offset = current_token(ctx)->offset;
|
||||
arg->base.length = current_token(ctx)->length;
|
||||
arg->name = arg_name;
|
||||
arg->typev = typev;
|
||||
arg->typec = typec;
|
||||
}
|
||||
|
||||
// Add it to the list.
|
||||
@@ -1946,10 +1956,7 @@ static Node *parse_function_definition(Context *ctx)
|
||||
arg->base.next = argv;
|
||||
argv = (Node*) arg;
|
||||
|
||||
// Get either ',' or ')'.
|
||||
|
||||
if(next(ctx) == ')')
|
||||
break;
|
||||
// Expect either ',' or ')'.
|
||||
|
||||
if(done(ctx))
|
||||
{
|
||||
@@ -1957,6 +1964,9 @@ static Node *parse_function_definition(Context *ctx)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if(current(ctx) == ')')
|
||||
break;
|
||||
|
||||
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);
|
||||
@@ -1970,6 +1980,50 @@ static Node *parse_function_definition(Context *ctx)
|
||||
|
||||
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))
|
||||
{
|
||||
Error_Report(ctx->error, 0, "Source ended before function body");
|
||||
|
||||
+8
-2
@@ -139,7 +139,10 @@ static _Bool interpret_file(const char *file)
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
@@ -164,7 +167,10 @@ static _Bool interpret_code(const char *code)
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
|
||||
@@ -1147,6 +1147,39 @@ static _Bool step(Runtime *runtime, Error *error)
|
||||
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:
|
||||
{
|
||||
assert(opc == 1);
|
||||
@@ -1157,6 +1190,15 @@ static _Bool step(Runtime *runtime, Error *error)
|
||||
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:
|
||||
assert(opc == 1);
|
||||
assert(ops[0].type == OPTP_INT);
|
||||
|
||||
@@ -71,6 +71,12 @@ void _Error_Report2(Error *err, _Bool internal,
|
||||
assert(func);
|
||||
assert(line > 0);
|
||||
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);
|
||||
|
||||
err->occurred = 1;
|
||||
|
||||
@@ -1,24 +1,20 @@
|
||||
|
||||
#source
|
||||
|
||||
fun add(a, b) {
|
||||
return a + b;
|
||||
fun nop(a) {
|
||||
return a;
|
||||
}
|
||||
|
||||
#bytecode
|
||||
|
||||
PUSHFUN fun, 2;
|
||||
ASS "add";
|
||||
PUSHFUN fun, 1;
|
||||
ASS "nop";
|
||||
POP 1;
|
||||
JUMP end;
|
||||
fun:
|
||||
ASS "b";
|
||||
POP 1;
|
||||
ASS "a";
|
||||
POP 1;
|
||||
PUSHVAR "a";
|
||||
PUSHVAR "b";
|
||||
ADD;
|
||||
RETURN 1;
|
||||
RETURN 0;
|
||||
end:
|
||||
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
Reference in New Issue
Block a user