diff --git a/src/content/_t/parse.js.jmacs b/src/content/_t/parse.js.jmacs index 0f41306..dffe08b 100644 --- a/src/content/_t/parse.js.jmacs +++ b/src/content/_t/parse.js.jmacs @@ -280,30 +280,47 @@ const OPHOP = Object.prototype.hasOwnProperty; @def error_args(gc_args={}) @//@object-literal + @.{ + let sj_index = /* syntax: js */ `this.i`; + } @if gc_args.rewind + @.{ + sj_index = /* syntax: js */ `i_caret`; + } ...(() => { - const i_token_end = this.i - /[ \t]*$/.exec(this.s.slice(0, this.i))[0].length; - const i_token_start = i_token_end - (@{gc_args.rewind}); - const i_caret = i_token_start + (@{gc_args.caret}); + @*{ + let a_rewinds = gc_args.rewind; + if(!Array.isArray(a_rewinds)) a_rewinds = [a_rewinds]; + + let sj_token_prev = /* syntax: js */ `this.i`; + const nl_rewinds = a_rewinds.length; + for(let i_rewind=0; i_rewind {}; -const F_THROW = (e_what) => { - throw e_what; +const F_THROW = (e_read) => { + throw e_read; }; class Consumer { @@ -1883,7 +1910,7 @@ class Consumer { @; // term constructors - if(this.tolerant) { + if(this._b_tolerant) { @*{yield* declare_properties({ // term constructors blank_node: /* syntax: js */ `function(s_label) { @@ -1917,7 +1944,7 @@ class Consumer { caret: /* syntax: js */ `2 + ( /[^${RANGE_PN_CHARS_U()}0-9]/u.test(s_label[0]) ? 0 - : /[^${RANGE_PN_CHARS_U()}\\-0-9\\xb7]|[^\\u{0300}-\\u{036f}\\u{203f}-\\u{2040}]/u.exec(s_label.slice(1)).index || s_label.length-1 + : /[^${RANGE_PN_CHARS_U()}\\-0-9\\xb7\\u{203f}-\\u{2040}]/u.exec(s_label)?.index || s_label.length-1 )`.replace(/[\r\n\t]+/g, ''), })} }); @@ -2095,6 +2122,25 @@ class Consumer { consume_stream(ds_input) { // go async return new Promise((fk_consumed, fe_reject) => { + // re-route error handling to promise rejection + if(F_THROW === this._fe_error) { + this._fe_error = fe_reject; + } + // wrap error handler + else { + // ref original handler + const fe_handler = this._fe_error; + + // set new handler + this._fe_error = (e_read) => { + // callback user-defined error handler + fe_handler(e_read); + + // then, reject promise + fe_reject(e_read); + }; + } + // bind data listener ds_input.on('data', (s_chunk) => { @{write_chunk()} @@ -2109,7 +2155,7 @@ class Consumer { const e_eof = this.eof(1); // reject with failure - if(e_eof) return fe_reject(e_eof); + if(e_eof) return this._fe_error(e_eof); // resolve promise fk_consumed(); diff --git a/src/content/error.mjs b/src/content/error.mjs index bb17ba4..000cf2a 100644 --- a/src/content/error.mjs +++ b/src/content/error.mjs @@ -1,61 +1,37 @@ +const HM_PRIVATES = new Map(); + export class ContentError extends Error { constructor(gc_error) { super(); - this._k_content = gc_error.content; - this._s_message = gc_error.message; - this._s_state = gc_error.state; - this._s_source = gc_error.source; - this._g_location = gc_error.location; - ({ - caret: this._i_caret, - start: this._i_token_start=-1, - end: this._i_token_end=-1, - } = gc_error.token); - - this.name = `@graphy/content.${this.title}`; - // this.name = this.title; - } - get title() { - return (this.name || this.constructor.name)+'<'+this.instanceName+'>'; - } - - get instanceName() { - return this._k_content._dc_actor.name; - } - - get message() { - return this.toString(); - } - - // get stack() { - // return this.title+'::'+this._s_message+'\n'+this.toString(); - // } - - toString() { - const i_pos = this._i_caret; + const { + caret: i_caret, + start: i_token_start=-1, + end: i_token_end=-1, + } = gc_error.token; // sets the max width of the preview const nl_width = Math.min(90, process?.stdout?.columns || 90); // 0.62 sets the relative bias to the front of the message - const i_off = Math.min(i_pos, Math.abs(i_pos-Math.floor(nl_width*0.62))); + const i_off = Math.min(i_caret, Math.abs(i_caret-Math.floor(nl_width*0.62))); // format preview - const s_preview = this._s_source.substr(i_off, nl_width).replace(/[\n\t]/g, ' '); -debugger; + const s_preview = gc_error.source.substr(i_off, nl_width).replace(/[\n\t]/g, ' '); + // border strings - const nl_pre = i_pos - i_off; + const nl_pre = i_caret - i_off; let s_border_top= ''; let s_border_btm = ''; - if(this._i_token_start >= 0) { + // token highlighting + if(i_token_start >= 0) { const a_border_top = '┈'.repeat(nl_width).split(''); const a_border_btm = [...a_border_top]; - const ir_token_start = this._i_token_start - i_off; - const ir_caret = this._i_caret - i_off; - const ir_token_end = this._i_token_end - i_off; + const ir_token_start = i_token_start - i_off; + const ir_caret = i_caret - i_off; + const ir_token_end = i_token_end - i_off; if(ir_token_start >= 0) { a_border_top[ir_token_start] = '┍'; @@ -67,12 +43,19 @@ debugger; a_border_btm[ir_token_end-1] = '┙'; } - a_border_top[ir_caret] = '┻'; - a_border_btm[ir_caret] = '┳'; + if(ir_token_start !== ir_token_end && (ir_token_start === ir_caret || ir_token_end === ir_caret)) { + a_border_top[ir_caret] = '╇'; + a_border_btm[ir_caret] = '╈'; + } + else { + a_border_top[ir_caret] = '┻'; + a_border_btm[ir_caret] = '┳'; + } s_border_top = a_border_top.join(''); s_border_btm = a_border_btm.join(''); } + // caret only else { s_border_top = '┈'.repeat(nl_pre); const s_post = '┈'.repeat(nl_width-nl_pre-1); @@ -80,24 +63,62 @@ debugger; s_border_top += '┻'+s_post; } + const g_self = { + s_instance: gc_error.content?._dc_actor?.name, + s_message: gc_error.message, + s_state: gc_error.state, + s_source: gc_error.source, + g_location: gc_error.location, + i_caret, + s_preview, + s_border_top, + s_border_btm, + }; + + HM_PRIVATES.set(this, g_self); + } + + get name() { + const { + s_instance, + s_name, + } = HM_PRIVATES.get(this); + + const s_title = (s_name || this.constructor.name)+(s_instance? '<'+s_instance+'>': ''); + return `@graphy/content.${s_title}`; + } + + get message() { + const { + g_location, + s_border_top, + s_preview, + s_border_btm, + s_message, + } = HM_PRIVATES.get(this); + return this.description+'\n' - +(this._g_location? ` at { line: ${this._g_location.line}, col: ${this._g_location.col} }`: ' to see the line/col offset, remove or disable the `swift: true` option')+'\n' + +(g_location? ` at { line: ${g_location.line}, col: ${g_location.col} }`: ' to see the line/col offset, remove or disable the `swift: true` option')+'\n' +` ${s_border_top}\n` +` ${s_preview}\n` +` ${s_border_btm}\n` - +`\n ${this._s_message}`; + +`\n ${s_message}`; } + + // get stack() { + // return this.title+'::'+this.message; + // } } export class ContentSyntaxError extends ContentError {} -ContentSyntaxError.prototype.name = 'ContentSyntaxError'; -ContentSyntaxError.prototype.description = 'A syntax error was found while reading input.'; +ContentSyntaxError.prototype.description = 'Syntax error found while reading input.'; export class UnexpectedTokenError extends ContentSyntaxError { constructor(gc_error) { super(gc_error); - const s_char = this._s_source[this._i_caret]; - this._s_message = `Expected ${this._s_state} ${gc_error.eofed? 'but encountered <>': ''}. Failed to parse a valid token starting at ${s_char? '"'+s_char+'"': '<>'}`; + const g_self = HM_PRIVATES.get(this); + const s_char = g_self.s_source[g_self.i_caret]; + g_self.s_message = `Expected ${gc_error.state} ${gc_error.eofed? 'but encountered <>': ''}. Failed to parse a valid token starting at ${s_char? '"'+s_char+'"': '<>'}`; } } @@ -107,7 +128,8 @@ InvalidStateChangeError.prototype.description = 'An invalid state change was det export class NoSuchPrefixError extends ContentError { constructor(gc_error) { super(gc_error); - this._s_message = `No such prefix '${gc_error.prefix}' was declared.`; + const g_self = HM_PRIVATES.get(this); + g_self.s_message = `No such prefix '${gc_error.prefix}' was declared.`; } } NoSuchPrefixError.description = 'Missing prefix declaration.'; @@ -115,7 +137,8 @@ NoSuchPrefixError.description = 'Missing prefix declaration.'; export class ExceededMaximumTokenLengthError extends ContentError { constructor(gc_error) { super(gc_error); - this._s_message = `The maximum token length is currently set to ${gc_error.mtl}. You can adjust this value in the parameters.`; + const g_self = HM_PRIVATES.get(this); + g_self.s_message = `The maximum token length is currently set to ${gc_error.mtl}. You can adjust this value in the parameters.`; } } ExceededMaximumTokenLengthError.prototype.description = 'Exceeded maximum token length while reading input.';