import { LexerMatcher } from "./LexerMatcher"; import { LexerEvent } from "./LexerEvent"; export class Lexer { static readonly defaultMode = "default"; private _modes = new Map(); 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 ) { 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; } }