improve die parser

This commit is contained in:
Denis-Cosmin Nutiu 2024-01-21 21:54:53 +02:00
parent 700007893e
commit a59a4c203f
3 changed files with 27 additions and 16 deletions

View file

@ -1,36 +1,41 @@
import logging
import tatsu
from src.dice.semantics import DieSemantics
DIE_GRAMMAR = '''
DIE_GRAMMAR = """
@@grammar::Die
@@whitespace :: None
start = [number_of_dies:number] die:die [modifier:die_modifier] $;
die = die_type:die_type die_number:number;
die_modifier = op:die_modifier_op modifier:number;
die_modifier_op = '+' | '-';
start = die:die [modifier:die_modifier] $;
die = [number_of_dies:number] die_type:die_type die_number:number;
die_modifier = op:operator modifier:number;
operator = '+' | '-';
die_type = 'd' | 'zd';
number = /[0-9]+/ ;
'''
"""
class DieParser:
"""
Parser for the die grammar defined above.
Parser for the die grammar defined above.
"""
def __init__(self):
self._parser = tatsu.compile(DIE_GRAMMAR)
self._semantics = DieSemantics()
self._logger = logging.getLogger("DieParser")
def parse(self, expression: str) -> int:
"""
Parses the die expression and returns the result.
Parses the die expression and returns the result.
"""
clean_expression = "".join(expression.split())
result = self._parser.parse(clean_expression, semantics=self._semantics)
number_of_dies = result.get("number_of_dies") or 1
modifier = result.get("modifier") or 0
die = result.get("die")
return number_of_dies * die + modifier
logging.debug(f"rolling die for {expression} -> result={result}")
return result

View file

@ -7,19 +7,25 @@ class DieSemantics:
def number(self, ast):
return int(ast)
def start(self, ast):
modifier = ast.get("modifier") or 0
die = ast.get("die")
return die + modifier
def die(self, ast):
if not isinstance(ast, AST):
return ast
number_of_dies = ast.get("number_of_dies") or 1
die_type = ast.get("die_type")
die_number = ast.get("die_number", 1)
die_number = ast.get("die_number") or 1
if die_number <= 0:
raise ValueError(f"Invalid die number: {die_number}")
return 0
# normal die
if die_type == "d":
return random.randint(1, die_number)
return sum([random.randint(1, die_number) for _ in range(number_of_dies)])
# zero-based die can output 0.
if die_type == "zd":
return random.randint(0, die_number)
return sum([random.randint(0, die_number) for _ in range(number_of_dies)])
raise ValueError(f"Invalid die type: {die_type}")
def die_modifier(self, ast):

0
tests/dice/__init__.py Normal file
View file