diff --git a/CMakeLists.txt b/CMakeLists.txt index d403678..2b5fa82 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -44,7 +44,6 @@ target_sources(paw PUBLIC ${PAW_SOURCE_DIR}/api.h ${PAW_SOURCE_DIR}/ast.h ${PAW_SOURCE_DIR}/auxlib.h - ${PAW_SOURCE_DIR}/array.h ${PAW_SOURCE_DIR}/call.h ${PAW_SOURCE_DIR}/code.h ${PAW_SOURCE_DIR}/debug.h @@ -65,10 +64,10 @@ target_sources(paw ${PAW_SOURCE_DIR}/unify.h ${PAW_SOURCE_DIR}/util.h ${PAW_SOURCE_DIR}/value.h + ${PAW_SOURCE_DIR}/vector.h PRIVATE ${PAW_SOURCE_DIR}/api.c ${PAW_SOURCE_DIR}/ast.c ${PAW_SOURCE_DIR}/auxlib.c - ${PAW_SOURCE_DIR}/array.c ${PAW_SOURCE_DIR}/call.c ${PAW_SOURCE_DIR}/check.c ${PAW_SOURCE_DIR}/code.c @@ -91,7 +90,8 @@ target_sources(paw ${PAW_SOURCE_DIR}/type.c ${PAW_SOURCE_DIR}/unify.c ${PAW_SOURCE_DIR}/util.c - ${PAW_SOURCE_DIR}/value.c) + ${PAW_SOURCE_DIR}/value.c + ${PAW_SOURCE_DIR}/vector.c) target_link_libraries(paw PRIVATE paw_context PUBLIC gc) diff --git a/README.md b/README.md index 802e190..b147799 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ Global variables are intoduced with the `global` keyword, and locals with `let`, Both globals and locals can only be referenced where they are visible (see [scope](#scope)), but globals can only be declared at the module level. Local variables can be shadowed and 'rebound', but globals cannot. A global can be shadowed by a local, however. -Locals can also be captured in a function or method body (see [functions](#functions)). +Locals can also be captured in a function body (see [functions](#functions)). ``` // initializer (' = 0') is required let x: int = 0 @@ -41,9 +41,8 @@ global g: string = "hello, world!" ``` ### Types -paw is statically-typed, meaning that types are bound to variables, not values. -The type of each variable must be known at compile time. -paw supports type inference on variable definitions. +paw is statically-typed, meaning all types must be known at compile-time. +paw supports type inference on variable definitions and function template calls. The following example demonstrates creation of the basic value types. ``` @@ -55,7 +54,7 @@ let v: Vec[int] = Vec[int]{1, 2, 3} let m: Map[string, int] = Map[string, int]{'a': 1, 'b': 2} let f: fn() -> int = some_function -// supports type inference +// type is inferred from the initializer let b = false let i = 40 + 2 let f = 1.0 * 2 @@ -74,7 +73,7 @@ let method = instance.times2 // fn(Object, int) -> int ``` ### Scope -paw implements lexical scoping, meaning variables declared in a given block can only be referenced from within that block, or one of its subblocks. +paw uses lexical scoping, meaning variables declared in a given block can only be referenced from within that block, or one of its subblocks. A block begins when a '{' token is encountered, and ends when a matching '}' is found. Many language constructs use blocks to create their own scope, like functions, structures, for loops, etc. Explicit scoping blocks are also supported. @@ -86,7 +85,7 @@ Explicit scoping blocks are also supported. ### Functions Functions are first-class in paw, which means they are treated like any other paw value. -Functions can be stored in variables, or passed as parameters to compose higher-order functions. +They can be stored in variables, or passed as parameters to higher-order functions. ``` fn fib(n: int) -> int { if n < 2 { @@ -100,13 +99,15 @@ fib(10) ### Structures ``` struct Object { - method() { - return self.value - } + a: int + b: string } -// create an instance -let o = Object{} +// all fields must be initialized +let o = Object{ + a: 1, + b: 'two', +} ``` ### Control flow @@ -172,71 +173,59 @@ assert(s == ','.join(a)) ``` ### Generics +Paw supports basic parametric polymorphism. +Variables with generic types must be treated generically, that is, they can only be assigned to other variables of the same type, passed to functions expecting a generic parameter, or stored in a container. +This allows each template to be type checked a single time, rather than once for each unique instantiation, and makes it easier to generate meaningful error messages. ``` -// function template -fn fib[T](n: T) -> T { - if n < 2 { - return n - } - return fib(n - 2) + fib(n - 1) +fn mapval[A, B](f: fn(A) -> B, a: A) -> B { + return f(a) +} +fn float2int(value: float) -> int { + return int(value) } -fib[int](10) - -// A template has no value representation. 'func' must be explicitly -// instantiated before it is stored in a variable. -let fib_i = fib[int] -fib_i(10) - -fib(10) // infer T = int +// infer A = float, B = int +let i = mapval(float2int, 1.5) +assert(i == 1) // struct template -struct Cls[S, T] { +struct Object[S, T] { a: S b: T - f(s: S, t: T) -> T { - self.a = self.a + s - return self.b + t - } - // method template - g[U](u: U) -> U { - return u - } } -let c = Cls{ +let c = Object{ a: 123, // infer S = int b: 'abc', // infer T = string } -let g_i = c.g[int] -g_i(42) -c.g(123) +let a = c.a + 1 +let b = c.b + 'two' ``` ### Vectors -TODO: implement as struct template Vec[T] ``` -let a = Vec{1, 2, 3} // infer T = int -assert(a[:1] == Vec {1}) -assert(a[1:-1] == Vec {2}) -assert(a[-1:] == Vec {3}) +let v = Vector{1, 2, 3} // infer T = int +assert(v[:1] == Vector{1}) +assert(v[1:-1] == Vector{2}) +assert(v[-1:] == Vector{3}) ``` ### Maps -TODO: implement as struct template Map[K, V] ``` let m = Map{1: 'a', 2: 'b'} // infer K = int, V = string m[3] = 42 m.erase(1) +assert(m == Map{2: 'b'}) + // prints 'default' print(m.get(1, 'default')) ``` ### Error handling ``` -fn divide_by_0(n: int) -> int { - return n / 0 +fn divide_by_0(n: int) { + n = n / 0 } let status = try(divide_by_0, 42) assert(status != 0) @@ -264,13 +253,15 @@ assert(status != 0) ## Roadmap + [x] static typing ++ [ ] builtin containers + [ ] pattern matching (`match` construct) + [ ] pattern matching (`let` bindings) + [ ] sum types/discriminated unions (`enum`) + [ ] product types (tuple) -+ [ ] nested templates ++ [ ] generic constraints/bounds + [ ] custom garbage collector + [ ] associated types on `struct`s (`A::B`) ++ [ ] struct methods + [ ] metamethods + [ ] existential types diff --git a/src/api.c b/src/api.c index f4efd3c..9a6a3e0 100644 --- a/src/api.c +++ b/src/api.c @@ -16,6 +16,7 @@ #include "str.h" #include "type.h" #include "value.h" +#include "vector.h" #include #include #include @@ -128,7 +129,7 @@ paw_Bool paw_is_function(paw_Env *P, int index) paw_Bool paw_is_array(paw_Env *P, int index) { - return paw_type(P, index) == PAW_TARRAY; + return paw_type(P, index) == PAW_TVECTOR; } paw_Bool paw_is_tuple(paw_Env *P, int index) @@ -206,32 +207,26 @@ void paw_push_float(paw_Env *P, paw_Float f) pawC_pushf(P, f); } -//void paw_push_int(paw_Env *P, paw_Int i) -//{ -// if (i < PAW_INT_MIN || i > PAW_INT_MAX) { -// // 'i' is wider than 47 bits and must go in a bigint. -// Value *pv = pawC_push0(P); -// pawB_from_int(P, pv, i); -// } else { -// pawC_pushi(P, i); -// } -//} -// -//void paw_push_native(paw_Env *P, paw_Function fn, int n) -//{ -// Value *pv = pawC_push0(P); -// Native *o = pawV_new_native(P, fn, n); -// v_set_native(pv, o); -// -// StackPtr top = P->top.p; -// const StackPtr base = top - n - 1; -// for (int i = 0; i < n; ++i) { -// o->up[i] = base[i]; -// } -// // Replace upvalues with closure object -// base[0] = top[-1]; -// P->top.p = base + 1; -//} +void paw_push_int(paw_Env *P, paw_Int i) +{ + pawC_pushi(P, i); +} + +void paw_push_native(paw_Env *P, paw_Function fn, int n) +{ + Value *pv = pawC_push0(P); + Native *o = pawV_new_native(P, fn, n); + v_set_object(pv, o); + + StackPtr top = P->top.p; + const StackPtr base = top - n - 1; + for (int i = 0; i < n; ++i) { + o->up[i] = base[i]; + } + // replace upvalues with closure object + base[0] = top[-1]; + P->top.p = base + 1; +} const char *paw_push_string(paw_Env *P, const char *s) { @@ -246,10 +241,10 @@ const char *paw_push_nstring(paw_Env *P, const char *s, size_t n) const char *paw_push_vfstring(paw_Env *P, const char *fmt, va_list arg) { -// Buffer buf; -// pawL_init_buffer(P, &buf); -// pawL_add_vfstring(P, &buf, fmt, arg); -// pawL_push_result(P, &buf); + Buffer buf; + pawL_init_buffer(P, &buf); + pawL_add_vfstring(P, &buf, fmt, arg); + pawL_push_result(P, &buf); return paw_string(P, -1); } @@ -262,31 +257,14 @@ const char *paw_push_fstring(paw_Env *P, const char *fmt, ...) return s; } -//void paw_push_bigint(paw_Env *P, paw_Digit *d, int n, int neg) -//{ -// const BigInt bi = { -// .neg = neg, -// .size = n, -// .buf = d, -// }; -// Value *pv = pawC_push0(P); -// pawB_copy(P, pv, &bi, 0); -//} - paw_Bool paw_bool(paw_Env *P, int index) { return v_true(*access(P, index)); } -paw_Int paw_intx(paw_Env *P, int index, paw_Bool *plossless) +paw_Int paw_int(paw_Env *P, int index) { - paw_Bool lossless; - const Value v = *access(P, index); - //const paw_Int i = pawV_to_int64(v, &lossless); - if (plossless) { - *plossless = lossless; - } - //return i; + return v_int(*access(P, index)); } paw_Float paw_float(paw_Env *P, int index) @@ -336,15 +314,27 @@ void *paw_pointer(paw_Env *P, int index) // pawR_to_string(P, &len); // paw_replace(P, i); //} -// + size_t paw_length(paw_Env *P, int index) { -// paw_push_value(P, index); -// pawR_unop(P, PAW_OPLEN); // replace value with its length -// -// const paw_Int n = paw_int(P, -1); -// paw_pop(P, 1); -// return cast_size(n); + size_t result; + paw_push_value(P, index); + const Value v = P->top.p[-1]; + switch (v.o->gc_kind) { + case VSTRING: + result = pawS_length(v_string(v)); + break; + case VVECTOR: + result = pawA_length(v_vector(v)); + break; + case VMAP: + result = pawH_length(v_map(v)); + break; + default: + result = 0; + } + paw_pop(P, 1); + return result; } void paw_pop(paw_Env *P, int n) diff --git a/src/api.h b/src/api.h index e98d24a..d505f39 100644 --- a/src/api.h +++ b/src/api.h @@ -20,8 +20,8 @@ static inline const char *api_typename(int type) return "float"; case PAW_TSTRING: return "string"; - case PAW_TARRAY: - return "array"; + case PAW_TVECTOR: + return "vector"; case PAW_TFUNCTION: return "function"; case PAW_TSTRUCT: diff --git a/src/auxlib.c b/src/auxlib.c index 923f463..6f9b661 100644 --- a/src/auxlib.c +++ b/src/auxlib.c @@ -2,11 +2,11 @@ // 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 "array.h" #include "map.h" #include "mem.h" #include "rt.h" #include "util.h" +#include "vector.h" #include static void grow_buffer(paw_Env *P, Buffer *buf, int boxloc) diff --git a/src/check.c b/src/check.c index f158ff8..33a64ce 100644 --- a/src/check.c +++ b/src/check.c @@ -6,7 +6,6 @@ // from the parser into a graph by unifying types based on lexical scope. #include "ast.h" -#include "array.h" #include "check.h" #include "code.h" #include "debug.h" @@ -17,6 +16,7 @@ #include "str.h" #include "type.h" #include "unify.h" +#include "vector.h" // Helper macros #define syntax_error(R, ...) pawX_error((R)->lex, __VA_ARGS__) @@ -774,6 +774,7 @@ static void visit_cond_expr(AstVisitor *V, CondExpr *e) Type *lhs = resolve_expr(V, e->lhs); Type *rhs = resolve_expr(V, e->rhs); unify(V->state.R, lhs, rhs); + e->type = lhs; } static void visit_unop_expr(AstVisitor *V, UnOpExpr *e) diff --git a/src/codegen.c b/src/codegen.c index 73c9b8c..73e0712 100644 --- a/src/codegen.c +++ b/src/codegen.c @@ -2,7 +2,6 @@ // 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 "array.h" #include "ast.h" #include "code.h" #include "gc_aux.h" @@ -11,6 +10,7 @@ #include "mem.h" #include "parse.h" #include "type.h" +#include "vector.h" #define syntax_error(G, ...) pawX_error((G)->lex, __VA_ARGS__) #define is_global(lex) (is_toplevel(lex) && (lex)->fs->bs->outer == NULL) @@ -730,7 +730,7 @@ static void code_composite_lit(AstVisitor *V, LiteralExpr *lit) AstExpr *attr = e->items->first; while (attr != NULL) { V->visit_expr(V, attr); - pawK_code_U(fs, OP_INITATTR, attr->item.index); + pawK_code_U(fs, OP_INITFIELD, attr->item.index); attr = attr->hdr.next; } } @@ -815,7 +815,7 @@ static void code_unop_expr(AstVisitor *V, UnOpExpr *e) FuncState *fs = V->state.G->fs; V->visit_expr(V, e->target); - code_op(fs, OP_UNOP, e->op, e->type); + code_op(fs, OP_UNOP, e->op, a_type(e->target)); } static void code_binop_expr(AstVisitor *V, BinOpExpr *e) @@ -823,7 +823,9 @@ static void code_binop_expr(AstVisitor *V, BinOpExpr *e) FuncState *fs = V->state.G->fs; V->visit_expr(V, e->lhs); V->visit_expr(V, e->rhs); - code_op(fs, OP_BINOP, e->op, e->type); + + paw_assert(a_type(e->lhs) == a_type(e->rhs)); + code_op(fs, OP_BINOP, e->op, a_type(e->lhs)); } static void code_decl_stmt(AstVisitor *V, AstDeclStmt *s) @@ -1172,13 +1174,13 @@ static void code_for_stmt(AstVisitor *V, ForStmt *s) // FuncState *fs = G->fs; // // visit_exprs(V, e->items); -// pawK_code_U(fs, OP_NEWARRAY, e->nitems); +// pawK_code_U(fs, OP_NEWVECTOR, e->nitems); //} // //static void code_map_expr(AstVisitor *V, MapExpr *e) //{ // visit_exprs(V, e->items); -// pawK_code_U(G->fs, OP_NEWARRAY, e->items.size); +// pawK_code_U(G->fs, OP_NEWMAP, e->items.size); //} static paw_Bool is_index_template(Generator *G, const Type *type) diff --git a/src/debug.c b/src/debug.c index e0bfc2c..6f1e060 100644 --- a/src/debug.c +++ b/src/debug.c @@ -1,5 +1,5 @@ #include "debug.h" -#include "array.h" +#include "vector.h" #include "auxlib.h" #include "call.h" #include "map.h" @@ -84,8 +84,8 @@ const char *paw_op_name(Op op) return "PUSHCONST"; case OP_COPY: return "COPY"; - case OP_INITATTR: - return "INITATTR"; + case OP_INITFIELD: + return "INITFIELD"; case OP_POP: return "POP"; case OP_CLOSE: @@ -122,8 +122,8 @@ const char *paw_op_name(Op op) return "SETUPVALUE"; case OP_NEWINSTANCE: return "NEWINSTANCE"; - case OP_NEWARRAY: - return "NEWARRAY"; + case OP_NEWVECTOR: + return "NEWVECTOR"; case OP_NEWMAP: return "NEWMAP"; case OP_VARARG: @@ -183,8 +183,8 @@ void paw_dump_opcode(OpCode opcode) case OP_COPY: printf("COPY\n"); break; - case OP_INITATTR: - printf("INITATTR\n"); + case OP_INITFIELD: + printf("INITFIELD\n"); break; case OP_POP: printf("POP\n"); @@ -240,8 +240,8 @@ void paw_dump_opcode(OpCode opcode) case OP_NEWINSTANCE: printf("NEWINSTANCE\n"); break; - case OP_NEWARRAY: - printf("NEWARRAY\n"); + case OP_NEWVECTOR: + printf("NEWVECTOR\n"); break; case OP_NEWMAP: printf("NEWMAP\n"); @@ -322,7 +322,7 @@ void dump_aux(paw_Env *P, Proto *proto, Buffer *print) break; } - case OP_NEWARRAY: { + case OP_NEWVECTOR: { pawL_add_fstring(P, print, " ; %d elements", get_U(opcode)); break; } @@ -516,6 +516,9 @@ void paw_dump_stack(paw_Env *P) const paw_Type code = info.var.code; printf(" %3d: %s%s (%d)\n", i, name->text, capture, code); } + if (cf == P->cf) { + break; + } cf = cf->next; } } diff --git a/src/gc_aux.c b/src/gc_aux.c index 0efd9db..2897744 100644 --- a/src/gc_aux.c +++ b/src/gc_aux.c @@ -2,11 +2,11 @@ // 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 "gc_aux.h" -#include "array.h" #include "env.h" #include "map.h" #include "mem.h" #include "util.h" +#include "vector.h" #include #include #include diff --git a/src/lib.c b/src/lib.c index b3cab98..433927c 100644 --- a/src/lib.c +++ b/src/lib.c @@ -1,6 +1,5 @@ #include "lib.h" #include "api.h" -#include "array.h" #include "auxlib.h" #include "call.h" #include "gc_aux.h" @@ -9,6 +8,7 @@ #include "os.h" #include "rt.h" #include "type.h" +#include "vector.h" #include #include #include @@ -203,7 +203,7 @@ static int base_print(paw_Env *P) //static int array_insert(paw_Env *P) //{ // pawL_check_argc(P, 2); -// Array *a = v_array(cf_base(0)); +// Vector *a = v_vector(cf_base(0)); // const paw_Int i = pawL_check_int(P, 1); // pawA_insert(P, a, i, cf_base(2)); // return 0; @@ -212,7 +212,7 @@ static int base_print(paw_Env *P) //static int array_push(paw_Env *P) //{ // const int argc = pawL_check_varargc(P, 1, UINT8_MAX); -// Array *a = v_array(cf_base(0)); +// Vector *a = v_vector(cf_base(0)); // for (int i = 0; i < argc; ++i) { // pawA_push(P, a, cf_base(i + 1)); // } @@ -226,7 +226,7 @@ static int base_print(paw_Env *P) // // Argument, if present, indicates the index at which to remove an // // element. Default to -1: the last element. // const paw_Int i = argc ? v_int(cf_base(1)) : -1; -// Array *a = v_array(cf_base(0)); +// Vector *a = v_vector(cf_base(0)); // // P->top.p[-1] = *pawA_get(P, a, i); // may replace 'a' // pawA_pop(P, a, i); // never allocates, last line OK @@ -236,7 +236,7 @@ static int base_print(paw_Env *P) //static int array_clone(paw_Env *P) //{ // pawL_check_argc(P, 0); -// Array *a = v_array(cf_base(0)); +// Vector *a = v_vector(cf_base(0)); // Value *pv = pawC_push0(P); // pawA_clone(P, pv, a); // return 1; @@ -320,7 +320,7 @@ static int string_find(paw_Env *P) // Buffer buf; // pawL_init_buffer(P, &buf); // paw_Int itr = PAW_ITER_INIT; -// Array *a = v_array(seq); +// 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. @@ -370,36 +370,36 @@ static int string_clone(paw_Env *P) return 1; } -//static int map_get(paw_Env *P) -//{ -// const int argc = pawL_check_varargc(P, 1, 2); -// const Value key = cf_base(1); -// Map *m = v_map(cf_base(0)); -// const Value *pv = pawH_get(P, m, key); -// if (pv) { -// P->top.p[-1] = *pv; -// } else if (argc != 2) { -// pawH_key_error(P, key); -// } -// return 1; -//} -// -//static int map_erase(paw_Env *P) -//{ -// pawL_check_argc(P, 1); -// Map *m = v_map(cf_base(0)); -// pawH_remove(P, m, cf_base(1)); -// return 0; -//} -// -//static int map_clone(paw_Env *P) -//{ -// pawL_check_argc(P, 0); -// Map *m = v_map(cf_base(0)); -// Value *pv = pawC_push0(P); -// pawH_clone(P, pv, m); -// return 1; -//} +static int map_get(paw_Env *P) +{ + const int argc = pawL_check_varargc(P, 1, 2); + const Value key = cf_base(1); + Map *m = v_map(cf_base(0)); + const Value *pv = pawH_get(P, m, key); + if (pv) { + P->top.p[-1] = *pv; + } else if (argc != 2) { + pawH_key_error(P, key, PAW_TUNIT); // TODO: key type for printing + } + return 1; +} + +static int map_erase(paw_Env *P) +{ + pawL_check_argc(P, 1); + Map *m = v_map(cf_base(0)); + pawH_remove(P, m, cf_base(1)); + return 0; +} + +static int map_clone(paw_Env *P) +{ + pawL_check_argc(P, 0); + Map *m = v_map(cf_base(0)); + Value *pv = pawC_push0(P); + pawH_clone(P, pv, m); + return 1; +} #define L_MAX_SIZE 256 @@ -421,19 +421,6 @@ typedef struct pawL_Layout { int nmethods; } pawL_Layout; -// -// struct A[T] { -// a: A[int] // ?? -// b: A[T] -// c: A // sugar for A[T] -// } -// -// A[T] = declare_struct(A, 1) -// A[int] = instantiate_struct(A, int) -// A[T] = { -// a: A[int], -// } - typedef struct pawL_GenericCtx { int ngenerics; } pawL_GenericCtx; diff --git a/src/map.c b/src/map.c index 58f099a..4b13899 100644 --- a/src/map.c +++ b/src/map.c @@ -9,107 +9,87 @@ #include #include -#define next_idx(m, i) ((i + 1) & ((m)->capacity - 1)) - -static paw_Bool is_unoccupied(Value v) -{ - return pawH_is_vacant(v) || pawH_is_erased(v); -} - -static size_t prepare_insert(Map *m, Value key) +// Total number of bytes needed for each map slot +// The map's internal buffer is divided into 3 sections: the first is an array +// of MapMeta, and the last 2 are arrays of Value. Each array has the same length, +// equal to the map's 'capacity' field. The 'capacity' is guaranteed to be either +// 0 or a power-of-2 greater than or equal to paw_alignof(Value), so the Value +// arrays are suitably-aligned. +#define MAP_ITEM_TOTAL (sizeof(MapMeta) + sizeof(Value) * 2) + +static inline Value *insert_aux(Map *m, Value key) { - paw_assert(pawH_is_occupied(key)); - size_t itr = pawH_index(m, key); - pawH_locate(m, key, is_unoccupied); - // Search for the first vacant slot - for (; !pawH_is_vacant(m->keys[itr]); itr = next_idx(m, itr)) { - const Value k = m->keys[itr]; - if (pawH_is_occupied(k) && k.u == key.u) { - break; + MapCursor erased; + paw_Bool found_erased = PAW_FALSE; + MapCursor mc = h_cursor_init(m, key); + while (!h_is_vacant(&mc)) { + if (h_is_erased(&mc)) { + if (!found_erased) { + // new item replaces the first erased item, continue searching + // for a duplicate + found_erased = PAW_TRUE; + erased = mc; + } + } else if (h_cursor_key(&mc)->u == key.u) { + // found a duplicate: replace it + return h_cursor_value(&mc); } + h_cursor_next(&mc); } - return itr; + ++m->length; + mc = found_erased ? erased : mc; + h_set_state(&mc, MAP_ITEM_OCCUPIED); + h_cursor_key(&mc)[0] = key; + return h_cursor_value(&mc); } static void add_item(Map *m, Value key, Value value) { - const size_t i = prepare_insert(m, key); - m->keys[i] = key; - m->values[i] = value; - ++m->length; + Value *pvalue = insert_aux(m, key); + *pvalue = value; } -static void grow_map(paw_Env *P, Map *m) +static void rehash_map(Map old_m, Map *m) { - size_t n = 4; - while (n <= m->capacity) { - n *= 2; - } - - // NOTE: pawM_new*() might cause an emergency collection. m->keys and m->values - // are still reachable until pawM_new() returns, so they won't be freed. The - // calls to add_item() below will never cause an allocation. - Value *buffer = pawM_new_vec(P, n * 2, Value); - const size_t old_n = m->capacity; - - Value *keys = m->keys; - Value *values = m->values; - m->keys = buffer; - m->values = m->keys + n; - m->capacity = n; - + MapMeta *mm = old_m.data; + const Value *ks = pawH_key(&old_m, 0); + const Value *vs = pawH_value(&old_m, 0); const size_t count = m->length; m->length = 0; - for (size_t i = 0; m->length < count; ++i) { - if (pawH_is_occupied(keys[i])) { - add_item(m, keys[i], values[i]); + if (mm[i].state == MAP_ITEM_OCCUPIED) { + add_item(m, ks[i], vs[i]); } } - pawM_free_vec(P, keys, old_n * 2); - check_gc(P); } -static void grow_map_if_necessary(paw_Env *P, Map *m) +static void free_buffer(paw_Env *P, void *buffer, size_t capacity) { - if (m->length >= m->capacity / 4) { - grow_map(P, m); - } + pawM_free_(P, buffer, capacity * MAP_ITEM_TOTAL); } -#define MAP_FACTOR 4 - -void pawH_reserve(paw_Env *P, Map *m, size_t length) +static void grow_map(paw_Env *P, Map *m) { - if (length < m->capacity / MAP_FACTOR) { - return; - } - size_t n = 4; - while (n <= length) { + size_t n = paw_alignof(Value); + while (n <= m->capacity) { n *= 2; } - // NOTE: pawM_new*() might cause an emergency collection. m->keys and m->values - // are still reachable until pawM_new() returns, so they won't be freed. The - // calls to add_item() below will never cause an allocation. - Value *buffer = pawM_new_vec(P, n * 2, Value); - const size_t old_n = m->capacity; + // NOTE: Allocation might cause an emergency collection. Keys and values are + // still reachable until pawM_alloc() returns, so they won't be freed. + pawM_check_size(P, 0, n, MAP_ITEM_TOTAL); + const size_t new_size = n * MAP_ITEM_TOTAL; + void *buffer = pawM_alloc(P, NULL, 0, new_size); + memset(buffer, MAP_ITEM_VACANT, n * sizeof(MapMeta)); + const Map old_m = *m; - Value *keys = m->keys; - Value *values = m->values; - m->keys = buffer; - m->values = m->keys + n; + m->data = buffer; m->capacity = n; - const size_t count = m->length; - m->length = 0; - - for (size_t i = 0; m->length < count; ++i) { - if (pawH_is_occupied(keys[i])) { - add_item(m, keys[i], values[i]); - } + if (old_m.capacity > 0) { + rehash_map(old_m, m); } - pawM_free_vec(P, keys, old_n * 2); + free_buffer(P, old_m.data, old_m.capacity); check_gc(P); } @@ -122,20 +102,18 @@ Map *pawH_new(paw_Env *P) void pawH_free(paw_Env *P, Map *m) { - pawM_free_vec(P, m->keys, m->capacity * 2); + free_buffer(P, m->data, m->capacity); pawM_free(P, m); } -size_t pawH_create_aux_(paw_Env *P, Map *m, Value key) +#define FILL_FACTOR 4 + +Value *pawH_create(paw_Env *P, Map *m, Value key) { - grow_map_if_necessary(P, m); - const size_t i = prepare_insert(m, key); - if (!pawH_is_occupied(m->keys[i])) { - m->keys[i] = key; - v_set_0(&m->values[i]); - ++m->length; + if (m->length >= m->capacity / FILL_FACTOR) { + grow_map(P, m); } - return i; + return insert_aux(m, key); } void pawH_clone(paw_Env *P, StackPtr sp, Map *m) @@ -151,7 +129,7 @@ static paw_Bool items_equal(paw_Env *P, const Value x, const Value y) sp[0] = y; sp[1] = x; - paw_assert(0); + paw_assert(0); // TODO: need value type // pawR_binop(P, BINARY_EQ); const paw_Bool b = paw_bool(P, -1); @@ -164,36 +142,39 @@ paw_Bool pawH_equals(paw_Env *P, Map *lhs, Map *rhs) if (lhs->length != rhs->length) { return PAW_FALSE; } - paw_Int i = PAW_ITER_INIT; - while (pawH_iter(lhs, &i)) { - Value *v = pawH_action(P, rhs, lhs->keys[i], MAP_ACTION_NONE); - if (!v || !items_equal(P, lhs->values[i], *v)) { + MapCursor mc = {lhs, 0}; + while (mc.index < lhs->capacity) { + Value *v = pawH_action(P, rhs, *h_cursor_key(&mc), MAP_ACTION_NONE); + if (!v || !items_equal(P, *h_cursor_value(&mc), *v)) { return PAW_FALSE; } + ++mc.index; } return PAW_TRUE; } void pawH_extend(paw_Env *P, Map *dst, Map *src) { - for (size_t i = 0; i < src->capacity; ++i) { - Value key = src->keys[i]; - if (pawH_is_occupied(key)) { + MapCursor mc = {src, 0}; + while (mc.index < src->capacity) { + if (h_get_state(&mc) != MAP_ITEM_OCCUPIED) { + const Value key = *h_cursor_key(&mc); Value *value = pawH_action(P, dst, key, MAP_ACTION_CREATE); - *value = src->values[i]; + *value = *h_cursor_value(&mc); } + ++mc.index; } } -void pawH_key_error(paw_Env *P, Value key) +void pawH_key_error(paw_Env *P, Value key, paw_Type type) { - // Buffer buf; - // pawL_init_buffer(P, &buf); - // pawL_add_string(P, &buf, "key '"); - // pawC_pushv(P, key); - // pawL_add_value(P, &buf); - // pawL_add_string(P, &buf, "' does not exist"); - // pawL_add_char(P, &buf, '\0'); - // pawL_push_result(P, &buf); - pawC_throw(P, PAW_EKEY); + Buffer buf; + pawL_init_buffer(P, &buf); + pawL_add_string(P, &buf, "key '"); + pawC_pushv(P, key); + pawL_add_value(P, &buf, type); + pawL_add_string(P, &buf, "' does not exist"); + pawL_add_char(P, &buf, '\0'); + pawL_push_result(P, &buf); + pawC_throw(P, PAW_EKEY); } diff --git a/src/map.h b/src/map.h index 32a3fc1..f1c5c82 100644 --- a/src/map.h +++ b/src/map.h @@ -4,103 +4,114 @@ #ifndef PAW_MAP_H #define PAW_MAP_H -// TODO: This code won't work properly anymore: we do not have a way to indicate that a -// given key is a 'tombstone', without limiting what values can be used (using -1 -// value, but a valid integer could easily be -1). Before, we just used 'null'. Only -// works for pointer keys right now. -// Ideas: -// (+) Just use chaining. -// (+) Reserve a single key value to represent 'null', or 'does not exist'. -// Use the value field to indicate either that the item never existed, or that -// it was erased. Problematic, as it limits the keyspace. -// (+) Use a separate array (a bitfield, really) to track which keys are nonexistent. -// (+) Create a somewhat more complicated data structure with an 'index' (see Python -// 'dict' implementation). - #include "paw.h" #include "util.h" #include "value.h" -static inline paw_Bool pawH_is_vacant(Value key) +typedef struct MapCursor { + Map *map; + size_t index; +} MapCursor; + +#define h_is_vacant(mc) (h_get_state(mc) == MAP_ITEM_VACANT) +#define h_is_occupied(mc) (h_get_state(mc) == MAP_ITEM_OCCUPIED) +#define h_is_erased(mc) (h_get_state(mc) == MAP_ITEM_ERASED) + +#define pawH_meta(m, index) (&cast((m)->data, MapMeta *)[index]) + +static inline Value *pawH_key(Map *m, size_t index) { - return key.u == 0; + char *data = cast(m->data, char *); + data += sizeof(MapMeta) * m->capacity; + return &cast(data, Value *)[index]; } -static inline paw_Bool pawH_is_erased(Value key) +static inline Value *pawH_value(Map *m, size_t index) { - return key.i == -1; + return pawH_key(m, index) + m->capacity; } -static inline paw_Bool pawH_is_occupied(Value key) +static inline MapState h_get_state(MapCursor *mc) { - return !pawH_is_vacant(key) && !pawH_is_erased(key); + return cast(mc->map->data, MapMeta *)[mc->index].state; } -#define pawH_index(m, k) check_exp(pawH_is_occupied(k), pawV_hash(k) & ((m)->capacity - 1)) +static inline void h_set_state(MapCursor *mc, MapState state) +{ + cast(mc->map->data, MapMeta *)[mc->index].state = state; +} -// Set 'itr' to the index at which the key 'k' is located, or the first index for -// which the function-like macro 'cc' evaluates to true (if 'k' is not found). -// Must not be called if the map has length 0 -#define pawH_locate(m, k, cc) \ - for (size_t mask = (m)->capacity - 1;;) { \ - Value ki = (m)->keys[itr]; \ - if ((cc)(ki) || (ki).u == (k).u) { \ - break; \ - } \ - itr = (itr + 1) & mask; \ - } +static inline Value *h_cursor_key(MapCursor *mc) +{ + return pawH_key(mc->map, mc->index); +} -typedef enum MapAction { - MAP_ACTION_NONE, - MAP_ACTION_CREATE, - MAP_ACTION_REMOVE, -} MapAction; +static inline Value *h_cursor_value(MapCursor *mc) +{ + return pawH_value(mc->map, mc->index); +} + +static void h_cursor_next(MapCursor *mc) +{ + mc->index = (mc->index + 1) & (mc->map->capacity - 1); +} + +static inline MapCursor h_cursor_init(Map *m, Value key) +{ + return (MapCursor){m, pawV_hash(key) & (m->capacity - 1)}; +} + +static inline MapCursor h_cursor_lookup(Map *m, Value key) +{ + MapCursor mc = h_cursor_init(m, key); + while (!h_is_vacant(&mc)) { + if (h_is_occupied(&mc) && h_cursor_key(&mc)->u == key.u) { + break; + } + h_cursor_next(&mc); + } + return mc; +} Map *pawH_new(paw_Env *P); void pawH_free(paw_Env *P, Map *m); paw_Bool pawH_equals(paw_Env *P, Map *lhs, Map *rhs); void pawH_extend(paw_Env *P, Map *dst, Map *src); void pawH_clone(paw_Env *P, StackPtr sp, Map *m); -void pawH_key_error(paw_Env *P, Value key); -size_t pawH_create_aux_(paw_Env *P, Map *m, Value key); +void pawH_key_error(paw_Env *P, Value key, paw_Type type); +Value *pawH_create(paw_Env *P, Map *m, Value key); static inline size_t pawH_length(const Map *m) { return m->length; } -//void pawH_reserve(paw_Env *P, Map *m, size_t length); -// -//static size_t pawH_search(paw_Env *P, Map *m, Value key) -//{ -// pawH_reserve(P, m, pawH_length(m) + 1); -// size_t itr = pawH_index(m, key); -// pawH_locate(m, key, pawH_is_vacant); -// return itr; -//} +typedef enum MapAction { + MAP_ACTION_NONE, + MAP_ACTION_CREATE, + MAP_ACTION_REMOVE, +} MapAction; static inline Value *pawH_action(paw_Env *P, Map *m, Value key, MapAction action) { if (action == MAP_ACTION_CREATE) { - const size_t i = pawH_create_aux_(P, m, key); - return &m->values[i]; + return pawH_create(P, m, key); } else if (m->length == 0) { return NULL; } - size_t itr = pawH_index(m, key); - pawH_locate(m, key, pawH_is_vacant); - if (!pawH_is_occupied(m->keys[itr])) { + MapCursor mc = h_cursor_lookup(m, key); + if (!h_is_occupied(&mc)) { return NULL; } if (action == MAP_ACTION_REMOVE) { - m->keys[itr].i = -1; // tombstone + h_set_state(&mc, MAP_ITEM_ERASED); --m->length; // Return the address of the slot to indicate success. - return &m->keys[itr]; + return h_cursor_key(&mc); } paw_assert(action == MAP_ACTION_NONE); - return &m->values[itr]; + return h_cursor_value(&mc); } static inline paw_Bool pawH_contains(paw_Env *P, Map *m, Value key) @@ -112,7 +123,7 @@ static inline void pawH_insert(paw_Env *P, Map *m, Value key, Value value) { Value *slot = pawH_action(P, m, key, MAP_ACTION_CREATE); if (!slot) { - pawH_key_error(P, key); + pawH_key_error(P, key, PAW_TSTRING); // TODO: key type } *slot = value; } @@ -120,7 +131,7 @@ static inline void pawH_insert(paw_Env *P, Map *m, Value key, Value value) static inline void pawH_remove(paw_Env *P, Map *m, Value key) { if (!pawH_action(P, m, key, MAP_ACTION_REMOVE)) { - pawH_key_error(P, key); + pawH_key_error(P, key, PAW_TSTRING); // TODO: key type } } @@ -137,8 +148,8 @@ static inline void pawH_set(paw_Env *P, Map *m, Value key, Value value) static inline paw_Bool pawH_iter(const Map *m, paw_Int *itr) { for (++*itr; *itr < paw_cast_int(m->capacity); ++*itr) { - Value *k = &m->keys[*itr]; - if (pawH_is_occupied(*k)) { + const MapMeta *mm = pawH_meta(m, *itr); + if (mm->state == MAP_ITEM_OCCUPIED) { return PAW_TRUE; } } diff --git a/src/opcode.h b/src/opcode.h index a11de6c..062e46c 100644 --- a/src/opcode.h +++ b/src/opcode.h @@ -115,8 +115,8 @@ OP_GETUPVALUE,// U - Up[u] - OP_SETUPVALUE,// U v - Up[u] = v OP_NEWINSTANCE,// U - v v = new instance of class C[u] -OP_INITATTR,// TODO: call this OP_INITFIELD U i v i i.fields[u] = v -OP_NEWARRAY,// U v_u..v_1 [v_u..v_1] - +OP_INITFIELD,// U i v i i.fields[u] = v +OP_NEWVECTOR,// U v_u..v_1 [v_u..v_1] - OP_NEWMAP,// U v_2n..v_1 {v_2n..v_1} - OP_FORNUM0,// S *-*-*-*-*-*-*-*-* see notes *-*-*-*-*-*-*-*-* @@ -141,8 +141,6 @@ OP_GETATTR,// U v v.fields[u] - OP_SETATTR,// U v x - v.fields[u]=x OP_GETITEM,// - v i v[i] - OP_SETITEM,// - v i x - v[i]=x -OP_GETSLICE,// - v i j v[i:j] - -OP_SETSLICE,// - v i j x - v[i:j]=x NOPCODES } Op; diff --git a/src/parse.c b/src/parse.c index 03d6a93..96e525f 100644 --- a/src/parse.c +++ b/src/parse.c @@ -776,9 +776,10 @@ static AstExpr *suffixed_expr(Lex *lex) case '.': e = selector_expr(lex, e); break; - case TK_COLON2: - e = access_expr(lex, e); - break; + // TODO: Change syntax of conditional expr so that this will work +// case TK_COLON2: +// e = access_expr(lex, e); +// break; case '[': e = index_expr(lex, e); break; diff --git a/src/paw.h b/src/paw.h index 12e0375..b3b64c3 100644 --- a/src/paw.h +++ b/src/paw.h @@ -67,13 +67,14 @@ int paw_call(paw_Env *P, int argc); #define PAW_TFLOAT 3 #define PAW_TSTRING 4 #define PAW_TTUPLE 5 -#define PAW_TARRAY 6 -#define PAW_TENUM 7 -#define PAW_TFUNCTION 8 -#define PAW_TSTRUCT 9 -#define PAW_TFOREIGN 10 -#define PAW_TMODULE 11 -#define PAW_NTYPES 12 +#define PAW_TVECTOR 6 +#define PAW_TMAP 7 +#define PAW_TENUM 8 +#define PAW_TFUNCTION 9 +#define PAW_TSTRUCT 10 +#define PAW_TFOREIGN 11 +#define PAW_TMODULE 12 +#define PAW_NTYPES 13 paw_Bool paw_is_truthy(paw_Env *P, int index); paw_Bool paw_is_null(paw_Env *P, int index); @@ -150,13 +151,8 @@ void paw_eq_m(paw_Env *P); // Getters (stack -> C): // -// Return a value as a 64-bit signed integer -// If nonnull, this function will store 0 in 'plossless' if the integer was -// too large to fit in the return type, and 1 otherwise. Truncation affects the -// high bits. -paw_Int paw_intx(paw_Env *P, int index, paw_Bool *plossless); - paw_Bool paw_bool(paw_Env *P, int index); +paw_Int paw_int(paw_Env *P, int index); paw_Float paw_float(paw_Env *P, int index); const char *paw_string(paw_Env *P, int index); paw_Function paw_native(paw_Env *P, int index); @@ -222,11 +218,6 @@ void paw_rotate(paw_Env *P, int index, int n); void paw_shift(paw_Env *P, int n); void paw_copy(paw_Env *P, int from, int to); -static inline paw_Int paw_int(paw_Env *P, int index) -{ - return paw_intx(P, index, NULL); -} - // Move the top stack value to the given index // Shifts elements above the target index up by 1 slot. static inline void paw_insert(paw_Env *P, int index) diff --git a/src/rt.c b/src/rt.c index ddec553..f89279d 100644 --- a/src/rt.c +++ b/src/rt.c @@ -3,7 +3,7 @@ // LICENSE.md. See AUTHORS.md for a list of contributor names. #include "prefix.h" -#include "array.h" +#include "vector.h" #include "auxlib.h" #include "call.h" #include "env.h" @@ -46,18 +46,17 @@ #define vm_pushb(b) pawC_pushb(P, b) #define vm_pusho(o) pawC_pusho(P, cast_object(o)) -// Slot 0 (the callable or 'self') is an implicit parameter that doesn't -// contribute to argc. +// 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_push0() pushes null) so the GC -// doesn't get confused. Both the vm_push0(), and the pawA_new calls -// might fail and cause an error to be thrown, so we have to be careful -// not to leave a junk value on top of the stack. -#define vm_array_init(pa, pv) \ - pv = vm_push0(); \ - pa = pawA_new(P); \ +// Requires a placeholder slot (the vm_push0() pushes an empty slot) so +// the GC doesn't get confused. Both the vm_push0(), and the pawA_new calls +// might fail and cause an error to be thrown, so we have to be careful not +// to leave a junk value on top of the stack. +#define vm_vector_init(pa, pv) \ + pv = vm_push0(); \ + pa = pawA_new(P); \ v_set_object(pv, pa); #define vm_map_init(pm, pv) \ @@ -333,21 +332,20 @@ void pawR_setattr(paw_Env *P, int index) vm_pop(2); } -void pawR_setitem(paw_Env *P, int ttarget, int tindex) +void pawR_setitem(paw_Env *P, paw_Type ttarget) { -// // TODO: Don't need index type. -// const Value val = *vm_peek(0); -// const Value key = *vm_peek(1); -// const Value obj = *vm_peek(2); -// if (ttarget == PAW_TARRAY) { -// const paw_Int idx = v_int(key); -// Value *slot = pawA_get(P, v_array(obj), idx); -// *slot = val; -// } else { -// paw_assert(ttarget == PAW_TMAP); -// pawH_insert(P, v_map(obj), key, val); -// } -// vm_pop(3); + const Value val = *vm_peek(0); + const Value key = *vm_peek(1); + const Value obj = *vm_peek(2); + if (ttarget == PAW_TVECTOR) { + const paw_Int idx = v_int(key); + Value *slot = pawA_get(P, v_vector(obj), idx); + *slot = val; + } else { + paw_assert(ttarget == PAW_TMAP); + pawH_insert(P, v_map(obj), key, val); + } + vm_pop(3); } void pawR_init(paw_Env *P) @@ -396,8 +394,8 @@ static paw_Bool forin_init(paw_Env *P, paw_Type t) { // const Value v = *vm_peek(0); // paw_Int itr = PAW_ITER_INIT; -// if (t == PAW_TARRAY) { -// Array *arr = v_array(v); +// if (t == PAW_TVECTOR) { +// Vector *arr = v_vector(v); // if (pawA_iter(arr, &itr)) { // vm_pushi(itr); // vm_pushv(arr->begin[itr]); @@ -419,8 +417,8 @@ static paw_Bool forin(paw_Env *P, paw_Type t) { // const Value obj = *vm_peek(1); // const Value itr = *vm_peek(0); -// if (t == PAW_TARRAY) { -// Array *arr = v_array(obj); +// if (t == PAW_TVECTOR) { +// Vector *arr = v_vector(obj); // paw_Int i = v_int(itr); // if (pawA_iter(arr, &i)) { // v_set_int(vm_peek(0), i); @@ -477,18 +475,18 @@ static void eq_ne(paw_Env *P, BinaryOp binop, paw_Type t, Value x, Value y) paw_Bool result; const paw_Bool bt = binop == BINARY_EQ; const paw_Bool bf = binop != BINARY_EQ; -// if (t == PAW_TARRAY) { -// const Array *lhs = v_array(x); -// const Array *rhs = v_array(y); -// result = pawA_equals(P, lhs, rhs); -//// } else if (t == PAW_TMAP) { -//// Map *lhs = v_map(x); -//// Map *rhs = v_map(y); -//// result = pawH_equals(P, lhs, rhs); -// } else { + if (t == PAW_TVECTOR) { + const Vector *lhs = v_vector(x); + const Vector *rhs = v_vector(y); + result = pawA_equals(P, lhs, rhs); + } else if (t == PAW_TMAP) { + Map *lhs = v_map(x); + Map *rhs = v_map(y); + result = pawH_equals(P, lhs, rhs); + } else { // Fall back to comparing the value representation. result = x.u == y.u; -// } + } v_set_bool(vm_peek(1), result ? bt : bf); vm_pop(1); } @@ -618,17 +616,16 @@ static void float_binop(paw_Env *P, BinaryOp binop, paw_Float x, paw_Float y) static void other_binop(paw_Env *P, BinaryOp binop, paw_Type t, Value x, Value y) { if (binop == BINARY_IN) { -// if (t_is_array(tag)) { -// v_set_bool(vm_peek(1), pawA_contains(P, v_array(y), x)); -//// } else { -//// paw_assert(t_is_map(tag)); -//// v_set_bool(vm_peek(1), pawH_contains(P, v_map(y), x)); -// } + if (t == PAW_TVECTOR) { + v_set_bool(vm_peek(1), pawA_contains(P, v_vector(y), x)); + } else { + paw_assert(t == PAW_TMAP); + v_set_bool(vm_peek(1), pawH_contains(P, v_map(y), x)); + } vm_pop(1); - } else if (t == PAW_TSTRING) { + } else { + paw_assert(t == PAW_TSTRING); string_binop(P, binop, x, y); -// } else if (t_is_array(tag)) { -// array_binop(P, binop, x, y); } } @@ -719,23 +716,23 @@ void pawR_getattr(paw_Env *P, int index) *vm_peek(0) = ins->attrs[1 + index]; } -static void getitem_list(paw_Env *P, Value obj, Value key) +static void getitem_vector(paw_Env *P, Value obj, Value key) { -// Array *a = v_array(obj); -// const paw_Int i = v_int(key); -// *vm_peek(1) = *pawA_get(P, a, i); -// vm_pop(1); + Vector *a = v_vector(obj); + const paw_Int i = v_int(key); + *vm_peek(1) = *pawA_get(P, a, i); + vm_pop(1); } static int getitem_map(paw_Env *P, Value obj, Value key) { -// const Value *pv = pawH_get(P, v_map(obj), key); -// if (pv) { -// *vm_peek(1) = *pv; -// vm_pop(1); -// return 0; -// } -// return -1; + const Value *pv = pawH_get(P, v_map(obj), key); + if (pv) { + *vm_peek(1) = *pv; + vm_pop(1); + return 0; + } + return -1; } static void getitem_string(paw_Env *P, Value obj, Value key) @@ -749,133 +746,51 @@ static void getitem_string(paw_Env *P, Value obj, Value key) vm_pop(1); } -int pawR_getitem(paw_Env *P, int ttarget, int tindex) -{ -// const Value obj = *vm_peek(1); -// const Value key = *vm_peek(0); -// if (ttarget == PAW_TARRAY) { -// getitem_list(P, obj, key); -// } else if (ttarget == PAW_TMAP) { -// if (getitem_map(P, obj, key)) { -// return -1; -// } -// } else if (ttarget == PAW_TSTRING) { -// getitem_string(P, obj, key); -// } -// return 0; -} - -// NOTE: Only called for range indexing on arrays and strings, where the bounds are -// always of type int. -static void cannonicalize_slice(size_t len, Value begin, Value end, paw_Int *bout, paw_Int *eout, paw_Int *nout) -{ - // TODO: broken now... int value cannot be null -// const paw_Int ibegin = v_is_null(begin) -// ? 0 // null acts like 0 -// : pawA_abs_index(v_int(begin), len); -// const paw_Int iend = v_is_null(end) -// ? paw_cast_int(len) // null acts like #a -// : pawA_abs_index(v_int(end), len); -// // clamp to sequence bounds -// *bout = paw_min(paw_max(ibegin, 0), paw_cast_int(len)); -// *eout = paw_min(paw_max(iend, 0), paw_cast_int(len)); -// *nout = paw_max(0, *eout - *bout); -} - -void pawR_getslice(paw_Env *P, int ttarget) +int pawR_getitem(paw_Env *P, paw_Type ttarget) { -// const Value obj = *vm_peek(2); -// const Value begin = *vm_peek(1); -// const Value end = *vm_peek(0); -// -// if (ttarget == PAW_TARRAY) { -// paw_Int i1, i2, n; -// const Array *src = v_array(obj); -// cannonicalize_slice(pawA_length(src), begin, end, &i1, &i2, &n); -// -// Value *pv; -// Array *dst; -// vm_array_init(dst, pv); -// pawA_resize(P, dst, cast_size(n)); -// for (paw_Int i = i1; i < i2; ++i) { -// dst->begin[i - i1] = src->begin[i]; -// } -// } else { -// paw_assert(ttarget == PAW_TSTRING); -// paw_Int i1, i2, n; -// const String *src = v_string(obj); -// cannonicalize_slice(src->length, begin, end, &i1, &i2, &n); -// -// Value *pv = vm_push0(); // placeholder -// String *dst = pawS_new_nstr(P, src->text + i1, cast_size(n)); -// v_set_object(pv, dst); -// } -// vm_shift(3); -} - -void pawR_setslice(paw_Env *P, int ttarget) -{ -// const Value obj = *vm_peek(3); -// const Value begin = *vm_peek(2); -// const Value end = *vm_peek(1); -// const Value val = *vm_peek(0); -// paw_assert(ttarget == PAW_TARRAY); -// -// paw_Int i1, i2, replace; -// // If 'a == b', then we must be executing something like 'a[i:j] = a'. -// // This will work, as long as memmove is used (making room for '#a' items, -// // so the first memmove will never overwrite items we still need). -// Array *a = v_array(obj); -// const Array *b = v_array(val); -// const size_t alen = pawA_length(a); -// const size_t blen = pawA_length(b); -// cannonicalize_slice(alen, begin, end, &i1, &i2, &replace); -// -// // Resize 'a' to the final length, preserving items after the region -// // of items being replaced. -// if (cast_size(replace) > blen) { -// memmove(a->begin + i1 + blen, a->begin + i1 + replace, -// (alen - cast_size(i1 + replace)) * sizeof(a->begin[0])); -// } -// const paw_Int length = i1 + paw_cast_int(alen + blen) - i2; -// pawA_resize(P, a, cast_size(length)); // a[:i1] + b + a[i2:] -// if (cast_size(replace) < blen) { -// memmove(a->begin + i1 + blen, a->begin + i2, -// (alen - cast_size(i2)) * sizeof(a->begin[0])); -// } -// memmove(a->begin + i1, b->begin, blen * sizeof(a->begin[0])); -// vm_pop(4); + const Value obj = *vm_peek(1); + const Value key = *vm_peek(0); + if (ttarget == PAW_TVECTOR) { + getitem_vector(P, obj, key); + } else if (ttarget == PAW_TMAP) { + if (getitem_map(P, obj, key)) { + return -1; + } + } else if (ttarget == PAW_TSTRING) { + getitem_string(P, obj, key); + } + return 0; } -void pawR_literal_array(paw_Env *P, int n) +void pawR_literal_vector(paw_Env *P, int n) { -// Array *a; -// StackPtr sp; -// vm_array_init(a, sp); -// if (n > 0) { -// pawA_resize(P, a, cast_size(n)); -// Value *pv = a->end; -// do { -// *--pv = *--sp; -// } while (pv != a->begin); -// // Replace contents with array itself. -// vm_shift(n); -// } + Vector *v; + StackPtr sp; + vm_vector_init(v, sp); + if (n > 0) { + pawA_resize(P, v, cast_size(n)); + Value *pv = v->end; + do { + *--pv = *--sp; + } while (pv != v->begin); + // Replace contents with vector itself. + vm_shift(n); + } } void pawR_literal_map(paw_Env *P, int n) { -// Map *m; -// StackPtr sp; -// vm_map_init(m, sp); -// if (n > 0) { -// for (int i = 0; i < n; ++i) { -// const Value value = *--sp; -// pawH_insert(P, m, *--sp, value); -// } -// // Replace contents with map itself. -// vm_shift(2 * n); -// } + Map *m; + StackPtr sp; + vm_map_init(m, sp); + if (n > 0) { + for (int i = 0; i < n; ++i) { + const Value value = *--sp; + pawH_insert(P, m, *--sp, value); + } + // Replace contents with map itself. + vm_shift(2 * n); + } } // TODO: 'null' -> Option[T]::None @@ -898,29 +813,21 @@ static paw_Bool should_jump_false(paw_Env *P) return !v_true(*vm_peek(0)); } -#include "debug.h" void pawR_execute(paw_Env *P, CallFrame *cf) { Closure *fn; const OpCode *pc; const Value *K; Struct **C; - Type **T; top: pc = cf->pc; fn = cf->fn; K = fn->p->k; C = fn->p->c; - T = P->mod->types; for (;;) { const OpCode opcode = *pc++; - - printf("n = %d, ",(int)(P->top.p-P->stack.p)); - paw_dump_opcode(opcode); - // paw_dump_stack(P); - vm_switch(get_OP(opcode)) { vm_case(POP) : @@ -965,19 +872,19 @@ void pawR_execute(paw_Env *P, CallFrame *cf) pawR_binop(P, get_A(opcode), get_B(opcode)); } -// vm_case(NEWARRAY) : -// { -// vm_protect(); -// pawR_literal_array(P, get_U(opcode)); -// check_gc(P); -// } -// -// vm_case(NEWMAP) : -// { -// vm_protect(); -// pawR_literal_map(P, get_U(opcode)); -// check_gc(P); -// } + vm_case(NEWVECTOR) : + { + vm_protect(); + pawR_literal_vector(P, get_U(opcode)); + check_gc(P); + } + + vm_case(NEWMAP) : + { + vm_protect(); + pawR_literal_map(P, get_U(opcode)); + check_gc(P); + } vm_case(CASTBOOL) : { @@ -1006,7 +913,7 @@ void pawR_execute(paw_Env *P, CallFrame *cf) check_gc(P); } - vm_case(INITATTR) : + vm_case(INITFIELD) : { vm_protect(); const int u = get_U(opcode); @@ -1070,27 +977,15 @@ void pawR_execute(paw_Env *P, CallFrame *cf) vm_case(GETITEM) : { vm_protect(); - if (pawR_getitem(P, get_A(opcode), get_B(opcode))) { - pawH_key_error(P, *vm_peek(0)); + if (pawR_getitem(P, get_A(opcode))) { + pawH_key_error(P, *vm_peek(0), PAW_TSTRING); // TODO: lookup key type } } vm_case(SETITEM) : { vm_protect(); - pawR_setitem(P, get_A(opcode), get_B(opcode)); - } - - vm_case(GETSLICE) : - { - vm_protect(); - pawR_getslice(P, get_A(opcode)); - } - - vm_case(SETSLICE) : - { - vm_protect(); - pawR_setslice(P, get_A(opcode)); + pawR_setitem(P, get_U(opcode)); } vm_case(CLOSE) : @@ -1158,15 +1053,15 @@ void pawR_execute(paw_Env *P, CallFrame *cf) // const int nactual = vm_argc(); // const int nextra = nactual - nexpect; // Value *pv; -// Array *argv; -// vm_array_init(argv, pv); +// Vector *argv; +// vm_vector_init(argv, pv); // if (nextra) { // pawA_resize(P, argv, cast_size(nextra)); // StackPtr argv0 = cf->base.p + 1 + nexpect; // for (int i = 0; i < nextra; ++i) { // argv->begin[i] = argv0[i]; // } -// // replace first variadic parameter with 'argv' array +// // replace first variadic parameter with 'argv' vector // vm_shift(nextra); // } // check_gc(P); diff --git a/src/rt.h b/src/rt.h index 3f26036..f7dcab4 100644 --- a/src/rt.h +++ b/src/rt.h @@ -22,9 +22,9 @@ void pawR_attr_error(paw_Env *P, Value attr); void pawR_name_error(paw_Env *P, Value name); void pawR_getattr(paw_Env *P, int index); -int pawR_getitem(paw_Env *P, int ttarget, int tindex); +int pawR_getitem(paw_Env *P, paw_Type ttarget); void pawR_setattr(paw_Env *P, int index); -void pawR_setitem(paw_Env *P, int ttarget, int tslice); +void pawR_setitem(paw_Env *P, paw_Type ttarget); int pawR_getattr_raw(paw_Env *P, paw_Bool fallback); void pawR_setattr_raw(paw_Env *P); @@ -34,15 +34,15 @@ void pawR_read_global(paw_Env *P, int g); void pawR_write_global(paw_Env *P, int g); void pawR_execute(paw_Env *P, CallFrame *cf); -void pawR_literal_array(paw_Env *P, int n); +void pawR_literal_vector(paw_Env *P, int n); void pawR_literal_map(paw_Env *P, int n); void pawR_close_upvalues(paw_Env *P, const StackPtr top); -int pawR_array_insert(paw_Env *P); -int pawR_array_push(paw_Env *P); -int pawR_array_pop(paw_Env *P); -int pawR_array_clone(paw_Env *P); +int pawR_vector_insert(paw_Env *P); +int pawR_vector_push(paw_Env *P); +int pawR_vector_pop(paw_Env *P); +int pawR_vector_clone(paw_Env *P); int pawR_map_erase(paw_Env *P); int pawR_map_clone(paw_Env *P); int pawR_string_starts_with(paw_Env *P); diff --git a/src/value.c b/src/value.c index c92d3a3..1eff9e3 100644 --- a/src/value.c +++ b/src/value.c @@ -3,7 +3,6 @@ // LICENSE.md. See AUTHORS.md for a list of contributor names. #include "prefix.h" -#include "array.h" #include "gc_aux.h" #include "map.h" #include "mem.h" @@ -12,6 +11,7 @@ #include "type.h" #include "util.h" #include "value.h" +#include "vector.h" #include #include #include @@ -72,6 +72,30 @@ const char *pawV_to_string(paw_Env *P, Value v, paw_Type type, size_t *nout) return s->text; } +static paw_Int string_to_int(paw_Env *P, String *s) +{ + return 0; // TODO +} + +static paw_Int float_to_int(paw_Env *P, paw_Float f) +{ + return 0; // TODO +} + +paw_Int pawV_to_int(paw_Env *P, Value v, paw_Type type) +{ + switch (type) { + case PAW_TBOOL: + return v_true(v) ? 1 : 0; + case PAW_TFLOAT: + return float_to_int(P, v_float(v)); + case PAW_TSTRING: + return string_to_int(P, v_string(v)); + default: + return v_int(v); + } +} + const char *pawV_name(ValueKind kind) { switch (kind) { @@ -172,17 +196,17 @@ void pawV_unlink_upvalue(UpValue *u) } } -Array_ *pawV_new_array(paw_Env *P, int nelems) +Tuple *pawV_new_tuple(paw_Env *P, int nelems) { - Array_ *arr = pawM_new_flex(P, Array_, cast_size(nelems), - sizeof(arr->elems[0])); - pawG_add_object(P, cast_object(arr), VARRAY); - return arr; + Tuple *t = pawM_new_flex(P, Tuple, cast_size(nelems), + sizeof(t->elems[0])); + pawG_add_object(P, cast_object(t), VARRAY); + return t; } -void pawV_free_array(paw_Env *P, Array_ *arr, int nelems) +void pawV_free_tuple(paw_Env *P, Tuple *t, int nelems) { - pawM_free_flex(P, arr, nelems, sizeof(arr->elems[0])); + pawM_free_flex(P, t, nelems, sizeof(t->elems[0])); } Closure *pawV_new_closure(paw_Env *P, int nup) @@ -204,7 +228,6 @@ Struct *pawV_new_struct(paw_Env *P, Value *pv) { Struct *struct_ = pawM_new(P, Struct); v_set_object(pv, struct_); // anchor - struct_->methods = pawA_new(P); pawG_add_object(P, cast_object(struct_), VSTRUCT); return struct_; } @@ -308,7 +331,7 @@ paw_Bool pawV_truthy(Value v, paw_Type type) case PAW_TSTRING: return pawS_length(v_string(v)) > 0; // case PAW_TARRAY: -// return pawA_length(v_array(v)) > 0; +// return pawA_length(v_vector(v)) > 0; // case PAW_TMAP: // return pawH_length(v_map(v)) > 0; default: @@ -367,7 +390,7 @@ paw_Int pawV_length(Value v, paw_Type type) len = pawS_length(v_string(v)); break; // case PAW_TARRAY: -// len = pawA_length(v_array(v)); +// len = pawA_length(v_vector(v)); // break; // case PAW_TMAP: // len = pawH_length(v_map(v)); @@ -479,14 +502,14 @@ int pawV_parse_float(paw_Env *P, const char *text) return 0; } -static inline Value *pawV_vec_get(paw_Env *P, Array *a, paw_Int index) +static inline Value *pawV_vec_get(paw_Env *P, Vector *a, paw_Int index) { const paw_Int abs = pawV_abs_index(index, cast_size(a->end - a->begin)); const size_t i = pawV_check_abs(P, abs, pawV_vec_length(a)); return &a->begin[i]; } -static inline paw_Bool pawV_vec_iter(const Array *a, paw_Int *itr) +static inline paw_Bool pawV_vec_iter(const Vector *a, paw_Int *itr) { return ++*itr < paw_cast_int(pawV_vec_length(a)); } @@ -497,12 +520,12 @@ void pawV_index_error(paw_Env *P, paw_Int index, size_t length) index, paw_cast_int(length)); } -static size_t array_capacity(const Array *a) +static size_t array_capacity(const Vector *a) { return cast_size(a->upper - a->begin); } -static void realloc_array(paw_Env *P, Array *a, size_t alloc0, size_t alloc) +static void realloc_array(paw_Env *P, Vector *a, size_t alloc0, size_t alloc) { const size_t end = pawV_vec_length(a); pawM_resize(P, a->begin, alloc0, alloc); @@ -511,7 +534,7 @@ static void realloc_array(paw_Env *P, Array *a, size_t alloc0, size_t alloc) check_gc(P); } -static void ensure_space(paw_Env *P, Array *a, size_t have, size_t want) +static void ensure_space(paw_Env *P, Vector *a, size_t have, size_t want) { if (want > PAW_SIZE_MAX / sizeof(Value)) { pawM_error(P); @@ -524,7 +547,7 @@ static void ensure_space(paw_Env *P, Array *a, size_t have, size_t want) realloc_array(P, a, have, n); } -static void reserve_extra(paw_Env *P, Array *a, size_t extra) +static void reserve_extra(paw_Env *P, Vector *a, size_t extra) { paw_assert(extra > 0); if (extra <= cast_size(a->upper - a->end)) { @@ -539,7 +562,7 @@ static void move_items(Value *src, ptrdiff_t shift, size_t count) memmove(src + shift, src, cast_size(count) * sizeof(src[0])); } -static void vec_reserve(paw_Env *P, Array *a, size_t want) +static void vec_reserve(paw_Env *P, Vector *a, size_t want) { const size_t have = array_capacity(a); if (want <= have) { @@ -548,13 +571,13 @@ static void vec_reserve(paw_Env *P, Array *a, size_t want) ensure_space(P, a, have, want); } -void pawV_vec_push(paw_Env *P, Array *a, Value v) +void pawV_vec_push(paw_Env *P, Vector *a, Value v) { reserve_extra(P, a, 1); *a->end++ = v; } -void pawV_vec_resize(paw_Env *P, Array *a, size_t length) +void pawV_vec_resize(paw_Env *P, Vector *a, size_t length) { const size_t n = pawV_vec_length(a); if (length > n) { @@ -567,7 +590,7 @@ void pawV_vec_resize(paw_Env *P, Array *a, size_t length) a->end = a->begin + length; } -void pawV_vec_insert(paw_Env *P, Array *a, paw_Int index, Value v) +void pawV_vec_insert(paw_Env *P, Vector *a, paw_Int index, Value v) { // Clamp to the vector bounds. const size_t len = pawV_vec_length(a); @@ -582,7 +605,7 @@ void pawV_vec_insert(paw_Env *P, Array *a, paw_Int index, Value v) ++a->end; } -void pawV_vec_pop(paw_Env *P, Array *a, paw_Int index) +void pawV_vec_pop(paw_Env *P, Vector *a, paw_Int index) { const size_t len = pawV_vec_length(a); const paw_Int fixed = pawV_abs_index(index, len); @@ -594,22 +617,22 @@ void pawV_vec_pop(paw_Env *P, Array *a, paw_Int index) --a->end; } -Array *pawV_vec_new(paw_Env *P) +Vector *pawV_vec_new(paw_Env *P) { - Array *a = pawM_new(P, Array); + Vector *a = pawM_new(P, Vector); pawG_add_object(P, cast_object(a), VARRAY); return a; } -void pawV_vec_free(paw_Env *P, Array *a) +void pawV_vec_free(paw_Env *P, Vector *a) { pawM_free_vec(P, a->begin, array_capacity(a)); pawM_free(P, a); } -Array *pawV_vec_clone(paw_Env *P, StackPtr sp, const Array *a) +Vector *pawV_vec_clone(paw_Env *P, StackPtr sp, const Vector *a) { - Array *a2 = pawV_vec_new(P); + Vector *a2 = pawV_vec_new(P); v_set_object(sp, a2); // anchor if (pawV_vec_length(a)) { pawV_vec_resize(P, a2, pawV_vec_length(a)); @@ -634,7 +657,7 @@ static paw_Bool elems_equal(paw_Env *P, Value x, Value y) return b; } -paw_Bool pawV_vec_equals(paw_Env *P, const Array *lhs, const Array *rhs) +paw_Bool pawV_vec_equals(paw_Env *P, const Vector *lhs, const Vector *rhs) { const size_t len = pawV_vec_length(lhs); if (len != pawV_vec_length(rhs)) { @@ -648,7 +671,7 @@ paw_Bool pawV_vec_equals(paw_Env *P, const Array *lhs, const Array *rhs) return PAW_TRUE; } -paw_Bool pawV_vec_contains(paw_Env *P, const Array *a, const Value v) +paw_Bool pawV_vec_contains(paw_Env *P, const Vector *a, const Value v) { for (size_t i = 0; i < pawV_vec_length(a); ++i) { if (elems_equal(P, v, a->begin[i])) { diff --git a/src/value.h b/src/value.h index 525d59c..24b75f3 100644 --- a/src/value.h +++ b/src/value.h @@ -23,6 +23,8 @@ #define v_closure(v) (o_closure(v_object(v))) #define v_upvalue(v) (o_upvalue(v_object(v))) #define v_string(v) (o_string(v_object(v))) +#define v_map(v) (o_map(v_object(v))) +#define v_vector(v) (o_vector(v_object(v))) #define v_text(v) (v_string(v)->text) #define v_instance(v) (o_instance(v_object(v))) #define v_struct(v) (o_struct(v_object(v))) @@ -41,6 +43,8 @@ #define o_is_proto(o) (o_kind(o) == VPROTO) #define o_is_closure(o) (o_kind(o) == VCLOSURE) #define o_is_upvalue(o) (o_kind(o) == VUPVALUE) +#define o_is_map(o) (o_kind(o) == VMAP) +#define o_is_vector(o) (o_kind(o) == VVECTOR) #define o_is_instance(o) (o_kind(o) == VINSTANCE) #define o_is_struct(o) (o_kind(o) == VSTRUCT) #define o_is_method(o) (o_kind(o) == VMETHOD) @@ -51,6 +55,8 @@ #define o_proto(o) check_exp(o_is_proto(o), (Proto *)(o)) #define o_closure(o) check_exp(o_is_closure(o), (Closure *)(o)) #define o_upvalue(o) check_exp(o_is_upvalue(o), (UpValue *)(o)) +#define o_map(o) check_exp(o_is_map(o), (Map *)(o)) +#define o_vector(o) check_exp(o_is_vector(o), (Vector *)(o)) #define o_instance(o) check_exp(o_is_instance(o), (Instance *)(o)) #define o_struct(o) check_exp(o_is_struct(o), (Struct *)(o)) #define o_method(o) check_exp(o_is_method(o), (Method *)(o)) @@ -94,6 +100,7 @@ typedef enum ValueKind { VSTRING, VARRAY, VMAP, + VVECTOR, VSTRUCT, VINSTANCE, VMETHOD, @@ -170,6 +177,7 @@ typedef struct String { } String; const char *pawV_to_string(paw_Env *P, Value v, paw_Type type, size_t *nout); +paw_Int pawV_to_int(paw_Env *P, Value v, paw_Type type); typedef struct VarDesc { String *name; @@ -253,43 +261,52 @@ typedef struct Native { GC_HEADER; uint16_t nup; paw_Function func; - UpValue *up[]; + Value up[]; } Native; Native *pawV_new_native(paw_Env *P, paw_Function func, int nup); -typedef struct Array_ { +typedef struct Tuple { GC_HEADER; Value elems[]; -} Array_; +} Tuple; -Array_ *pawV_new_array(paw_Env *P, int nelems); +Tuple *pawV_new_tuple(paw_Env *P, int nelems); -typedef struct Array { // TODO: Call this Vec +typedef struct Vector { // TODO: Call this Vec GC_HEADER; Value *begin; Value *end; Value *upper; -} Array; - -Array *pawV_vec_new(paw_Env *P); -void pawV_vec_free(paw_Env *P, Array *a); -paw_Bool pawV_vec_equals(paw_Env *P, const Array *lhs, const Array *rhs); -paw_Bool pawV_vec_contains(paw_Env *P, const Array *a, Value v); -void pawV_vec_resize(paw_Env *P, Array *a, size_t length); -void pawV_vec_insert(paw_Env *P, Array *a, paw_Int index, Value v); -void pawV_vec_push(paw_Env *P, Array *a, Value v); -void pawV_vec_pop(paw_Env *P, Array *a, paw_Int index); - -static inline size_t pawV_vec_length(const Array *a) +} Vector; + +Vector *pawV_vec_new(paw_Env *P); +void pawV_vec_free(paw_Env *P, Vector *a); +paw_Bool pawV_vec_equals(paw_Env *P, const Vector *lhs, const Vector *rhs); +paw_Bool pawV_vec_contains(paw_Env *P, const Vector *a, Value v); +void pawV_vec_resize(paw_Env *P, Vector *a, size_t length); +void pawV_vec_insert(paw_Env *P, Vector *a, paw_Int index, Value v); +void pawV_vec_push(paw_Env *P, Vector *a, Value v); +void pawV_vec_pop(paw_Env *P, Vector *a, paw_Int index); + +static inline size_t pawV_vec_length(const Vector *a) { return cast_size(a->end - a->begin); } +typedef enum MapState { + MAP_ITEM_VACANT, + MAP_ITEM_ERASED, + MAP_ITEM_OCCUPIED, +} MapState; + +typedef struct MapMeta { + MapState state: 2; +} MapMeta; + typedef struct Map { GC_HEADER; - Value *keys; - Value *values; + void *data; size_t length; size_t capacity; } Map; @@ -297,9 +314,7 @@ typedef struct Map { typedef struct Struct { GC_HEADER; // common members for GC paw_Type type; // index in module type list - VarDesc *field_info; // RTTI for fields - VarDesc *method_info; // RTTI for methods - Array *methods; // functions with 'self' (Array[Closure]) + VarDesc *fields; // RTTI for fields } Struct; Struct *pawV_new_struct(paw_Env *P, Value *pv); @@ -309,12 +324,11 @@ void pawV_free_struct(paw_Env *P, Struct *struct_); typedef struct Instance { GC_HEADER; // common members for GC Value attrs[]; // fixed array of attributes - //Value fields[]; // data fields, inc. superstruct + //Value fields[]; // data fields } Instance; Instance *pawV_new_instance(paw_Env *P, int nfields); void pawV_free_instance(paw_Env *P, Instance *ins, int nfields); -//Value *pawV_find_attr(Value *attrs, String *name, Type *type); // Method bound to an instance typedef struct Method { diff --git a/src/array.c b/src/vector.c similarity index 75% rename from src/array.c rename to src/vector.c index f741149..c4b7568 100644 --- a/src/array.c +++ b/src/vector.c @@ -1,26 +1,25 @@ // 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 "array.h" #include "gc_aux.h" #include "mem.h" -//#include "rt.h" +#include "rt.h" +#include "vector.h" #include "util.h" #include void pawA_index_error(paw_Env *P, paw_Int index, size_t length) { - paw_assert(0); - // pawR_error(P, PAW_EINDEX, "index %I is out of bounds for array of length %I", - // index, paw_cast_int(length) /* fits in paw_Int */); + pawR_error(P, PAW_EINDEX, "index %I is out of bounds for array of length %I", + index, paw_cast_int(length) /* fits in paw_Int */); } -static size_t array_capacity(const Array *a) +static size_t array_capacity(const Vector *a) { return cast_size(a->upper - a->begin); } -static void realloc_array(paw_Env *P, Array *a, size_t alloc0, size_t alloc) +static void realloc_array(paw_Env *P, Vector *a, size_t alloc0, size_t alloc) { const size_t end = pawA_length(a); pawM_resize(P, a->begin, alloc0, alloc); @@ -29,7 +28,7 @@ static void realloc_array(paw_Env *P, Array *a, size_t alloc0, size_t alloc) check_gc(P); } -static void ensure_space(paw_Env *P, Array *a, size_t have, size_t want) +static void ensure_space(paw_Env *P, Vector *a, size_t have, size_t want) { if (want > PAW_SIZE_MAX / sizeof(Value)) { pawM_error(P); @@ -42,7 +41,7 @@ static void ensure_space(paw_Env *P, Array *a, size_t have, size_t want) realloc_array(P, a, have, n); } -static void reserve_extra(paw_Env *P, Array *a, size_t extra) +static void reserve_extra(paw_Env *P, Vector *a, size_t extra) { paw_assert(extra > 0); if (extra <= cast_size(a->upper - a->end)) { @@ -57,7 +56,7 @@ static void move_items(Value *src, ptrdiff_t shift, size_t count) memmove(src + shift, src, cast_size(count) * sizeof(src[0])); } -void pawA_reserve(paw_Env *P, Array *a, size_t want) +void pawA_reserve(paw_Env *P, Vector *a, size_t want) { const size_t have = array_capacity(a); if (want <= have) { @@ -66,13 +65,13 @@ void pawA_reserve(paw_Env *P, Array *a, size_t want) ensure_space(P, a, have, want); } -void pawA_push(paw_Env *P, Array *a, Value v) +void pawA_push(paw_Env *P, Vector *a, Value v) { reserve_extra(P, a, 1); *a->end++ = v; } -void pawA_resize(paw_Env *P, Array *a, size_t length) +void pawA_resize(paw_Env *P, Vector *a, size_t length) { const size_t n = pawA_length(a); if (length > n) { @@ -85,7 +84,7 @@ void pawA_resize(paw_Env *P, Array *a, size_t length) a->end = a->begin + length; } -void pawA_insert(paw_Env *P, Array *a, paw_Int index, Value v) +void pawA_insert(paw_Env *P, Vector *a, paw_Int index, Value v) { // Clamp to the array bounds. const size_t len = pawA_length(a); @@ -100,7 +99,7 @@ void pawA_insert(paw_Env *P, Array *a, paw_Int index, Value v) ++a->end; } -void pawA_pop(paw_Env *P, Array *a, paw_Int index) +void pawA_pop(paw_Env *P, Vector *a, paw_Int index) { const size_t len = pawA_length(a); const paw_Int fixed = pawA_abs_index(index, len); @@ -112,22 +111,22 @@ void pawA_pop(paw_Env *P, Array *a, paw_Int index) --a->end; } -Array *pawA_new(paw_Env *P) +Vector *pawA_new(paw_Env *P) { - Array *a = pawM_new(P, Array); + Vector *a = pawM_new(P, Vector); pawG_add_object(P, cast_object(a), VARRAY); return a; } -void pawA_free(paw_Env *P, Array *a) +void pawA_free(paw_Env *P, Vector *a) { pawM_free_vec(P, a->begin, array_capacity(a)); pawM_free(P, a); } -Array *pawA_clone(paw_Env *P, StackPtr sp, const Array *a) +Vector *pawA_clone(paw_Env *P, StackPtr sp, const Vector *a) { - Array *a2 = pawA_new(P); + Vector *a2 = pawA_new(P); v_set_object(sp, a2); // anchor if (pawA_length(a)) { pawA_resize(P, a2, pawA_length(a)); @@ -152,7 +151,7 @@ static paw_Bool elems_equal(paw_Env *P, Value x, Value y) return b; } -paw_Bool pawA_equals(paw_Env *P, const Array *lhs, const Array *rhs) +paw_Bool pawA_equals(paw_Env *P, const Vector *lhs, const Vector *rhs) { const size_t len = pawA_length(lhs); if (len != pawA_length(rhs)) { @@ -166,7 +165,7 @@ paw_Bool pawA_equals(paw_Env *P, const Array *lhs, const Array *rhs) return PAW_TRUE; } -paw_Bool pawA_contains(paw_Env *P, const Array *a, const Value v) +paw_Bool pawA_contains(paw_Env *P, const Vector *a, const Value v) { for (size_t i = 0; i < pawA_length(a); ++i) { if (elems_equal(P, v, a->begin[i])) { diff --git a/src/array.h b/src/vector.h similarity index 58% rename from src/array.h rename to src/vector.h index e4b291d..a4a1490 100644 --- a/src/array.h +++ b/src/vector.h @@ -7,16 +7,16 @@ #include "paw.h" #include "value.h" -Array *pawA_new(paw_Env *P); -void pawA_free(paw_Env *P, Array *a); -paw_Bool pawA_equals(paw_Env *P, const Array *lhs, const Array *rhs); -paw_Bool pawA_contains(paw_Env *P, const Array *a, Value v); -void pawA_reserve(paw_Env *P, Array *a, size_t capacity); -void pawA_resize(paw_Env *P, Array *a, size_t length); -void pawA_insert(paw_Env *P, Array *a, paw_Int index, Value v); -void pawA_push(paw_Env *P, Array *a, Value v); -void pawA_pop(paw_Env *P, Array *a, paw_Int index); -Array *pawA_clone(paw_Env *P, StackPtr sp, const Array *a); +Vector *pawA_new(paw_Env *P); +void pawA_free(paw_Env *P, Vector *a); +paw_Bool pawA_equals(paw_Env *P, const Vector *lhs, const Vector *rhs); +paw_Bool pawA_contains(paw_Env *P, const Vector *a, Value v); +void pawA_reserve(paw_Env *P, Vector *a, size_t capacity); +void pawA_resize(paw_Env *P, Vector *a, size_t length); +void pawA_insert(paw_Env *P, Vector *a, paw_Int index, Value v); +void pawA_push(paw_Env *P, Vector *a, Value v); +void pawA_pop(paw_Env *P, Vector *a, paw_Int index); +Vector *pawA_clone(paw_Env *P, StackPtr sp, const Vector *a); void pawA_index_error(paw_Env *P, paw_Int index, size_t length); static paw_Int pawA_abs_index(paw_Int index, size_t length) @@ -33,19 +33,19 @@ static inline size_t pawA_check_abs(paw_Env *P, paw_Int index, size_t length) return cast_size(index); } -static inline size_t pawA_length(const Array *a) +static inline size_t pawA_length(const Vector *a) { return cast_size(a->end - a->begin); } -static inline Value *pawA_get(paw_Env *P, Array *a, paw_Int index) +static inline Value *pawA_get(paw_Env *P, Vector *a, paw_Int index) { const paw_Int abs = pawA_abs_index(index, cast_size(a->end - a->begin)); const size_t i = pawA_check_abs(P, abs, pawA_length(a)); return &a->begin[i]; } -static inline paw_Bool pawA_iter(const Array *a, paw_Int *itr) +static inline paw_Bool pawA_iter(const Vector *a, paw_Int *itr) { return ++*itr < paw_cast_int(pawA_length(a)); } diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 51c911a..6de481a 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -20,12 +20,12 @@ function(test_script NAME ARGS) endfunction() build_test(test_impl) -build_test(test_api) +#build_test(test_api) TODO: Fix this test: use the new API build_test(test_rt) -build_test(test_oom) -build_test(test_so) -build_test(test_error) +#build_test(test_oom) +#build_test(test_so) +#build_test(test_error) -test_script(bubble "500") -test_script(binary_trees "5") -test_script(mandelbrot "-2.1,0.9,80,-1.3,1.3,24") +#test_script(bubble "500") +#test_script(binary_trees "5") +#test_script(mandelbrot "-2.1,0.9,80,-1.3,1.3,24") diff --git a/test/scripts/operator.paw b/test/scripts/operator.paw index d169abd..03fd253 100644 --- a/test/scripts/operator.paw +++ b/test/scripts/operator.paw @@ -1,30 +1,30 @@ --- operators.paw +// operator.paw { let s = '' s = s + 'abc' - s = s * 3 - assert(s == 'abcabcabc') + s = s + s + assert(s == 'abcabc') } --- Arithmetic operator precedence: +// Arithmetic operator precedence: { - assert(5 == 1 + 2 * 3 - 4 // 2) + assert(5 == 1 + 2 * 3 - 4 / 2) assert(8 == 1 << 2 * 2 >> 1) assert(-3 == 1 | ~2 ^ 3 & 4) - -- '==' and '!=' bind tighter than '&&' and '||' + // '==' and '!=' bind tighter than '&&' and '||' assert(1 == 1 && 2 == 2) - assert(!(1 == (1 && 2) == 2)) - -- Bitwise operators bind tighter than comparisons + // Bitwise operators bind tighter than comparisons assert(4 & 5 == 4) - assert(!(4 & int(5 == 4))) +// assert(!(4 & int(5 == 4))) +// TODO: int() conversion } --- Comparisons: +// Comparisons: { - fn test_cmp(a, b) { + fn test_icmp(a: int, b: int) { assert(a == a) assert(a != b) assert(a <= a) @@ -34,91 +34,47 @@ assert(a < b) assert(b > a) } + test_icmp(1, 2) + test_icmp(-1, 1) + test_icmp(123, 124) - test_cmp(1, 2) - test_cmp(-1, 1) - test_cmp(123, 124) - test_cmp('a', 'b') - test_cmp('ab', 'abc') - test_cmp('abc', 'abd') -} - --- Conversions: -{ - assert(int(true)) - assert(float(true)) - assert(int(1)) - assert(float(1)) - assert(int(1 << 50)) - assert(float(1 << 50)) -} - -{ - fn gencheck(op) { - let f = load('return fn(x, y) {return x' ++ op ++ 'y}') - return f() -- Generate function + fn test_scmp(a: string, b: string) { + assert(a == a) + assert(a != b) + assert(a <= a) + assert(a <= b) + assert(a >= a) + assert(b >= a) + assert(a < b) + assert(b > a) } - let add = gencheck('+') - assert(add(123, 321) == 444) - assert(add(123, 321.0) == 444.0) - assert(add(123.0, 321) == 444.0) - assert(add(123.0, 321.0) == 444.0) - - let sub = gencheck('-') - assert(sub(123, 321) == -198) - assert(sub(123, 321.0) == -198.0) - assert(sub(123.0, 321) == -198.0) - assert(sub(123.0, 321.0) == -198.0) - - let div = gencheck('/') - assert(div(100, 10) == 10.0) - assert(div(100, 10.0) == 10.0) - assert(div(100.0, 10) == 10.0) - assert(div(100.0, 10.0) == 10.0) - - let idiv = gencheck('//') - assert(idiv(100, 10) == 10) - assert(idiv(100, 10.0) == 10) - assert(idiv(100.0, 10) == 10) - assert(idiv(100.0, 10.0) == 10) - - let mod = gencheck('%') - assert(mod(100, 3) == 1) - assert(mod(100, 3.0) == 1.0) - assert(mod(100.0, 3) == 1.0) - assert(mod(100.0, 3.0) == 1.0) + test_scmp('a', 'b') + test_scmp('ab', 'abc') + test_scmp('abc', 'abd') } +// TODO +//// Conversions: +//{ +// assert(1 == int(true)) +// assert(1.0 == float(true)) +// assert(1 == int(1)) +// assert(1.0 == float(1)) +//} + { assert(0b0111 == (0b0110 | 0b0011)) assert(0b0010 == (0b0110 & 0b0011)) assert(0b0101 == (0b0110 ^ 0b0011)) } --- Null coalescing operator: -{ - assert(1 == (1 ?: null ?: null)) - assert(2 == (null ?: 2 ?: null)) - assert(3 == (null ?: null ?: 3)) - - assert(1 == (1 ?: null ?: 3)) - assert(1 == (1 ?: 2 ?: null)) - assert(2 == (null ?: 2 ?: 3)) - - assert(1 == (1 ?: null ?: null)) - assert(2 == (null ?: 2 ?: null)) - assert(3 == (null ?: null ?: 3)) - - assert(null == (null ?: null ?: null)) -} - --- Conditional expressions: +// Conditional expressions: { assert('true' == (true ?? 'true' :: 'false')) assert('false' == (false ?? 'true' :: 'false')) - assert('true' == (1 + 1 + 1 ?? 'true' :: 'false')) - assert('false' == (0 + 0 + 0 ?? 'true' :: 'false')) + assert('true' == (1 + 1 + 1 == 3 ?? 'true' :: 'false')) + assert('false' == (0 + 0 + 0 == 3 ?? 'true' :: 'false')) assert(42 == (true ?? 40 + 2 :: 0)) assert(0 == (false ?? 40 + 2 :: 0)) @@ -138,17 +94,7 @@ let x = true ?? 1 :: 0 assert(x == 1) - fn forward(a) { - return a - } - fn test(a) { - return forward(a)? ?? 'then' :: 'else' - } - assert(test(null) == null) - assert(test(0) == 'else') - assert(test(1) == 'then') - - fn test(v) { + fn test(v: int) -> int { return v == 1 ?? 10 :: v == 2 ?? 20 :: v == 3 ?? 30 :: 40 @@ -159,461 +105,3 @@ assert(30 == test(3)) assert(40 == test(4)) } - --- Test metamethods: -{ - let alt = '' -- For metamethods with no return - class MM { - __init() {self.x = ''} - __len() {return '__len'} - __neg() {return '__neg'} - __not() {return '__not'} - __bnot() {return '__bnot'} - __add(y) {return '__add'} - __sub(y) {return '__sub'} - __mul(y) {return '__mul'} - __div(y) {return '__div'} - __idiv(y) {return '__idiv'} - __mod(y) {return '__mod'} - __pow(y) {return '__pow'} - __concat(y) {return '__concat'} - __bxor(y) {return '__bxor'} - __band(y) {return '__band'} - __bor(y) {return '__bor'} - __shl(y) {return '__shl'} - __shr(y) {return '__shr'} - __eq(y) {return '__eq'} - __lt(y) {return '__lt'} - __le(y) {return '__le'} - __contains(x) {return '__contains'} - __getattr(x) {return '__getattr'} - __setattr(x, y) {alt = '__setattr'} - __getitem(x) {return '__getitem'} - __setitem(x, y) {alt = '__setitem'} - __radd(x) {return '__radd'} - __rsub(x) {return '__rsub'} - __rmul(x) {return '__rmul'} - __rdiv(x) {return '__rdiv'} - __ridiv(x) {return '__ridiv'} - __rmod(x) {return '__rmod'} - __rpow(x) {return '__rpow'} - __rconcat(x) {return '__rconcat'} - __rbxor(x) {return '__rbxor'} - __rband(x) {return '__rband'} - __rbor(x) {return '__rbor'} - __rshl(x) {return '__rshl'} - __rshr(x) {return '__rshr'} - } - - -- Precedence helper - fn check(x, y) { - assert(x == y) - } - let v = 123 - let mm = MM() - - check(#mm, '__len') - check(-mm, '__neg') - check(!mm, '__not') - check(~mm, '__bnot') - - check(mm + v, '__add') - check(mm - v, '__sub') - check(mm * v, '__mul') - check(mm / v, '__div') - check(mm // v, '__idiv') - check(mm % v, '__mod') --- check(mm**v, '__pow') - check(mm ++ v, '__concat') - check(mm ^ v, '__bxor') - check(mm & v, '__band') - check(mm | v, '__bor') - check(mm << v, '__shl') - check(mm >> v, '__shr') - check(mm == v, '__eq') - check(mm < v, '__lt') - check(mm <= v, '__le') - check(v in mm, '__contains') - check(mm.attr, '__getattr') - check(mm[0], '__getitem') - check(v + mm, '__radd') - check(v - mm, '__rsub') - check(v * mm, '__rmul') - check(v / mm, '__rdiv') - check(v // mm, '__ridiv') - check(v % mm, '__rmod') --- check(v**mm, '__rpow') - check(v ++ mm, '__rconcat') - check(v ^ mm, '__rbxor') - check(v & mm, '__rband') - check(v | mm, '__rbor') - check(v << mm, '__rshl') - check(v >> mm, '__rshr') - - -- Assignment is not an expression, so the return value is ignored on - -- these 2 metamethods. - mm.attr = 'attr' - check(alt, '__setattr') - - mm[0] = 'item' - check(alt, '__setitem') - - -- __*item() can be called with any type of key. - mm['key'] = 'item' - check(mm[true], '__getitem') - mm[1.23] = 'item' - check(mm[null], '__getitem') -} - -{ - fn test(n) { - -- Use up a lot of stack - let a = [1, 2, 3, 4, 5, 6, 7, 8, - 1, 2, 3, 4, 5, 6, 7, 8, - 1, 2, 3, 4, 5, 6, 7, 8, - 1, 2, 3, 4, 5, 6, 7, 8, - 1, 2, 3, 4, 5, 6, 7, 8, - 1, 2, 3, 4, 5, 6, 7, 8, - 1, 2, 3, 4, 5, 6, 7, 8, - 1, 2, 3, 4, 5, 6, 7, 8] - if n > 0 { - test(n - 1) - } - } - class Meta { - __len() { - test(100) - return 0 - } - } - - -- Attempt to provoke a stack reallocation in a metamethod - let m = Meta() - assert(#m == 0) -} - -{ - class Set { - __init(set) { - let s = {} - for v in set { - s[v] = null - } - self.set = s - } - - __add(y) { - let s = self.set.clone() - s[y] = null - return Set(s) - } - __radd(x) { - return self.__add(x) - } - - __sub(y) { - let s = self.set.clone() - s.erase(y) - return Set(s) - } - - __contains(v) { - return v in self.set - } - } - - fn check(s, a) { - for v in a { - assert(v in s) - } - for k in s.set { - assert(k in a) - } - } - - let s = Set([]) - check(s, []) - - s = s + 2 - check(s, [2]) - - s = 1 + s - check(s, [1, 2]) - - s = s - 2 - 1 - check(s, []) -} - -{ - -- Simulates an integer - class Int { - __init(v) {self.v = v} - __neg() {return Int(-self.v)} - __not() {return Int(~self.v)} - __bnot() {return Int(~self.v)} - __add(y) {return Int(self.v + y.v)} - __sub(y) {return Int(self.v - y.v)} - __mul(y) {return Int(self.v * y.v)} - __div(y) {return Int(self.v / y.v)} - __idiv(y) {return Int(self.v // y.v)} - __mod(y) {return Int(self.v % y.v)} --- __pow(y) {return Int(self.v**y.v)} - __concat(y) {return Int(self.v ++ y.v)} - __bxor(y) {return Int(self.v ^ y.v)} - __band(y) {return Int(self.v & y.v)} - __bor(y) {return Int(self.v | y.v)} - __shl(y) {return Int(self.v << y.v)} - __shr(y) {return Int(self.v >> y.v)} - __eq(y) {return self.v == y.v} - __lt(y) {return self.v < y.v} - __le(y) {return self.v <= y.v} - __radd(x) {return Int(x.v + self.v)} - __rsub(x) {return Int(x.v - self.v)} - __rmul(x) {return Int(x.v * self.v)} - __rdiv(x) {return Int(x.v / self.v)} - __ridiv(x) {return Int(x.v // self.v)} - __rmod(x) {return Int(x.v % self.v)} --- __rpow(x) {return Int(x.v**self.v)} - __rconcat(x) {return Int(x.v ++ self.v)} - __rbxor(x) {return Int(x.v ^ self.v)} - __rband(x) {return Int(x.v & self.v)} - __rbor(x) {return Int(x.v | self.v)} - __rshl(x) {return Int(x.v << self.v)} - __rshr(x) {return Int(x.v >> self.v)} - } - fn check(lhs, rhs) { - assert(lhs.v == rhs) - } - - let x = Int(1) - let y = Int(2) - let z = Int(3) - check(x + y * z, 1 + 2 * 3) - check(Int(100) / x // y % z, 100 / 1 // 2 % 3) -} - -{ - class MM {__call() {return 0}} - let mm = MM() - assert(mm() == 0) - - class MM {__call(x) {return x}} - let mm = MM() - assert(mm(1) == 1) - - class MM {__call(x, y) {return y}} - let mm = MM() - assert(mm(1, 2) == 2) - - class MM {__call(x, y, z) {return z}} - let mm = MM() - assert(mm(1, 2, 3) == 3) - - class MM {__call(...) {return argv[-1]}} - let mm = MM() - assert(mm(1) == 1) - assert(mm(2) == 2) - assert(mm(3) == 3) - - class MM {__call(x, ...) {return x + argv[-1]}} - let mm = MM() - assert(mm(1, 2) == 3) - assert(mm(1, 2, 3) == 4) - assert(mm(1, 2, 3, 4) == 5) - - class MM {__call(x, y, ...) {return y + argv[-1]}} - let mm = MM() - assert(mm(1, 2, 3) == 5) - assert(mm(1, 2, 3, 4) == 6) - assert(mm(1, 2, 3, 4, 5) == 7) - - class MM {__call(x, y, z, ...) {return z + argv[-1]}} - let mm = MM() - assert(mm(1, 2, 3, 4) == 7) - assert(mm(1, 2, 3, 4, 5) == 8) - assert(mm(1, 2, 3, 4, 5, 6) == 9) -} - -{ - class Test { - __init(v) { - self.value = v - } - - __null() { - return self.value - ?? self - :: null - } - } - - fn test(v) { - let obj = Test(v) - let val = obj?.value - return 42 - } - assert(0 == test(0).value) - assert(42 == test(1)) - - assert(1 == (Test(0) ?: 1)) - assert(1 == (Test(1) ?: 2).value) -} - --- Test iterable instance: when a class has both __len and __getitem, --- it can be used in for...in loops. -{ - class Iterable { - __init(n) { - self.n = n - } - - __len() { - return self.n - } - - __getitem(index) { - return index - } - } - - for i in Iterable(0) { - assert(false) - } - - let scale = 25 - for n = 0,2 { - let count = 0 - let total = n * scale + 1 - let iter = Iterable(total) - for value in iter { - assert(value == count) - count = count + 1 - } - assert(count == total) - } -} - --- Short-circuit evaluation: -{ - let state = [0, 0, 0] - fn setup(i) { - return fn(n) { - assert(state[i] == 0) - state[i] = 1 - return n - } - } - fn check(a, b, c) { - assert(state[0] == a) - assert(state[1] == b) - assert(state[2] == c) - state = [0, 0, 0] - } - let f = setup(0) - let g = setup(1) - let h = setup(2) - - fn test(a, b, c) { - return f(a) && g(b) && h(c) - } - - assert(test(1, 1, 1)); check(1, 1, 1) - assert(!test(0, 1, 1)); check(1, 0, 0) - assert(!test(1, 0, 1)); check(1, 1, 0) - assert(!test(1, 1, 0)); check(1, 1, 1) - assert(!test(1, 0, 0)); check(1, 1, 0) - assert(!test(0, 1, 0)); check(1, 0, 0) - assert(!test(0, 0, 1)); check(1, 0, 0) - assert(!test(0, 0, 0)); check(1, 0, 0) - - fn test(a, b, c) { - return f(a) || g(b) && h(c) - } - - assert(test(1, 1, 1)); check(1, 0, 0) - assert(test(0, 1, 1)); check(1, 1, 1) - assert(test(1, 0, 1)); check(1, 0, 0) - assert(test(1, 1, 0)); check(1, 0, 0) - assert(test(1, 0, 0)); check(1, 0, 0) - assert(!test(0, 1, 0)); check(1, 1, 1) - assert(!test(0, 0, 1)); check(1, 1, 0) - assert(!test(0, 0, 0)); check(1, 1, 0) - - fn test(a, b, c) { - return f(a) && g(b) || h(c) - } - - assert(test(1, 1, 1)); check(1, 1, 0) - assert(test(0, 1, 1)); check(1, 0, 1) - assert(test(1, 0, 1)); check(1, 1, 1) - assert(test(1, 1, 0)); check(1, 1, 0) - assert(!test(1, 0, 0)); check(1, 1, 1) - assert(!test(0, 1, 0)); check(1, 0, 1) - assert(test(0, 0, 1)); check(1, 0, 1) - assert(!test(0, 0, 0)); check(1, 0, 1) - - fn test(a, b, c) { - return f(a) || g(b) || h(c) - } - - assert(test(1, 1, 1)); check(1, 0, 0) - assert(test(0, 1, 1)); check(1, 1, 0) - assert(test(1, 0, 1)); check(1, 0, 0) - assert(test(1, 1, 0)); check(1, 0, 0) - assert(test(1, 0, 0)); check(1, 0, 0) - assert(test(0, 1, 0)); check(1, 1, 0) - assert(test(0, 0, 1)); check(1, 1, 1) - assert(!test(0, 0, 0)); check(1, 1, 1) - - fn check(expr, answer) { - assert(expr == answer) - } - check(0 || 1, 1) - check(1 || 2, 1) - check(true && true, true) - check(true && false, false) - check('' && false, '') - check(false && '', false) - check(false && true, false) -} - -{ - fn test(_) { - fn check(answer, result) { - assert(answer == str(result)) - } - check('b', _('a') && _('b')) - check('', _('a') && _('')) - check('', _('') && _('b')) - check('', _('') && _('b')) - check('a', _('a') || _('b')) - check('a', _('a') || _('')) - check('b', _('') || _('b')) - check('', _('') || _('')) - - check('b', _('a') && _('b') || _('c')) - check('b', _('a') && _('b') || _('')) - check('c', _('a') && _('') || _('c')) - check('', _('a') && _('') || _('')) - check('c', _('') && _('b') || _('c')) - check('', _('') && _('b') || _('')) - check('c', _('') && _('') || _('c')) - check('', _('') && _('') || _('')) - } - - test(fn(v) {return v}) - - class A { - __init(v) { - self.v = v - } - __bool() { - -- NOTE: must return a boolean, - return self.v - } - __str() { - return self.v - } - } - test(A) -} diff --git a/test/test_api.c b/test/test_api.c index f23faca..5b5351d 100644 --- a/test/test_api.c +++ b/test/test_api.c @@ -18,7 +18,7 @@ static void check_error(paw_Env *P, int status) static paw_Env *start_test(void) { const char *source = - "let var = 'global' \n" + "let var = 'variable' \n" "let set \n" "let get \n" "{ \n" @@ -123,14 +123,14 @@ int main(void) check_error(P, status); paw_get_global(P, "var"); - str_equals(P, -1, "global"); + str_equals(P, -1, "variable"); paw_pop(P, 1); // Set global variable 'var', read it back paw_push_int(P, 123); paw_set_global(P, "var"); paw_get_global(P, "var"); - check(paw_is_integer(P, -1)); + check(paw_is_int(P, -1)); check(paw_int(P, -1) == 123); paw_pop(P, 1); diff --git a/test/test_impl.c b/test/test_impl.c index a497f9d..1142306 100644 --- a/test/test_impl.c +++ b/test/test_impl.c @@ -1,4 +1,4 @@ -#include "array.h" +#include "vector.h" #include "call.h" #include "map.h" #include "paw.h" @@ -11,128 +11,156 @@ #include #include #include +#include // Test the primitive value representations static void test_primitives(void) { - Value v; + Value v = {.i = -1}; - // VNULL - v_set_null(&v); - check(v_is_null(v)); + v_set_0(&v); + check(v.u == 0); - // VTRUE/FALSE v_set_bool(&v, PAW_TRUE); - check(v_type(v) == VTRUE); check(v_true(v)); - check(pawV_is_true(v)); - check(!pawV_is_false(v)); v_set_bool(&v, PAW_FALSE); - check(v_type(v) == VFALSE); check(!v_true(v)); - check(!pawV_is_true(v)); - check(pawV_is_false(v)); - check(!pawV_is_object(v)); - // pawV_is_float(v) v_set_float(&v, 0.0); - check(v_type(v) <= VNUMBER); check(v_float(v) == 0.0); - check(pawV_is_float(v)); v_set_float(&v, 12.3); - check(v_type(v) <= VNUMBER); check(v_float(v) == 12.3); - check(pawV_is_float(v)); v_set_float(&v, 12.3e123); - check(v_type(v) <= VNUMBER); check(v_float(v) == 12.3e123); - check(pawV_is_float(v)); - check(!pawV_is_object(v)); v_set_float(&v, INFINITY); - check(v_type(v) <= VNUMBER); check(!isfinite(v_float(v))); - check(pawV_is_float(v)); - check(!pawV_is_object(v)); v_set_float(&v, -INFINITY); - check(v_type(v) <= VNUMBER); check(!isfinite(v_float(v))); - check(pawV_is_float(v)); - check(!pawV_is_object(v)); v_set_float(&v, nan("")); - check(v_type(v) <= VNUMBER); check(isnan(v_float(v))); - check(pawV_is_float(v)); - check(!pawV_is_object(v)); v_set_float(&v, DBL_MAX); - check(v_type(v) <= VNUMBER); check(isfinite(v_float(v))); - check(pawV_is_float(v)); - check(!pawV_is_object(v)); v_set_float(&v, DBL_MIN); - check(v_type(v) <= VNUMBER); check(isfinite(v_float(v))); - check(pawV_is_float(v)); - check(!pawV_is_object(v)); - // pawV_is_int(v) v_set_int(&v, 0); - check(v_type(v) == VNUMBER); check(v_int(v) == 0); - check(pawV_is_int(v)); v_set_int(&v, 123); - check(v_type(v) == VNUMBER); check(v_int(v) == 123); - check(pawV_is_int(v)); v_set_int(&v, -123); - check(v_type(v) == VNUMBER); check(v_int(v) == -123); - check(pawV_is_int(v)); - v_set_int(&v, VINT_MAX); - check(v_type(v) == VNUMBER); - check(v_int(v) == VINT_MAX); - check(pawV_is_int(v)); - v_set_int(&v, VINT_MIN); - check(v_type(v) == VNUMBER); - check(v_int(v) == VINT_MIN); - check(pawV_is_int(v)); - check(!pawV_is_object(v)); + v_set_int(&v, PAW_INT_MAX); + check(v_int(v) == PAW_INT_MAX); + v_set_int(&v, PAW_INT_MIN); + check(v_int(v) == PAW_INT_MIN); } -static void test_objects(void) +#define N 500 + +static Map *map_new(paw_Env *P) { - Value v; - - // Make sure the value representation can store 48 bits of pointer info. - // We actually have to shift pointers to the right by 1 bit for this to - // work, so the fake pointer must be aligned to at least 2. - // 0b101010101010101010101010101010101010101010101010 - void *fake_ptr = (void *)187649984473770; - Map *fake_obj = fake_ptr; - - v_set_map(&v, fake_obj); - check(v_type(v) == VMAP); - check(pawV_is_map(v)); - check(pawV_is_object(v)); - - Map *m = v_map(v); - check(m == fake_ptr); + Value *pv = pawC_stkinc(P, 1); + Map *m = pawH_new(P); + v_set_object(pv, m); // anchor + return m; } -#define N 500 +static void map_free(paw_Env *P, Map *map) +{ + paw_assert(map == P->top.p[-1].p); + pawC_pop(P); +} -static void test_map(paw_Env *P) +static paw_Int map_get(paw_Env *P, Map *map, paw_Int k) { - StackPtr sp = pawC_stkinc(P, 1); - Map *m = pawH_new(P); - v_set_map(sp++, m); // Anchor + const Value key = {.i = k}; + const Value *pvalue = pawH_get(P, map, key); + paw_assert(pvalue != NULL); + return pvalue->i; +} + +static const paw_Int *map_try(paw_Env *P, Map *map, paw_Int k) +{ + const Value key = {.i = k}; + const Value *pvalue = pawH_get(P, map, key); + return pvalue ? &pvalue->i : NULL; +} + +static void map_put(paw_Env *P, Map *map, paw_Int k, paw_Int v) +{ + const Value key = {.i = k}; + const Value value = {.i = v}; + pawH_insert(P, map, key, value); +} + +static void map_del(paw_Env *P, Map *map, paw_Int k) +{ + const Value key = {.i = k}; + pawH_remove(P, map, key); +} + +static void dump_map(Map *m) +{ + printf("Map{\n"); + for (size_t i = 0; i < m->capacity; ++i) { + const MapMeta *mm = pawH_meta(m, i); + printf(" %.4zu: ", i); + if (mm->state == MAP_ITEM_OCCUPIED) { + printf("%" PRId64 ": %" PRId64 "\n", + pawH_key(m, i)->i, pawH_value(m, i)->i); + } else if (mm->state == MAP_ITEM_ERASED) { + printf("\n"); + } else if (mm->state == MAP_ITEM_VACANT) { + printf("\n"); + } + } + printf("}\n"); +} + +static void test_map1(paw_Env *P) +{ + Map *m = map_new(P); + map_put(P, m, 1, 1); + map_put(P, m, 2, 2); + map_put(P, m, 3, 3); + check(1 == map_get(P, m, 1)); + check(2 == map_get(P, m, 2)); + check(3 == map_get(P, m, 3)); + map_free(P, m); +} + +static void test_map2(paw_Env *P) +{ + Map *m = map_new(P); + map_put(P, m, 1, 1); + map_put(P, m, 2, 2); + map_put(P, m, 3, 3); + map_put(P, m, 4, 4); + map_put(P, m, 5, 5); + map_put(P, m, 6, 6); + + map_del(P, m, 1); + map_del(P, m, 2); + map_del(P, m, 4); + map_del(P, m, 5); + + check(NULL == map_try(P, m, 1)); + check(NULL == map_try(P, m, 2)); + check(3 == map_get(P, m, 3)); + check(NULL == map_try(P, m, 4)); + check(NULL == map_try(P, m, 5)); + check(6 == map_get(P, m, 6)); + map_free(P, m); +} + +static void test_map3(paw_Env *P) +{ + Map *m = map_new(P); // Add known integers for validation. const paw_Int known[] = {-1, -2, -10, -20, -100, -200}; for (size_t i = 0; i < paw_countof(known); ++i) { - paw_push_value(P, -1); - paw_push_int(P, known[i]); - paw_push_int(P, known[i]); - pawR_setitem(P); + map_put(P, m, known[i], known[i]); } check(cast_size(paw_length(P, -1)) == paw_countof(known)); @@ -140,85 +168,39 @@ static void test_map(paw_Env *P) // Fill the map with nonnegative integers (may have repeats). paw_Int integers[N]; for (int i = 0; i < N; ++i) { - const paw_Int ival = test_randint(0, VINT_MAX); - paw_push_value(P, -1); - paw_push_int(P, ival); - paw_push_int(P, ival); - pawR_setitem(P); + const paw_Int ival = test_randint(0, 10000); + map_put(P, m, ival, ival); integers[i] = ival; } check(cast_size(paw_length(P, -1)) <= N + paw_countof(known)); - // Add some strings (should not affect the integers). - char strings[N][64]; - static const size_t ns = sizeof(strings[0]); - for (int i = 0; i < N; ++i) { - test_randstr(strings[i], ns); - paw_push_value(P, -1); - paw_push_nstring(P, strings[i], ns); - paw_push_nstring(P, strings[i], ns); - pawR_setitem(P); - } - - check(cast_size(paw_length(P, -1)) <= 2 * N + paw_countof(known)); - - // Find all items. - for (int i = 0; i < N; ++i) { - paw_push_value(P, -1); - paw_push_nstring(P, strings[i], ns); - pawR_getitem(P); - check(paw_length(P, -1) == ns); - check(0 == memcmp(strings[i], paw_string(P, -1), ns)); - paw_pop(P, 1); - - paw_push_value(P, -1); - paw_push_int(P, integers[i]); - pawR_getitem(P); - check(paw_int(P, -1) == integers[i]); - paw_pop(P, 1); - } - // Erase all nonnegative integers. paw_Int itr = PAW_ITER_INIT; while (pawH_iter(m, &itr)) { - const Value key = m->keys[itr]; - if (pawV_is_int(key) && v_int(key) >= 0) { - pawH_action(P, m, key, MAP_ACTION_REMOVE); - } - } - - check(cast_size(pawH_length(m)) <= N + paw_countof(known)); - - // Erase the strings. - itr = PAW_ITER_INIT; - while (pawH_iter(m, &itr)) { - const Value key = m->keys[itr]; - if (pawV_is_string(key)) { - pawH_action(P, m, key, MAP_ACTION_REMOVE); + const Value key = *pawH_key(m, cast_size(itr)); + if (v_int(key) >= 0) { + map_del(P, m, key.i); } } - check(cast_size(pawH_length(m)) == paw_countof(known)); + check(cast_size(pawH_length(m)) <= paw_countof(known)); // Check known items. for (size_t i = 0; i < paw_countof(known); ++i) { - Value key; - v_set_int(&key, known[i]); - const Value *value = pawH_action(P, m, key, MAP_ACTION_NONE); - check(value); - check(v_int(*value) == known[i]); + const paw_Int value = map_get(P, m, known[i]); + check(value == known[i]); } - pawC_pop(P); // pop map + map_free(P, m); } static void test_stack(paw_Env *P) { - paw_push_nnull(P, 2); + paw_push_unit(P, 2); check(paw_get_count(P) == 2); - check(paw_is_null(P, 0)); - check(paw_is_null(P, 1)); + check(paw_int(P, 0) == 0); + check(paw_int(P, 1) == 0); } static void driver(void (*callback)(paw_Env *)) @@ -232,9 +214,9 @@ static void driver(void (*callback)(paw_Env *)) int main(void) { test_primitives(); - test_objects(); - driver(test_stack); - driver(test_map); + driver(test_map1); + driver(test_map2); + driver(test_map3); return 0; } diff --git a/test/test_rt.c b/test/test_rt.c index 2af9d5a..ad1ded2 100644 --- a/test/test_rt.c +++ b/test/test_rt.c @@ -12,9 +12,9 @@ int main(void) script("basic"); script("block"); script("loop"); + script("operator"); return 0; // TODO script("string"); - script("operator"); script("integer"); script("float"); script("closure"); diff --git a/test/test_so.c b/test/test_so.c index 7997291..b580c17 100644 --- a/test/test_so.c +++ b/test/test_so.c @@ -10,11 +10,12 @@ static void handle_error(paw_Env *P, int status, paw_Bool fatal) int main(void) { const char *source = - "fn f(n) { \n" + "fn f(n: int) { \n" " if n > 0 { \n" " f(n - 1) \n" " } \n" - "} \n"; + "} \n" + "return f \n"; struct TestAlloc a = {0}; paw_Env *P = test_open(NULL, &a); int status = test_open_string(P, source); @@ -23,7 +24,7 @@ int main(void) status = paw_call(P, 0); handle_error(P, status, 1); - paw_get_global(P, "f"); + // 'f' is on top of the stack paw_push_int(P, PAW_STACK_MAX); status = paw_call(P, 1); check(status == PAW_EMEMORY);