#Creating an XML parser. How should I deal with Unicode?
1 messages · Page 1 of 1 (latest)
const State = struct {
tokens: std.ArrayList(Token) = undefined,
fn tokenize(self: *State, bytes: []u8) !void {
// Assume ASCII encoding
// Convert into codepoint then determine if the codepoint is within range.
_ = self;
var it = std.unicode.Utf8Iterator{
.bytes = bytes,
.i = 0,
};
while (it.i < it.bytes.len) {
const peek = it.peek(peekLen);
const isAscii = switch (peek.len) {
1 .. peekLen => true,
else => false,
};
if (isAscii) {
if (peek.len == 1) {
} else if ()
}
const code = try std.unicode.utf8Decode(slice);
if (!isValidCodepoint(code)) {
return error.invalidCodepoint;
}
}
};
I think the code before I did this change was cleaner.
feel free to check https://github.com/nektro/zig-xml for reference
Alright thanks.
I'll check after I give it some more thought.
while (it.nextCodepointSlice()) |slice| {
const bytes = it.peek(1);
if (bytes.len == 1 and slice.len == 1) {
// ASCII
}
}
I like this. Seems more sensible
Well I cannot peek early like this. Or else I might skip some ascii characters.
i could put the slice and the peek into a buffer and compare it the the characters i want
Might be simpler to iterate by codepoints (it.nextCodepoint) rather than slices and run the parser one character behind, so except for the first iteration, the iterator is running at your peek position and your parser is running on the previous character. To advance, shift the lookahead character to the parser's character and iterate. Keep a little bit of logic to handle the end of sequence, by filling the lookahead with the invalid unicode character as a sentinel.
isAscii is then just char <=127
Doing it this way, the focus is on your parser and anlyzing incoming characters, rather than navigating variable-length utf8 sequences.
So you have a string pool which you reference strings from?
Do you use a hashmap?
What is the extras feild in Document?
Ah I found it. Didnt know you can import variables like that
pub fn eatRangeM(ore: *Parser, comptime from: u21, comptime to: u21) !?u21 {
const from_len = comptime std.unicode.utf8CodepointSequenceLength(from) catch unreachable;
const to_len = comptime std.unicode.utf8CodepointSequenceLength(to) catch unreachable;
const amt = @max(from_len, to_len);
try ore.peekAmt(amt) orelse return null;
const len = std.unicode.utf8ByteSequenceLength(ore.buf[0]) catch return null;
if (amt != len) return null;
const mcp = std.unicode.utf8Decode(ore.buf[0..amt]) catch return null;
if (mcp >= from and mcp <= to) {
defer ore.shiftLAmt(len);
return @intCast(mcp);
}
return null;
}
Why do this line?
if (amt != len) return null;
Doesn't this exclude the codepoints which require less bytes?
This complex logistics around length of byte sequences bolsters my argument that you're better off iterating code points, rather than sequences. It's unnecessary complexity.
Maybe they have a test that shows this function in action.
sorry i didnt pitch in more, was on a flight today
if there's still any questions about it im happy to answer
@graceful quiver just the question I posted about amt != len
that check is saying that if the byte length of the next available codepoint isnt the same as the codepoint being asked for then we know its not it
Why get the max byte length of the start and end range though?
there's likely a very subtle bug here if from_len and to_len arent the same
you're right in that it should check both if that assumption isnt true
Neat. Just checking because it didn't make sense to me. Otherwise reading your code has been helpfull.
UTF8 is made in such away that you can make apart ascii characters by just looking at individual bytes. Bytes with the highest bit being 0 can't be part of a multibyte sequence. If you just need to process ascii bytes, you don't really need an iterator
indeed, given that all the defined keywords and tokens in xml are contained entirely within ASCII encoding, you can effectively just go off of direct ASCII comparisons, and defer unicode verification to a later step
if you're outside a tag, just seek until the next & or <
if you're inside a tag, seek to the next non-whitespace character