[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:
( scratchpad ) "4d8" parse-roll . [ 0 4 [ 8 random + 1 + ] times ] ( scratchpad ) "4d8" parse-roll call . 15 ( scratchpad ) "foo" parse-roll unknown dice
We can now define a piece of SYNTAX: for dice rolls:
SYNTAX: ROLL: scan parse-roll append ;
And using it:
( 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.
Need to change definition as below for working…
ReplyDeleteSYNTAX: ROLL: scan-token parse-roll suffix! \ call suffix! ;
Hmm, we did rename scan to scan-token, here's a simpler version:
ReplyDeleteSYNTAX: ROLL: scan-token parse-roll append! ;