# Re: Factor

Factor: the language, the theory, and the practice.

## Rolling Dice

Thursday, July 1, 2010

Update: I noticed that random is `[0,b)` (from `0` to `n-1`). The code has been fixed to make it `[1..b]` (e.g., return a number from 1 to n).

I was curious about the ability to define new syntax for Factor, and to learn a little about how the lexer works. Inspired by a recent project that I’ve been working on, I thought it would be interesting to define a simple DSL for calculating dice rolls.

We need a way to describe a dice roll. There are various methods used in Python or Ruby. A common example of a common short-hand description is `4d8` – specifying how many times (`4`) to roll a dice with a number (`8`) of sides.

Let’s setup a vocabulary and a list of dependencies that will be used to implement this functionality:

``````USING: fry kernel lexer math math.parser peg.ebnf random
sequences strings ;

IN: dice
``````

We will be using EBNF parsers similar to what was used in building a calculator. First we will implement support for basic (e.g., `4d8`) rolls.

``````EBNF: parse-roll

number = ([0-9])+    => [[ >string string>number ]]
dice   = "d" number  => [[ second '[ _ random ] ]]
roll   = number dice => [[ first2 '[ 0 _ [ @ + 1 + ] times ] ]]
error  = .*          => [[ "unknown dice" throw ]]
total  = roll | error

;EBNF
``````

We can see how this works by trying it out:

``````IN: scratchpad "4d8" parse-roll .
[ 0 4 [ 8 random + 1 + ] times ]

IN: scratchpad "4d8" parse-roll call .
15

unknown dice
``````

We can now define a piece of SYNTAX: for dice rolls:

``````SYNTAX: ROLL: scan parse-roll append ;
``````

And using it:

``````IN: scratchpad ROLL: 4d8 .
13
``````

This could be extended to support other things such as “base” numbers (e.g., `4d8+10`), negative rolls, ranges, and better handling of parse errors. The code for this is available on my GitHub.