Skip to content

Instantly share code, notes, and snippets.

@mattrussell-sonocent
Created March 19, 2019 09:14
Show Gist options
  • Save mattrussell-sonocent/cdddb6b44b777ed6ded7fa1f322e50d2 to your computer and use it in GitHub Desktop.
Save mattrussell-sonocent/cdddb6b44b777ed6ded7fa1f322e50d2 to your computer and use it in GitHub Desktop.
parsimmon example
import { alt, any, eof, notFollowedBy, oneOf, optWhitespace, Parser, regexp, seqMap, string } from 'parsimmon'
enum CommandType {
IMPORTANT = 'IMPORTANT',
REVIEW = 'REVIEW',
TASK = 'TASK',
HEADING = 'HEADING'
}
interface CommandParseResult {
text: string
command?: CommandType
}
const commandLetter: Parser<CommandType> = oneOf('irthIRTH').map(letter => {
switch (letter.toLowerCase()) {
case 'i':
return CommandType.IMPORTANT
case 'r':
return CommandType.REVIEW
case 't':
return CommandType.TASK
case 'h':
return CommandType.HEADING
default:
throw new Error(`Unexpected letter ${letter}`)
}
})
const nonWhitespace = regexp(/\S/)
// /t but not /tuesday
const commandDirective: Parser<CommandType> = string('/')
.then(commandLetter)
.notFollowedBy(nonWhitespace)
const cardText: Parser<string> = notFollowedBy(commandDirective.then(eof))
.then(any)
.many()
.tie()
const commandThenOptionalText: Parser<CommandParseResult> = seqMap(
commandDirective.skip(optWhitespace),
cardText,
(command, text) => ({
text,
command
})
)
const optional = <T>(parser: Parser<T>): Parser<T | undefined> => parser.fallback(undefined)
const textThenOptionalCommand: Parser<CommandParseResult> = seqMap(
cardText.skip(optWhitespace),
optional(commandDirective.skip(eof)),
(text, command) => ({
text,
command
})
)
const commandParser: Parser<CommandParseResult> = alt(commandThenOptionalText, textThenOptionalCommand)
const trimText = ({ text, command }: CommandParseResult): CommandParseResult => ({ text: text.trim(), command })
const parseCommand = (s: string): CommandParseResult => trimText(commandParser.tryParse(s.trim()))
it('should work', () => {
expect(parseCommand('/t foo bar')).toEqual({ text: 'foo bar', command: CommandType.TASK })
expect(parseCommand(' /tuesday')).toEqual({ text: '/tuesday' })
expect(parseCommand(' /t')).toEqual({ text: '', command: CommandType.TASK })
expect(parseCommand('/t')).toEqual({ text: '', command: CommandType.TASK })
expect(parseCommand('/t ')).toEqual({ text: '', command: CommandType.TASK })
expect(parseCommand('foo bar /t')).toEqual({ text: 'foo bar', command: CommandType.TASK })
expect(parseCommand(' foo bar /t')).toEqual({ text: 'foo bar', command: CommandType.TASK })
expect(parseCommand('foo bar /t ')).toEqual({ text: 'foo bar', command: CommandType.TASK })
expect(parseCommand('foo bar /t ')).toEqual({ text: 'foo bar', command: CommandType.TASK })
expect(parseCommand(' /t foo bar ')).toEqual({ text: 'foo bar', command: CommandType.TASK })
expect(parseCommand(' /t foo bar ')).toEqual({ text: 'foo bar', command: CommandType.TASK })
expect(parseCommand('foo bar/t')).toEqual({ text: 'foo bar', command: CommandType.TASK })
expect(parseCommand('foo bar /t baz buz')).toEqual({ text: 'foo bar /t baz buz' })
expect(parseCommand('foo bar /t baz buz /t')).toEqual({ text: 'foo bar /t baz buz', command: CommandType.TASK })
expect(parseCommand('bar /t baz /t buzz /t')).toEqual({ text: 'bar /t baz /t buzz', command: CommandType.TASK })
expect(parseCommand('foo bar /t baz buz /tuesday')).toEqual({ text: 'foo bar /t baz buz /tuesday' })
})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment