library-ts/browser/text/lexer/LexerQuery.ts

261 lines
5.6 KiB
TypeScript
Raw Normal View History

2025-03-08 08:16:54 +00:00
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 ];
}
}