import {LexInput} from '../lexer.js' import {ExpectedEndOfInputError, InvalidVariableNameError, UnexpectedEndOfInputError} from "../parse.js"; export type StrLVal = { term: 'variable', name: string } export type StrTerm = { term: 'string', value: string } | StrLVal export class ParseContext { constructor( private inputs: LexInput[], ) {} assertEmpty() { if ( this.inputs.length ) { throw new ExpectedEndOfInputError(`Expected end of input. Found: ${this.inputs[0].value}`) } } popOptionalTerm(): StrTerm|undefined { if ( this.inputs.length ) return this.popTerm() return undefined } popTerm(): StrTerm { if ( !this.inputs.length ) { throw new UnexpectedEndOfInputError('Unexpected end of input. Expected term.'); } const input = this.inputs.shift()! // Check if the token is a literal variable name: if ( !input.literal && input.value.startsWith('$') ) { if ( !input.value.match(/^\$[a-zA-Z0-9_]+$/) ) { throw new InvalidVariableNameError(`Invalid variable name: ${input.value}`) } return { term: 'variable', name: input.value } } // Otherwise, parse it as a string literal: return { term: 'string', value: input.value } } popLVal(): StrLVal { if ( !this.inputs.length ) { throw new UnexpectedEndOfInputError('Unexpected end of input. Expected lval.'); } const input = this.inputs.shift()! if ( input.literal || !input.value.match(/^\$[a-zA-Z0-9_]+$/) ) { throw new InvalidVariableNameError(`Expected variable name. Found: ${input.value}`) } return { term: 'variable', name: input.value } } } export type CommandData = Record export abstract class Command { abstract isParseCandidate(token: LexInput): boolean abstract attemptParse(context: ParseContext): TData abstract getDisplayName(): string protected isKeyword(token: LexInput, keyword: string): boolean { return !token.literal && token.value === keyword } }