import { BooleanExpression } from "../../expressions/BooleanExpression"; import { LexerEvent } from "./LexerEvent"; export class LexerQuery { source:string; tokens:LexerEvent[]; _index:Map = new Map(); createTokenIndex() { for ( let i = 0; i < this.tokens.length; i++ ) { this.tokens[ i ].getMatch( this.source ); this._index.set( this.tokens[ i ], i ); } } createReplacedOutput( replacements:Map ) { let output = []; for ( let i = 0; i < this.tokens.length; i++ ) { let token = this.tokens[ i ]; if ( token.isDone || token.isError ) { continue; } if ( replacements.has( token ) ) { output.push( replacements.get( token ) ); } else { output.push( token.match ); } } return output.join( "" ); } linePosition( offset:number ) { let lineIndex = 1; let lastLineBreak = 0; for ( let i = 0; i < offset; i++ ) { if ( this.source[ i ] === "\n" || this.source === "\r" ) { if ( this.source[ i ] === "\n" && this.source[ i + 1 ] === "\r" ) { i++ } lineIndex++; lastLineBreak = i; } } let characterIndex = offset - lastLineBreak; return { line: lineIndex, characterIndex: characterIndex }; } lineInfo( l:LexerEvent, breaksBefore:number=2, breaksAfter:number=2 ) { let startIndex = l.offset - 1; let endIndex = l.offset + 1; let numLineBreakBeforeFound = 0; for ( let i = startIndex; i >= 0 && numLineBreakBeforeFound < breaksBefore; i-- ) { if ( this.source[ i ] === "\n" || this.source[ i ] === "\r" ) { if ( this.source[ i ] === "\n" && i > 0 && this.source[ i - 1 ] === "\r" ) { i--; } numLineBreakBeforeFound ++; } else { startIndex = i; } } let numLineBreakAfterFound = 0; for ( let i = startIndex; i >= 0 && numLineBreakAfterFound < breaksAfter; i++ ) { if ( this.source[ i ] === "\n" || this.source[ i ] === "\r" ) { if ( this.source[ i ] === "\r" && i < ( this.source.length - 1 ) && this.source[ i + 1 ] === "\n" ) { i++; } numLineBreakAfterFound ++; } else { endIndex = i; } } let info = this.source.substring( startIndex, endIndex ); return info; } index( l:LexerEvent ) { return this._index.get( l ); } all( matcher:BooleanExpression ):LexerEvent[] { let output:LexerEvent[] = []; for ( let i = 0; i < this.tokens.length; i++ ) { if ( matcher.evaluate( this.tokens[ i ] ) ) { output.push( this.tokens[ i ] ); } } return output; } forAllMatches( index:number, end:number, callback:(s:string)=>void) { for ( let i = index; i <= end; i++ ) { callback( this.tokens[ i ].match ); } } forAllWithType( type:string, callback:( t:LexerEvent )=>void ) { for ( let i =0; i < this.tokens.length; i++ ) { if ( ! this.tokens[ i ].isType( type ) ) { continue; } callback( this.tokens[ i ] ); } } searchBlockIndices( index:number, start:BooleanExpression, end:BooleanExpression) { let startIndex = -1; let numOpen = 0; let blocksFound = false; for ( let i = index; i < this.tokens.length; i++ ) { let token = this.tokens[ i ]; if ( start.evaluate( token ) ) { if ( ! blocksFound ) { startIndex = i; blocksFound = true; } numOpen ++; } if ( end.evaluate( token ) ) { numOpen --; if ( numOpen < 0 ) { return null; } if ( numOpen === 0 ) { return { startIndex, endIndex: i, length: ( 1 + i - startIndex ) }; } } } return null; } searchIndex( index:number, forward:boolean, matcher:BooleanExpression, ignore?:BooleanExpression, maxItems:number=100000 ) { let itemCount = 0; if ( forward ) { let next = index + 1; for ( let i = next; i < this.tokens.length && itemCount < maxItems; i++, itemCount++ ) { let token = this.tokens[ i ]; if ( ignore && ignore.evaluate( token ) ) { continue; } if ( matcher.evaluate( token ) ) { return i; } itemCount++; } } else { let previous = index - 1; for ( let i = previous; i >=0 && itemCount < maxItems; i--, itemCount++ ) { let token = this.tokens[ i ]; if ( ignore && ignore.evaluate( token ) ) { continue; } if ( matcher.evaluate( token ) ) { return i; } itemCount++; } } return -1; } searchItem( index:number, forward:boolean, matcher:BooleanExpression, ignore?:BooleanExpression, maxItems:number = 100000 ) { let nextIndex = this.searchIndex( index, forward, matcher, ignore ); return nextIndex === -1 ? null : this.tokens[ nextIndex ]; } }