Skip to content

Commit

Permalink
feat: Key and/or value type of foreach and for loops can be infered a…
Browse files Browse the repository at this point in the history
…nd `var` can be used

closes #296
  • Loading branch information
giann committed Jun 2, 2024
1 parent 9d58c7a commit 3dabb3a
Show file tree
Hide file tree
Showing 5 changed files with 79 additions and 13 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
# Unreleased

## Added
- Enum can now have `rg`, `ud`, `void`, `pat` has value type
- Object can have `const` properties (https://github.com/buzz-language/buzz/issues/13). A object with only `const` properties is considered itself `const`. Although we don't do anything yet with that concept. https://github.com/buzz-language/buzz/issues/114 is the objective but it requires being able to build objects and instances at compile time which is not yet possible.
- `rg.subsetOf`, `rg.intersect`, `rg.union`

## Modified
- Enum can now have `rg`, `ud`, `void`, `pat` has value type
- `var` can be used for key and/or value type in `for` and `foreach` loop

## Fixed
- Type checking was not done on object instance property assignments
Expand Down
Binary file modified example.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions examples/finobacci.buzz
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ fun fibonacci(int n) > void *> int? {
var n2 = 1;
int? next = null;

foreach (int _ in 0..n) {
foreach (var _ in 0..n) {
_ = yield n1;
next = n1 + n2;
n1 = n2;
Expand All @@ -19,7 +19,7 @@ fun main([str] args) > void {
else
10;

foreach (int? n in &fibonacci(N)) {
foreach (var n in &fibonacci(N)) {
std.print("{n}");
}
}
79 changes: 72 additions & 7 deletions src/Parser.zig
Original file line number Diff line number Diff line change
Expand Up @@ -1270,6 +1270,7 @@ fn declaration(self: *Self) Error!?Ast.Node.Index {
.Semicolon,
false,
true,
false,
)
else if (try self.match(.Pat))
try self.varDeclaration(
Expand All @@ -1278,6 +1279,7 @@ fn declaration(self: *Self) Error!?Ast.Node.Index {
.Semicolon,
constant,
true,
false,
)
else if (try self.match(.Ud))
try self.varDeclaration(
Expand All @@ -1286,6 +1288,7 @@ fn declaration(self: *Self) Error!?Ast.Node.Index {
.Semicolon,
constant,
true,
false,
)
else if (try self.match(.Str))
try self.varDeclaration(
Expand All @@ -1294,6 +1297,7 @@ fn declaration(self: *Self) Error!?Ast.Node.Index {
.Semicolon,
constant,
true,
false,
)
else if (try self.match(.Int))
try self.varDeclaration(
Expand All @@ -1302,6 +1306,7 @@ fn declaration(self: *Self) Error!?Ast.Node.Index {
.Semicolon,
constant,
true,
false,
)
else if (try self.match(.Float))
try self.varDeclaration(
Expand All @@ -1310,6 +1315,7 @@ fn declaration(self: *Self) Error!?Ast.Node.Index {
.Semicolon,
constant,
true,
false,
)
else if (try self.match(.Bool))
try self.varDeclaration(
Expand All @@ -1318,6 +1324,7 @@ fn declaration(self: *Self) Error!?Ast.Node.Index {
.Semicolon,
constant,
true,
false,
)
else if (try self.match(.Range))
try self.varDeclaration(
Expand All @@ -1326,6 +1333,7 @@ fn declaration(self: *Self) Error!?Ast.Node.Index {
.Semicolon,
constant,
true,
false,
)
else if (try self.match(.Type))
try self.varDeclaration(
Expand All @@ -1334,6 +1342,7 @@ fn declaration(self: *Self) Error!?Ast.Node.Index {
.Semicolon,
constant,
true,
false,
)
else if (try self.match(.Any))
try self.varDeclaration(
Expand All @@ -1342,6 +1351,7 @@ fn declaration(self: *Self) Error!?Ast.Node.Index {
.Semicolon,
constant,
true,
false,
)
else if (self.current_token.? > 0 and self.current_token.? - 1 < self.ast.tokens.len - 1 and
self.ast.tokens.items(.tag)[self.current_token.?] == .Identifier and
Expand All @@ -1353,6 +1363,7 @@ fn declaration(self: *Self) Error!?Ast.Node.Index {
.Semicolon,
constant,
true,
false,
)
else if (try self.match(.Fib))
try self.varDeclaration(
Expand All @@ -1361,6 +1372,7 @@ fn declaration(self: *Self) Error!?Ast.Node.Index {
.Semicolon,
constant,
true,
false,
)
else if (try self.match(.Obj))
try self.varDeclaration(
Expand All @@ -1369,6 +1381,7 @@ fn declaration(self: *Self) Error!?Ast.Node.Index {
.Semicolon,
constant,
true,
false,
)
else if (try self.match(.LeftBracket))
try self.listDeclaration(constant)
Expand All @@ -1383,6 +1396,7 @@ fn declaration(self: *Self) Error!?Ast.Node.Index {
.Semicolon,
constant,
true,
false,
)
else if (global_scope and try self.match(.Import))
try self.importStatement()
Expand Down Expand Up @@ -1429,6 +1443,7 @@ fn declaration(self: *Self) Error!?Ast.Node.Index {
.Semicolon,
true,
true,
false,
);
}

Expand Down Expand Up @@ -6762,6 +6777,7 @@ fn varDeclaration(
terminator: DeclarationTerminator,
constant: bool,
should_assign: bool,
type_provided_later: bool,
) Error!Ast.Node.Index {
var var_type = if (parsed_type) |ptype|
try self.ast.nodes.items(.type_def)[ptype].?.toInstance(
Expand Down Expand Up @@ -6810,7 +6826,7 @@ fn varDeclaration(
} else {
self.current.?.locals[slot].type_def = var_type;
}
} else if (parsed_type == null) {
} else if (parsed_type == null and !type_provided_later) {
self.reporter.reportErrorAt(
.inferred_type,
self.ast.tokens.get(start_location),
Expand Down Expand Up @@ -6903,6 +6919,7 @@ fn listDeclaration(self: *Self, constant: bool) Error!Ast.Node.Index {
.Semicolon,
constant,
true,
false,
);
}

Expand All @@ -6920,6 +6937,7 @@ fn mapDeclaration(self: *Self, constant: bool) Error!Ast.Node.Index {
.Semicolon,
constant,
true,
false,
);
}

Expand Down Expand Up @@ -7928,6 +7946,7 @@ fn userVarDeclaration(self: *Self, _: bool, constant: bool) Error!Ast.Node.Index
.Semicolon,
constant,
true,
false,
);
}

Expand All @@ -7945,10 +7964,14 @@ fn forStatement(self: *Self) Error!Ast.Node.Index {
try init_declarations.append(
try self.varDeclaration(
false,
try self.parseTypeDef(null, true),
if (try self.match(.Var))
null
else
try self.parseTypeDef(null, true),
.Nothing,
false,
true,
false,
),
);

Expand Down Expand Up @@ -8026,27 +8049,37 @@ fn forEachStatement(self: *Self) Error!Ast.Node.Index {

try self.beginScope(null);

const infer_key_type = try self.match(.Var);
var key = try self.varDeclaration(
false,
try self.parseTypeDef(null, true),
if (infer_key_type)
null
else
try self.parseTypeDef(null, true),
.Nothing,
false,
false,
infer_key_type,
);

var value = if (try self.match(.Comma))
const key_omitted = !(try self.match(.Comma));
const infer_value_type = !key_omitted and try self.match(.Var);
var value = if (!key_omitted)
try self.varDeclaration(
false,
try self.parseTypeDef(null, true),
if (infer_value_type)
null
else
try self.parseTypeDef(null, true),
.Nothing,
false,
false,
infer_value_type,
)
else
null;

// If key is omitted, prepare the slot anyway
const key_omitted = value == null;
if (key_omitted) {
value = key;

Expand Down Expand Up @@ -8080,8 +8113,40 @@ fn forEachStatement(self: *Self) Error!Ast.Node.Index {
);

const iterable = try self.expression(false);
const iterable_type_def = self.ast.nodes.items(.type_def)[iterable].?;

self.current.?.locals[iterable_slot].type_def = iterable_type_def;

// Infer key/value type
if (infer_key_type and !key_omitted) {
const key_type = switch (iterable_type_def.def_type) {
.List, .String => self.gc.type_registry.int_type,
.Map => iterable_type_def.resolved_type.?.Map.key_type,
else =>
// Other type don't have key type
self.current.?.locals[self.ast.nodes.items(.components)[key].VarDeclaration.slot].type_def,
};

self.current.?.locals[iterable_slot].type_def = self.ast.nodes.items(.type_def)[iterable].?;
self.current.?.locals[self.ast.nodes.items(.components)[key].VarDeclaration.slot].type_def = key_type;
self.ast.nodes.items(.type_def)[key] = key_type;
}

if (infer_value_type or (infer_key_type and key_omitted)) {
const value_type = switch (iterable_type_def.def_type) {
.List => iterable_type_def.resolved_type.?.List.item_type,
.Range => self.gc.type_registry.int_type,
// .String => self.gc.type_registry.str_type,
.Map => iterable_type_def.resolved_type.?.Map.value_type,
.Enum => try iterable_type_def.toInstance(self.gc.allocator, &self.gc.type_registry),
.Fiber => iterable_type_def.resolved_type.?.Fiber.yield_type,
else =>
// Other type are invalid and will be caught at codegen
self.current.?.locals[self.ast.nodes.items(.components)[value.?].VarDeclaration.slot].type_def,
};

self.current.?.locals[self.ast.nodes.items(.components)[value.?].VarDeclaration.slot].type_def = value_type;
self.ast.nodes.items(.type_def)[value.?] = value_type;
}

self.markInitialized();

Expand Down
6 changes: 3 additions & 3 deletions src/builtin/range.zig
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ pub fn invert(ctx: *obj.NativeCtx) c_int {
ctx.vm.push(
Value.fromObj((ctx.vm.gc.allocateObject(
obj.ObjRange,
obj.ObjRange{
.{
.high = range.low,
.low = range.high,
},
Expand Down Expand Up @@ -93,7 +93,7 @@ pub fn intersect(ctx: *obj.NativeCtx) c_int {
ctx.vm.push(
Value.fromObj((ctx.vm.gc.allocateObject(
obj.ObjRange,
obj.ObjRange{
.{
.high = @max(
@min(rangeB.low, rangeB.high),
@min(rangeA.low, rangeA.high),
Expand All @@ -119,7 +119,7 @@ pub fn @"union"(ctx: *obj.NativeCtx) c_int {
ctx.vm.push(
Value.fromObj((ctx.vm.gc.allocateObject(
obj.ObjRange,
obj.ObjRange{
.{
.high = @min(
@min(rangeB.low, rangeB.high),
@min(rangeA.low, rangeA.high),
Expand Down

0 comments on commit 3dabb3a

Please sign in to comment.