lib/games_dice/parser.rb in games_dice-0.0.6 vs lib/games_dice/parser.rb in games_dice-0.1.1

- old
+ new

@@ -2,31 +2,37 @@ # converts string dice descriptions to data usable for the GamesDice::Dice constructor class GamesDice::Parser < Parslet::Parser # Descriptive language examples (capital letters stand in for integers) - # NdX - a roll of N dice, with X sides each, values summed to total - # NdX + C - a roll of N dice, with X sides each, values summed to total plus a constant C - # NdX - C - a roll of N dice, with X sides each, values summed to total minus a constant C - # NdX + MdY + C - any number of +C, -C, +NdX, -NdX etc sub-expressions can be combined - # NdXkZ - a roll of N dice, sides X, keep best Z results and sum them # NdXk[Z,worst] - a roll of N dice, sides X, keep worst Z results and sum them - # NdXrZ - a roll of N dice, sides X, re-roll and replace Zs # NdXr[Z,add] - a roll of N dice, sides X, re-roll and add on a result of Z # NdXr[Y..Z,add] - a roll of N dice, sides X, re-roll and add on a result of Y..Z - # NdXx - exploding dice, synonym for NdXr[X,add] - # NdXmZ - mapped dice, values below Z score 0, values above Z score 1 # NdXm[>=Z,A] - mapped dice, values greater than or equal to Z score A (unmapped values score 0 by default) # These are the Parslet rules that define the dice grammar rule(:integer) { match('[0-9]').repeat(1) } + rule(:range) { integer.as(:range_start) >> str('..') >> integer.as(:range_end) } rule(:dlabel) { match('[d]') } rule(:bunch_start) { integer.as(:ndice) >> dlabel >> integer.as(:sides) } + + rule(:reroll_label) { match(['r']).as(:reroll) } + rule(:keep_label) { match(['k']).as(:keep) } + rule(:map_label) { match(['m']).as(:map) } + rule(:alias_label) { match(['x']).as(:alias) } + + rule(:single_modifier) { alias_label } + rule(:modifier_label) { reroll_label | keep_label | map_label } + rule(:simple_modifier) { modifier_label >> integer.as(:simple_value) } + rule(:complex_modifier) { modifier_label >> str('[') >> str(']') } # TODO: param extraction + + rule(:bunch_modifier) { single_modifier | simple_modifier } + rule(:bunch) { bunch_start >> bunch_modifier.repeat.as(:mods) } rule(:space) { match('\s').repeat(1) } rule(:space?) { space.maybe } rule(:operator) { match('[+-]').as(:op) >> space? } - rule(:add_bunch) { operator >> bunch_start >> space? } + rule(:add_bunch) { operator >> bunch >> space? } rule(:add_constant) { operator >> integer.as(:constant) >> space? } rule(:dice_expression) { add_bunch | add_constant } rule(:expressions) { dice_expression.repeat.as(:bunches) } root :expressions @@ -57,10 +63,29 @@ out_hash[:multiplier] = case optype when '+' then 1 when '-' then -1 end end + + # Modifiers + if in_hash[:mods] + in_hash[:mods].each do |mod| + case + when mod[:alias] + collect_alias_modifier mod, out_hash + when mod[:keep] + collect_keeper_rule mod, out_hash + when mod[:map] + out_hash[:maps] ||= [] + collect_map_rule mod, out_hash + when mod[:reroll] + out_hash[:rerolls] ||= [] + collect_reroll_rule mod, out_hash + end + end + end + out_hash end end def collect_offset dice_expressions @@ -72,8 +97,47 @@ else total -= c end total end + end + + # Called when we have a single letter convenient alias for common dice adjustments + def collect_alias_modifier alias_mod, out_hash + alias_name = alias_mod[:alias].to_s + case alias_name + when 'x' # Exploding re-roll + out_hash[:rerolls] ||= [] + out_hash[:rerolls] << [ out_hash[:sides], :==, :reroll_add ] + end + end + + # Called for any parsed reroll rule + def collect_reroll_rule reroll_mod, out_hash + out_hash[:rerolls] ||= [] + if reroll_mod[:simple_value] + out_hash[:rerolls] << [ reroll_mod[:simple_value].to_i, :>=, :reroll_replace, 1 ] + end + # TODO: Handle complex descriptions + end + + # Called for any parsed keeper mode + def collect_keeper_rule keeper_mod, out_hash + if keeper_mod[:simple_value] + out_hash[:keep_mode] = :keep_best + out_hash[:keep_number] = keeper_mod[:simple_value].to_i + return + end + # TODO: Handle complex descriptions + end + + # Called for any parsed map mode + def collect_map_rule map_mod, out_hash + out_hash[:maps] ||= [] + if map_mod[:simple_value] + out_hash[:maps] << [ map_mod[:simple_value].to_i, :<=, 1 ] + return + end + # TODO: Handle complex descriptions end end # class Parser