import { ISourceList } from '@cafetextual/util'; import PObjectMap, { PRuleResult } from './../org/subalternproductions/seepResource/dsl/parser/PObjectMap'; import IncrementalParser from "../org/subalternproductions/seepResource/dsl/parser/IncrementalParser"; import ParseModel from "./ParseModel"; import { int } from "@cafetextual/nlist/dist/src/ntree/types"; import RuleState from '../org/subalternproductions/seepResource/dsl/parsertooling/RuleState'; import ParserDebug from '../org/subalternproductions/seepResource/dsl/parsertooling/ParserDebug'; import ParserState from '../org/subalternproductions/seepResource/dsl/parsertooling/ParserState'; import ParseStackItem from '../org/subalternproductions/seepResource/dsl/parsertooling/ParseStackItem'; import Assert from '@cafetextual/util/dist/src/assert/Assert'; import SeepParser from '../org/subalternproductions/seepResource/dsl/parser/SeepParser'; import SourceLocation from '@cafetextual/util/dist/src/source/SourceLocation'; import StackUtil from '../debugger/StackUtil'; import ParseDebugUtil from '../org/subalternproductions/seepResource/dsl/parsertooling/ParseDebugUtil'; import Show from './Show'; import IParserDebug from '../org/subalternproductions/seepResource/dsl/parsertooling/IParserDebug'; /* * Wrapper around the incremental parser wih api to manage incremental and debug parsing * * - allows invalidation of parsing at a particular line * * - stateful. * * * * */ export default class ParseMgr implements IParserDebug { // -- Result data data:Object; // -- ParseStatus ParseModel parsingState model:ParseModel ip:IncrementalParser; // -- ParseFault (ie when valid = false) isValid():boolean { return this.model && this.model.isValid(); } isDone():boolean { return this.ip && this.ip.done; } hasErrored():boolean { return this.ip && this.ip.hasErrored(); } atBreakpoint():boolean { return this.ip && this.ip.atBreakpoint(); } static create(model:ParseModel):ParseMgr { var ps:ParseMgr = new ParseMgr(); ps.model = model; return ps; } // init lastValid():int { return this.ip.getLastValidLine() } lastValidLine():int { return this.isValid() ? this.ip.getLastValidLine() : -1 } getLastInvalidated():int { return this._lastInvalidated } private _lastInvalidated:int= -1 /** * invalidate (if necessary) * * returns true if an invalidatation has been performed * (often it is unnecessary, for instance if parsing has errored before the * line being invalidated) * */ invalidate(n:int):boolean { if (this.isValid()) { if ((n != this._lastInvalidated) && (this.lastValid() < 0 && n == 0) || this.lastValid() >= n -1 ) { // <--- hardcoding source index as 0. console.log(' ---- invalidataing line: ' + n + ' ---- lastValid: ' + this.lastValid()) this._lastInvalidated = n try { this.ip.invalidateLine(n) } catch (e) { console.log(' ---- invalidation bug ----') console.log(e.getStackTrace()) console.log('x') } return true } else { console.log(' ---- no need to invalidate: ' + n + ' ---- lastValid: ' + this.lastValid()) } } return false } isStartable():boolean { return this.isValid() && (!this.ip || this.hasErrored() || this.isDone()); } stop():ParseMgr { return ParseMgr.create(this.model); } step():ParseMgr { if (!this.atBreakpoint()) { return this; } return this.start_low(true, false, 0); } // step parseLine(n:int = 1):ParseMgr { Assert.assert(!this.atBreakpoint()) return this.start_low(false, false, n) } cont(n:int = -1):ParseMgr { if (!this.atBreakpoint()) { // an unfortunate artifact return this; } return this.start_low(false, true, n); } start():ParseMgr { return this.start_low(false, false, 0); } private start_low(step:boolean, cont:boolean, maxIterations:int):ParseMgr { if (!this.isValid() || this.isDone() || this.hasErrored()) { return this; } if (!this.ip) { this.ip = new IncrementalParser; this.ip.init(new SeepParser, this.model.content, this.model.rule, this.model.enableDebug ? this.model.breakExp : null); // ip.parseLine(); } if (cont) { Assert.assert(this.ip.atBreakpoint()); this.ip.cont(); } else if (step) { Assert.assert(this.ip.atBreakpoint()); this.ip.step(this as IParserDebug); } var count:int = maxIterations while ( this.ip.inProgress() && (count < 0 || count > 0 )) { count-- this.ip.parseLine(); } // 3. create a new status object to return var ps:ParseMgr = new ParseMgr(); ps.ip = this.ip; ps.model = this.model; if (this.ip.hasErrored()) { Assert.assert(ps.hasErrored()); } else if (this.ip.done) { ps.data = ps.ip.mapdata() } else { Assert.assert(ps.atBreakpoint() || maxIterations >= 0); } return ps; } // --- map():PObjectMap { Assert.assert(this.ip.done) return this.ip.root.map } result():PRuleResult { return this.ip.root } // ---- error reporting // { line:Int, loc:SourceLocation, msg:string) errorObject():Object { Assert.assert(this.ip.hasErrored()) var lineNum:int = this.ip.erroredAt(); var errLocation:SourceLocation = this.ip.errLocation() var msg:string = this.ip.errorStr() return {line: lineNum, loc:errLocation, msg:msg} } // ---- debug stack mechanism ---- private static toSp(v:string):string { var len:int = v ? v.length : 0; var out:string = ""; for (var i:int = 0; i < len; i++) { out += " "; } return out; } // toSp parsedSoFar:string = "" private updateParsed():string { this.parsedSoFar = "---- parsed ------\n" var content:ISourceList = this.model.content; var first:int = content.first(); var last:int = this.ip.hasErrored() ? this.ip.erroredAt() : content.last(true); var i:int = first; while (i > -1 && !content.isAfter(i, last)) { var line:string = content.indexToObject(i) as string; this.parsedSoFar += line + "\n" i = content.nextLineIndex(i); } return this.parsedSoFar; } private _stateAtBreakpoint:ParserState; debug(state:ParserState):boolean { this._stateAtBreakpoint = state; if (state.breakpoint == ParserDebug.DONE || state.breakpoint == ParserDebug.DONE_WITH_UNMATCHED_TEXT) { var out:PRuleResult = (state as RuleState).ruleResult; /* model.sscope.map = out.map; pmodel.lastResult = out; andParseDone(out); pmodel.setStacktrace([]); debugContinue(); return true */ } //pmodel.debugInProgress = true; //var stack:Array = ParseDebugUtil.toStackTrace(state, null, state.breakpoint); //pmodel.setStacktrace(stack); //selectStackItem(stack.length -1); /* if (state.breakpoint | ParserDebug.ELEMENT_MATCH) { var traceStr:string = ParseDebugUtil.toStateString(state, false); pmodel.consoleMsg(" breakpoint \n" + traceStr); } */ return true; } // debug stack:Array stackItem:ParseStackItem; detail:string = "--"; setStackIndex(index:int):ParseMgr { this.stackItem = this.stack && this.stack.length > index ? this.stack[index] : null; if (this.stackItem) { this.detail = StackUtil.toDetailStr(this.stackItem.state, this.stackItem.breakpoint); } else { this.detail = null; } return this; } includeRules:boolean = false; includeElements:boolean = true; includeNamedRules:boolean = true; toDebugStack():Array { if (!this.atBreakpoint() || !this.isValid() ) { return []; } this.stack = ParseDebugUtil.toStateStack(this.ip.breakpointState(), true, "#parse-debug/"); this.stack = StackUtil.filter(this.stack, this.includeRules, this.includeElements, this.includeNamedRules); var keepStackItem:boolean = (this.stackItem && this.stack.indexOf(this.stackItem) < 0); if (!keepStackItem) { if (this.stack.length > 0){ this.setStackIndex(this.stack.length -1); } else { this.stackItem = null; this.detail = "--"; } } return this.stack } toStatus():string { if (!this.isValid()) { return "parse model not valid" + (this.model ? this.model.status() : ""); } else if (this.atBreakpoint()) { /*var o:string = "at breakpoint: \n" + updateParsed(); o += ip.currentMatch + "\n"; var rem:string = toSp(ip.currentMatch) + ip.remainder; o += rem; */ return ParseDebugUtil.toStateString(this.ip.breakpointState(), false); } else if (this.hasErrored()) { return "error: \n" + this.ip.errorStr() + "\n" + this.updateParsed(); } else if (this.isDone()) { console.log(' --- Parse OK : lastValidLine: ' + this.lastValid()) var o:Object = this.ip.mapdata() var objectDesc:string = o ? Show.serializeSimpleObject(o, "") : " no object " return "success: \n" + objectDesc + "\n" + this.updateParsed(); } return "status: unknown"; } resultObject():Object { if (this.isDone()) { return this.ip.mapdata(); } return null; } clear():void { if (this.ip) { this.ip = null // shoudl realy have a clear mechanism } this.model = null } } // class