1
Fork 0
mirror of https://github.com/RGBCube/DML synced 2025-07-27 23:57:44 +00:00

Initial Commit

This commit is contained in:
RGBCube 2022-03-19 22:15:26 +03:00 committed by GitHub
parent 66739939c2
commit 1e815a2dc0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 243 additions and 1 deletions

16
dml/__init__.py Normal file
View file

@ -0,0 +1,16 @@
"""
Dotted Markup Language
Translate text to and from DML with ease.
"""
__title__ = "DML"
__author__ = "RGBCube"
__license__ = "MIT"
__copyright__ = "Copyright (c) 2022-present RGBCube"
__version__ = "1.0.0"
from .encoder import *
from .errors import *
from .fileutils import *
from .run import *

61
dml/encoder.py Normal file
View file

@ -0,0 +1,61 @@
import typing as t
from .errors import DecodeError
from .symbols import symbols as s
__all__ = ("encode", "decode")
def encode(text: t.Union[t.Generator, t.List[str], str], *, out: str = None) -> t.Optional[t.Generator[str, None, None]]:
"""Encodes text into Dotted Markup Language (DML)
Arguments:
text (Union[Generator, List[str], str]): The text to encode.
out (str): The filepath to write the encoded file to. If not specified, the encoded text will be returned as a generator.
Returns:
Optional[Generator[str, None, None]]: The encoded text as a generator.
"""
def inner() -> t.Generator[str, None, None]:
for char_ in text:
yield format(ord(char_), "b").replace("0", s["0"]).replace("1", s["1"]) + s["stop"]
if out:
out = out + ".dml" if not out.endswith(".dml") else out
with open(out, "w") as f:
for char in inner():
f.write(char)
else:
return inner()
def decode(text: t.Union[t.Generator, t.List[str], str], *, out: str = None) -> t.Optional[t.Generator[str, None, None]]:
"""Decodes text from Dotted Markup Language (DML)
Arguments:
text (Union[Generator, List[str], str]): The text to decode.
out (str): The filepath to write the decoded file to. If not specified, the decoded text will be returned as a generator.
Returns:
Optional[Generator[str, None, None]]: The decoded text as a generator.
Raises:
DecodeError: If the text is not valid DML.
"""
def inner() -> t.Generator[str, None, None]:
nonlocal text
if isinstance(text, str):
text = [e + s["stop"] for e in text.split(s["stop"]) if e]
for char_ in text:
if any(e not in s.values() for e in char_):
raise DecodeError(f"Invalid character: '{char_}', expected {s['1']}, {s['0']} or {s['stop']}")
yield chr(int(char_[:-1].replace(s['0'], "0").replace(s['1'], "1"), 2))
if out:
with open(out, "w") as f:
for char in inner():
f.write(char)
else:
return inner()

11
dml/errors.py Normal file
View file

@ -0,0 +1,11 @@
__all__ = ("DottedMarkupLanguageException", "DecodeError")
class DottedMarkupLanguageException(Exception):
"""Base class for all exceptions in this module."""
pass
class DecodeError(DottedMarkupLanguageException):
"""Raised when there is an error decoding a string."""
pass

67
dml/fileutils.py Normal file
View file

@ -0,0 +1,67 @@
import typing as t
from .encoder import encode, decode
from .errors import DecodeError
from .symbols import symbols as s
__all__ = ("decode_file", "encode_file")
def encode_file(fp: str, *, out: str = None) -> t.Optional[t.Generator[str, None, None]]:
"""Encodes a file to DML.
Arguments:
fp (str): The filepath to the file to encode.
out (str): The filepath to write the encoded file to. If not specified, the encoded text will be returned as a generator.
Returns:
Optional[Generator[str, None, None]]: The encoded text as a generator.
"""
def read_file() -> t.Generator[str, None, None]:
with open(fp) as f_:
while char_ := f_.read(1):
yield char_
if out:
out = out + ".dml" if not out.endswith(".dml") else out
with open(out, "w") as f:
for char in encode(read_file()):
f.write(char)
else:
return encode(read_file())
def decode_file(fp: str, *, out: str = None) -> t.Optional[t.Generator[str, None, None]]:
"""Decodes a file that has DML encoded in it.
Arguments:
fp (str): The filepath to the file to decode.
out (str): The filepath to write the decoded file to. If not specified, the decoded text will be returned as a generator.
Returns:
Optional[Generator[str, None, None]]: The decoded text as a generator.
Raises:
DecodeError: If the file is not valid DML.
"""
def read_file() -> t.Generator[str, None, None]:
buffer = ""
with open(fp) as f_:
while char_ := f_.read(1):
if char_ not in s.values():
raise DecodeError(f"Invalid character: '{char_}', expected {s['1']}, {s['0']} or {s['stop']}")
elif char_ != s["stop"]:
buffer += char_
else:
yield buffer + s["stop"]
buffer = ""
if out:
out = out + ".dml" if not out.endswith(".dml") else out
with open(out, "w") as f:
for char in decode(read_file()):
f.write(char)
else:
return decode(read_file())

12
dml/run.py Normal file
View file

@ -0,0 +1,12 @@
from .encoder import decode
from .fileutils import decode_file
__all__ = ("run_file", "run")
def run(dml: str) -> None:
exec("".join(decode(dml)))
def run_file(fp: str) -> None:
exec("".join(decode_file(fp)))

5
dml/symbols.py Normal file
View file

@ -0,0 +1,5 @@
symbols = {
"1": "\u0701", # Syriac Supralinear Full Stop
"0": "\u0660", # Arabic-Indic Digit Zero
"stop": "\u06F0", # Extended Arabic-Indic Digit Zero
}