str/src/vm/commands/trim.ts

56 lines
1.7 KiB
TypeScript

import {Command, ParseContext, StrTerm} from "./command.js";
import {LexInput} from "../lexer.js";
import {StrVM} from "../vm.js";
import {Awaitable} from "../../util/types.js";
import {rexEscape} from "../string.js";
export type TrimData = {
type?: 'start'|'end'|'both'|'left'|'right'|'lines',
char?: StrTerm,
}
export class Trim extends Command<TrimData> {
attemptParse(context: ParseContext): TrimData {
return {
type: context.popOptionalKeywordInSet(['start', 'end', 'both', 'left', 'right', 'lines'])?.value,
char: context.popOptionalTerm(),
}
}
getDisplayName(): string {
return 'trim'
}
isParseCandidate(token: LexInput): boolean {
return this.isKeyword(token, 'trim')
}
execute(vm: StrVM, data: TrimData): Awaitable<StrVM> {
return vm.replaceContextMatchingTerm(ctx => ({
string: sub => {
const char = data.char
? rexEscape(ctx.resolveString(data.char))
: '\\s'
if ( !data.type || ['start', 'left', 'both'].includes(data.type) ) {
const leftRex = new RegExp(`^${char || '\\s'}*`, 's')
sub = sub.replace(leftRex, '')
}
if ( !data.type || ['end', 'right', 'both'].includes(data.type) ) {
const rightRex = new RegExp(`${char || '\\s'}*$`, 's')
sub = sub.replace(rightRex, '')
}
if ( data.type === 'lines' ) {
sub = sub.split('\n')
.filter(l => l.trim())
.join('\n')
}
return sub
},
}))
}
}