Skip to content

Instantly share code, notes, and snippets.

@javierfernandes
Created November 15, 2020 15:05
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save javierfernandes/34186422ba8a444d1bee4f4deba092f8 to your computer and use it in GitHub Desktop.
Save javierfernandes/34186422ba8a444d1bee4f4deba092f8 to your computer and use it in GitHub Desktop.
const author = { sessionId: 'my_session', user: { _id: 'JOHN' } }
const UNDO = (_id, index = 0, timestamp) => ({ _id: `-${_id}-${index}`, author, reverts: { _id }, timestamp })
const REDO = (_id, index = 0, timestamp) => ({ _id: `+${_id}-${index}`, author, reapplies: { _id }, timestamp })
const change = (_id, timestamp) => ({ _id, author, timestamp })
const parseArray = elementParser => pipe(
drop(1), dropLast(1), // [ ... ]
trim,
split(','),
map(trim),
filter(pipe(equals(''), not)),
map(elementParser)
)
const extractMatch = regex => pipe(match(regex), nth(1))
const extractParam = extractMatch(/^[UR]\(([^)]*)\)$/)
const extractCursorId = extractMatch(/^\(([^)]*)\)$/)
const parseHistory = parseArray(cond([
[test(/^U\((.*)\)$/), pipe(extractParam, UNDO)],
[test(/^R\((.*)\)$/), pipe(extractParam, REDO)],
[T, change]
]))
const parseItems = parseArray(cond([
[test(/^\([^)]*\)$/), pipe(extractCursorId, change, assoc('hasCursor', true))],
[T, change]
]))
const doParsing = pipe(
split('->'),
map(trim),
applySpec({
history: pipe(nth(0), parseHistory),
items: pipe(nth(1), parseItems),
}),
)
const computeCursor = pipe(
applySpec({
history: prop('history'),
items: prop('items'),
cursor: pipe(prop('items'), findIndex(propEq('hasCursor', true))),
}),
over(lensProp('items'), map(dissoc('hasCursor'))),
when(propEq('cursor', -1), e => ({ ...e, cursor: e.items.length }))
)
const parseInput = pipe(doParsing, computeCursor)
describe('Tests for DSL (for testing)', () => {
describe('parseInput', () => {
it('should parse empty arrays', () => {
expect(parseInput('[] -> []')).toEqual({ history: [], items: [], cursor: 0 })
})
describe('history parsing', () => {
[
['[A] -> []', [change('A')]],
['[A, B] -> []', [change('A'), change('B')]],
['[A, B, C, D ] -> []', [change('A'), change('B'), change('C'), change('D')]],
['[U(A)] -> []', [UNDO('A')]],
['[R(A)] -> []', [REDO('A')]],
['[U(B), U(C), C, B, A] -> []', [UNDO('B'), UNDO('C'), change('C'), change('B'), change('A')]],
].forEach(([input, expected]) => {
it(`${input} -> ${JSON.stringify(expected)}`, () => {
expect(parseInput(input)).toEqual({ history: expected, items: [], cursor: 0 })
})
})
})
describe('expected items parsing', () => {
it('should parse items with one id', () => {
expect(parseInput('[] -> [A]')).toEqual({ history: [], items: [change('A')], cursor: 1 })
})
it('should parse items with two ids', () => {
expect(parseInput('[] -> [A, B ]')).toEqual({ history: [], items: [change('A'), change('B')], cursor: 2 })
})
it('should parse items with 1 id that is the current cursor', () => {
expect(parseInput('[] -> [(A)]')).toEqual({ history: [], items: [change('A')], cursor: 0 })
})
it('should parse the cursor at the middle', () => {
expect(parseInput('[] -> [C, (B), A]')).toEqual({ history: [], items: [change('C'), change('B'), change('A')], cursor: 1 })
})
})
describe('combined examples', () => {
it('should parse one with UNDO, REDO and cursor', () => {
expect(parseInput('[R(B), R(A), U(A), U(B), B, A] -> [(B), A]'))
.toEqual({ history: [REDO('B'), REDO('A'), UNDO('A'), UNDO('B'), change('B'), change('A')], items: [change('B'), change('A')], cursor: 0 })
})
it('should fix a regression', () => {
expect(parseInput('[U(B), U(C), C, B, A] -> [C, B, (A)]'))
.toEqual({ history: [UNDO('B'), UNDO('C'), change('C'), change('B'), change('A')], items: [change('C'), change('B'), change('A')], cursor: 2 })
})
})
})
})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment