Skip to content

Commit

Permalink
Add enumseq
Browse files Browse the repository at this point in the history
  • Loading branch information
bgk- committed May 16, 2024
1 parent 0314755 commit a75216a
Show file tree
Hide file tree
Showing 14 changed files with 178 additions and 92 deletions.
9 changes: 4 additions & 5 deletions build.zig.zon
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
.{
.name = "topiary",
.version = "0.12.0",
.paths = .{""},
.dependencies = .{
},
.name = "topiary",
.version = "0.12.1",
.paths = .{""},
.dependencies = .{},
}
39 changes: 37 additions & 2 deletions docs/syntax.md
Original file line number Diff line number Diff line change
Expand Up @@ -316,8 +316,6 @@ map.clear() // Map{}
#### While

While loops will execute so long as the condition is met.
~~However there is an internal limit of 100,000 to catch infinite loops.
This can be adjusted by setting `Topiary.MaxWhile = -1 // no limit`~~

```topi
var i = 0
Expand Down Expand Up @@ -468,6 +466,43 @@ enum Cardinal = {
var direction = Cardinal.North
```

Under the hood they are just index integers, which cannot be changed.
This does mean you can use comparitive operators with enums.

```topi
var north = Cardinal.North
var south = Cardinal.South
if (north < south) print(true) // true
```

## Sequences

Enum Sequences (`enumseq`) are special enums, they are the same except they cannot be changed to a previous value.
If attempted, topi will ignore the assignment and remain at the current value.

```topi
enumseq QuestGiver = {
None,
LearnedOfQuestGiver,
MetQuestGiver,
AcceptedQuest,
CompletedQuest,
RecievedAward
}
var seq = QuestGiver.None
seq = QuestGiver.MetQuestGiver
seq = QuestGiver.LearnedOfQuestGiver // ignored
print(seq) // QuestGiver.MetQuestGiver
```

Sequences are useful in that all previous states are inferred from the current.
If the player met the quest giver, they must have learned of them.
Same if they accepted the quest, they must have met them, and so on.
For more information you can watch this great talk by
[Inkle's Jon Ingold](https://www.youtube.com/watch?v=HZft_U4Fc-U).

### Classes

Classes are an encapsulation of named data.
Expand Down
1 change: 1 addition & 0 deletions src/ast.zig
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ pub const Statement = struct {
},
@"enum": struct {
name: []const u8,
is_seq: bool,
values: [][]const u8,
},
expression: Expression,
Expand Down
67 changes: 44 additions & 23 deletions src/builtins.zig
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,30 @@ const Print = struct {
const writer = std.debug;
args[0].print(writer, null);
writer.print("\n", .{});
return values.Nil;
return values.Void;
}
};

pub const Assert = struct {
const Self = @This();
var value: Value = .{
.obj = &Self.obj,
};
var obj: Value.Obj = .{
.data = .{
.builtin = .{
.backing = Self.builtin,
.arity = 2,
.is_method = false,
.name = "assert",
},
},
};
fn builtin(_: *Gc, args: []Value) Value {
const expr = args[0];
const msg = args[1];
if (!expr.eql(values.True)) return msg;
return values.Void;
}
};

Expand All @@ -97,24 +120,22 @@ const Definition = struct {
value: *Value,
};

pub const builtins = [_]Definition{
.{
.name = "rnd",
.value = &Rnd.value,
},
.{
.name = "rnd01",
.value = &Rnd01.value,
},
.{
.name = "print",
.value = &Print.value,
},
.{
.name = "round",
.value = &Round.value,
},
};
pub const builtins = [_]Definition{ .{
.name = "rnd",
.value = &Rnd.value,
}, .{
.name = "rnd01",
.value = &Rnd01.value,
}, .{
.name = "print",
.value = &Print.value,
}, .{
.name = "round",
.value = &Round.value,
}, .{
.name = "assert",
.value = &Assert.value,
} };

pub const Count = struct {
const Self = @This();
Expand Down Expand Up @@ -155,7 +176,7 @@ pub const Add = struct {
.set => args[0].obj.data.set.put(item, {}) catch {},
else => unreachable,
}
return values.Nil;
return values.Void;
}
};

