diff --git a/src/alloc.c b/src/alloc.c index 18e1c92..b2b659f 100644 --- a/src/alloc.c +++ b/src/alloc.c @@ -9,8 +9,7 @@ #include "mem.h" #define MIN_RUNTIME_SIZE (32 * 1024) -#define MIN_HEAP_SIZE (aligned(sizeof(struct BlockAllocator)) + \ - aligned(sizeof(struct FastBins)) + \ +#define MIN_HEAP_SIZE (aligned(sizeof(struct Allocator)) + \ MIN_RUNTIME_SIZE) #define OS_MALLOC(P, size) ((P)->alloc((P)->ud, NULL, 0, size)) @@ -19,11 +18,6 @@ #define BUMP(p, n) ERASE_TYPE(CAST_UPTR(p) + (n)) #define ERASE_TYPE(p) CAST(p, void *) -static inline paw_Bool is_pow2(size_t v) -{ - return v > 0 && !(v & (v >> 1)); -} - static inline paw_Bool is_aligned(void *p, size_t align) { return !(CAST_UPTR(p) & (align - 1)); @@ -51,21 +45,11 @@ void __asan_unpoison_memory_region(const volatile void *ptr, size_t size); # define UNPOISON_MEMORY_REGION(p, z) #endif -#define BIN_FACTOR 8 -#define MIN_BIN_SIZE (sizeof(Value) * 2) -#define MAX_BIN_SIZE (MIN_BIN_SIZE * FAST_BIN_COUNT) #define FLAGS_PER_BYTE 8 - -#define BIN_ID(size) CHECK_EXP((size) > 0 && aligned(size), ((size) - 1) / MIN_BIN_SIZE) #define FLAG_BASE(H, uptr) CHECK_EXP(Z_IN_BOUNDS(H, uptr), ((uptr) - (H)->bounds[0]) / sizeof(void *)) #define FLAG_ID(base) ((base) / FLAGS_PER_BYTE) #define FLAG_BIT(base) ((base) % FLAGS_PER_BYTE) -struct BinInfo { - struct BinInfo *prev; - char buffer[]; -}; - struct BlockId { uint32_t v; }; @@ -86,15 +70,16 @@ struct Block { #define B_HDR(L, i) (&(L)->blocks[(i) - 1].hdr) #define B_LIST(L, i) (&(L)->blocks[i].list) -#define BLOCK_NLISTS 61 +#define MAX_SMALL 10 +#define NUM_HASH 61 #define KEY_BLOCK_SHIFT 3 #define BLOCK_SIZE sizeof(struct Block) #define BAD_BLOCK 0 -// Allocator for larger regions of memory -// Based off of mem3.c from SQLite -struct BlockAllocator { - struct BlockId free[BLOCK_NLISTS]; +// Allocator based off of mem3.c from SQLite +struct Allocator { + struct BlockId small[MAX_SMALL - 1]; + struct BlockId hash[NUM_HASH]; struct Block *blocks; size_t nblocks; @@ -130,13 +115,10 @@ void pawZ_clear_flag(struct Heap *H, uintptr_t uptr) *pflag = *pflag & ~(1 << FLAG_BIT(id)); } -static void *block_malloc(struct BlockAllocator *, size_t); -static void block_free(struct BlockAllocator *, void *, size_t); - -static void init_block_allocator(struct BlockAllocator *a, void *heap, size_t heap_size) +static void init_block_allocator(struct Allocator *a, void *heap, size_t heap_size) { paw_assert(is_aligned(heap, PAW_ALIGN)); - memset(a->free, 0, sizeof(a->free)); + memset(a, 0, sizeof(*a)); assert(BLOCK_SIZE == 8); a->blocks = heap; @@ -151,13 +133,13 @@ static void init_block_allocator(struct BlockAllocator *a, void *heap, size_t he B_HDR(a, 1 + a->nblocks)->size4x = 1; } -static struct Block *block_at(struct BlockAllocator *a, struct BlockId id) +static struct Block *block_at(struct Allocator *a, struct BlockId id) { paw_assert(id.v < a->nblocks); return &a->blocks[id.v]; } -static void unlink_from_list(struct BlockAllocator *a, struct BlockId id, struct BlockId *proot) +static void unlink_from_list(struct Allocator *a, struct BlockId id, struct BlockId *proot) { struct Block *b = block_at(a, id); const struct BlockId prev = b->list.prev; @@ -176,18 +158,22 @@ static void unlink_from_list(struct BlockAllocator *a, struct BlockId id, struct b->list.next.v = 0; } -static void unlink_block(struct BlockAllocator *a, struct BlockId id) +static void unlink_block(struct Allocator *a, struct BlockId id) { paw_assert((B_HDR(a, id.v)->size4x & 1) == 0); paw_assert(id.v >= 1); const uint32_t size = B_HDR(a, id.v)->size4x / 4; paw_assert(size == B_HDR(a, id.v + size)->prev_size); paw_assert(size >= 2); - const uint32_t hash = size % BLOCK_NLISTS; - unlink_from_list(a, id, &a->free[hash]); + if (size <= MAX_SMALL) { + unlink_from_list(a, id, &a->small[size - 2]); + } else { + const uint32_t hash = size % NUM_HASH; + unlink_from_list(a, id, &a->hash[hash]); + } } -static void link_into_list(struct BlockAllocator *a, struct BlockId id, struct BlockId *proot) +static void link_into_list(struct Allocator *a, struct BlockId id, struct BlockId *proot) { B_LIST(a, id.v)->next = *proot; B_LIST(a, id.v)->prev.v = 0; @@ -197,18 +183,22 @@ static void link_into_list(struct BlockAllocator *a, struct BlockId id, struct B *proot = id; } -static void link_block(struct BlockAllocator *a, struct BlockId id) +static void link_block(struct Allocator *a, struct BlockId id) { paw_assert(id.v >= 1); paw_assert((B_HDR(a, id.v)->size4x & 1) == 0); const uint32_t size = B_HDR(a, id.v)->size4x / 4; paw_assert(size == B_HDR(a, id.v + size)->prev_size); paw_assert(size >= 2); - const uint32_t hash = size % BLOCK_NLISTS; - link_into_list(a, id, &a->free[hash]); + if (size <= MAX_SMALL) { + link_into_list(a, id, &a->small[size - 2]); + } else { + const uint32_t hash = size % NUM_HASH; + link_into_list(a, id, &a->hash[hash]); + } } -static void fix_block_list(struct BlockAllocator *a, struct BlockId *proot) +static void fix_block_list(struct Allocator *a, struct BlockId *proot) { struct BlockId next; @@ -240,7 +230,7 @@ static void fix_block_list(struct BlockAllocator *a, struct BlockId *proot) } } -static void *checkout_block(struct BlockAllocator *a, struct BlockId i, uint32_t nblocks) +static void *checkout_block(struct Allocator *a, struct BlockId i, uint32_t nblocks) { paw_assert(i.v >= 1); paw_assert(B_HDR(a, i.v)->size4x / 4 == nblocks); @@ -252,7 +242,7 @@ static void *checkout_block(struct BlockAllocator *a, struct BlockId i, uint32_t return &a->blocks[i.v]; } -static void *key_block_alloc(struct BlockAllocator *a, uint32_t nblocks) +static void *key_block_alloc(struct Allocator *a, uint32_t nblocks) { assert(a->key_size >= nblocks); if (nblocks >= a->key_size - 1) { @@ -279,25 +269,33 @@ static void *key_block_alloc(struct BlockAllocator *a, uint32_t nblocks) } #define TOO_MANY_BLOCKS(nbytes) ((nbytes + 11) / BLOCK_SIZE > UINT32_MAX) -#define COMPUTE_NUM_BLOCKS(nbytes) CHECK_EXP(BLOCK_SIZE == 8 && (nbytes) > 12, \ - ((nbytes) + 11) / BLOCK_SIZE) +#define COMPUTE_NUM_BLOCKS(nbytes) CHECK_EXP(BLOCK_SIZE == 8, (nbytes) <= 12 ? 2 :((nbytes) + 11) / 8) // Modified from SQLite (mem3.c) -static void *block_malloc(struct BlockAllocator *a, size_t nbytes) +static void *z_malloc(struct Heap *H, size_t nbytes) { + struct Allocator *a = H->a; if (TOO_MANY_BLOCKS(nbytes)) return NULL; uint32_t nblocks = COMPUTE_NUM_BLOCKS(nbytes); paw_assert(nblocks >= 2); // search for an exact fit - const uint32_t hash = nblocks % BLOCK_NLISTS; - for (struct BlockId id = a->free[hash]; - id.v != BAD_BLOCK; - id = B_LIST(a, id.v)->next) { - if (B_HDR(a, id.v)->size4x / 4 == nblocks) { - unlink_from_list(a, id, &a->free[hash]); + if (nblocks <= MAX_SMALL) { + struct BlockId id = a->small[nblocks - 2]; + if (id.v != BAD_BLOCK) { + unlink_from_list(a, id, &a->small[nblocks - 2]); return checkout_block(a, id, nblocks); } + } else { + const uint32_t hash = nblocks % NUM_HASH; + for (struct BlockId id = a->hash[hash]; + id.v != BAD_BLOCK; + id = B_LIST(a, id.v)->next) { + if (B_HDR(a, id.v)->size4x / 4 == nblocks) { + unlink_from_list(a, id, &a->hash[hash]); + return checkout_block(a, id, nblocks); + } + } } if (a->key_size >= nblocks) { @@ -312,8 +310,11 @@ static void *block_malloc(struct BlockAllocator *a, size_t nbytes) a->key.v = BAD_BLOCK; a->key_size = 0; } - for (uint32_t i = 0; i < BLOCK_NLISTS; ++i) { - fix_block_list(a, &a->free[i]); + for (uint32_t i = 0; i < NUM_HASH; ++i) { + fix_block_list(a, &a->hash[i]); + } + for (uint32_t i = 0; i < MAX_SMALL - 1; ++i) { + fix_block_list(a, &a->small[i]); } if (a->key_size != 0) { unlink_block(a, a->key); @@ -327,8 +328,9 @@ static void *block_malloc(struct BlockAllocator *a, size_t nbytes) } // Modified from SQLite (mem3.c) -static void block_free(struct BlockAllocator *a, void *ptr, size_t nbytes) +static void z_free(struct Heap *H, void *ptr, size_t nbytes) { + struct Allocator *a = H->a; paw_assert(!TOO_MANY_BLOCKS(nbytes)); uint32_t nblocks = COMPUTE_NUM_BLOCKS(nbytes); paw_assert(nblocks >= 2); @@ -371,41 +373,6 @@ static size_t block_size(void *ptr) return (b[-1].hdr.size4x & ~3) * 2 - 4; } -static void *alloc_uninit(struct FastBins *a, size_t id) -{ - void *ptr = a->uninit; - const size_t want = MIN_BIN_SIZE * (id + 1); - if (want > a->uninit_size) return NULL; - UNPOISON_MEMORY_REGION(a->uninit, want); - a->uninit = BUMP(a->uninit, want); - a->uninit_size -= want; - return ptr; -} - -static void *fast_malloc(struct FastBins *a, size_t size) -{ - paw_assert(0 < size && size <= MAX_BIN_SIZE); - const int id = BIN_ID(size); - struct BinInfo **pbin = &a->info[id]; - if (*pbin != NULL) { - struct BinInfo *bin = *pbin; - *pbin = bin->prev; - return ERASE_TYPE(bin); - } - return alloc_uninit(a, id); -} - -static void fast_free(struct FastBins *a, void *ptr, size_t size) -{ - if (ptr == NULL) return; - struct BinInfo *bin = CAST(ptr, struct BinInfo *); - const int id = BIN_ID(size); - *bin = (struct BinInfo){ - .prev = a->info[id], - }; - a->info[id] = bin; -} - // TODO: Put everything in the same allocation int pawZ_init(paw_Env *P, size_t heap_size) { @@ -430,21 +397,11 @@ int pawZ_init(paw_Env *P, size_t heap_size) #define SKIP_CHUNK(z) (heap = BUMP(heap, aligned(z)), \ heap_size -= aligned(z)) - H->a_block = heap; - SKIP_CHUNK(sizeof(struct BlockAllocator)); - init_block_allocator(H->a_block, heap, heap_size); + H->a = heap; + SKIP_CHUNK(sizeof(struct Allocator)); + init_block_allocator(H->a, heap, heap_size); #undef SKIP_CHUNK - const size_t arena_size = heap_size / BIN_FACTOR; - void *arena = block_malloc(H->a_block, arena_size); - if (arena == NULL) goto no_memory; - POISON_MEMORY_REGION(arena, arena_size); - H->bins = (struct FastBins){ - .uninit_size = arena_size, - .arena_size = arena_size, - .uninit = arena, - .arena = arena, - }; H->bounds[0] = CAST_UPTR(H->heap); H->bounds[1] = H->bounds[0] + H->heap_size; return PAW_OK; @@ -479,10 +436,6 @@ void pawZ_uninit(paw_Env *P) { struct Heap *H = P->H; if (H != NULL) { - if (H->bins.arena != NULL) { - UNPOISON_MEMORY_REGION(H->bins.uninit, H->bins.uninit_size); - block_free(P->H->a_block, H->bins.arena, H->bins.arena_size); - } if (H->heap != NULL) OS_FREE(P, H->heap, H->heap_size); OS_FREE(P, H, sizeof(*H) + H->nflags / FLAGS_PER_BYTE); P->H = NULL; @@ -491,25 +444,6 @@ void pawZ_uninit(paw_Env *P) #define CHECK_UNUSED(H, ptr) paw_assert(!pawZ_get_flag(H, CAST_UPTR(ptr))) -static void z_free(struct Heap *H, void *ptr, size_t size) -{ - size = aligned(size); - if (size <= MAX_BIN_SIZE) { - fast_free(&H->bins, ptr, size); - } else { - block_free(H->a_block, ptr, size); - } -} - -static void *z_malloc(struct Heap *H, size_t size) -{ - size = aligned(size); - void *ptr = size <= MAX_BIN_SIZE - ? fast_malloc(&H->bins, size) - : block_malloc(H->a_block, size); - return ptr; -} - static void *z_realloc(struct Heap *H, void *old_ptr, size_t old_size, size_t new_size) { void *new_ptr = z_malloc(H, new_size); diff --git a/src/alloc.h b/src/alloc.h index 2d2899d..63d2882 100644 --- a/src/alloc.h +++ b/src/alloc.h @@ -23,20 +23,9 @@ struct GcFlag { uint8_t value; }; -#define FAST_BIN_COUNT 64 - -struct FastBins { - struct BinInfo *info[FAST_BIN_COUNT]; - size_t uninit_size; - size_t arena_size; - void *uninit; - void *arena; -}; - struct Heap { - struct FastBins bins; - struct BlockAllocator *a_block; + struct Allocator *a; uintptr_t bounds[2]; size_t heap_size; void *heap; diff --git a/src/ast.c b/src/ast.c index ce7fc13..0b08fe0 100644 --- a/src/ast.c +++ b/src/ast.c @@ -39,7 +39,7 @@ void pawAst_free(struct Ast *ast) struct T *pawAst_new_##name(struct Ast *ast, enum T##Kind kind) \ { \ struct T *r = pawK_pool_alloc(ENV(ast), &(ast)->pool, sizeof(struct T)); \ - r->hdr.line = (ast)->lex->line; \ + r->hdr.line = (ast)->lex->last_line; \ r->hdr.kind = kind; \ return r; \ } diff --git a/src/auxlib.c b/src/auxlib.c index d3d2e1f..331e68c 100644 --- a/src/auxlib.c +++ b/src/auxlib.c @@ -91,7 +91,7 @@ void pawL_add_value(paw_Env *P, Buffer *buf, paw_Type type) // Table and stringify algorithm modified from micropython static const uint8_t kLogBase2[] = { 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 4, - 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, // + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, }; size_t pawL_integer_format_size(size_t nbits, int base) diff --git a/src/code.c b/src/code.c index 94f454a..b6fe0f8 100644 --- a/src/code.c +++ b/src/code.c @@ -15,7 +15,7 @@ static void add_line(struct FuncState *fs) } pawM_grow(P, p->lines, fs->nlines, p->nlines); p->lines[fs->nlines++] = (struct LineInfo){ - .line = -1, // TODO: Get line from somewhere... + .line = fs->line, .pc = fs->pc, }; } @@ -26,6 +26,7 @@ void pawK_fix_line(struct FuncState *fs, int line) fs->proto->lines[fs->nlines - 1].line = line; } +#include "debug.h" static void add_opcode(struct FuncState *fs, OpCode code) { paw_Env *P = ENV(fs->G); @@ -37,6 +38,9 @@ static void add_opcode(struct FuncState *fs, OpCode code) pawM_grow(P, p->source, fs->pc, p->length); p->source[fs->pc] = code; ++fs->pc; + + printf("%d (pc=%d): ", fs->line, fs->pc-1); + paw_dump_opcode(code); } void pawK_code_0(struct FuncState *fs, Op op) diff --git a/src/codegen.c b/src/codegen.c index 65fe601..2183e47 100644 --- a/src/codegen.c +++ b/src/codegen.c @@ -10,7 +10,7 @@ #include "parse.h" #include "type.h" -#define SYNTAX_ERROR(G, ...) pawE_error(ENV(G), PAW_ESYNTAX, -1, __VA_ARGS__) +#define SYNTAX_ERROR(G, ...) pawE_error(ENV(G), PAW_ESYNTAX, (G)->fs->line, __VA_ARGS__) #define GET_DECL(G, id) pawHir_get_decl((G)->hir, id) #define TYPE2CODE(G, type) (pawP_type2code((G)->C, type)) @@ -503,7 +503,7 @@ static struct HirVarInfo find_var(struct Generator *G, const String *name) if (!resolve_local(fs, name, &info) && // not local !resolve_upvalue(fs, name, &info) && // not local to caller !resolve_global(G, name, &info)) { // not found - pawE_error(ENV(G), PAW_ENAME, -1, "undefined variable '%s'", name->text); + pawE_error(ENV(G), PAW_ENAME, fs->line, "undefined variable '%s'", name->text); } return info; } @@ -625,6 +625,8 @@ static void code_basic_lit(struct HirVisitor *V, struct HirLiteralExpr *e) { struct Generator *G = V->ud; struct FuncState *fs = G->fs; + fs->line = e->line; + if (e->basic.t == PAW_TUNIT) { pawK_code_0(fs, OP_PUSHUNIT); } else if (e->basic.t != PAW_TBOOL) { @@ -639,21 +641,25 @@ static void code_basic_lit(struct HirVisitor *V, struct HirLiteralExpr *e) static void code_tuple_lit(struct HirVisitor *V, struct HirLiteralExpr *e) { + struct Generator *G = V->ud; + struct FuncState *fs = G->fs; + fs->line = e->line; + struct HirTupleLit *lit = &e->tuple; V->VisitExprList(V, lit->elems); - struct Generator *G = V->ud; - struct FuncState *fs = G->fs; pawK_code_U(fs, OP_NEWTUPLE, lit->elems->count); } static void code_container_lit(struct HirVisitor *V, struct HirLiteralExpr *e) { + struct Generator *G = V->ud; + struct FuncState *fs = G->fs; + fs->line = e->line; + struct HirContainerLit *lit = &e->cont; V->VisitExprList(V, lit->items); - struct Generator *G = V->ud; - struct FuncState *fs = G->fs; const Op op = lit->code == PAW_TVECTOR ? OP_NEWVECTOR : OP_NEWMAP; pawK_code_U(fs, op, lit->items->count); } @@ -664,6 +670,7 @@ static void code_composite_lit(struct HirVisitor *V, struct HirLiteralExpr *e) struct HirCompositeLit *lit = &e->comp; struct Generator *G = V->ud; struct FuncState *fs = G->fs; + fs->line = e->line; const DefId did = e->type->adt.did; struct HirAdtDecl *d = &GET_DECL(G, did)->adt; @@ -693,11 +700,8 @@ static void code_literal_expr(struct HirVisitor *V, struct HirLiteralExpr *e) } } -static void code_and(struct HirVisitor *V, struct HirLogicalExpr *e) +static void code_and(struct HirVisitor *V, struct FuncState *fs, struct HirLogicalExpr *e) { - struct Generator *G = V->ud; - struct FuncState *fs = G->fs; - V->VisitExpr(V, e->lhs); const int jump = code_jump(fs, OP_JUMPFALSE); pawK_code_U(fs, OP_POP, 1); @@ -705,11 +709,8 @@ static void code_and(struct HirVisitor *V, struct HirLogicalExpr *e) patch_here(fs, jump); } -static void code_or(struct HirVisitor *V, struct HirLogicalExpr *e) +static void code_or(struct HirVisitor *V, struct FuncState *fs, struct HirLogicalExpr *e) { - struct Generator *G = V->ud; - struct FuncState *fs = G->fs; - V->VisitExpr(V, e->lhs); const int else_jump = code_jump(fs, OP_JUMPFALSE); const int then_jump = code_jump(fs, OP_JUMP); @@ -721,10 +722,14 @@ static void code_or(struct HirVisitor *V, struct HirLogicalExpr *e) static void code_logical_expr(struct HirVisitor *V, struct HirLogicalExpr *e) { + struct Generator *G = V->ud; + struct FuncState *fs = G->fs; + fs->line = e->line; + if (e->is_and) { - code_and(V, e); + code_and(V, fs, e); } else { - code_or(V, e); + code_or(V, fs, e); } } @@ -732,6 +737,7 @@ static void code_chain_expr(struct HirVisitor *V, struct HirChainExpr *e) { struct Generator *G = V->ud; struct FuncState *fs = G->fs; + fs->line = e->line; V->VisitExpr(V, e->target); const int jump = code_jump(fs, OP_JUMPNULL); @@ -743,6 +749,7 @@ static void code_unop_expr(struct HirVisitor *V, struct HirUnOpExpr *e) { struct Generator *G = V->ud; struct FuncState *fs = G->fs; + fs->line = e->line; V->VisitExpr(V, e->target); CODE_OP(fs, OP_UNOP, e->op, HIR_TYPEOF(e->target)); @@ -752,6 +759,8 @@ static void code_binop_expr(struct HirVisitor *V, struct HirBinOpExpr *e) { struct Generator *G = V->ud; struct FuncState *fs = G->fs; + fs->line = e->line; + V->VisitExpr(V, e->lhs); V->VisitExpr(V, e->rhs); CODE_OP(fs, OP_BINOP, e->op, HIR_TYPEOF(e->rhs)); @@ -766,6 +775,7 @@ static void code_expr_stmt(struct HirVisitor *V, struct HirExprStmt *s) { struct Generator *G = V->ud; struct FuncState *fs = G->fs; + fs->line = s->line; if (s->rhs != NULL) { code_setter(V, s->lhs, s->rhs); @@ -835,6 +845,8 @@ static void code_func(struct HirVisitor *V, struct HirFuncDecl *d, struct HirVar static void code_field_decl(struct HirVisitor *V, struct HirFieldDecl *d) { struct Generator *G = V->ud; + G->fs->line = d->line; + new_local(G->fs, d->name, d->type); } @@ -842,23 +854,20 @@ static void code_var_decl(struct HirVisitor *V, struct HirVarDecl *d) { struct Generator *G = V->ud; struct FuncState *fs = G->fs; + fs->line = d->line; + declare_local(fs, d->name, d->type); V->VisitExpr(V, d->init); define_local(fs); } -static void code_adt_decl(struct HirVisitor *V, struct HirAdtDecl *d) -{ - // NOOP - paw_unused(V); - paw_unused(d); -} - static void code_block_stmt(struct HirVisitor *V, struct HirBlock *b) { struct BlockState bs; struct Generator *G = V->ud; struct FuncState *fs = G->fs; + fs->line = b->line; + enter_block(fs, &bs, PAW_FALSE); V->VisitStmtList(V, b->stmts); leave_block(fs); @@ -868,6 +877,8 @@ static void code_return_stmt(struct HirVisitor *V, struct HirReturnStmt *s) { struct Generator *G = V->ud; struct FuncState *fs = G->fs; + fs->line = s->line; + V->VisitExpr(V, s->expr); pawK_code_0(fs, OP_RETURN); } @@ -941,6 +952,8 @@ static void code_variant_constructor(struct HirVisitor *V, struct HirType *type, static void code_path_expr(struct HirVisitor *V, struct HirPathExpr *e) { struct Generator *G = V->ud; + G->fs->line = e->line; + if (is_variant_constructor(G, e->type)) { code_variant_constructor(V, e->type, NULL); } else { @@ -954,6 +967,7 @@ static void code_call_expr(struct HirVisitor *V, struct HirCallExpr *e) paw_assert(HirIsFuncType(e->func)); struct Generator *G = V->ud; struct FuncState *fs = G->fs; + fs->line = e->line; if (is_variant_constructor(G, e->func)) { code_variant_constructor(V, e->func, e->args); @@ -974,28 +988,12 @@ static void code_conversion_expr(struct HirVisitor *V, struct HirConversionExpr const Op op = e->to == PAW_TBOOL ? OP_CASTBOOL : e->to == PAW_TINT ? OP_CASTINT : OP_CASTFLOAT; + G->fs->line = e->line; V->VisitExpr(V, e->arg); pawK_code_U(G->fs, op, from->adt.base); } -// Stamp out monomorphizations of a function template -static void monomorphize_func(struct HirVisitor *V, struct HirFuncDecl *d) -{ - if (d->monos == NULL) { - return; - } - struct Generator *G = V->ud; - for (int i = 0; i < d->monos->count; ++i) { - struct HirDecl *decl = d->monos->data[i]; - struct HirFuncDecl *inst = HirGetFuncDecl(decl); - struct HirFuncDef *fdef = HirGetFuncDef(inst->type); - inst->name = mangle_name(G, inst->name, fdef->types); - const struct HirVarInfo info = new_global(G, inst->name, inst->type, d->is_pub); - code_func(V, inst, info); - } -} - static void register_func(struct Generator *G, struct HirFuncDecl *d) { const struct HirVarInfo info = new_global(G, d->name, d->type, d->is_pub); @@ -1046,21 +1044,11 @@ static void code_items(struct HirVisitor *V) } } -static void code_func_decl(struct HirVisitor *V, struct HirFuncDecl *d) -{ - struct Generator *G = V->ud; - if (d->generics == NULL) { - const struct HirVarInfo info = new_global(G, d->name, d->type, d->is_pub); - code_func(V, d, info); - } else { - monomorphize_func(V, d); - } -} - static void code_if_stmt(struct HirVisitor *V, struct HirIfStmt *s) { struct Generator *G = V->ud; struct FuncState *fs = G->fs; + fs->line = s->line; V->VisitExpr(V, s->cond); const int else_jump = code_jump(fs, OP_JUMPFALSEPOP); @@ -1085,13 +1073,15 @@ static void close_until_loop(struct FuncState *fs) } bs = outer; } - pawE_error(ENV(fs->G), PAW_ESYNTAX, -1, "label outside loop"); + pawE_error(ENV(fs->G), PAW_ESYNTAX, fs->line, "label outside loop"); } static void code_label_stmt(struct HirVisitor *V, struct HirLabelStmt *s) { struct Generator *G = V->ud; struct FuncState *fs = G->fs; + fs->line = s->line; + close_until_loop(fs); // fix the stack add_label(fs, s->label); // emit a jump, to be patched later } @@ -1118,14 +1108,15 @@ static void code_dowhile_stmt(struct HirVisitor *V, struct HirWhileStmt *s) static void code_while_stmt(struct HirVisitor *V, struct HirWhileStmt *s) { - if (s->is_dowhile) { - code_dowhile_stmt(V, s); - return; - } struct Generator *G = V->ud; struct FuncState *fs = G->fs; struct BlockState bs; + fs->line = s->line; + if (s->is_dowhile) { + code_dowhile_stmt(V, s); + return; + } enter_block(fs, &bs, PAW_TRUE); const int loop = fs->pc; V->VisitExpr(V, s->cond); @@ -1203,6 +1194,8 @@ static void code_for_stmt(struct HirVisitor *V, struct HirForStmt *s) struct BlockState bs; struct Generator *G = V->ud; struct FuncState *fs = G->fs; + fs->line = s->line; + enter_block(fs, &bs, PAW_TRUE); if (s->is_fornum) { code_fornum_stmt(V, s); @@ -1215,27 +1208,33 @@ static void code_for_stmt(struct HirVisitor *V, struct HirForStmt *s) static void code_index_expr(struct HirVisitor *V, struct HirIndex *e) { struct Generator *G = V->ud; + struct FuncState *fs = G->fs; struct HirType *target = HIR_TYPEOF(e->target); const paw_Type t = basic_code(G, target); + fs->line = e->line; + V->VisitExpr(V, e->target); if (e->is_slice) { code_slice_indices(V, e->first, e->second, target); - pawK_code_U(G->fs, OP_GETSLICE, t); + pawK_code_U(fs, OP_GETSLICE, t); } else { V->VisitExpr(V, e->first); - pawK_code_U(G->fs, OP_GETITEM, t); + pawK_code_U(fs, OP_GETITEM, t); } } static void code_selector_expr(struct HirVisitor *V, struct HirSelector *e) { struct Generator *G = V->ud; + struct FuncState *fs = G->fs; + fs->line = e->line; + V->VisitExpr(V, e->target); if (e->is_index) { - pawK_code_U(G->fs, OP_GETTUPLE, e->index); + pawK_code_U(fs, OP_GETTUPLE, e->index); } else { const struct HirVarInfo info = resolve_attr(G, HIR_TYPEOF(e->target), e->name); - pawK_code_U(G->fs, OP_GETATTR, info.index); + pawK_code_U(fs, OP_GETATTR, info.index); } } @@ -1273,13 +1272,16 @@ static void setup_pass(struct HirVisitor *V, struct Generator *G) V->VisitLabelStmt = code_label_stmt; V->VisitReturnStmt = code_return_stmt; V->VisitVarDecl = code_var_decl; - V->VisitFuncDecl = code_func_decl; - V->VisitAdtDecl = code_adt_decl; V->VisitFieldDecl = code_field_decl; add_builtin_func(G, "assert"); add_builtin_func(G, "print"); + add_builtin_func(G, "_string_split"); + add_builtin_func(G, "_string_join"); + add_builtin_func(G, "_string_find"); + add_builtin_func(G, "_string_starts_with"); + add_builtin_func(G, "_string_ends_with"); add_builtin_func(G, "_vector_push"); add_builtin_func(G, "_vector_pop"); add_builtin_func(G, "_vector_insert"); diff --git a/src/debug.c b/src/debug.c index 74afa66..de5bd0c 100644 --- a/src/debug.c +++ b/src/debug.c @@ -1,3 +1,6 @@ +// 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 "debug.h" #include "auxlib.h" #include "call.h" diff --git a/src/lib.c b/src/lib.c index 025edd4..bc084d4 100644 --- a/src/lib.c +++ b/src/lib.c @@ -45,89 +45,6 @@ int pawL_check_varargc(paw_Env *P, int min, int max) return narg; } -// static void try_aux(paw_Env *P, void *arg) -//{ -// const int argc = *CAST(arg, int *); -// const Value f = CF_BASE(1); -// pawC_call(P, v_object(f), argc - 1); -// } -// -// static int base_try(paw_Env *P) -//{ -// int argc = pawL_check_varargc(P, 1, UINT8_MAX); -// const int status = pawC_try(P, try_aux, &argc); -// paw_push_int(P, status); -// return 1; -// } -// -// static int base_require(paw_Env *P) -//{ -// pawL_check_argc(P, 1); -// const char *name = pawL_check_string(P, 1); -// pawL_require_lib(P, name); -// return 1; -// } -// -// #de f in e make_to_bool(suffix, T) \ -// static int base_to_bool_ ## suffix(paw_Env *P) \ -// { \ -// pawL_check_argc(P, 1); \ -// pawR_to_bool(P, T); \ -// return 1; \ -// } -// make_to_bool(s, PAW_TSTRING) -// make_to_bool(i, PAW_TINT) -// make_to_bool(f, PAW_TFLOAT) -// -// #de f in e make_to_int(suffix, T) \ -// static int base_to_int_ ## suffix(paw_Env *P) \ -// { \ -// pawL_check_argc(P, 1); \ -// pawR_to_int(P, T); \ -// return 1; \ -// } -// make_to_int(s, PAW_TSTRING) -// make_to_int(i, PAW_TINT) -// make_to_int(f, PAW_TFLOAT) -// -// #de f in e make_to_float(suffix, T) \ -// static int base_to_float_ ## suffix(paw_Env *P) \ -// { \ -// pawL_check_argc(P, 1); \ -// pawR_to_float(P, T); \ -// return 1; \ -// } -// make_to_float(s, PAW_TSTRING) -// make_to_float(i, PAW_TINT) -// make_to_float(f, PAW_TFLOAT) -// -// static int base_chr(paw_Env *P) -//{ -// pawL_check_argc(P, 1); -// const paw_Int ord = pawL_check_int(P, 1); -// if (0x00 <= ord && ord <= 0xFF) { -// const uint8_t chr[] = {ord}; -// paw_push_nstring(P, (const char *)chr, 1); -// } else { -// // TODO: Encode UTF-8 codepoint -// pawR_error(P, PAW_EOVERFLOW, "FIXME: Support UTF-8!"); -// } -// return 1; -// } -// -// static int base_ord(paw_Env *P) -//{ -// pawL_check_argc(P, 1); -// const char *str = pawL_check_string(P, 1); -// const size_t len = paw_length(P, 1); -// if (!len || len > 4) { -// pawR_error(P, PAW_EVALUE, "invalid UTF-8"); -// } -// // TODO: Decode UTF-8 codepoint -// paw_push_int(P, str[0]); -// return 1; -// } - static int base_assert(paw_Env *P) { if (v_false(CF_BASE(1))) { @@ -144,61 +61,6 @@ static int base_print(paw_Env *P) return 0; } -// struct ReaderState { -// const char *data; -// size_t size; -// }; -// -// static const char *reader(paw_Env *P, void *ud, size_t *size) -//{ -// paw_unused(P); -// struct ReaderState *ld = ud; -// -// // Return the whole string. -// *size = ld->size; -// ld->size = 0; -// return ld->data; -// } -// -// static int base_load(paw_Env *P) -//{ -// // load() takes a single parameter: a string containing source code. -// pawL_check_argc(P, 1); -// pawL_check_type(P, 1, PAW_TSTRING); -// -// struct ReaderState state = { -// .data = paw_string(P, 1), -// .size = paw_length(P, 1), -// }; -// // Load the code and leave it in a closure object on the stack. -// const int status = paw_load(P, reader, "", &state); -// if (status != PAW_OK) { -// // Rethrow the error. The error message is already on the stack. -// pawC_throw(P, status); -// } -// return 1; -// } -// -// static int base_getattr(paw_Env *P) -//{ -// const int argc = pawL_check_varargc(P, 2, 3); -// const paw_Bool fallback = argc == 3; -// if (fallback) { -// paw_rotate(P, -3, 1); -// } -// if (pawR_getattr_raw(P, fallback)) { -// pawR_attr_error(P, P->top.p[-1]); -// } -// return 1; -// } -// -// static int base_setattr(paw_Env *P) -//{ -// pawL_check_argc(P, 3); -// pawR_setattr(P); -// return 0; -// } - static int vector_insert(paw_Env *P) { Vector *vec = v_vector(CF_BASE(1)); @@ -229,8 +91,7 @@ static int vector_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)); - return i < 0 ? 0 : i >= n ? n - 1 - : i; + return i < 0 ? 0 : i >= n ? n - 1 : i; } // TODO: It would be nice to let pop() take an optional parameter indicating the @@ -259,25 +120,14 @@ static int vector_clone(paw_Env *P) return 1; } -// static String *check_string(paw_Env *P, int i) -//{ -// const Value v = CF_BASE(i); -// if (v_type(v) != VSTRING) { -// pawR_error(P, PAW_ETYPE, "expected string"); -// } -// return v_string(v); -// } - -static const char *find_substr(const char *str, size_t nstr, const char *sub, - size_t nsub) +static const char *find_substr(const char *str, size_t nstr, const char *sub, size_t nsub) { - if (nsub == 0) { - return str; - } + if (nsub == 0) return str; const char *ptr = str; const char *end = str + nstr; while ((ptr = strchr(ptr, sub[0]))) { - if (nsub <= CAST_SIZE(end - ptr) && 0 == memcmp(ptr, sub, nsub)) { + if (nsub <= CAST_SIZE(end - ptr) && + 0 == memcmp(ptr, sub, nsub)) { return ptr; } str = ptr + nsub; @@ -287,11 +137,12 @@ static const char *find_substr(const char *str, size_t nstr, const char *sub, static int string_find(paw_Env *P) { - pawL_check_argc(P, 1); - const String *s = v_string(CF_BASE(0)); - const String *find = v_string(CF_BASE(1)); - const char *result = - find_substr(s->text, s->length, find->text, find->length); + pawL_check_argc(P, 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); if (result) { // index of substring v_set_int(P->top.p - 1, result - s->text); } else { // not found @@ -300,77 +151,76 @@ static int string_find(paw_Env *P) return 1; } -//// TODO: Remove '/*TODO*/+1', these should be methods, which take the -//// context as the implicit first parameter -// static int string_split(paw_Env *P) -//{ -// pawL_check_argc(P, 1); -// const String *sep = v_string(CF_BASE(1)); -// String *s = v_string(CF_BASE(0)); -// if (sep->length == 0) { -// pawR_error(P, PAW_EVALUE, "empty separator"); -// } -// -// paw_Int npart = 0; -// const char *part; -// size_t nstr = s->length; -// const char *pstr = s->text; -// while ((part = find_substr(pstr, nstr, sep->text, sep->length))) { -// const size_t n = CAST_SIZE(part - pstr); -// pawC_pushns(P, pstr, n); -// part += sep->length; // skip separator -// pstr = part; -// nstr -= n; -// ++npart; -// } -// const char *end = s->text + s->length; // add the rest -// pawC_pushns(P, pstr, CAST_SIZE(end - pstr)); -// ++npart; -// -// pawR_literal_array(P, npart); -// return 1; -// } -// -// static int string_join(paw_Env *P) -//{ -// pawL_check_argc(P, 1); -// const Value seq = CF_BASE(1); -// String *s = v_string(CF_BASE(0)); -// -// Buffer buf; -// pawL_init_buffer(P, &buf); -// paw_Int itr = PAW_ITER_INIT; -// Vector *a = v_vector(seq); -// while (pawA_iter(a, &itr)) { -// const Value v = a->begin[itr]; -// // Add a chunk, followed by the separator if necessary. -// const String *chunk = v_string(v); -// pawL_add_nstring(P, &buf, chunk->text, chunk->length); -// if (CAST_SIZE(itr + 1) < pawA_length(a)) { -// pawL_add_nstring(P, &buf, s->text, s->length); -// } -// } -// pawL_push_result(P, &buf); -// return 1; -// } +// TODO: These should be methods, which take the context as the implicit first parameter +static int string_split(paw_Env *P) +{ + pawL_check_argc(P, 2); + 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"); + } + + paw_Int npart = 0; + const char *part; + size_t nstr = s->length; + const char *pstr = s->text; + while ((part = find_substr(pstr, nstr, sep->text, sep->length))) { + const size_t n = CAST_SIZE(part - pstr); + pawC_pushns(P, pstr, n); + part += sep->length; // skip separator + pstr = part; + nstr -= n; + ++npart; + } + const char *end = s->text + s->length; // add the rest + pawC_pushns(P, pstr, CAST_SIZE(end - pstr)); + ++npart; + + pawR_literal_vector(P, npart); + return 1; + } + +static int string_join(paw_Env *P) +{ + pawL_check_argc(P, 2); + String *s = v_string(CF_BASE(1)); + const Value seq = CF_BASE(2); + + Buffer buf; + pawL_init_buffer(P, &buf); + paw_Int itr = PAW_ITER_INIT; + Vector *a = v_vector(seq); + while (pawV_vec_iter(a, &itr)) { + const Value v = a->begin[itr]; + // Add a chunk, followed by the separator if necessary. + const String *chunk = v_string(v); + pawL_add_nstring(P, &buf, chunk->text, chunk->length); + if (CAST_SIZE(itr + 1) < pawV_vec_length(a)) { + pawL_add_nstring(P, &buf, s->text, s->length); + } + } + pawL_push_result(P, &buf); + return 1; + } static int string_starts_with(paw_Env *P) { - pawL_check_argc(P, 1); - String *s = v_string(CF_BASE(0)); - const String *prefix = v_string(CF_BASE(1)); + pawL_check_argc(P, 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); + const paw_Bool b = s->length >= prelen && + 0 == memcmp(prefix->text, s->text, prelen); v_set_bool(P->top.p - 1, b); return 1; } static int string_ends_with(paw_Env *P) { - pawL_check_argc(P, 1); - String *s = v_string(CF_BASE(0)); - const String *suffix = v_string(CF_BASE(1)); + pawL_check_argc(P, 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) { @@ -381,12 +231,9 @@ static int string_ends_with(paw_Env *P) return 1; } -static int string_clone(paw_Env *P) +static int int_to_string(paw_Env *P) { - // Leave the string receiver on top of the stack. The copy of the string - // into this function's receiver slot serves as the clone. Strings are - // immutable: and have copy-on-write sematics. - paw_unused(P); + pawR_to_string(P, PAW_TINT, NULL); return 1; } @@ -404,6 +251,7 @@ static int map_get(paw_Env *P) static int map_erase(paw_Env *P) { + pawL_check_argc(P, 2); Map *m = v_map(CF_BASE(1)); pawH_erase(m, CF_BASE(2)); return 0; @@ -411,6 +259,7 @@ static int map_erase(paw_Env *P) static int map_clone(paw_Env *P) { + pawL_check_argc(P, 1); Map *m = v_map(CF_BASE(1)); Value *pv = pawC_push0(P); pawH_clone(P, pv, m); @@ -450,6 +299,11 @@ void pawL_init(paw_Env *P) add_builtin_func(P, "print", base_print); // fn print(string) // TODO: Replace with real methods + add_builtin_func(P, "_string_starts_with", string_starts_with); + add_builtin_func(P, "_string_ends_with", string_ends_with); + add_builtin_func(P, "_string_find", string_find); + add_builtin_func(P, "_string_split", string_split); + add_builtin_func(P, "_string_join", string_join); add_builtin_func(P, "_vector_push", vector_push); add_builtin_func(P, "_vector_pop", vector_pop); add_builtin_func(P, "_vector_insert", vector_insert); diff --git a/src/parse.c b/src/parse.c index 3065220..104464a 100644 --- a/src/parse.c +++ b/src/parse.c @@ -1336,11 +1336,17 @@ static const char kPrelude[] = "pub fn assert(cond: bool) \n" // TODO: Replace with methods - "pub fn _vector_push(vec: [T], v: T) \n" - "pub fn _vector_pop(vec: [T]) -> T \n" - "pub fn _vector_insert(vec: [T], i: int, v: T)\n" - "pub fn _vector_erase(vec: [T], i: int) -> T \n" - "pub fn _vector_clone(vec: [T]) -> [T] \n"; + "pub fn _int_to_string(self: int, base: int) -> str\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 _vector_push(self: [T], v: T) \n" + "pub fn _vector_pop(self: [T]) -> T \n" + "pub fn _vector_insert(self: [T], i: int, v: T)\n" + "pub fn _vector_erase(self: [T], i: int) -> T \n" + "pub fn _vector_clone(self: [T]) -> [T] \n"; struct PreludeReader { size_t size; diff --git a/src/parse.h b/src/parse.h index 8bb1a9f..f66aa03 100644 --- a/src/parse.h +++ b/src/parse.h @@ -71,6 +71,7 @@ struct FuncState { int nproto; // number of nested functions int nlines; // number of source lines int pc; // number of instructions + int line; enum FuncKind kind; // type of function }; diff --git a/src/resolve.c b/src/resolve.c index 9f3b556..db7c2c0 100644 --- a/src/resolve.c +++ b/src/resolve.c @@ -2,9 +2,10 @@ // 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. // -// check.c: Implementation of the type checker. This code transforms an AST -// from the parser into a graph by unifying types based on lexical scope. +// resolve.c: Implementation of the type checker. This code transforms an AST +// from the parser into a type-annotated HIR tree. +#include "resolve.h" #include "ast.h" #include "code.h" #include "compile.h" @@ -15,14 +16,13 @@ #include "map.h" #include "mem.h" #include "parse.h" -#include "resolve.h" #include "str.h" #include "type.h" #include "unify.h" -#define NAME_ERROR(R, line, ...) pawE_error(ENV(R), PAW_ENAME, line, __VA_ARGS__) -#define SYNTAX_ERROR(R, ...) pawE_error(ENV(R), PAW_ESYNTAX, -1, __VA_ARGS__) -#define TYPE_ERROR(R, ...) pawE_error(ENV(R), PAW_ETYPE, -1, __VA_ARGS__) +#define NAME_ERROR(R, ...) pawE_error(ENV(R), PAW_ENAME, (R)->line, __VA_ARGS__) +#define SYNTAX_ERROR(R, ...) pawE_error(ENV(R), PAW_ESYNTAX, (R)->line, __VA_ARGS__) +#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)) @@ -315,8 +315,7 @@ static struct HirSymbol *new_global(struct Resolver *R, String *name, struct Hir for (int i = 0; i < st->symbols->count; ++i) { struct HirSymbol *symbol = st->symbols->data[i]; if (pawS_eq(symbol->name, name)) { - NAME_ERROR(R, decl->hdr.line, - "duplicate global '%s' (declared previously on line %d)", + NAME_ERROR(R, "duplicate global '%s' (declared previously on line %d)", name->text, symbol->decl->hdr.line); } } @@ -335,16 +334,6 @@ static struct HirSymbol *try_resolve_symbol(struct Resolver *R, const String *na const int index = pawHir_find_symbol(scope, name); if (index >= 0) { struct HirSymbol *symbol = scope->symbols->data[index]; - if (scope->fn_depth != R->func_depth && - !HirIsFuncDecl(symbol->decl) && - !symbol->is_type) { - // TODO: note that this is not currently possible: named functions (non-closures) can only - // appear at the toplevel, anything they reference outside themselves is global - // TODO: replace is_type with more specific flag, this will mess - // up function templates! - TYPE_ERROR(R, "attempt to reference non-local variable '%s' " - "(consider using a closure)", name->text); - } return scope->symbols->data[index]; } } @@ -360,7 +349,7 @@ static struct HirSymbol *resolve_symbol(struct Resolver *R, const String *name) { struct HirSymbol *symbol = try_resolve_symbol(R, name); if (symbol == NULL) { - NAME_ERROR(R, -1, "undefined symbol '%s'", name->text); + NAME_ERROR(R, "undefined symbol '%s'", name->text); } return symbol; } @@ -692,7 +681,6 @@ static void instantiate_func_aux(struct Resolver *R, struct HirFuncDecl *base, s r->fdef.types = types; inst->type = r; - // NOTE: '.scope' not used for functions struct HirTypeList *generics = HirGetFuncDef(base->type)->types; prep_func_instance(R, generics, types, inst); @@ -777,9 +765,7 @@ 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); - } + 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) { @@ -792,9 +778,8 @@ static struct HirDecl *instantiate_func(struct Resolver *R, struct HirFuncDecl * struct HirDecl *pawP_instantiate(struct Resolver *R, struct HirDecl *base, struct HirTypeList *types) { - if (types == NULL) { - // not polymorphic - } else if (HIR_IS_POLY_ADT(base)) { + if (types == NULL) return base; + if (HIR_IS_POLY_ADT(base)) { return instantiate_struct(R, &base->adt, types); } else if (HIR_IS_POLY_FUNC(base)) { return instantiate_func(R, &base->func, types); @@ -968,7 +953,7 @@ static struct HirDecl *expect_attr(struct Resolver *R, const struct StructPack * { struct HirDecl *attr = resolve_attr(pack->fields, name); if (attr == NULL) { - NAME_ERROR(R, -1, "field '%s' does not exist in type '%s'", name->text, + NAME_ERROR(R, "field '%s' does not exist in type '%s'", name->text, pack->name->text); } return attr; @@ -1023,9 +1008,7 @@ static struct HirType *typeof_path(struct HirPath *path) static struct HirType *resolve_tuple_type(struct Resolver *R, struct AstTupleType *e) { - if (e->types->count == 0) { - return get_type(R, PAW_TUNIT); - } + if (e->types->count == 0) return get_type(R, PAW_TUNIT); struct HirType *r = new_type(R, NO_DECL, kHirTupleType, e->line); r->tuple.elems = resolve_type_list(R, e->types); return r; @@ -1150,10 +1133,8 @@ static void op_type_error(struct Resolver *R, const struct HirType *type, const static struct HirType *binop_vector(struct Resolver *R, BinaryOp op, struct HirType *type) { const struct HirType *elem_t = hir_vector_elem(type); - if (op == BINARY_ADD) { - // 2 vectors with the same element type can be added - return type; - } else if (!HIR_IS_BASIC_T(elem_t)) { + 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); @@ -1605,7 +1586,7 @@ static struct HirType *resolve_composite_lit(struct Resolver *R, struct AstCompo struct HirStructField *item = HirGetStructField(hir_item); v_set_object(&key, item->name); if (pawH_contains(map, key)) { - NAME_ERROR(R, hir_item->hdr.line, "duplicate field '%s' in initializer for struct '%s'", + NAME_ERROR(R, "duplicate field '%s' in initializer for struct '%s'", item->name->text, pack.name->text); } Value *value = pawH_create(P, map, key); @@ -1618,7 +1599,7 @@ static struct HirType *resolve_composite_lit(struct Resolver *R, struct AstCompo v_set_object(&key, field->name); Value *value = pawH_get(map, key); if (value == NULL) { - NAME_ERROR(R, field->line, "missing initializer for field '%s' in struct '%s'", + NAME_ERROR(R, "missing initializer for field '%s' in struct '%s'", field->name->text, pack.name->text); } const paw_Int index = v_int(*value); @@ -1634,7 +1615,7 @@ static struct HirType *resolve_composite_lit(struct Resolver *R, struct AstCompo paw_Int iter = PAW_ITER_INIT; while (pawH_iter(map, &iter)) { const Value *pkey = pawH_key(map, CAST_SIZE(iter)); - NAME_ERROR(R, -1, "unexpected field '%s' in initializer for struct '%s'", + NAME_ERROR(R, "unexpected field '%s' in initializer for struct '%s'", v_string(*pkey), pack.name->text); } paw_assert(pack.fields->count == e->items->count); @@ -1819,7 +1800,7 @@ static struct HirExpr *resolve_index(struct Resolver *R, struct AstIndex *e) expect = get_type(R, PAW_TINT); r->type = get_type(R, PAW_TSTRING); } else { - not_container: +not_container: TYPE_ERROR(R, "type cannot be indexed (not a container)"); } if (e->is_slice) { @@ -1869,7 +1850,7 @@ static struct HirExpr *resolve_selector(struct Resolver *R, struct AstSelector * TYPE_ERROR(R, "expected name of struct field (integer indices can " "only be used with tuples)"); } else if (pack.fields == NULL) { - NAME_ERROR(R, e->line, "unit structure '%s' has no fields", pack.name->text); + NAME_ERROR(R, "unit structure '%s' has no fields", pack.name->text); } struct HirDecl *attr = expect_attr(R, &pack, e->name); r->type = HIR_TYPEOF(attr); @@ -1907,6 +1888,7 @@ static struct HirDecl *ResolveGenericDecl(struct Resolver *R, struct AstGenericD static struct HirDecl *resolve_decl(struct Resolver *R, struct AstDecl *decl) { + R->line = decl->hdr.line; switch (AST_KINDOF(decl)) { #define DEFINE_CASE(a, b) \ case kAst##a: \ @@ -1918,6 +1900,7 @@ static struct HirDecl *resolve_decl(struct Resolver *R, struct AstDecl *decl) static struct HirStmt *resolve_stmt(struct Resolver *R, struct AstStmt *stmt) { + R->line = stmt->hdr.line; switch (AST_KINDOF(stmt)) { #define DEFINE_CASE(a, b) \ case kAst##a: \ @@ -1934,6 +1917,7 @@ static struct HirStmt *resolve_stmt(struct Resolver *R, struct AstStmt *stmt) static struct HirType *resolve_type(struct Resolver *R, struct AstExpr *expr) { struct HirType *r; + R->line = expr->hdr.line; switch (AST_KINDOF(expr)) { case kAstPathExpr: r = resolve_path_type(R, AstGetPathExpr(expr)); @@ -1953,6 +1937,7 @@ static struct HirType *resolve_type(struct Resolver *R, struct AstExpr *expr) static struct HirExpr *resolve_expr(struct Resolver *R, struct AstExpr *expr) { struct HirExpr *r; + R->line = expr->hdr.line; switch (AST_KINDOF(expr)) { case kAstLiteralExpr: r = resolve_literal_expr(R, AstGetLiteralExpr(expr)); diff --git a/src/resolve.h b/src/resolve.h index 173f8a9..a2095b7 100644 --- a/src/resolve.h +++ b/src/resolve.h @@ -21,6 +21,7 @@ struct Resolver { int nresults; int vector_gid; int map_gid; + int line; paw_Bool in_closure; // 1 if the enclosing function is a closure, else 0 }; diff --git a/src/rt.c b/src/rt.c index 09c4bb7..c7d2f81 100644 --- a/src/rt.c +++ b/src/rt.c @@ -60,7 +60,7 @@ 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, int); + const int pc = CAST(cf->pc - p->source - 1, int); int i = 0; for (; i < p->nlines - 1; ++i) { diff --git a/src/value.c b/src/value.c index 9c28548..50fad20 100644 --- a/src/value.c +++ b/src/value.c @@ -24,9 +24,11 @@ static void int_to_string(paw_Env *P, paw_Int i) // Don't call llabs(INT64_MIN). The result is undefined on 2s complement // systems. - uint64_t u = i == INT64_MIN ? (1ULL << 63) : (uint64_t)llabs(i); + uint64_t u = i == INT64_MIN + ? UINT64_C(1) << 63 + : CAST(llabs(i), uint64_t); do { - *ptr-- = (char)(u % 10 + '0'); + *ptr-- = CAST(u % 10 + '0', char); u /= 10; } while (u); if (negative) { @@ -44,7 +46,7 @@ static void float_to_string(paw_Env *P, paw_Float f) pawC_pushns(P, temp, CAST_SIZE(n)); } -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 v, paw_Type type, size_t *plength) { switch (type) { case PAW_TSTRING: @@ -64,7 +66,7 @@ const char *pawV_to_string(paw_Env *P, Value v, paw_Type type, size_t *nout) return NULL; } const String *s = v_string(P->top.p[-1]); - *nout = s->length; + if (plength != NULL) *plength = s->length; return s->text; } @@ -408,17 +410,13 @@ static int char2base(char c) } } -static int check_suffix(const char *p, const char *text) +static int check_suffix(const char *p, const char *base) { - if (*p && !ISSPACE(*p)) { - return -1; - } + if (*p != '\0' && !ISSPACE(*p)) return -1; // If one of the pawV_parse_* functions are called on a string like " ", // then all of the checks will pass, despite " " not being a valid number. // Make sure that doesn't happen. - if (p == text) { - return -1; - } + if (p == base) return -1; return 0; } @@ -458,30 +456,28 @@ int pawV_parse_uint64(paw_Env *P, const char *text) return PAW_OK; } -#define skip_digits(p) \ +#define SKIP_DIGITS(p) \ while (ISDIGIT(*(p))) { \ - ++(p); \ + ++(p); \ } int pawV_parse_float(paw_Env *P, const char *text) { // First, validate the number format. const char *p = text; - if (p[0] == '0' && p[1] && !is_fp(p[1])) { + if (p[0] == '0' && p[1] != '\0' && !is_fp(p[1])) { return PAW_ESYNTAX; } - skip_digits(p); + SKIP_DIGITS(p) if (*p == '.') { ++p; - skip_digits(p); + SKIP_DIGITS(p) } if (*p == 'e' || *p == 'E') { - ++p; - if (*p == '+' || *p == '-') { - ++p; - } - skip_digits(p); + p += 1 + (p[1] == '+' || p[1] == '-'); + if (!ISDIGIT(*p)) return PAW_ESYNTAX; + SKIP_DIGITS(p) } if (check_suffix(p, text)) { return PAW_ESYNTAX; diff --git a/test/scripts/class.paw b/test/scripts/class.paw deleted file mode 100644 index 35deee6..0000000 --- a/test/scripts/class.paw +++ /dev/null @@ -1,423 +0,0 @@ --- class.paw - -{ - class A {} - let a = A() - - -- Functions added after __init are not bound to the instance. They - -- can be called and assigned to variables, but they will not receive - -- the hidden 'self' parameter, nor will they have access to 'super'. - a.a = fn() {} - a.b = fn(a) {return a} - assert(a.a() == null) - assert(a.b(42) == 42) - - let aa = a.a - let ab = a.b - assert(aa() == null) - assert(ab(42) == 42) -} - -{ - fn test(code) { - fn test() { - load(code)() - } - assert(0 != try(test)) - } - -- Classes are closed after creation (after closing '}' is encountered - -- in class the definition). - test('class A {}; A.a = 123') - test('class A {}; A.a = fn() {}') - - -- Non-bound functions cannot access 'self' or 'super'. - test('class A {}; let a = A(); a.a = fn() {self.value = 123}; a.a()') - test('class A {}; let a = A(); a.a = fn() {super.value = 123}; a.a()') -} - -{ - fn test(ins) { - -- 'c' and 'd' do not exist. Attempting to access either is an error. - assert(ins.a == 1) - assert(ins.b == null) - - ins.method() - assert(ins.a == 1) - assert(ins.b == 2) - assert(ins.c == 3) - } - - class Class { - __init() { - self.a = 1 - self.b = null - } - - method() { - self.b = 2 - self.c = 3 - } - } - - test(Class()) - - let cls = Class - test(cls()) -} - -{ - let cls - let a = [0] - { - let b = [0] - { - let c = [0] - - class Capture { - __init() { - self.a = a - self.b = b - self.c = c - } - } - cls = Capture - c[0] = 3 - } - b[0] = 2 - } - a[0] = 1 - - let ins = cls() - assert(ins.a[0] == 1) - assert(ins.b[0] == 2) - assert(ins.c[0] == 3) -} - -{ - let get - let set - let ins - let num - - fn setup(cls, n) { - num = n - ins = cls() - ins.setter(n) - assert(ins.getter() == n) - - get = ins.getter - set = ins.setter - assert(get() == n) - } - - fn test() { - assert(get() == num) - set(123) - assert(get() == 123) - } - - { - { - class Class { - setter(x) { - self.x = x - } - getter() { - return self.x - } - } - -- NOTE: Default __init() returns an instance of the class - setup(Class, 42) - assert(ins.x == 42) - } - test() - } - - { - { - let x = 0 - class Class { - setter(y) { - x = y - } - getter() { - return x - } - } - setup(Class, 321) - assert(x == 321) - } - test() - } -} - -{ - class Class { - __init() { - self.cls = Class - self.num = 42 - } - test() { - self.ins = self.cls() - } - } - let ins = Class() - ins.test() - - assert(ins.num == 42) - assert(ins.ins.num == 42) -} - -{ - class Class { - count(n) { - assert(n >= 0) - return n == 0 ?? 0 :: 1 + self.count(n - 1) - } - } - let c = Class() - assert(c.count(10) == 10) - assert(c.count(20) == 20) - assert(c.count(30) == 30) -} - --- Test inheritance: -{ - class Parent {} - class Child: Parent {} - let c = Child() - - let n = 0 - class Parent {method() {n = n + 1}} - class Child: Parent {} - let c = Child() - let m = c.method - m() -- Call bound method - c.method() -- Invoke directly - assert(n == 2) - - let n = 0 - class Grandparent {method() {n = -1}} - class Parent {method() {n = n + 1}} - class Child: Parent {} - let c = Child() - let m = c.method - -- Calls Parent.method() - m() - c.method() - assert(n == 2) -} - -{ - class Parent { - __init() { - self.name = 'Parent' - } - } - class Child: Parent {} - let c = Child() - -- __init method is not inherited - assert('not found' == getattr(c, '__init', 'not found')) -} - --- Inheritance from self is not possible -{ - class A {f() {}} - class A: A {} -- superclass is 'A' from the previous line - let a = A() - a.f() -} - --- Test 'super': -{ - - class Parent { - __init() { - self.name = 'Parent' - } - } - class Child: Parent { - __init() { - super.__init() - } - } - let c = Child() - assert(c.name == 'Parent') -} - -{ - class A { - method() { - return 'A' - } - } - - class B: A { - method() { - return 'B' - } - - test() { - return super.method() - } - } - - class C: B {} - - assert('A' == C().test()) -} - -{ - class A { - __init() { - self.v = 'A' - } - } - class B: A { - __init() {} - } - let b = B() - assert(null == getattr(b, 'v', null)) - - class B: A { - __init(x) { - super.__init() - } - } - let b = B('unused') - assert('A' == getattr(b, 'v', null)) -} - -{ - fn test(code) { - load(code)() - } - assert(0 != try(test, 'class _ABC: _ABC {}')) -- '_ABC' not declared (declared/defined at '}') - assert(0 != try(test, 'class A: 1 {}')) -- inherit from primitive - assert(0 != try(test, 'class A: [] {}')) -- inherit from builtin type - - -- In the second statement, the superclass 'A' refers to the class defined - -- in the first statement (class name is 'registered' at the '{'). - class A {} - class A: A {} -} - -{ - class Slice { - __init() { - self.value = [1, 2, 3] - } - __getslice(begin, end) { - return self.value[begin:end] - } - __setslice(begin, end, seq) { - self.value[begin:end] = seq - } - } - let slice = Slice() - assert(slice[:] == [1, 2, 3]) - assert(slice[1:-1] == [2]) - slice[3:] = [4, 5, 6] - assert(slice.value == [1, 2, 3, 4, 5, 6]) -} - -{ - class A { - __init(n) { - self.n = n - } - __null() { - return self.n < 0 ?? null :: self - } - } - - { - fn test(n) { - let r = A(n)? - return r.n - } - assert(test(-2).n == -2) - assert(test(-1).n == -1) - assert(test(0) == 0) - assert(test(1) == 1) - } - - { - fn test(n) { - return A(n) ?: n - } - assert(test(-2) == -2) - assert(test(-1) == -1) - assert(test(0).n == 0) - assert(test(1).n == 1) - } -} - -{ - class A { - __init(n) { - self.n = n - } - __null() { - -- Return an integer, rather than 'self'. When a given instance - -- of A 'x' is not semantically null, the expression 'x?' evaluates - -- to 'x.n', with no return. The expression 'x :? 123' also evaluates - -- to 'x.n'. - return self.n < 0 ?? null :: self.n - } - } - - fn test(n) { - let r = A(n)? - -- r == A(n).n - return r - } - assert(test(-1).n == -1) - assert(test(0) == 0) - - fn test(n) { - return A(n) ?: 'rhs' - } - assert(test(-1) == 'rhs') - assert(test(0) == 0) - assert(test(1) == 1) -} - -{ - class Comparable { - __init(a) { - self.a = a - } - __eq(b) { - return self.a == int(b) - } - __lt(b) { - return self.a < int(b) - } - __le(b) { - return self.a <= int(b) - } - __gt(b) { - return self.a > int(b) - } - __ge(b) { - return self.a >= int(b) - } - __int() { - return self.a - } - } - let c1 = Comparable(1) - assert(0 < c1 && c1 < 2) - assert(0 <= c1 && c1 <= 2) - assert(1 <= c1 && c1 <= 1) - assert(c1 > 0 && 2 > c1) - assert(c1 >= 0 && 2 >= c1) - assert(c1 >= 1 && 1 >= c1) - - let c2 = Comparable(2) - assert(c1 < c2 && c2 > c1) - assert(c1 <= c2 && c2 >= c1) - assert(c1 <= c1 && c2 >= c2) -} - diff --git a/test/scripts/map.paw b/test/scripts/map.paw index 6e9d814..190a58a 100644 --- a/test/scripts/map.paw +++ b/test/scripts/map.paw @@ -1,6 +1,6 @@ // map.paw -{ +pub fn test_getters() { let map = [ 1: 'abc', 2: 'def', @@ -10,16 +10,20 @@ assert(map[2] == 'def') assert(map[3] == 'ghi') } -{ + +pub fn test_empty_maps_with_annotations() { let map: [bool: int] = [:] - let map: [float: string] = [:] - let map: [string: bool] = [:] + let map: [float: str] = [:] + let map: [str: bool] = [:] } -{ + +fn call(map: [int: int]) {} +fn call2(map: [int: [int: int]]) {} + +pub fn test_inference() { let map = [1: [2, 3]] // [int: [int]] let map = [1: [2: 3]] // [int: [int: int]] -} -{ + let map = [1: [2: 3], 4: [:], 5: [:]] let map = [1: [:], 2: [3: 4], 5: [:]] let map = [1: [:], 2: [:], 3: [4: 5]] @@ -32,8 +36,7 @@ let map: [int: [int]] = [1: [2, 3]] let map: [int: [int: int]] = [1: [2: 3]] -} -{ + let map = [:] map = [0: 0] @@ -45,13 +48,11 @@ map = [:] map = [0: 0] - fn test(map: [int: int]) {} let a = [:] - test(a) + call(a) - fn test(map: [int: [int: int]]) {} let a = [0: [:]] - test(a) + call2(a) let a = [:] let b = a diff --git a/test/scripts/matmul.paw b/test/scripts/matmul.paw index ef87f5e..5e902a3 100644 --- a/test/scripts/matmul.paw +++ b/test/scripts/matmul.paw @@ -36,10 +36,11 @@ fn matmul(n: int, a: [[float]], b: [[float]]) -> [[float]] { return c } -pub fn main() { +pub fn main(args: [str]) -> float { let n = 100 let a = matgen(n) let b = matgen(n) let c = matmul(n, a, b) let r = (c[n / 2][n / 2]) + return r } diff --git a/test/scripts/nqueen.paw b/test/scripts/nqueen.paw new file mode 100644 index 0000000..fa3b40b --- /dev/null +++ b/test/scripts/nqueen.paw @@ -0,0 +1,45 @@ +// Translated from https://github.com/attractivechaos/plb2/blob/master/src/lua/nqueen.lua +fn nq_solve(n: int) -> int { + let m = 0 + let a = [] + let l = [] + let c = [] + let r = [] + let y0 = (1 << n) - 1 + for i = 0, n { + _vector_push(a, -1) + _vector_push(l, 0) + _vector_push(c, 0) + _vector_push(r, 0) + } + let k = 0 + while k >= 0 { + let y = ((l[k] | c[k]) | r[k]) & y0 + if ((y ^ y0) >> (a[k] + 1)) != 0 { + let i = a[k] + 1 + while i < n && (y & (1 << i)) != 0 { + i = i + 1 + } + if k < n - 1 { + let z = 1 << i + a[k] = i + k = k + 1 + l[k] = (l[k - 1] | z) << 1 + c[k] = c[k - 1] | z + r[k] = (r[k - 1] | z) >> 1 + } else { + m = m + 1 + k = k - 1 + } + } else { + a[k] = -1 + k = k - 1 + } + } + return m +} + +pub fn main() { + let n = 5 + let m = nq_solve(n) +} diff --git a/test/scripts/string.paw b/test/scripts/string.paw index 126b30d..900c879 100644 --- a/test/scripts/string.paw +++ b/test/scripts/string.paw @@ -1,90 +1,78 @@ // string.paw -{ - assert(!'') - assert('s') - assert(!!'s') - - assert('str'.clone() == 'str') +pub fn test_starts_with() { + let str = 'abcdef' + assert(_string_starts_with(str, 'abcdef')) + assert(_string_starts_with(str, 'abcde')) + assert(_string_starts_with(str, 'abc')) + assert(_string_starts_with(str, 'a')) + assert(_string_starts_with(str, '')) + assert(!_string_starts_with(str, 'bcdef')) + assert(!_string_starts_with(str, 'abcdf')) + assert(!_string_starts_with(str, 'abd')) + assert(!_string_starts_with(str, 'ac')) } -{ +pub fn test_ends_with() { let str = 'abcdef' - assert(str.starts_with('abcdef')) - assert(str.starts_with('abcde')) - assert(str.starts_with('abc')) - assert(str.starts_with('a')) - assert(str.starts_with('')) - assert(str.ends_with('abcdef')) - assert(str.ends_with('bcdef')) - assert(str.ends_with('def')) - assert(str.ends_with('f')) - assert(str.ends_with('')) - assert(!str.starts_with('bcdef')) - assert(!str.starts_with('abcdf')) - assert(!str.starts_with('abd')) - assert(!str.starts_with('ac')) - assert(!str.ends_with('abcde')) - assert(!str.ends_with('acdef')) - assert(!str.ends_with('cef')) - assert(!str.ends_with('df')) + assert(_string_ends_with(str, 'abcdef')) + assert(_string_ends_with(str, 'bcdef')) + assert(_string_ends_with(str, 'def')) + assert(_string_ends_with(str, 'f')) + assert(_string_ends_with(str, '')) + assert(!_string_ends_with(str, 'abcde')) + assert(!_string_ends_with(str, 'acdef')) + assert(!_string_ends_with(str, 'cef')) + assert(!_string_ends_with(str, 'df')) +} + +pub fn test_slices() { + let s = 'abc' + assert(s[:] == 'abc') + assert(s[0:#s] == 'abc') + + assert(s[:-1] == 'ab') + assert(s[:#s-1] == 'ab') + assert(s[:-2] == 'a') + assert(s[:#s-2] == 'a') + assert(s[:-3] == '') + assert(s[:#s-3] == '') + + assert(s[1:] == 'bc') + assert(s[-2:] == 'bc') + assert(s[2:] == 'c') + assert(s[-1:] == 'c') + assert(s[3:] == '') + assert(s[0:0] == '') +} + +pub fn test_find() { + let check = |s, sub, n| { + assert(n == _string_find(s, sub)) + } + check('abc', 'a', 0) + check('abc', 'b', 1) + check('abc', 'c', 2) + check('abc', 'd', -1) } -//{ -// let s = 'abc' -// assert(s[:] == 'abc') -// assert(s[:null] == 'abc') -// assert(s[null:] == 'abc') -// assert(s[null:null] == 'abc') -// assert(s[0:#s] == 'abc') -// -// assert(s[:-1] == 'ab') -// assert(s[:#s-1] == 'ab') -// assert(s[:-2] == 'a') -// assert(s[:#s-2] == 'a') -// assert(s[:-3] == '') -// assert(s[:#s-3] == '') -// -// assert(s[1:] == 'bc') -// assert(s[-2:] == 'bc') -// assert(s[2:] == 'c') -// assert(s[-1:] == 'c') -// assert(s[3:] == '') -// assert(s[0:0] == '') -// -// // clamped -// assert(s[4:] == '') -// assert(s[:-4] == '') -//} -// -//// String find: -//{ -// fn check(s: string, sub: string, n: int) { -// assert(n == s.find(sub)) -// } -// check('abc', 'a', 0) -// check('abc', 'b', 1) -// check('abc', 'c', 2) -// check('abc', 'd', -1) -//} -// -//// String split/join: -//{ -// fn check(s, sep, parts) { -// let a = s.split(sep) -// for i = 0, #a { -// assert(a[i] == parts[i]) -// } -// assert(#a == #parts) -// let result = sep.join(a) -// assert(result == s); -// } -// check('abc', 'a', ['', 'bc']) -// check('abc', 'b', ['a', 'c']) -// check('abc', 'c', ['ab', '']) -// check('abc', 'd', ['abc']) -// -// let a = ',a,,b,,,c,,,,d,,,,,e,,,,,,'.split(',') -// assert(''.join(a) == 'abcde') -//} +pub fn test_split_and_join() { + let check = |s, sep, parts: [str]| { + let a = _string_split(s, sep) + for i = 0, #a { + assert(a[i] == parts[i]) + } + assert(#a == #parts) + let result = _string_join(sep, a) + assert(result == s); + } + check('abc', 'a', ['', 'bc']) + check('abc', 'b', ['a', 'c']) + check('abc', 'c', ['ab', '']) + check('abc', 'd', ['abc']) + + let s = ',a,,b,,,c,,,,d,,,,,e,,,,,,' + let a = _string_split(s, ',') + assert(_string_join('', a) == 'abcde') +} diff --git a/test/test.h b/test/test.h index 68ccb2b..53c4852 100644 --- a/test/test.h +++ b/test/test.h @@ -9,10 +9,12 @@ X(primitive) \ X(function) \ X(closure) \ + X(string) \ X(struct) \ X(tuple) \ X(enum) \ X(vector) \ + X(map) \ X(poly_function) \ X(poly_struct) \ X(poly_enum) diff --git a/test/test_alloc.c b/test/test_alloc.c index fe3c11e..d2d5234 100644 --- a/test/test_alloc.c +++ b/test/test_alloc.c @@ -115,7 +115,7 @@ static void test_lots_of_allocations(paw_Env *P) int main(void) { -// driver(open_and_close); -// driver(test_small_allocations); + driver(open_and_close); + driver(test_small_allocations); driver(test_lots_of_allocations); } diff --git a/test/test_error.c b/test/test_error.c index 526445b..9a0fc67 100644 --- a/test/test_error.c +++ b/test/test_error.c @@ -207,6 +207,15 @@ static void test_syntax_error(void) test_compiler_error(PAW_ESYNTAX, "nested_struct", "", "struct S {x: int}"); test_compiler_error(PAW_ESYNTAX, "nested_enum", "", "enum E {X}"); test_compiler_error(PAW_ESYNTAX, "toplevel_var", "let v = 1", ""); + test_compiler_error(PAW_ESYNTAX, "bad_float", "", "let f = -1.0-"); + test_compiler_error(PAW_ESYNTAX, "bad_float_2", "", "let f = 1-.0-"); + test_compiler_error(PAW_ESYNTAX, "bad_float_3", "", "let f = 1e--1"); + test_compiler_error(PAW_ESYNTAX, "bad_float_4", "", "let f = 1e++1"); + test_compiler_error(PAW_ESYNTAX, "bad_float_5", "", "let f = 1e-1.0"); + test_compiler_error(PAW_ESYNTAX, "bad_float_6", "", "let f = 1e+1.0"); + test_compiler_error(PAW_ESYNTAX, "bad_float_7", "", "let f = 1e-1e1"); + test_compiler_error(PAW_ESYNTAX, "bad_float_8", "", "let f = 1e+1e1"); + test_compiler_error(PAW_ESYNTAX, "bad_float_9", "", "let f = 1.0.0"); } static void test_arithmetic_error(void) @@ -253,7 +262,6 @@ static void test_map_error(void) int main(void) { - test_compiler_error(PAW_ESYNTAX, "struct_unit_with_braces_on_init", "struct A", "let a = A{}"); test_name_error(); test_syntax_error(); test_type_error();