import IParserDebug from "./IParserDebug"; import ParserState from "./ParserState"; import { PRuleResult } from "../parser/PObjectMap"; import Assert from "@cafetextual/util/dist/src/assert/Assert"; export default class ParserDebug implements IParserDebug { // TODO - rewrite constante in shift notation ... ::: --> static toBreakStr(br:number):string { switch (br) { case ParserDebug.RULE_BEFORE: return "rule(before)" case ParserDebug.ELEMENT_BEFORE: return "element(before)" case ParserDebug.ELEMENT_FAIL: return "element(fail)" case ParserDebug.ELEMENT_SKIP: return "element(skip)" case ParserDebug.ELEMENT_MATCH: return "element(match)" case ParserDebug.RULE_FAIL: return "rule(fail)" case ParserDebug.RULE_MATCH: return "rule(match)" case ParserDebug.LINE_RULE_BEFORE: return "line-rule(before)" case ParserDebug.LINE_ELEMENT_SKIP: return "line-element(skip)" case ParserDebug.LINE_RULE_FAIL: return "line-rule(fail)" case ParserDebug.LINE_RULE_MATCH: return "line-rule(match)" case ParserDebug.LINE_AFTER: return "line(after)" case ParserDebug.DONE: return "done" case ParserDebug.ELEMENT_LEAF_POST: return "element-leaf(after)" case ParserDebug.PARENT_LINE_DONE: return "parent-line(done)" case ParserDebug.SECONDARY_PRE_LINE_DONE: return "secondary-preline(done)" case ParserDebug.SECONDARY_LINE_DONE: return "secondary-line(done)" case ParserDebug.LINE_RULE_SKIP: return "line-rule(skip)" case ParserDebug.DONE_WITH_UNMATCHED_TEXT: return "done-with-unmatched-text" case ParserDebug.RULE_CUTPOINT_FAIL: return "rule(cutpoint-fail)" case ParserDebug.PARENT_LINE_FAIL: return "parent-line(fail)" } Assert.fail(); return "unknown" } // Q: do we really need these before states static RULE_BEFORE:number = 1; static ELEMENT_BEFORE:number = 2; static ELEMENT_FAIL:number = 4; static ELEMENT_SKIP:number = 8; static ELEMENT_MATCH:number = 16; static RULE_FAIL:number = 32; static RULE_MATCH:number = 64; //static :number = 128; static LINE_RULE_BEFORE:number = 256; static LINE_ELEMENT_SKIP:number = 512; static LINE_RULE_FAIL:number = 1024; // includes non-child rules (ie top level) static LINE_RULE_MATCH:number = 2048; static LINE_AFTER:number = ParserDebug.LINE_ELEMENT_SKIP | ParserDebug.LINE_RULE_FAIL | ParserDebug.LINE_RULE_MATCH; static DONE:number = 4096; static ELEMENT_LEAF_POST:number = 4096*2; static PARENT_LINE_DONE:number = 4096*4; // called when a line + all of its child rules are done static SECONDARY_PRE_LINE_DONE:number = 4096*8; static SECONDARY_LINE_DONE:number = 4096*32; static LINE_RULE_SKIP:number = 4096*64; static DONE_WITH_UNMATCHED_TEXT:number = 4096*128; static RULE_CUTPOINT_FAIL:number = 4096*128*2; static PARENT_LINE_FAIL:number = 4096*128*4; debugger:IParserDebug; enabled:boolean = true; // some useful filterings static ALL:number = ParserDebug.RULE_BEFORE | ParserDebug.ELEMENT_BEFORE | ParserDebug.ELEMENT_FAIL | ParserDebug.ELEMENT_SKIP | ParserDebug.ELEMENT_MATCH | ParserDebug.RULE_FAIL | ParserDebug.RULE_MATCH; static NONE:number = 0; static ELEMENT_AFTER:number = ParserDebug.ELEMENT_MATCH | ParserDebug.ELEMENT_FAIL | ParserDebug.ELEMENT_SKIP; filter:number = ParserDebug.ALL; static RULES:number = ParserDebug.RULE_BEFORE | ParserDebug.RULE_FAIL | ParserDebug.RULE_MATCH; static RULE_AFTER:number = ParserDebug.RULE_FAIL | ParserDebug.RULE_MATCH; // paused state paused:boolean = false; private _pausedState:ParserState; private _doneCallback:Function; private _pauseBreakpoint:number; debug(state:ParserState):boolean { // filter Assert.assert(!this.paused); if (!this.enabled || !(this.filter & state.breakpoint)) { return false; } switch (state.breakpoint) { case ParserDebug.RULE_BEFORE: case ParserDebug.ELEMENT_FAIL: case ParserDebug.ELEMENT_MATCH: case ParserDebug.ELEMENT_SKIP: case ParserDebug.RULE_FAIL: case ParserDebug.RULE_MATCH: case ParserDebug.ELEMENT_BEFORE: case ParserDebug.DONE: if (this.debugger) { this.paused = true; this._doneCallback = state.continueFn; // swapping this._pausedState = state; this._pauseBreakpoint = state.breakpoint; state.continueFn = this.continueCallback; // TODO - here we're basically decorating the callback // to inject debugger functionality. Unfortunately this involves // actually altering the state this.debugger.debug(state); return true; } break; } return false; } // reportState private continueCallback(state:ParserState):void { Assert.assert(this.paused == true); Assert.assert(state == this._pausedState); var lastState:ParserState = this._pausedState; var lastDoneCallback:Function = this._doneCallback; if (this._doneCallback == null) { Assert.assert(this._pauseBreakpoint == ParserDebug.DONE); // the only reason we wouldn't have a callback } // 1. clear pause state (when invoke contine in the parser, it may synchronusly invoke another breakpoint) this.clear(); // 2. continue with parsing if (lastDoneCallback != null) { lastDoneCallback(lastState); } } // continueCallback clear():void { this.paused = false; this._pausedState = null; this._pauseBreakpoint = -1; this._doneCallback = null; this.result = null; } result:PRuleResult; breakPoints:Array; } // class