Expand All @@ -176,7 +197,7 @@ pub const AddMap = struct {
.map => args[0].obj.data.map.put(key, item) catch {},
else => unreachable,
}
return values.Nil;
return values.Void;
}
};
pub const Remove = struct {
Expand All @@ -203,7 +224,7 @@ pub const Remove = struct {
.map => _ = args[0].obj.data.map.orderedRemove(item),
else => unreachable,
}
return values.Nil;
return values.Void;
}
};

Expand Down Expand Up @@ -259,6 +280,6 @@ pub const Clear = struct {
.set => data.set.clearAndFree(),
else => {},
}
return values.Nil;
return values.Void;
}
};
3 changes: 2 additions & 1 deletion src/compiler-error.zig
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,8 @@ pub const CompilerErrors = struct {
var lineStart: usize = 0;
if (lineNumber == 1) lineStart = if (std.mem.startsWith(u8, l, "\xEF\xBB\xBF")) 3 else @as(usize, 0);
try writer.print("{s}\n", .{l[lineStart..]});
try writer.writeByteNTimes(' ', column - 1);
try writer.writeByteNTimes(' ', @max(column - 1, 0));
try writer.print("{s}", .{color_prefix});
try writer.writeByteNTimes('~', end - start);
try writer.writeAll("\n\x1b[0m");
break;
Expand Down
1 change: 1 addition & 0 deletions src/compiler.zig
Original file line number Diff line number Diff line change
Expand Up @@ -459,6 +459,7 @@ pub const Compiler = struct {
obj.* = .{
.data = .{
.@"enum" = .{
.is_seq = e.is_seq,
.name = try self.allocator.dupe(u8, e.name),
.values = try names.toOwnedSlice(),
},
Expand Down
8 changes: 3 additions & 5 deletions src/enum.zig
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,10 @@ const Value = @import("values.zig").Value;
pub const Enum = struct {
name: []const u8,
values: [][]const u8,
is_seq: bool,

pub fn init(name: []const u8, values: [][]const u8) Enum {
return .{
.name = name,
.values = values,
};
pub fn init(name: []const u8, values: [][]const u8, is_seq: bool) Enum {
return .{ .name = name, .values = values, .is_seq = is_seq };
}

pub const Val = struct {
Expand Down
50 changes: 20 additions & 30 deletions src/export.zig
Original file line number Diff line number Diff line change
Expand Up @@ -511,25 +511,13 @@ const ExportRunner = struct {

const TestRunner = struct {
pub fn onLine(vm_ptr: usize, dialogue: *ExportLine) void {
std.debug.print("{s}: {s} ", .{
dialogue.speaker[0..dialogue.speaker_length],
dialogue.content[0..dialogue.content_length],
});
for (dialogue.tags[0..dialogue.tags_length]) |t| {
std.debug.print("#{s} ", .{t});
}
std.debug.print("\n", .{});
_ = dialogue;
selectContinue(vm_ptr);
}

pub fn onChoices(vm_ptr: usize, choices: [*]ExportChoice, choices_len: u8) void {
for (choices, 0..choices_len) |choice, i| {
std.debug.print("[{d}] {s} ", .{ i, choice.content });
for (choice.tags[0..choice.tags_length]) |t| {
std.debug.print("#{s} ", .{t});
}
std.debug.print("\n", .{});
}
_ = choices;
_ = choices_len;
selectChoice(vm_ptr, 0);
}

Expand All @@ -539,7 +527,7 @@ const TestRunner = struct {
};

fn testSubscriber(value: ExportValue) void {
std.debug.print("ExportSubscriber: {s}\n", .{value.data.string});
std.testing.expectEqualSlices(u8, "321 test", value.data.string[0..8]) catch {};
}

test "Create and Destroy Vm" {
Expand Down Expand Up @@ -568,9 +556,7 @@ test "Create and Destroy Vm" {
;

debug_log = TestRunner.log;
debug_severity = .info;
defer debug_log = null;
defer debug_severity = .err;

const file = try std.fs.cwd().createFile("tmp.topi", .{ .read = true });
defer std.fs.cwd().deleteFile("tmp.topi") catch {};
Expand All @@ -595,9 +581,7 @@ test "Create and Destroy Vm" {
const vm: *Vm = @ptrFromInt(vm_ptr);

defer destroyVm(vm_ptr);
vm.bytecode.print(std.debug);
defer vm.bytecode.free(alloc);
std.debug.print("\n=====\n", .{});
const val_name = "value";
subscribe(
vm_ptr,
Expand Down Expand Up @@ -628,10 +612,11 @@ test "Create and Destroy Vm" {
list_name.len,
&list_value,
)) {
std.debug.print("List: {}\n", .{list_value.data.list});
for (list_value.data.list.items[0..list_value.data.list.count]) |item| {
std.debug.print("List Item: {d}\n", .{item.data.number});
}
const list = list_value.data.list.items;
try std.testing.expectEqual(1, list[0].data.number);
try std.testing.expectEqual(2, list[1].data.number);
try std.testing.expectEqual(3, list[2].data.number);
try std.testing.expectEqual(4, list[3].data.number);
destroyValue(&list_value);
}

Expand All @@ -643,9 +628,10 @@ test "Create and Destroy Vm" {
set_name.len,
&set_value,
)) {
for (set_value.data.list.items[0..set_value.data.list.count]) |item| {
std.debug.print("Set Item: {s}\n", .{item.data.string});
}
const set = set_value.data.list.items;
try std.testing.expectEqualSlices(u8, "some", set[0].data.string[0..4]);
try std.testing.expectEqualSlices(u8, "string", set[1].data.string[0..6]);
try std.testing.expectEqualSlices(u8, "values", set[2].data.string[0..6]);
destroyValue(&set_value);
}

Expand All @@ -657,9 +643,13 @@ test "Create and Destroy Vm" {
map_name.len,
&map_value,
)) {
for (map_value.data.list.items[0 .. map_value.data.list.count * 2]) |item| {
std.debug.print("Map Item: {d}\n", .{item.data.number});
}
const map = map_value.data.list.items;
try std.testing.expectEqual(0, map[0].data.number);
try std.testing.expectEqual(0.0001, map[1].data.number);
try std.testing.expectEqual(1, map[2].data.number);
try std.testing.expectEqual(1.1111, map[3].data.number);
try std.testing.expectEqual(2, map[4].data.number);
try std.testing.expectEqual(2.222, map[5].data.number);
destroyValue(&map_value);
}
}
4 changes: 3 additions & 1 deletion src/parser.zig
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ pub const Parser = struct {
return switch (self.current_token.token_type) {
.include => try self.includeStatement(),
.class => try self.classDeclaration(),
.@"enum" => try self.enumDeclaration(),
.@"enum", .enumseq => try self.enumDeclaration(),
.@"extern", .@"var", .@"const" => try self.varDeclaration(),
.bough => try self.boughStatement(),
.divert => try self.divertStatement(),
Expand Down Expand Up @@ -274,6 +274,7 @@ pub const Parser = struct {

fn enumDeclaration(self: *Parser) Error!Statement {
const start = self.current_token;
const is_seq = start.token_type == .enumseq;
self.next();
const name = try self.consumeIdentifier();
try self.expectCurrent(.equal);
Expand All @@ -291,6 +292,7 @@ pub const Parser = struct {
.token = start,
.type = .{
.@"enum" = .{
.is_seq = is_seq,
.name = name,
.values = try values.toOwnedSlice(),
},
Expand Down
3 changes: 3 additions & 0 deletions src/state.zig
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,8 @@ pub const State = struct {
try stream.beginObject();
try stream.objectField("name");
try stream.write(e.name);
try stream.objectField("is_seq");
try stream.write(e.is_seq);
try stream.objectField("values");
try stream.beginArray();
for (e.values) |v| try stream.write(v);
Expand Down Expand Up @@ -275,6 +277,7 @@ pub const State = struct {
for (values_items, 0..) |t, i| vals[i] = try vm.allocator.dupe(u8, t.string);
var result = try vm.gc.create(vm, .{ .@"enum" = .{
.name = v.object.get("name").?.string,
.is_seq = v.object.get("is_seq").?.bool,
.values = vals,
} });
result.obj.id = id.?;
Expand Down
Loading

0 comments on commit a75216a

Please sign in to comment.