zena:string-reader
import {…} from 'zena:string-reader';
Classes
StringReader
final class StringReader
StringReader - A cursor-based string parser that tracks byte positions.
Use StringReader for parsing to ensure slices are always at valid UTF-8 code point boundaries. Positions from mark() are safe to use with sliceBytes().
Example usage:
let r = new StringReader(input);
r.skipWhitespace();
let start = r.mark();
while (!r.isAtEnd && r.peekByte() != 34) { // 34 = '"'
r.advance(); // Move by code point (safe for Unicode)
}
let token = r.sliceFrom(start); // Safe: positions from mark()
new(source: String)
peekByte(): i32
Peek at the current byte without consuming it. Returns -1 if at end of string. Use for ASCII characters (< 128) like delimiters: { } [ ] , : " etc.
peekByteAt(offset: i32): i32
Peek at a byte at offset from current position. Returns -1 if out of bounds.
advanceByte(): i32
Consume and return the current byte. Returns -1 if at end. WARNING: Only use for ASCII (< 128). For Unicode text, use advance().
peek(): i32
Peek at the current Unicode code point without consuming it. Returns -1 if at end of string. Decodes UTF-8 sequences properly.
TODO: Use shift operators (<< 6, << 12, << 18) when available in Zena. Currently using multiplication as a workaround.
advance(): i32
Consume and return the current Unicode code point. Returns -1 if at end. Advances by the correct number of bytes (1-4 for UTF-8).
TODO: Use shift operators (<< 6, << 12, << 18) when available in Zena. Currently using multiplication as a workaround.
skip(count: i32): void
Skip N code points (not bytes). Properly handles multi-byte UTF-8 sequences.
mark(): i32
Mark the current position for later slicing. Returns an opaque position value safe to use with sliceFrom/sliceRange.
reset(pos: i32): void
Reset to a previously marked position.
sliceFrom(start: i32): String
Extract a slice from a marked position to current position. This is SAFE because both positions are at code point boundaries.
sliceRange(start: i32, end: i32): String
Extract a slice between two marked positions. This is SAFE because both positions are at code point boundaries.
matchByte(expected: i32): boolean
Check if current byte matches, and advance if it does. Returns true if matched.
skipWhitespace(): void
Skip ASCII whitespace (space, tab, newline, carriage return).
skipBytes(n: i32): void
Skip N bytes (not code points). Use with caution - only when you know the bytes are at valid boundaries.
skipBytesWhile(predicate: (byte: i32) => boolean): void
Skip bytes while predicate returns true. Predicate receives byte value (not code point). Useful for ASCII character classes: digits, letters, etc.
readUntil(needle: String): String | null
Reads a substring up to the next occurrence of needle.
If needle is found, returns the substring before it and advances the cursor past needle.
If needle is not found, returns null and does not advance the cursor.
readToEnd(): String
Reads the remaining substring from current position to the end of the string, advancing the cursor to the end.