Validate a formula someone typed
A formula typed by a person is wrong sooner or later. This guide checks one before it costs you a roll, and shows the reader what was wrong with it.
Check without rolling
Section titled “Check without rolling”In JavaScript, parseFormula reads a formula and throws if it cannot. It does the parsing
and none of the rolling, which costs about a hundredth as much.
import { parseFormula } from 'opendice'
function validate(text) { try { parseFormula(text) return { ok: true } } catch (error) { return { ok: false, message: error.message } }}
validate('4d6kh3') // { ok: true }validate('4d6kj3') // { ok: false, message: 'Cannot parse "4d6kj3" near "kj3"' }Use this to enable a submit button, or to mark a field as you type. Over HTTP there is no parse-only endpoint — roll it and read the error, which costs one request either way.
Read the error over HTTP
Section titled “Read the error over HTTP”A formula the API refuses comes back as a 400 with a code.
{ "error": { "code": "invalid_formula", "message": "Cannot parse \"4d6kj3\" near \"kj3\"" }}Branch on code, never on message:
const response = await fetch(url)
if (!response.ok) { const { error } = await response.json()
if (error.code === 'invalid_formula' || error.code === 'invalid_request') { return showToUser(error.message) }
throw new Error(error.code)}invalid_request means the request did not match the schema at all — a missing formula, a
formula over the length limit, a malformed tags list. invalid_formula means the formula
was well-formed as a request and the parser still refused it. Both are the caller’s to fix
and neither is worth retrying unchanged. Errors lists the rest.
Put the message on the page
Section titled “Put the message on the page”You can render a parse error directly. Before quoting anything back, the parser replaces
every character a formula cannot contain and truncates what is left, so a formula of
2d6<script> comes back describing a forbidden character rather than carrying the tag into
your page.
{ error && <p className="error">{error.message}</p>}This holds for parse errors specifically, which are the ones that quote input. Treat it as one less thing to escape, not as a reason to stop escaping elsewhere.
Accept the tags you expect
Section titled “Accept the tags you expect”A trailing word is a parse error unless you have said it is a tag. That is deliberate: an unrecognised word is far more often a typo than a tag.
roll('2d6 fire') // throws — 'fire' is a stray tokenroll('2d6 fire', { tags: ['fire'] }) // rolls, result.tag === 'fire'{ "formula": "2d6 fire", "tags": ["fire"] }List every word you recognise, in lowercase. If your interface offers a fixed set of damage types or categories, that set is the list. See the grammar for what a tag may contain.