added better error reporting and exceptions...

Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
This commit is contained in:
Alex A. Naanou 2020-07-28 01:26:42 +03:00
parent 241c259da0
commit 09be10556f
4 changed files with 78 additions and 13 deletions

View File

@ -54,6 +54,8 @@ This code is an evolution of that parser.
- [Contents](#contents) - [Contents](#contents)
- [Installation](#installation) - [Installation](#installation)
- [Basic usage](#basic-usage) - [Basic usage](#basic-usage)
- [Error reporting](#error-reporting)
- [XXX add subsections by task](#xxx-add-subsections-by-task)
- [Configuration](#configuration) - [Configuration](#configuration)
- [Option/command configuration](#optioncommand-configuration) - [Option/command configuration](#optioncommand-configuration)
- [`<option>.handler(..)`](#optionhandler) - [`<option>.handler(..)`](#optionhandler)
@ -83,10 +85,11 @@ This code is an evolution of that parser.
- [Nested parsers](#nested-parsers) - [Nested parsers](#nested-parsers)
- [Components and API](#components-and-api) - [Components and API](#components-and-api)
- [`THEN`, `STOP` and `ERROR`](#then-stop-and-error) - [`THEN`, `STOP` and `ERROR`](#then-stop-and-error)
- [`ParserError(..)`](#parsererror)
- [`Parser(..)`](#parser) - [`Parser(..)`](#parser)
- [`<parser>.then(..)`](#parserthen) - [`<parser>.then(..)`](#parserthen)
- [`<parser>.stop(..)`](#parserstop) - [`<parser>.stop(..)`](#parserstop)
- [`<parser>.error(..)`](#parsererror) - [`<parser>.error(..)`](#parsererror-1)
- [`<parser>.off(..)`](#parseroff) - [`<parser>.off(..)`](#parseroff)
- [`<parser>(..)`](#parser-1) - [`<parser>(..)`](#parser-1)
- [Advanced parser API](#advanced-parser-api) - [Advanced parser API](#advanced-parser-api)
@ -228,6 +231,17 @@ $ ./script.js -fb
``` ```
## Error reporting
XXX
## XXX add subsections by task
XXX
XXX might be a good idea to split out the rest to a INDETAIL.md or similar...
## Configuration ## Configuration
``` ```
@ -687,6 +701,17 @@ Values that if returned by option/command handlers can control the parse flow.
- `ERROR` &ndash; Stop parsing, call `<parser>.error(..)` callbacks and - `ERROR` &ndash; Stop parsing, call `<parser>.error(..)` callbacks and
exit with an error. exit with an error.
### `ParserError(..)`
A base error constructor, if an instance of `ParseError` is thrown by the
handler it has the same effect as returning `ERROR` with one difference being
that the error `.name`/`.message` will get printed.
The following error constructors are also defined:
- `ParserTypeError(..)`
- `ParserValueError(..)`
### `Parser(..)` ### `Parser(..)`
Construct a parser instance Construct a parser instance

49
argv.js
View File

@ -37,6 +37,18 @@ module.ERROR =
{doc: 'option processing error, triggers .error(..) handlers'} {doc: 'option processing error, triggers .error(..) handlers'}
module.ParserError =
object.Constructor('ParserError', Error, {
// NOTE: I do not get why JavaScript's Error implements this
// statically...
get name(){
return this.constructor.name }, })
module.ParserTypeError =
object.Constructor('ParserTypeError', module.ParserError, {})
module.ParserValueError =
object.Constructor('ParserValueError', module.ParserError, {})
//--------------------------------------------------------------------- //---------------------------------------------------------------------
// Helpers... // Helpers...
@ -215,20 +227,18 @@ function(name, pre, post){
// will think about it when we need this feature more than once... // will think about it when we need this feature more than once...
// //
// XXX should type handlers produce errors??? // XXX should type handlers produce errors???
// XXX add support for ParserError exception handling...
// XXX should -help work for any command? ..not just nested parsers? // XXX should -help work for any command? ..not just nested parsers?
// ...should we indicate which thinks have more "-help"?? // ...should we indicate which thinks have more "-help"??
var Parser = var Parser =
module.Parser = module.Parser =
object.Constructor('Parser', { object.Constructor('Parser', {
// Signature:
// //
// handler(value, ...options) // handler(value, ...options)
// -> value // -> value
// //
// NOTE: options are passed to the definition in the option handler,
// i.e. the list of values separated by '|' after the type
// definition.
typeHandlers: { typeHandlers: {
string: function(v){ return v.toString() }, string: function(v){ return v.toString() },
bool: function(v){ return !!v }, bool: function(v){ return !!v },
@ -242,11 +252,11 @@ object.Constructor('Parser', {
.map(function(e){ return e.trim() }) }, .map(function(e){ return e.trim() }) },
}, },
// Signature: //
//
// handler(value, stored_value, key, ...options) // handler(value, stored_value, key, ...options)
// -> stored_value // -> stored_value
// //
// For more info see docs for .typeHandlers
valueCollectors: { valueCollectors: {
// format: 'string' | 'string|<separator>' // format: 'string' | 'string|<separator>'
string: function(v, cur, _, sep){ string: function(v, cur, _, sep){
@ -290,7 +300,6 @@ object.Constructor('Parser', {
// ... // ...
// ] // ]
// //
// XXX do we need to output <doc> here???
options: function(...prefix){ options: function(...prefix){
var that = this var that = this
prefix = prefix.length == 0 ? prefix = prefix.length == 0 ?
@ -824,6 +833,9 @@ object.Constructor('Parser', {
// helpers... // helpers...
var handleError = function(reason, arg, rest){ var handleError = function(reason, arg, rest){
reason = reason instanceof Error ?
[reason.name, reason.message].join(': ')
: reason
parsed.error(reason, arg, rest) parsed.error(reason, arg, rest)
parsed.handleErrorExit parsed.handleErrorExit
&& parsed.handleErrorExit(arg, reason) } && parsed.handleErrorExit(arg, reason) }
@ -851,11 +863,24 @@ object.Constructor('Parser', {
parsed.printError('value missing:', arg+'=?') parsed.printError('value missing:', arg+'=?')
return module.ERROR } return module.ERROR }
// run handler... try {
var res = parsed.handle(handler, rest, arg, value) // run handler...
var res = parsed.handle(handler, rest, arg, value)
} catch(err){
// re-throw the error...
// NOTE: do not like that this can mask the location of
// the original error.
if(!(err instanceof module.ParserError)){
throw err }
res = err }
// NOTE: we also need to handle the errors passed to us from
// nested parsers...
res === module.STOP res === module.STOP
&& parsed.stop(arg, rest) && parsed.stop(arg, rest)
res instanceof module.ParserError
&& handleError(res, arg, rest)
res === module.ERROR res === module.ERROR
&& handleError('unknown', arg, rest) && handleError('unknown', arg, rest)
return res } return res }
@ -924,7 +949,9 @@ object.Constructor('Parser', {
var res = runHandler(handler, arg, rest) var res = runHandler(handler, arg, rest)
// handle stop conditions... // handle stop conditions...
if(res === module.STOP || res === module.ERROR){ if(res === module.STOP
|| res === module.ERROR
|| res instanceof module.ParserError){
return nested ? return nested ?
res res
: parsed } : parsed }

View File

@ -1,6 +1,6 @@
{ {
"name": "ig-argv", "name": "ig-argv",
"version": "2.4.2", "version": "2.5.0",
"description": "simple argv parser", "description": "simple argv parser",
"main": "argv.js", "main": "argv.js",
"scripts": { "scripts": {

13
test.js
View File

@ -17,6 +17,10 @@ var argv = require('./argv')
//--------------------------------------------------------------------- //---------------------------------------------------------------------
argv.Parser.typeHandlers.error = function(){
throw new argv.ParserTypeError('type error') }
var p = var p =
module.p = module.p =
argv.Parser({ argv.Parser({
@ -79,6 +83,15 @@ argv.Parser({
'-sh': { '-sh': {
doc: 'short option', }, doc: 'short option', },
'-type-error': {
doc: 'throw an type error',
type: 'error',
},
'-error': {
doc: 'throw an error',
handler: function(){
throw argv.ParserError('error') }},
'-test': argv.Parser({ '-test': argv.Parser({
env: 'TEST', env: 'TEST',