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)
- [Installation](#installation)
- [Basic usage](#basic-usage)
- [Error reporting](#error-reporting)
- [XXX add subsections by task](#xxx-add-subsections-by-task)
- [Configuration](#configuration)
- [Option/command configuration](#optioncommand-configuration)
- [`<option>.handler(..)`](#optionhandler)
@ -83,10 +85,11 @@ This code is an evolution of that parser.
- [Nested parsers](#nested-parsers)
- [Components and API](#components-and-api)
- [`THEN`, `STOP` and `ERROR`](#then-stop-and-error)
- [`ParserError(..)`](#parsererror)
- [`Parser(..)`](#parser)
- [`<parser>.then(..)`](#parserthen)
- [`<parser>.stop(..)`](#parserstop)
- [`<parser>.error(..)`](#parsererror)
- [`<parser>.error(..)`](#parsererror-1)
- [`<parser>.off(..)`](#parseroff)
- [`<parser>(..)`](#parser-1)
- [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
```
@ -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
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(..)`
Construct a parser instance

49
argv.js
View File

@ -37,6 +37,18 @@ module.ERROR =
{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...
@ -215,20 +227,18 @@ function(name, pre, post){
// will think about it when we need this feature more than once...
//
// XXX should type handlers produce errors???
// XXX add support for ParserError exception handling...
// XXX should -help work for any command? ..not just nested parsers?
// ...should we indicate which thinks have more "-help"??
var Parser =
module.Parser =
object.Constructor('Parser', {
// Signature:
//
// handler(value, ...options)
// -> 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: {
string: function(v){ return v.toString() },
bool: function(v){ return !!v },
@ -242,11 +252,11 @@ object.Constructor('Parser', {
.map(function(e){ return e.trim() }) },
},
// Signature:
//
//
// handler(value, stored_value, key, ...options)
// -> stored_value
//
// For more info see docs for .typeHandlers
valueCollectors: {
// format: 'string' | 'string|<separator>'
string: function(v, cur, _, sep){
@ -290,7 +300,6 @@ object.Constructor('Parser', {
// ...
// ]
//
// XXX do we need to output <doc> here???
options: function(...prefix){
var that = this
prefix = prefix.length == 0 ?
@ -824,6 +833,9 @@ object.Constructor('Parser', {
// helpers...
var handleError = function(reason, arg, rest){
reason = reason instanceof Error ?
[reason.name, reason.message].join(': ')
: reason
parsed.error(reason, arg, rest)
parsed.handleErrorExit
&& parsed.handleErrorExit(arg, reason) }
@ -851,11 +863,24 @@ object.Constructor('Parser', {
parsed.printError('value missing:', arg+'=?')
return module.ERROR }
// run handler...
var res = parsed.handle(handler, rest, arg, value)
try {
// 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
&& parsed.stop(arg, rest)
res instanceof module.ParserError
&& handleError(res, arg, rest)
res === module.ERROR
&& handleError('unknown', arg, rest)
return res }
@ -924,7 +949,9 @@ object.Constructor('Parser', {
var res = runHandler(handler, arg, rest)
// handle stop conditions...
if(res === module.STOP || res === module.ERROR){
if(res === module.STOP
|| res === module.ERROR
|| res instanceof module.ParserError){
return nested ?
res
: parsed }

View File

@ -1,6 +1,6 @@
{
"name": "ig-argv",
"version": "2.4.2",
"version": "2.5.0",
"description": "simple argv parser",
"main": "argv.js",
"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 =
module.p =
argv.Parser({
@ -79,6 +83,15 @@ argv.Parser({
'-sh': {
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({
env: 'TEST',