Skip to content

Commit

Permalink
Fix map implementation
Browse files Browse the repository at this point in the history
Requires that we use extra metadata to distinguish tombstones from
vacant/occupied slots (no more type tag that we can use for
this purpose). Get more tests to work.
  • Loading branch information
andy-byers committed Jun 23, 2024
1 parent 5d062c4 commit 2704362
Show file tree
Hide file tree
Showing 27 changed files with 737 additions and 1,379 deletions.
6 changes: 3 additions & 3 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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)
Expand Down
87 changes: 39 additions & 48 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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.

```
Expand All @@ -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
Expand All @@ -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.
Expand All @@ -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 {
Expand All @@ -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
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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

Expand Down
104 changes: 47 additions & 57 deletions src/api.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include "str.h"
#include "type.h"
#include "value.h"
#include "vector.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
{
Expand All @@ -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);
}

Expand All @@ -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)
Expand Down Expand Up @@ -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)
Expand Down
4 changes: 2 additions & 2 deletions src/api.h
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
2 changes: 1 addition & 1 deletion src/auxlib.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 <stdlib.h>

static void grow_buffer(paw_Env *P, Buffer *buf, int boxloc)
Expand Down
Loading

0 comments on commit 2704362

Please sign in to comment.