В node.js, кстати, есть превосходная альтернатива евалу - модуль
vm
Он умеет запускать код в совершенно изолированной песочнице, где нет ничего, кроме стандартной библиотеки js. То есть никаких require, импортов, взаимодействий с операционной системой и т.д. Там нет даже setTimeout! Всё необходимое ты сам передаешь внутрь через "контекст". Вот в нем и можно запустить код безопасно. Если код свалится от синтаксической ошибки, как в примере из вопроса, то надо просто обернуть вызов в try-catch
try {
vm.runInNewContext(code, {}); // второй параметр - тот самый "контекст", см. примеры
} catch (e) {
console.log('error: ', e.message);
}
правда, и тут может подгадить злоумышленник - воткнуть бесконечный цикл или сожрать всю память. Вероятно, стоит использовать vm в отдельном потоке, установив ему лимит по времени, допустим, в полсекунды. Но вот что сделать с памятью, непонятно...
потому, если исполняемый код ограничен только математическим выражением, то таки да, лучше кастомный парсер.