Implement sub-command parsing + add on/word/line/over commands
This commit is contained in:
@@ -4,7 +4,13 @@ import {StreamLogger} from '../util/log.js'
|
||||
import {log} from '../log.js'
|
||||
import {Commands} from './commands/index.js'
|
||||
import {Command, CommandData, ParseContext} from './commands/command.js'
|
||||
import {Executable, InternalParseError, InvalidCommandError, IsNotKeywordError} from './parse.js'
|
||||
import {
|
||||
Executable,
|
||||
InternalParseError,
|
||||
InvalidCommandError,
|
||||
IsNotKeywordError,
|
||||
UnexpectedEndOfInputError
|
||||
} from './parse.js'
|
||||
|
||||
export class Parser extends BehaviorSubject<Executable<CommandData>> {
|
||||
private logger: StreamLogger
|
||||
@@ -12,10 +18,10 @@ export class Parser extends BehaviorSubject<Executable<CommandData>> {
|
||||
private parseCandidate?: Command<CommandData>
|
||||
private inputForCandidate: LexInput[] = []
|
||||
|
||||
constructor(lexer: Lexer, private commands: Commands) {
|
||||
constructor(private commands: Commands, lexer?: Lexer) {
|
||||
super()
|
||||
this.logger = log.getStreamLogger('parser')
|
||||
lexer.subscribe(token => this.handleToken(token))
|
||||
lexer?.subscribe(token => this.handleToken(token))
|
||||
}
|
||||
|
||||
async handleToken(token: LexToken) {
|
||||
@@ -46,9 +52,9 @@ export class Parser extends BehaviorSubject<Executable<CommandData>> {
|
||||
if ( token.type === 'terminator' ) {
|
||||
try {
|
||||
// Have the candidate attempt to parse itself from the collecte data:
|
||||
const context = new ParseContext(this.inputForCandidate)
|
||||
const context = this.getContext()
|
||||
this.logger.verbose({ parsing: this.parseCandidate.getDisplayName(), context })
|
||||
const data = this.parseCandidate.attemptParse(context)
|
||||
const data = await this.parseCandidate.attemptParse(context)
|
||||
|
||||
// The candidate must consume every token in the context:
|
||||
context.assertEmpty()
|
||||
@@ -91,4 +97,28 @@ export class Parser extends BehaviorSubject<Executable<CommandData>> {
|
||||
|
||||
return `(${token.literal ? 'LITERAL' : 'INPUT'}) ${token.value}`
|
||||
}
|
||||
|
||||
private getContext(): ParseContext {
|
||||
return new ParseContext(
|
||||
this.inputForCandidate,
|
||||
async tokens => {
|
||||
const childParser = new Parser(this.commands)
|
||||
|
||||
while ( !childParser.currentValue ) {
|
||||
if ( !tokens.length ) {
|
||||
await childParser.handleToken({ type: 'terminator' })
|
||||
break
|
||||
}
|
||||
|
||||
await childParser.handleToken(tokens.shift()!)
|
||||
}
|
||||
|
||||
const parsedExecutable = childParser.currentValue
|
||||
if ( !parsedExecutable ) {
|
||||
throw new UnexpectedEndOfInputError('Unable to parse child command: unexpected end of tokens')
|
||||
}
|
||||
return [parsedExecutable, tokens]
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user