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[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 */ },
+4
View File
@@ -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}},
+3
View File
@@ -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,
+2
View File
@@ -192,5 +192,7 @@ typedef struct {
typedef struct {
Node base;
char *name;
int typec;
Node *typev;
} ArgumentNode;
#endif
+132 -4
View File
@@ -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);
}
+17 -8
View File
@@ -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;
}
}
+2 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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);
+42
View File
@@ -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);
+6
View File
@@ -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;
+4 -8
View File
@@ -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:
+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;