Skip to content

Instantly share code, notes, and snippets.

@GerHobbelt
Last active July 6, 2016 21:25
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save GerHobbelt/e18f2ef8ee575d4ff49d to your computer and use it in GitHub Desktop.
Save GerHobbelt/e18f2ef8ee575d4ff49d to your computer and use it in GitHub Desktop.
JISON sample wrapper showcasing custom error handlers (stripped JavaScript, ... where more must be done)

This code is a stripped version of actual production code which employs this JISON clone:

(https://github.com/GerHobbelt/jison)

hence make sure to diff that one against vanilla to see differences/features.


code shows .yy.parseError, lexer.post_lex, parser.post_parser, how to get the token identifier strings from the JISON-generated parser class, etc.

Note that the event reg/unreg/trigger functions are quick replacements for this gist only, use your own event system here. The key is that this works, even when you fire events from user action code inside the lexer or parser itself, hence more powerful than simply hooking .yy.parseError only!

//
// This code is a stripped version of actual production code hich employs this JISON clone:
//
// https://github.com/GerHobbelt/jison
//
// hence make sure to diff that one against vanilla to see differences/features.
//
// ---
//
// code shows .yy.parseError, lexer.post_lex, parser.post_parser, how to get
// the token identifier strings from the JISON-generated parser class, etc.
//
// Note that the event reg/unreg/trigger functions are quick replacements for this gist only,
// use your own event system here. The key is that this works, even when you fire events
// from user action code inside the lexer or parser itself, hence more powerful than simply
// hooking .yy.parseError only!
//
V.P = function () {
var parser = function () {
};
// `parserWithTokens` is the 'class' generated by JISON
//
// `parser` / `V.P` is the user-defined 'wrapper class'
parser.parse = function (userInput, options) {
// the array of JISON terminal strings: used to convert parser-internal numeric IDs to human legible strings for these.
var terminals = parserWithTokens.terminals_;
options = options || {};
var catch_nasty_deep_parse_errors_f = function (d) {
// d: { message: ..., attributes }
// console.log("≈≈≈ onParseError catching deep parser errors: ", d);
// When the parser or lexer themselves already detected the error, we don't need to mark it up!
// This is for catching those DEEP errors only
if (d.parserErrorHash || d.lexerErrorHash)
return;
...
};
register_listener_for_parse_error_event(catch_nasty_deep_parse_errors_f);
// override the default parseError method! Do this every time again as it references closure variables (resultOutput) which it will patch.
parserWithTokens.yy.parseError = function (str, hash) {
//console.log("≈≈≈ parse error: ", str, hash);
if (hash.expected /* parser error? */) {
fire_parse_error_event({
message: "Parser error: " + str,
parserErrorHash: hash
});
//var faulty_str = this.lexer.upcomingInput(-1); // get all the remaining input to parse:
...
return null;
} else {
/* lexer error */
fire_parse_error_event({
message: "Lexer error: " + str,
lexerErrorHash: hash
});
assert(this.lexer.ERROR);
return this.lexer.ERROR;
}
};
// logging the tokens+types as part of each lex() run:
parserWithTokens.lexer.options.post_lex = function (token) {
//assert(this === parserWithTokens.yy.lexer);
if (!log_it && token === this.ERROR) {
/*
* consume one character of pending input:
* this is the minimum amount that we should move forward if we don't want the error to 'stick',
* for we ALWAYS want to end at the EOF token at the very end of the run.
*/
log_it = this.input();
}
if (log_it /* not EOF and match isn't simply whitespace */) {
//gives the tokens
var token_str = (terminals[token] || token);
log_token(log_it, token_str);
}
return token;
};
parserWithTokens.post_parse = function (yy, retval) {
/*
Make sure we've lexed the entire input, even when an error occurred!
We will need that when we are parsing erroneous input where we want to analyze the tokenArray anyhow...
*/
var token;
var lexer = yy.lexer;
...
do {
token = lexer.lex();
...
} while (token !== parserWithTokens.lexer.EOF);
...
return retval;
};
try {
resultOutput = parserWithTokens.parse(userInput);
...
} catch (e) {
...
fire_parse_error_event({
message: (e.name || "Error") + ": " + e.message,
exception: e
});
...
}
// make sure the handler we registered before is now detached again so any later calls cannot print their errors through /our/ lingering handler...
unregister_listener_for_parse_error_event(catch_nasty_deep_parse_errors_f);
return resultOutput;
};
return parser;
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment