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

170 lines
3.6 KiB
TypeScript
Raw Normal View History

2025-03-08 08:16:54 +00:00
import { LexerMatcher } from "./LexerMatcher";
import { LexerEvent } from "./LexerEvent";
export class Lexer
{
static readonly defaultMode = "default";
private _modes = new Map<string,LexerMatcher[]>();
get modes()
{
return this._modes;
}
get matchers():LexerMatcher[]
{
let output:LexerMatcher[] = [];
for ( let mode of this._modes )
{
let matchers = mode[ 1 ];
for ( let matcher of matchers )
{
output.push( matcher );
}
}
return output;
}
addMatcher( matcher:LexerMatcher )
{
let list:LexerMatcher[] = this._modes.get( matcher.mode );
if ( ! list )
{
list = [];
this._modes.set( matcher.mode, list );
}
list.push( matcher );
}
resolveMatches( source:string, events:LexerEvent[] )
{
events.forEach( e => e.getMatch( source ) );
}
addAllMatchers( ...matchers:LexerMatcher[] )
{
for ( let m of matchers )
{
this.addMatcher( m );
}
}
public lex( source:string, callback:( e:LexerEvent) => void, offset:number = 0, mode:string = "" )
{
let lexerEvent = new LexerEvent( "", 0, -2 );
let lastMatcher = null;
while ( offset < source.length )
{
if ( ! this._modes.has( mode ) )
{
let errorMessage = "@Lexer-Error. Mode not found: '" + mode + "'";
console.log( errorMessage, "@", offset );
lexerEvent.set( errorMessage, offset, LexerEvent.LENGTH_ERROR_ID );
callback( lexerEvent );
return;
}
let matchers = this._modes.get( mode );
let foundSomething = false;
for ( let i = 0; i < matchers.length; i++ )
{
let matcher = matchers[ i ];
let matchLength = matcher.matchLength( source, offset );
if ( matchLength > 0 )
{
lexerEvent.set( matcher.type, offset, matchLength );
callback( lexerEvent );
foundSomething = true;
i = matchers.length;
if ( matcher.nextMode )
{
mode = matcher.nextMode;
}
offset += matchLength;
}
}
if ( ! foundSomething )
{
let errorMessage = "@Lexer-Error. No match: '" + mode + "'";
console.log( errorMessage, "@", offset );
lexerEvent.set( errorMessage, offset, LexerEvent.LENGTH_ERROR_ID );
callback( lexerEvent );
return;
}
}
lexerEvent.set( mode, offset, LexerEvent.LENGTH_DONE_ID );
callback( lexerEvent );
}
lexTokens( source:string, offset:number = 0, mode:string = Lexer.defaultMode )
{
let events = this.lexToList( source, offset, mode );
this.resolveMatches( source, events );
return events;
}
lexToList( source:string, offset:number = 0, mode:string = Lexer.defaultMode )
{
var list:LexerEvent[] = [];
this.lex( source,
( token:LexerEvent )=>
{
list.push( token.clone() );
},
offset, mode
);
return list;
}
compress( tokens:LexerEvent[], compressTypes:Set<string> )
{
let lastToken:LexerEvent = null;
let compressedTokens:LexerEvent[] = [];
tokens.forEach(
t =>
{
if ( lastToken && t.type === lastToken.type && compressTypes.has( t.type ) )
{
lastToken.extendLength( t.length );
}
else
{
lastToken = t.clone();
compressedTokens.push( lastToken );
}
}
);
return compressedTokens;
}
}