P
Paul Novak
------=_Part_19248_25669739.1136746627816
Content-Type: text/plain; charset=ISO-8859-1
Content-Transfer-Encoding: quoted-printable
Content-Disposition: inline
Leexer/parsers? We ain't got no Leexer/parsers. We don't need no
Leexer/parsers. I don't have to show you any steenking Leexer/parser.=20
Just call eval and use Ruby's fine lexer/parser (apologies to Mel
Brooks, John Huston and Banditos Mexicanos everywhere).
This approach uses regex substitutions to first munge the input
expression to deal with the default cases (like d6 to 1d6 and 1% to
1d100), then it substitutes ** for d and hands it over to the
evaluator and prints the result.
Eval does what we want after an override of Fixnum.** to give us our
dice-rolling d-operator behavior. Conveniently, ** has the desired
precedence relative to the other operators, plus it is binary and
left-associative. This feels so evil. Seduced by the Dark Side I am.
Note that we get lot's of additional behavior we don't need, but the
original spec said the BNF was incomplete, so I was lazy and left the
undef[ining] of >>, << et al is 'as an exercise...' I don't know
enough D&D to know if they might be useful.
BTW. In the spirit of 'why can't we all just get along?': the dice
rolling algorithm is ported from this Python code at
http://www.onlamp.com/pub/a/python/2002/07/11/recipes.html?page=3D3
from random import randrange
def dice(num,sides):
=09return reduce(lambda x,y,s=3Dsides:x + randrange(s), range(num+1))+num
here is the Ruby:
#!/usr/bin/env ruby
#
# roll.rb
#
# fix up Fixnum to override ** with our desired d behavior
class Fixnum
def ** (sides)
# validation
if sides<1
raise "Invalid sides value: '#{sides}', must be a positive Integer"
end
if self<1
raise "Invalid number of rolls: '#{self}', must be a postitive Integ=
er"
end
# roll the dice
(0..self).to_a.inject{|x,y| x + rand(sides)}+self
end
end
dice_expression =3D ARGV[0]
# default number of rolls is 1, substitute d6 =3D> 1d6
dice_expression =3D dice_expression.gsub(/(^|[^0-9)\s])(\s*d)/, '\11d')
# d% =3D> d100
dice_expression =3D dice_expression.gsub(/d%/,'d100 ')
# this feels so dirty...substitute d =3D> **
dice_expression =3D dice_expression.gsub(/d/, "**")
(ARGV[1] || 1).to_i.times { print "#{eval(dice_expression)} " }
------=_Part_19248_25669739.1136746627816
Content-Type: application/octet-stream; name=roll.rb
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment; filename="roll.rb"
#!/usr/bin/env ruby
#
# roll.rb
#
# fix up Fixnum to override ** with our desired d behavior
class Fixnum
def ** (sides)
# validation
if sides<1
raise "Invalid sides value: '#{sides}', must be a positive Integer"
end
if self<1
raise "Invalid number of rolls: '#{self}', must be a postitive Integer"
end
# roll the dice
(0..self).to_a.inject{|x,y| x + rand(sides)}+self
end
end
dice_expression = ARGV[0]
# default number of rolls is 1, substitute d6 => 1d6
dice_expression = dice_expression.gsub(/(^|[^0-9)\s])(\s*d)/, '\11d')
# d% => d100
dice_expression = dice_expression.gsub(/d%/,'d100 ')
# this feels so dirty...substitute d => **
dice_expression = dice_expression.gsub(/d/, "**")
(ARGV[1] || 1).to_i.times { print "#{eval(dice_expression)} " }
------=_Part_19248_25669739.1136746627816--
Content-Type: text/plain; charset=ISO-8859-1
Content-Transfer-Encoding: quoted-printable
Content-Disposition: inline
Leexer/parsers? We ain't got no Leexer/parsers. We don't need no
Leexer/parsers. I don't have to show you any steenking Leexer/parser.=20
Just call eval and use Ruby's fine lexer/parser (apologies to Mel
Brooks, John Huston and Banditos Mexicanos everywhere).
This approach uses regex substitutions to first munge the input
expression to deal with the default cases (like d6 to 1d6 and 1% to
1d100), then it substitutes ** for d and hands it over to the
evaluator and prints the result.
Eval does what we want after an override of Fixnum.** to give us our
dice-rolling d-operator behavior. Conveniently, ** has the desired
precedence relative to the other operators, plus it is binary and
left-associative. This feels so evil. Seduced by the Dark Side I am.
Note that we get lot's of additional behavior we don't need, but the
original spec said the BNF was incomplete, so I was lazy and left the
undef[ining] of >>, << et al is 'as an exercise...' I don't know
enough D&D to know if they might be useful.
BTW. In the spirit of 'why can't we all just get along?': the dice
rolling algorithm is ported from this Python code at
http://www.onlamp.com/pub/a/python/2002/07/11/recipes.html?page=3D3
from random import randrange
def dice(num,sides):
=09return reduce(lambda x,y,s=3Dsides:x + randrange(s), range(num+1))+num
here is the Ruby:
#!/usr/bin/env ruby
#
# roll.rb
#
# fix up Fixnum to override ** with our desired d behavior
class Fixnum
def ** (sides)
# validation
if sides<1
raise "Invalid sides value: '#{sides}', must be a positive Integer"
end
if self<1
raise "Invalid number of rolls: '#{self}', must be a postitive Integ=
er"
end
# roll the dice
(0..self).to_a.inject{|x,y| x + rand(sides)}+self
end
end
dice_expression =3D ARGV[0]
# default number of rolls is 1, substitute d6 =3D> 1d6
dice_expression =3D dice_expression.gsub(/(^|[^0-9)\s])(\s*d)/, '\11d')
# d% =3D> d100
dice_expression =3D dice_expression.gsub(/d%/,'d100 ')
# this feels so dirty...substitute d =3D> **
dice_expression =3D dice_expression.gsub(/d/, "**")
(ARGV[1] || 1).to_i.times { print "#{eval(dice_expression)} " }
------=_Part_19248_25669739.1136746627816
Content-Type: application/octet-stream; name=roll.rb
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment; filename="roll.rb"
#!/usr/bin/env ruby
#
# roll.rb
#
# fix up Fixnum to override ** with our desired d behavior
class Fixnum
def ** (sides)
# validation
if sides<1
raise "Invalid sides value: '#{sides}', must be a positive Integer"
end
if self<1
raise "Invalid number of rolls: '#{self}', must be a postitive Integer"
end
# roll the dice
(0..self).to_a.inject{|x,y| x + rand(sides)}+self
end
end
dice_expression = ARGV[0]
# default number of rolls is 1, substitute d6 => 1d6
dice_expression = dice_expression.gsub(/(^|[^0-9)\s])(\s*d)/, '\11d')
# d% => d100
dice_expression = dice_expression.gsub(/d%/,'d100 ')
# this feels so dirty...substitute d => **
dice_expression = dice_expression.gsub(/d/, "**")
(ARGV[1] || 1).to_i.times { print "#{eval(dice_expression)} " }
------=_Part_19248_25669739.1136746627816--