feature. See also
. The project being documented here (as the example) is the Zig library itself.
macos.SystemVersionTokenizer
const SystemVersionTokenizer = struct
File
Code
const SystemVersionTokenizer = struct {
bytes: []const u8,
index: usize = 0,
state: State = .begin,
fn next(self: *@This()) !?Token {
var mark: usize = self.index;
var tag = Tag{};
var content: []const u8 = "";
while (self.index < self.bytes.len) {
const char = self.bytes[self.index];
switch (self.state) {
.begin => switch (char) {
'<' => {
self.state = .tag0;
self.index += 1;
tag = Tag{};
mark = self.index;
},
'>' => {
return error.BadToken;
},
else => {
self.state = .content;
content = "";
mark = self.index;
},
},
.tag0 => switch (char) {
'<' => {
return error.BadToken;
},
'>' => {
self.state = .begin;
self.index += 1;
tag.name = self.bytes[mark..self.index];
return Token{ .tag = tag };
},
'"' => {
self.state = .tag_string;
self.index += 1;
},
'/' => {
self.state = .tag0_end_or_empty;
self.index += 1;
},
'A'...'Z', 'a'...'z' => {
self.state = .tagN;
tag.kind = .start;
self.index += 1;
},
else => {
self.state = .tagN;
self.index += 1;
},
},
.tag0_end_or_empty => switch (char) {
'<' => {
return error.BadToken;
},
'>' => {
self.state = .begin;
tag.kind = .empty;
tag.name = self.bytes[self.index..self.index];
self.index += 1;
return Token{ .tag = tag };
},
else => {
self.state = .tagN;
tag.kind = .end;
mark = self.index;
self.index += 1;
},
},
.tagN => switch (char) {
'<' => {
return error.BadToken;
},
'>' => {
self.state = .begin;
tag.name = self.bytes[mark..self.index];
self.index += 1;
return Token{ .tag = tag };
},
'"' => {
self.state = .tag_string;
self.index += 1;
},
'/' => {
self.state = .tagN_end;
tag.kind = .end;
self.index += 1;
},
else => {
self.index += 1;
},
},
.tagN_end => switch (char) {
'>' => {
self.state = .begin;
tag.name = self.bytes[mark..self.index];
self.index += 1;
return Token{ .tag = tag };
},
else => {
return error.BadToken;
},
},
.tag_string => switch (char) {
'"' => {
self.state = .tagN;
self.index += 1;
},
else => {
self.index += 1;
},
},
.content => switch (char) {
'<' => {
self.state = .tag0;
content = self.bytes[mark..self.index];
self.index += 1;
tag = Tag{};
mark = self.index;
return Token{ .content = content };
},
'>' => {
return error.BadToken;
},
else => {
self.index += 1;
},
},
}
}
return null;
}
fn expectContent(self: *@This()) ![]const u8 {
if (try self.next()) |tok| {
switch (tok) {
.content => |content| {
return content;
},
else => {},
}
}
return error.UnexpectedToken;
}
fn skipUntilTag(self: *@This(), kind: Tag.Kind, name: []const u8) !void {
while (try self.next()) |tok| {
switch (tok) {
.tag => |tag| {
if (tag.kind == kind and mem.eql(u8, tag.name, name)) return;
},
else => {},
}
}
return error.TagNotFound;
}
const State = enum {
begin,
tag0,
tag0_end_or_empty,
tagN,
tagN_end,
tag_string,
content,
};
const Token = union(enum) {
tag: Tag,
content: []const u8,
};
const Tag = struct {
kind: Kind = .unknown,
name: []const u8 = "",
const Kind = enum { unknown, start, end, empty };
};
}