Skip to content

Commit

Permalink
feat: New value ObjRange
Browse files Browse the repository at this point in the history
Range are now a special value that can be transformed into an [int] with toList.
This allows to use them in a foreach statement without instanciating a list.

closes #170
  • Loading branch information
giann committed Mar 13, 2024
1 parent 391e125 commit 3497e6c
Show file tree
Hide file tree
Showing 19 changed files with 668 additions and 153 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,11 @@ var value = from {
- `namespace` (https://github.com/buzz-language/buzz/issues/271): if a script exports at least one symbol, it has to define a namespace for the script with `namespace mynamespace`
- By default, imported symbols from another script will be under `libprefix.XXXX`
- When importing something, you can still redefine its namespace prefix with `import "..." as mynewnamespace` or remove it altogether with `import "..." _`
- Ranges are now an actual buzz value (https://github.com/buzz-language/buzz/issues/170)
- new `range` type
- `myrange.toList()` transforms a range into a list of integers
- `myrange.low` and `myrange.high` to get a range bounds
- works with `foreach`

## Changed
- Map type notation has changed from `{K, V}` to `{K: V}`. Similarly map expression with specified typed went from `{<K, V>, ...}` to `{<K: V>, ...}` (https://github.com/buzz-language/buzz/issues/253)
Expand Down
13 changes: 12 additions & 1 deletion src/Ast.zig
Original file line number Diff line number Diff line change
Expand Up @@ -583,7 +583,6 @@ pub fn isConstant(self: Self, node: Node.Index) bool {
.ObjectDeclaration,
.ObjectInit,
.ProtocolDeclaration,
.Range,
.Resolve,
.Resume,
.Return,
Expand All @@ -598,6 +597,7 @@ pub fn isConstant(self: Self, node: Node.Index) bool {
=> false,
.As => self.isConstant(self.nodes.items(.components)[node].As.left),
.Is => self.isConstant(self.nodes.items(.components)[node].Is.left),
.Range => self.isConstant(self.nodes.items(.components)[node].Range.low) and self.isConstant(self.nodes.items(.components)[node].Range.high),
.Binary => {
const components = self.nodes.items(.components)[node].Binary;

Expand Down Expand Up @@ -946,6 +946,17 @@ pub fn toValue(self: Self, node: Node.Index, gc: *GarbageCollector) Error!Value
else
try self.toValue(components.else_branch.?, gc);
},
.Range => range: {
const components = self.nodes.items(.components)[node].Range;

break :range (try gc.allocateObject(
obj.ObjRange,
.{
.low = (try self.toValue(components.low, gc)).integer(),
.high = (try self.toValue(components.high, gc)).integer(),
},
)).toValue();
},
.List => list: {
const components = self.nodes.items(.components)[node].List;
const type_def = self.nodes.items(.type_def)[node];
Expand Down
3 changes: 3 additions & 0 deletions src/Chunk.zig
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ pub const OpCode = enum(u8) {
OP_LOOP,
OP_STRING_FOREACH,
OP_LIST_FOREACH,
OP_RANGE_FOREACH,
OP_ENUM_FOREACH,
OP_MAP_FOREACH,
OP_FIBER_FOREACH,
Expand All @@ -73,6 +74,7 @@ pub const OpCode = enum(u8) {
OP_FIBER_INVOKE,
OP_LIST_INVOKE,
OP_MAP_INVOKE,
OP_RANGE_INVOKE,

OP_CLOSURE,
OP_CLOSE_UPVALUE,
Expand Down Expand Up @@ -102,6 +104,7 @@ pub const OpCode = enum(u8) {
OP_GET_STRING_PROPERTY,
OP_GET_PATTERN_PROPERTY,
OP_GET_FIBER_PROPERTY,
OP_GET_RANGE_PROPERTY,
OP_SET_OBJECT_PROPERTY,
OP_SET_INSTANCE_PROPERTY,
OP_SET_FCONTAINER_INSTANCE_PROPERTY,
Expand Down
37 changes: 37 additions & 0 deletions src/Codegen.zig
Original file line number Diff line number Diff line change
Expand Up @@ -1346,6 +1346,7 @@ fn generateCall(self: *Self, node: Ast.Node.Index, breaks: ?*std.ArrayList(usize
.Fiber => .OP_FIBER_INVOKE,
.List => .OP_LIST_INVOKE,
.Map => .OP_MAP_INVOKE,
.Range => .OP_RANGE_INVOKE,
else => unexpected: {
std.debug.assert(self.reporter.had_error);
break :unexpected if (components.tail_call)
Expand Down Expand Up @@ -1425,6 +1426,7 @@ fn generateDot(self: *Self, node: Ast.Node.Index, breaks: ?*std.ArrayList(usize)
.Pattern,
.Fiber,
.ForeignContainer,
.Range,
=> {},
else => self.reporter.reportErrorAt(
.field_access,
Expand All @@ -1450,6 +1452,7 @@ fn generateDot(self: *Self, node: Ast.Node.Index, breaks: ?*std.ArrayList(usize)
.String => .OP_GET_STRING_PROPERTY,
.Pattern => .OP_GET_PATTERN_PROPERTY,
.Fiber => .OP_GET_FIBER_PROPERTY,
.Range => .OP_GET_RANGE_PROPERTY,
else => null,
};

Expand Down Expand Up @@ -1535,6 +1538,21 @@ fn generateDot(self: *Self, node: Ast.Node.Index, breaks: ?*std.ArrayList(usize)
@intCast(components.value_or_call_or_enum.EnumCase),
);
},
.Range => {
if (components.member_kind == .Call) {
try self.emitOpCode(locations[node], .OP_COPY);

_ = try self.generateNode(components.value_or_call_or_enum.Call, breaks);
} else {
std.debug.assert(components.member_kind != .Value);

try self.emitCodeArg(
locations[node],
get_code.?,
try self.identifierConstant(identifier_lexeme),
);
}
},
.EnumInstance => {
std.debug.assert(std.mem.eql(u8, identifier_lexeme, "value"));

Expand Down Expand Up @@ -1887,6 +1905,11 @@ fn generateForEach(self: *Self, node: Ast.Node.Index, breaks: ?*std.ArrayList(us
self.ast.tokens.get(locations[components.key]),
"No key available when iterating over enum.",
),
.Range => self.reporter.reportErrorAt(
.foreach_key_type,
self.ast.tokens.get(locations[components.key]),
"No key available when iterating over range.",
),
else => self.reporter.reportErrorAt(
.foreach_iterable,
self.ast.tokens.get(locations[components.iterable]),
Expand Down Expand Up @@ -1931,6 +1954,18 @@ fn generateForEach(self: *Self, node: Ast.Node.Index, breaks: ?*std.ArrayList(us
);
}
},
.Range => {
if (value_type_def.def_type != .Integer or value_type_def.optional) {
self.reporter.reportTypeCheck(
.foreach_value_type,
self.ast.tokens.get(locations[components.iterable]),
try self.gc.type_registry.getTypeDef(.{ .def_type = .Integer }),
self.ast.tokens.get(locations[components.value]),
value_type_def,
"Bad value type",
);
}
},
.String => {
if (value_type_def.def_type != .String) {
self.reporter.reportErrorAt(
Expand Down Expand Up @@ -1986,6 +2021,7 @@ fn generateForEach(self: *Self, node: Ast.Node.Index, breaks: ?*std.ArrayList(us
.Map => obj.ObjMap.cast(iterable).?.map.count() == 0,
.String => obj.ObjString.cast(iterable).?.string.len == 0,
.Enum => obj.ObjEnum.cast(iterable).?.cases.len == 0,
.Range => obj.ObjRange.cast(iterable).?.high == obj.ObjRange.cast(iterable).?.low,
else => self.reporter.had_error,
}) {
try self.patchOptJumps(node);
Expand All @@ -2008,6 +2044,7 @@ fn generateForEach(self: *Self, node: Ast.Node.Index, breaks: ?*std.ArrayList(us
.Enum => .OP_ENUM_FOREACH,
.Map => .OP_MAP_FOREACH,
.Fiber => .OP_FIBER_FOREACH,
.Range => .OP_RANGE_FOREACH,
else => unexpected: {
std.debug.assert(self.reporter.had_error);
break :unexpected .OP_STRING_FOREACH;
Expand Down
159 changes: 52 additions & 107 deletions src/Jit.zig
Original file line number Diff line number Diff line change
Expand Up @@ -1412,6 +1412,7 @@ fn unwrap(self: *Self, def_type: o.ObjTypeDef.Type, value: m.MIR_op_t, dest: m.M
.Type,
.Fiber,
.UserData,
.Range,
=> self.buildValueToObj(value, dest),
.ForeignContainer => try self.buildValueToForeignContainer(value, dest),
.Placeholder,
Expand Down Expand Up @@ -1442,6 +1443,7 @@ fn wrap(self: *Self, def_type: o.ObjTypeDef.Type, value: m.MIR_op_t, dest: m.MIR
.Type,
.Fiber,
.UserData,
.Range,
=> self.buildValueFromObj(value, dest),
.ForeignContainer,
.Placeholder,
Expand Down Expand Up @@ -1682,6 +1684,7 @@ fn generateCall(self: *Self, node: Ast.Node.Index) Error!?m.MIR_op_t {
.Fiber,
.List,
.Map,
.Range,
=> try self.buildExternApiCall(
switch (invoked_on.?) {
.ObjectInstance, .ProtocolInstance => .bz_getInstanceField,
Expand All @@ -1690,6 +1693,7 @@ fn generateCall(self: *Self, node: Ast.Node.Index) Error!?m.MIR_op_t {
.Fiber => .bz_getFiberField,
.List => .bz_getListField,
.Map => .bz_getMapField,
.Range => .bz_getRangeField,
else => unreachable,
},
callee,
Expand Down Expand Up @@ -2825,123 +2829,23 @@ fn generateList(self: *Self, node: Ast.Node.Index) Error!?m.MIR_op_t {

fn generateRange(self: *Self, node: Ast.Node.Index) Error!?m.MIR_op_t {
const components = self.state.?.ast.nodes.items(.components)[node].Range;
const type_def = self.state.?.ast.nodes.items(.type_def)[node];

const new_list = m.MIR_new_reg_op(
self.ctx,
try self.REG("new_list", m.MIR_T_I64),
);

try self.buildExternApiCall(
.bz_newList,
new_list,
&[_]m.MIR_op_t{
m.MIR_new_reg_op(self.ctx, self.state.?.vm_reg.?),
m.MIR_new_uint_op(self.ctx, type_def.?.resolved_type.?.List.item_type.toValue().val),
},
);

// Prevent collection
try self.buildPush(new_list);

const low = (try self.generateNode(components.low)).?;
const high = (try self.generateNode(components.high)).?;

const current = m.MIR_new_reg_op(
self.ctx,
try self.REG("current", m.MIR_T_I64),
);
self.MOV(current, low);
const reached_limit = m.MIR_new_reg_op(
self.ctx,
try self.REG("reached_limit", m.MIR_T_I64),
);
const unwrapped_low = m.MIR_new_reg_op(
self.ctx,
try self.REG("unwrapped_low", m.MIR_T_I64),
);
try self.unwrap(.Integer, low, unwrapped_low);

const unwrapped_high = m.MIR_new_reg_op(
self.ctx,
try self.REG("unwrapped_high", m.MIR_T_I64),
);
try self.unwrap(.Integer, high, unwrapped_high);

// Select increment
const is_negative_delta = m.MIR_new_reg_op(
const new_range = m.MIR_new_reg_op(
self.ctx,
try self.REG("is_negative_delta", m.MIR_T_I64),
);
self.GES(is_negative_delta, unwrapped_low, unwrapped_high);

const neg_loop_label = m.MIR_new_label(self.ctx);
self.BEQ(
m.MIR_new_label_op(self.ctx, neg_loop_label),
is_negative_delta,
m.MIR_new_uint_op(self.ctx, 1),
);

const exit_label = m.MIR_new_label(self.ctx);

const pos_loop_label = m.MIR_new_label(self.ctx);
self.append(pos_loop_label);

self.GES(reached_limit, unwrapped_low, unwrapped_high);
self.BEQ(
m.MIR_new_label_op(self.ctx, exit_label),
reached_limit,
m.MIR_new_uint_op(self.ctx, 1),
try self.REG("new_range", m.MIR_T_I64),
);

// Add new element
try self.buildExternApiCall(
.bz_listAppend,
null,
.bz_newRange,
new_range,
&[_]m.MIR_op_t{
m.MIR_new_reg_op(self.ctx, self.state.?.vm_reg.?),
new_list,
current,
(try self.generateNode(components.low)).?,
(try self.generateNode(components.high)).?,
},
);

// Increment
self.ADDS(unwrapped_low, unwrapped_low, m.MIR_new_uint_op(self.ctx, 1));
self.wrap(.Integer, unwrapped_low, current);

self.JMP(pos_loop_label);

self.append(neg_loop_label);

self.LES(reached_limit, unwrapped_low, unwrapped_high);
self.BEQ(
m.MIR_new_label_op(self.ctx, exit_label),
reached_limit,
m.MIR_new_uint_op(self.ctx, 1),
);

// Add new element
try self.buildExternApiCall(
.bz_listAppend,
null,
&[_]m.MIR_op_t{
m.MIR_new_reg_op(self.ctx, self.state.?.vm_reg.?),
new_list,
current,
},
);

// Increment
self.SUBS(unwrapped_low, unwrapped_low, m.MIR_new_uint_op(self.ctx, 1));
self.wrap(.Integer, unwrapped_low, current);

self.JMP(neg_loop_label);

self.append(exit_label);

try self.buildPop(null); // Pop list

return new_list;
return new_range;
}

fn generateMap(self: *Self, node: Ast.Node.Index) Error!?m.MIR_op_t {
Expand Down Expand Up @@ -3067,6 +2971,30 @@ fn generateDot(self: *Self, node: Ast.Node.Index) Error!?m.MIR_op_t {
}
},

.Range => {
switch (components.member_kind) {
.Call => return try self.generateCall(components.value_or_call_or_enum.Call),
else => {
const res = m.MIR_new_reg_op(
self.ctx,
try self.REG("res", m.MIR_T_I64),
);
try self.buildExternApiCall(
.bz_getRangeField,
res,
&[_]m.MIR_op_t{
m.MIR_new_reg_op(self.ctx, self.state.?.vm_reg.?),
(try self.generateNode(components.callee)).?,
m.MIR_new_uint_op(self.ctx, member_identifier),
m.MIR_new_uint_op(self.ctx, 1),
},
);

return res;
},
}
},

.Object => {
switch (components.member_kind) {
.Call => return try self.generateCall(components.value_or_call_or_enum.Call),
Expand Down Expand Up @@ -3940,6 +3868,7 @@ fn generateForEach(self: *Self, node: Ast.Node.Index) Error!?m.MIR_op_t {
.Map => o.ObjMap.cast(iterable.obj()).?.map.count() == 0,
.String => o.ObjString.cast(iterable.obj()).?.string.len == 0,
.Enum => o.ObjEnum.cast(iterable.obj()).?.cases.len == 0,
.Range => o.ObjRange.cast(iterable.obj()).?.high == o.ObjRange.cast(iterable.obj()).?.low,
else => unreachable,
}) {
return null;
Expand Down Expand Up @@ -3982,6 +3911,22 @@ fn generateForEach(self: *Self, node: Ast.Node.Index) Error!?m.MIR_op_t {
},
);

// If next key is null stop, otherwise do loop
self.BEQ(
m.MIR_new_label_op(self.ctx, out_label),
try self.LOAD(value_ptr),
m.MIR_new_uint_op(self.ctx, Value.Null.val),
);
} else if (iterable_type_def.?.def_type == .Range) {
try self.buildExternApiCall(
.bz_rangeNext,
try self.LOAD(value_ptr),
&[_]m.MIR_op_t{
iterable,
try self.LOAD(value_ptr),
},
);

// If next key is null stop, otherwise do loop
self.BEQ(
m.MIR_new_label_op(self.ctx, out_label),
Expand Down
Loading

0 comments on commit 3497e6c

Please sign in to comment.