Skip to content

Commit

Permalink
First implementation of unpackable function arguments
Browse files Browse the repository at this point in the history
Can now unpack function arguments with a syntex similar to unpacking
declarations/assignments:
```
fun unpackArgs((a, b), c)
    print(a, b, c)
end

var tuple = 1, 2
unpack-args(tuple, 3)
```
Will print
```
1 2 3
```
  • Loading branch information
bamless committed May 6, 2024
1 parent 1ba9276 commit 9b11aac
Show file tree
Hide file tree
Showing 4 changed files with 126 additions and 23 deletions.
21 changes: 16 additions & 5 deletions include/jstar/parse/ast.h
Expand Up @@ -135,6 +135,17 @@ typedef enum JStarStmtType {
JSR_BREAK
} JStarStmtType;

typedef struct FormalArg {
enum {
ARG,
UNPACK
} type;
union {
JStarIdentifier arg;
ext_vector(JStarIdentifier) unpack;
} as;
} FormalArg;

struct JStarDecl {
ext_vector(JStarExpr*) decorators;
bool isStatic;
Expand All @@ -146,15 +157,15 @@ struct JStarDecl {
} var;
struct {
JStarIdentifier id;
ext_vector(JStarIdentifier) formalArgs;
ext_vector(FormalArg) formalArgs;
ext_vector(JStarExpr*) defArgs;
JStarIdentifier vararg;
bool isGenerator;
JStarStmt* body;
} fun;
struct {
JStarIdentifier id;
ext_vector(JStarIdentifier) formalArgs;
ext_vector(FormalArg) formalArgs;
ext_vector(JStarExpr*) defArgs;
JStarIdentifier vararg;
} native;
Expand Down Expand Up @@ -234,7 +245,7 @@ JSTAR_API bool jsrIdentifierEq(const JStarIdentifier* id1, const JStarIdentifier
// EXPRESSION NODES
// -----------------------------------------------------------------------------

JSTAR_API JStarExpr* jsrFuncLiteral(int line, ext_vector(JStarIdentifier) args,
JSTAR_API JStarExpr* jsrFuncLiteral(int line, ext_vector(FormalArg) args,
ext_vector(JStarExpr*) defArgs, JStarTok* vararg,
bool isGenerator, JStarStmt* body);
JSTAR_API JStarExpr* jsrTernaryExpr(int line, JStarExpr* cond, JStarExpr* thenExpr,
Expand Down Expand Up @@ -265,10 +276,10 @@ JSTAR_API void jsrExprFree(JStarExpr* e);
// STATEMENT NODES
// -----------------------------------------------------------------------------

JSTAR_API JStarStmt* jsrFuncDecl(int line, JStarTok* name, ext_vector(JStarIdentifier) args,
JSTAR_API JStarStmt* jsrFuncDecl(int line, JStarTok* name, ext_vector(FormalArg) args,
ext_vector(JStarExpr*) defArgs, JStarTok* vararg, bool isGenerator,
JStarStmt* body);
JSTAR_API JStarStmt* jsrNativeDecl(int line, JStarTok* name, ext_vector(JStarIdentifier) args,
JSTAR_API JStarStmt* jsrNativeDecl(int line, JStarTok* name, ext_vector(FormalArg) args,
ext_vector(JStarExpr*) defArgs, JStarTok* vararg);
JSTAR_API JStarStmt* jsrForStmt(int line, JStarStmt* init, JStarExpr* cond, JStarExpr* act,
JStarStmt* body);
Expand Down
53 changes: 48 additions & 5 deletions src/compiler.c
Expand Up @@ -41,8 +41,9 @@
#define MAX_INLINE_ARGS 10

// String constants
#define THIS_STR "this"
#define ANON_FMT "anonymous[line:%d]"
#define THIS_STR "this"
#define ANON_FMT "anonymous[line:%d]"
#define UNPACK_ARG_FMT "@unpack:%d"

static const int opcodeStackUsage[] = {
#define OPCODE(opcode, args, stack) stack,
Expand Down Expand Up @@ -1696,6 +1697,46 @@ static void compileLoopExitStmt(Compiler* c, JStarStmt* s) {
// DECLARATIONS
// -----------------------------------------------------------------------------

static void compileFormalArg(Compiler *c, const FormalArg* arg, int argIdx, int line) {
switch(arg->type) {
case ARG: {
Variable var = declareVar(c, &arg->as.arg, false, line);
defineVar(c, &var, line);
break;
}
case UNPACK: {
char name[sizeof(ANON_FMT) + STRLEN_FOR_INT(int)];
sprintf(name, UNPACK_ARG_FMT, argIdx);
JStarIdentifier id = createIdentifier(name);

Variable var = declareVar(c, &id, false, line);
defineVar(c, &var, line);
break;
}
}
}

static void unpackFormalArgs(Compiler* c, ext_vector(FormalArg) args, int line) {
int argIdx = 0;
ext_vec_foreach(const FormalArg* arg, args) {
if(arg->type == UNPACK) {
char name[sizeof(ANON_FMT) + STRLEN_FOR_INT(int)];
sprintf(name, UNPACK_ARG_FMT, argIdx);
JStarIdentifier id = createIdentifier(name);

compileVarLit(c, &id, false, line);
emitOpcode(c, OP_UNPACK, line);
emitByte(c, ext_vec_size(arg->as.unpack), line);

ext_vec_foreach(const JStarIdentifier* id, arg->as.unpack) {
Variable unpackedArg = declareVar(c, id, false, line);
defineVar(c, &unpackedArg, line);
}
}
}
argIdx++;
}

static ObjFunction* function(Compiler* c, ObjModule* m, ObjString* name, JStarStmt* s) {
size_t defaults = ext_vec_size(s->as.decl.as.fun.defArgs);
size_t arity = ext_vec_size(s->as.decl.as.fun.formalArgs);
Expand All @@ -1717,9 +1758,9 @@ static ObjFunction* function(Compiler* c, ObjModule* m, ObjString* name, JStarSt
int receiverLocal = addLocal(c, &receiverName, s->line);
initializeLocal(c, receiverLocal);

ext_vec_foreach(const JStarIdentifier* argName, s->as.decl.as.fun.formalArgs) {
Variable arg = declareVar(c, argName, false, s->line);
defineVar(c, &arg, s->line);
int argIdx = 0;
ext_vec_foreach(const FormalArg* arg, s->as.decl.as.fun.formalArgs) {
compileFormalArg(c, arg, argIdx, s->line);
}

if(isVararg) {
Expand All @@ -1731,6 +1772,8 @@ static ObjFunction* function(Compiler* c, ObjModule* m, ObjString* name, JStarSt
emitOpcode(c, OP_GENERATOR, s->line);
}

unpackFormalArgs(c, s->as.decl.as.fun.formalArgs, s->line);

JStarStmt* body = s->as.decl.as.fun.body;
compileStatements(c, body->as.blockStmt.stmts);

Expand Down
17 changes: 14 additions & 3 deletions src/parse/ast.c
Expand Up @@ -155,7 +155,7 @@ JStarExpr* jsrCompundAssExpr(int line, JStarTokType op, JStarExpr* lval, JStarEx
return e;
}

JStarExpr* jsrFuncLiteral(int line, ext_vector(JStarIdentifier) args,
JStarExpr* jsrFuncLiteral(int line, ext_vector(FormalArg) args,
ext_vector(JStarExpr*) defArgs, JStarTok* vararg, bool isGenerator,
JStarStmt* body) {
JStarExpr* e = newExpr(line, JSR_FUNC_LIT);
Expand Down Expand Up @@ -267,7 +267,7 @@ static JStarStmt* newDecl(int line, JStarStmtType type) {

// Declarations

JStarStmt* jsrFuncDecl(int line, JStarTok* name, ext_vector(JStarIdentifier) args,
JStarStmt* jsrFuncDecl(int line, JStarTok* name, ext_vector(FormalArg) args,
ext_vector(JStarExpr*) defArgs, JStarTok* varargName, bool isGenerator,
JStarStmt* body) {
JStarStmt* f = newDecl(line, JSR_FUNCDECL);
Expand All @@ -280,7 +280,7 @@ JStarStmt* jsrFuncDecl(int line, JStarTok* name, ext_vector(JStarIdentifier) arg
return f;
}

JStarStmt* jsrNativeDecl(int line, JStarTok* name, ext_vector(JStarIdentifier) args,
JStarStmt* jsrNativeDecl(int line, JStarTok* name, ext_vector(FormalArg) args,
ext_vector(JStarExpr*) defArgs, JStarTok* varargName) {
JStarStmt* n = newDecl(line, JSR_NATIVEDECL);
n->as.decl.as.native.id = (JStarIdentifier){name->length, name->lexeme};
Expand Down Expand Up @@ -463,6 +463,11 @@ void jsrStmtFree(JStarStmt* s) {
}
case JSR_FUNCDECL: {
freeDeclaration(s);
ext_vec_foreach(FormalArg* arg, s->as.decl.as.fun.formalArgs) {
if (arg->type == UNPACK){
ext_vec_free(arg->as.unpack);
}
}
ext_vec_free(s->as.decl.as.fun.formalArgs);
ext_vec_foreach(JStarExpr** e, s->as.decl.as.fun.defArgs) {
jsrExprFree(*e);
Expand All @@ -473,6 +478,12 @@ void jsrStmtFree(JStarStmt* s) {
}
case JSR_NATIVEDECL: {
freeDeclaration(s);
ext_vec_foreach(FormalArg* arg, s->as.decl.as.fun.formalArgs) {
if (arg->type == UNPACK){
ext_vec_free(arg->as.unpack);
}
}
ext_vec_free(s->as.decl.as.fun.formalArgs);
ext_vec_free(s->as.decl.as.native.formalArgs);
ext_vec_foreach(JStarExpr** e, s->as.decl.as.native.defArgs) {
jsrExprFree(*e);
Expand Down
58 changes: 48 additions & 10 deletions src/parse/parser.c
@@ -1,5 +1,6 @@
#include "parse/parser.h"

#include <fcntl.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdio.h>
Expand Down Expand Up @@ -294,7 +295,7 @@ static void checkUnpackAssignement(Parser* p, JStarExpr* lvals, JStarTokType ass
error(p, "Unpack cannot use compound assignement");
return;
}
ext_vec_foreach(JStarExpr** it, lvals->as.list) {
ext_vec_foreach(JStarExpr * *it, lvals->as.list) {
JStarExpr* expr = *it;
if(expr && !isLValue(expr->type)) {
error(p, "left hand side of unpack assignment must be composed of lvalues");
Expand Down Expand Up @@ -337,29 +338,64 @@ static JStarExpr* literal(Parser* p);
static JStarExpr* tupleLiteral(Parser* p);

typedef struct FormalArgs {
ext_vector(JStarIdentifier) arguments;
ext_vector(FormalArg) arguments;
ext_vector(JStarExpr*) defaults;
JStarTok vararg;
} FormalArgs;

static FormalArg parseUnpackArgument(Parser* p) {
ext_vector(JStarIdentifier) names = NULL;
require(p, TOK_LPAREN);

do {
JStarTok id = require(p, TOK_IDENTIFIER);
skipNewLines(p);

ext_vec_push_back(names, createIdentifier(&id));

if(!match(p, TOK_COMMA)) {
break;
}

advance(p);
skipNewLines(p);
} while(match(p, TOK_IDENTIFIER));

require(p, TOK_RPAREN);

return (FormalArg){.type = UNPACK, .as = {.unpack = names}};
}

static FormalArgs formalArgs(Parser* p, JStarTokType open, JStarTokType close) {
FormalArgs args = {0};

require(p, open);
skipNewLines(p);

while(match(p, TOK_IDENTIFIER)) {
JStarTok argument = advance(p);
while(match(p, TOK_IDENTIFIER) || match(p, TOK_LPAREN)) {
JStarTok peek = p->peek;
FormalArg arg;

if(peek.type == TOK_LPAREN) {
arg = parseUnpackArgument(p);
} else {
JStarTok argument = advance(p);
arg = (FormalArg){.type = ARG, .as = {.arg = createIdentifier(&argument)}};
}

skipNewLines(p);

if(match(p, TOK_EQUAL)) {
jsrLexRewind(&p->lex, &argument);
if(arg.type == UNPACK){
error(p, "Unpack argument cannot have default value");
}

jsrLexRewind(&p->lex, &peek);
jsrNextToken(&p->lex, &p->peek);
break;
}

ext_vec_push_back(args.arguments, createIdentifier(&argument));
skipNewLines(p);
ext_vec_push_back(args.arguments, arg);

if(!match(p, close)) {
require(p, TOK_COMMA);
Expand All @@ -381,7 +417,8 @@ static FormalArgs formalArgs(Parser* p, JStarTokType open, JStarTokType close) {
error(p, "Default argument must be a constant");
}

ext_vec_push_back(args.arguments, createIdentifier(&argument));
FormalArg arg = {.type = ARG, .as = {.arg = createIdentifier(&argument)}};
ext_vec_push_back(args.arguments, arg);
ext_vec_push_back(args.defaults, constant);

if(!match(p, close)) {
Expand Down Expand Up @@ -673,7 +710,7 @@ static ext_vector(JStarExpr*) parseDecorators(Parser* p) {
}

static void freeDecorators(ext_vector(JStarExpr*) decorators) {
ext_vec_foreach(JStarExpr **e, decorators) {
ext_vec_foreach(JStarExpr * *e, decorators) {
jsrExprFree(*e);
}
ext_vec_free(decorators);
Expand Down Expand Up @@ -908,7 +945,8 @@ static JStarStmt* parseProgram(Parser* p) {
}

// Top level function doesn't have name or arguments, so pass them empty
return jsrFuncDecl(0, &(JStarTok){0}, NULL, NULL, &(JStarTok){0}, false, jsrBlockStmt(0, stmts));
return jsrFuncDecl(0, &(JStarTok){0}, NULL, NULL, &(JStarTok){0}, false,
jsrBlockStmt(0, stmts));
}

// -----------------------------------------------------------------------------
Expand Down

0 comments on commit 9b11aac

Please sign in to comment.