261 lines
5.6 KiB
TypeScript
261 lines
5.6 KiB
TypeScript
import { BooleanExpression } from "../../expressions/BooleanExpression";
|
|
import { LexerEvent } from "./LexerEvent";
|
|
|
|
|
|
export class LexerQuery
|
|
{
|
|
source:string;
|
|
tokens:LexerEvent[];
|
|
|
|
_index:Map<LexerEvent,number> = new Map<LexerEvent,number>();
|
|
|
|
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<LexerEvent,string> )
|
|
{
|
|
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> ):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<LexerEvent>, end:BooleanExpression<LexerEvent>)
|
|
{
|
|
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<LexerEvent>, ignore?:BooleanExpression<LexerEvent>, 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<LexerEvent>, ignore?:BooleanExpression<LexerEvent>, maxItems:number = 100000 )
|
|
{
|
|
let nextIndex = this.searchIndex( index, forward, matcher, ignore );
|
|
|
|
return nextIndex === -1 ? null : this.tokens[ nextIndex ];
|
|
}
|
|
} |