Pull parsers can deal with arbitrarily large messages too. And they also do one pass over the input data.
Yeah if you're unfamiliar, SAX is like this (pseudocode):
interface SAXCallbacks {
void onBeginToken(string name);
void onAttribute(string key, string val);
void onText(string text);
void onEndToken(string name);
}
void parse(Reader input, SAXCallbacks yourCallbackImplementations);
Whereas pull parsers are like this:
enum Token {
Begin(string name),
Attribute(string key, string val),
Text(string text),
End(string name),
}
class PullParser {
void open(Reader input);
Token next();
}
They are
much easier to use because you can trivially write a recursive descent parser:
void parseThing(parser) {
let token = parser.next();
if (token == Begin("foo")) {
parseFoo(parser);
} else if ...
Whereas with SAX you're going to end up with some monstrous hand-coded state machine like
class ThingParser {
enum State {
ParsingThing,
ParsingFoo,
ParsingFooExpectingAttributes,
ParsingFooExpectingEndTag,
...
So painful. Honestly it's so obviously the right way to do tokenisation and parsing that I have yet to see another language that even has names for them. They all just use pull parsers. Nobody else does callback-based parsing like SAX because it's obviously ridiculous.