diff --git a/app/hlds/definitions.py b/app/hlds/definitions.py index 252e746..45b27d2 100644 --- a/app/hlds/definitions.py +++ b/app/hlds/definitions.py @@ -1,7 +1,9 @@ # Import this class to load the standard HLDS definitions from os import path +from typing import List from .parser import parse_all_directory +from .models import Location __all__ = ["location_definitions"] @@ -10,4 +12,4 @@ __all__ = ["location_definitions"] DATA_DIR = path.join(path.dirname(__file__), "..", "..", "data") # pylint: disable=invalid-name -location_definitions = parse_all_directory(DATA_DIR) +location_definitions: List[Location] = parse_all_directory(DATA_DIR) diff --git a/app/hlds/hlds.tatsu b/app/hlds/hlds.tatsu index bff6d7e..d1edf86 100644 --- a/app/hlds/hlds.tatsu +++ b/app/hlds/hlds.tatsu @@ -80,7 +80,7 @@ identifier = /[a-z0-9_-]+/ ; string = /[^\n]+/ ; choice_type = 'single_choice' | 'multi_choice' ; -number = /[0-9]+(\.[0-9]+)?/ ; +int = /[0-9]+/ ; currency = '€' ; -price = currency:currency s value:number ; +price = currency:currency s value_unit:int [ '.' value_cents:/[0-9]{,2}/ ] ; diff --git a/app/hlds/models.py b/app/hlds/models.py index 510a53f..881cdf5 100644 --- a/app/hlds/models.py +++ b/app/hlds/models.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # pylint: disable=too-few-public-methods -from typing import List +from typing import List, Mapping, Any def _format_tags(tags): @@ -19,11 +19,11 @@ def _format_type_and_choice(type_and_choice): class Option: def __init__(self, id_, *, name, description, price, tags): - self.id = id_ - self.name = name - self.description = description - self.price = price - self.tags = tags + self.id: str = id_ + self.name: str = name + self.description: str = description + self.price: int = price + self.tags: List[str] = tags def __str__(self): return "{0.id}: {0.name}{1}{2}{3}".format( @@ -36,9 +36,9 @@ class Option: class Choice: def __init__(self, id_, *, name, description, options): - self.id = id_ - self.name = name - self.description = description + self.id: str = id_ + self.name: str = name + self.description: str = description self.options: List[Option] = options @@ -52,11 +52,11 @@ class Choice: class Dish: def __init__(self, id_, *, name, description, price, tags, choices): - self.id = id_ - self.name = name - self.description = description - self.price = price - self.tags = tags + self.id: str = id_ + self.name: str = name + self.description: str = description + self.price: int = price + self.tags: List[str] = tags self.choices: List[(str, Choice)] = choices @@ -72,9 +72,9 @@ class Dish: class Location: def __init__(self, id_, *, name, attributes, dishes): - self.id = id_ - self.name = name - self.attributes = attributes + self.id: str = id_ + self.name: str = name + self.attributes: Mapping[str, Any] = attributes self.dishes: List[Dish] = dishes diff --git a/app/hlds/parser.py b/app/hlds/parser.py index 9792d93..58d231c 100644 --- a/app/hlds/parser.py +++ b/app/hlds/parser.py @@ -3,7 +3,9 @@ from glob import glob from os import path import itertools +from typing import Iterable, List, Union, Tuple from tatsu import parse as tatsu_parse +from tatsu.ast import AST from .models import Location, Choice, Option, Dish @@ -18,7 +20,7 @@ def filter_instance(cls, iterable): # pylint: disable=no-self-use class HldsSemanticActions: - def location(self, ast): + def location(self, ast) -> Location: choices = {choice.id: choice for choice in filter_instance(Choice, ast["items_"])} dishes = filter_instance(Dish, ast["items_"]) for dish in dishes: @@ -33,7 +35,7 @@ class HldsSemanticActions: dishes=dishes, ) - def base_block(self, ast): + def base_block(self, ast) -> Dish: return Dish( ast["id"], name=ast["name"], @@ -43,7 +45,7 @@ class HldsSemanticActions: choices=ast["choices"], ) - def choice_block(self, ast): + def choice_block(self, ast) -> Choice: return Choice( ast["id"], name=ast["name"], @@ -51,14 +53,14 @@ class HldsSemanticActions: options=ast["entries"], ) - def indent_choice_block(self, ast): + def indent_choice_block(self, ast) -> Tuple[str, Union[Choice, AST]]: return ( (ast["type"], self.choice_block(ast)) if ast["kind"] == "declaration" else (ast["type"], ast["id"]) ) - def indent_choice_entry(self, ast): + def indent_choice_entry(self, ast) -> Option: return Option( ast["id"], name=ast["name"], @@ -69,8 +71,15 @@ class HldsSemanticActions: noindent_choice_entry = indent_choice_entry - def price(self, ast): - return "{0[currency]} {0[value]}".format(ast) + def price(self, ast) -> int: + return ( + 100 * int(ast["value_unit"]) + + ( + 0 if not ast["value_cents"] else + 10 * int(ast["value_cents"]) if len(ast["value_cents"]) == 1 else + int(ast["value_cents"]) + ) + ) def _default(self, ast): return ast @@ -78,22 +87,22 @@ class HldsSemanticActions: SEMANTICS = HldsSemanticActions() -def parse(menu): +def parse(menu: str) -> List[Location]: parsed = tatsu_parse(GRAMMAR, menu, semantics=SEMANTICS) return parsed -def parse_file(filename): +def parse_file(filename: str) -> List[Location]: with open(filename, "r") as file_handle: return parse(file_handle.read()) -def parse_files(files): +def parse_files(files: Iterable[str]) -> List[Location]: menus = map(parse_file, files) return list(itertools.chain.from_iterable(menus)) -def parse_all_directory(directory): +def parse_all_directory(directory: str) -> List[Location]: # TODO Use proper way to get resources, see https://stackoverflow.com/a/10935674 files = glob(path.join(directory, "**.hlds"), recursive=True) return parse_files(files)