diff --git a/README.md b/README.md index 9182ec0..b208fea 100644 --- a/README.md +++ b/README.md @@ -71,6 +71,15 @@ Every chat instance exposes the following methods: The handler accepts two parameters: a message and a state object, and returns the response and the new state object. If the returned state object is `None`, then the chat loop exits. Non-command handler (i.e. what should be done on any user input that doesn't start with a registered command) is registered under the `default` command. +### `deck.xlat` + +Character translation helper library. For now, only supports base Cyrillic character set. + +Exports the following functions: + +- `enc(s, mapname='CYR')`: encode from the selected character set into Latin +- `dec(s, mapname='CYR')`: decode from Latin into the selected character set + ### `deck.fs` FS helper librаry. diff --git a/deck/xlat.py b/deck/xlat.py new file mode 100644 index 0000000..907bc18 --- /dev/null +++ b/deck/xlat.py @@ -0,0 +1,72 @@ +# deck.xlat: character translation routines with some presets +# Usage: from deck import xlat +# If there's no preset selected, CYR (cyrillic) gets selected by default +# Created by Luxferre in 2025, released into public domain + +maps = { + 'CYR': { + 'prefix_chars': ['J', 'X', 'Y', 'j', 'x', 'y'], + 'source': '\u0410\u0430\u0411\u0431\u0412\u0432\u0413\u0433\u0490\u0491\u0414\u0434\u0415\u0435\u0404\u0454\u0401\u0451\u0416\u0436\u0417\u0437\u0418\u0438\u0406\u0456\u0407\u0457\u0419\u0439\u041a\u043a\u041b\u043b\u041c\u043c\u041d\u043d\u041e\u043e\u041f\u043f\u0420\u0440\u0421\u0441\u0422\u0442\u0423\u0443\u0424\u0444\u0425\u0445\u0426\u0446\u0427\u0447\u0428\u0448\u0429\u0449\u042a\u044a\u042b\u044b\u042c\u044c\u042d\u044d\u042e\u044e\u042f\u044f', + 'mapping': [ + 'A', 'a', 'B', 'b', 'W', 'w', 'G', 'g', + 'XG', 'xg', + 'D', 'd', 'E', 'e', + 'JE', 'je', 'JO', 'jo', + 'V', 'v', 'Z', 'z', 'I', 'i', + 'YI', 'yi', 'JI', 'ji', 'JJ', 'jj', + 'K', 'k', 'L', 'l', 'M', 'm', 'N', 'n', + 'O', 'o', 'P', 'p', 'R', 'r', 'S', 's', + 'T', 't', 'U', 'u', 'F', 'f', 'H', 'h', + 'C', 'c', + 'XX', 'xx', 'XW', 'xw', 'XQ', 'xq', + "x'", "x'", 'YY', 'yy', "'", "'", + 'YE', 'ye', 'JU', 'ju', 'Q', 'q' + ] + } +} + +# generate direct and reverse key-value mappings +for mkey, m in maps.items(): + maps[mkey]['s2m'] = {} + maps[mkey]['m2s'] = {} + idx = 0 + for char in m['source']: + dst = m['mapping'][idx] + maps[mkey]['s2m'][char] = dst + maps[mkey]['m2s'][dst] = char + idx += 1 + +def enc(s, cmap=None): + if cmap == None: + cmap = 'CYR' + mapobj = maps[cmap] + out_str = '' + for char in s: + if char in mapobj['s2m']: + out_str += mapobj['s2m'][char] + else: + out_str += char + return out_str + +def dec(s, cmap=None): + if cmap == None: + cmap = 'CYR' + mapobj = maps[cmap] + prefixes = mapobj['prefix_chars'] + m2s = mapobj['m2s'] + out_str = '' + buf = '' + for char in s: + if char in prefixes: + buf += char + else: + if buf != '': + buf += char + else: + buf = char + if buf in m2s: + out_str += m2s[buf] + else: + out_str += buf + buf = '' + return out_str