https://habr.com/post/419361/- Ненормальное программирование
- Python
- JavaScript
Файл AppData\Local\Dropbox\info.json:
{
"personal": {
"host": 5060852864,
"is_team": false,
"subscription_type": "Basic",
"path": "C:\\Users\\DNS\\Dropbox"
}
}
|
В новом формате выглядит так:
personal
host = 5060852864
is_team = 0B
subscription_type = Basic
path = C:\Users\DNS\Dropbox
|
Конфигурационный файл редактора Sublime Text:
{
"added_words":
[
"plugin",
"habrahabr",
"закомментированным"
],
"default_line_ending": "unix",
//"font_face": "Fira Code",
"font_size": 11,
"ignored_packages":
[
"Sublimerge Pro",
"Vintage"
],
"ignored_words":
[
"utf"
],
"line_numbers": false,
"show_encoding": true,
"show_line_endings": true,
"tab_size": 4,
"theme": "Default.sublime-theme"
}
|
В новом формате выглядит так:
added_words = [
plugin
habrahabr
закомментированным
]
default_line_ending = unix
//font_face = Fira Code
font_size = 11
ignored_packages = [
Sublimerge Pro
Vintage
]
ignored_words = [
utf
]
line_numbers = 0B
show_encoding = 1B
show_line_endings = 1B
tab_size = 4
theme = Default.sublime-theme
|
Немного истории
Данный формат обязан своим появлением другому формату со странным названием blockpar.
Blockpar разработал
Алексей Дубовой (один из основателей Elemental Games) в процессе работы над игрой Космические рейнджеры. Впоследствии
Александр Зеберг (бывший ведущий программист Katauri Interactive
[после "распада" компании на Katauri Interactive и Elemental Games он ушёл в Katauri]) решил использовать данный формат для игры King's Bounty: Легенда о рыцаре.
Определение каждого игрового объекта хранилось в формате blockpar в отдельном файле с расширением .atom, например вот вырезка из файла
data/data.kfs/spider.atom:
main {
class=chesspiece
model=spider.bma
cullcat=0
}
arena_params {
race=neutral
cost=24
level=1
leadership=14
attack=4
defense=4
...
resistances {
physical=20
poison=0
magic=0
fire=-10
}
...
}
...
Впоследствии
[во время работы над проектом Royal Quest] я немного расширил этот формат, чтобы можно было вместо:
button {
name=close
pos=400,600
size=200,40
image=button_close.png
anchors=0,0,100,0
}
писать так:
button=close,400,600,200,40 {
image=button_close.png
anchors=0,0,100,0
}
Также я добавил поддержку многострочных строковых значений посредством обратного апострофа (backtick — `):
button=... {
onmouseover=`
...
`
}
Почему я отказался от `строк, заключённых в обратные апострофы`В значении параметров допустимо непосредственно вставлять код скриптов, а в комментариях в коде я использую обратные апострофы в том же значении, как они применяются в Markdown и
пк-разметке. Например:
if len(indentation_levels) and indentation_levels[-1][0] == None: # сразу после символа `{` идёт новый произвольный отступ, который действует вплоть до парного символа `}`
И наконец в результате моего знакомства с Python-ом идея отказа от фигурных скобок настолько захватила меня, что я решил, что формат blockpar можно ещё больше упростить
[отказавшись от обязательных фигурных скобок].
Также влияние на меня оказали:
- Текстовый формат хранения в Yet Another Serialization Library, использующейся в клиенте Royal Quest.
- Формат файла конфигурации nginx (впрочем, я отбросил идею отказаться от разделителя (символа
=
или :
) между именем параметра и его значением (почему)).
- YAML (а именно идея использовать
.
перед именем элемента массива [в YAML используется -
]).
Почему 0В и 1В?
- Зачастую true/false используются в значении yes/no, on/off или enable/disable (например: you can enable show line endings; turn logging on/off; is digit? yes), а в булевой алгебре используются 0 и 1, так что использование ключевых слов true и false в большинстве случае довольно спорно.
- Мне не нравится истина/ложь в русской версии формата, а 0В и 1В (здесь В — русская заглавная в) можно связать с 0Выключено и 1Включено. [Вопрос о целесообразности русской версии прошу не поднимать.]
Строки в одиночных парных кавычках
Ещё один
[помимо 0В и 1В] спорный/непривычный элемент данного формата — это использование парных кавычек
‘’
для сырых строк
[без управляющих последовательностей \ escape sequences].
Но мой выбор оправдывает тот факт, что Консорциум Юникода утвердил этот год — кодом открывающей одиночной парной кавычки.
Как такие кавычки набирать на клавиатуре — смотрите
здесь.
Если в строке присутствуют непарные кавычки, тогда нужно выполнить "балансировку строки" аналогично тому, как это делается в пк-разметке для вставки HTML-кода.
Например есть строка
don’t
.
Так как в ней присутствует несбалансированная закрывающая кавычка, добавим балансирующую открывающую кавычку в самое начало строки:
‘
don’t
.
Сбалансированную строку заключаем в парные кавычки:
‘
‘don’t
’
.
Теперь необходимо как-то показать парсеру, что добавленную слева кавычку не следует включать в строку, так как она нужна только для восстановления
баланса. Для этого используется символ машинописного апострофа ', который нужно поставить по одной штуке на каждую балансирующую кавычку
[таким образом, один машинописный апостроф "съедает" одну парную кавычку], в данном случае его необходимо поставить в начало строки:
'‘‘don’t’
.
Сбалансированную строку можно как есть вставлять в другие строки в парных кавычках:
‘text = '‘‘don’t’’
.
Использование
В данный момент есть реализация
на Python и
на JavaScript (можно попробовать cконвертировать JSON в новый формат прямо в браузере на
веб-странице проекта).
Для Python — устанавливаем как обычно:
pip install thindf
Для JavaScript:
npm install thindf
node
const thindf = require('thindf');
И используем:
thindf.to_thindf(object, indent = 4)
для получения строки в формате thindf соответствующей переданному объекту (аналог json.dumps
и JSON.stringify
).
thindf.parse(str)
для получения объекта из строки в формате thindf (аналог json.loads
и JSON.parse
).
В заключение приведу ещё несколько примеров:Несколько строчек из
моего Default (Windows).sublime-keymap:
[
{ "keys": ["f4"], "command": "f4" },
{ "keys": ["shift+f4"], "command": "f4", "args": {"shift_key_pressed": true} },
{ "keys": ["alt+shift+`"], "command": "insert", "args": {"characters": "`"} }, // (
{ "keys": [":", ")"], "command": "insert_snippet", "args": {"contents": ":)(:"} },
{ "keys": ["alt+9"], "context": [{"key": "selector", "operator": "equal", "operand": "text.pq"}], "command": "insert_pq" }, // ‘ (for balance)
{ "keys": ["alt+0"], "context": [{"key": "selector", "operator": "equal", "operand": "text.pq"}], "command": "insert", "args": {"characters": "’"} },
]
С использованием нового формата я бы записал так:
f4 = on_f4()
shift+f4 = on_f4(shift_key_pressed' 1B)
alt+shift+` = insert(characters' ‘`’) // (
:,) = insert_snippet(contents' ‘:)(:’)
alt+9 = if selector == ‘text.pq’ {insert_pq()} else 0B // ‘ (for balance)
alt+0 = if selector == ‘text.pq’ {insert(characters' "’")} else 0B
Кусочек из файла
d.json [из репозитория менеджера плагинов для Sublime Text]:
{
"schema_version": "3.0.0",
"packages": [
{
"name": "Django Lookup Snippets",
"details": "https://github.com/icycandle/sublime-django-lookup",
"releases": [
{
"sublime_text": "*",
"tags": true
}
]
},
{
"name": "Django Manage Commands",
"details": "https://github.com/vladimirnani/DjangoCommands",
"labels": ["Django", "python", "web", "management"],
"releases": [
{
"sublime_text": "<3000",
"tags": "st2-"
},
{
"sublime_text": ">=3000",
"tags": "st3-"
}
]
}
]
}
В новом формате выглядит так:
schema_version = ‘3.0.0’
packages = [
. name = Django Lookup Snippets
details = https://github.com/icycandle/sublime-django-lookup
releases = [
. sublime_text = *
tags = 1B
]
. name = Django Manage Commands
details = https://github.com/vladimirnani/DjangoCommands
labels = [
Django
python
web
management
]
releases = [
. sublime_text = <3000
tags = st2-
. sublime_text = >=3000
tags = st3-
]
]
Some corner cases: |
{
"a": "‘...’",
"b": "string which ends with a space ",
"c d": "\n",
"e ": "3",
"dirs": [
["Doc,Scans", ".t’xt"]
],
"node": null,
"n" : "N",
"files": [],
"f": "[]",
"ff": [
[]
],
"products": {}
}
|
a = ‘‘...’’
b = ‘string which ends with a space ’
c d = "\n"
‘e ’ = ‘3’
dirs = [
[‘Doc,Scans’, '‘‘.t’xt’]
]
node = N
n = ‘N’
files = []
f = ‘[]’
ff = [
[]
]
products = {}
|