Skip to content
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
Expand Up @@ -13,14 +13,15 @@
- `rg.contains`
- `map.hasKey`
- `toml` std lib
- `match` statement/expression for value matching with type-specific condition semantics (https://github.com/buzz-language/buzz/issues/80)

## Changed

- Extern libraries now must expose only one function which will be called by the compiler to lookup the functions of the library
- `int` are now `i48` instead of `i32` (https://github.com/buzz-language/buzz/issues/306). If you're wondering why, it's because all buzz values live in a NaN boxed f64 and the maximum bits available for an integer in there is 48. However, C ABI does not understand `i48` so we're still stuck with `i32` in FFI for now.
- `main` signature can omit `args` argument
- Maximum number of enum cases is now 16 777 215 instead of 255
- `pattern.match` returns now a list of `obj{ start: int, end: int, capture: str }` and `matchAll` a list of those lists
- `pattern.matchAgainst` returns now a list of `obj{ start: int, end: int, capture: str }` and `matchAllAgainst` a list of those lists
- Selective import erases the imported namespace: `import print from "std"; ... print("hello world");`
- Common part of imported namespace gets erased: il imported file as namesapce `a\b\c` and importing script has namespace `a\b`, only `c\` remains
- Enum name can be omitted if it can be inferred (`final list: [Locale] = [ .fr, .it, .en ]`) (https://github.com/buzz-language/buzz/issues/360)
Expand Down
111 changes: 109 additions & 2 deletions src/Ast.zig
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,14 @@ pub const Slice = struct {
},
.Block => try node_queue.appendSlice(allocator, comp.Block),
.BlockExpression => try node_queue.appendSlice(allocator, comp.BlockExpression),
.Match => {
try node_queue.append(allocator, comp.Match.value);

for (comp.Match.branches) |branch| {
try node_queue.appendSlice(allocator, branch.conditions);
try node_queue.append(allocator, branch.expression);
}
},
.Call => {
// Avoid loop between Call and Dot nodes
if (tags[comp.Call.callee] != .Dot or
Expand Down Expand Up @@ -424,6 +432,31 @@ pub const Slice = struct {
return true;
}
},
.Match => {
const components = ast.nodes.items(.components)[node].Match;
const type_defs = ast.nodes.items(.type_def);
const value_type_def = type_defs[components.value].?;

if (components.is_statement) {
self.result = false;
return true;
}

for (components.branches) |branch| {
for (branch.conditions) |condition| {
const condition_type_def = type_defs[condition].?;

if ((!condition_type_def.optional and condition_type_def.def_type == .Pattern and
value_type_def.def_type == .String and !value_type_def.optional) or
(!condition_type_def.optional and condition_type_def.def_type == .String and
value_type_def.def_type == .Pattern and !value_type_def.optional))
{
self.result = false;
return true;
}
}
}
},
.List => {
const components = ast.nodes.items(.components)[node].List;
const node_types = ast.nodes.items(.tag);
Expand Down Expand Up @@ -801,6 +834,43 @@ pub const Slice = struct {
}
}

fn matchConditionValue(
self: Self.Slice,
gc: *GC,
value_type_def: *obj.ObjTypeDef,
match_value: Value,
condition: Node.Index,
) !bool {
const condition_type_def = self.nodes.items(.type_def)[condition].?;
const condition_value = try self.toValue(condition, gc);

if (!condition_type_def.optional and condition_type_def.def_type == .Range and
(value_type_def.def_type == .Integer or value_type_def.def_type == .Double) and
!value_type_def.optional)
{
const range = obj.ObjRange.cast(condition_value.obj()).?;
const number = if (match_value.isInteger())
@as(v.Double, @floatFromInt(match_value.integer()))
else
match_value.double();
const low: v.Double = @floatFromInt(range.low);
const high: v.Double = @floatFromInt(range.high);

return (high >= low and number >= low and number < high) or
(low >= high and number >= high and number < low);
} else if (!condition_type_def.optional and condition_type_def.def_type == .Type and
(value_type_def.optional or value_type_def.def_type != .Type))
{
return condition_value.is(match_value);
} else if (!value_type_def.optional and value_type_def.def_type == .Type and
(condition_type_def.optional or condition_type_def.def_type != .Type))
{
return match_value.is(condition_value);
} else {
return match_value.eql(condition_value);
}
}

pub fn typeCheckAndToValue(
self: Self.Slice,
node: Node.Index,
Expand Down Expand Up @@ -913,6 +983,29 @@ pub const Slice = struct {
else
try self.toValue(if_components.else_branch.?, gc);
},
.Match => match: {
const match_components = components[node].Match;
const value_type_def = self.nodes.items(.type_def)[match_components.value].?;
const match_value = try self.toValue(match_components.value, gc);

for (match_components.branches) |branch| {
for (branch.conditions) |condition| {
if (try self.matchConditionValue(
gc,
value_type_def,
match_value,
condition,
)) {
break :match try self.toValue(branch.expression, gc);
}
}
}

break :match if (match_components.else_branch) |else_branch|
try self.toValue(else_branch, gc)
else
Value.Void;
},
.Range => range: {
const rg_components = components[node].Range;

Expand Down Expand Up @@ -1180,6 +1273,7 @@ pub const Node = struct {
ListType,
Map,
MapType,
Match,
Namespace,
NamedVariable,
Null,
Expand Down Expand Up @@ -1257,6 +1351,7 @@ pub const Node = struct {
ListType: Node.Index,
Map: Map,
MapType: MapType,
Match: Match,
Namespace: []const TokenIndex,
NamedVariable: NamedVariable,
Null: void,
Expand Down Expand Up @@ -1450,7 +1545,7 @@ pub const AnonymousEnumCase = struct {
pub const Binary = struct {
left: Node.Index,
right: Node.Index,
operator: Token.Type,
operator: Token.Tag,
};

pub const BreakContinue = struct {
Expand Down Expand Up @@ -1621,6 +1716,18 @@ pub const If = struct {
is_statement: bool,
};

pub const Match = struct {
is_statement: bool,
value: Node.Index,
branches: []const Branch,
else_branch: ?Node.Index,

pub const Branch = struct {
conditions: []const Node.Index,
expression: Node.Index,
};
};

pub const Import = struct {
imported_symbols: []const TokenIndex,
prefix: ?[]const TokenIndex,
Expand Down Expand Up @@ -1753,7 +1860,7 @@ pub const Try = struct {
};

pub const Unary = struct {
operator: Token.Type,
operator: Token.Tag,
expression: Node.Index,
};

Expand Down
Loading
Loading