mirror of
https://github.com/Faraphel/Atlas-Install.git
synced 2025-07-02 02:38:30 +02:00
164 lines
6.8 KiB
Python
164 lines
6.8 KiB
Python
from typing import Generator
|
|
import re
|
|
|
|
from source.mkw import Tag, Slot
|
|
|
|
TOKEN_START = "{{"
|
|
TOKEN_END = "}}"
|
|
|
|
common_token_map = { # these operators and function are considered safe to use in the template
|
|
operator: operator
|
|
for operator in
|
|
["+", "-", "*", "/", "%", "**", ",", "(", ")", "[", "]", "==", "!=", "in", ">", "<", ">=", "<=", "and", "or", "&",
|
|
"|", "^", "~", "<<", ">>", "not", "is", "if", "else", "abs", "int", "bin", "hex", "oct", "chr", "ord", "len",
|
|
"str", "bool", "float", "round", "min", "max", "sum", "zip", "any", "all", "issubclass", "reversed", "enumerate",
|
|
"list", "sorted", "hasattr", "for", "range", "type", "isinstance", "repr", "None", "True", "False"
|
|
]
|
|
|
|
} | { # these methods are considered safe, except for the magic methods
|
|
f".{method}": f".{method}"
|
|
for method in dir(str) + dir(list) + dir(int) + dir(float)
|
|
if not method.startswith("__")
|
|
}
|
|
|
|
|
|
class TokenParsingError(Exception):
|
|
def __init__(self, token: str):
|
|
super().__init__(f"Invalid token while parsing track representation:\n{token}")
|
|
|
|
|
|
# representation of a custom track
|
|
class Track:
|
|
def __init__(self, special: Slot = None, music: Slot = None, tags: list[Tag] = None, weight: int = None, **kwargs):
|
|
self.special: Slot = special if special is not None else "T11"
|
|
self.music: Slot = music if music is not None else "T11"
|
|
self.tags: list[Tag] = tags if tags is not None else []
|
|
self.weight: int = weight if weight is not None else 1
|
|
|
|
# others not mandatory attributes
|
|
for key, value in kwargs.items():
|
|
# if the attribute start with __, this is a magic attribute, and it should not be modified
|
|
if key.startswith("__"): continue
|
|
setattr(self, key, value)
|
|
|
|
@classmethod
|
|
def from_dict(cls, track_dict: dict) -> "Track | TrackGroup":
|
|
"""
|
|
create a track from a dict, or create a track group is it is a group
|
|
:param track_dict: dict containing the track information
|
|
:return: Track
|
|
"""
|
|
if "group" in track_dict:
|
|
from source.mkw.TrackGroup import TrackGroup
|
|
return TrackGroup.from_dict(track_dict)
|
|
return cls(**track_dict)
|
|
|
|
def get_tracks(self) -> Generator["Track", None, None]:
|
|
"""
|
|
Get all the track elements
|
|
:return: track elements
|
|
"""
|
|
for _ in range(self.weight):
|
|
yield self
|
|
|
|
def repr_format(self, mod_config: "ModConfig", format: str) -> str:
|
|
"""
|
|
return the representation of the track from the format
|
|
:param mod_config: configuration of the mod
|
|
:return: formatted representation of the track
|
|
"""
|
|
|
|
token_map = common_token_map | { # replace the suffix and the prefix by the corresponding values
|
|
"prefix": self.get_prefix(mod_config, ""),
|
|
"suffix": self.get_prefix(mod_config, ""),
|
|
} | { # replace the track attribute by the corresponding values
|
|
f"track.{attr}": f"track.{attr}" for attr, value in self.__dict__.items()
|
|
} | { # replace the track variable by the corresponding value, if not used with an attribute
|
|
"track": "track"
|
|
}
|
|
|
|
def format_token(match: re.Match) -> str:
|
|
# get the token string without the brackets, then strip it
|
|
process_token = match.group(1).strip()
|
|
final_token: str = ""
|
|
|
|
def matched(match: re.Match | str | None, value: str = None) -> bool:
|
|
"""
|
|
check if token is matched, if yes, add it to the final token and remove it from the processing token
|
|
:param match: match object
|
|
:param value: if the match is a string, the value to replace the text with
|
|
:return: True if matched, False otherwise
|
|
"""
|
|
nonlocal final_token, process_token
|
|
|
|
# if there is no match or the string is empty, return False
|
|
if not match: return False
|
|
|
|
if isinstance(match, re.Match):
|
|
process_token_raw = process_token[match.end():]
|
|
value = match.group()
|
|
|
|
else:
|
|
if not process_token.startswith(match): return False
|
|
process_token_raw = process_token[len(match):]
|
|
|
|
process_token = process_token_raw.lstrip()
|
|
|
|
final_token += value + (len(process_token_raw) - len(process_token)) * " "
|
|
|
|
return True
|
|
|
|
while process_token: # while there is still tokens to process
|
|
# if the section is a string, add it to the final token
|
|
# example : "hello", "hello \" world"
|
|
if matched(re.match(r'^\"(?:[^"\\]|\\.)*\"', process_token)):
|
|
continue
|
|
|
|
# if the section is a float or an int, add it to the final token
|
|
# example : 102, 102.59
|
|
if matched(re.match(r'^[0-9]+(?:\.[0-9]+)?', process_token)):
|
|
continue
|
|
|
|
# if the section is a variable, operator or function, replace it by its value
|
|
# example : track.special, +
|
|
for key, value in token_map.items():
|
|
if matched(key, value):
|
|
break
|
|
|
|
# else, the token is invalid, so raise an error
|
|
else:
|
|
raise TokenParsingError(process_token)
|
|
|
|
# if final_token is set, eval final_token and return the result
|
|
if final_token:
|
|
return str(eval(final_token, {}, {"track": self}))
|
|
else:
|
|
return final_token
|
|
|
|
# pass everything between TOKEN_START and TOKEN_END in the function
|
|
return re.sub(rf"{TOKEN_START}(.*?){TOKEN_END}", format_token, format)
|
|
|
|
def get_prefix(self, mod_config: "ModConfig", default: any = None) -> any:
|
|
"""
|
|
return the prefix of the track
|
|
:param default: default value if no prefix is found
|
|
:param mod_config: mod configuration
|
|
:return: formatted representation of the track prefix
|
|
"""
|
|
for tag in filter(lambda tag: tag in mod_config.tags_prefix, self.tags):
|
|
return mod_config.tags_prefix[tag]
|
|
return default
|
|
|
|
def get_suffix(self, mod_config: "ModConfig", default: any = None) -> any:
|
|
"""
|
|
return the suffix of the track
|
|
:param default: default value if no suffix is found
|
|
:param mod_config: mod configuration
|
|
:return: formatted representation of the track suffix
|
|
"""
|
|
for tag in filter(lambda tag: tag in mod_config.tags_suffix, self.tags):
|
|
return mod_config.tags_suffix[tag]
|
|
return default
|
|
|
|
def get_highlight(self, mod_config: "ModConfig", default: any = None) -> any:
|
|
...
|