thindf - new text data format (alternative to JSON)

File AppData \ Local \ Dropbox \ info.json:
{
    "personal": {
        "host": 5060852864,
        "is_team": false,
        "subscription_type": "Basic",
        "path": "C:\\Users\\DNS\\Dropbox"
    }
}
In the new format looks like this:
 
personal
    host = 5060852864
    is_team = 0B
    subscription_type = Basic
    path = C:\Users\DNS\Dropbox
Sublime Text Editor Configuration File:
{
    "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"
}
In the new format looks like this:
 
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

A bit of history


This format owes its appearance to another format with a strange name blockpar.
Blockpar was developed by Alexey Dubovoy (one of the founders of Elemental Games) while working on the game Space Rangers. Subsequently, Alexander Zeberg (the former leading programmer of Katauri Interactive [ after the company 's "disintegration" at Katauri Interactive and Elemental Games he went to Katauri ] ) decided to use this format for the game King's Bounty: Legend of the Knight.

The definition of each game object was stored in blockpar format in a separate file with the .atom extension, for example, here is a clipping from the data / data.kfs / spider.atom file :
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
  }
  ...
}
...

Subsequently [ while working on the Royal Quest project ] I slightly expanded this format so that instead of:
button {
    name=close
    pos=400,600
    size=200,40
    image=button_close.png
    anchors=0,0,100,0
}
write like this:
button=close,400,600,200,40 {
    image=button_close.png
    anchors=0,0,100,0
}

I also added support for multi-line string values ​​via the reverse apostrophe (backtick - `):
button=... {
    onmouseover=`
    ...
    `
}
Why I refused to `strings enclosed in reverse apostrophes`
In the value of the parameters, it is permissible to directly insert the script code, and in the comments in the code I use backward apostrophes in the same meaning as they are used in Markdown and PC markup . For example:
if len(indentation_levels) and indentation_levels[-1][0] == -1: # сразу после символа `{` идёт новый произвольный отступ, который действует вплоть до парного символа `}`

And finally, as a result of my acquaintance with Python, the idea of ​​abandoning curly braces so captured me that I decided that the blockpar format could be further simplified [by abandoning mandatory curly brackets ] .

Also influenced me:
  • The text storage format in the Yet Another Serialization Library used in the Royal Quest client.
  • The format of the nginx configuration file (however, I dropped the idea of ​​abandoning the separator (character =or :) between the parameter name and its value ( why )).
  • YAML (namely, the idea to use .before the name of an element of an array [ in YAML is used -] ).

Why 0B and 1B?
  • Зачастую true/false используются в значении yes/no (YES/NO используется в Objective-C), 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В используются в языке программирования 11l по причинам обозначенным в документации.

Strings in single pair quotes


Another [ in addition to 0B and 1B ] controversial / unusual element of this format is the use of double quotes ‘’for raw strings [ without escape sequences \ escape sequences ] .
But my choice justifies the fact that the Unicode Consortium approved this year - the opening single pair quote code .

How to type such quotes on the keyboard - see here .

If there are unmatched quotes in the string, then you need to "balance the string" in the same way as it is done in the PC markup to insert HTML-code.
For example, there is a string don’t.
Since it has a unbalanced closing quotation mark, add balancing a quote at the very beginning of the line: . Balanced line conclude in smart quotes: . Now you need to somehow show the parser that the quotation mark added to the left should not be included in the string, since it is needed only to restore the balance . To do this, use the apostrophe character typed 'to be put on one piece for each balancing quote [ so one typewritten apostrophe "eats" one smart quotes ] , in this case it is necessary to put in the beginning of the line: . A balanced string can be inserted as it is into other strings in double quotes:don’t
‘don’t
'‘‘don’t’

‘text = '‘‘don’t’’.

Using


At the moment there is an implementation in Python and in JavaScript (you can try to convert JSON to a new format directly in the browser on the project’s web page ).

For Python, install as usual:
pip install thindf

For javascript:
npm install thindf
node
const thindf = require('thindf');

And we use:
  • thindf.to_thindf(object, indent = 4)to get a string in thindf format corresponding to the transferred object (analogue json.dumpsand JSON.stringify).
  • thindf.parse(str)to get an object from a string in thindf format (analogue json.loadsand JSON.parse).

In conclusion, I will give some more examples:
Несколько строчек из моего 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 = {}

Also popular now: