Last active
June 2, 2022 13:59
-
-
Save fersilva16/22e15be1d940db6df4e52da90a8cf9c4 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const assert = require('assert'); | |
const Value = (n) => ({ type: 'value', n }); | |
const Sum = (a, b) => ({ type: 'sum', a, b }); | |
const Prod = (a, b) => ({ type: 'prod', a, b }); | |
const Div = (a, b) => ({ type: 'div', a, b }); | |
const Sub = (a, b) => ({ type: 'sub', a, b }); | |
const sliceLast = (a, n) => a.slice(n > a.length ? 0 : a.length - n, a.length); | |
const parseValue = (str) => { | |
const [n] = sliceLast(str, 1); | |
const int = parseInt(n); | |
if (Number.isNaN(int)) return undefined; | |
return Value(int); | |
}; | |
const parseSum = (str, [nop]) => { | |
const [a, op, b] = sliceLast(str, 3); | |
if (!['value', 'prod', 'div', 'sum', 'sub'].includes(a?.type)) { | |
return undefined; | |
} | |
if (op !== '+') return undefined; | |
if (!['value', 'prod', 'div', 'sum', 'sub'].includes(b?.type)) { | |
return undefined; | |
} | |
if (nop === '*') return undefined; | |
if (nop === '/') return undefined; | |
return Sum(a, b); | |
}; | |
const parseSub = (str, [nop]) => { | |
const [a, op, b] = sliceLast(str, 3); | |
if (!['value', 'prod', 'div', 'sum', 'sub'].includes(a?.type)) { | |
return undefined; | |
} | |
if (op !== '-') return undefined; | |
if (!['value', 'prod', 'div', 'sum', 'sub'].includes(b?.type)) { | |
return undefined; | |
} | |
if (nop === '*') return undefined; | |
if (nop === '/') return undefined; | |
return Sub(a, b); | |
}; | |
const parseProd = (str) => { | |
const [a, op, b] = sliceLast(str, 3); | |
if (!['value', 'prod', 'div'].includes(a?.type)) return undefined; | |
if (op !== '*') return undefined; | |
if (!['value', 'prod', 'div'].includes(b?.type)) { | |
return undefined; | |
} | |
return Prod(a, b); | |
}; | |
const parseDiv = (str) => { | |
const [a, op, b] = sliceLast(str, 3); | |
if (!['value', 'prod', 'div'].includes(a?.type)) return undefined; | |
if (op !== '/') return undefined; | |
if (!['value', 'prod', 'div'].includes(b?.type)) { | |
return undefined; | |
} | |
return Div(a, b); | |
}; | |
const reduce = (str, la) => { | |
return ( | |
parseProd(str, la) || | |
parseDiv(str, la) || | |
parseSum(str, la) || | |
parseSub(str, la) || | |
parseValue(str, la) | |
); | |
}; | |
/** @param {string} str */ | |
const parse = (str) => { | |
const tbp = str.trim().split(''); | |
const state = []; | |
while (tbp.length) { | |
state.push(tbp.shift()); | |
let i = 1; | |
while (i < state.length + 1) { | |
const slice = state.splice(state.length - i, state.length); | |
const result = reduce(slice, tbp.slice(0, 1)); | |
i++; | |
if (result === undefined) state.push(...slice); | |
else if (result === '') { | |
} else { | |
const shouldBreak = !state.length; | |
state.push(result); | |
if (shouldBreak) break; | |
i = 1; | |
} | |
} | |
} | |
if (state.length === 1) return state[0]; | |
return undefined; | |
}; | |
const print = (n) => { | |
if (n?.type === 'value') return `Value(${n.n})`; | |
if (n?.type === 'sum') return `Sum(${print(n.a)}, ${print(n.b)})`; | |
if (n?.type === 'sub') return `Sub(${print(n.a)}, ${print(n.b)})`; | |
if (n?.type === 'prod') return `Prod(${print(n.a)}, ${print(n.b)})`; | |
if (n?.type === 'div') return `Div(${print(n.a)}, ${print(n.b)})`; | |
return n; | |
}; | |
assert(parse('') === undefined); | |
assert(parse('1+') === undefined); | |
assert(parse('1-') === undefined); | |
assert(parse('1*') === undefined); | |
assert(parse('1/') === undefined); | |
assert.deepEqual(parse('1'), Value(1)); | |
assert.deepEqual(parse('1+1'), Sum(Value(1), Value(1))); | |
assert.deepEqual(parse('1+1'), Sum(Value(1), Value(1))); | |
assert.deepEqual(parse('1-1'), Sub(Value(1), Value(1))); | |
assert.deepEqual(parse('1*2'), Prod(Value(1), Value(2))); | |
assert.deepEqual(parse('1/2'), Div(Value(1), Value(2))); | |
assert.deepEqual(parse('1*1+2'), Sum(Prod(Value(1), Value(1)), Value(2))); | |
assert.deepEqual(parse('1*1-2'), Sub(Prod(Value(1), Value(1)), Value(2))); | |
assert.deepEqual(parse('1/1+2'), Sum(Div(Value(1), Value(1)), Value(2))); | |
assert.deepEqual(parse('1/1-2'), Sub(Div(Value(1), Value(1)), Value(2))); | |
assert.deepEqual(parse('1-1+2'), Sum(Sub(Value(1), Value(1)), Value(2))); | |
assert.deepEqual(parse('1-1-2'), Sub(Sub(Value(1), Value(1)), Value(2))); | |
assert.deepEqual(parse('1+1+2'), Sum(Sum(Value(1), Value(1)), Value(2))); | |
assert.deepEqual(parse('1+1-2'), Sub(Sum(Value(1), Value(1)), Value(2))); | |
assert.deepEqual(parse('1+1*2'), Sum(Value(1), Prod(Value(1), Value(2)))); | |
assert.deepEqual(parse('1-1*2'), Sub(Value(1), Prod(Value(1), Value(2)))); | |
assert.deepEqual(parse('1+1/2'), Sum(Value(1), Div(Value(1), Value(2)))); | |
assert.deepEqual(parse('1-1/2'), Sub(Value(1), Div(Value(1), Value(2)))); | |
assert.deepEqual(parse('1+1-2'), Sub(Sum(Value(1), Value(1)), Value(2))); | |
assert.deepEqual(parse('1-1-2'), Sub(Sub(Value(1), Value(1)), Value(2))); | |
assert.deepEqual(parse('1+1+2'), Sum(Sum(Value(1), Value(1)), Value(2))); | |
assert.deepEqual(parse('1-1+2'), Sum(Sub(Value(1), Value(1)), Value(2))); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment