diff --git a/ir.c b/ir.c index 64596de..88f26c7 100644 --- a/ir.c +++ b/ir.c @@ -21,6 +21,7 @@ const char *irinstr_str[IRInstrEnumSize] = { [IRJnz] = "jnz", [IRCallInternal] = "calli", [IRAddrOf] = "addrof", + [IRArrMake] = "mkarr", }; #define IRLIST_INIT_CAP_LONG 4096 @@ -86,13 +87,16 @@ void irlist_term(IRList *v) { case IRJnz: free_irparam(&i->tok.CJmp.condition, true); break; - case IRCallInternal: { - size_t n_args = i->tok.CallI.n_args; - for (size_t j = 0; j < n_args; j++) + case IRCallInternal: + for (size_t j = 0; j < i->tok.CallI.n_args; j++) free_irparam(&i->tok.CallI.args[j], true); free(i->tok.CallI.args); break; - } + case IRArrMake: + for (size_t j = 0; j < i->tok.ArrMake.len; j++) + free_irparam(&i->tok.ArrMake.vals[j], true); + free(i->tok.ArrMake.vals); + break; default: ASSERT_UNREACHED(); } @@ -198,6 +202,14 @@ void print_ir(IRList *v, const BuiltinFunc *builtin_funcs) { } break; } + case IRArrMake: { + printf(" %%%zx", i->tok.ArrMake.arr_addr); + for (size_t j = 0; j < i->tok.ArrMake.len; j++) { + printf(" "); + print_irparam(&i->tok.ArrMake.vals[j]); + } + break; + } default: ASSERT_UNREACHED(); } printf(" ; %zu:%zu", i->tok.ln, i->tok.col); diff --git a/ir.h b/ir.h index 48339d4..f4caa21 100644 --- a/ir.h +++ b/ir.h @@ -51,6 +51,7 @@ enum IRInstr { IRJnz, IRCallInternal, IRAddrOf, + IRArrMake, IRInstrEnumSize, }; typedef enum IRInstr IRInstr; @@ -106,6 +107,12 @@ typedef struct IRTok { size_t n_args; IRParam *args; } CallI; + + struct { + size_t arr_addr; + size_t len, cap; + IRParam *vals; + } ArrMake; }; } IRTok; diff --git a/lex.c b/lex.c index 89e6f82..a5be97b 100644 --- a/lex.c +++ b/lex.c @@ -247,6 +247,8 @@ TokList lex(const char *s) { case '}': case '(': case ')': + case '[': + case ']': case ',': case '+': case '-': diff --git a/parse.c b/parse.c index 870f493..abb832b 100644 --- a/parse.c +++ b/parse.c @@ -66,6 +66,9 @@ static void set_irtok_dest_addr(IRTok *t, size_t addr) { case IRCallInternal: t->CallI.ret_addr = addr; break; + case IRArrMake: + t->ArrMake.arr_addr = addr; + break; default: ASSERT_UNREACHED(); } @@ -313,7 +316,7 @@ static ExprRet expr(IRList *out_ir, TokList *toks, Map *funcs, Scope *parent_sc, eval_func_in_place = false; if (t->next->tok.kind == TokOp) { if (t->next->tok.Op == OpComma) { - toklist_del(toks, t->next, t->next); /* delete right parenthesis */ + toklist_del(toks, t->next, t->next); /* delete comma */ continue; } else if (t->next->tok.Op == OpRParen) { toklist_del(toks, t->next, t->next); /* delete right parenthesis */ @@ -394,6 +397,99 @@ static ExprRet expr(IRList *out_ir, TokList *toks, Map *funcs, Scope *parent_sc, } } + /* Collapse array. */ + else if (t->tok.kind == TokOp && t->tok.Op == OpLBrack) { + TokListItem *lbrack = t; + bool eval_immediately = true; + size_t elems_len = 0; + size_t elems_cap = 0; + IRParam *elems = NULL; + + if (t->next->tok.kind == TokOp && t->next->tok.Op == OpRBrack) { + /* empty array */ + toklist_del(toks, t->next, t->next); /* delete right bracket */ + } else { + elems_cap = 16; + elems = xmalloc(sizeof(IRParam) * elems_cap); + for (;;) { + if (elems_len+1 > elems_cap) + elems = xrealloc(elems, (elems_cap *= 2)); + IRParam e; + TRY_RET_ELSE(e = expr_into_irparam(out_ir, toks, funcs, &sc, t->next), (ExprRet){0}, free(elems)); + if (e.kind != IRParamLiteral) + eval_immediately = false; + elems[elems_len++] = e; + if (t->next->tok.kind == TokOp) { + if (t->next->tok.Op == OpComma) { + toklist_del(toks, t->next, t->next); /* delete comma */ + continue; + } else if (t->next->tok.Op == OpRBrack) { + toklist_del(toks, t->next, t->next); /* delete right bracket */ + break; + } + } + mark_err(&t->next->tok); + set_err("Expected ',' or ']' after array element"); + free(elems); + return (ExprRet){0}; + } + } + + if (eval_immediately) { + /* turn array into value */ + Value arr = { + .type = TypeArr, + .Arr = { + .type = TypeVoid, + .is_string = false, + .dynamically_allocated = false, + .vals = NULL, + .len = elems_len, + .cap = elems_len ? elems_cap : 0, + }, + }; + if (elems_len) { + Type arr_ty = elems[0].Literal.type; + void *arr_vals = xmalloc(type_size[arr_ty] * elems_cap); + for (size_t i = 0; i < elems_len; i++) { + Value *v = &elems[i].Literal; + if (v->type != arr_ty) { + free(arr_vals); + free(elems); + set_err("Type of array item %zu (%s) differs from array type (%s)", i, type_str[v->type], type_str[arr_ty]); + return (ExprRet){0}; + } + memcpy((uint8_t*)arr_vals + type_size[arr_ty] * i, &v->Void, type_size[arr_ty]); + } + arr.Arr.type = arr_ty; + arr.Arr.vals = arr_vals; + } + /* set lbracket to collapsed array value */ + lbrack->tok.kind = TokVal; + lbrack->tok.Val = arr; + /* free the now no longer needed element IRParam values */ + free(elems); + } else { + /* array initialization IR instruction */ + IRTok ir_tok = { + .ln = lbrack->tok.ln, + .col = lbrack->tok.col, + .instr = IRArrMake, + .ArrMake = { + .arr_addr = 0, + .len = elems_len, + .cap = elems_cap, + .vals = elems, + }, + }; + + /* return if we've just evaluated the last instruction */ + ExprRet ret; + if (expr_flush_ir_and_maybe_return(out_ir, toks, ir_tok, start, &sc, lbrack, &ret)) + return ret; + } + } + /* Collapse unary operation. */ if (perform_unary) { Tok *v = &t->tok; /* what we want to perform the operation on */ diff --git a/tok.c b/tok.c index f20d9be..ed68d88 100644 --- a/tok.c +++ b/tok.c @@ -117,6 +117,7 @@ int8_t op_prec[OperatorEnumSize] = { [OpNewLn] = PREC_DELIM, [OpLCurl] = PREC_DELIM, [OpRParen] = PREC_DELIM, + [OpRBrack] = PREC_DELIM, [OpComma] = PREC_DELIM, [OpAnd] = 0, [OpOr] = 0, @@ -137,6 +138,8 @@ const char *op_str[OperatorEnumSize] = { [OpRCurl] = "}", [OpLParen] = "(", [OpRParen] = ")", + [OpLBrack] = "[", + [OpRBrack] = "]", [OpComma] = ",", [OpAdd] = "+", [OpSub] = "-", diff --git a/tok.h b/tok.h index e1b6a72..6366d00 100644 --- a/tok.h +++ b/tok.h @@ -51,6 +51,8 @@ enum Operator { OpRCurl = '}', OpLParen = '(', OpRParen = ')', + OpLBrack = '[', + OpRBrack = ']', OpComma = ',', OpAdd = '+', OpSub = '-', diff --git a/vm.c b/vm.c index ac9ea68..ea6107d 100644 --- a/vm.c +++ b/vm.c @@ -92,6 +92,8 @@ void run(IRList *ir, const BuiltinFunc *builtin_funcs) { } case IRAddrOf: { if (instr->Unary.val.kind != IRParamAddr) { + free(fn_args); + stack_term(&s); set_err("Unable to take the address of a literal"); return; } @@ -174,6 +176,39 @@ void run(IRList *ir, const BuiltinFunc *builtin_funcs) { } break; } + case IRArrMake: { + size_t arr_len = instr->ArrMake.len, arr_cap = instr->ArrMake.cap; + Value arr = { + .type = TypeArr, + .Arr = { + .type = TypeVoid, + .is_string = false, + .dynamically_allocated = true, + .vals = NULL, + .len = arr_len, + .cap = arr_len ? arr_cap : 0, + }, + }; + if (arr_len) { + Type arr_ty = irparam_to_val(&s, &instr->ArrMake.vals[0])->type; + void *arr_vals = xmalloc(type_size[arr_ty] * arr_cap); + for (size_t j = 0; j < arr_len; j++) { + Value *v = irparam_to_val(&s, &instr->ArrMake.vals[j]); + if (v->type != arr_ty) { + free(arr_vals); + free(fn_args); + stack_term(&s); + set_err("Type of array item %zu (%s) differs from array type (%s)", j, type_str[v->type], type_str[arr_ty]); + return; + } + memcpy((uint8_t*)arr_vals + type_size[arr_ty] * j, &v->Void, type_size[arr_ty]); + } + arr.Arr.type = arr_ty; + arr.Arr.vals = arr_vals; + } + stack_assign(&s, instr->ArrMake.arr_addr, &arr); + break; + } default: ASSERT_UNREACHED(); }