From 12dd278cad5bb9ba8c159df5d9f7d623c20e2387 Mon Sep 17 00:00:00 2001 From: Andrew Byers Date: Sat, 31 Aug 2024 17:52:23 -0500 Subject: [PATCH] Refactor and add tests for coverage --- fuzz/fuzz.c | 2 +- src/alloc.c | 4 +- src/api.c | 100 +++++---- src/api.h | 22 +- src/ast.c | 6 +- src/ast.h | 6 +- src/auxlib.c | 55 +++-- src/auxlib.h | 4 +- src/call.c | 56 +++--- src/call.h | 47 +---- src/code.h | 2 +- src/codegen.c | 66 +++--- src/compile.c | 51 +++++ src/compile.h | 3 +- src/config.h | 14 +- src/debug.c | 27 ++- src/debug.h | 2 + src/env.h | 6 +- src/gc.c | 9 +- src/hir.c | 191 +++--------------- src/hir.h | 39 +--- src/lex.c | 40 ++-- src/lib.c | 107 ++++++---- src/lib.h | 2 + src/map.h | 10 +- src/os.c | 2 +- src/os.h | 1 + src/parse.c | 85 ++------ src/paw.c | 9 +- src/paw.h | 8 +- src/resolve.c | 380 +++++++++++++++-------------------- src/rt.c | 143 +++++++------ src/rt.h | 4 +- src/str.c | 44 +++- src/str.h | 6 +- src/util.h | 8 +- src/value.c | 78 ++++--- src/value.h | 27 +-- test/CMakeLists.txt | 1 + test/scripts/fib.paw | 18 ++ test/scripts/integer.paw | 2 +- test/scripts/list.paw | 24 +++ test/scripts/poly_struct.paw | 39 ++++ test/test_alloc.c | 2 +- test/test_api.c | 65 +++++- test/test_error.c | 286 +++++++++++++++----------- test/test_impl.c | 126 +++++------- test/test_oom.c | 146 +++++++++++--- test/test_rt.c | 3 +- 49 files changed, 1250 insertions(+), 1128 deletions(-) create mode 100644 test/scripts/fib.paw diff --git a/fuzz/fuzz.c b/fuzz/fuzz.c index 6c60b56..9d385f6 100644 --- a/fuzz/fuzz.c +++ b/fuzz/fuzz.c @@ -7,7 +7,7 @@ extern int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { - struct FuzzState fs = fuzz_open(PAW_HEAP_MIN); + struct FuzzState fs = fuzz_open(PAW_HEAP_DEFAULT); pawL_load_nchunk(fs.P, "fuzz", (const char *)data, size); fuzz_close(fs); return 0; diff --git a/src/alloc.c b/src/alloc.c index 96d8961..24cf5cf 100644 --- a/src/alloc.c +++ b/src/alloc.c @@ -75,8 +75,6 @@ struct Allocator { uint32_t key_min; }; -_Static_assert(PAW_HEAP_MIN >= HEAP_META_SIZE, "PAW_HEAP_MIN is too small"); - uint8_t pawZ_get_flag(const struct Heap *H, uintptr_t uptr) { const size_t id = FLAG_BASE(H, uptr); @@ -384,6 +382,7 @@ static size_t compute_flag_count(size_t h) const size_t F = FLAGS_PER_BYTE; const size_t m = HEAP_META_SIZE; const size_t p = sizeof(void *); + if (h < m) return 0; // too small return F * (h - m) / (F * p + 1); } @@ -393,6 +392,7 @@ int pawZ_init(paw_Env *P, void *heap, size_t heap_size, paw_Bool is_owned) paw_assert(PAW_IS_ALIGNED(heap)); paw_assert(PAW_IS_ALIGNED(heap_size)); const size_t nf = compute_flag_count(heap_size); + if (nf == 0) return PAW_EMEMORY; // initialize heap manager struct Heap *H = heap; diff --git a/src/api.c b/src/api.c index e366ca7..d745245 100644 --- a/src/api.c +++ b/src/api.c @@ -4,13 +4,14 @@ #include "prefix.h" #include -#include "paw.h" +#include "api.h" #include "alloc.h" #include "compile.h" #include "env.h" #include "gc.h" #include "lib.h" #include "parse.h" +#include "paw.h" #include "rt.h" static void *default_alloc(void *ud, void *ptr, size_t old_size, size_t new_size) @@ -39,8 +40,8 @@ size_t paw_bytes_used(const paw_Env *P) static void open_aux(paw_Env *P, void *arg) { paw_unused(arg); - pawG_init(P); pawC_init(P); + pawG_init(P); pawS_init(P); pawP_init(P); pawR_init(P); @@ -49,12 +50,13 @@ static void open_aux(paw_Env *P, void *arg) } #define OR_DEFAULT(a, b) ((a) ? (a) : (b)) +#define HEAP_MIN (PAW_ROUND_UP(sizeof(paw_Env))) paw_Env *paw_open(const struct paw_Options *o) { size_t heap_size = OR_DEFAULT(o->heap_size, PAW_HEAP_DEFAULT); heap_size &= ~(PAW_ALIGN - 1); // round down - if (heap_size < PAW_HEAP_MIN) return NULL; + if (heap_size < HEAP_MIN) return NULL; void *ud = OR_DEFAULT(o->ud, NULL); paw_Alloc alloc = OR_DEFAULT(o->alloc, default_alloc); @@ -78,7 +80,7 @@ paw_Env *paw_open(const struct paw_Options *o) if (owns_heap) alloc(ud, ph, zh, 0); return NULL; } - if (pawC_try(P, open_aux, NULL)) { + if (pawC_raw_try(P, open_aux, NULL)) { paw_close(P); return NULL; } @@ -122,36 +124,41 @@ int paw_lookup_item(paw_Env *P, struct paw_Item *pitem) void paw_push_value(paw_Env *P, int index) { const Value v = *at(P, index); - pawC_pushv(P, v); + *P->top.p = v; + API_INCR_TOP(P, 1); } void paw_push_zero(paw_Env *P, int n) { for (int i = 0; i < n; ++i) { - pawC_push0(P); + V_SET_0(P->top.p + i); } + API_INCR_TOP(P, n); } void paw_push_boolean(paw_Env *P, paw_Bool b) { - pawC_pushb(P, b); + V_SET_BOOL(P->top.p, b); + API_INCR_TOP(P, 1); } void paw_push_float(paw_Env *P, paw_Float f) { - pawC_pushf(P, f); + V_SET_FLOAT(P->top.p, f); + API_INCR_TOP(P, 1); } void paw_push_int(paw_Env *P, paw_Int i) { - pawC_pushi(P, i); + V_SET_INT(P->top.p, i); + API_INCR_TOP(P, 1); } void paw_new_native(paw_Env *P, paw_Function fn, int nup) { - Value *pv = pawC_push0(P); Native *o = pawV_new_native(P, fn, nup); - V_SET_OBJECT(pv, o); + V_SET_OBJECT(P->top.p, o); + API_INCR_TOP(P, 1); StackPtr top = P->top.p; const StackPtr base = top - nup - 1; @@ -170,8 +177,10 @@ const char *paw_push_string(paw_Env *P, const char *s) const char *paw_push_nstring(paw_Env *P, const char *s, size_t n) { - const Value *pv = pawC_pushns(P, s, n); - return V_TEXT(*pv); + String *str = pawS_new_nstr(P, s, n); + V_SET_OBJECT(P->top.p, str); + API_INCR_TOP(P, 1); + return str->text; } const char *paw_push_vfstring(paw_Env *P, const char *fmt, va_list arg) @@ -221,8 +230,21 @@ paw_Function paw_native(paw_Env *P, int index) void *paw_userdata(paw_Env *P, int index) { - const Foreign *f = V_FOREIGN(*at(P, index)); - return f->data; + Value v = *at(P, index); + return V_FOREIGN(v)->data; +} + +void *paw_pointer(paw_Env *P, int index) +{ + // must be an object + Object *o = V_OBJECT(*at(P, index)); + if (o->gc_kind == VNATIVE) { + return ERASE_TYPE(CAST_UPTR(O_NATIVE(o)->func)); + } else if (o->gc_kind == VFOREIGN) { + return O_FOREIGN(o)->data; + } else { + return o; + } } void paw_pop(paw_Env *P, int n) @@ -280,7 +302,7 @@ int paw_call(paw_Env *P, int argc) int paw_get_count(paw_Env *P) { - return CAST(P->top.p - P->cf->base.p, int); + return CAST(int, P->top.p - P->cf->base.p); } void paw_get_typename(paw_Env *P, paw_Type code) @@ -294,14 +316,16 @@ void paw_get_typename(paw_Env *P, paw_Type code) void paw_get_global(paw_Env *P, int gid) { - *pawC_push0(P) = *pawE_get_val(P, gid); + *P->top.p = *pawE_get_val(P, gid); + API_INCR_TOP(P, 1); } -void paw_call_global(paw_Env *P, int gid, int argc) +int paw_call_global(paw_Env *P, int gid, int argc) { + // a1..an f => f a1..an paw_get_global(P, gid); - paw_insert(P, -argc - 2); - paw_call(P, argc); + paw_insert(P, -argc - 1); + return paw_call(P, argc); } static int upvalue_index(int nup, int index) @@ -316,23 +340,23 @@ static int upvalue_index(int nup, int index) void paw_get_upvalue(paw_Env *P, int ifn, int index) { - Value *pv = pawC_push0(P); Object *o = at(P, ifn)->o; switch (o->gc_kind) { case VNATIVE: { Native *f = O_NATIVE(o); - *pv = f->up[upvalue_index(f->nup, index)]; + *P->top.p = f->up[upvalue_index(f->nup, index)]; break; } case VCLOSURE: { Closure *f = O_CLOSURE(o); UpValue *u = f->up[upvalue_index(f->nup, index)]; - *pv = *u->p.p; + *P->top.p = *u->p.p; break; } default: pawR_error(P, PAW_ETYPE, "type of object has no upvalues"); } + API_INCR_TOP(P, 1); } void paw_new_list(paw_Env *P, int n) @@ -347,7 +371,8 @@ void paw_new_map(paw_Env *P, int n) void *paw_new_foreign(paw_Env *P, size_t size, int nfields) { - Foreign *ud = pawV_push_foreign(P, size, nfields); + Foreign *ud = pawV_new_foreign(P, size, nfields, P->top.p); + API_INCR_TOP(P, 1); return ud->data; } @@ -392,7 +417,7 @@ void paw_shift(paw_Env *P, int n) paw_pop(P, n); } -#define CAST_ARITH2(op) CAST(op - PAW_ARITH_ADD, enum ArithOp2) +#define CAST_ARITH2(op) CAST(enum ArithOp2, op - PAW_ARITH_ADD) _Static_assert(ARITH2_ADD == 0, "CAST_ARITH2 is incorrect"); void paw_arithi(paw_Env *P, enum paw_ArithOp op) @@ -427,54 +452,59 @@ void paw_arithf(paw_Env *P, enum paw_ArithOp op) void paw_cmpi(paw_Env *P, enum paw_CmpOp op) { - pawR_cmpi(P, CAST(op, enum CmpOp)); + pawR_cmpi(P, CAST(enum CmpOp, op)); } void paw_cmpf(paw_Env *P, enum paw_CmpOp op) { - pawR_cmpf(P, CAST(op, enum CmpOp)); + pawR_cmpf(P, CAST(enum CmpOp, op)); } void paw_cmps(paw_Env *P, enum paw_CmpOp op) { - pawR_cmps(P, CAST(op, enum CmpOp)); + pawR_cmps(P, CAST(enum CmpOp, op)); } -#define CAST_BITW2(op) CAST(op - PAW_BITW_XOR, enum BitwOp2) +#define CAST_BITW2(op) CAST(enum BitwOp2, op - PAW_BITW_XOR) _Static_assert(BITW2_XOR == 0, "CAST_BITW2 is incorrect"); void paw_bitw(paw_Env *P, enum paw_BitwOp op) { switch (op) { case PAW_BITW_NOT: - pawR_bitwi1(P, BITW1_NOT); + pawR_bitw1(P, BITW1_NOT); break; case PAW_BITW_XOR: case PAW_BITW_AND: case PAW_BITW_OR: case PAW_BITW_SHL: case PAW_BITW_SHR: - pawR_bitwi2(P, CAST_BITW2(op)); + pawR_bitw2(P, CAST_BITW2(op)); } } void paw_boolop(paw_Env *P, enum paw_BoolOp op) { - pawR_boolop(P, CAST(op, enum BoolOp)); + pawR_boolop(P, CAST(enum BoolOp, op)); } void paw_strop(paw_Env *P, enum paw_StrOp op) { - pawR_strop(P, CAST(op, enum StrOp)); + pawR_strop(P, CAST(enum StrOp, op)); } void paw_listop(paw_Env *P, enum paw_ListOp op) { - pawR_listop(P, CAST(op, enum ListOp)); + pawR_listop(P, CAST(enum ListOp, op)); } void paw_mapop(paw_Env *P, enum paw_MapOp op) { - pawR_mapop(P, CAST(op, enum MapOp)); + pawR_mapop(P, CAST(enum MapOp, op)); } +const char *paw_to_string(paw_Env *P, int index, paw_Type type, size_t *plen) +{ + Value *pv = at(P, index); + return pawV_to_string(P, pv, type, plen); +} diff --git a/src/api.h b/src/api.h index 5250bdc..516de1d 100644 --- a/src/api.h +++ b/src/api.h @@ -6,6 +6,26 @@ #define PAW_API_H #include "paw.h" -#include "value.h" +#include "util.h" + +// Public API checks based off those in Lua + +#if defined(PAW_USE_API_CHECK) +# include +# define API_CHECK(P, e, msg) assert(e) +#else +# define API_CHECK(P, e, msg) ((void)(P), paw_assert((e) && msg)) +#endif + +#define API_INCR_TOP(P, n) \ + ((P)->top.p += (n), API_CHECK(P, (P)->top.p <= (P)->cf->top.p, "stack overflow")) + +#define API_CHECK_PUSH(P, n) \ + API_CHECK(P, (n) < ((P)->top.p - (P)->cf->base.p), \ + "stack is too large for push") + +#define API_CHECK_POP(P, n) \ + API_CHECK(P, (n) < (P)->top.p - (P)->cf->base.p, \ + "stack is not large enough for pop") #endif // PAW_API_H diff --git a/src/ast.c b/src/ast.c index ac1e2b3..4a92707 100644 --- a/src/ast.c +++ b/src/ast.c @@ -50,6 +50,8 @@ struct AstSegment *pawAst_segment_new(struct Ast *ast) return pawK_pool_alloc(ENV(ast), &ast->pool, sizeof(struct AstSegment)); } +#if defined(PAW_DEBUG_EXTRA) + typedef struct Printer { Buffer *buf; paw_Env *P; @@ -128,7 +130,7 @@ static void dump_decl(Printer *P, struct AstDecl *d) DUMP_FMT(P, "line: %d\n", d->hdr.line); switch (AST_KINDOF(d)) { case kAstFuncDecl: - DUMP_FMT(P, "receiver: %p\n", CAST(d->func.receiver, void *)); + DUMP_FMT(P, "receiver: %p\n", CAST(void *, d->func.receiver)); DUMP_FMT(P, "name: %s\n", d->func.name->text); dump_decl_list(P, d->func.generics, "generics"); dump_decl_list(P, d->func.params, "params"); @@ -376,3 +378,5 @@ void pawAst_dump(struct Ast *ast) } pawL_push_result(P, &buf); } + +#endif diff --git a/src/ast.h b/src/ast.h index 37cadb8..8893ed9 100644 --- a/src/ast.h +++ b/src/ast.h @@ -446,9 +446,9 @@ struct AstDecl *pawAst_new_decl(struct Ast *ast, int line, enum AstDeclKind kind struct AstExpr *pawAst_new_expr(struct Ast *ast, int line, enum AstExprKind kind); struct AstStmt *pawAst_new_stmt(struct Ast *ast, int line, enum AstStmtKind kind); -#define AST_CAST_DECL(x) CAST(x, struct AstDecl *) -#define AST_CAST_EXPR(x) CAST(x, struct AstExpr *) -#define AST_CAST_STMT(x) CAST(x, struct AstStmt *) +#define AST_CAST_DECL(x) CAST(struct AstDecl *, x) +#define AST_CAST_EXPR(x) CAST(struct AstExpr *, x) +#define AST_CAST_STMT(x) CAST(struct AstStmt *, x) DEFINE_LIST(struct Ast, pawAst_decl_list_, AstDeclList, struct AstDecl) DEFINE_LIST(struct Ast, pawAst_expr_list_, AstExprList, struct AstExpr) diff --git a/src/auxlib.c b/src/auxlib.c index 58279e5..7b29265 100644 --- a/src/auxlib.c +++ b/src/auxlib.c @@ -22,12 +22,10 @@ static void grow_buffer(paw_Env *P, Buffer *buf, int boxloc) } else { // Add a new Foreign 'box' to contain the buffer. Prevents a memory leak // if an error is thrown before the buffer is freed. - Foreign *ud = pawV_push_foreign(P, alloc, 0); + Value *pbox = &P->top.p[boxloc]; + Foreign *ud = pawV_new_foreign(P, alloc, 0, pbox); memcpy(ud->data, buf->stack, buf->size); buf->data = ud->data; - Value *pbox = &P->top.p[boxloc - 1]; - V_SET_OBJECT(pbox, ud); - paw_pop(P, 1); } buf->alloc = alloc; } @@ -90,29 +88,33 @@ void pawL_add_nstring(paw_Env *P, Buffer *buf, const char *s, size_t n) add_nstring(P, buf, s, n, -1); } -void pawL_add_integer(paw_Env *P, Buffer *buf, paw_Int i) +void pawL_add_value(paw_Env *P, Buffer *buf, paw_Type type) { - Value v; - V_SET_INT(&v, i); + size_t len; + const char *str = pawV_to_string(P, &P->top.p[-1], type, &len); + add_nstring(P, buf, str, len, -2); + paw_pop(P, 1); +} +static void add_int(paw_Env *P, Buffer *buf, paw_Int i) +{ size_t len; - const char *str = pawV_to_string(P, v, PAW_TINT, &len); + paw_push_int(P, i); + const char *str = paw_to_string(P, -1, PAW_TINT, &len); add_nstring(P, buf, str, len, -2); // string above box - pawC_stkdec(P, 1); + paw_pop(P, 1); } -void pawL_add_float(paw_Env *P, Buffer *buf, paw_Float f) +static void add_float(paw_Env *P, Buffer *buf, paw_Float f) { - Value v; - V_SET_FLOAT(&v, f); - size_t len; - const char *str = pawV_to_string(P, v, PAW_TFLOAT, &len); + paw_push_float(P, f); + const char *str = paw_to_string(P, -1, PAW_TFLOAT, &len); add_nstring(P, buf, str, len, -2); // string above box - pawC_stkdec(P, 1); + paw_pop(P, 1); } -void pawL_add_pointer(paw_Env *P, Buffer *buf, void *p) +static void add_pointer(paw_Env *P, Buffer *buf, void *p) { char temp[32]; const int n = snprintf(temp, sizeof(temp), "%p", p); @@ -122,9 +124,7 @@ void pawL_add_pointer(paw_Env *P, Buffer *buf, void *p) static const char *add_non_fmt(paw_Env *P, Buffer *buf, const char *ptr) { const char *p = ptr; - while (*p && *p != '%') { - ++p; - } + while (*p && *p != '%') ++p; if (p != ptr) { pawL_add_nstring(P, buf, ptr, CAST_SIZE(p - ptr)); ptr = p; @@ -136,11 +136,8 @@ void pawL_add_vfstring(paw_Env *P, Buffer *buf, const char *fmt, va_list arg) { for (;; ++fmt) { fmt = add_non_fmt(P, buf, fmt); - if (!*fmt) { - break; - } - // Skip '%'. - ++fmt; + if (*fmt == '\0') break; + ++fmt; // skip '%' switch (*fmt) { case 's': { @@ -152,22 +149,22 @@ void pawL_add_vfstring(paw_Env *P, Buffer *buf, const char *fmt, va_list arg) pawL_add_char(P, buf, '%'); break; case 'u': - pawL_add_integer(P, buf, va_arg(arg, unsigned)); + add_int(P, buf, va_arg(arg, unsigned)); break; case 'd': - pawL_add_integer(P, buf, va_arg(arg, int)); + add_int(P, buf, va_arg(arg, int)); break; case 'I': - pawL_add_integer(P, buf, va_arg(arg, int64_t)); + add_int(P, buf, va_arg(arg, int64_t)); break; case 'c': pawL_add_char(P, buf, va_arg(arg, int)); break; case 'f': - pawL_add_float(P, buf, va_arg(arg, double)); + add_float(P, buf, va_arg(arg, double)); break; case 'p': - pawL_add_pointer(P, buf, va_arg(arg, void *)); + add_pointer(P, buf, va_arg(arg, void *)); break; default: pawR_error(P, PAW_EVALUE, "invalid format option '%%%c'", *fmt); diff --git a/src/auxlib.h b/src/auxlib.h index e9337b0..bcd350d 100644 --- a/src/auxlib.h +++ b/src/auxlib.h @@ -32,9 +32,7 @@ void pawL_buffer_resize(paw_Env *P, Buffer *buf, size_t n); void pawL_add_char(paw_Env *P, Buffer *buf, char c); void pawL_add_string(paw_Env *P, Buffer *buf, const char *s); void pawL_add_nstring(paw_Env *P, Buffer *buf, const char *s, size_t n); -void pawL_add_integer(paw_Env *P, Buffer *buf, paw_Int i); -void pawL_add_float(paw_Env *P, Buffer *buf, paw_Float f); -void pawL_add_pointer(paw_Env *P, Buffer *buf, void *p); +void pawL_add_value(paw_Env *P, Buffer *buf, paw_Type type); void pawL_add_vfstring(paw_Env *P, Buffer *buf, const char *fmt, va_list arg); void pawL_add_fstring(paw_Env *P, Buffer *buf, const char *fmt, ...); diff --git a/src/call.c b/src/call.c index 3b5f9c5..6e8c15e 100644 --- a/src/call.c +++ b/src/call.c @@ -3,8 +3,11 @@ // LICENSE.md. See AUTHORS.md for a list of contributor names. #include "prefix.h" +#include +#include #include "call.h" +#include "alloc.h" #include "env.h" #include "gc.h" #include "map.h" @@ -13,14 +16,12 @@ #include "rt.h" #include "str.h" #include "util.h" -#include -#include // Lua-style error handling #define THROW(P, c) longjmp((c)->jmp, 1) #define TRY(P, c, a) if (!setjmp((c)->jmp)) {a} -#define FRAME_SIZE 512 +#define CFRAME_SIZE 512 struct Jump { struct Jump *prev; @@ -56,11 +57,11 @@ static void finish_resize(paw_Env *P) void pawC_stack_realloc(paw_Env *P, int n) { - _Static_assert(PAW_STACK_MAX >= FRAME_SIZE, "stack limit is too small"); + _Static_assert(PAW_STACK_MAX >= CFRAME_SIZE, "stack limit is too small"); _Static_assert(PAW_STACK_MAX <= INT_MAX, "stack limit is too large"); paw_assert(n >= pawC_stklen(P)); // don't lose live values - const size_t alloc = PAW_MAX(CAST_SIZE(n), FRAME_SIZE); + const size_t alloc = PAW_MAX(CAST_SIZE(n), CFRAME_SIZE); if (alloc > PAW_STACK_MAX) pawM_error(P); // Turn off emergency GC and convert pointers into the stack into offsets @@ -79,7 +80,7 @@ void pawC_stack_realloc(paw_Env *P, int n) pawM_error(P); // out of memory } // Cause the 'bound' pointer to be placed at the new end of the stack. - P->bound.d = CAST(alloc, ptrdiff_t); + P->bound.d = CAST(ptrdiff_t, alloc); P->stack.p = stack; finish_resize(P); } @@ -103,7 +104,7 @@ void pawC_stack_grow(paw_Env *P, int n) { paw_assert(n > 0); paw_assert(P->bound.p >= P->stack.p); - const int alloc = CAST(P->bound.p - P->stack.p, int); + const int alloc = CAST(int, P->bound.p - P->stack.p); pawC_stack_realloc(P, NEXT_ALLOC(alloc, n)); } @@ -152,17 +153,17 @@ static void call_return(paw_Env *P, StackPtr base, paw_Bool has_return) static void handle_ccall(paw_Env *P, StackPtr base, Native *ccall) { const ptrdiff_t offset = SAVE_OFFSET(P, base); - ENSURE_STACK(P, FRAME_SIZE + STACK_EXTRA); + ENSURE_STACK(P, CFRAME_SIZE + STACK_EXTRA); base = RESTORE_POINTER(P, offset); // The C function may cause the stack to be reallocated. Save the relative // position of 'base' so it can be restored after the call. CallFrame *cf = next_call_frame(P, P->top.p); + cf->base.p = base; + cf->top.p = base + CFRAME_SIZE + STACK_EXTRA; cf->flags = CFF_C; cf->pc = NULL; cf->fn = NULL; - cf->base.p = base; - cf->top.p = base; // call the C function const int nret = ccall->func(P); @@ -197,7 +198,8 @@ CallFrame *pawC_precall(paw_Env *P, StackPtr base, Object *callable, int argc) } Proto *p = fn->p; const ptrdiff_t offset = SAVE_OFFSET(P, base); - ENSURE_STACK(P, p->max_stack + STACK_EXTRA); + const size_t frame_size = p->max_stack + STACK_EXTRA; + ENSURE_STACK(P, frame_size); base = RESTORE_POINTER(P, offset); CallFrame *cf = next_call_frame(P, P->top.p); @@ -205,10 +207,10 @@ CallFrame *pawC_precall(paw_Env *P, StackPtr base, Object *callable, int argc) cf->flags = 0; cf->pc = p->source; cf->base.p = base; - cf->top.p = base; + cf->top.p = base + frame_size; cf->fn = fn; - P->modname = p->modname; // TODO: what about C functions called first? + P->modname = p->modname; check_fixed_args(P, p, argc); return cf; @@ -227,7 +229,7 @@ void pawC_call(paw_Env *P, Object *f, int argc) } } -static int exceptional_call(paw_Env *P, Call call, void *arg) +int pawC_raw_try(paw_Env *P, Call call, void *arg) { struct Jump jmp = { .status = PAW_OK, @@ -243,7 +245,7 @@ int pawC_try(paw_Env *P, Call call, void *arg) { CallFrame *cf = P->cf; const ptrdiff_t top = SAVE_OFFSET(P, P->top.p); - const int status = exceptional_call(P, call, arg); + const int status = pawC_raw_try(P, call, arg); if (status != PAW_OK) { paw_assert(top <= SAVE_OFFSET(P, P->top.p)); StackPtr ptr = RESTORE_POINTER(P, top); @@ -261,23 +263,27 @@ int pawC_try(paw_Env *P, Call call, void *arg) _Noreturn void pawC_throw(paw_Env *P, int error) { - if (P->jmp == NULL) { - abort(); - } + if (P->jmp == NULL) abort(); P->jmp->status = error; THROW(P, P->jmp); } void pawC_init(paw_Env *P) { - P->stack.p = pawM_new_vec(P, FRAME_SIZE + STACK_EXTRA, Value); - P->bound.p = P->stack.p + FRAME_SIZE + STACK_EXTRA; - P->top.p = P->stack.p; + const int frame_size = CFRAME_SIZE + STACK_EXTRA; + // allocate manually since normal error handling code requires the stack (this is the + // stack allocation itself) + Value *ptr = pawZ_alloc(P, NULL, frame_size * sizeof(Value)); + if (ptr == NULL) pawC_throw(P, PAW_EMEMORY); + P->bound.p = ptr + frame_size; + P->stack.p = ptr; + P->top.p = ptr; // Set up the main call frame. - P->main.base.p = P->stack.p; - P->main.top.p = P->main.base.p; - P->main.fn = NULL; + P->main = (CallFrame){ + .base.p = P->stack.p, + .top.p = P->bound.p, + }; P->cf = &P->main; } @@ -289,7 +295,7 @@ void pawC_uninit(paw_Env *P) StackPtr pawC_return(paw_Env *P, int nret) { - StackPtr base = cf_stack_return(P->cf); + StackPtr base = CF_STACK_RETURN(P->cf); StackPtr top = P->top.p; for (int i = 0; i < nret; ++i) { base[i] = *--top; diff --git a/src/call.h b/src/call.h index 20a9610..a202cff 100644 --- a/src/call.h +++ b/src/call.h @@ -9,7 +9,7 @@ #include "paw.h" #include "value.h" -#define STACK_EXTRA 1 /* number of slots reserved for errors */ +#define STACK_EXTRA 25 /* slots for error handling */ #define SAVE_OFFSET(P, ptr) ((ptr) - (P)->stack.p) #define RESTORE_POINTER(P, ofs) ((P)->stack.p + (ofs)) #define ENSURE_STACK(P, n) \ @@ -22,6 +22,7 @@ typedef void (*Call)(paw_Env *P, void *arg); CallFrame *pawC_precall(paw_Env *P, StackPtr base, Object *callable, int argc); StackPtr pawC_return(paw_Env *P, int nret); int pawC_try(paw_Env *P, Call call, void *arg); +int pawC_raw_try(paw_Env *P, Call call, void *arg); _Noreturn void pawC_throw(paw_Env *P, int error); void pawC_call(paw_Env *P, Object *f, int argc); void pawC_init(paw_Env *P); @@ -33,7 +34,7 @@ void pawC_stack_overflow(paw_Env *P); static inline int pawC_stklen(paw_Env *P) { - return CAST(P->top.p - P->stack.p, int); + return CAST(int, P->top.p - P->stack.p); } // Increase the stack size @@ -41,25 +42,17 @@ static inline int pawC_stklen(paw_Env *P) // collection runs. static inline StackPtr pawC_stkinc(paw_Env *P, int n) { - paw_assert(n <= CAST(P->bound.p - P->top.p, int)); - StackPtr sp = P->top.p; - P->top.p += n; - return sp; + paw_assert(n <= CAST(int, P->bound.p - P->top.p)); + return (P->top.p += n) - n; } // Decrease the stack size static inline void pawC_stkdec(paw_Env *P, int n) { + paw_assert(n <= pawC_stklen(P)); P->top.p -= n; } -static inline Value *pawC_pushv(paw_Env *P, Value v) -{ - StackPtr sp = pawC_stkinc(P, 1); - *sp = v; - return sp; -} - static inline Value *pawC_push0(paw_Env *P) { StackPtr sp = pawC_stkinc(P, 1); @@ -67,34 +60,6 @@ static inline Value *pawC_push0(paw_Env *P) return sp; } -static inline Value *pawC_pushi(paw_Env *P, paw_Int i) -{ - StackPtr sp = pawC_stkinc(P, 1); - V_SET_INT(sp, i); - return sp; -} - -static inline Value *pawC_pushf(paw_Env *P, paw_Float f) -{ - StackPtr sp = pawC_stkinc(P, 1); - V_SET_FLOAT(sp, f); - return sp; -} - -static inline Value *pawC_pushb(paw_Env *P, paw_Bool b) -{ - StackPtr sp = pawC_stkinc(P, 1); - V_SET_BOOL(sp, b); - return sp; -} - -static inline Value *pawC_pusho(paw_Env *P, Object *o) -{ - StackPtr sp = pawC_stkinc(P, 1); - V_SET_OBJECT(sp, o); - return sp; -} - Value *pawC_pushns(paw_Env *P, const char *s, size_t n); Value *pawC_pushs(paw_Env *P, const char *s); diff --git a/src/code.h b/src/code.h index d7fec00..f6a6989 100644 --- a/src/code.h +++ b/src/code.h @@ -62,7 +62,7 @@ void pawK_pool_free(struct Pool *pool, void *ptr, size_t size); void *next = pawK_pool_alloc(X->P, &X->pool, bufsz); \ const size_t usedsz = CAST_SIZE(list->count) * elemsz; \ memcpy(next, list->data, usedsz); \ - list->alloc = CAST(nextcap, int); \ + list->alloc = CAST(int, nextcap); \ list->data = next; \ } \ list->data[list->count++] = node; \ diff --git a/src/codegen.c b/src/codegen.c index 3dee741..9711f57 100644 --- a/src/codegen.c +++ b/src/codegen.c @@ -17,7 +17,7 @@ #include "mem.h" #include "parse.h" -#define SYNTAX_ERROR(G, ...) pawE_error(ENV(G), PAW_ESYNTAX, (G)->fs->line, __VA_ARGS__) +#define ERROR(G, code, ...) pawE_error(ENV(G), code, (G)->fs->line, __VA_ARGS__) #define GET_DECL(G, id) pawHir_get_decl((G)->hir, id) #define TYPE_CODE(G, type) (pawP_type2code((G)->C, type)) @@ -137,10 +137,10 @@ static int add_constant(struct Generator *G, Value v, paw_Type code) // ensure uniqueness of constant values per function Map *kmap = kcache_map(fs, code); Value *pk = pawH_get(kmap, v); - if (pk != NULL) return CAST(pk->i, int); + if (pk != NULL) return CAST(int, pk->i); if (fs->nk == CONSTANT_MAX) { - SYNTAX_ERROR(G, "too many constants"); + ERROR(G, PAW_ESYNTAX, "too many constants"); } pawM_grow(ENV(G), p->k, fs->nk, p->nk); p->k[fs->nk] = v; @@ -155,7 +155,7 @@ static int add_proto(struct Generator *G, Proto *proto) struct FuncState *fs = G->fs; Proto *p = fs->proto; if (fs->nproto == ITEM_MAX) { - SYNTAX_ERROR(G, "too many functions"); + ERROR(G, PAW_ESYNTAX, "too many functions"); } else if (fs->nproto == p->nproto) { pawM_grow(ENV(G), p->p, fs->nproto, p->nproto); for (int i = fs->nproto; i < p->nproto; ++i) { @@ -228,7 +228,7 @@ static struct HirVarInfo declare_local(struct FuncState *fs, String *name, struc static void begin_local_scope(struct FuncState *fs, int n) { if (fs->nlocals > LOCAL_MAX - n) { - SYNTAX_ERROR(fs->G, "too many locals"); + ERROR(fs->G, PAW_ESYNTAX, "too many locals"); } fs->nlocals += n; } @@ -271,7 +271,7 @@ static void patch_jump(struct FuncState *fs, int from, int to) { const int jump = to - (from + 1); if (jump > JUMP_MAX) { - SYNTAX_ERROR(fs->G, "too many instructions to jump"); + ERROR(fs->G, PAW_ESYNTAX, "too many instructions to jump"); } Proto *p = fs->proto; SET_S(&p->source[from], jump); @@ -286,7 +286,7 @@ static void code_loop(struct FuncState *fs, Op op, int to) { const int jump = to - (fs->pc + 1); if (jump < -JUMP_MAX) { - SYNTAX_ERROR(fs->G, "too many instructions in loop"); + ERROR(fs->G, PAW_ESYNTAX, "too many instructions in loop"); } pawK_code_S(fs, op, jump); } @@ -414,6 +414,9 @@ static void leave_function(struct Generator *G) struct BlockState *bs = fs->bs; Proto *p = fs->proto; + // module itself has no prototype + if (fs->kind == FUNC_MODULE) return; + // end function-scoped locals end_local_scope(fs, bs); paw_assert(fs->nlocals == 0); @@ -446,7 +449,7 @@ static void enter_function(struct Generator *G, struct FuncState *fs, struct Blo { *fs = (struct FuncState){ .first_local = G->C->dm->vars.count, - .scopes = pawHir_new_symtab(G->hir), + .scopes = pawHir_symtab_new(G->hir), .proto = proto, .outer = G->fs, .name = name, @@ -494,9 +497,15 @@ static struct HirVarInfo resolve_attr(struct Generator *G, struct HirType *type, paw_assert(HirIsAdt(type)); struct HirDecl *decl = GET_DECL(G, type->adt.base); struct HirAdtDecl *adt = &decl->adt; - struct HirScope *scope = adt->scope; - const int index = pawHir_find_symbol(scope, name); - paw_assert(index >= 0); // already found + int index; // must exist: found in last pass + for (int i = 0; i < adt->fields->count; ++i) { + struct HirDecl *decl = adt->fields->data[i]; + struct HirFieldDecl *field = HirGetFieldDecl(decl); + if (pawS_eq(field->name, name)) { + index = i; + break; + } + } return (struct HirVarInfo){ .kind = VAR_FIELD, .index = index, @@ -515,7 +524,7 @@ static void add_upvalue(struct FuncState *fs, struct HirVarInfo *info, paw_Bool } } if (fs->nup == UPVALUE_MAX) { - SYNTAX_ERROR(fs->G, "too many upvalues"); + ERROR(fs->G, PAW_ESYNTAX, "too many upvalues"); } pawM_grow(ENV(fs->G), f->u, fs->nup, f->nup); f->u[fs->nup] = (struct UpValueInfo){ @@ -556,7 +565,7 @@ static struct HirVarInfo find_var(struct Generator *G, const String *name) return info; } -#define CODE_OP(fs, op, subop) pawK_code_U(fs, op, CAST(subop, int)) +#define CODE_OP(fs, op, subop) pawK_code_U(fs, op, CAST(int, subop)) // Get the length of the container object on top of the stack static void code_len(struct FuncState *fs, paw_Type type); @@ -1094,27 +1103,15 @@ static void code_instance_getter(struct HirVisitor *V, struct HirType *type) const String *name = decl->hdr.name; const Value k = {.o = CAST_OBJECT(name)}; - const Value *pv = pawH_get(P->builtin, k); + // builtins are not monomorphized: there is a single C function implementing each + // builtin function that might handle some paramenters generically + const Value *pv = pawH_get(P->builtin, k); name = mangle_name(G, name, pv ? NULL : type->fdef.types); const struct HirVarInfo info = find_var(G, name); code_getter(V, info); } -struct VariantInfo { - struct HirScope *fields; - int choice; -}; - -static struct VariantInfo unpack_variant(struct Generator *G, struct HirType *type) -{ - struct HirDecl *decl = GET_DECL(G, type->fdef.did); - return (struct VariantInfo){ - .fields = decl->variant.scope, - .choice = decl->variant.index, - }; -} - static paw_Bool is_instance_call(const struct HirType *type) { return HirIsFuncDef(type) && type->fdef.types != NULL; @@ -1136,13 +1133,14 @@ static void code_variant_constructor(struct HirVisitor *V, struct HirType *type, struct FuncState *fs = G->fs; int count = 0; - struct VariantInfo info = unpack_variant(G, type); + struct HirDecl *decl = GET_DECL(G, HirGetFuncDef(type)->did); + struct HirVariantDecl *d = HirGetVariantDecl(decl); if (args != NULL) { V->VisitExprList(V, args); count = args->count; paw_assert(count > 0); } - pawK_code_AB(fs, OP_NEWVARIANT, info.choice, count); + pawK_code_AB(fs, OP_NEWVARIANT, d->index, count); } static void code_path_expr(struct HirVisitor *V, struct HirPathExpr *e) @@ -1431,23 +1429,30 @@ static void set_cfunc(struct Generator *G, String *name, int g) const Value k = {.o = CAST_OBJECT(name)}; const Value *pv = pawH_get(P->builtin, k); + if (pv == NULL) ERROR(G, PAW_ENAME, "C function '%s' not loaded", name->text); *pval = *pv; } static void code_items(struct HirVisitor *V) { + struct FuncState fs; + struct BlockState bs; struct Generator *G = V->ud; struct ToplevelList *items = G->items; + String *name = SCAN_STRING(G->C, "(toplevel)"); + enter_function(G, &fs, &bs, name, NULL, NULL, FUNC_MODULE); for (int i = 0; i < items->count; ++i) { struct ItemSlot *item = items->data[i]; if (!HirIsFuncDecl(item->decl)) continue; struct HirFuncDecl *d = HirGetFuncDecl(item->decl); + fs.line = d->line; if (item->info.kind == VAR_CFUNC) { set_cfunc(G, d->name, item->info.index); } else { code_func(V, d, item->info); } } + leave_function(G); } static void setup_pass(struct HirVisitor *V, struct Generator *G) @@ -1492,7 +1497,6 @@ void pawP_codegen(struct Compiler *C, struct Hir *hir) struct Generator G = { .hir = hir, .sym = hir->symtab, - .globals = hir->symtab->globals, .items = item_list_new(hir), .P = P, .C = C, diff --git a/src/compile.c b/src/compile.c index cd5d3da..2736ccd 100644 --- a/src/compile.c +++ b/src/compile.c @@ -8,6 +8,57 @@ #include "hir.h" #include "map.h" +static const char *kKeywords[] = { + "pub", + "fn", + "type", + "enum", + "struct", + "let", + "if", + "else", + "for", + "do", + "while", + "break", + "continue", + "return", + "in", + "as", + "true", + "false", +}; + +static String *basic_type_name(paw_Env *P, const char *name, paw_Type code) +{ + String *s = pawS_new_fixed(P, name); + s->flag = FLAG2CODE(code); // works either direction + return s; +} + +void pawP_init(paw_Env *P) +{ + // Add all keywords to the interned strings table. Fix them so they are + // never collected. Also added to the lexer string map. + for (size_t i = 0; i < paw_countof(kKeywords); ++i) { + const char *kw = kKeywords[i]; + String *str = pawS_new_fixed(P, kw); + str->flag = i + FIRST_KEYWORD; + } + // note that keywords are already fixed + P->str_cache[CSTR_SELF] = pawS_new_str(P, "self"); + P->str_cache[CSTR_TRUE] = pawS_new_str(P, "true"); + P->str_cache[CSTR_FALSE] = pawS_new_str(P, "false"); + P->str_cache[CSTR_BOOL] = basic_type_name(P, "bool", PAW_TBOOL); + P->str_cache[CSTR_INT] = basic_type_name(P, "int", PAW_TINT); + P->str_cache[CSTR_FLOAT] = basic_type_name(P, "float", PAW_TFLOAT); + P->str_cache[CSTR_STR] = basic_type_name(P, "str", PAW_TSTR); + P->str_cache[CSTR_LIST] = pawS_new_fixed(P, "_List"); + P->str_cache[CSTR_MAP] = pawS_new_fixed(P, "_Map"); + P->str_cache[CSTR_OPTION] = pawS_new_fixed(P, "Option"); + P->str_cache[CSTR_RESULT] = pawS_new_fixed(P, "Result"); +} + paw_Type pawP_type2code(struct Compiler *C, struct HirType *type) { if (HirIsAdt(type)) { diff --git a/src/compile.h b/src/compile.h index 0150dd7..3255166 100644 --- a/src/compile.h +++ b/src/compile.h @@ -109,7 +109,9 @@ struct Resolver { struct HirType *adt; // enclosing ADT struct HirType *result; // enclosing function return type struct HirSymtab *symtab; // scoped symbol table + struct HirScope *globals; struct DynamicMem *dm; // dynamic memory + struct LazyItemList *items; int func_depth; // number of nested functions int nresults; int list_gid; @@ -150,7 +152,6 @@ struct DynamicMem { typedef struct Generator { struct Compiler *C; struct HirSymtab *sym; - struct HirScope *globals; struct Hir *hir; struct FuncState *fs; struct ToplevelList *items; diff --git a/src/config.h b/src/config.h index 43989e1..9e9cc0a 100644 --- a/src/config.h +++ b/src/config.h @@ -31,10 +31,6 @@ # define PAW_HEAP_DEFAULT ((size_t)33554432) #endif -#ifndef PAW_HEAP_MIN -# define PAW_HEAP_MIN ((size_t)262144) -#endif - #if defined(__APPLE__) # define PAW_OS_MACOS # define PAW_OS_POSIX @@ -61,4 +57,14 @@ _Noreturn static inline void paw_unreachable_(void) _Noreturn static inline void paw_unreachable_(void) {} #endif +#if !defined(PAW_LIKELY) +# if defined(__GNUC__) || defined(__clang__) +# define P_LIKELY(x) (__builtin_expect((x) != 0, 1)) +# define P_UNLIKELY(x) (__builtin_expect((x) != 0, 0)) +# else +# define PAW_LIKELY(x) (x) +# define PAW_UNLIKELY(x) (x) +# endif +#endif + #endif // PAW_CONFIG_H diff --git a/src/debug.c b/src/debug.c index a1ba547..339fcfe 100644 --- a/src/debug.c +++ b/src/debug.c @@ -12,6 +12,24 @@ #include #include +#define PC_REL(p, pc) CAST(int, (pc) - (p)->source - 1) + +int pawD_line_number(const CallFrame *cf, const OpCode *pc) +{ + if (!CF_IS_PAW(cf)) return -1; + + int i = 0; + Proto *p = cf->fn->p; + const int r = PC_REL(p, pc); + for (; i < p->nlines - 1; ++i) { + if (p->lines[i].pc >= r) break; + } + return p->lines[i].line; +} + +// TODO: Most of this should not be in the core: use hooks for debugging +#if defined(PAW_DEBUG_EXTRA) + const char *paw_unop_name(enum UnaryOp unop) { switch (unop) { @@ -309,9 +327,8 @@ void dump_aux(paw_Env *P, Proto *proto, Buffer *print) } case OP_GETGLOBAL: { - const int iw = GET_U(opcode); - pawL_add_string(P, print, " ; id = "); - pawL_add_integer(P, print, iw); + const int u = GET_U(opcode); + pawL_add_fstring(P, print, " ; id = %d", u); break; } @@ -413,7 +430,7 @@ void paw_stacktrace(paw_Env *P) pawL_add_fstring(P, &buf, "%d: File ", i); pawL_add_nstring(P, &buf, modname->text, modname->length); pawL_add_fstring(P, &buf, ", line %d, in ", current_line(cf)); - if (cf_is_paw(cf)) { + if (CF_IS_PAW(cf)) { Proto *p = cf->fn->p; const String *name = p->name; pawL_add_nstring(P, &buf, name->text, name->length); @@ -425,3 +442,5 @@ void paw_stacktrace(paw_Env *P) } pawL_push_result(P, &buf); } + +#endif // defined(PAW_DEBUG_EXTRA) diff --git a/src/debug.h b/src/debug.h index 8e074b3..53b4043 100644 --- a/src/debug.h +++ b/src/debug.h @@ -8,6 +8,8 @@ #include "env.h" #include "paw.h" +int pawD_line_number(const CallFrame *cf, const OpCode *pc); + const char *paw_op_name(Op op); void paw_dump_opcode(OpCode opcode); void paw_dump_source(paw_Env *P, Proto *proto); diff --git a/src/env.h b/src/env.h index 11c8fae..59af9fa 100644 --- a/src/env.h +++ b/src/env.h @@ -17,9 +17,9 @@ typedef uint16_t ValueId; #define CFF_C 1 #define CFF_ENTRY 2 -#define cf_is_paw(cf) (!((cf)->flags & CFF_C)) -#define cf_is_entry(cf) ((cf)->flags & CFF_ENTRY) -#define cf_stack_return(cf) ((cf)->base.p) +#define CF_IS_PAW(cf) (!((cf)->flags & CFF_C)) +#define CF_IS_ENTRY(cf) ((cf)->flags & CFF_ENTRY) +#define CF_STACK_RETURN(cf) ((cf)->base.p) typedef struct CallFrame { struct CallFrame *prev; diff --git a/src/gc.c b/src/gc.c index f606e61..01643f0 100644 --- a/src/gc.c +++ b/src/gc.c @@ -13,7 +13,7 @@ #include #ifndef PAW_GC_LIMIT -#define PAW_GC_LIMIT (1024 * 1024) +# define PAW_GC_LIMIT (1024 * 1024) #endif static void gc_trace_object(const char *msg, void *ptr) @@ -57,11 +57,9 @@ static Object **get_gc_list(Object *o) return &O_VARIANT(o)->gc_list; case VTUPLE: return &O_TUPLE(o)->gc_list; - case VNATIVE: - return &O_NATIVE(o)->gc_list; default: - paw_assert(o->gc_kind == VMETHOD); - return &(O_METHOD(o))->gc_list; + paw_assert(o->gc_kind == VNATIVE); + return &O_NATIVE(o)->gc_list; } } @@ -252,7 +250,6 @@ static void sweep_phase(paw_Env *P) } } -void pawS_check(paw_Env*P); void pawG_collect(paw_Env *P) { P->gc_gray = NULL; diff --git a/src/hir.c b/src/hir.c index c78f101..392f0e6 100644 --- a/src/hir.c +++ b/src/hir.c @@ -2,12 +2,12 @@ // This source code is licensed under the MIT License, which can be found in // LICENSE.md. See AUTHORS.md for a list of contributor names. +#include +#include #include "hir.h" #include "compile.h" #include "map.h" #include "mem.h" -#include -#include #define LIST_MIN 8 #define FIRST_ARENA_SIZE 4096 @@ -23,7 +23,7 @@ struct Hir *pawHir_new(struct Compiler *C) // initialize memory pools for storing HIR components pawK_pool_init(P, &dm->hir->pool, FIRST_ARENA_SIZE, sizeof(void *) * LARGE_ARENA_MIN); - dm->hir->symtab = pawHir_new_symtab(dm->hir); + dm->hir->symtab = pawHir_symtab_new(dm->hir); return dm->hir; } @@ -87,49 +87,23 @@ struct HirSymbol *pawHir_new_symbol(struct Hir *hir) return pawK_pool_alloc(ENV(hir), &hir->pool, sizeof(struct HirSymbol)); } -static struct HirScope *new_scope(struct Hir *hir) -{ - struct HirScope *scope = pawK_pool_alloc(ENV(hir), &hir->pool, sizeof(struct HirScope)); - scope->symbols = pawHir_symbol_list_new(hir); - return scope; -} - -struct HirSymtab *pawHir_new_symtab(struct Hir *hir) -{ - struct HirSymtab *symtab = pawK_pool_alloc(ENV(hir), &hir->pool, sizeof(struct HirSymtab)); - symtab->globals = new_scope(hir); - symtab->scopes = pawHir_scope_list_new(hir); - return symtab; -} - -struct HirScope *pawHir_new_scope(struct Hir *hir, struct HirSymtab *table) -{ - struct HirScope *scope = new_scope(hir); - pawHir_add_scope(hir, table, scope); - return scope; -} - void pawHir_add_scope(struct Hir *hir, struct HirSymtab *table, struct HirScope *scope) { - if (table->scopes->count == UINT16_MAX) { - pawC_throw(ENV(hir), PAW_EMEMORY); -// limit_error(hir->C, "scopes", UINT16_MAX); // TODO - } - pawHir_scope_list_push(hir, table->scopes, scope); + if (table->count == UINT16_MAX) pawM_error(ENV(hir)); + pawHir_symtab_push(hir, table, scope); } struct HirSymbol *pawHir_add_symbol(struct Hir *hir, struct HirScope *table) { struct HirSymbol *symbol = pawHir_new_symbol(hir); - pawHir_symbol_list_push(hir, table->symbols, symbol); + pawHir_scope_push(hir, table, symbol); return symbol; } int pawHir_find_symbol(struct HirScope *scope, const String *name) { - struct HirSymbolList *list = scope->symbols; - for (int i = list->count - 1; i >= 0; --i) { - const struct HirSymbol *symbol = list->data[i]; + for (int i = scope->count - 1; i >= 0; --i) { + const struct HirSymbol *symbol = scope->data[i]; if (pawS_eq(name, symbol->name)) { if (symbol->is_init) { return i; @@ -274,12 +248,6 @@ static void VisitCallExpr(struct HirVisitor *V, struct HirCallExpr *e) V->VisitExprList(V, e->args); } -static void VisitVariantExpr(struct HirVisitor *V, struct HirVariantExpr *e) -{ - V->VisitTypeList(V, e->types); - V->VisitExprList(V, e->fields); -} - static void VisitConversionExpr(struct HirVisitor *V, struct HirConversionExpr *e) { V->VisitExpr(V, e->arg); @@ -294,7 +262,7 @@ static void VisitFuncDecl(struct HirVisitor *V, struct HirFuncDecl *d) { V->VisitDeclList(V, d->generics); V->VisitDeclList(V, d->params); - V->VisitBlock(V, d->body); + if (d->body != NULL) V->VisitBlock(V, d->body); } static void VisitIfStmt(struct HirVisitor *V, struct HirIfStmt *s) @@ -487,7 +455,7 @@ static struct HirStmt *FoldBlock(struct HirFolder *F, struct HirBlock *s) s->stmts = F->FoldStmtList(F, s->stmts); return HIR_CAST_STMT(s); } -#define FOLD_BLOCK(F, s) CAST((F)->FoldBlock(F, s), struct HirBlock *) +#define FOLD_BLOCK(F, s) CAST(struct HirBlock *, (F)->FoldBlock(F, s)) #define MAYBE_FOLD_BLOCK(F, s) ((s) != NULL ? FOLD_BLOCK(F, s) : NULL) static struct HirExpr *FoldLogicalExpr(struct HirFolder *F, struct HirLogicalExpr *e) @@ -619,13 +587,6 @@ static struct HirExpr *FoldCallExpr(struct HirFolder *F, struct HirCallExpr *e) return HIR_CAST_EXPR(e); } -static struct HirExpr *FoldVariantExpr(struct HirFolder *F, struct HirVariantExpr *e) -{ - e->types = F->FoldTypeList(F, e->types); - e->fields = F->FoldExprList(F, e->fields); - return HIR_CAST_EXPR(e); -} - static struct HirExpr *FoldConversionExpr(struct HirFolder *F, struct HirConversionExpr *e) { e->arg = F->FoldExpr(F, e->arg); @@ -642,7 +603,7 @@ static struct HirDecl *FoldFuncDecl(struct HirFolder *F, struct HirFuncDecl *d) { d->generics = F->FoldDeclList(F, d->generics); d->params = F->FoldDeclList(F, d->params); - d->body = FOLD_BLOCK(F, d->body); + if (d->body != NULL) d->body = FOLD_BLOCK(F, d->body); return HIR_CAST_DECL(d); } @@ -872,7 +833,7 @@ static struct HirStmt *copy_block_stmt(struct HirFolder *F, struct HirBlock *s) return r; } -#define COPY_BLOCK(F, s) CAST((F)->FoldBlock(F, s), struct HirBlock *) +#define COPY_BLOCK(F, s) CAST(struct HirBlock *, (F)->FoldBlock(F, s)) static struct HirExpr *copy_logical_expr(struct HirFolder *F, struct HirLogicalExpr *e) { @@ -1050,16 +1011,6 @@ static struct HirExpr *copy_call_expr(struct HirFolder *F, struct HirCallExpr *e return r; } -static struct HirExpr *copy_variant_expr(struct HirFolder *F, struct HirVariantExpr *e) -{ - struct HirExpr *result = COPY_PREP_EXPR(F, e); - struct HirVariantExpr *r = HirGetVariantExpr(result); - r->types = F->FoldTypeList(F, e->types); - r->fields = F->FoldExprList(F, e->fields); - r->index = e->index; - return result; -} - static struct HirPath *copy_path(struct HirFolder *F, struct HirPath *e) { struct HirPath *r = pawHir_path_new(F->hir); @@ -1085,7 +1036,7 @@ static struct HirDecl *copy_func_decl(struct HirFolder *F, struct HirFuncDecl *d r->func.name = d->name; r->func.generics = F->FoldDeclList(F, d->generics); r->func.params = F->FoldDeclList(F, d->params); - r->func.body = COPY_BLOCK(F, d->body); + if (d->body != NULL) r->func.body = COPY_BLOCK(F, d->body); r->func.monos = F->FoldDeclList(F, d->monos); r->func.fn_kind = d->fn_kind; return r; @@ -1174,7 +1125,6 @@ static void setup_copy_pass(struct HirFolder *F, struct Copier *C) F->FoldBinOpExpr = copy_binop_expr; F->FoldConversionExpr = copy_conversion_expr; F->FoldCallExpr = copy_call_expr; - F->FoldVariantExpr = copy_variant_expr; F->FoldIndex = copy_index_expr; F->FoldSelector = copy_select_expr; F->FoldFieldExpr = copy_field_expr; @@ -1378,21 +1328,6 @@ static struct HirExpr *expand_call_expr(struct HirFolder *F, struct HirCallExpr return HIR_CAST_EXPR(e); } -static struct HirExpr *expand_variant_expr(struct HirFolder *F, struct HirVariantExpr *e) -{ - struct Expander *E = F->ud; - struct HirAdt *adt = HirGetAdt(e->type); - if (e->types != NULL) { - struct HirTypeList *types = F->FoldTypeList(F, e->types); - struct HirDecl *base = pawHir_get_decl(F->hir, adt->base); - struct HirDecl *inst = pawP_instantiate(E->R, base, types); - e->type = HIR_TYPEOF(inst); - e->types = types; - } - e->fields = F->FoldExprList(F, e->fields); - return HIR_CAST_EXPR(e); -} - static struct HirDecl *expand_adt_decl(struct HirFolder *F, struct HirAdtDecl *d) { paw_unused(F); @@ -1457,7 +1392,7 @@ static void enter_def_(struct DefGenerator *dg, struct DefState *ds, struct HirD dg->ds = ds; } -#define ENTER_DEF(dg, ds, decl, def) enter_def_(dg, ds, HIR_CAST_DECL(decl), CAST(def, struct Def *)) +#define ENTER_DEF(dg, ds, decl, def) enter_def_(dg, ds, HIR_CAST_DECL(decl), CAST(struct Def *, def)) #define LEAVE_DEF(dg) ((dg)->ds = (dg)->ds->outer) static struct Type *create_type(struct DefGenerator *dg, struct HirType *type); @@ -1728,6 +1663,8 @@ struct HirDeclList *pawHir_define(struct Compiler *C, struct Hir *hir) return dg.decls; } +#if defined(PAW_DEBUG_EXTRA) + struct Printer { struct Hir *hir; Buffer *buf; @@ -1749,89 +1686,12 @@ static void indent_line(struct Printer *P) #define DUMP_FMT(P, ...) (indent_line(P), pawL_add_fstring(ENV(P), (P)->buf, __VA_ARGS__)) #define DUMP_MSG(P, msg) (indent_line(P), pawL_add_string(ENV(P), (P)->buf, msg)) -static void repr_type_aux(struct Printer *P, struct HirType *type); - -static void repr_typelist(struct Printer *P, struct HirTypeList *list) -{ - for (int i = 0; i < list->count; ++i) { - repr_type_aux(P, list->data[i]); - if (i < list->count - 1) { - DUMP_LITERAL(P, ", "); - } - } -} - static const char *get_typename(struct DynamicMem *dm, DefId did) { const struct HirDecl *decl = dm->decls->data[did]; return decl->hdr.name ? decl->hdr.name->text : "(null)"; } -static void repr_type_aux(struct Printer *P, struct HirType *type) -{ - switch (HIR_KINDOF(type)) { - case kHirTupleType: - DUMP_LITERAL(P, "("); - for (int i = 0; i < type->tuple.elems->count; ++i) { - repr_type_aux(P, type->tuple.elems->data[i]); - if (i < type->tuple.elems->count - 1) { - DUMP_LITERAL(P, ", "); - } - } - DUMP_LITERAL(P, ")"); - break; - case kHirFuncPtr: - case kHirFuncDef: - DUMP_LITERAL(P, "fn"); - if (HirIsFuncDef(type)) { - DUMP_FSTRING(P, " %s", get_typename(P->hir->dm, type->fdef.did)); - } - DUMP_LITERAL(P, "("); - for (int i = 0; i < type->fptr.params->count; ++i) { - repr_type_aux(P, type->fptr.params->data[i]); - if (i < type->fptr.params->count - 1) { - DUMP_LITERAL(P, ", "); - } - } - DUMP_LITERAL(P, ") -> "); - repr_type_aux(P, type->fptr.result); - break; - case kHirAdt: - DUMP_FSTRING(P, "%s", get_typename(P->hir->dm, type->adt.did)); - if (type->adt.types != NULL) { - DUMP_LITERAL(P, "<"); - const struct HirTypeList *list = type->adt.types; - for (int i = 0; i < list->count; ++i) { - repr_type_aux(P, list->data[i]); - if (i < list->count - 1) { - DUMP_LITERAL(P, ", "); - } - } - DUMP_LITERAL(P, ">"); - } - break; - case kHirUnknown: - DUMP_FSTRING(P, "?%d", type->unknown.index); - break; - default: - paw_assert(HirIsGeneric(type)); - DUMP_FSTRING(P, "?%s", type->generic.name->text); - } -} - -static void repr_type(struct Printer *P, struct HirType *type) -{ - if (type != NULL) { - repr_type_aux(P, type); - } else { - DUMP_LITERAL(P, "(null)"); - } - DUMP_LITERAL(P, "\n"); -} - -static void dump_stmt(struct Printer *, struct HirStmt *); -static void dump_expr(struct Printer *, struct HirExpr *); - #define DEFINE_KIND_PRINTER(name, T) \ static int print_##name##_kind(struct Printer *P, void *node) \ { \ @@ -1883,7 +1743,7 @@ static void dump_path(struct Printer *P, struct HirPath *p) if (seg->types != NULL) { DUMP_LITERAL(P, "<"); for (int j = 0; j < seg->types->count; ++j) { - repr_type(P, seg->types->data[i]); + dump_type(P, seg->types->data[i]); if (j < seg->types->count - 1) { DUMP_LITERAL(P, ", "); } @@ -1928,8 +1788,7 @@ static void dump_type(struct Printer *P, struct HirType *t) case kHirUnknown: DUMP_FMT(P, "index: %d\n", t->unknown.index); break; - default: - paw_assert(HirIsGeneric(t)); + case kHirGeneric: DUMP_FMT(P, "name: %s\n", t->generic.name->text); DUMP_FMT(P, "did: %d\n", t->generic.did); } @@ -1989,8 +1848,6 @@ static void dump_decl(struct Printer *P, struct HirDecl *d) DUMP_NAME(P, d->type.name); dump_type_list(P, d->inst.types, "types"); break; - default: - paw_assert(0); } --P->indent; DUMP_MSG(P, "}\n"); @@ -2051,8 +1908,8 @@ static void dump_stmt(struct Printer *P, struct HirStmt *s) DUMP_MSG(P, "expr: "); dump_expr(P, s->result.expr); break; - default: - break; + case kHirLabelStmt: + DUMP_FMT(P, "label: %s\n", s->label.label == LBREAK ? "BREAK" : "CONTINUE"); } --P->indent; DUMP_MSG(P, "}\n"); @@ -2103,8 +1960,7 @@ static void dump_expr(struct Printer *P, struct HirExpr *e) DUMP_MSG(P, "lit_kind: CONTAINER\n"); dump_expr_list(P, e->literal.cont.items, "items"); break; - default: - paw_assert(e->literal.lit_kind == kHirLitComposite); + case kHirLitComposite: DUMP_MSG(P, "lit_kind: COMPOSITE\n"); DUMP_MSG(P, "target: "); dump_path(P, e->literal.comp.path); @@ -2191,9 +2047,6 @@ static void dump_expr(struct Printer *P, struct HirExpr *e) dump_expr(P, e->assign.lhs); DUMP_MSG(P, "rhs: "); dump_expr(P, e->assign.rhs); - break; - default: - paw_assert(0); } --P->indent; DUMP_MSG(P, "}\n"); @@ -2214,3 +2067,5 @@ void pawHir_dump(struct Hir *hir) } pawL_push_result(P, &buf); } + +#endif diff --git a/src/hir.h b/src/hir.h index 357d870..f8fcb9d 100644 --- a/src/hir.h +++ b/src/hir.h @@ -29,7 +29,6 @@ struct Resolver; X(ClosureExpr, clos) \ X(ConversionExpr, conv) \ X(CallExpr, call) \ - X(VariantExpr, variant) \ X(Index, index) \ X(Selector, select) \ X(FieldExpr, field) \ @@ -55,23 +54,7 @@ struct Resolver; struct Hir; -// Represents a single lexical scope -struct HirScope { - struct HirSymbolList *symbols; - int bk_depth; - int fn_depth; -}; - -struct HirSymtab { - struct HirScopeList *scopes; - struct HirScope *toplevel; - struct HirScope *globals; -}; - #define last_scope(t) CHECK_EXP((t)->size > 0, (t)->data[(t)->size - 1]) -struct HirScope *pawHir_new_scope(struct Hir *hir, struct HirSymtab *table); -struct HirSymtab *pawHir_new_symtab(struct Hir *hir); -void pawHir_add_scope(struct Hir *hir, struct HirSymtab *table, struct HirScope *scope); struct HirSymbol *pawHir_add_symbol(struct Hir *hir, struct HirScope *table); int pawHir_find_symbol(struct HirScope *scope, const String *name); @@ -213,6 +196,7 @@ enum HirDeclKind { String *name; \ int line; \ DefId did; \ + uint8_t flags : 8; \ enum HirDeclKind kind : 8 struct HirDeclHeader { HIR_DECL_HEADER; @@ -248,7 +232,6 @@ struct HirAdtDecl { HIR_DECL_HEADER; paw_Bool is_pub : 1; paw_Bool is_struct : 1; - struct HirScope *scope; struct HirDeclList *fields; struct HirDeclList *generics; struct HirDeclList *monos; @@ -257,7 +240,6 @@ struct HirAdtDecl { struct HirVariantDecl { HIR_DECL_HEADER; int index; - struct HirScope *scope; struct HirDeclList *fields; }; @@ -435,13 +417,6 @@ struct HirIndex { struct HirExpr *second; }; -struct HirVariantExpr { - HIR_EXPR_HEADER; - int index; - struct HirTypeList *types; - struct HirExprList *fields; -}; - struct HirConversionExpr { HIR_EXPR_HEADER; paw_Type to; @@ -664,14 +639,14 @@ DEFINE_LIST(struct Hir, pawHir_decl_list_, HirDeclList, struct HirDecl) DEFINE_LIST(struct Hir, pawHir_expr_list_, HirExprList, struct HirExpr) DEFINE_LIST(struct Hir, pawHir_stmt_list_, HirStmtList, struct HirStmt) DEFINE_LIST(struct Hir, pawHir_type_list_, HirTypeList, struct HirType) -DEFINE_LIST(struct Hir, pawHir_symbol_list_, HirSymbolList, struct HirSymbol) -DEFINE_LIST(struct Hir, pawHir_scope_list_, HirScopeList, struct HirScope) +DEFINE_LIST(struct Hir, pawHir_scope_, HirScope, struct HirSymbol) +DEFINE_LIST(struct Hir, pawHir_symtab_, HirSymtab, struct HirScope) DEFINE_LIST(struct Hir, pawHir_path_, HirPath, struct HirSegment) -#define HIR_CAST_DECL(x) ((struct HirDecl *)(x)) -#define HIR_CAST_EXPR(x) ((struct HirExpr *)(x)) -#define HIR_CAST_STMT(x) ((struct HirStmt *)(x)) -#define HIR_CAST_TYPE(x) ((struct HirType *)(x)) +#define HIR_CAST_DECL(x) CAST(struct HirDecl *, x) +#define HIR_CAST_EXPR(x) CAST(struct HirExpr *, x) +#define HIR_CAST_STMT(x) CAST(struct HirStmt *, x) +#define HIR_CAST_TYPE(x) CAST(struct HirType *, x) #define HIR_IS_UNIT_T(x) (HirIsAdt(x) && (x)->adt.base == PAW_TUNIT) #define HIR_IS_BASIC_T(x) (HirIsAdt(x) && (x)->adt.base <= PAW_TSTR) diff --git a/src/lex.c b/src/lex.c index ee4f2a7..e5cc92f 100644 --- a/src/lex.c +++ b/src/lex.c @@ -7,7 +7,7 @@ #define LEX_ERROR(x) pawX_error(x, "syntax error") #define SAVE_AND_NEXT(x) (save(x, (x)->c), next(x)) -#define IS_EOF(x) (CAST((x)->c, uint8_t) == TK_END) +#define IS_EOF(x) (CAST(uint8_t, (x)->c) == TK_END) #define IS_NEWLINE(x) ((x)->c == '\r' || (x)->c == '\n') static void add_location(paw_Env *P, Buffer *print, const String *s, int line) @@ -44,7 +44,7 @@ static char next_raw(struct Lex *x) --x->nchunk; ++x->chunk; } else { - x->c = CAST(TK_END, char); + x->c = CAST(char, TK_END); } return x->c; } @@ -103,7 +103,6 @@ static struct Token make_int(struct Lex *x) { paw_Env *P = ENV(x); const Value v = P->top.p[-1]; - pawC_stkdec(P, 1); return (struct Token){ .kind = TK_INTEGER, .value = v, @@ -114,7 +113,6 @@ static struct Token make_float(struct Lex *x) { paw_Env *P = ENV(x); const Value v = P->top.p[-1]; - pawC_stkdec(P, 1); return (struct Token){ .kind = TK_FLOAT, .value = v, @@ -141,7 +139,7 @@ static struct Token consume_name(struct Lex *x) struct Token t = make_string(x, TK_NAME); const String *s = V_STRING(t.value); if (IS_KEYWORD(s)) { - t.kind = CAST(s->flag, TokenKind); + t.kind = CAST(TokenKind, s->flag); } else if (s->length > PAW_NAME_MAX) { pawX_error(x, "name (%I chars) is too long", PAW_CAST_INT(s->length)); } @@ -185,7 +183,7 @@ static int consume_utf8(struct Lex *x) uint32_t state = 0; do { - const uint8_t c = CAST(x->c, uint8_t); + const uint8_t c = CAST(uint8_t, x->c); state = kLookup1[c] + state * 12; state = kLookup2[state]; SAVE_AND_NEXT(x); @@ -316,24 +314,32 @@ static struct Token consume_int(struct Lex *x) { paw_Env *P = ENV(x); struct DynamicMem *dm = x->dm; - const int rc = pawV_parse_int(P, dm->scratch.data, 0); + paw_Int i; + + const int rc = pawV_parse_int(P, dm->scratch.data, 0, &i); if (rc == PAW_EOVERFLOW) { pawX_error(x, "integer '%s' is out of range for 'int' type", dm->scratch.data); } else if (rc == PAW_ESYNTAX) { - pawX_error(x, "malformed integer '%s'", dm->scratch.data); + pawX_error(x, "invalid integer '%s'", dm->scratch.data); } - const Value *pv = &P->top.p[-1]; - return make_int(x); + return (struct Token){ + .kind = TK_INTEGER, + .value.i = i, + }; } static struct Token consume_float(struct Lex *x) { + paw_Env *P = ENV(x); struct DynamicMem *dm = x->dm; - const int rc = pawV_parse_float(ENV(x), dm->scratch.data); - if (rc != PAW_OK) { - pawX_error(x, "invalid number '%s'", dm->scratch.data); - } - return make_float(x); + paw_Float f; + + const int rc = pawV_parse_float(ENV(x), dm->scratch.data, &f); + if (rc != PAW_OK) pawX_error(x, "invalid number '%s'", dm->scratch.data); + return (struct Token){ + .kind = TK_FLOAT, + .value.f = f, + }; } static struct Token consume_number(struct Lex *x) @@ -418,11 +424,11 @@ static void skip_whitespace(struct Lex *x) static struct Token advance(struct Lex *x) { try_again: -#define T(kind) make_token(CAST(kind, TokenKind)) +#define T(kind) make_token(CAST(TokenKind, kind)) skip_whitespace(x); // cast to avoid sign extension - struct Token token = T(CAST(x->c, uint8_t)); + struct Token token = T(CAST(uint8_t, x->c)); x->dm->scratch.count = 0; switch (token.kind) { case '\'': diff --git a/src/lib.c b/src/lib.c index 463a000..8e7c1d9 100644 --- a/src/lib.c +++ b/src/lib.c @@ -14,7 +14,7 @@ #include #include -#define CF_BASE(i) P->cf->base.p[i] +#define CF_BASE(i) (P->cf->base.p + i) void lib_error(paw_Env *P, int error, const char *fmt, ...) { @@ -37,7 +37,7 @@ static int get_argc(paw_Env *P) static int base_assert(paw_Env *P) { - if (V_FALSE(CF_BASE(1))) { + if (V_FALSE(*CF_BASE(1))) { pawR_error(P, PAW_ERUNTIME, "assertion failed"); } return 0; @@ -46,29 +46,29 @@ static int base_assert(paw_Env *P) static int base_print(paw_Env *P) { const String *s = V_STRING(P->top.p[-1]); - pawO_write(stdout, s->text, s->length); + pawO_write_all(P, stdout, s->text, s->length); fflush(stdout); return 0; } static int list_insert(paw_Env *P) { - List *list = V_LIST(CF_BASE(1)); - const paw_Int index = V_INT(CF_BASE(2)); - pawV_list_insert(P, list, index, CF_BASE(3)); + List *list = V_LIST(*CF_BASE(1)); + const paw_Int index = V_INT(*CF_BASE(2)); + pawV_list_insert(P, list, index, *CF_BASE(3)); return 0; } static int list_push(paw_Env *P) { - List *list = V_LIST(CF_BASE(1)); - pawV_list_push(P, list, CF_BASE(2)); + List *list = V_LIST(*CF_BASE(1)); + pawV_list_push(P, list, *CF_BASE(2)); return 0; } static int list_pop(paw_Env *P) { - List *list = V_LIST(CF_BASE(1)); + List *list = V_LIST(*CF_BASE(1)); const paw_Int length = PAW_CAST_INT(pawV_list_length(list)); if (length == 0) { pawR_error(P, PAW_EVALUE, "pop from empty List"); @@ -80,7 +80,7 @@ static int list_pop(paw_Env *P) static paw_Int clamped_index(paw_Env *P, int loc, paw_Int n) { - const paw_Int i = V_INT(CF_BASE(loc)); + const paw_Int i = V_INT(*CF_BASE(loc)); return i < 0 ? 0 : i >= n ? n - 1 : i; } @@ -91,12 +91,12 @@ static paw_Int clamped_index(paw_Env *P, int loc, paw_Int n) // equality between user-defined types right now. static int list_remove(paw_Env *P) { - List *list = V_LIST(CF_BASE(1)); + List *list = V_LIST(*CF_BASE(1)); const paw_Int length = PAW_CAST_INT(pawV_list_length(list)); if (length == 0) { pawR_error(P, PAW_EVALUE, "remove from empty List"); } - const paw_Int index = V_INT(CF_BASE(2)); + const paw_Int index = V_INT(*CF_BASE(2)); P->top.p[-1] = *pawV_list_get(P, list, index); pawV_list_pop(P, list, index); return 1; @@ -119,8 +119,8 @@ static const char *find_substr(const char *str, size_t nstr, const char *sub, si static int string_find(paw_Env *P) { - const String *s = V_STRING(CF_BASE(1)); - const String *find = V_STRING(CF_BASE(2)); + const String *s = V_STRING(*CF_BASE(1)); + const String *find = V_STRING(*CF_BASE(2)); const char *result = find_substr( s->text, s->length, find->text, find->length); @@ -134,8 +134,8 @@ static int string_find(paw_Env *P) static int string_split(paw_Env *P) { - const String *sep = V_STRING(CF_BASE(2)); - String *s = V_STRING(CF_BASE(1)); + const String *sep = V_STRING(*CF_BASE(2)); + String *s = V_STRING(*CF_BASE(1)); if (sep->length == 0) { pawR_error(P, PAW_EVALUE, "empty separator"); } @@ -162,8 +162,8 @@ static int string_split(paw_Env *P) static int string_join(paw_Env *P) { - String *s = V_STRING(CF_BASE(1)); - const Value seq = CF_BASE(2); + String *s = V_STRING(*CF_BASE(1)); + const Value seq = *CF_BASE(2); Buffer buf; pawL_init_buffer(P, &buf); @@ -184,8 +184,8 @@ static int string_join(paw_Env *P) static int string_starts_with(paw_Env *P) { - String *s = V_STRING(CF_BASE(1)); - const String *prefix = V_STRING(CF_BASE(2)); + String *s = V_STRING(*CF_BASE(1)); + const String *prefix = V_STRING(*CF_BASE(2)); const size_t prelen = prefix->length; const paw_Bool b = s->length >= prelen && 0 == memcmp(prefix->text, s->text, prelen); @@ -195,8 +195,8 @@ static int string_starts_with(paw_Env *P) static int string_ends_with(paw_Env *P) { - String *s = V_STRING(CF_BASE(1)); - const String *suffix = V_STRING(CF_BASE(2)); + String *s = V_STRING(*CF_BASE(1)); + const String *suffix = V_STRING(*CF_BASE(2)); const size_t suflen = suffix->length; paw_Bool b = PAW_FALSE; if (s->length >= suflen) { @@ -209,40 +209,41 @@ static int string_ends_with(paw_Env *P) static int int_to_string(paw_Env *P) { - const Value v = CF_BASE(1); - pawV_to_string(P, v, PAW_TINT, NULL); + Value *pv = CF_BASE(1); + pawV_to_string(P, pv, PAW_TINT, NULL); return 1; } static int string_parse_float(paw_Env *P) { - const char *str = V_TEXT(CF_BASE(1)); - const int rc = pawV_parse_float(P, str); - if (rc != PAW_OK) { - pawR_error(P, PAW_ESYNTAX, "invalid float '%s'", str); - } + paw_Float f; + const char *str = V_TEXT(*CF_BASE(1)); + const int status = pawV_parse_float(P, str, &f); + if (status != PAW_OK) pawR_error(P, PAW_ESYNTAX, "invalid float '%s'", str); + V_SET_FLOAT(CF_BASE(1), f); return 1; } static int string_parse_int(paw_Env *P) { - const char *str = V_TEXT(CF_BASE(1)); - const paw_Int base = V_INT(CF_BASE(2)); - pawC_pop(P); + const char *str = V_TEXT(*CF_BASE(1)); + const paw_Int base = V_INT(*CF_BASE(2)); - const int rc = pawV_parse_int(P, str, 0); + paw_Int i; + const int rc = pawV_parse_int(P, str, base, &i); if (rc == PAW_ESYNTAX) { pawR_error(P, PAW_ESYNTAX, "invalid integer '%s'", str); } else if (rc == PAW_EOVERFLOW) { pawR_error(P, PAW_EOVERFLOW, "integer '%s' is out of range", str); } + V_SET_INT(&P->top.p[-1], i); return 1; } static int map_get(paw_Env *P) { - Map *m = V_MAP(CF_BASE(1)); - const Value key = CF_BASE(2); + Map *m = V_MAP(*CF_BASE(1)); + const Value key = *CF_BASE(2); const Value *pv = pawH_get(m, key); if (pv != NULL) P->top.p[-1] = *pv; return 1; @@ -250,8 +251,8 @@ static int map_get(paw_Env *P) static int map_erase(paw_Env *P) { - Map *m = V_MAP(CF_BASE(1)); - pawH_erase(m, CF_BASE(2)); + Map *m = V_MAP(*CF_BASE(1)); + pawH_erase(m, *CF_BASE(2)); return 0; } @@ -308,25 +309,29 @@ void pawL_init(paw_Env *P) struct FileReader { char data[512]; FILE *file; + paw_Bool err; }; static const char *file_reader(paw_Env *P, void *ud, size_t *psize) { paw_unused(P); struct FileReader *fr = ud; - *psize = pawO_read(fr->file, fr->data, sizeof(fr->data)); - return fr->data; + const size_t zchunk = sizeof(fr->data); + *psize = pawO_read(fr->file, fr->data, zchunk); + if (*psize != zchunk) fr->err = ferror(fr->file); + return *psize > 0 ? fr->data : NULL; } int pawL_load_file(paw_Env *P, const char *pathname) { - struct FileReader fr; + struct FileReader fr = {0}; fr.file = pawO_open(pathname, "r"); - if (fr.file == NULL) { - paw_push_string(P, strerror(errno)); - return PAW_ESYSTEM; + if (fr.file != NULL) { + const int status = paw_load(P, file_reader, pathname, &fr); + if (!fr.err) return status; } - return paw_load(P, file_reader, pathname, &fr); + paw_push_string(P, strerror(errno)); + return PAW_ESYSTEM; } struct ChunkReader { @@ -354,3 +359,17 @@ int pawL_load_chunk(paw_Env *P, const char *name, const char *source) return pawL_load_nchunk(P, name, source, strlen(source)); } +int pawL_register_func(paw_Env *P, const char *name, paw_Function func, int nup) +{ + paw_new_native(P, func, nup); + + V_SET_OBJECT(P->top.p, P->builtin); + API_INCR_TOP(P, 1); + + paw_push_string(P, name); + + // func, builtin, name => builtin, name, func + paw_rotate(P, -3, -1); + paw_mapop(P, PAW_MAP_SET); + return 0; +} diff --git a/src/lib.h b/src/lib.h index b012807..24d8898 100644 --- a/src/lib.h +++ b/src/lib.h @@ -14,4 +14,6 @@ int pawL_load_file(paw_Env *P, const char *pathname); int pawL_load_nchunk(paw_Env *P, const char *name, const char *source, size_t length); int pawL_load_chunk(paw_Env *P, const char *name, const char *source); +int pawL_register_func(paw_Env *P, const char *name, paw_Function func, int nup); + #endif // PAW_LIB_H diff --git a/src/map.h b/src/map.h index 1a6acd1..c78e63e 100644 --- a/src/map.h +++ b/src/map.h @@ -13,13 +13,13 @@ typedef struct MapCursor { size_t index; } MapCursor; -#define pawH_meta(m, index) (&CAST((m)->data, MapMeta *)[index]) +#define pawH_meta(m, index) (&CAST(MapMeta *, (m)->data)[index]) static inline Value *pawH_key(Map *m, size_t index) { - char *data = CAST(m->data, char *); + char *data = CAST(char *, m->data); data += sizeof(MapMeta) * m->capacity; - return &CAST(data, Value *)[index]; + return &CAST(Value *, data)[index]; } static inline Value *pawH_value(Map *m, size_t index) @@ -29,12 +29,12 @@ static inline Value *pawH_value(Map *m, size_t index) static inline MapState h_get_state(MapCursor *mc) { - return CAST(mc->map->data, MapMeta *)[mc->index].state; + return CAST(MapMeta *, mc->map->data)[mc->index].state; } static inline void h_set_state(MapCursor *mc, MapState state) { - CAST(mc->map->data, MapMeta *)[mc->index].state = state; + CAST(MapMeta *, mc->map->data)[mc->index].state = state; } static inline Value *h_cursor_key(MapCursor *mc) diff --git a/src/os.c b/src/os.c index 3f1194f..aa0c8d6 100644 --- a/src/os.c +++ b/src/os.c @@ -34,7 +34,7 @@ void pawO_close(FILE *file) for (int i = 0; i < INTR_TIMEOUT; ++i) { const int rc = fclose(file); if (rc == 0 || errno != EINTR) { - break; // Success or non-interrupt failure + break; // success or non-interrupt failure } } } diff --git a/src/os.h b/src/os.h index 149514d..7c0aa04 100644 --- a/src/os.h +++ b/src/os.h @@ -17,4 +17,5 @@ size_t pawO_write(FILE *file, const void *data, size_t size); void pawO_read_exact(paw_Env *P, FILE *file, void *data, size_t size); void pawO_write_all(paw_Env *P, FILE *file, const void *data, size_t size); + #endif // PAW_IO_H diff --git a/src/parse.c b/src/parse.c index da976c6..c07ad2f 100644 --- a/src/parse.c +++ b/src/parse.c @@ -562,7 +562,7 @@ static struct AstExpr *unop_expr(struct Lex *lex, enum UnOp op) struct AstExpr *result = NEW_EXPR(lex, kAstUnOpExpr); struct AstUnOpExpr *r = &result->unop; skip(lex); // unary operator token - r->op = CAST(op, enum UnaryOp); // same order + r->op = CAST(enum UnaryOp, op); // same order r->target = expression(lex, kUnOpPrecedence); leave_nested(lex); return result; @@ -928,7 +928,7 @@ static struct AstExpr *binop_expr(struct Lex *lex, enum InfixOp op, struct AstEx struct AstExpr *rhs = expression(lex, right_prec(op)); struct AstExpr *result = NEW_EXPR(lex, kAstBinOpExpr); struct AstBinOpExpr *r = AstGetBinOpExpr(result); - r->op = CAST(op, enum BinaryOp); // same order + r->op = CAST(enum BinaryOp, op); // same order r->lhs = lhs; r->rhs = rhs; leave_nested(lex); @@ -1137,7 +1137,7 @@ static struct AstDecl *function(struct Lex *lex, String *name, enum FuncKind kin r->func.result = test_next(lex, TK_ARROW) ? type_expr(lex) : unit_type(lex); - r->func.body = lex->in_prelude + r->func.body = test_next(lex, ';') ? NULL : block(lex); return r; @@ -1377,22 +1377,22 @@ static const char kPrelude[] = " Err(E), \n" "} \n" - "pub fn print(message: str)\n" - "pub fn assert(cond: bool) \n" + "pub fn print(message: str);\n" + "pub fn assert(cond: bool); \n" // TODO: Replace with methods - "pub fn _int_to_string(self: int) -> str\n" - "pub fn _string_parse_int(self: str, base: int) -> int \n" - "pub fn _string_parse_float(self: str) -> float \n" - "pub fn _string_split(self: str, sep: str) -> [str] \n" - "pub fn _string_join(self: str, seq: [str]) -> str \n" - "pub fn _string_find(self: str, target: str) -> int \n" - "pub fn _string_starts_with(self: str, prefix: str) -> bool\n" - "pub fn _string_ends_with(self: str, suffix: str) -> bool \n" - "pub fn _list_push(self: [T], v: T) \n" - "pub fn _list_pop(self: [T]) -> T \n" - "pub fn _list_insert(self: [T], i: int, v: T)\n" - "pub fn _list_erase(self: [T], i: int) -> T \n"; + "pub fn _int_to_string(self: int) -> str;\n" + "pub fn _string_parse_int(self: str, base: int) -> int; \n" + "pub fn _string_parse_float(self: str) -> float; \n" + "pub fn _string_split(self: str, sep: str) -> [str]; \n" + "pub fn _string_join(self: str, seq: [str]) -> str; \n" + "pub fn _string_find(self: str, target: str) -> int; \n" + "pub fn _string_starts_with(self: str, prefix: str) -> bool;\n" + "pub fn _string_ends_with(self: str, suffix: str) -> bool; \n" + "pub fn _list_push(self: [T], v: T); \n" + "pub fn _list_pop(self: [T]) -> T; \n" + "pub fn _list_insert(self: [T], i: int, v: T);\n" + "pub fn _list_erase(self: [T], i: int) -> T; \n"; struct PreludeReader { size_t size; @@ -1421,57 +1421,6 @@ static void load_prelude(struct Lex *lex) // All paw language keywords // // ORDER TokenKind -static const char *kKeywords[] = { - "pub", - "fn", - "type", - "enum", - "struct", - "let", - "if", - "else", - "for", - "do", - "while", - "break", - "continue", - "return", - "in", - "as", - "true", - "false", -}; - -static String *basic_type_name(paw_Env *P, const char *name, paw_Type code) -{ - String *s = pawS_new_fixed(P, name); - s->flag = FLAG2CODE(code); // works either direction - return s; -} - -void pawP_init(paw_Env *P) -{ - // Add all keywords to the interned strings table. Fix them so they are - // never collected. Also added to the lexer string map. - for (size_t i = 0; i < paw_countof(kKeywords); ++i) { - const char *kw = kKeywords[i]; - String *str = pawS_new_fixed(P, kw); - str->flag = i + FIRST_KEYWORD; - } - // note that keywords are already fixed - P->str_cache[CSTR_SELF] = pawS_new_str(P, "self"); - P->str_cache[CSTR_TRUE] = pawS_new_str(P, "true"); - P->str_cache[CSTR_FALSE] = pawS_new_str(P, "false"); - P->str_cache[CSTR_BOOL] = basic_type_name(P, "bool", PAW_TBOOL); - P->str_cache[CSTR_INT] = basic_type_name(P, "int", PAW_TINT); - P->str_cache[CSTR_FLOAT] = basic_type_name(P, "float", PAW_TFLOAT); - P->str_cache[CSTR_STR] = basic_type_name(P, "str", PAW_TSTR); - P->str_cache[CSTR_LIST] = pawS_new_fixed(P, "_List"); - P->str_cache[CSTR_MAP] = pawS_new_fixed(P, "_Map"); - P->str_cache[CSTR_OPTION] = pawS_new_fixed(P, "Option"); - P->str_cache[CSTR_RESULT] = pawS_new_fixed(P, "Result"); -} - static void skip_hashbang(struct Lex *lex) { if (test_next(lex, '#') && test_next(lex, '!')) { diff --git a/src/paw.c b/src/paw.c index 9d892da..d842635 100644 --- a/src/paw.c +++ b/src/paw.c @@ -208,12 +208,6 @@ static void setup_stack(paw_Env *P, int argc, const char **argv) paw_new_list(P, argc); } -static void call_main(paw_Env *P, int argc) -{ - const int status = paw_call(P, argc); - handle_error(P, status); -} - int main(int argc, const char **argv) { parse_options(&argc, &argv); @@ -225,7 +219,8 @@ int main(int argc, const char **argv) ? 1 << s_opt.H : 0 /* use default */); setup_stack(P, argc, argv); - call_main(P, argc); + const int status = paw_call(P, 1); + handle_error(P, status); paw_close(P); return 0; diff --git a/src/paw.h b/src/paw.h index 4e2c1b4..759bfd9 100644 --- a/src/paw.h +++ b/src/paw.h @@ -180,6 +180,10 @@ const char *paw_string(paw_Env *P, int index); paw_Function paw_native(paw_Env *P, int index); void *paw_userdata(paw_Env *P, int index); +// Get a pointer to the internal representation of an object +// Value must be of object type, e.g. a list or a function. +void *paw_pointer(paw_Env *P, int index); + void paw_pop(paw_Env *P, int n); // Return the number of values in the current stack frame @@ -208,7 +212,7 @@ void paw_get_typename(paw_Env *P, paw_Type type); void paw_get_global(paw_Env *P, int gid); // Call a global function -void paw_call_global(paw_Env *P, int gid, int argc); +int paw_call_global(paw_Env *P, int gid, int argc); void paw_get_upvalue(paw_Env *P, int index, int iup); void paw_get_field(paw_Env *P, int index, int ifield); @@ -247,4 +251,6 @@ static inline void paw_replace(paw_Env *P, int index) paw_pop(P, 1); } +const char *paw_to_string(paw_Env *P, int index, paw_Type type, size_t *plen); + #endif // PAW_PAW_H diff --git a/src/resolve.c b/src/resolve.c index 3ecc445..a05001a 100644 --- a/src/resolve.c +++ b/src/resolve.c @@ -23,25 +23,29 @@ #define TYPE_ERROR(R, ...) pawE_error(ENV(R), PAW_ETYPE, (R)->line, __VA_ARGS__) #define CACHED_STR(R, i) pawE_cstr(ENV(R), CAST_SIZE(i)) #define TYPE2CODE(R, type) (pawP_type2code((R)->C, type)) +#define IS_BUILTIN_DECL(R, decl) ((decl)->hdr.did <= (R)->C->builtins[NBUILTINS - 1].did) -struct PartialItem { +struct LazyItem { struct AstDecl *ast_decl; struct HirDecl *hir_decl; + struct HirSymbol *symbol; struct HirScope *scope; + paw_Bool is_resolved; }; -static struct PartialItem *new_partial_item(struct Resolver *R, struct AstDecl *ad, struct HirDecl *hd, struct HirScope *scope) +static struct LazyItem *new_lazy_item(struct Resolver *R, struct AstDecl *ad, struct HirDecl *hd, struct HirScope *scope, struct HirSymbol *symbol) { - struct PartialItem *slot = pawK_pool_alloc(ENV(R), &R->hir->pool, sizeof(struct PartialItem)); - *slot = (struct PartialItem){ + struct LazyItem *item = pawK_pool_alloc(ENV(R), &R->hir->pool, sizeof(struct LazyItem)); + *item = (struct LazyItem){ .ast_decl = ad, .hir_decl = hd, + .symbol = symbol, .scope = scope, }; - return slot; + return item; } -DEFINE_LIST(struct Hir, item_list_, PartialItemList, struct PartialItem) +DEFINE_LIST(struct Hir, item_list_, LazyItemList, struct LazyItem) static struct HirStmt *resolve_stmt(struct Resolver *, struct AstStmt *); static struct HirExpr *resolve_expr(struct Resolver *, struct AstExpr *); @@ -105,6 +109,17 @@ static struct HirBlock *new_block(struct Resolver *R, int line) return HirGetBlock(r); } +static struct HirSymbol *new_symbol(struct Resolver *R, struct HirDecl *decl) +{ + struct HirSymbol *symbol = pawHir_new_symbol(R->hir); + *symbol = (struct HirSymbol){ + .is_init = PAW_TRUE, + .name = decl->hdr.name, + .decl = decl, + }; + return symbol; +} + static paw_Bool is_unit_variant(struct Resolver *R, const struct HirType *type) { if (HirIsFuncDef(type)) { @@ -182,38 +197,8 @@ static paw_Bool test_types(struct Resolver *R, const struct HirType *a, const st return PAW_FALSE; } -static struct HirScope *push_symbol_table(struct Resolver *R) +static DefId add_decl(struct Resolver *R, struct HirDecl *decl) { - return pawHir_new_scope(R->hir, R->symtab); -} - -static void pop_symbol_table(struct Resolver *R) -{ - // Last symbol table should have been assigned to an AST node. The - // next call to push_symbol_table() will allocate a new table. - struct HirSymtab *st = R->symtab; - paw_assert(st->scopes->count > 0); - --st->scopes->count; -} - -static void sanity_check(struct Resolver *R, struct HirDecl *new_decl) -{ -#ifndef NDEBUG - struct DynamicMem *dm = R->dm; - for (DefId did = 0; did < dm->decls->count; ++did) { - struct HirDecl *old_decl = dm->decls->data[did]; - paw_assert(old_decl->hdr.did == did); - paw_assert(old_decl != new_decl); - } -#else - paw_unused(R); - paw_unused(new_decl); -#endif -} - -static DefId add_def(struct Resolver *R, struct HirDecl *decl) -{ - sanity_check(R, decl); return pawHir_add_decl(R->hir, decl); } @@ -267,10 +252,10 @@ static void leave_inference_ctx(struct Resolver *R) pawU_leave_binder(R->U); } -static struct HirScope *enclosing_scope(struct Resolver *R) +static struct HirScope *enclosing_scope(struct HirSymtab *st) { - struct HirSymtab *st = R->symtab; - return st->scopes->data[st->scopes->count - 1]; + paw_assert(st->count > 0); + return pawHir_symtab_get(st, st->count - 1); } static struct HirSymbol *add_symbol(struct Resolver *R, struct HirScope *scope, String *name, struct HirDecl *decl) @@ -283,7 +268,7 @@ static struct HirSymbol *add_symbol(struct Resolver *R, struct HirScope *scope, static struct HirSymbol *declare_local(struct Resolver *R, String *name, struct HirDecl *decl) { - return add_symbol(R, enclosing_scope(R), name, decl); + return add_symbol(R, enclosing_scope(R->symtab), name, decl); } // Allow a previously-declared variable to be accessed @@ -292,38 +277,49 @@ static void define_local(struct HirSymbol *symbol) symbol->is_init = PAW_TRUE; } -static struct HirSymbol *new_global(struct Resolver *R, String *name, struct HirDecl *decl, paw_Bool is_pub) +static struct HirSymbol *new_global(struct Resolver *R, String *name, struct HirDecl *decl) { - struct HirScope *st = R->symtab->globals; - for (int i = 0; i < st->symbols->count; ++i) { - struct HirSymbol *symbol = st->symbols->data[i]; + struct HirScope *scope = R->globals; + for (int i = 0; i < scope->count; ++i) { + struct HirSymbol *symbol = scope->data[i]; if (pawS_eq(symbol->name, name)) { NAME_ERROR(R, "duplicate global '%s' (declared previously on line %d)", name->text, symbol->decl->hdr.line); } } - struct HirSymbol *symbol = add_symbol(R, st, name, decl); + struct HirSymbol *symbol = add_symbol(R, scope, name, decl); symbol->is_init = PAW_TRUE; return symbol; } +static void resolve_item(struct Resolver *R, struct LazyItem *item); + static struct HirSymbol *try_resolve_symbol(struct Resolver *R, const String *name) { // search the scoped symbols struct HirSymtab *scopes = R->symtab; - const int nscopes = scopes->scopes->count; + const int nscopes = scopes->count; for (int depth = nscopes - 1; depth >= 0; --depth) { - struct HirScope *scope = scopes->scopes->data[depth]; + struct HirScope *scope = scopes->data[depth]; const int index = pawHir_find_symbol(scope, name); if (index >= 0) { - struct HirSymbol *symbol = scope->symbols->data[index]; - return scope->symbols->data[index]; + struct HirSymbol *symbol = scope->data[index]; + return scope->data[index]; + } + } + + for (int i = 0; i < R->items->count; ++i) { + struct LazyItem *item = R->items->data[i]; + if (pawS_eq(name, item->hir_decl->hdr.name)) { + resolve_item(R, item); + return item->symbol; } } + // search the global symbols - const int index = pawHir_find_symbol(scopes->globals, name); + const int index = pawHir_find_symbol(R->globals, name); if (index < 0) return NULL; - return scopes->globals->symbols->data[index]; + return R->globals->data[index]; } static struct HirSymbol *resolve_symbol(struct Resolver *R, const String *name) @@ -354,32 +350,27 @@ static struct HirSymbol *new_local(struct Resolver *R, String *name, struct HirD static struct HirScope *leave_block(struct Resolver *R) { - struct HirScope *scope = enclosing_scope(R); - pop_symbol_table(R); + struct HirSymtab *st = R->symtab; + struct HirScope *scope = enclosing_scope(st); + --st->count; return scope; } static void enter_block(struct Resolver *R, struct HirScope *scope) { - if (scope == NULL) { - scope = push_symbol_table(R); - } else { - pawHir_add_scope(R->hir, R->symtab, scope); - } - scope->fn_depth = R->func_depth; + if (scope == NULL) scope = pawHir_scope_new(R->hir); + pawHir_symtab_push(R->hir, R->symtab, scope); } static struct HirScope *leave_function(struct Resolver *R) { struct HirScope *scope = leave_block(R); CHECK_GC(ENV(R)); - --R->func_depth; return scope; } static void enter_function(struct Resolver *R, struct HirScope *scope, struct HirFuncDecl *func) { - ++R->func_depth; enter_block(R, scope); new_local(R, func->name, HIR_CAST_DECL(func)); } @@ -398,7 +389,7 @@ static struct HirStmt *ResolveBlock(struct Resolver *R, struct AstBlock *block) static struct HirType *register_decl_type(struct Resolver *R, struct HirDecl *decl, enum HirTypeKind kind) { - const DefId did = add_def(R, decl); + const DefId did = add_decl(R, decl); struct HirType *r = new_type(R, did, kind, decl->hdr.line); decl->hdr.type = r; return r; @@ -439,7 +430,7 @@ static void instantiate_field(struct Resolver *R, struct HirTypeList *before, st { decl->hdr.type = instantiate_type(R, before, after, HIR_TYPEOF(decl)); new_local(R, decl->hdr.name, decl); - add_def(R, decl); + add_decl(R, decl); } static void instantiate_variant(struct Resolver *R, struct HirTypeList *before, struct HirTypeList *after, struct HirDecl *decl) @@ -687,7 +678,7 @@ static void instantiate_struct_aux(struct Resolver *R, struct HirAdtDecl *base, enter_block(R, NULL); Instantiate callback = base->is_struct ? instantiate_field : instantiate_variant; inst->fields = instantiate_fields(R, generics, types, base->fields, callback); - inst->scope = leave_block(R); + leave_block(R); leave_block(R); R->adt = enclosing; @@ -726,16 +717,12 @@ static void normalize_type_list(struct Resolver *R, struct HirTypeList *types) static struct HirDecl *instantiate_struct(struct Resolver *R, struct HirAdtDecl *base, struct HirTypeList *types) { check_template_param(R, base->generics, types); - if (types == NULL) { - return HIR_CAST_DECL(base); - } normalize_type_list(R, types); struct HirDecl *inst = find_struct_instance(R, base, types); - if (inst == NULL) { - inst = pawHir_new_decl(R->hir, base->line, kHirAdtDecl); - pawHir_decl_list_push(R->hir, base->monos, inst); - instantiate_struct_aux(R, base, &inst->adt, types); - } + if (inst != NULL) return inst; + inst = pawHir_new_decl(R->hir, base->line, kHirAdtDecl); + pawHir_decl_list_push(R->hir, base->monos, inst); + instantiate_struct_aux(R, base, &inst->adt, types); return inst; } @@ -743,14 +730,12 @@ static struct HirDecl *instantiate_struct(struct Resolver *R, struct HirAdtDecl static struct HirDecl *instantiate_func(struct Resolver *R, struct HirFuncDecl *base, struct HirTypeList *types) { check_template_param(R, base->generics, types); - if (types == NULL) return HIR_CAST_DECL(base); normalize_type_list(R, types); struct HirDecl *inst = find_func_instance(R, base, types); - if (inst == NULL) { - inst = pawHir_new_decl(R->hir, base->line, kHirInstanceDecl); - pawHir_decl_list_push(R->hir, base->monos, inst); - instantiate_func_aux(R, base, &inst->inst, types); - } + if (inst != NULL) return inst; + inst = pawHir_new_decl(R->hir, base->line, kHirInstanceDecl); + pawHir_decl_list_push(R->hir, base->monos, inst); + instantiate_func_aux(R, base, &inst->inst, types); return inst; } @@ -772,14 +757,10 @@ static struct HirScope *register_func(struct Resolver *R, struct AstFuncDecl *d, r->generics = register_generics(R, d->generics); r->monos = pawHir_decl_list_new(R->hir); } - r->params = resolve_decl_list(R, d->params); - struct HirType *result = resolve_type(R, d->result); struct HirScope *scope = leave_block(R); struct HirType *t = register_decl_type(R, HIR_CAST_DECL(r), kHirFuncDef); t->fdef.types = collect_decl_types(R, r->generics); - t->fdef.params = collect_decl_types(R, r->params); - t->fdef.result = result; t->fdef.base = r->did; r->type = t; return scope; @@ -789,7 +770,7 @@ static struct HirDecl *ResolveFieldDecl(struct Resolver *R, struct AstFieldDecl { struct HirDecl *result = pawHir_new_decl(R->hir, d->line, kHirFieldDecl); struct HirFieldDecl *r = HirGetFieldDecl(result); - add_def(R, result); + add_decl(R, result); r->name = d->name == NULL ? SCAN_STRING(R, "(field)") @@ -807,7 +788,7 @@ static struct HirDecl *ResolveVariantDecl(struct Resolver *R, struct AstVariantD enter_block(R, NULL); r->fields = resolve_decl_list(R, d->fields); - r->scope = leave_block(R); + leave_block(R); new_local(R, d->name, result); r->type = register_variant(R, r); @@ -839,52 +820,66 @@ static struct HirDeclList *resolve_fields(struct Resolver *R, struct AstDeclList return dst; } -static void register_struct(struct Resolver *R, struct AstAdtDecl *d, struct HirAdtDecl *r) +static struct HirScope *register_adt(struct Resolver *R, struct AstAdtDecl *d, struct HirAdtDecl *r) { struct HirType *t = register_decl_type(R, HIR_CAST_DECL(r), kHirAdt); struct HirType *enclosing = R->adt; R->adt = t; - enter_block(R, NULL); + if (d->generics != NULL) { r->generics = register_generics(R, d->generics); r->monos = pawHir_decl_list_new(R->hir); } - if (d->fields != NULL) { - r->fields = resolve_fields(R, d->fields, d->name); - } - enter_block(R, NULL); - allocate_decls(R, r->fields); - r->scope = leave_block(R); - - leave_block(R); + struct HirScope *scope = leave_block(R); R->adt = enclosing; t->adt.types = collect_decl_types(R, r->generics); t->adt.base = r->did; r->type = t; + return scope; +} + +static void resolve_adt_fields(struct Resolver *R, struct AstAdtDecl *d, struct HirAdtDecl *r, struct HirScope *scope) +{ + if (d->fields == NULL) return; + struct HirType *enclosing = R->adt; + R->adt = r->type; + enter_block(R, scope); + + r->fields = resolve_fields(R, d->fields, d->name); + allocate_decls(R, r->fields); + + leave_block(R); + R->adt = enclosing; } -static void resolve_func_body(struct Resolver *R, struct AstFuncDecl *d, struct HirFuncDecl *r, struct HirScope *scope, enum FuncKind kind) +static void resolve_func(struct Resolver *R, struct AstFuncDecl *d, struct HirFuncDecl *r, struct HirScope *scope, enum FuncKind kind) { r->fn_kind = kind; enter_function(R, scope, r); + r->params = resolve_decl_list(R, d->params); allocate_decls(R, r->params); - struct HirType *last_result = R->result; - const int last_count = R->nresults; - R->result = HirGetFuncDef(r->type)->result; - R->nresults = 0; + struct HirFuncDef *fdef = HirGetFuncDef(r->type); + fdef->params = collect_decl_types(R, r->params); + fdef->result = resolve_type(R, d->result); - enter_inference_ctx(R); - r->body = RESOLVE_BLOCK(R, d->body); - leave_inference_ctx(R); + if (d->body != NULL) { + struct HirType *last_result = R->result; + const int last_count = R->nresults; + R->result = fdef->result; + R->nresults = 0; + enter_inference_ctx(R); + r->body = RESOLVE_BLOCK(R, d->body); + leave_inference_ctx(R); + R->nresults = last_count; + R->result = last_result; + } leave_function(R); - R->nresults = last_count; - R->result = last_result; } static struct HirStmt *ResolveReturnStmt(struct Resolver *R, struct AstReturnStmt *s) @@ -1086,19 +1081,8 @@ static struct HirType *get_value_type(struct Resolver *R, struct HirType *target return NULL; } -static struct HirType *resolve_in_expr(struct Resolver *R, struct HirType *elem, struct HirType *adt) -{ - struct HirType *type = get_value_type(R, adt); - if (type == NULL) { - TYPE_ERROR(R, "expected List or Map"); - } - unify(R, elem, type); - return get_type(R, PAW_TBOOL); -} - static struct HirExpr *resolve_unop_expr(struct Resolver *R, struct AstUnOpExpr *e) { - // clang-format off static const uint8_t kValidOps[NUNARYOPS][PAW_NTYPES + 2] = { // type = 0, b, i, f, s, l, m [UNARY_LEN] = {0, 0, 0, 0, 1, 1, 1}, @@ -1106,7 +1090,6 @@ static struct HirExpr *resolve_unop_expr(struct Resolver *R, struct AstUnOpExpr [UNARY_NOT] = {0, 1, 1, 1, 0, 0, 0}, [UNARY_BNOT] = {0, 0, 1, 0, 0, 0, 0}, }; - // clang-format on struct HirExpr *result = pawHir_new_expr(R->hir, e->line, kHirUnOpExpr); struct HirUnOpExpr *r = HirGetUnOpExpr(result); @@ -1127,37 +1110,6 @@ static struct HirExpr *resolve_unop_expr(struct Resolver *R, struct AstUnOpExpr return result; } -static void op_type_error(struct Resolver *R, const struct HirType *type, const char *what) -{ - if (HirIsUnknown(type)) { - TYPE_ERROR(R, "%s type must be known before comparison", what); - } else { - TYPE_ERROR(R, "%s type not equality comparable", what); - } -} - -static struct HirType *binop_list(struct Resolver *R, enum BinaryOp op, struct HirType *type) -{ - const struct HirType *elem_t = hir_list_elem(type); - if (op == BINARY_ADD) return type; - if (!HIR_IS_BASIC_T(elem_t)) { - op_type_error(R, elem_t, "element"); - } - return get_type(R, PAW_TBOOL); -} - -static struct HirType *binop_map(struct Resolver *R, struct HirType *type) -{ - const struct HirType *key_t = hir_map_key(type); - const struct HirType *value_t = hir_map_value(type); - if (!HIR_IS_BASIC_T(key_t)) { - op_type_error(R, key_t, "key"); - } else if (!HIR_IS_BASIC_T(value_t)) { - op_type_error(R, value_t, "value"); - } - return get_type(R, PAW_TBOOL); -} - static struct HirExpr *resolve_binop_expr(struct Resolver *R, struct AstBinOpExpr *e) { static const uint8_t kValidOps[NBINARYOPS][PAW_NTYPES + 2] = { @@ -1194,10 +1146,6 @@ static struct HirExpr *resolve_binop_expr(struct Resolver *R, struct AstBinOpExp paw_assert(code == TYPE2CODE(R, rhs)); if (code < 0 || !kValidOps[e->op][code]) { TYPE_ERROR(R, "unsupported operand types for binary operator"); - } else if (code == BUILTIN_LIST) { - r->type = binop_list(R, e->op, lhs); - } else if (code == BUILTIN_MAP) { - r->type = binop_map(R, lhs); } else if (BINOP_IS_BOOL(e->op)) { r->type = get_type(R, PAW_TBOOL); } else { @@ -1302,7 +1250,7 @@ static struct HirDecl *resolve_closure_param(struct Resolver *R, struct AstField { struct HirDecl *result = pawHir_new_decl(R->hir, d->line, kHirFieldDecl); struct HirFieldDecl *r = HirGetFieldDecl(result); - add_def(R, result); + add_decl(R, result); r->name = d->name; r->type = d->tag == NULL @@ -1368,7 +1316,7 @@ static void maybe_fix_builtin(struct Resolver *R, String *name, DefId did) } } -static struct PartialItem *register_adt_item(struct Resolver *R, struct AstAdtDecl *d) +static struct LazyItem *register_adt_item(struct Resolver *R, struct AstAdtDecl *d) { struct HirDecl *result = pawHir_new_decl(R->hir, d->line, kHirAdtDecl); struct HirAdtDecl *r = HirGetAdtDecl(result); @@ -1376,11 +1324,21 @@ static struct PartialItem *register_adt_item(struct Resolver *R, struct AstAdtDe r->is_struct = d->is_struct; r->name = d->name; - struct HirSymbol *symbol = new_global(R, d->name, result, d->is_pub); + struct HirSymbol *symbol = IS_BUILTIN_DECL(R, result) + ? new_global(R, d->name, result) + : new_symbol(R, result); symbol->is_type = PAW_TRUE; - register_struct(R, d, r); + + struct HirScope *scope = register_adt(R, d, r); maybe_fix_builtin(R, r->name, r->did); - return new_partial_item(R, AST_CAST_DECL(d), result, NULL); + return new_lazy_item(R, AST_CAST_DECL(d), result, scope, symbol); +} + +static void resolve_adt_item(struct Resolver *R, struct LazyItem *item) +{ + struct AstAdtDecl *ast_adt = AstGetAdtDecl(item->ast_decl); + struct HirAdtDecl *hir_adt = HirGetAdtDecl(item->hir_decl); + if (ast_adt->fields != NULL) resolve_adt_fields(R, ast_adt, hir_adt, item->scope); } static struct HirDecl *ResolveAdtDecl(struct Resolver *R, struct AstAdtDecl *d) @@ -1405,7 +1363,7 @@ static struct HirDecl *ResolveVarDecl(struct Resolver *R, struct AstVarDecl *d) struct HirType *tag = resolve_type(R, d->tag); unify(R, init, tag); } - add_def(R, result); + add_decl(R, result); r->type = init; return result; } @@ -1437,31 +1395,10 @@ static struct HirType *infer_func_template(struct Resolver *R, struct HirFuncDec return HIR_TYPEOF(inst); } -static struct HirExpr *resolve_variant_expr(struct Resolver *R, struct AstCallExpr *call) -{ - struct HirExpr *expr = resolve_expr(R, call->target); - if (!HirIsPathExpr(expr)) return NULL; - struct HirType *type = HIR_TYPEOF(expr); - if (!HirIsAdt(type)) return NULL; - struct HirAdtDecl *adt = get_adt(R, type); - if (!adt->is_struct) return NULL; - - struct HirExpr *result = pawHir_new_expr(R->hir, call->line, kHirVariantExpr); - struct HirVariantExpr *r = HirGetVariantExpr(result); - - struct HirPath *path = HirGetPathExpr(expr)->path; - struct HirSegment *last = pawHir_path_get(path, path->count - 1); - struct HirDecl *decl = expect_field(R, adt, last->name); - r->index = HirGetVariantDecl(decl)->index; - return result; -} - // Resolve a function call or enumerator constructor static struct HirExpr *resolve_call_expr(struct Resolver *R, struct AstCallExpr *e) { - struct HirExpr *result = resolve_variant_expr(R, e); - if (result != NULL) return result; - result = pawHir_new_expr(R->hir, e->line, kHirCallExpr); + struct HirExpr *result = pawHir_new_expr(R->hir, e->line, kHirCallExpr); struct HirCallExpr *r = HirGetCallExpr(result); r->target = resolve_expr(R, e->target); @@ -1681,7 +1618,7 @@ static struct HirExpr *resolve_literal_expr(struct Resolver *R, struct AstLitera struct HirLiteralExpr *r = HirGetLiteralExpr(result); // literal kinds correspond 1-to-1 between AST and HIR - r->lit_kind = CAST(e->lit_kind, enum HirLitKind); + r->lit_kind = CAST(enum HirLitKind, e->lit_kind); if (e->lit_kind == kAstBasicLit) { r->type = resolve_basic_lit(R, &e->basic, &r->basic); @@ -1696,7 +1633,7 @@ static struct HirExpr *resolve_literal_expr(struct Resolver *R, struct AstLitera return result; } -static struct PartialItem *register_func_item(struct Resolver *R, struct AstFuncDecl *d) +static struct LazyItem *register_func_item(struct Resolver *R, struct AstFuncDecl *d) { struct HirDecl *result = pawHir_new_decl(R->hir, d->line, kHirFuncDecl); struct HirFuncDecl *r = HirGetFuncDecl(result); @@ -1704,19 +1641,19 @@ static struct PartialItem *register_func_item(struct Resolver *R, struct AstFunc r->fn_kind = d->fn_kind; r->name = d->name; - struct HirSymbol *symbol = new_global(R, d->name, result, d->is_pub); + struct HirSymbol *symbol = IS_BUILTIN_DECL(R, result) + ? new_global(R, d->name, result) + : new_symbol(R, result); symbol->is_type = d->generics != NULL; struct HirScope *scope = register_func(R, d, r); - return new_partial_item(R, AST_CAST_DECL(d), result, scope); + return new_lazy_item(R, AST_CAST_DECL(d), result, scope, symbol); } -static void resolve_func_item(struct Resolver *R, struct PartialItem *slot) +static void resolve_func_item(struct Resolver *R, struct LazyItem *item) { - struct AstFuncDecl *ast_func = AstGetFuncDecl(slot->ast_decl); - struct HirFuncDecl *hir_func = HirGetFuncDecl(slot->hir_decl); - if (ast_func->body != NULL) { - resolve_func_body(R, ast_func, hir_func, slot->scope, FUNC_FUNCTION); - } + struct AstFuncDecl *ast_func = AstGetFuncDecl(item->ast_decl); + struct HirFuncDecl *hir_func = HirGetFuncDecl(item->hir_decl); + resolve_func(R, ast_func, hir_func, item->scope, FUNC_FUNCTION); } static struct HirDecl *ResolveFuncDecl(struct Resolver *R, struct AstFuncDecl *d) @@ -1762,7 +1699,7 @@ static struct HirStmt *ResolveWhileStmt(struct Resolver *R, struct AstWhileStmt static void visit_forbody(struct Resolver *R, String *iname, struct HirType *itype, struct AstBlock *b, struct HirForStmt *r) { - enter_block(R, NULL); + enter_block(R, NULL); // TODO: move down a few source lines, next 4 statements can come before r->control = pawHir_new_decl(R->hir, b->line, kHirVarDecl); struct HirVarDecl *control = HirGetVarDecl(r->control); @@ -1877,6 +1814,8 @@ static struct HirExpr *resolve_selector(struct Resolver *R, struct AstSelector * r->is_index = PAW_TRUE; r->type = types->data[e->index]; return result; + } else if (!HirIsAdt(target)) { + TYPE_ERROR(R, "type has no fields"); } const struct HirAdtDecl *adt = get_adt(R, target); if (!adt->is_struct) { @@ -1914,7 +1853,7 @@ static struct HirDecl *ResolveGenericDecl(struct Resolver *R, struct AstGenericD { struct HirDecl *result = pawHir_new_decl(R->hir, d->line, kHirGenericDecl); struct HirGenericDecl *r = HirGetGenericDecl(result); - add_def(R, result); + add_decl(R, result); r->type = new_type(R, r->did, kHirGeneric, d->line); struct HirGeneric *t = HirGetGeneric(r->type); @@ -2023,42 +1962,50 @@ static struct HirExpr *resolve_expr(struct Resolver *R, struct AstExpr *expr) return r; } -static struct HirDeclList *register_items(struct Resolver *R, struct AstDeclList *items, struct PartialItemList **pslots) +static struct HirDeclList *register_items(struct Resolver *R, struct AstDeclList *decls, struct LazyItemList **pitems) { - struct PartialItemList *slots = item_list_new(R->hir); + *pitems = item_list_new(R->hir); struct HirDeclList *output = pawHir_decl_list_new(R->hir); - for (int i = 0; i < items->count; ++i) { - struct AstDecl *item = items->data[i]; - struct PartialItem *slot; - switch (HIR_KINDOF(item)) { - case kAstAdtDecl: - slot = register_adt_item(R, AstGetAdtDecl(item)); - break; - default: - slot = register_func_item(R, AstGetFuncDecl(item)); + for (int i = 0; i < decls->count; ++i) { + struct AstDecl *decl = decls->data[i]; + struct LazyItem *item; + if (AstIsAdtDecl(decl)) { + item = register_adt_item(R, AstGetAdtDecl(decl)); + } else if (AstIsFuncDecl(decl)) { + item = register_func_item(R, AstGetFuncDecl(decl)); + } else { + continue; } - pawHir_decl_list_push(R->hir, output, slot->hir_decl); - item_list_push(R->hir, slots, slot); + pawHir_decl_list_push(R->hir, output, item->hir_decl); + item_list_push(R->hir, *pitems, item); } - *pslots = slots; return output; } -static void resolve_items(struct Resolver *R, struct PartialItemList *items) +static void resolve_item(struct Resolver *R, struct LazyItem *item) +{ + if (item->is_resolved) return; + item->is_resolved = PAW_TRUE; + if (AstIsFuncDecl(item->ast_decl)) { + resolve_func_item(R, item); + } else if (AstIsAdtDecl(item->ast_decl)) { + resolve_adt_item(R, item); + } +} + +static void resolve_items(struct Resolver *R, struct LazyItemList *items) { for (int i = 0; i < items->count; ++i) { - struct PartialItem *item = items->data[i]; - if (AstIsFuncDecl(item->ast_decl)) { - resolve_func_item(R, item); - } + resolve_item(R, items->data[i]); } } static struct HirDeclList *resolve_module(struct Resolver *R, struct AstDeclList *items) { - struct PartialItemList *slots; - struct HirDeclList *output = register_items(R, items, &slots); - resolve_items(R, slots); + // 2-phase symbol resolution: the second phase might happen out-of-order, + // depending on how items are referenced + struct HirDeclList *output = register_items(R, items, &R->items); + resolve_items(R, R->items); return output; } @@ -2091,8 +2038,8 @@ static void visit_module(struct Resolver *R) hir->items = resolve_module(R, ast->items); leave_inference_ctx(R); - symtab->globals = leave_block(R); - paw_assert(symtab->scopes->count == 0); + leave_block(R); + paw_assert(symtab->count == 0); pawHir_expand(R, hir); } @@ -2104,6 +2051,7 @@ struct Hir *pawP_resolve(struct Compiler *C, struct Ast *ast) .dm = C->dm, .ast = ast, .hir = hir, + .globals = pawHir_scope_new(hir), .symtab = hir->symtab, .strings = C->strings, .U = &C->dm->unifier, diff --git a/src/rt.c b/src/rt.c index b7ed7a4..bc570d8 100644 --- a/src/rt.c +++ b/src/rt.c @@ -1,41 +1,45 @@ // Copyright (c) 2024, The paw Authors. All rights reserved. // This source code is licensed under the MIT License, which can be found in // LICENSE.md. See AUTHORS.md for a list of contributor names. -#include "auxlib.h" #include "prefix.h" +#include +#include "rt.h" +#include "auxlib.h" +#include "debug.h" #include "env.h" #include "call.h" #include "gc.h" #include "map.h" -#include "rt.h" #include "value.h" -#include // Helpers for the VM: #define vm_switch(x) switch (x) -#define vm_case(x) \ - break; \ - case OP_##x -#define vm_default \ - break; \ - default -#define VM_CONTINUE continue +#define vm_case(x) break; case OP_##x +#define vm_default break; default + +#define VM_FETCH(P) do { \ + if (PAW_UNLIKELY(trap)) { \ + trap = pawD_trace_exec(P, pc); \ + updatebase(ci); \ + } \ + i = *pc++; \ + } while (0) #define VM_SHIFT(n) (*VM_TOP((n) + 1) = *VM_TOP(1), VM_POP(n)) #define VM_POP(n) pawC_stkdec(P, n) #define VM_TOP(i) (&P->top.p[-(i)]) -#define VM_SAVE() (VM_PROTECT(), cf->top = P->top) -#define VM_PROTECT() (cf->pc = pc) +#define VM_SAVE_PC() (cf->pc = pc) #define VM_UPVALUE(u) (fn->up[u]->p.p) -#define VM_PUSH(v) pawC_pushv(P, v) -#define VM_PUSH_0() pawC_push0(P) -#define VM_PUSH_BOOL(b) pawC_pushb(P, b) -#define VM_PUSH_INT(i) pawC_pushi(P, i) -#define VM_PUSH_FLOAT(f) pawC_pushf(P, f) -#define VM_PUSH_OBJECT(o) pawC_pusho(P, CAST_OBJECT(o)) +#define VM_PUSH(v) CHECK_EXP((P)->bound.p - (P)->top.p > 0, \ + (*P->top.p++ = (v), &P->top.p[-1])) +#define VM_PUSH_0() VM_PUSH((Value){.u = 0}) +#define VM_PUSH_BOOL(B) VM_PUSH((Value){.b = B}) +#define VM_PUSH_INT(I) VM_PUSH((Value){.i = I}) +#define VM_PUSH_FLOAT(F) VM_PUSH((Value){.f = F}) +#define VM_PUSH_OBJECT(O) VM_PUSH((Value){.o = O}) #define VM_SET_0(top) V_SET_0(VM_TOP(top)) #define VM_SET_BOOL(top, b) V_SET_BOOL(VM_TOP(top), b) @@ -50,9 +54,6 @@ #define VM_LIST(top) V_LIST(*VM_TOP(top)) #define VM_MAP(top) V_MAP(*VM_TOP(top)) -// Slot 0 (the callable) is an implicit parameter. -#define VM_ARGC() (paw_get_count(P) - 1) - // Generate code for creating common builtin objects // Requires a placeholder slot (the VM_PUSH_0() pushes an empty slot) so // the GC doesn't get confused. Both the VM_PUSH_0(), and the pawV_list_new calls @@ -74,31 +75,17 @@ static void add_zeros(paw_Env *P, int n) } } -static int current_line(const CallFrame *cf) -{ - Proto *p = cf->fn->p; - const int pc = CAST(cf->pc - p->source - 1, int); - - int i = 0; - for (; i < p->nlines - 1; ++i) { - if (p->lines[i].pc >= pc) { - break; - } - } - return p->lines[i].line; -} - static void add_location(paw_Env *P, Buffer *buf) { const CallFrame *cf = P->cf; for (; cf != &P->main; cf = cf->prev) { - if (cf_is_paw(cf)) { + const int line = pawD_line_number(cf, cf->pc); + if (line >= 0) { const Proto *p = cf->fn->p; - const int line = current_line(cf); const char *name = p->modname->text; pawL_add_fstring(P, buf, "%s:%d: ", name, line); break; - } else if (cf_is_entry(cf)) { + } else if (CF_IS_ENTRY(cf)) { L_ADD_LITERAL(P, buf, "[C]: "); break; } @@ -146,8 +133,8 @@ void pawR_error(paw_Env *P, int error, const char *fmt, ...) // Assumes 2's complement, which means PAW_INT_MIN is a power-of-2 with // an exact paw_Float representation. #define FLOAT2INT_AUX(f, pv) \ - ((f) >= CAST(PAW_INT_MIN, paw_Float) && \ - (f) < -CAST(PAW_INT_MIN, paw_Float) && \ + ((f) >= CAST(paw_Float, PAW_INT_MIN) && \ + (f) < -CAST(paw_Float, PAW_INT_MIN) && \ (V_SET_INT(pv, PAW_CAST_INT(f)), 1)) static void float2int(paw_Env *P, paw_Float f, Value *pv) @@ -184,7 +171,7 @@ void pawR_cast_float(paw_Env *P, paw_Type type) if (type != PAW_TFLOAT) { Value *pv = VM_TOP(1); const paw_Int i = V_INT(*pv); - V_SET_FLOAT(pv, CAST(i, paw_Float)); + V_SET_FLOAT(pv, CAST(paw_Float, i)); } } @@ -236,7 +223,7 @@ void pawR_init(paw_Env *P) V_SET_OBJECT(&P->mem_errmsg, errmsg); } -#define stop_loop(i, i2, d) \ +#define STOP_LOOP(i, i2, d) \ (((d) < 0 && (i) <= (i2)) || ((d) > 0 && (i) >= (i2))) static paw_Bool fornum_init(paw_Env *P) @@ -247,7 +234,7 @@ static paw_Bool fornum_init(paw_Env *P) if (step == 0) { pawR_error(P, PAW_ERUNTIME, "loop step equals 0"); } - const paw_Bool skip = stop_loop(begin, end, step); + const paw_Bool skip = STOP_LOOP(begin, end, step); if (!skip) { V_SET_INT(VM_TOP(3), begin); V_SET_INT(VM_TOP(2), end); @@ -263,7 +250,7 @@ static paw_Bool fornum(paw_Env *P) const paw_Int step = V_INT(*VM_TOP(1)); const paw_Int end = V_INT(*VM_TOP(2)); const paw_Int next = itr + step; - if (stop_loop(next, end, step)) { + if (STOP_LOOP(next, end, step)) { return PAW_FALSE; } V_SET_INT(VM_TOP(3), next); @@ -327,7 +314,7 @@ static paw_Bool formap(paw_Env *P) return PAW_FALSE; // stop the loop } -#define I2U(i) (CAST(i, uint64_t)) +#define I2U(i) (CAST(uint64_t, i)) #define U2I(u) PAW_CAST_INT(u) // Generate code for int operators @@ -414,7 +401,7 @@ void pawR_arithi2(paw_Env *P, enum ArithOp2 op) VM_POP(1); } -void pawR_bitwi1(paw_Env *P, enum BitwOp1 op) +void pawR_bitw1(paw_Env *P, enum BitwOp1 op) { paw_Int x = VM_INT(1); switch (op) { @@ -424,7 +411,7 @@ void pawR_bitwi1(paw_Env *P, enum BitwOp1 op) VM_SET_INT(1, x); } -void pawR_bitwi2(paw_Env *P, enum BitwOp2 op) +void pawR_bitw2(paw_Env *P, enum BitwOp2 op) { paw_Int x = VM_INT(2); paw_Int y = VM_INT(1); @@ -558,12 +545,18 @@ static void str_concat(paw_Env *P) const String *x = VM_STR(2); const String *y = VM_STR(1); - Buffer print; - pawL_init_buffer(P, &print); - pawL_add_nstring(P, &print, x->text, x->length); - pawL_add_nstring(P, &print, y->text, y->length); - pawL_push_result(P, &print); - VM_SHIFT(2); + paw_assert(x->length < PAW_SIZE_MAX); + paw_assert(y->length < PAW_SIZE_MAX); + if (x->length > PAW_SIZE_MAX - y->length) { + pawR_error(P, PAW_EMEMORY, "string is too large"); + } + String *z = pawS_new_uninit(P, x->length + y->length); + memcpy(z->text, x->text, x->length); + memcpy(z->text + x->length, y->text, y->length); + pawS_register(P, &z); + + V_SET_OBJECT(VM_TOP(2), z); + VM_POP(1); } static void str_get(paw_Env *P) @@ -850,7 +843,6 @@ void pawR_execute(paw_Env *P, CallFrame *cf) const Value *K; const OpCode *pc; Closure *fn; - top: pc = cf->pc; fn = cf->fn; @@ -880,7 +872,8 @@ void pawR_execute(paw_Env *P, CallFrame *cf) vm_case(COPY) : { const int u = GET_U(opcode); - VM_PUSH(*VM_TOP(u + 1)); + const Value v = *VM_TOP(u + 1); + VM_PUSH(v); } vm_case(PUSHZERO) : @@ -940,12 +933,12 @@ void pawR_execute(paw_Env *P, CallFrame *cf) vm_case(BITW1) : { - pawR_bitwi1(P, GET_U(opcode)); + pawR_bitw1(P, GET_U(opcode)); } vm_case(BITW2) : { - pawR_bitwi2(P, GET_U(opcode)); + pawR_bitw2(P, GET_U(opcode)); } vm_case(BOOLOP) : @@ -955,39 +948,39 @@ void pawR_execute(paw_Env *P, CallFrame *cf) vm_case(STROP) : { - VM_PROTECT(); + VM_SAVE_PC(); pawR_strop(P, GET_U(opcode)); } vm_case(LISTOP) : { - VM_PROTECT(); + VM_SAVE_PC(); pawR_listop(P, GET_U(opcode)); } vm_case(MAPOP) : { - VM_PROTECT(); + VM_SAVE_PC(); pawR_mapop(P, GET_U(opcode)); } vm_case(NEWTUPLE) : { - VM_PROTECT(); + VM_SAVE_PC(); pawR_literal_tuple(P, GET_U(opcode)); CHECK_GC(P); } vm_case(NEWLIST) : { - VM_PROTECT(); + VM_SAVE_PC(); pawR_literal_list(P, GET_U(opcode)); CHECK_GC(P); } vm_case(NEWMAP) : { - VM_PROTECT(); + VM_SAVE_PC(); pawR_literal_map(P, GET_U(opcode)); CHECK_GC(P); } @@ -1009,14 +1002,14 @@ void pawR_execute(paw_Env *P, CallFrame *cf) vm_case(NEWVARIANT) : { - VM_PROTECT(); + VM_SAVE_PC(); new_variant(P, GET_A(opcode), GET_B(opcode)); CHECK_GC(P); } vm_case(NEWINSTANCE) : { - VM_PROTECT(); + VM_SAVE_PC(); Value *pv = VM_PUSH_0(); Tuple *tuple = pawV_new_tuple(P, GET_U(opcode)); V_SET_OBJECT(pv, tuple); @@ -1025,7 +1018,7 @@ void pawR_execute(paw_Env *P, CallFrame *cf) vm_case(INITFIELD) : { - VM_PROTECT(); + VM_SAVE_PC(); const int u = GET_U(opcode); Tuple *tuple = V_TUPLE(*VM_TOP(2)); tuple->elems[u] = *VM_TOP(1); @@ -1066,19 +1059,19 @@ void pawR_execute(paw_Env *P, CallFrame *cf) vm_case(GETFIELD) : { - VM_PROTECT(); + VM_SAVE_PC(); pawR_getfield(P, GET_U(opcode)); } vm_case(SETFIELD) : { - VM_PROTECT(); + VM_SAVE_PC(); pawR_setfield(P, GET_U(opcode)); } vm_case(CLOSURE) : { - VM_PROTECT(); + VM_SAVE_PC(); Value *pv = VM_PUSH_0(); Proto *proto = fn->p->p[GET_U(opcode)]; Closure *closure = pawV_new_closure(P, proto->nup); @@ -1100,7 +1093,7 @@ void pawR_execute(paw_Env *P, CallFrame *cf) { const uint8_t argc = GET_U(opcode); StackPtr ptr = VM_TOP(argc + 1); - VM_SAVE(); + VM_SAVE_PC(); CallFrame *callee = pawC_precall(P, ptr, V_OBJECT(*ptr), argc); if (callee) { @@ -1121,13 +1114,13 @@ void pawR_execute(paw_Env *P, CallFrame *cf) const Value result = *VM_TOP(1); VM_POP(1); - P->top.p = cf_stack_return(cf); - VM_SAVE(); + P->top.p = CF_STACK_RETURN(cf); + VM_SAVE_PC(); pawR_close_upvalues(P, VM_TOP(1)); VM_PUSH(result); P->cf = cf->prev; - if (cf_is_entry(cf)) { + if (CF_IS_ENTRY(cf)) { return; } cf = P->cf; @@ -1166,7 +1159,7 @@ void pawR_execute(paw_Env *P, CallFrame *cf) vm_case(FORNUM0) : { - VM_PROTECT(); + VM_SAVE_PC(); if (fornum_init(P)) { pc += GET_S(opcode); // skip } @@ -1182,7 +1175,7 @@ void pawR_execute(paw_Env *P, CallFrame *cf) #define VM_FORIN0(t, T) \ vm_case(FOR##T##0) : \ { \ - VM_PROTECT(); \ + VM_SAVE_PC(); \ if (for##t##_init(P)) { \ VM_PUSH_0(); \ pc += GET_S(opcode); \ diff --git a/src/rt.h b/src/rt.h index bd5fd32..ce1807c 100644 --- a/src/rt.h +++ b/src/rt.h @@ -17,8 +17,8 @@ void pawR_arithi1(paw_Env *P, enum ArithOp1 op); void pawR_arithi2(paw_Env *P, enum ArithOp2 op); void pawR_arithf1(paw_Env *P, enum ArithOp1 op); void pawR_arithf2(paw_Env *P, enum ArithOp2 op); -void pawR_bitwi1(paw_Env *P, enum BitwOp1 op); -void pawR_bitwi2(paw_Env *P, enum BitwOp2 op); +void pawR_bitw1(paw_Env *P, enum BitwOp1 op); +void pawR_bitw2(paw_Env *P, enum BitwOp2 op); void pawR_boolop(paw_Env *P, enum BoolOp op); void pawR_strop(paw_Env *P, enum StrOp op); diff --git a/src/str.c b/src/str.c index c870b80..75af22a 100644 --- a/src/str.c +++ b/src/str.c @@ -11,7 +11,7 @@ static String *new_string(paw_Env *P, size_t length) { if (length > PAW_SIZE_MAX - sizeof(String) - 1 /* '\0' */) { - pawM_error(P); // size too big for paw_Int + pawM_error(P); } String *str = pawM_new_flex(P, String, length + 1, sizeof(char)); pawG_add_object(P, CAST_OBJECT(str), VSTRING); @@ -106,11 +106,47 @@ void pawS_free_str(paw_Env *P, String *s) { StringTable *st = &P->strings; String **p = &st->strings[ST_INDEX(st, s->hash)]; - while (*p != s) { - p = &(*p)->next; - } + while (*p != s) p = &(*p)->next; *p = s->next; // remove --st->count; pawM_free_flex(P, s, s->length + 1, sizeof(char)); } + +String *pawS_new_uninit(paw_Env *P, size_t length) +{ + StringTable *st = &P->strings; + if (st->count * 4 > st->capacity) { + grow_table(P, st); + } + if (length > PAW_SIZE_MAX - sizeof(String) - 1 /* '\0' */) { + pawM_error(P); + } + String *str = pawM_new_flex(P, String, length + 1, sizeof(char)); + str->text[length] = '\0'; + str->length = length; + str->next = NULL; + str->flag = 0; + return str; +} + +void pawS_register(paw_Env *P, String **pinit) +{ + String *str = *pinit; + StringTable *st = &P->strings; + const uint32_t hash = pawS_hash(str->text, str->length, 0); + String **plist = &st->strings[ST_INDEX(st, hash)]; + for (String *p = *plist; p; p = p->next) { + if (str->length == p->length && + memcmp(p->text, str->text, str->length) == 0) { + pawM_free_flex(P, str, str->length + 1, sizeof(char)); + *pinit = p; + return; + } + } + pawG_add_object(P, CAST_OBJECT(str), VSTRING); + str->hash = hash; + ++st->count; + str->next = *plist; + *plist = str; +} diff --git a/src/str.h b/src/str.h index 941e43f..79837ee 100644 --- a/src/str.h +++ b/src/str.h @@ -46,10 +46,14 @@ void pawS_init(paw_Env *P); void pawS_uninit(paw_Env *P); void pawS_remove_str(paw_Env *P, String *s); -String *pawS_alloc_str(paw_Env *P, size_t length); String *pawS_new_str(paw_Env *P, const char *text); String *pawS_new_nstr(paw_Env *P, const char *text, size_t length); String *pawS_new_fixed(paw_Env *P, const char *text); void pawS_free_str(paw_Env *P, String *s); +// TODO: Hack for 2-phase initialization, used by string concatenation in +// rt.c. +String *pawS_new_uninit(paw_Env *P, size_t length); +void pawS_register(paw_Env *P, String **pinit); + #endif // PAW_STR_H diff --git a/src/util.h b/src/util.h index 6f89025..3f8bc2b 100644 --- a/src/util.h +++ b/src/util.h @@ -23,10 +23,10 @@ #define PAW_IS_ALIGNED(p) (!(CAST_UPTR(p) & (PAW_ALIGN - 1))) #define CHECK_EXP(c, e) (paw_assert(c), e) -#define CAST(x, t) ((t)(x)) -#define CAST_SIZE(x) CAST(x, size_t) -#define CAST_UPTR(x) CAST(x, uintptr_t) -#define ERASE_TYPE(p) CAST(p, void *) +#define CAST(t, x) ((t)(x)) +#define CAST_SIZE(x) CAST(size_t, x) +#define CAST_UPTR(x) CAST(uintptr_t, x) +#define ERASE_TYPE(p) CAST(void *, p) #define BUMP_PTR(p, n) ERASE_TYPE(CAST_UPTR(p) + (n)) // Check for inclusion in one of the character classes diff --git a/src/value.c b/src/value.c index f8b36e1..e95cdaa 100644 --- a/src/value.c +++ b/src/value.c @@ -2,17 +2,15 @@ // This source code is licensed under the MIT License, which can be found in // LICENSE.md. See AUTHORS.md for a list of contributor names. #include "prefix.h" +#include +#include +#include "value.h" #include "gc.h" #include "map.h" #include "mem.h" #include "rt.h" #include "str.h" -#include "util.h" -#include "value.h" -#include -#include -#include #define ERROR(P, kind, ...) pawE_error(P, kind, -1, __VA_ARGS__) @@ -35,7 +33,7 @@ static int check_suffix(const char *p, const char *base) return 0; } -static void int_to_string(paw_Env *P, paw_Int i) +static void int_to_string(paw_Env *P, paw_Int i, Value *out) { char temp[32]; const paw_Bool negative = i < 0; @@ -46,9 +44,9 @@ static void int_to_string(paw_Env *P, paw_Int i) // systems. uint64_t u = i == INT64_MIN ? UINT64_C(1) << 63 - : CAST(llabs(i), uint64_t); + : CAST(uint64_t, llabs(i)); do { - *ptr-- = CAST(u % 10 + '0', char); + *ptr-- = CAST(char, u % 10 + '0'); u /= 10; } while (u); if (negative) { @@ -56,34 +54,34 @@ static void int_to_string(paw_Env *P, paw_Int i) } else { ++ptr; } - pawC_pushns(P, ptr, CAST_SIZE(end - ptr)); + String *str = pawS_new_nstr(P, ptr, CAST_SIZE(end - ptr)); + V_SET_OBJECT(out, str); } -static void float_to_string(paw_Env *P, paw_Float f) +static void float_to_string(paw_Env *P, paw_Float f, Value *out) { char temp[32]; const int n = snprintf(temp, paw_countof(temp), "%.*g", 17, f); - pawC_pushns(P, temp, CAST_SIZE(n)); + String *str = pawS_new_nstr(P, temp, CAST_SIZE(n)); + V_SET_OBJECT(out, str); } -const char *pawV_to_string(paw_Env *P, Value v, paw_Type type, size_t *plength) +const char *pawV_to_string(paw_Env *P, Value *pv, paw_Type type, size_t *plength) { switch (type) { case PAW_TSTR: - pawC_pushv(P, v); // copy break; case PAW_TINT: - int_to_string(P, V_INT(v)); + int_to_string(P, V_INT(*pv), pv); break; case PAW_TFLOAT: - float_to_string(P, V_FLOAT(v)); + float_to_string(P, V_FLOAT(*pv), pv); break; default: paw_assert(type == PAW_TBOOL); - V_SET_OBJECT(&v, pawE_cstr(P, V_TRUE(v) ? CSTR_TRUE : CSTR_FALSE)); - pawC_pushv(P, v); + V_SET_OBJECT(pv, pawE_cstr(P, V_TRUE(*pv) ? CSTR_TRUE : CSTR_FALSE)); } - const String *s = V_STRING(P->top.p[-1]); + const String *s = V_STRING(*pv); if (plength != NULL) *plength = s->length; return s->text; } @@ -184,14 +182,9 @@ void pawV_free_variant(paw_Env *P, Variant *var) pawM_free_flex(P, var, CAST_SIZE(var->nfields), sizeof(var->fields[0])); } -static void clear_attrs(Value *pv, int nattrs) -{ - memset(pv, 0, CAST_SIZE(nattrs) * sizeof(*pv)); -} - Native *pawV_new_native(paw_Env *P, paw_Function func, int nup) { - // TODO: nup > UINT16_MAX, check it or assert? + paw_assert(nup <= UINT16_MAX); Native *nat = pawM_new_flex(P, Native, nup, sizeof(nat->up[0])); pawG_add_object(P, CAST_OBJECT(nat), VNATIVE); nat->func = func; @@ -204,22 +197,19 @@ void pawV_free_native(paw_Env *P, Native *f) pawM_free_flex(P, f, f->nup, sizeof(f->up[0])); } -Foreign *pawV_push_foreign(paw_Env *P, size_t size, int nfields) +Foreign *pawV_new_foreign(paw_Env *P, size_t size, int nfields, Value *out) { - if (size > PAW_SIZE_MAX) { - pawM_error(P); - } - Value *pv = pawC_push0(P); + if (size > PAW_SIZE_MAX) pawM_error(P); Foreign *ud = pawM_new_flex(P, Foreign, nfields, sizeof(ud->fields[0])); pawG_add_object(P, CAST_OBJECT(ud), VFOREIGN); - V_SET_OBJECT(pv, ud); // anchor + V_SET_OBJECT(out, ud); // anchor ud->nfields = nfields; ud->size = size; if (size > 0) { - // Allocate space to hold 'size' bytes of foreign data. + // allocate space to hold 'size' bytes of foreign data ud->data = pawM_new_vec(P, size, char); } - clear_attrs(ud->fields, nfields); + memset(ud->fields, 0, CAST_SIZE(nfields) * sizeof(ud->fields[0])); return ud; } @@ -264,7 +254,7 @@ static int char2base(char c) #define SKIP_SPACES(p) \ while (ISSPACE(*(p))) ++(p); -int pawV_parse_uint64(paw_Env *P, const char *text, int base) +int pawV_parse_uint64(paw_Env *P, const char *text, int base, uint64_t *out) { int b = 10; SKIP_SPACES(text); @@ -276,7 +266,7 @@ int pawV_parse_uint64(paw_Env *P, const char *text, int base) } p += 2; // skip base prefix } else if (p[1] == '\0') { - pawC_pushi(P, 0); + *out = 0; return PAW_OK; } else { return PAW_ESYNTAX; @@ -296,8 +286,7 @@ int pawV_parse_uint64(paw_Env *P, const char *text, int base) if (check_suffix(p, text)) { return PAW_ESYNTAX; } - Value *pv = pawC_push0(P); - pv->u = value; + *out = value; return PAW_OK; } @@ -311,19 +300,20 @@ static paw_Bool parse_negative(const char **ptext) return PAW_FALSE; } -int pawV_parse_int(paw_Env *P, const char *text, int base) +int pawV_parse_int(paw_Env *P, const char *text, int base, paw_Int *out) { const char *original = text; SKIP_SPACES(text); const paw_Bool negative = parse_negative(&text); if (!ISHEX(*text)) return PAW_ESYNTAX; - const int rc = pawV_parse_uint64(P, text, base); - if (rc != PAW_OK) return rc; - Value *pv = &P->top.p[-1]; - if (pv->u > CAST(PAW_INT_MAX, uint64_t) + negative) { + + uint64_t u; + const int status = pawV_parse_uint64(P, text, base, &u); + if (status != PAW_OK) return status; + if (u > CAST(uint64_t, PAW_INT_MAX) + negative) { return PAW_EOVERFLOW; } - pv->i = PAW_CAST_INT(negative ? -pv->u : pv->u); + *out = PAW_CAST_INT(negative ? -u : u); return PAW_OK; } @@ -332,7 +322,7 @@ int pawV_parse_int(paw_Env *P, const char *text, int base) ++(p); \ } -int pawV_parse_float(paw_Env *P, const char *text) +int pawV_parse_float(paw_Env *P, const char *text, paw_Float *out) { const char *original = text; SKIP_SPACES(text); @@ -358,6 +348,6 @@ int pawV_parse_float(paw_Env *P, const char *text) return PAW_ESYNTAX; } const paw_Float f = strtod(text, NULL); - pawC_pushf(P, negative ? -f : f); + *out = negative ? -f : f; return PAW_OK; } diff --git a/src/value.h b/src/value.h index 0fcf3c5..ea07f38 100644 --- a/src/value.h +++ b/src/value.h @@ -28,7 +28,6 @@ #define V_TEXT(v) (V_STRING(v)->text) #define V_TUPLE(v) (O_TUPLE(V_OBJECT(v))) #define V_VARIANT(v) (O_VARIANT(V_OBJECT(v))) -#define V_METHOD(v) (O_METHOD(V_OBJECT(v))) #define V_FOREIGN(v) (O_FOREIGN(V_OBJECT(v))) #define V_SET_0(v) ((v)->u = 0) @@ -47,7 +46,6 @@ #define O_IS_LIST(o) (O_KIND(o) == VLIST) #define O_IS_TUPLE(o) (O_KIND(o) == VTUPLE) #define O_IS_VARIANT(o) (O_KIND(o) == VVARIANT) -#define O_IS_METHOD(o) (O_KIND(o) == VMETHOD) #define O_IS_FOREIGN(o) (O_KIND(o) == VFOREIGN) #define O_STRING(o) CHECK_EXP(O_IS_STRING(o), (String *)(o)) @@ -59,10 +57,9 @@ #define O_LIST(o) CHECK_EXP(O_IS_LIST(o), (List *)(o)) #define O_TUPLE(o) CHECK_EXP(O_IS_TUPLE(o), (Tuple *)(o)) #define O_VARIANT(o) CHECK_EXP(O_IS_VARIANT(o), (Variant *)(o)) -#define O_METHOD(o) CHECK_EXP(O_IS_METHOD(o), (Method *)(o)) #define O_FOREIGN(o) CHECK_EXP(O_IS_FOREIGN(o), (Foreign *)(o)) -#define CAST_OBJECT(x) (CAST(CAST(x, void *), Object *)) +#define CAST_OBJECT(x) ((Object *)(void *)(x)) typedef enum ValueKind { // scalar types @@ -76,7 +73,6 @@ typedef enum ValueKind { VMAP, VTUPLE, VVARIANT, - VMETHOD, VFOREIGN, VTYPE, @@ -139,13 +135,13 @@ static inline size_t pawV_check_abs(paw_Env *P, paw_Int index, size_t length, co // Understands non-decimal base prefixes '0b', '0o', '0x', and their uppercase // counterparts. Returns PAW_ESYNTAX if the integer is malformed, // PAW_EOVERFLOW if it is too large to fit in a uint64_t, and PAW_OK otherwise. -int pawV_parse_uint64(paw_Env *P, const char *text, int base); +int pawV_parse_uint64(paw_Env *P, const char *text, int base, uint64_t *out); -int pawV_parse_int(paw_Env *P, const char *text, int base); +int pawV_parse_int(paw_Env *P, const char *text, int base, paw_Int *out); // Convert a null-terminated string into a float // Returns 0 on success, -1 otherwise. -int pawV_parse_float(paw_Env *P, const char *text); +int pawV_parse_float(paw_Env *P, const char *text, paw_Float *out); typedef struct String { GC_HEADER; @@ -156,7 +152,7 @@ typedef struct String { char text[]; } String; -const char *pawV_to_string(paw_Env *P, Value v, paw_Type type, size_t *nout); +const char *pawV_to_string(paw_Env *P, Value *pv, paw_Type type, size_t *nout); typedef struct Proto { GC_HEADER; @@ -306,17 +302,6 @@ typedef struct Variant { Variant *pawV_new_variant(paw_Env *P, int k, int nfields); void pawV_free_variant(paw_Env *P, Variant *var); -// Method bound to an instance -typedef struct Method { - GC_HEADER; - Object *gc_list; - Value self; - Value f; -} Method; - -Method *pawV_new_method(paw_Env *P, Value self, Value call); -void pawV_free_method(paw_Env *P, Method *); - typedef struct Foreign { GC_HEADER; uint8_t flags; @@ -327,7 +312,7 @@ typedef struct Foreign { Value fields[]; } Foreign; -Foreign *pawV_push_foreign(paw_Env *P, size_t size, int nfields); +Foreign *pawV_new_foreign(paw_Env *P, size_t size, int nfields, Value *out); void pawV_free_foreign(paw_Env *P, Foreign *ud); #endif // PAW_VALUE_H diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index cdb773f..dee057e 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -27,6 +27,7 @@ build_test(test_so) build_test(test_error) build_test(test_api) +test_script(fib "15") test_script(bubble "500") test_script(matmul "50") test_script(mandelbrot "-2.1,0.9,80,-1.3,1.3,24") diff --git a/test/scripts/fib.paw b/test/scripts/fib.paw new file mode 100644 index 0000000..2c523e6 --- /dev/null +++ b/test/scripts/fib.paw @@ -0,0 +1,18 @@ +// fib.paw + +fn fib(n: int) -> int { + if n < 2 { + return n; + } + return fib(n - 2) + fib(n - 1); +} + +pub fn main(args: [str]) { + let n = 35; + if #args > 0 { + n = _string_parse_int(args[0], 10); + } + let r = fib(n); + print('fib(' + _int_to_string(n) + ') = ' + + _int_to_string(r) + '\n'); +} diff --git a/test/scripts/integer.paw b/test/scripts/integer.paw index 2ccf7bf..504a614 100644 --- a/test/scripts/integer.paw +++ b/test/scripts/integer.paw @@ -19,7 +19,7 @@ pub fn test_non_decimal() { } pub fn test_parse() { - let check = |s: str, want: int| { + let check = |s, want| { assert(_string_parse_int(s, 10) == want); }; diff --git a/test/scripts/list.paw b/test/scripts/list.paw index d4b681c..2b5475a 100644 --- a/test/scripts/list.paw +++ b/test/scripts/list.paw @@ -568,3 +568,27 @@ pub fn test_infer_l() { } assert(result == 'aaabbbcccdddeeefffggghhhiii'); } + +struct ComplexStruct { + value: ComplexEnum +} + +enum ComplexEnum { + JustT(T), + WithStr(T, str), +} + +pub fn test_complex_elem() { + let list = []; + for i = 0, 1000 { + if i < 500 { + _list_push(list, ComplexStruct{ + value: ComplexEnum::::JustT(i), + }); + } else { + _list_push(list, ComplexStruct{ + value: ComplexEnum::::WithStr(i, 'str-' + _int_to_string(i)), + }); + } + } +} diff --git a/test/scripts/poly_struct.paw b/test/scripts/poly_struct.paw index a2fee8d..b81f25d 100644 --- a/test/scripts/poly_struct.paw +++ b/test/scripts/poly_struct.paw @@ -6,6 +6,21 @@ struct Singleton { pub fn test_initialization() { let singleton: Singleton = Singleton::{value: 1}; let singleton = Singleton::{value: 1}; + + let a = Singleton::<(int,)>{value: (1,)}; + let b = Singleton:: float>{value: |x: int| x as float}; + let c = Singleton::>{value: Singleton::{value: 2}}; + + let a2 = Singleton{value: (1,)}; + let b2 = Singleton{value: |x: int| x as float}; + let c2 = Singleton{value: Singleton{value: 2}}; + + a = a2; + b = b2; + c = c2; + + // from further down in the file + let b = TopLevelB{t: 42}; } struct Empty; @@ -152,3 +167,27 @@ pub fn test_infer_vec_field() { _list_push(p.b, p.a); assert(p.a == p.b[0]); } + +pub fn test_scope() { + let a = TopLevelA{ + b: TopLevelB{ + t: TopLevelB{t: 42} + } + }; + assert(a.b.t.t == 42); + let b = toplevel_call(a); + assert(b.t.t == 42); +} + +struct TopLevelA { + b: TopLevelB +} + +struct TopLevelB { + t: T +} + +fn toplevel_call(a: TopLevelA) -> TopLevelB { + return a.b; +} + diff --git a/test/test_alloc.c b/test/test_alloc.c index 3563e63..a07e54a 100644 --- a/test/test_alloc.c +++ b/test/test_alloc.c @@ -54,7 +54,7 @@ static void check_defer_data(const void *ptr, size_t size) { const uint8_t *data = ptr; for (size_t i = 0; i < size; ++i) { - check(data[i] == CAST(size & 255, uint8_t)); + check(data[i] == (uint8_t)(size & 255)); } } diff --git a/test/test_api.c b/test/test_api.c index 709b258..46ba097 100644 --- a/test/test_api.c +++ b/test/test_api.c @@ -34,7 +34,7 @@ static paw_Env *start_test(void) " let b = Enum::::B('B'); \n" " return #args; \n" "} \n"; - paw_Env *P = test_open(NULL, &s_alloc, PAW_HEAP_MIN); + paw_Env *P = test_open(NULL, &s_alloc, PAW_HEAP_DEFAULT); const int status = pawL_load_chunk(P, "test", source); check_error(P, status); return P; @@ -128,13 +128,14 @@ int main(void) struct paw_Item info; int status = paw_lookup_item(P, &info); check(status == PAW_OK && info.global_id >= 0); - paw_get_global(P, info.global_id); paw_push_string(P, "abc"); paw_new_list(P, 1); - status = paw_call(P, 1); + status = paw_call_global(P, info.global_id, 1); check_error(P, status); + check(paw_bytes_used(P) > 0); + check(paw_int(P, -1) == 1); paw_pop(P, 1); @@ -152,25 +153,66 @@ int main(void) } // Perform some VM operations. +#define CHECK_AND_POP_I(i) \ + paw_push_int(P, i); \ + paw_cmpi(P, PAW_CMP_EQ); \ + check(paw_bool(P, -1)); \ + paw_pop(P, 1); { + // -(-1) == 1 + paw_push_int(P, -1); + paw_arithi(P, PAW_ARITH_NEG); + CHECK_AND_POP_I(1); + // 1 + 1 == 2 paw_push_int(P, 1); paw_push_int(P, 1); paw_arithi(P, PAW_ARITH_ADD); - paw_push_int(P, 2); - paw_cmpi(P, PAW_CMP_EQ); - check(paw_bool(P, -1)); - paw_pop(P, 1); + CHECK_AND_POP_I(2); + + // ~1 == -2 + paw_push_int(P, 1); + paw_bitw(P, PAW_BITW_NOT); + CHECK_AND_POP_I(-2); // 1 << 1 <= 2 paw_push_int(P, 1); paw_push_int(P, 1); paw_bitw(P, PAW_BITW_SHL); + CHECK_AND_POP_I(2); + + // 2 <= 2 + paw_push_int(P, 2); paw_push_int(P, 2); paw_cmpi(P, PAW_CMP_LE); check(paw_bool(P, -1)); paw_pop(P, 1); + } +#define CHECK_AND_POP_F(f) \ + paw_push_float(P, f); \ + paw_cmpf(P, PAW_CMP_EQ); \ + check(paw_bool(P, -1)); \ + paw_pop(P, 1); + { + // -(-1.0) == 1.0 + paw_push_float(P, -1.0); + paw_arithf(P, PAW_ARITH_NEG); + CHECK_AND_POP_F(1.0); + + // 1.0 + 1.0 == 2.0 + paw_push_float(P, 1.0); + paw_push_float(P, 1.0); + paw_arithf(P, PAW_ARITH_ADD); + CHECK_AND_POP_F(2.0); + // 1.1 > 1.0 + paw_push_float(P, 1.1); + paw_push_float(P, 1.0); + paw_cmpf(P, PAW_CMP_GT); + check(paw_bool(P, -1)); + paw_pop(P, 1); + } + { // "ab" + "c" + "123" == "abc123" paw_push_string(P, "ab"); paw_push_string(P, "c"); @@ -212,6 +254,15 @@ int main(void) test_print_type(P, "Struct", 0); test_print_type(P, "Enum", 0); + // test foreign objects + void *ptr = paw_new_foreign(P, 8, 1); + ((paw_Int *)ptr)[0] = 42; + check(((paw_Int *)paw_userdata(P, -1))[0] == 42); + paw_pop(P, 1); + + // test format strings + paw_push_fstring(P, "%% %s %u %d %I %c %f %p", "str", 1U, -1, PAW_INT_MIN, 'P', 1.0, P); + finish_test(P); return 0; } diff --git a/test/test_error.c b/test/test_error.c index cce1532..bc6b813 100644 --- a/test/test_error.c +++ b/test/test_error.c @@ -24,7 +24,7 @@ static void write_main(char *out, const char *items, const char *text) #undef ADD_CHUNK } -static void test_compiler_error(int expect, const char *name, const char *item, const char *text) +static void test_compiler_status(int expect, const char *name, const char *item, const char *text) { char buffer[4096]; write_main(buffer, item, text); @@ -36,7 +36,7 @@ static void test_compiler_error(int expect, const char *name, const char *item, paw_close(P); } -static void test_runtime_error(int expect, const char *name, const char *item, const char *text) +static void test_runtime_status(int expect, const char *name, const char *item, const char *text) { char buffer[4096]; write_main(buffer, item, text); @@ -61,9 +61,9 @@ static void test_runtime_error(int expect, const char *name, const char *item, c static void test_name_error(void) { - test_compiler_error(PAW_ENAME, "use_before_def_local", "", "let x = x;"); - test_compiler_error(PAW_ENAME, "undef_variable", "", "x = 1;"); - test_compiler_error(PAW_ENAME, "undef_field", "struct A;", "let a = A.value;"); + test_compiler_status(PAW_ENAME, "use_before_def_local", "", "let x = x;"); + test_compiler_status(PAW_ENAME, "undef_variable", "", "x = 1;"); + test_compiler_status(PAW_ENAME, "undef_field", "struct A;", "let a = A.value;"); } static const char *get_literal(int kind) @@ -95,7 +95,7 @@ static void check_unop_type_error(const char *op, paw_Type k) snprintf(text_buf, sizeof(text_buf), "let x = %s%s;", op, get_literal(k)); - test_compiler_error(PAW_ETYPE, name_buf, "", text_buf); + test_compiler_status(PAW_ETYPE, name_buf, "", text_buf); } static void check_unification_errors(void) @@ -113,7 +113,7 @@ static void check_unification_errors(void) snprintf(text_buf, sizeof(text_buf), "let x = %s; let y = %s; x = y;", get_literal(k), get_literal(k2)); - test_compiler_error(PAW_ETYPE, name_buf, "", text_buf); + test_compiler_status(PAW_ETYPE, name_buf, "", text_buf); } } } @@ -128,7 +128,7 @@ static void check_binop_type_error(const char *op, paw_Type k, paw_Type k2) snprintf(text_buf, sizeof(text_buf), "let x = %s %s %s;", get_literal(k), op, get_literal(k2)); - test_compiler_error(PAW_ETYPE, name_buf, "", text_buf); + test_compiler_status(PAW_ETYPE, name_buf, "", text_buf); } static void check_binop_type_errors(const char *op, paw_Type *types) @@ -183,153 +183,205 @@ static void test_type_error(void) check_binop_type_errors("==", mklist(PAW_TBOOL, PAW_TINT, PAW_TFLOAT, PAW_TSTR)); check_binop_type_errors("!=", mklist(PAW_TBOOL, PAW_TINT, PAW_TFLOAT, PAW_TSTR)); - test_compiler_error(PAW_ETYPE, "call_unit_variant", "enum E {X}", "let x = E::X();"); - test_compiler_error(PAW_ETYPE, "wrong_constructor_args", "enum E {X(int)}", "let x = E::X(1.0);"); + test_compiler_status(PAW_ETYPE, "call_unit_variant", "enum E {X}", "let x = E::X();"); + test_compiler_status(PAW_ETYPE, "wrong_constructor_args", "enum E {X(int)}", "let x = E::X(1.0);"); + test_compiler_status(PAW_ETYPE, "selector_on_function", "fn func() {}", "let a = func.field;"); } static void test_syntax_error(void) { - test_compiler_error(PAW_ESYNTAX, "string_missing_second_surrogate", "", "let s = '\\ud801';"); - test_compiler_error(PAW_ESYNTAX, "string_missing_first_surrogate", "", "let s = '\\udc00';"); - test_compiler_error(PAW_ESYNTAX, "string_malformed_surrogate_1", "", "let s = '\\ud801\\....';"); - test_compiler_error(PAW_ESYNTAX, "string_malformed_surrogate_2", "", "let s = '\\ud801\\u....';"); - test_compiler_error(PAW_ESYNTAX, "string_invalid_surrogate_low", "", "let s = '\\ud801\\udbff';"); - test_compiler_error(PAW_ESYNTAX, "string_invalid_surrogate_high", "", "let s = '\\ud801\\ue000';"); - - test_compiler_error(PAW_ESYNTAX, "misplaced_2dots", "", "let x = ..;"); - test_compiler_error(PAW_ESYNTAX, "misplaced_3dots", "", "let x = ...;"); - test_compiler_error(PAW_ESYNTAX, "misplaced_fat_arrow", "", "let x => 1;"); - test_compiler_error(PAW_ESYNTAX, "missing_end_of_block_comment", "", "/* block comment"); - test_compiler_error(PAW_ESYNTAX, "overflow_integer", "", "let d = -9223372036854775808;"); // overflows before '-' applied - test_compiler_error(PAW_ESYNTAX, "binary_digit_range", "", "let b = 0b001201;"); - test_compiler_error(PAW_ESYNTAX, "octal_digit_range", "", "let o = 0o385273;"); - test_compiler_error(PAW_ESYNTAX, "hex_digit_range", "", "let x = 0x5A2CG3;"); - test_compiler_error(PAW_ESYNTAX, "malformed_binary", "", "let b = 0b00$101;"); - test_compiler_error(PAW_ESYNTAX, "malformed_octal", "", "let o = 0o37=273;"); - test_compiler_error(PAW_ESYNTAX, "malformed_hex", "", "let x = 0y5A2CF3;"); - test_compiler_error(PAW_ESYNTAX, "stmt_after_return", "fn f() {return; f()}", ""); - test_compiler_error(PAW_ESYNTAX, "missing_right_paren", "fn f(a: int, b: int, c: int -> int {return (a + b + c);}", ""); - test_compiler_error(PAW_ESYNTAX, "missing_left_paren", "fn fa: int, b: int, c: int) -> int {return (a + b + c);}", ""); - test_compiler_error(PAW_ESYNTAX, "missing_right_curly", "fn f(a: int, b: int, c: int) -> int {return (a + b + c);", ""); - test_compiler_error(PAW_ESYNTAX, "missing_left_curly", "fn f(a: int, b: int, c: int) -> int return (a + b + c);}", ""); - test_compiler_error(PAW_ESYNTAX, "missing_right_angle", "fn f() {}", ""); - test_compiler_error(PAW_ESYNTAX, "missing_turbo", "struct A", "let a = A;"); - test_compiler_error(PAW_ESYNTAX, "partial_turbo", "struct A", "let a = A:;"); - test_compiler_error(PAW_ESYNTAX, "missing_left_angle_tubofish", "struct A", "let a = A::int>;"); - test_compiler_error(PAW_ESYNTAX, "missing_right_angle_turbofish", "struct A", "let a = A::() {let t = T;}", ""); - test_compiler_error(PAW_ETYPE, "function_is_not_a_type", "fn test() {}", "let a: test = test;"); - test_compiler_error(PAW_ETYPE, "variable_is_not_a_type", "", "let a = 1; let b: a = a;"); - test_compiler_error(PAW_ETYPE, "own_name_is_not_a_type", "", "let a: a = 1;"); + test_compiler_status(PAW_ESYNTAX, "string_missing_second_surrogate", "", "let s = '\\ud801';"); + test_compiler_status(PAW_ESYNTAX, "string_missing_first_surrogate", "", "let s = '\\udc00';"); + test_compiler_status(PAW_ESYNTAX, "string_malformed_surrogate_1", "", "let s = '\\ud801\\....';"); + test_compiler_status(PAW_ESYNTAX, "string_malformed_surrogate_2", "", "let s = '\\ud801\\u....';"); + test_compiler_status(PAW_ESYNTAX, "string_invalid_surrogate_low", "", "let s = '\\ud801\\udbff';"); + test_compiler_status(PAW_ESYNTAX, "string_invalid_surrogate_high", "", "let s = '\\ud801\\ue000';"); + + test_compiler_status(PAW_ESYNTAX, "misplaced_2dots", "", "let x = ..;"); + test_compiler_status(PAW_ESYNTAX, "misplaced_3dots", "", "let x = ...;"); + test_compiler_status(PAW_ESYNTAX, "misplaced_fat_arrow", "", "let x => 1;"); + test_compiler_status(PAW_ESYNTAX, "missing_end_of_block_comment", "", "/* block comment"); + test_compiler_status(PAW_ESYNTAX, "overflow_integer", "", "let d = -9223372036854775808;"); // overflows before '-' applied + test_compiler_status(PAW_ESYNTAX, "binary_digit_range", "", "let b = 0b001201;"); + test_compiler_status(PAW_ESYNTAX, "octal_digit_range", "", "let o = 0o385273;"); + test_compiler_status(PAW_ESYNTAX, "hex_digit_range", "", "let x = 0x5A2CG3;"); + test_compiler_status(PAW_ESYNTAX, "malformed_binary", "", "let b = 0b00$101;"); + test_compiler_status(PAW_ESYNTAX, "malformed_octal", "", "let o = 0o37=273;"); + test_compiler_status(PAW_ESYNTAX, "malformed_hex", "", "let x = 0y5A2CF3;"); + test_compiler_status(PAW_ESYNTAX, "stmt_after_return", "fn f() {return; f()}", ""); + test_compiler_status(PAW_ESYNTAX, "missing_right_paren", "fn f(a: int, b: int, c: int -> int {return (a + b + c);}", ""); + test_compiler_status(PAW_ESYNTAX, "missing_left_paren", "fn fa: int, b: int, c: int) -> int {return (a + b + c);}", ""); + test_compiler_status(PAW_ESYNTAX, "missing_right_curly", "fn f(a: int, b: int, c: int) -> int {return (a + b + c);", ""); + test_compiler_status(PAW_ESYNTAX, "missing_left_curly", "fn f(a: int, b: int, c: int) -> int return (a + b + c);}", ""); + test_compiler_status(PAW_ESYNTAX, "missing_right_angle", "fn f() {}", ""); + test_compiler_status(PAW_ESYNTAX, "missing_turbo", "struct A", "let a = A;"); + test_compiler_status(PAW_ESYNTAX, "partial_turbo", "struct A", "let a = A:;"); + test_compiler_status(PAW_ESYNTAX, "missing_left_angle_tubofish", "struct A", "let a = A::int>;"); + test_compiler_status(PAW_ESYNTAX, "missing_right_angle_turbofish", "struct A", "let a = A::() {let t = T;}", ""); + test_compiler_status(PAW_ETYPE, "function_is_not_a_type", "fn test() {}", "let a: test = test;"); + test_compiler_status(PAW_ETYPE, "variable_is_not_a_type", "", "let a = 1; let b: a = a;"); + test_compiler_status(PAW_ETYPE, "own_name_is_not_a_type", "", "let a: a = 1;"); + + test_compiler_status(PAW_ENAME, "duplicate_global", "struct A; struct A;", ""); } static void test_closure_error(void) { - test_compiler_error(PAW_OK, "infer_by_usage", "", "let f = |x| {}; f(1);"); + test_compiler_status(PAW_OK, "infer_by_usage", "", "let f = |x| {}; f(1);"); - test_compiler_error(PAW_ETYPE, "call_with_wrong_type_annotation", "", "let f = |x: int| x; f(2.0);"); - test_compiler_error(PAW_ETYPE, "call_with_wrong_type_inference", "", "let f = |x| x; f(1); f(2.0);"); - test_compiler_error(PAW_ETYPE, "cannot_infer_unused_param", "", "let f = |x| {};"); + test_compiler_status(PAW_ETYPE, "call_with_wrong_type_annotation", "", "let f = |x: int| x; f(2.0);"); + test_compiler_status(PAW_ETYPE, "call_with_wrong_type_inference", "", "let f = |x| x; f(1); f(2.0);"); + test_compiler_status(PAW_ETYPE, "cannot_infer_unused_param", "", "let f = |x| {};"); } static void test_arithmetic_error(void) { - test_runtime_error(PAW_ERUNTIME, "division_by_0_int", "", "let x = 1 / 0;"); - test_runtime_error(PAW_ERUNTIME, "division_by_0_float", "", "let x = 1.0 / 0.0;"); - test_runtime_error(PAW_ERUNTIME, "negative_left_shift", "", "let x = 1 << -2;"); - test_runtime_error(PAW_ERUNTIME, "negative_right_shift", "", "let x = 1 >> -2;"); + test_runtime_status(PAW_ERUNTIME, "division_by_0_int", "", "let x = 1 / 0;"); + test_runtime_status(PAW_ERUNTIME, "division_by_0_float", "", "let x = 1.0 / 0.0;"); + test_runtime_status(PAW_ERUNTIME, "negative_left_shift", "", "let x = 1 << -2;"); + test_runtime_status(PAW_ERUNTIME, "negative_right_shift", "", "let x = 1 >> -2;"); } static void test_tuple_error(void) { - test_compiler_error(PAW_ETYPE, "tuple_square_brackets", "", "let x = (1, 2); let y = x[0];"); - test_compiler_error(PAW_ETYPE, "tuple_index_out_of_range", "", "let x = (1, 2); let y = x.2;"); + test_compiler_status(PAW_ETYPE, "tuple_square_brackets", "", "let x = (1, 2); let y = x[0];"); + test_compiler_status(PAW_ETYPE, "tuple_named_field", "", "let x = (1, 2); let y = x.first;"); + test_compiler_status(PAW_ETYPE, "tuple_index_out_of_range", "", "let x = (1, 2); let y = x.2;"); } static void test_struct_error(void) { - test_compiler_error(PAW_ESYNTAX, "struct_unit_with_braces_on_def", "struct A {}", "let a = A;"); - test_compiler_error(PAW_ESYNTAX, "struct_unit_with_braces_on_init", "struct A;", "let a = A{};"); - test_compiler_error(PAW_ESYNTAX, "struct_unit_without_semicolon", "struct A", ""); - test_compiler_error(PAW_ESYNTAX, "struct_missing_braces", "struct A {a: int}", "let a = A;"); - test_compiler_error(PAW_ENAME, "struct_missing_only_field", "struct A {a: int}", "let a = A{};"); - test_compiler_error(PAW_ENAME, "struct_missing_field", "struct A {a: int, b: float}", "let a = A{a: 1};"); - test_compiler_error(PAW_ENAME, "struct_extra_field", "struct A {a: int}", "let a = A{a: 1, b: 2};"); - test_compiler_error(PAW_ENAME, "struct_duplicate_field", "struct A {a: int}", "let a = A{a: 1, a: 1};"); - test_compiler_error(PAW_ENAME, "struct_wrong_field", "struct A {a: int}", "let a = A{b: 2};"); - test_compiler_error(PAW_ETYPE, "struct_access_by_index", "struct S{x: int}", "let x = S{x: 1}; let y = x.0;"); + test_compiler_status(PAW_ESYNTAX, "struct_unit_with_braces_on_def", "struct A {}", "let a = A;"); + test_compiler_status(PAW_ESYNTAX, "struct_unit_with_braces_on_init", "struct A;", "let a = A{};"); + test_compiler_status(PAW_ESYNTAX, "struct_unit_without_semicolon", "struct A", ""); + test_compiler_status(PAW_ESYNTAX, "struct_missing_braces", "struct A {a: int}", "let a = A;"); + test_compiler_status(PAW_ENAME, "struct_missing_only_field", "struct A {a: int}", "let a = A{};"); + test_compiler_status(PAW_ENAME, "struct_missing_field", "struct A {a: int, b: float}", "let a = A{a: 1};"); + test_compiler_status(PAW_ENAME, "struct_extra_field", "struct A {a: int}", "let a = A{a: 1, b: 2};"); + test_compiler_status(PAW_ENAME, "struct_duplicate_field", "struct A {a: int}", "let a = A{a: 1, a: 1};"); + test_compiler_status(PAW_ENAME, "struct_wrong_field", "struct A {a: int}", "let a = A{b: 2};"); + test_compiler_status(PAW_ETYPE, "struct_access_by_index", "struct S{x: int}", "let x = S{x: 1}; let y = x.0;"); + test_compiler_status(PAW_ETYPE, "struct_not_enough_types", "struct S;", "let x = S::;"); + test_compiler_status(PAW_ETYPE, "struct_too_many_types", "struct S;", "let x = S::;"); } static void test_enum_error(void) { - test_compiler_error(PAW_ESYNTAX, "enum_unit_with_braces_on_def", "enum A {}", "let a = A;"); - test_compiler_error(PAW_ETYPE, "enum_unit_with_braces_on_init", "enum A;", + test_compiler_status(PAW_ESYNTAX, "enum_unit_with_braces_on_def", "enum A {}", "let a = A;"); + test_compiler_status(PAW_ETYPE, "enum_unit_with_braces_on_init", "enum A;", "let a = A{}; // looks like struct literal"); - test_compiler_error(PAW_ESYNTAX, "enum_unit_without_semicolon", "enum A", ""); - test_compiler_error(PAW_ESYNTAX, "enum_missing_variant", "enum A {X}", "let a = A;"); - test_compiler_error(PAW_ENAME, "enum_duplicate_variant", "enum A {X, X}", ""); - test_compiler_error(PAW_ENAME, "enum_nonexistent_variant", "enum A {X}", "let a = A::Y;"); - test_compiler_error(PAW_ETYPE, "enum_missing_only_field", "enum A {X(int)}", "let a = A::X;"); - test_compiler_error(PAW_ESYNTAX, "enum_missing_field", "enum A {X(int, float)}", "let a = A::X(42);"); - test_compiler_error(PAW_ESYNTAX, "enum_extra_field", "enum A {X(int)}", "let a = A::X(42, true);"); - test_compiler_error(PAW_ETYPE, "enum_wrong_field_type", "enum A {X(int)}", "let a = A::X(1.0);"); - test_compiler_error(PAW_ETYPE, "enum_requires_pattern_matching", "enum E{X(int)}", "let x = E::X(1); let y = x.0;"); + test_compiler_status(PAW_ESYNTAX, "enum_unit_without_semicolon", "enum A", ""); + test_compiler_status(PAW_ESYNTAX, "enum_missing_variant", "enum A {X}", "let a = A;"); + test_compiler_status(PAW_ENAME, "enum_duplicate_variant", "enum A {X, X}", ""); + test_compiler_status(PAW_ENAME, "enum_nonexistent_variant", "enum A {X}", "let a = A::Y;"); + test_compiler_status(PAW_ETYPE, "enum_missing_only_field", "enum A {X(int)}", "let a = A::X;"); + test_compiler_status(PAW_ESYNTAX, "enum_missing_field", "enum A {X(int, float)}", "let a = A::X(42);"); + test_compiler_status(PAW_ESYNTAX, "enum_extra_field", "enum A {X(int)}", "let a = A::X(42, true);"); + test_compiler_status(PAW_ETYPE, "enum_wrong_field_type", "enum A {X(int)}", "let a = A::X(1.0);"); + test_compiler_status(PAW_ETYPE, "enum_requires_pattern_matching", "enum E{X(int)}", "let x = E::X(1); let y = x.0;"); } static void test_list_error(void) { - test_compiler_error(PAW_ETYPE, "list_cannot_infer", "", "let a = [];"); - test_compiler_error(PAW_ETYPE, "list_cannot_infer_binop", "", "let a = [] + [];"); - test_compiler_error(PAW_ETYPE, "list_use_before_inference", "", "let a = []; let b = #a;"); - test_compiler_error(PAW_ETYPE, "list_incompatible_types", "", "let a = [1]; a = [2.0];"); - test_compiler_error(PAW_ETYPE, "list_incompatible_types_2", "", "let a = []; if true {a = [0];} else {a = [true];}"); - test_compiler_error(PAW_ETYPE, "list_mixed_types", "", "let a = [1, 2, 3, 4, '5'];"); - test_compiler_error(PAW_ETYPE, "list_mixed_nesting", "", "let a = [[[1]], [[2]], [3]];"); + test_compiler_status(PAW_ETYPE, "list_cannot_infer", "", "let a = [];"); + test_compiler_status(PAW_ETYPE, "list_cannot_infer_binop", "", "let a = [] + [];"); + test_compiler_status(PAW_ETYPE, "list_use_before_inference", "", "let a = []; let b = #a;"); + test_compiler_status(PAW_ETYPE, "list_incompatible_types", "", "let a = [1]; a = [2.0];"); + test_compiler_status(PAW_ETYPE, "list_incompatible_types_2", "", "let a = []; if true {a = [0];} else {a = [true];}"); + test_compiler_status(PAW_ETYPE, "list_mixed_types", "", "let a = [1, 2, 3, 4, '5'];"); + test_compiler_status(PAW_ETYPE, "list_mixed_nesting", "", "let a = [[[1]], [[2]], [3]];"); } static void test_map_error(void) { - test_compiler_error(PAW_ETYPE, "map_cannot_infer", "", "let a = [:];"); - test_compiler_error(PAW_ETYPE, "map_use_before_inference", "", "let a = [:]; let b = #a;"); - test_compiler_error(PAW_ETYPE, "map_incompatible_types", "", "let a = [1: 2]; a = [3: 4.0];"); - test_compiler_error(PAW_ETYPE, "map_incompatible_types_2", "", "let a = [:]; if true {a = [0: 0];} else {a = [1: true];}"); - test_compiler_error(PAW_ETYPE, "map_mixed_types", "", "let a = [1: 2, 3: 4, 5: '6'];"); - test_compiler_error(PAW_ETYPE, "map_mixed_nesting", "", "let a = [1: [1: 1], 2: [2: 2], 3: [3: [3: 3]]];"); - test_compiler_error(PAW_ETYPE, "map_nonhashable_literal_key", "", "let map = [[1]: 1];"); - test_compiler_error(PAW_ETYPE, "map_nonhashable_type_key", "", "let map: [[int]: int] = [:];"); + test_compiler_status(PAW_ETYPE, "map_cannot_infer", "", "let a = [:];"); + test_compiler_status(PAW_ETYPE, "map_use_before_inference", "", "let a = [:]; let b = #a;"); + test_compiler_status(PAW_ETYPE, "map_incompatible_types", "", "let a = [1: 2]; a = [3: 4.0];"); + test_compiler_status(PAW_ETYPE, "map_incompatible_types_2", "", "let a = [:]; if true {a = [0: 0];} else {a = [1: true];}"); + test_compiler_status(PAW_ETYPE, "map_mixed_types", "", "let a = [1: 2, 3: 4, 5: '6'];"); + test_compiler_status(PAW_ETYPE, "map_mixed_nesting", "", "let a = [1: [1: 1], 2: [2: 2], 3: [3: [3: 3]]];"); + test_compiler_status(PAW_ETYPE, "map_nonhashable_literal_key", "", "let map = [[1]: 1];"); + test_compiler_status(PAW_ETYPE, "map_nonhashable_type_key", "", "let map: [[int]: int] = [:];"); + test_compiler_status(PAW_ETYPE, "map_slice", "", "let map = [:]; let val = map[0:10];"); +} + +static int next_conflicting_int(paw_Env *P) +{ + paw_unused(P); + // return the pointer, caller reinterprets as an integer + return 1; +} + +static void test_gc_conflict(void) +{ + const char source[] = + "pub fn conflicting_int(t: T) -> int;\n" + "pub fn main() {\n" + " let N = 500;\n" + // create a bunch of dynamically-allocated objects + " let objects = [];\n" + " for i = 0, N {_list_push(objects, [i, i + 1, i + 2]);}\n" + // fill a list with integers that conflict with the object addresses + " let conflicts = [];\n" + " for i = 0, N {_list_push(conflicts, conflicting_int(objects[i]));}\n" + // use a lot of memory to cause garbage collections + " let memory = [];\n" + " for i = 0, N {_list_push(memory, [[i], [i + 1], [i + 2]]);}\n" + "}\n"; + + paw_Env *P = paw_open(&(struct paw_Options){0}); + pawL_register_func(P, "conflicting_int", next_conflicting_int, 0); + + int status = pawL_load_chunk(P, "gc_conflict", source); + check(status == PAW_OK); + + paw_push_string(P, "main"); + paw_mangle_name(P, NULL); + + struct paw_Item info; + status = paw_lookup_item(P, &info); + check(status == PAW_OK && info.global_id >= 0); + paw_get_global(P, info.global_id); + + status = paw_call(P, 0); + check(status == PAW_OK); + + paw_close(P); } int main(void) { + test_gc_conflict(); test_enum_error(); test_name_error(); test_syntax_error(); diff --git a/test/test_impl.c b/test/test_impl.c index e15ed70..2b01e8d 100644 --- a/test/test_impl.c +++ b/test/test_impl.c @@ -2,6 +2,12 @@ // This source code is licensed under the MIT License, which can be found in // LICENSE.md. See AUTHORS.md for a list of contributor names. +#include +#include +#include +#include +#include +#include #include "call.h" #include "map.h" #include "paw.h" @@ -9,12 +15,6 @@ #include "test.h" #include "util.h" #include "value.h" -#include -#include -#include -#include -#include -#include // Test the primitive value representations static void test_primitives(void) @@ -275,33 +275,33 @@ static void driver(void (*callback)(paw_Env *)) test_close(P, &a); } -static int parse_int(paw_Env *P, void *ud) +static int parse_int(paw_Env *P, const char *text) { - const int rc = pawV_parse_int(P, ud, 0); - if (rc == PAW_OK) paw_pop(P, 1); - return rc; + paw_Int i; + return pawV_parse_int(P, text, 0, &i); } static void roundtrip_int(paw_Env *P, paw_Int i); -static void pac_int_aux(paw_Env *P, void *ud, paw_Int result) +static void pac_int_aux(paw_Env *P, const char *text, paw_Int result) { - check(PAW_OK == pawV_parse_int(P, ud, 0)); - check(paw_int(P, -1) == result); - paw_pop(P, 1); + paw_Int i; + check(PAW_OK == pawV_parse_int(P, text, 0, &i)); + check(i == result); } static void roundtrip_int(paw_Env *P, paw_Int i) { - pawV_to_string(P, (Value){.i = i}, PAW_TINT, NULL); + paw_push_int(P, i); + paw_to_string(P, -1, PAW_TINT, NULL); const char *str = paw_string(P, -1); pac_int_aux(P, ERASE_TYPE(str), i); paw_pop(P, 1); } -static void parse_and_check_int(paw_Env *P, void *ud, paw_Int result) +static void parse_and_check_int(paw_Env *P, const char *text, paw_Int result) { - pac_int_aux(P, ud, result); + pac_int_aux(P, text, result); roundtrip_int(P, result); } @@ -329,36 +329,37 @@ static void test_parse_int(paw_Env *P) check(PAW_EOVERFLOW == parse_int(P, "999999999999999999999999999999999999999")); check(PAW_EOVERFLOW == parse_int(P, "-999999999999999999999999999999999999999")); - check(PAW_EVALUE == pawV_parse_int(P, "0b0", 10 /* wrong base */)); - check(PAW_EVALUE == pawV_parse_int(P, "0o0", 10 /* wrong base */)); - check(PAW_EVALUE == pawV_parse_int(P, "0x0", 10 /* wrong base */)); + paw_Int i; + check(PAW_EVALUE == pawV_parse_int(P, "0b0", 10 /* wrong base */, &i)); + check(PAW_EVALUE == pawV_parse_int(P, "0o0", 10 /* wrong base */, &i)); + check(PAW_EVALUE == pawV_parse_int(P, "0x0", 10 /* wrong base */, &i)); } -static int parse_float(paw_Env *P, void *ud) +static int parse_float(paw_Env *P, const char *text) { - const int rc = pawV_parse_float(P, ud); - if (rc == PAW_OK) paw_pop(P, 1); - return rc; + paw_Float f; + return pawV_parse_float(P, text, &f); } -static void pac_float_aux(paw_Env *P, void *ud, paw_Float result) +static void pac_float_aux(paw_Env *P, const char *text, paw_Float result) { - check(PAW_OK == pawV_parse_float(P, ud)); - check(paw_float(P, -1) == result); - paw_pop(P, 1); + paw_Float f; + check(PAW_OK == pawV_parse_float(P, text, &f)); + check(f == result); } static void roundtrip_float(paw_Env *P, paw_Float f) { - pawV_to_string(P, (Value){.f = f}, PAW_TFLOAT, NULL); + paw_push_float(P, f); + paw_to_string(P, -1, PAW_TFLOAT, NULL); const char *str = paw_string(P, -1); pac_float_aux(P, ERASE_TYPE(str), f); paw_pop(P, 1); } -static void parse_and_check_float(paw_Env *P, void *ud, paw_Float result) +static void parse_and_check_float(paw_Env *P, const char *text, paw_Float result) { - pac_float_aux(P, ud, result); + pac_float_aux(P, text, result); roundtrip_float(P, result); } @@ -410,50 +411,29 @@ static void test_immediates(void) #undef CHECK_BOUND } -#include "ast.h" -#include "hir.h" -#include "compile.h" -#include "os.h" -#include - -static void maybe_print_tree(void) +static void test_buffer(paw_Env *P) { -#ifdef PAW_TEST_PRINT_TREES - fprintf(stderr, "%s\n", paw_string(P, -1)); -#endif -} - -static void dump_trees_from_file(paw_Env *P, const char *name) -{ - const char *pathname = test_pathname(name); - - FILE *file = pawO_open(pathname, "r"); - struct TestReader rd = {.file = file}; - rd.data = rd.buf; - check(file); - - struct Compiler C; - struct DynamicMem dm = {0}; - pawP_startup(P, &C, &dm, "test"); - - struct Ast *ast = pawP_parse(&C, test_reader, &rd); - pawAst_dump(ast); - maybe_print_tree(); - paw_pop(P, 1); + Buffer buf; + pawL_init_buffer(P, &buf); + pawL_add_char(P, &buf, 'a'); + pawL_add_string(P, &buf, "bc"); + pawL_add_nstring(P, &buf, "def", 2); + check(buf.size == 5); + check(memcmp(buf.data, "abcde", 5) == 0); + pawL_discard_result(P, &buf); + + pawL_init_buffer(P, &buf); + for (int i = 0; i < 1234; ++i) { + paw_push_int(P, i); + pawL_add_value(P, &buf, PAW_TINT); + } + pawL_buffer_resize(P, &buf, 16); + pawL_push_result(P, &buf); - struct Hir *hir = pawP_resolve(&C, ast); - pawHir_dump(hir); - maybe_print_tree(); + paw_push_string(P, "0123456789101112"); + paw_cmps(P, PAW_CMP_EQ); + check(paw_bool(P, -1)); paw_pop(P, 1); - - pawP_teardown(P, &dm); -} - -static void test_dump_trees(paw_Env *P) -{ -#define DUMP_TREE(name) dump_trees_from_file(P, #name); - TEST_SCRIPTS(DUMP_TREE) -#undef DUMP_TREE } int main(void) @@ -469,6 +449,6 @@ int main(void) driver(test_map_extend); driver(test_parse_int); driver(test_parse_float); - driver(test_dump_trees); + driver(test_buffer); return 0; } diff --git a/test/test_oom.c b/test/test_oom.c index 2aa4b19..778b444 100644 --- a/test/test_oom.c +++ b/test/test_oom.c @@ -5,20 +5,10 @@ // test_oom.c: Heap exhaustion tests #include "test.h" +#include "alloc.h" #include "call.h" #include "env.h" -static void *oom_alloc(void *ud, void *ptr, size_t size0, size_t size) -{ - struct TestAlloc *a = ud; - if (a->extra + size0 < size) { - return NULL; - } - a->extra += size0; - a->extra -= size; - return test_alloc(ud, ptr, size0, size); -} - static int run_tests(paw_Env *P) { struct DefList defs = P->defs; @@ -29,45 +19,147 @@ static int run_tests(paw_Env *P) if (!def->hdr.is_pub) continue; const String *name = def->hdr.name; if (name->length >= kLength && - 0 == memcmp(name->text, kPrefix, kLength)) { + memcmp(name->text, kPrefix, kLength) == 0) { check(def->hdr.kind == DEF_FUNC); - pawC_pushv(P, *pawE_get_val(P, def->func.vid)); + paw_push_zero(P, 1); + P->top.p[-1] = *pawE_get_val(P, def->func.vid); return paw_call(P, 0); } } return PAW_OK; } -static int script(const char *name, size_t heap_size) +static void check_status(paw_Env *P, int status) +{ + if (status != PAW_OK && status != PAW_EMEMORY) { + check(paw_get_count(P) >= 1); + const char *s = paw_string(P, -1); + fprintf(stderr, "%s\n", s); + abort(); + } +} + +static int run_script_or_chunk(const char *name_or_chunk, size_t heap_size, paw_Bool is_chunk) { paw_Env *P = paw_open(&(struct paw_Options){ .heap_size = heap_size, }); if (P == NULL) return PAW_EMEMORY; - int status = test_open_file(P, name); + int status = is_chunk + ? test_open_string(P, name_or_chunk) + : test_open_file(P, name_or_chunk); if (status == PAW_OK) status = run_tests(P); + check_status(P, status); paw_close(P); return status; } -static void test_oom(const char *name) +static size_t s_passing_heap_size; +static const char *s_name_or_chunk; +static paw_Bool s_is_chunk; +static int s_count; + +static void start_oom(const char *name_or_chunk, paw_Bool is_chunk) +{ + s_count = 0; + s_passing_heap_size = 0; + s_name_or_chunk = name_or_chunk; + s_is_chunk = is_chunk; +} + +static int run_one(size_t heap_size) +{ + const int status = run_script_or_chunk(s_name_or_chunk, heap_size, s_is_chunk); + if (status != PAW_EMEMORY) { + check(status == PAW_OK); + s_passing_heap_size = heap_size; + } else { + ++s_count; + } + return status; +} + +static void finish_oom(void) +{ + check(s_count > 0); + + printf("[PASS] %s: passing_heap_size=%zu, oom_count=%d\n", + s_is_chunk ? "(chunk)" : s_name_or_chunk, + s_passing_heap_size, s_count); +} + +static void test_oom(const char *name_or_chunk, paw_Bool is_chunk) { - size_t heap_size = 1; - int count = 0; - int rc; + // list of heap sizes that are too small + const size_t special_sizes[] = { + 1, + sizeof(paw_Env), + sizeof(struct Heap), + sizeof(struct Heap) + 1, + sizeof(struct Heap) + 10, + sizeof(struct Heap) + 100, + sizeof(struct Heap) + 200, + sizeof(struct Heap) + 300, + sizeof(struct Heap) + 500, + sizeof(struct Heap) + 1000, + sizeof(struct Heap) + 10000, + }; + start_oom(name_or_chunk, is_chunk); + for (size_t i = 0; i < paw_countof(special_sizes); ++i) { + const int status = run_one(special_sizes[i]); + check(status == PAW_EMEMORY); + } + + int status; + size_t heap_size = 1 << 10; do { - rc = script(name, heap_size); - heap_size *= 2; - ++count; - } while (rc == PAW_EMEMORY); - check(rc == PAW_OK); - check(count > 0); - printf("OOM count: %d\n", count); + status = run_one(heap_size); + heap_size += heap_size; + } while (status != PAW_OK); + finish_oom(); +} + +static void test_call_frames(void) +{ + test_oom( + "fn poly_recur(_: T, n: int) {\n" + " if n > 0 { \n" + " poly_recur(_, n - 1); \n" + " } \n" + "} \n" + "fn recur(n: int) { \n" + " if n > 0 { \n" + " recur(n - 1);\n" + " } \n" + "} \n" + "pub fn test_call_frames() {\n" + " recur(25);\n" + " poly_recur(true, 250);\n" + " poly_recur(1.0, 2500);\n" + "}\n", PAW_TRUE); +} + +static void test_list_ops(void) +{ + test_oom( + "fn push_n(list: [T], value: T, n: int) {\n" + " for i = 0, n { \n" + " _list_push(list, value); \n" + " } \n" + "} \n" + "pub fn test_lists() { \n" + " let list = []; \n" + " push_n(list, 42, 10000);\n" + "}\n", PAW_TRUE); } int main(void) { -#define RUN_SCRIPT(name) test_oom(#name); +#define RUN_SCRIPT(name) test_oom(#name, PAW_FALSE); TEST_SCRIPTS(RUN_SCRIPT) +#undef RUN_SCRIPT + + test_call_frames(); + test_list_ops(); } diff --git a/test/test_rt.c b/test/test_rt.c index b37e85f..bf6636a 100644 --- a/test/test_rt.c +++ b/test/test_rt.c @@ -48,7 +48,8 @@ static void run_tests(const char *name, struct TestAlloc *a, const char *prefix) // toplevel functions prefixed with 'test_' must be public check(def->hdr.is_pub); fprintf(stderr, " %s\n", def->func.name->text); - pawC_pushv(P, *pawE_get_val(P, def->func.vid)); + paw_push_zero(P, 1); + P->top.p[-1] = *pawE_get_val(P, def->func.vid); status = paw_call(P, 0); if (handle_error(P, status)) { ++s_counters.runtime_errors;