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