Skip to content

Commit

Permalink
Complete @host type, use embed API for File types. Create tuples fr…
Browse files Browse the repository at this point in the history
…om `@host` funcs, pairIterator is now seqIterator.
  • Loading branch information
fubark committed Sep 27, 2023
1 parent 82dba02 commit e6e8e24
Show file tree
Hide file tree
Showing 40 changed files with 1,560 additions and 1,188 deletions.
2 changes: 1 addition & 1 deletion build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ pub fn build(b: *std.build.Builder) !void {

var opts = getDefaultOptions(target, optimize);
opts.ffi = false;
opts.malloc = .malloc;
opts.malloc = if (target.getCpuArch().isWasm()) .zig else .malloc;
opts.cli = false;
opts.applyOverrides();

Expand Down
14 changes: 7 additions & 7 deletions docs/hugo/content/docs/toc/control-flow.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,18 +80,18 @@ var list = [1, 2, 3, 4, 5]
for list each n:
print n
```
When the `as` clause contains two variables, the for loop will iterate a `PairIterable` object. A PairIterable type contains a `pairIterator()` method that returns a `PairIterator` object. A PairIterator type contains a `nextPair()` method that returns two values or `none` on the first value when finished.
The list object is also a PairIterable and the key is the index of the value in the list.
When the `as` clause contains a sequence destructuring expression, the for loop will iterate a `SequenceIterator` created from the iterating value's `seqIterator()` method. The loop then continues to invoke `nextSeq()` from the `SequenceIterator` to get the next sequence object until `none` is returned.
The list object produces an index and value tuple when iterated like this.
```cy
-- Iterate on values and indexes.
for list each i, n:
for list each [i, n]:
print '{i} -> {n}'

-- Iterate on just indexes.
for list each i, _:
for list each [i, _]:
print i
```
The `for` clause can also iterate over maps with the same idea.
The `for` clause can also iterate over maps.
```cy
var map = { a: 123, b: 234 }

Expand All @@ -100,11 +100,11 @@ for map each v:
print v

-- Iterate on values and keys.
for map each k, v:
for map each [k, v]:
print '{k} -> {v}'

-- Iterate on just keys.
for map each k, _:
for map each [k, _]:
print k
```

Expand Down
11 changes: 7 additions & 4 deletions docs/hugo/content/docs/toc/data-types.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ weight: 2
---

# Data Types.
In Cyber, there are primitive types and object types. Primitives are copied around by value and don't need additional heap memory or reference counts. Primitives include [Booleans](#booleans), [Floats](#floats), [Integers](#integers), [Enums](#enums), [Symbols](#symbols), [Errors]({{<relref "/docs/toc/errors">}}), [Static Strings](#strings), and the `none` value. Object types include [Lists](#lists), [Maps](#maps), [Strings](#strings), [Custom Objects](#objects), [Lambdas]({{<relref "/docs/toc/functions#lambdas">}}), [Fibers]({{<relref "/docs/toc/concurrency#fibers">}}), [Errors with payloads]({{<relref "/docs/toc/errors">}}), [Pointers]({{<relref "/docs/toc/ffi#pointers">}}), and several internal object types.
In Cyber, there are primitive types and object types. Primitives are copied around by value and don't need additional heap memory or reference counts. Primitives include [Booleans](#booleans), [Floats](#floats), [Integers](#integers), [Enums](#enums), [Symbols](#symbols), [Errors]({{<relref "/docs/toc/errors">}}), [Static Strings](#strings), and the `none` value. Object types include [Lists](#lists), [Tuples](#tuples), [Maps](#maps), [Strings](#strings), [Custom Objects](#objects), [Lambdas]({{<relref "/docs/toc/functions#lambdas">}}), [Fibers]({{<relref "/docs/toc/concurrency#fibers">}}), [Errors with payloads]({{<relref "/docs/toc/errors">}}), [Pointers]({{<relref "/docs/toc/ffi#pointers">}}), and several internal object types.

The `none` value represents an empty value. This is similar to null in other languages.

Expand Down Expand Up @@ -368,11 +368,14 @@ list.remove(1)
| `iterator() Iterator<any>` | Returns a new iterator over the list elements. |
| `joinString(separator any) string` | Returns a new string that joins the elements with `separator`. |
| `len() int` | Returns the number of elements in the list. |
| `pairIterator() PairIterator<int, any>` | Returns a new pair iterator over the list elements. |
| `seqIterator() SequenceIterator<int, any>` | Returns a new sequence iterator over the list elements. |
| `remove(idx int) none` | Removes an element at index `idx`. |
| `resize(len int) none` | Resizes the list to `len` elements. If the new size is bigger, `none` values are appended to the list. If the new size is smaller, elements at the end of the list are removed. |
| `sort(less func (a, b) bool) none` | Sorts the list with the given `less` function. If element `a` should be ordered before `b`, the function should return `true` otherwise `false`. |
## Tuples.
> _Incomplete: Tuples can only be created from @host funcs at the moment._
## Maps.
Maps are a builtin type that store key value pairs in dictionaries.
```cy
Expand Down Expand Up @@ -421,15 +424,15 @@ print map.size()
map.remove 123

-- Iterating a list.
for map each val, key:
for map each [val, key]:
print '{key} -> {value}'
```

### `type Map`
| Method | Summary |
| ------------- | ----- |
| `iterator() Iterator<any>` | Returns a new iterator over the map elements. |
| `pairIterator() PairIterator<any, any>` | Returns a new pair iterator over the map elements. |
| `seqIterator() SequenceIterator<any, any>` | Returns a new sequence iterator over the map elements. |
| `remove(key any) none` | Removes the element with the given key `key`. |
| `size() int` | Returns the number of key-value pairs in the map. |

Expand Down
1 change: 0 additions & 1 deletion docs/hugo/content/docs/toc/modules.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,6 @@ type Thing object: -- Exported type.
```

The annotation `@hide` provides a hint to editors that the static symbol should not appear in the auto-complete. Despite this, the symbol is still reachable.
> _Planned Feature_

## Builtin Modules.
Builtin modules are the bare minimum that comes with Cyber. The [embeddable library]({{<relref "/docs/toc/embedding">}}) contains these modules and nothing more. They include:
Expand Down
2 changes: 1 addition & 1 deletion examples/account.cy
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ type Account object:
balance

func new(name):
return Account{ name: name, balance: 0 }
return Account{ name: name, balance: 0.0 }

func deposit(self, amt):
self.balance += amt
Expand Down
2 changes: 1 addition & 1 deletion examples/c-embedded/bind_module.c
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ int main() {
csDeinit(vm);

// Check that all references were accounted for. (Should be 0)
printf("Global RC: %zu\n", csGetGlobalRC(vm));
printf("Live objects: %zu\n", csCountObjects(vm));
csDestroy(vm);

return 0;
Expand Down
4 changes: 2 additions & 2 deletions examples/ffi.cy
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@
import os 'os'

var lib = os.bindLib('./libfoo.so', [
os.CFunc{ sym: 'add', args: [#int, #int], ret: #int }
os.CFunc{ sym: 'add', args: [.int, .int], ret: .int }
])
lib.add(123, 321)
print lib.add(123.0, 321.0)
2 changes: 1 addition & 1 deletion examples/fiber.cy
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@ func go():
coyield

var f = coinit go()
while f.status() != #done:
while f.status() != .done:
coresume f
9 changes: 5 additions & 4 deletions src/api.zig
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const fmt = @import("fmt.zig");
const debug = @import("debug.zig");
const VM = cy.VM;
const Value = cy.Value;
const fs = @import("std/fs.zig");

/// A simplified VM handle.
pub const UserVM = struct {
Expand Down Expand Up @@ -286,15 +287,15 @@ pub const UserVM = struct {
}

pub inline fn allocDir(self: *UserVM, fd: std.os.fd_t, iterable: bool) !Value {
return cy.heap.allocDir(self.internal(), fd, iterable);
return fs.allocDir(self.internal(), fd, iterable);
}

pub inline fn allocDirIterator(self: *UserVM, dir: *cy.Dir, recursive: bool) !Value {
return cy.heap.allocDirIterator(self.internal(), dir, recursive);
pub inline fn allocDirIterator(self: *UserVM, dir: Value, recursive: bool) !Value {
return fs.allocDirIterator(self.internal(), dir, recursive);
}

pub inline fn allocFile(self: *UserVM, fd: std.os.fd_t) !Value {
return cy.heap.allocFile(self.internal(), fd);
return fs.allocFile(self.internal(), fd);
}

pub inline fn valueAsString(self: *UserVM, val: Value) []const u8 {
Expand Down
94 changes: 68 additions & 26 deletions src/arc.zig
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,6 @@ const vmc = cy.vmc;
const rt = cy.rt;

pub fn release(vm: *cy.VM, val: cy.Value) linksection(cy.HotSection) void {
releaseExt(vm, val, false);
}

pub fn releaseExt(vm: *cy.VM, val: cy.Value, comptime trackFrees: bool) linksection(cy.HotSection) void {
if (cy.Trace) {
vm.trace.numReleaseAttempts += 1;
}
Expand All @@ -39,9 +35,12 @@ pub fn releaseExt(vm: *cy.VM, val: cy.Value, comptime trackFrees: bool) linksect
}
if (obj.head.rc == 0) {
// Free children and the object.
@call(.never_inline, cy.heap.freeObject, .{vm, obj, true, false, true, trackFrees});
if (trackFrees) {
numObjectsFreed += 1;
@call(.never_inline, cy.heap.freeObject, .{vm, obj, true, false, true});

if (cy.Trace) {
if (vm.countFrees) {
vm.numFreed += 1;
}
}
}
} else {
Expand Down Expand Up @@ -77,10 +76,6 @@ pub fn checkDoubleFree(vm: *cy.VM, obj: *cy.HeapObject) void {
}

pub fn releaseObject(vm: *cy.VM, obj: *cy.HeapObject) linksection(cy.HotSection) void {
releaseObjectExt(vm, obj, false);
}

pub fn releaseObjectExt(vm: *cy.VM, obj: *cy.HeapObject, comptime trackFrees: bool) linksection(cy.HotSection) void {
if (cy.Trace) {
checkDoubleFree(vm, obj);
}
Expand All @@ -94,9 +89,11 @@ pub fn releaseObjectExt(vm: *cy.VM, obj: *cy.HeapObject, comptime trackFrees: bo
vm.trace.numReleaseAttempts += 1;
}
if (obj.head.rc == 0) {
@call(.never_inline, cy.heap.freeObject, .{vm, obj, true, false, true, trackFrees});
if (trackFrees) {
numObjectsFreed += 1;
@call(.never_inline, cy.heap.freeObject, .{vm, obj, true, false, true});
if (cy.Trace) {
if (vm.countFrees) {
vm.numFreed += 1;
}
}
}
}
Expand Down Expand Up @@ -228,11 +225,17 @@ fn performMark(vm: *cy.VM) !void {
}
}

var numObjectsFreed: u32 = 0;

fn performSweep(vm: *cy.VM) !GCResult {
// Collect cyc nodes and release their children (child cyc nodes are skipped).
numObjectsFreed = 0;
if (cy.Trace) {
vm.countFrees = true;
vm.numFreed = 0;
}
defer {
if (cy.Trace) {
vm.countFrees = false;
}
}
var cycObjs: std.ArrayListUnmanaged(*cy.HeapObject) = .{};
defer cycObjs.deinit(vm.alloc);
for (vm.heapPages.items()) |page| {
Expand All @@ -242,7 +245,7 @@ fn performSweep(vm: *cy.VM) !GCResult {
if (obj.freeSpan.typeId != cy.NullId) {
if (obj.isGcConfirmedCyc()) {
try cycObjs.append(vm.alloc, obj);
cy.heap.freeObject(vm, obj, true, true, false, true);
cy.heap.freeObject(vm, obj, true, true, false);
} else if (obj.isGcMarked()) {
obj.resetGcMarked();
}
Expand All @@ -260,7 +263,7 @@ fn performSweep(vm: *cy.VM) !GCResult {
const obj = node.getHeapObject();
if (obj.isGcConfirmedCyc()) {
try cycObjs.append(vm.alloc, obj);
cy.heap.freeObject(vm, obj, true, true, false, true);
cy.heap.freeObject(vm, obj, true, true, false);
} else if (obj.isGcMarked()) {
obj.resetGcMarked();
}
Expand All @@ -277,16 +280,17 @@ fn performSweep(vm: *cy.VM) !GCResult {
vm.refCounts -= obj.head.rc;
}
// No need to bother with their refcounts.
cy.heap.freeObject(vm, obj, false, false, true, false);
cy.heap.freeObject(vm, obj, false, false, true);
}

if (cy.Trace) {
vm.trace.numCycFrees += @intCast(cycObjs.items.len);
vm.numFreed += @intCast(cycObjs.items.len);
}

return GCResult{
.numCycFreed = @intCast(cycObjs.items.len),
.numObjFreed = @intCast(cycObjs.items.len + numObjectsFreed),
.numObjFreed = if (cy.Trace) vm.numFreed else 0,
};
}

Expand Down Expand Up @@ -397,22 +401,60 @@ fn markValue(vm: *cy.VM, v: cy.Value) void {
// TODO: Visit other fiber stacks.
},
else => {
if (typeId < rt.NumBuiltinTypes) {
// Skip, non-cyclable object.
} else {
// Assume caller used isCycPointer and obj is cyclable.
const entry = &vm.types.buf[typeId];
if (!entry.isHostObject) {
// User type.
const numMembers = vm.types.buf[typeId].numFields;
const members = obj.object.getValuesConstPtr()[0..numMembers];
const members = obj.object.getValuesConstPtr()[0..entry.data.numFields];
for (members) |m| {
if (m.isCycPointer()) {
markValue(vm, m);
}
}
} else {
// Host type.
if (entry.data.hostObject.getChildren) |getChildren| {
const children = @as(cy.ObjectGetChildrenFn, @ptrCast(@alignCast(getChildren)))(@ptrCast(vm), @ptrFromInt(@intFromPtr(obj) + 8));
for (children.slice()) |child| {
if (child.isCycPointer()) {
markValue(vm, child);
}
}
}
}
},
}
}

pub fn countObjects(vm: *cy.VM) usize {
var count: usize = 0;
for (vm.heapPages.items()) |page| {
var i: u32 = 1;
while (i < page.objects.len) {
const obj = &page.objects[i];
if (obj.freeSpan.typeId != cy.NullId) {
count += 1;
i += 1;
} else {
// Freespan, skip to end.
i += obj.freeSpan.len;
}
}
}

// Traverse non-pool cyc nodes.
var mbNode: ?*cy.heap.DListNode = vm.cyclableHead;
while (mbNode) |node| {
count += 1;
mbNode = node.next;
}

// Account for the dummy cyclable node.
count -= 1;

return count;
}

pub fn checkGlobalRC(vm: *cy.VM) !void {
const rc = getGlobalRC(vm);
if (rc != vm.expGlobalRC) {
Expand Down
Loading

0 comments on commit e6e8e24

Please sign in to comment.