@masala/parser
Version:
Masala Parser
320 lines (284 loc) • 11.7 kB
JavaScript
import { describe, it, expect } from 'vitest'
import Streams from '../../lib/stream/index'
import { F, C } from '../../lib/parsec/index'
import { GenLex } from '../../lib'
function testParser(parser, string) {
let stream = Streams.ofString(string)
let parsing = parser.parse(stream)
return parsing
}
describe('Flow Bundle Tests', () => {
it('subStream is ok on string stream', () => {
const text = 'Hello World'
const parser = F.subStream(6).then(C.string('World'))
const response = parser.parse(Streams.ofString(text))
expect(response.isAccepted()).toBe(true)
expect(response.value.size()).toBe(7)
})
it('subStream is ok on genlex stream', () => {
const genlex = new GenLex()
genlex.setSeparatorsParser(F.not(C.charIn('+-<>[],.')))
genlex.keywords(['+', '-', '<', '>', '[', ']', ',', '.'])
const grammar = F.subStream(4).drop().then(F.any().rep())
const parser = genlex.use(grammar)
const text = '++++ and then >>'
const response = parser.parse(Streams.ofString(text))
expect(response.isAccepted()).toBe(true)
expect(response.value.size()).toBe(2)
})
it('not parser should not eat offset', () => {
const text = 'this is a line'
const line = text + '\n'
const eol = C.char('\n')
const parser = F.not(eol).rep()
let response = parser.parse(Streams.ofString(line))
expect(response.isAccepted()).toBe(true)
expect(response.offset).toBe(text.length)
const withParser = F.not(eol).rep().then(eol)
response = withParser.parse(Streams.ofString(line))
expect(response.isAccepted()).toBe(true)
expect(response.offset).toBe(line.length)
})
it('expect flatten result to be ok', () => {
const string = 'foobar'
const parser = C.char('f')
.then(C.char('o'))
.then(C.char('o'))
.then(C.string('bar'))
.array()
const parsing = testParser(parser, string)
expect(parsing.value).toEqual(['f', 'o', 'o', 'bar'])
})
it('expect returns to be ok when empty', () => {
const string = 'some'
const parser = F.any().rep().then(F.eos()).returns([])
const parsing = testParser(parser, string)
expect(parsing.isAccepted()).toBe(true)
expect(parsing.value).toEqual([])
})
it('expect startWith to start', () => {
const string = ' world'
const object = 'hello'
const parser = F.startWith(object)
.then(C.string(' world'))
.then(F.eos().drop())
const parsing = testParser(parser, string)
expect(parsing.isAccepted()).toBe(true)
expect(parsing.value.join('')).toBe('hello world')
})
it('test moveUntilFast string', () => {
const line = Streams.ofString('soXYZso')
const combinator = F.moveUntil('XYZ')
const parser = combinator.parse(line)
expect(parser.value).toBe('so')
expect(parser.offset).toBe(2)
})
it('test moveUntilFast string with include', () => {
const line = Streams.ofString('soXYZso')
const combinator = F.moveUntil('XYZ', true)
const parser = combinator.parse(line)
expect(parser.value).toBe('soXYZ')
expect(parser.offset).toBe(5)
})
it('test moveUntilFast string with continuation', () => {
const document = 'start-detect-XYZ-continues'
const line = Streams.ofString(document)
const start = C.string('start-')
const combinator = start
.drop()
.then(F.moveUntil('XYZ'))
.then(C.string('XYZ-continues').drop())
.single()
const parser = combinator.parse(line)
expect(parser.value).toBe('detect-')
expect(parser.offset).toBe(document.length)
})
it('test moveUntilFast array of string with continuation', () => {
const document = 'start-detect-XYZ-continues'
const line = Streams.ofString(document)
const start = C.string('start-')
const combinator = start
.drop()
.then(F.moveUntil(['ABC', 'ZE', 'XYZ']))
.then(C.string('XYZ-continues').drop())
.single()
const parsing = combinator.parse(line)
expect(parsing.value).toBe('detect-')
expect(parsing.offset).toBe(document.length)
})
it('test moveUntilFast array of string with include', () => {
const document = 'start-detect-XYZ-continues'
const line = Streams.ofString(document)
const start = C.string('start-')
const combinator = start
.drop()
.then(F.moveUntil(['ABC', 'ZE', 'XYZ'], true))
.then(C.string('-continues').drop())
.single()
const parsing = combinator.parse(line)
expect(parsing.isAccepted()).toBe(true)
expect(parsing.value).toBe('detect-XYZ')
expect(parsing.offset).toBe(document.length)
})
it('test moveUntilFast string fails', () => {
const document = 'start-detect-XYZ-continues'
const line = Streams.ofString(document)
const start = C.string('start-')
const combinator = start
.drop()
.then(F.moveUntil('EEE'))
.then(C.string('XYZ-continues').drop())
const parsing = combinator.parse(line)
expect(parsing.isAccepted()).toBe(false)
})
it('test moveUntilFast array of string fails', () => {
const document = 'start-detect-XYZ-continues'
const line = Streams.ofString(document)
const start = C.string('start-')
const combinator = start
.drop()
.then(F.moveUntil(['ABC', 'ZE', 'EEE']))
.then(C.string('XYZ-continues').drop())
const parsing = combinator.parse(line)
expect(parsing.isAccepted()).toBe(false)
})
it('test moveUntilFast fails if array stream', () => {
const document = ['More', 'XYZ']
const line = Streams.ofArray(document)
const combinator = F.moveUntil(['ABC', 'ZE', 'XYZ'])
expect(() => combinator.parse(line)).toThrow(
'Input source must be a String',
)
})
it('test moveUntilFastString fails if array stream', () => {
const document = ['More', 'XYZ']
const line = Streams.ofArray(document)
const combinator = F.moveUntil('XYZ')
expect(() => combinator.parse(line)).toThrow(
'Input source must be a String',
)
})
it('test moveUntil', () => {
const line = Streams.ofString('I write until James appears')
const combinator = F.moveUntil(C.string('James'))
.then(F.any().drop())
.single()
const value = combinator.parse(line).value
expect(value).toBe('I write until ')
})
it('test moveUntil parser, with include', () => {
const line = Streams.ofString('I write until James appears')
const combinator = F.moveUntil(C.string('James'), true)
.then(F.any().rep().drop())
.single()
const parsing = combinator.parse(line)
const value = parsing.value
expect(parsing.isAccepted()).toBe(true)
expect(value).toBe('I write until James')
expect(parsing.offset).toBe(line.source.length)
})
it('test moveUntil parser, with include and structure', () => {
const line = Streams.ofString('I write until James appears')
const combinator = F.moveUntil(
C.string('James').map((james) => ({
structure: james,
})),
true,
)
.then(F.any().rep().drop())
.single()
const parsing = combinator.parse(line)
const value = parsing.value
expect(parsing.isAccepted()).toBe(true)
expect(value).not.toBe('I write until James')
expect(parsing.offset).toBe(line.source.length)
})
it('test moveUntil parser, with eos, not including', () => {
const line = Streams.ofString('I write until the end')
const combinator = F.moveUntil(C.string('end'), false)
const parsing = combinator.parse(line)
const value = parsing.value
expect(parsing.isAccepted()).toBe(true)
expect(value).toBe('I write until the ')
expect(parsing.offset).toBe('I write until the '.length)
})
it('test moveUntil parser, with eos, including', () => {
const line = Streams.ofString('I write until James appears')
const combinator = F.moveUntil(C.string('appears'), true)
const parsing = combinator.parse(line)
const value = parsing.value
expect(parsing.isAccepted()).toBe(true)
expect(value).toBe('I write until James appears')
expect(parsing.offset).toBe(line.source.length)
})
it('test moveUntil Not found', () => {
const line = Streams.ofString('I write until James appears')
const combinator = F.moveUntil(C.string('Indiana'))
.then(C.string('I'))
.then(F.any().drop())
const accepted = combinator.parse(line).isAccepted()
expect(accepted).toBe(false)
})
it('test moveUntil found with failing parser', () => {
const line = Streams.ofString('I write until James Bond appears')
const combinator = F.moveUntil(C.string('James')).then(
F.dropTo(F.eos()),
)
const accepted = combinator.parse(line).isAccepted()
expect(accepted).toBe(false)
})
it('test dropTo with string', () => {
const line = Streams.ofString('I write until James Bond appears')
const combinator = F.dropTo('James')
.then(C.string(' Bond appears'))
.then(F.eos())
const accepted = combinator.parse(line).isAccepted()
expect(accepted).toBe(true)
})
it('test dropTo with string fail', () => {
const line = Streams.ofString('I write until James Bond appears')
const combinator = F.dropTo('James')
.then(C.string(' Bond appears'))
.then(F.eos())
const accepted = combinator.parse(line).isAccepted()
expect(accepted).toBe(true)
})
it('test dropTo with parser', () => {
const line = Streams.ofString('I write until James Bond appears')
const combinator = F.dropTo(C.string('James'))
.then(C.string(' Bond appears'))
.then(F.eos())
const accepted = combinator.parse(line).isAccepted()
expect(accepted).toBe(true)
})
it('test moveUntil found with more parsers', () => {
const line = Streams.ofString('I write until James Bond appears')
const combinator = F.moveUntil(C.string('James'))
.then(F.dropTo('appears'))
.then(F.eos().drop())
.single()
const value = combinator.parse(line).value
expect(value).toBe('I write until ')
})
it('lazy with a class', () => {
class SomeLazyParser {
constructor(char) {
this.char = char
}
first() {
return C.char(this.char).then(
this.second()
.opt()
.map((opt) => opt.orElse('')),
)
}
second() {
return C.char('b').then(F.lazy(this.first, ['a'], this))
}
}
const line = Streams.ofString('ababa')
const combinator = new SomeLazyParser('a').first().then(F.eos().drop())
const value = combinator.parse(line).value
expect(value.join('')).toBe('ababa')
})
})