added parsing ad compilation of function declarations

This commit is contained in:
cozis
2021-11-02 10:54:49 +00:00
parent 51f782362e
commit 051966a3f3
10 changed files with 362 additions and 22 deletions
+15
View File
@@ -9,6 +9,8 @@ typedef enum {
NODE_IFELSE,
NODE_COMP,
NODE_RETURN,
NODE_FUNC,
NODE_ARG,
} NodeKind;
typedef enum {
@@ -93,4 +95,17 @@ typedef struct {
Node base;
Node *val;
} ReturnNode;
typedef struct {
Node base;
char *name;
Node *argv;
int argc;
Node *body;
} FunctionNode;
typedef struct {
Node base;
char *name;
} ArgumentNode;
#endif
+80
View File
@@ -241,6 +241,86 @@ static _Bool emit_instr_for_node(ExeBuilder *exeb, Node *node, Error *error)
return 1;
}
case NODE_FUNC:
{
FunctionNode *func = (FunctionNode*) node;
Promise *func_index = Promise_New(ExeBuilder_GetAlloc(exeb), sizeof(long long int));
Promise *jump_index = Promise_New(ExeBuilder_GetAlloc(exeb), sizeof(long long int));
if(func_index == NULL || jump_index == NULL)
{
Error_Report(error, 1, "No memory");
return 0;
}
// Push function.
{
Operand ops[2] = {
{ .type = OPTP_PROMISE, .as_promise = func_index },
{ .type = OPTP_INT, .as_int = func->argc },
};
if(!ExeBuilder_Append(exeb, error, OPCODE_PUSHFUN, ops, 2, func->base.offset, func->base.length))
return 0;
}
// Assign variable.
Operand op = (Operand) { .type = OPTP_STRING, .as_string = func->name };
if(!ExeBuilder_Append(exeb, error, OPCODE_ASS, &op, 1, func->base.offset, func->base.length))
return 0;
// Pop function object.
op = (Operand) { .type = OPTP_INT, .as_int = 1 };
if(!ExeBuilder_Append(exeb, error, OPCODE_POP, &op, 1, func->base.offset, func->base.length))
return 0;
// Jump after the function code.
op = (Operand) { .type = OPTP_PROMISE, .as_promise = jump_index };
if(!ExeBuilder_Append(exeb, error, OPCODE_JUMP, &op, 1, func->base.offset, func->base.length))
return 0;
// This is the function code index.
long long int temp = ExeBuilder_InstrCount(exeb);
Promise_Resolve(func_index, &temp, sizeof(temp));
// Compile the function body.
{
// Assign the arguments.
if(func->argv)
assert(func->argv->kind == NODE_ARG);
ArgumentNode *arg = (ArgumentNode*) func->argv;
while(arg)
{
op = (Operand) { .type = OPTP_STRING, .as_string = arg->name };
if(!ExeBuilder_Append(exeb, error, OPCODE_ASS, &op, 1, arg->base.offset, arg->base.length))
return 0;
op = (Operand) { .type = OPTP_INT, .as_int = 1 };
if(!ExeBuilder_Append(exeb, error, OPCODE_POP, &op, 1, arg->base.offset, arg->base.length))
return 0;
assert(arg->base.next->kind == NODE_ARG);
arg = (ArgumentNode*) arg->base.next;
}
if(!emit_instr_for_node(exeb, func->body, error))
return 0;
}
// This is the first index after the function code.
temp = ExeBuilder_InstrCount(exeb);
Promise_Resolve(jump_index, &temp, sizeof(temp));
Promise_Free(func_index);
Promise_Free(jump_index);
return 1;
}
default:
UNREACHABLE;
return 0;
+185 -17
View File
@@ -25,6 +25,7 @@ typedef enum {
TIDENT,
TKWIF,
TKWFUN,
TKWELSE,
TKWNONE,
TKWTRUE,
@@ -51,6 +52,7 @@ static Node *parse_expression(Context *ctx);
static Node *parse_expression_statement(Context *ctx);
static Node *parse_ifelse_statement(Context *ctx);
static Node *parse_compound_statement(Context *ctx, TokenKind end);
static Node *parse_function_definition(Context *ctx);
static inline _Bool isoper(char c)
{
@@ -125,6 +127,10 @@ AST *parse(Source *src, BPAlloc *alloc, Error *error)
{
tok->kind = TKWIF;
}
else if(matchstr(str + tok->offset, tok->length, "fun"))
{
tok->kind = TKWFUN;
}
else if(matchstr(str + tok->offset, tok->length, "else"))
{
tok->kind = TKWELSE;
@@ -397,6 +403,9 @@ static Node *parse_statement(Context *ctx)
node->val = val;
return (Node*) node;
}
case TKWFUN:
return parse_function_definition(ctx);
}
Error_Report(ctx->error, 0, "Got token \"%.*s\" where the start of a statement was expected",
@@ -551,6 +560,20 @@ static Node *parse_string_primary_expression(Context *ctx)
return (Node*) node;
}
static char *copy_token_text(Context *ctx)
{
char *copy = BPAlloc_Malloc(ctx->alloc, ctx->token->length + 1);
if(copy == NULL)
return NULL;
memcpy(copy, ctx->src + ctx->token->offset, ctx->token->length);
copy[ctx->token->length] = '\0';
return copy;
}
static Node *parse_primary_expresion(Context *ctx)
{
assert(ctx != NULL);
@@ -789,23 +812,14 @@ static Node *parse_primary_expresion(Context *ctx)
case TIDENT:
{
char *copy;
int copyl;
char *copy = copy_token_text(ctx);
int copyl = ctx->token->length;
{
copy = BPAlloc_Malloc(ctx->alloc, ctx->token->length + 1);
if(copy == NULL)
{
Error_Report(ctx->error, 1, "No memory");
return NULL;
}
copyl = ctx->token->length;
memcpy(copy, ctx->src + ctx->token->offset, copyl);
copy[copyl] = '\0';
}
if(copy == NULL)
{
Error_Report(ctx->error, 1, "No memory");
return NULL;
}
IdentExprNode *node;
{
@@ -966,7 +980,6 @@ static Node *parse_expression(Context *ctx)
static Node *parse_ifelse_statement(Context *ctx)
{
assert(ctx != NULL);
assert(ctx->token != NULL);
if(done(ctx))
{
@@ -1076,4 +1089,159 @@ static Node *parse_compound_statement(Context *ctx, TokenKind end)
}
return (Node*) node;
}
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;
}
if(next(ctx) != '(')
{
if(done(ctx))
Error_Report(ctx->error, 0, "Source ended where a function argument list was expected");
else
Error_Report(ctx->error, 0, "Got unexpected token \"%.*s\" where a function argument list was expected", ctx->token->length, ctx->src + ctx->token->offset);
return NULL;
}
Node *argv = NULL;
int argc = 0;
if(next(ctx) != ')')
{
// Parse arguments.
while(1)
{
if(done(ctx))
{
Error_Report(ctx->error, 0, "Source ended inside a function argument list");
return NULL;
}
if(current(ctx) != TIDENT)
{
Error_Report(ctx->error, 0, "Got unexpected token \"%.*s\" where a function argument name was expected", ctx->token->length, ctx->src + ctx->token->offset);
return NULL;
}
char *arg_name = copy_token_text(ctx);
if(arg_name == NULL)
{
Error_Report(ctx->error, 1, "No memory");
return NULL;
}
ArgumentNode *arg;
{
// Make argument node.
arg = BPAlloc_Malloc(ctx->alloc, sizeof(ArgumentNode));
if(arg == NULL)
{
Error_Report(ctx->error, 1, "No memory");
return NULL;
}
arg->base.kind = NODE_ARG;
arg->base.next = NULL;
arg->base.offset = current_token(ctx)->offset;
arg->base.length = current_token(ctx)->length;
arg->name = arg_name;
}
// Add it to the list.
argc += 1;
arg->base.next = argv;
argv = (Node*) arg;
// Get either ',' or ')'.
if(next(ctx) == ')')
break;
if(done(ctx))
{
Error_Report(ctx->error, 0, "Source ended inside a function argument list");
return NULL;
}
if(current(ctx) != ',')
{
Error_Report(ctx->error, 0, "Got unexpected token \"%.*s\" where inside function argument list, where either ',' or ')' were expected", ctx->token->length, ctx->src + ctx->token->offset);
return NULL;
}
// Now prepare for the next identifier.
next(ctx);
}
}
next(ctx); // Consume the ')'.
if(done(ctx))
{
Error_Report(ctx->error, 0, "Source ended before function body");
return NULL;
}
Node *body = parse_statement(ctx);
if(body == NULL)
return NULL;
FunctionNode *func;
{
// Make argument node.
func = BPAlloc_Malloc(ctx->alloc, sizeof(FunctionNode));
if(func == NULL)
{
Error_Report(ctx->error, 1, "No memory");
return NULL;
}
func->base.kind = NODE_FUNC;
func->base.next = NULL;
func->base.offset = offset;
func->base.length = body->offset + body->length - offset;
func->name = name;
func->argv = argv;
func->argc = argc;
func->body = body;
}
return (Node*) func;
}