Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Key and/or value type of foreach and for loops can be infered and var can be used #297

Merged
merged 1 commit into from
Jun 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading