Сантехник. Чиню протечки в абстракциях. Не дорого.
35,1
рейтинг
19 января 2015 в 02:03

Разработка → Tree — убийца JSON, XML, YAML и иже с ними

Здравствуйте, меня зовут Дмитрий Карловский и я… много думал. Думал я о том, что не так с XML и почему его в последнее время променяли, на бестолковый JSON. Результатом этих измышлений стал новый стандарт формат данных, который вобрал в себя гибкость XML, простоту JSON и наглядность YAML.

image Tree — двумерный бинарно-безопасный формат представления структурированных данных. Легко читаемый как человеком так и компьютером. Простой, компактный, быстрый, выразительный и расширяемый. Сравнивая его с другими популярными форматами, можно составить следующую сравнительную таблицу:
Больше — лучше JSON XML YAML INI Tree
Человекопонятность 3 1 4 5 5
Удобство редактирования 3 1 4 5 5
Произвольная иерархия 3 3 3 1 5
Простота реализации 3 2 1 5 5
Скорость парсинга/сериализации 3 1 1 5 5
Размер в сериализованном виде 3 1 4 5 5
Поддержка поточной обработки 0 0 5 5 5
Бинарная безопасность 3 0 0 0 5
Распространённость 5 5 3 3 0
Поддержка редакторами 5 5 3 5 1
Поддержка языками программирования 5 5 3 5 1

Сравнение форматов


Человекопонятность

JSON и XML позволяют произвольно форматировать вывод пробелами и переносами строк. Однако, часто по различным причинам (основные — меньший объём, проще реализация) их форматируют в одну строку и тогда они становятся крайне не читаемыми.

{ "users" : [ { "name" : "Alice" , age : 20 } ] }

<users><user><name>Alice</name><age>20</age></user></users>

Кроме того, JSON не поддерживает многострочные тексты — они всегда представляются в виде одной строки, со специальной escape-последовательностью вместо переводов строк.

{ "description" : "Hello, Alice!\nHow do you do?" }

С другой стороны, XML позволяет внедрять свои тэги внутрь текста, что наглядно для простой разметки типа «выделение жирным», но сложная разметка типа «гиперссылка» даёт резко противоположный эффект.

<greeting>
    Hello, <b>Alice</b>!<br/>
    How do you do?
</greeting>

<greeting>
    Hello, <a href="http://example.org/user/alice?ref=xe3o7rubvo283xb">Alice</a>!<br/>
    How do you do?
</greeting>

Если текст содержит «специальные символы», то их приходится экранировать escape-последовательностями. В XML эти последовательности особенно громоздки и ненаглядны. А вот в Tree, наоборот, экранирование не требуется вовсе.

<title>"Rock&roll" = life</title>

{ "title" : "\"Rock&roll\" = life" }

image

Удобство редактирования

JSON и XML довольно неудобно редактировать без специальных редакторов, понимающих их синтаксис. Как минимум необходима разноцветная подсветка лексем. Очень помогает — автоформатирование, автодополнение и подсветка ошибок. К сожалению, экранировать спецсимволы приходится вручную во всех форматах, кроме Tree, где оно не требуется.

Произвольная иерархия

INI имеет жёстко ограниченную глубину иерархии.

В XML произвольные дочерние узлы могут быть только у элементов — это вынуждает использовать их вместо, например, аттрибутов, для большей гибкости и единообразия.

JSON и YAML для создания иерархий предлагают «списки» и «мапки». Не все структуры данных хорошо представимы с их помощью. Например, различные AST, где имена узлов могут повторяться и порядок следования которых важен.

В Tree есть только один тип узлов и любой узел может содержать произвольные дочерние. Как следствие, он не накладывает никаких ограничений на иерархию.

Простота реализации

JSON

Довольно простая грамматика (30 паттернов), чем и обусловлено большое число реализаций под разные языки.

XML

Довольно сложная грамматика (90 паттернов), которая могла бы быть куда проще, если бы не требование совместимости с sgml.

YAML

Крайне сложная грамматика (210 паттернов). Нужно быть очень терпеливым человеком, чтобы реализовать все нюансы, и потратить много человекочасов, чтобы избавиться ото всех багов.

INI

Крайне простая грамматика (8 паттернов), позволяющая описывать лишь одну, довольно простую структуру (ключ-ключ-значение).

Tree

Очень простая грамматика (10 паттернов), что, однако, не мешает описывать с его помощью произвольные иерархические структуры.

Скорость парсинга/сериализации

Не вдаваясь в сравнение скорости работы конкретных имплементаций, оценим теоретические пределы скоростей работы с разными формтами.

Предельная скорость обработки данных зависит от сложности синтаксиса. Именно поэтому YAML парсится на порядок дольше, чем JSON, а XML по скорости где-то между ними.

Tree помимо простой грамматики имеет ещё одно существенное преимущество — отсутствие необходимости в экранировании и разэкранировании спецсимволов.

Размер в сериализованном виде

Примеры файлов на разных языках: github.com/nin-jin/tree.d/tree/master/formats

Как видно, существенно больше всех места занимает XML, даже если его минифицировать. JSON в читабельном виде и YAML где-то по середине. А самые компактные — INI, Tree и минифицированный в одну строку JSON.

Поддержка поточной обработки

Поддерживающие поточную обработку форматы, позволяют добавлять данные в файл, просто подклеивая их в конец. Яркий пример — различные логи. И наоборот — нормально распарсить данные, имея лишь некоторое число начальных строк.

В случае XML и JSON такой возможности нет — документ с обрезанным концом или дополнительными данными в конце, является невалидным.

Бинарная безопасность

Почти все текстовые форматы не совместимы с бинарными данными. Именно поэтому Tree — на самом деле не текстовый формат, хотя его и можно редактировать в текстовом редакторе при соблюдении некоторых ограничений (использовать только unix-переводы строк, табуляцию для отступов, и не использовать произвольные бинарные данные).

Распространённость

XML довольно продолжительное время был в тренде, так что нашёл применение во множестве мест. Сейчас уверенными темпами популярность набирает JSON, благодаря своей простоте, но ценой некоторой потери гибкости. INI за счёт своей ограниченности применялся лишь для различных конфигов, но сейчас замещается более гибкими форматами. YAML остаётся довольно нишевым форматом ввиду своей переусложнённости, хотя и снискал некоторую популярность у любителей «писать меньше, делать больше, а потом хоть трава не расти». Tree пока ещё вначале пути и надеюсь не в конце.

Поддержка редакторами

XML и JSON благодаря своей популярности поддерживаются везде. Над поддержкой YAML многие разработчики редакторов просто не видят целесообразности заморачиваться. INI настолько прост, что для него никакой особой поддержки и не нужно. С Tree в принципе та же картина, но есть один плагин к IDEA о котором будет рассказано далее.

Поддержка языками программирования

Тут в целом та же ситуация, что и с поддержкой редакторами. Разве что для Tree есть две реализации — на языках D и TypeScript/JavaScript.

Подробнее о Tree


Уровни представления

Уровень формата. Определяет базовую модель данных и представление её в сериализованном виде.
Уровень языка. Определяет семантику узлов и представление их в отличных от Tree форматах.
Уровень приложения. Определяет API для взаимодействия с моделью данных Tree.

Модель данных

Модель Tree крайне проста — есть только один тип узлов, и каждый узел имеет: имя, значение, список дочерних узлов. Имя и значение являются взаимоисключающими, так что условно все узлы можно разделить на 3 типа:
Имена — узлы с непустым именем и пустым значением. Используются для именования поддеревьев. В имени не может быть пробельных символов, символа перевода строки и символа равенства.
Значения — узлы с пустым именем. Используются для хранения значений. Единственное ограничение на значения — они не могут содержать символ перевода строки.
Коллекции — узлы с пустым именем и значением, но не пустым списком дочерних узлов. Используются для работы со списком узлов как с одним узлом. В результате парсинга возвращается именно коллекция, содержащая список корневых узлов.

В Tree нет комментариев или инструкций процессору, знакомых нам из XML. Нет списков или мапок из JSON и YAML. Нет специального синтаксиса для секций, как в INI. Однако они и не только они могут быть введены в языках, основанных на формате Tree.

Строковое представление

Tree-файл состоит из набора строк, разделённых символом перевода строки (0x0D). Каждая строка начинается с некоторого количества символов табуляции, показывающих какой из предков является родителем первого узла в строке. И далее идёт список узлов разделённых пробелами. Каждый следующий при парсинге вкладывается в предыдущий. Узлы-имена представляются просто своим именем. Узлы-значения – значением, предварёнными символом равенства.
В одной строке может быть произвольное число узлов-имён, но узел-значение может быть только один, причём самым последним. Значение может содержать абсолютно любые символы за исключением символа перевода строки. Когда нужно поместить произвольные бинарные данные – их предварительно надо разбить по символу перевода строки на несколько узлов-значений. А при приведении дерева к строке именованные узлы будут отброшены, а данные из узлов-значений будут выведены как есть и между ними будут вставлены переводы строк.
Наличие табуляции в строке означает, что первый узел в этой строке должен быть вложен в последний узел последней строки имеющей табуляцию на один меньше.

По сухому описанию довольно сложно ухватить суть, так, что далее будет множество наглядных иллюстраций…

Примеры применения Tree в разных областях


Контекстно свободные грамматики

Хоть формат Tree и не является контекстно свободным, но разбить на лексемы его можно по сравнительно не сложной контекстно свободной грамматике, которую можно выразить тоже в формате Tree:

image

Подробнее о языке grammar.tree
Описание грамматики состоит из списка слов, для каждого из которых внутри задан соответствующий ему шаблон.

is

Предикат эквивалентности. Обозначает, что родительский узел может быть заменён на последовательность дочерних шаблонов.

image

Данное выражение определяет STATEMENT как последовательность из некоторого «выражения», за которым следует символ «точка с запятой».

octet

Совпадает с одним октетом (8 бит) с указанным внутри шестнадцатиричным значением.

image

Тут мы определяем SEMICOLON как октет с заданным значением. Если значение опущено, то такой шаблон совпадает с любым значением.

optional

Допускает отсутствие дочернего шаблона.

image

Совпадает либо с двумя байтами: возвращения каретки после которого идёт перевода строки. Либо с одним переводом строки.

any-of

Сопоставится с одним и только одним из дочерних шаблонов.

image

list-of

Позволяет последовательно повторить дочерний шаблон произвольное число раз (но как минимум одно совпадение должно быть).

image

Тут DELIMITER совпадёт с не пустой последовательной группой пробелов.

except

Служит для исключения дочернего шаблона из родительского. Это значит, что родительский шаблон будет сопоставлен лишь с таким набором байт, с которым не может быть сопоставлен дочерний.

image

Тут мы определяем EXPRESSION как произвольное число байт ни один из которых не является «точкой с запятой».

image

А этот шаблон уже совпадёт с произвольным набором байт (в том числе содержащего «точку с запятой»), но только не с одиночной «точкой с запятой».

with-delimiter

Указывает, что совпадения сестринских шаблонов должны быть разделены дочерним шаблоном.

image

Здесь SCRIPT определён как набор выражений, разделённых заданным символом.

Лог доступа к веб-серверу

Расширяемый структурированный формат логов. Может показаться громоздким, зато очень быстро и точно парсится как человеком так и машиной.

image

Поток сообщений от сервера в чате

Специальный разделитель "---" говорит клиенту о том, что завершилась пересылка очередной порции данных и можно приступать к её обработке.

image

Вёрстка статической веб-страницы

Специальный DSL на базе Tree позволяет лаконично описывать XML любой сложности. Трансформер из xml.tree в xml понимает специальные узлы «@», «!» и «?» формируя атрибуты, комментарии и инструкции процессору.

image

Подробнее о языке xml.tree
Структурные узлы соответствующие QName – элементы. Узлы данных – текстовые узлы.

image

     <html>
        <head>
            <title>Рога & Копыта</title>
        </head>
        <body>
            <h1>Привет!</h1>
            <p>Хочешь, я расскажу тебе сказку?</p>
        </body>
    </html>

Атрибуты представляются как узлы с QName именем, помещённые в узлы с именем «@».

image

    <script type="text/javascript" src="index.js" />

Имена атрибутов повторяться не должны. Значением атрибута является текст, но также внутри может быть и дерево, которое при создании XML должно быть сериализовано в текст.

Вложенные основанные на формате Tree языки помещаются в узел с именем этого языка, который помещается в узел c именем «%». От процессора требуется поддержка используемых языков, иначе он не сможет правильно собрать XML. Вложенные языки сериализуются в строку по своим правилам и вставляются в XML в качестве текстового узла.

image

    <link
        rel="canonical"
        href="/?article=rock%26roll&author=Nin+Jin"
    />

Комментарии помещаются в узел с именем "--". Заметьте, что в комментарий помещается всё поддерево, которое сериализуется по всем правилам xml.tree.

image

    <!--<a href="/">top</a>-->

Инструкции процессору помещаются в узлы с именем "?" и могут содержать как просто какие-то значения, так и пары ключ-значение.

image

    <?xml version="1.0" stanalone="yes" ololo?>

Дамп реляционной базы данных

Заметьте, тут используется приём расширения языка. Сначала мы декларируем схему базы данных, а потом используем заданные в схеме имена в качестве DSL для описания данных.

image

Абстрактное Синтаксическое Дерево

Эта структура данных используется языковыми трансляторами в качестве внутреннего представления обрабатываемых ими языков. Программируя на NodeJS велик соблазн использовать в качестве AST — некоторое подмножество JSON.

Например, у нас есть следующий исходник на JS:

function getEl( id ){
	return document.getElementById( id )
}

Интуитивно кажется, что AST должен выглядеть как-то так:

[
	{ "function": {
		"name": "getEl",
		"args": [ "id" ],
		"body": [
			{ "return": [
				{ "get": "document" },
				{ "call": {
					"name": "getElementById",
					"args": [
					 	{ "get": "id" }
					]
				}}
			]}
		]
	}}
]

Однако, это довольно не удобный для работы формат, так как чтобы узнать тип узла нужно проитерироваться по его свойствам и взять имя первого собственного свойства. Поэтому, чаще можно встретить формат типа такого, где первый элемент списка содержит тип узла, остальные — различный набор параметров специфичный для этого типа:

[ [ "function",
    "getEl",
    [ "id" ],
    [ "return",
      [ [ "get",
          "document" ],
        [ "call",
          "getElementById",
          [ "get", "id" ]
        ]
      ]
    ]
  ]
]

А теперь сравните с реализацией в Tree формате:

image

Редактирование других форматов, через Tree представление

Основная фишка модели данных Tree заключается в том, что она позволяет описывать почти любые другие модели данных. Например, модель данных XML — это сильно усечённая модель данных Tree.

Что это значит? А значит это, что многие форматы могут быть транслированы в некоторое подмножество Tree и обратно без потерь. То есть, вместо того, чтобы редактировать, например, XML как он есть, редактор может налету преобразовать его в xml.tree язык, позволив пользователю редактировать его в более удобной и наглядной форме, а при сохранении, делать обратное преобразование и сохранять именно XML.

Структурированный UNIX-трубопровод

Суть проблематики и вариант решения с использованием JSON можно почерпнуть из статьи "JSON pipes в шелле". Вкратце: в linux все команды выводят результат в неструктурированном ориентированном на человека виде, что затрудняет компоновку их друг с другом. Там предлагается использовать JSON, который привносит структуру, но ухудшает читаемость и производительность. Формат Tree в этом случае может привнести структуру практически не теряя ни в читаемости, ни в производительности.

Язык декларативного программирования

Формат Tree мог бы решить основную проблему языка Lisp — неимоверное число скобочек. Если в Lisp всё описывается как списки, то в языке на основе Tree — всё есть деревья. Точно также программа представляла бы из себя некий AST, который мог бы модифицировать сам себя для создания DSL на все случаи жизни, достигая максимальной выразительности.

Эталонная реализация


На языке D парсинг занимает не более 50 строк, а сериализация — 20. Простейший язык запросов — 15. Итого, чуть более 100 строк занимает минимально необходимый функционал: github.com/nin-jin/tree.d

Парсинг:
    string data = cast(string) read( "path/to/file.tree" ); // read from file
    Tree tree = Tree.parse( data , "http://example.org/source/uri" ); // parse to tree

Глубокие запросы:
    Tree userNames = tree.select( "user name" ); // returns name-nodes
    Tree userNamesValues = tree.select( "user name " ); // returns value-nodes

Работа с узлами:
    string name = userNames[0].name; // get node name
    string stringValue = userNames[0].value; // get value as string with "\n" as delimiter
    uint intValue =  userNames[0].value!uint; // get value converted from string to another type

    Tree[] childs = tree.childs; // get child nodes array
    string uri = tree.uri; // get uri like "http://example.org/source/uri#3:2"

Сериализация:
    string data = tree.toString(); // returns string representation of tree
    tree.pipe( stdout ); // prints tree to output buffer

Поддержка редакторами


В данный момент есть лишь плагин подсветки синтаксиса Tree к IDEA, но он понимает только базовые конструкции формата, описанные выше. Для языков на базе Tree нужно будет сделать отдельные плагины или же один, но расширяемый с помощью схем.

Кроме того, Alex222 буквально за вечер написал плагин к SynWrite

Заключение


Надеюсь мне удалось заразить вас идеей использования замечательного формата Tree. Рассказывайте о нём другим. Пишите библиотеки на используемых вами языках. Внедряйте хотя бы опциональную его поддержку в ваши приложения. Ищите ему новые применения — уверен их ещё много. И тогда у него будет шанс на выживание в современном мире Трендо Ориентированного Программирования.

Дмитрий Карловский @vintage
карма
14,0
рейтинг 35,1
Сантехник. Чиню протечки в абстракциях. Не дорого.
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Реклама

Самое читаемое Разработка

Комментарии (610)

    • –15
      image
  • +39
    Сделайте возможным пробел в качестве первого символа значения, иначе у вас ничего не получится.
    • –12
      Имеете ввиду сделать префикс значения не "=" а "= "?

      Изначально так и было. И отступы пробелами можно было делать. И переводы строк хоть юниксовые, хоть виндовые. Но что даёт нам этот разброд и шатание? Только усложнение всех систем, которые должны это всё поддерживать. Вот в XML вообще пишут слитно имя атрибута и значение, и никто не возмущается.

      У предлагаемого вами разделителя я вижу следующие проблемы:
      1. Не очевидно, что пробел не входит в значение и вообще не очень понятно с первого взгляда в какой момент оно начинается.
      2. Легко не заметить, что поставил 2 пробела вместо 1
      3. Нужно решать, что делать с ситуацией, когда после символа равенства идёт не пробел.
      4. +1 бесполезный (не несущий дополнительной семантики, только оформительский) байт на каждое значение.
      • +29
        > Вот в XML вообще пишут слитно имя атрибута и значение, и никто не возмущается.
        Там атрибуты — самый мелкий уровень, важнее отличить их друг от друга, чем имя от значения. Тут ситуация другая.

        Большинство проблем надуманные.
        1. Можно так же сказать, что неочевидно, входит ли знак равно в значение, или пробел до знака равно в название ключа.
        2. С моноширинным шрифтом сложно. А вот легко, например, не заметить проблемы вместо табуляции, однако вас это не смущает.
        3. Ничего страшного в отсутствии пробела нет.
        4. Без него «человекопонятность» никак не тянет на пятерочку.
        • –18
          Не вижу никакой разницы. Имена и значения в Tree — это практически и есть атрибуты XML.

          1. Можно, но куда сложнее.
          2. Не сложно, некоторые даже могут начать пытаться выравнивать значения пробелами после символа равенства, как они привыкли делать, например, в JSON. Про пробелы ругнётся либо редактор, когда попытается подсветить синтаксис, либо парсер, когда попытается распарсить.
          3. Страшно — когда что-то может быть, а может не быть. Чем чётче синтаксис, тем проще с ним работать.
          4. YAML как раз и получился в погоне за «человекопонятностью», а точнее за гламурным отображением. Понятность должна быть не только для человека, но и для машины. При этом машина должна чётко понимать, что имел ввиду человек, а человек должен чётко понимать, как это поймёт машина. Лишний пробел, который кажется вам критически важным, в лучшем случае никак не повлияет на точность понимания ни человеком, ни машиной. А в худшем — внесёт дополнительную неопределённость.
          • +16
            Для меня, как для программиста, ратующего за аккуратность кода, конструкция типа

            value =2

            просто недопустима. Это выглядит ужасно и неаккуратно. Лучше уж «value=2», чем ЭТО.
            • –14
              А для максимальной аккуратности стоит писать так:
              Value is equal 2.
              

              , забросить инженерное дело и заняться дизайном.
              • +4
                Не стоит так бросаться на всех, своё же детище так закопаете. В лучшем случае появятся более успешные форки.
                Мне лично тоже правильнее смотрятся:
                user
                	name=vintage
                	age=30
                

                и
                user
                	name = vintage
                	age = 30
                

                Просто потому что приятная симметрия. Второй вариант ещё читается лучше.

                Если формат весь из себя такой гибкий, то можно было бы менять поведение парсера через первую строку в файле, а поведение по умолчанию выбрать голосованием.
                • –8
                  Чтобы понять, почему именно так, попробуйте представить, что вместо символа "=" используется символ, например, "|". Единственная его функция — не разделять ключ и значение (это хоть и частый, но всего-лишь частный случай), а именно показывать начало потока бинарных данных.

                  Формат прежде всего простой, потом уже гибкий, а потом уже читабельный и в последнюю очередь компактный. Ни в одной из этих ипостасей он не идеален и при желании (а его я смотрю у хабраюзеров хоть отбавляй) всегда можно найти к чему придраться. Но всё же, на мой взгляд, он представляет из себя неплохой компромис в отличие от остальных упомянутых тут форматов, которые в чём-то хороши, а в чём-то просто отвратительны.
                  • +2
                    Формат прежде всего простой, потом уже гибкий, а потом уже читабельный и в последнюю очередь компактный
                    Никого не волнует насколько формат прост в реализации, его пишут 1 раз, обычно вообще есть несколько либ которые все используют, и всем до лампочки насколько формат прост. Формат должен быть в первую очередь читабельным/удобным для редактирования и потом — гибким.
                    • –1
                      У каждого, работающего с форматом, человека, в голове есть свой маленький криво написанный интерпретатор.
                      • +1
                        Совершенно верно, но кривизна этого интерпретатора сильно зависит от количества неочевидных моментов в формате. Так вот, в вашем формате такой неочевидный момент в каждом знаке равенства.
                        • –2
                          От количества правил. Степень очевидности — дело привычки.
                          • 0
                            Очевидность надо измерять для человека, впервые этот формат увидевшего. А то некоторые и Qr-коды читают, так что, они от этого стали человекопонятными и очевидными?
                            • 0
                              Эскейп последовательности, конечно, куда очевидней, для впервые их увидевшего, да.
                              • 0
                                В тех же XML и JSON надо экранировать далеко не каждую строку.
                                А вот в вашем Tree непонятность с пробелом и знаком равенства — в каждой строке, содержащей узел-значение.
                                • –1
                                  Какая непонятность? После знака равенства и до конца строки — значение.
                                  • 0
                                    Очевидность надо измерять для человека, впервые этот формат увидевшего.
                  • 0
                    А чем пробел и знак равенства проще чем просто знак равенства перед значением?

                    И как реализовать многострочное значение без \n? Только начиная каждую строку со знака пробела и равенства? Не выход.
                    • 0
                      Пробел хорошо визуально разделяет узлы и подчёркивает, что знак равенства — это не разделитель ключа и значения, а просто префикс. Другого такого удобного префикса нет (заметьте, что все специальные символы вводятся в любой раскладке нажатием одной клавиши).

                      Да, именно так. Благодаря префиксам снимаются неоднозначности вида «Обрезать ли ведущий и завершающий переводы строк?», «Обрезать ли отступы?» и «Если обрезать, то на сколько?».
                      • 0
                        Тогда получается, что XML выигрывает в возможностях многострочного значения.

                        А как быть с тем, что поддерживаются только UNIX'овые переводы строк?

                        А вообще язык меня зацепил, жаль что поддержки нет почти.
                        • 0
                          <article>
                              <comment>
                                  <code>Чем же выигрывает?
                          В нём, например, приходится писать без отступов, чтобы в строки не попало лишнего мусора.</code>
                              </comment>
                          </article>
                          


                          А зачем в современной разработке использовать переводы строк отличные от LF? Не в виндовом блокноте же редактируем код :-)

                          Речь о библиотеках? Либа на любом языке пилится за несколько часов.
                          • 0
                            Пилится, но для JSON пилить ничего не нужно.

                            Где-то есть информация на каких языках уже реализовано что бы велосипед не изобретать?

                            И ещё, ИМХО: JSON можно сказать просто для передачи информации, а XML именно для разметки. Tree же умудряется довольно хорошо совмещать эти две функции.
                            • 0
                              Когда-то и JSON был в такой же ситуации. Для XML-то ничего пилить не нужно было. ;-)

                              Есть реализации на D и TS/JS.

                              Для инлайновой разметки tree не очень подходит, в отличие от маркдауна. А для блочной — вполне.
            • 0
              А как по мне — нормально. Разделение до знака равенства есть? Есть. С точки зрения логики так даже лучше, знак = здесь и правда — маркер начала данных. А ещё так получаются визуально две лексемы вместо трёх, что воспринимается чётче. Я согласен с автором)
    • –1
      Полагаю, вы хотели бы обрезать пробелы по краям строки (trim)? Хм, а если значение — это символ пробела? Неоднозначность получается.
      • –2
        Нет, не хотел. Я хотел разрешить пробел перед значением. Если пробел один — это пробел. Если их два — второй уже значение. Никакой неоднозначности.
        • –4
          Неоднозначность заключается в том, что первый символ после символа равенства иногда включается в значение, а иногда не включается. И вы предлагаете усложнить реализацию исключительно из эстетических, но не практических соображений. Но я всё же, я сторонник принципа KISS.
          • +1
            первый символ после символа равенства иногда включается в значение, а иногда не включается

            Первый пробел после равенства никогда не включается в значение. Если первый символ после пробела после равенства — тоже пробел, то он считается, как значение. Элементарно же ж.
  • +8
    А где пример с не экранированными переносами строк?
    Удобство редактирования все так же под вопросом — все эти невидимые символы придется делать видимыми в редакторах.
    И еще вопрос, что вы думаете о Tree в сравнении с Lisp-деревьями?
    • +12
      Примерно так:

      image

      О каких невидимых символах идёт речь?

      Пример с JSON-AST из статьи — это фактически и есть Lisp-дерево:

      [ [ "function",
          "getEl",
          [ "id" ],
          [ "return",
            [ [ "get",
                "document" ],
              [ "call",
                "getElementById",
                [ "get", "id" ]
              ]
            ]
          ]
        ]
      ]

      Они не так наглядны, как Tree.
      • +14
        Ну т.е. скопировать текст все так же не получится из-за "=" (даже хуже, так как экранирование реализовано везде).

        Про невидимые символы: вы не слышали о пробелах и табуляции?) А это значит:
        1. Сложности с редактированием документа. Т.е. удобство не 5, а 3 примерно.
        2. Значения, не ограниченные ничем могут появляться сами собой (о поверьте, это огромная проблема).

        Т.е. одно дело мы используем табуляцию/пробелы в языках программирования — там четкое AST и лишние просто игнорируются, но для данных это беда.
        • +1
          Вырезать по одному символу вначале строки куда проще, чем убрать всё многообразие escape-последовательностей.

          1. Вы всегда можете настроить свой редактор, чтобы рисовал табуляцию хоть в 10 символов длиной :-)
          2. О чём тут идёт речь?

          В Tree тоже чёткое AST. Tree — это и есть самый что ни на есть AST.
          • +2
            Вырезать по одному символу вначале строки куда проще, чем убрать всё многообразие escape-последовательностей.

            Допустим я копирую текст из PHP в JavaScript и т.д. \n везде будет переводом строки.
            Чтобы убрать \n — мне достаточно простой регулярки заменяющей это на перевод строки (любой редактор). Да, у вас придется заменять перевод_строки= на перевод строки, но в чем достоинство?
            По поводу пробелов вы уже ответили ниже habrahabr.ru/post/248147/#comment_8229867 и признаете это проблемой.
            Т.е. в итоге лучше б все таки иметь гарантированные \s \t, чем незамеченные пробелы в начале/конце значения данных.
            Про AST разница в хранении данных и структуре кода в том, что в коде можно проигнорировать кучу символов (пробелы, переносы строк, табуляцию).
            var a = 5; Без разницы сколько тут пробелов до var и после точки с запятой. С данными такое не прокатит.
            • +1
              Допустим я копирую текст из PHP в JavaScript и т.д. \n везде будет переводом строки.

              А я, допустим, копирую текст из PHP в HTML и \n больше не будет переводом строки.

              Чтобы убрать \n — мне достаточно простой регулярки заменяющей это на перевод строки (любой редактор). Да, у вас придется заменять перевод_строки= на перевод строки, но в чем достоинство?

              В данном случае нет никакой разницы, да.

              Т.е. в итоге лучше б все таки иметь гарантированные \s \t, чем незамеченные пробелы в начале/конце значения данных.

              Только в конце. Проблема куда менее значительная, чем проблема редактирования, когда весь текст в одну строку. И проблема эта вполне решаемая. В любом решении можно найти кучу недостатков, вопрос лишь в том, перевешивают ли достоинства эти недостатки.

              var a = 5; Без разницы сколько тут пробелов до var и после точки с запятой.

              Ну да, а style guid-ы зачем пишут тогда, раз нет никакой разницы? :-)
              • +6
                Только в конце.

                А у меня редактор режет все \s в конце строки при сохранении файла. Ни пробелов, ни табуляций там никогда не имеется, так что очень даже проблема.
                • –10
                  Помочь вам настроить редактор так, чтобы он не занимался излишней самодеятельностью?
          • 0
            И еще, далеко не всегда визуально удобно видеть перенос строки как перенос строки.
            <locale>
                <var name="t1">Прекрасный день\Мы гуляем\nЖизнь боль</var>
                <var name="t2">Прекрасный день\Мы гуляем\nЖизнь боль</var>
                <var name="t3">Прекрасный день\Мы гуляем\nЖизнь боль</var>
            </locale>

            Здесь удобнее глазом разделять логические составляющие (переменные), чем собственно само значение.
            • +6
              А здесь ключи не надо выискивать в середине строки:

              image

              И, кстати, вы не заметили, как опечатались в экранировании :-)
              • +3
                Это не я, честно, это хабр)

                Ну да, а style guid-ы зачем пишут тогда, раз нет никакой разницы? :-)

                Ну все же есть разница в ошибке и в лишнем пробеле в стиле кода?

                Только в конце. Проблема куда менее значительная, чем проблема редактирования, когда весь текст в одну строку. И проблема эта вполне решаемая. В любом решении можно найти кучу недостатков, вопрос лишь в том, перевешивают ли достоинства эти недостатки.

                Не только, опять же habrahabr.ru/post/248147/#comment_8229867

                А здесь ключи не надо выискивать в середине строки:

                Но при этом на странице видно лишь очень малое количество значений. Для программирования все же важнее работать со структурами, чем с данными — для значений данных другие редакторы.
                • +1
                  Подлый хабр портит первое экранирование в строке :-)

                  Есть, но всё же несоответствие принятому стилю многими программистами воспринимается не менее рьяно, чем синтаксическая ошибка.

                  Когда как на самом деле. В конце концов, можно прикрутить автофолдинг к редактору.
                  • 0
                    Есть, но всё же несоответствие принятому стилю многими программистами воспринимается не менее рьяно, чем синтаксическая ошибка.

                    Соответствие стилю приводится почти любым редактором нажатием одной кнопки. Поэтому разницы нет сколько пробелов там удалится автоматически. Но с данными… Т.е. разница все же есть и большая.
                    • 0
                      В случае Tree необходимости в нажатии этой кнопки нет, потому как формат практически не допускает вольностей. И это хорошо — не надо всяких бьютификаторов, минификаторов, валидаторов оформления и прочей ерунды.
                      • +3
                        Ну все правильно, формат допускает только ручное выискивание лишних пробелов и табов — и это вы называете «человекоудобством». Зачем нам стремная автоматизация процесса — даешь перфоленты. Одна ошибка приравнивается к смерти.
                        • –3
                          Вы слишком гиперболизируете эту проблему. Инструмент, ненавязчиво подсвечивающий концы строк — не такой уж rocket science.
                          • +4
                            А вы гиперболизируете оценки Tree). Там, где по вашему мнению кавычки, скобки и экранирование мешают восприятию, по моему мнению, способствуют автоматизации и скорости работы. Мы все таки не художественные тексты пишем.

                            Инструмент, ненавязчиво подсвечивающий концы строк — не такой уж rocket science.

                            Верно, только тогда все преимущества отсутствия скобок вообще исчезают, так как мы по прежнему видим разделители и должны их мысленно парсить. Так то и кавычки можно сделать ненавязчимыми в JSON.
                            • –3
                              JSON плох не только и не столько обилием пунктуации, которую приходится куда-то распихивать (запятую в конце не ставь, открывающую скобку ставим в конце строки, закрывающая висит одиноко в начале и тд).
                              • +2
                                Ну может и стоит решать эти вопросы? habrahabr.ru/post/248147/#comment_8231413
                                habrahabr.ru/post/248147/#comment_8230645
                                Ну и я спрашивала про Lisp в начале обсуждения, вы сказали
                                Они не так наглядны, как Tree.

                                Теперь, я надеюсь, я донесла свою точку зрения про наглядность)

                                (запятую в конце не ставь, открывающую скобку ставим в конце строки, закрывающая висит одиноко в начале и тд).

                                У вас не менее жесткий набор правил, только основан на табах, = и пробелах. Или у вас можно 2 переноса строк, 2 "=" писать? Или = написать потом перенос строки и т.д.
                                • –5
                                  Теперь, я надеюсь, я донесла свою точку зрения про наглядность)
                                  Когда кругозор сужается до точки, человек называет эту точку «точкой зрения».

                                  Или у вас можно 2 переноса строк
                                  Можно

                                  2 "=" писать?
                                  Тоже можно.

                                  Или = написать потом перенос строки и т.д.
                                  И даже так можно.
                                  • +2
                                    Когда кругозор сужается до точки, человек называет эту точку «точкой зрения».

                                    Т.е. вы все еще не согласны с моей самой узкой в мире точкой зрения про наглядность? Тогда где аргументы?
                                    Можно

                                    Все три можно никак не повлияют на целостность данных, так?
                                    • –5
                                      Я всё ещё не понимаю что вы пытаетесь доказать.

                                      Разумеется.
                            • 0
                              Скобки, по-моему, помогают читать сильно структурированный текст — при помощи редакторов с подсветкой скобок типа Notepad++ — и несколько затрудняют-замедляют его написание. Так что неплохо бы ещё и, в виде, например, макроса, иметь «конвертер туда-сюда»…
                              • 0
                                А есть редакторы с подсветкой табов.
                  • 0
                    Мне в принципе понравилась идея. Поддержу дискуссию, тем, что у товарища наверно консоль на 50 строк вот ему все в одну и надо.

                    В Поддержку Tree могу сказать только одно, мне кажется или вот на таком конфиге json, tree будет выглядеть лучше?
                    Много текста
                    [
                        {
                            "title":"Карточка торгов",
                            "width":6,
                            "class":"",
                            "form":[
                                {
                                    "type":"textbox",
                                    "label":"Наименование",
                                    "required":true,
                                    "validate":{
                                    },
                                    "model":"name",
                                    "width":12
                                },
                                {
                                    "type":"datebox",
                                    "label":"Дата публикации в печатном издании",
                                    "required":true,
                                    "validate":{
                                    },
                                    "model":"publish",
                                    "width":12
                                },
                                {
                                    "type":"datebox",
                                    "label":"Дата публикации",
                                    "required":true,
                                    "validate":{
                                    },
                                    "model":"publish2",
                                    "width":12
                                },
                                {
                                    "type":"datebox",
                                    "label":"Дата публикации №2",
                                    "required":true,
                                    "validate":{
                                    },
                                    "model":"publish3",
                                    "width":12
                                }
                            ]
                        },
                        {
                            "title":"Карточка должника",
                            "width":6,
                            "class":"",
                            "form":[
                                {
                                    "type":"selectbox",
                                    "label":"Тип должника",
                                    "required":true,
                                    "validate":{
                                    },
                                    "model":"typepay",
                                    "width":12,
                                    "collection":{
                                        "type":"list",
                                        "mappings":{
                                            "id":"model.id",
                                            "value":"model.value"
                                        },
                                        "data":{
                                            "1":"Физическое лицо",
                                            "2":"Юридическое лицо",
                                            "3":"Индивидуальный предпрениматель"
                                        }
                                    }
                                },
                                {
                                    "type":"textbox",
                                    "label":"Фамилия",
                                    "required":true,
                                    "validate":{
                                    },
                                    "padding":"0px 5px 0px 0px",
                                    "additional":{
                                        "visible":{
                                            "typepay":{
                                                "eq":[1]
                                            }
                                        }
                                    },
                                    "model":"last_name",
                                    "width":6
                                },
                                {
                                    "type":"textbox",
                                    "label":"Имя",
                                    "required":true,
                                    "validate":{
                                    },
                                    "padding":"0px 0px 0px 5px",
                                    "additional":{
                                        "visible":{
                                            "typepay":{
                                                "eq":[1]
                                            }
                                        }
                                    },
                                    "model":"first_name",
                                    "width":6
                                },
                                {
                                    "type":"textbox",
                                    "label":"Отчество",
                                    "required":true,
                                    "validate":{
                                    },
                                    "padding":"0px 5px 0px 0px",
                                    "additional":{
                                        "visible":{
                                            "typepay":{
                                                "eq":[1]
                                            }
                                        }
                                    },
                                    "model":"father_name",
                                    "width":6
                                },
                                {
                                    "type":"textbox",
                                    "label":"Полное Наименование организации",
                                    "required":true,
                                    "validate":{
                                    },
                                    "padding":"0px 5px 0px 0px",
                                    "additional":{
                                        "visible":{
                                            "typepay":{
                                                "eq":[2,3]
                                            }
                                        }
                                    },
                                    "model":"full_name",
                                    "width":6
                                },
                                {
                                    "type":"textbox",
                                    "label":"Краткое Наименование организации",
                                    "required":true,
                                    "validate":{
                                    },
                                    "padding":"0px 0px 0px 5px",
                                    "additional":{
                                        "visible":{
                                            "typepay":{
                                                "eq":[2,3]
                                            }
                                        }
                                    },
                                    "model":"part_name",
                                    "width":6
                                },
                                {
                                    "type":"textbox",
                                    "label":"ОГРН",
                                    "required":true,
                                    "validate":{
                                    },
                                    "padding":"0px 5px 0px 0px",
                                    "additional":{
                                        "visible":{
                                            "typepay":{
                                                "eq":[2]
                                            }
                                        }
                                    },
                                    "model":"ogrn",
                                    "width":6
                                },
                                {
                                    "type":"textbox",
                                    "label":"ОГРНИП",
                                    "required":true,
                                    "validate":{
                                    },
                                    "padding":"0px 5px 0px 0px",
                                    "additional":{
                                        "visible":{
                                            "typepay":{
                                                "eq":[3]
                                            }
                                        }
                                    },
                                    "model":"ogrn",
                                    "width":6
                                },
                                {
                                    "type":"textbox",
                                    "label":"ИНН",
                                    "required":true,
                                    "validate":{
                                    },
                                    "padding":"0px 0px 0px 5px",
                                    "additional":{
                                        "visible":{
                                            "typepay":{
                                                "eq":[1,2,3]
                                            }
                                        }
                                    },
                                    "model":"inn",
                                    "width":6
                                }
                            ]
                        },
                        {
                            "seperator":"clear"
                        },
                        {
                            "title":"Карточка Орагизнатора",
                            "width":6,
                            "class":"",
                            "form":[],
                            "file":"app/forms/company_cart.json",
                            "position":0,
                            "modelprefix":"company."
                        },
                        {
                            "title":"Сведения об имуществе",
                            "width":6,
                            "class":"",
                            "form":[
                                {
                                    "type":"textarea",
                                    "label":"Сведения об имуществе на торгах",
                                    "required":true,
                                    "validate":{
                                    },
                                    "model":"info_stuff",
                                    "width":12
                                },
                                {
                                    "type":"textarea",
                                    "label":"Порядок ознакомления с имуществом",
                                    "required":true,
                                    "validate":{
                                    },
                                    "model":"oznak",
                                    "width":12
                                },
                                {
                                    "type":"textbox",
                                    "label":"Начальная цена продажи",
                                    "required":true,
                                    "validate":{
                                    },
                                    "padding":"0px 5px 0px 0px",
                                    "model":"start_cash",
                                    "width":6
                                },
                                {
                                    "type":"textbox",
                                    "label":"Шаг торгов",
                                    "required":true,
                                    "validate":{
                                    },
                                    "padding":"0px 0px 0px 5px",
                                    "model":"step_cash",
                                    "width":6
                                }
                            ]
                        },
                        {
                            "seperator":"clear"
                        },
                        {
                            "title":"Порядок оформления участия в торгах",
                            "width":6,
                            "class":"",
                            "form":[
                                {
                                    "type":"textbox",
                                    "label":"Порядок предоставления заявок",
                                    "required":true,
                                    "validate":{
                                    },
                                    "model":"step_add",
                                    "width":12
                                },
                                {
                                    "type":"textbox",
                                    "label":"Место предоставления заявок",
                                    "required":true,
                                    "validate":{
                                    },
                                    "model":"place_add",
                                    "width":12
                                },
                                {
                                    "type":"datebox",
                                    "label":"Начало приема заявок",
                                    "required":true,
                                    "validate":{
                                    },
                                    "padding":"0px 5px 0px 0px",
                                    "model":"start_date_add",
                                    "width":6
                                },
                                {
                                    "type":"datebox",
                                    "label":"Окончание приема заявок",
                                    "required":true,
                                    "validate":{
                                    },
                                    "padding":"0px 0px 0px 5px",
                                    "model":"end_date_add",
                                    "width":6
                                },
                                {
                                    "type":"textarea",
                                    "label":"Порядок оформления участия в торгах",
                                    "required":true,
                                    "validate":{
                                    },
                                    "model":"poradok_oform",
                                    "width":12
                                },
                                {
                                    "type":"textbox",
                                    "label":"Размер задатка",
                                    "required":true,
                                    "validate":{
                                    },
                                    "model":"size_precash",
                                    "width":12
                                },
                                {
                                    "type":"textbox",
                                    "label":"Информация счета задатка",
                                    "required":true,
                                    "validate":{
                                    },
                                    "model":"info_schet",
                                    "width":12
                                },
                                {
                                    "type":"filebox",
                                    "label":"Договор о задатке",
                                    "required":true,
                                    "validate":{
                                    },
                                    "filebox":{
                                        "type_id":"hdogovorzadatok",
                                        "multiple":false,
                                        "nofile":"Договор не выбран",
                                        "type":"type"
                    
                                    },
                                    "model":"dogovor_zadatok",
                                    "width":12
                                },
                                {
                                    "type":"datebox",
                                    "label":"Дата начала аукциона",
                                    "required":true,
                                    "validate":{
                                    },
                                    "model":"start_date",
                                    "width":12
                                }
                            ]
                        },
                        {
                            "title":"Порядки купли-продажи",
                            "width":6,
                            "class":"",
                            "form":[
                                {
                                    "type":"textarea",
                                    "label":"Порядок и срок заключения договора купли-продажи",
                                    "required":true,
                                    "validate":{
                                    },
                                    "model":"poradok_i_srok",
                                    "width":12
                                },
                                {
                                    "type":"textarea",
                                    "label":"Срок платежей, реквезиты",
                                    "required":true,
                                    "validate":{
                                    },
                                    "model":"srok_pay_req",
                                    "width":12
                                },
                                {
                                    "type":"filebox",
                                    "label":"Проект договора купли-продажи",
                                    "required":true,
                                    "validate":{
                                    },
                                    "filebox":{
                                        "type_id":"saleresaledogovor",
                                        "multiple":false,
                                        "nofile":"Договор не выбран",
                                        "type":"type"
                    
                                    },
                                    "model":"project_dog",
                                    "width":12
                                }
                            ]
                        },
                        {
                            "seperator":"clear"
                        }
                    ]
                    

                    • НЛО прилетело и опубликовало эту надпись здесь
                      • +1
                        Чуток промахнетесь и вырежите на строку ниже/выше, а то и из середины — и привет, то, что было на одном уровне внезапно стало дочерним элементом. А JSON/XML вам сразу ошибку нарисует и правильно — нефиг резать не глядя.

                        Лучше уж ошибочный документ, чем синтаксически правильный, но с некорректными данными. Ошибка — это не разборщик такой-сякой-плохой-нехорошый, а вам знак — что-то не в порядке!
                        • НЛО прилетело и опубликовало эту надпись здесь
                      • –4
                        Может останется, а может и не останется. Смотря какой кусок вырезать. Из любого формата можно вырезать кусок так, чтобы он остался wellformed. Контроль целостности передаваемых данных — это задача транспортного слоя, а не формата описания данных.
                    • 0
                      Ну да, как-то так:

                      image

                      И так далее.
                    • 0
                      Такой конфиг и в XML будет выглядеть лучше
        • –1
          Кстати, я тут допилил плагин к IDEA — теперь он и пробелы в конце строк делает видимыми, и на пробелы в начале оных ругается:

          image
    • 0
      По поводу s-expressions (Lisp) скажу — они, на мой взгляд,
      бывают (не всегда!) удобнее HTML, XML, JSON и прочего, когда в документе
      сравнительно мало текста. Т.е. для, скажем, представления
      AST какого-либо языка или для задания layout'а (вёрстки, на которую
      ложится CSS, без наполнения) HTML-страницы — очень хорошо.
      Скобки — проблема надуманная, при активном
      использовании обычно становятся незаметными
      недели через две, если, конечно, редактор имеет правильный
      autoindent и умеет подсвечивать парные скобки, а при использовании
      средств типа paredit скобки неплохо помогают в редактировании.

      Проблемы с s-expressions для представления данных / документов
      следующие:
      • если документ преимущественно текстовый — получается некрасиво;
      • по сравнению с XML, не хватает namespaces. В случае CL есть
        packages, но их трудно назвать достаточнго гибкой заменой
        пространств имён для файлов данных;
      • tooling — XPath/XQuery/Relax NG/etc. В случае использования s-exprs
        для данных обходимся без этих полезняшек или строим велосипеды.

      JSON по сравнению с s-exprs поддерживает унифицированное представление
      dicts (objects), вместо которых в символьных выражениях используются
      property lists, association lists либо какой-то кастомный синтаксис, специфичный
      для языка (например, Clojure) или приложения (reader macros в CL).
      По-моему, это не очень большая проблема, т.к. выразить ассоциативный
      массив в виде s-exprs таки не сложно.
      С другой стороны, JSON нет стандартных широко поддерживаемых
      комментариев и это [очень плохо]. _comment-поля, на мой взгляд — извращение.
      • 0
        >> выразить ассоциативный массив в виде s-exprs таки не сложно.
        Есть же alists:
        (users 
          (name . "Alice")
          (age . 20))
        
        • 0
          Ну а как же
          (users 
            (:name "Alice")
            (:age 20))
          
          • 0
            А что это? (на руби смахивает, хотя знаю я его поверхностно)
            • 0
              Common Lisp
        • 0
          Так я же и написал про association lists выше по тексту. Можно ещё
          plists (property lists):

          (users (:name "Alice" :age 20))
        • 0
          <занудство>
          А разве не так:
          (users 
            (user 
              (name . "Alice")
              (age . 20)))
          

          или
          (users 
            ((name . "Alice")
              (age . 20)))
          

          ?
          Там же ещё может быть
          (users 
            ((name . "Jane")
              (age . 21))
            ((name . "Alice")
              (age . 20)))
          

          </занудство>
  • +61
    Нужно сначала показывать продукт, а уже потом сравнивать его с чем-то.
    Пришлось промотать в самый низ, чтобы увидеть как выглядит это чудо.

    >JSON и YAML для создания иерархий предлагают «списки» и «мапки». Не все структуры данных хорошо представимы с их помощью.
    >В Tree есть только один тип узлов
    Одним типом узлов, конечно проще описать чем двумя /sarcasm.
    А если серьезно, то «списки» это вложенные JSON документы. Какая тут может быть проблема с представлением не понятно.

    >Как минимум необходима разноцветная подсветка лексем. Очень помогает — автоформатирование, автодополнение и подсветка ошибок
    Использовать Tree без всего этого будет не мене неудобно

    >Отсутствие необходимости в экранировании и разэкранировании спецсимволов
    Только для \n, видимо?

    >Почти все текстовые форматы не совместимы с бинарными данными.
    Простите? Что вам мешает хранить «бинарные данные» в JSON?

    >В Tree нет комментариев
    Мило

    >Табуляция
    Мило

    >Универсальноть
    Так и не объяснили что это значит.

    Кроме того, как я понимаю, в качестве ключа нельзя использовать blob или даже произвольные «строки», например, включающие символ '='?

    В остальном, если без всяких «языков на его основе», то выглядит неплохо. По сути просто json без лишних скобок и кавычек, что радует.
    • –24
      Нужно сначала показывать продукт, а уже потом сравнивать его с чем-то.

      Если сразу показать, то типичная реакция: «ой, фу, я так не привык, не хочу дальше читать, пойду напишу в коментах, что люблю json» Тут же я хотел сначала обрисовать недостатки известных форматов, подчеркнув, что в предлагаемом решении их нет. Вы же испортили себе всё впечатление :-)

      Одним типом узлов, конечно проще описать чем двумя /sarcasm.

      Одним универсальным проще, чем двумя ограниченными. Пример с AST весьма показателен.

      Использовать Tree без всего этого будет не мене неудобно

      На самом деле менее. В нём сложно написать что-то нечитаемое или допустить синтаксическую ошибку.

      Только для \n, видимо?

      Экранированием это можно назвать только с натяжкой, но таки да, в остальных форматах экранировать приходится гораздо большее число символов.

      Простите? Что вам мешает хранить «бинарные данные» в JSON?

      Я так понимаю вы намекаете на base64, base62 и другие способы конвертации бинарных данных в текстовые? Вот собственно эта фаза перекодировки и мешает. Она не позволяет просто потоком вгрузить файл в память и далее просто ссылаться на его участки — обязательно надо выделять отдельные куски памяти для отдельных декодированных значений.

      Так и не объяснили что это значит.

      Оно складывается из суммы остальных параметров, так что решил его убрать, а вот табличку не поправил, спасибо.

      Кроме того, как я понимаю, в качестве ключа нельзя использовать blob или даже произвольные «строки», например, включающие символ '='?

      В качестве имени — нельзя. В качестве ключа — можно. Например, аналогом такого JSON:

      {
          "a+b=c" : [ 0 , true ],
          "\"foo\"" : [ 1.1 , false ]
      }
      

      Будет такой json.tree:

      image
      • +8
        Не смотря на заявления о простате синтаксиса, он все равно мне кажется непонятным. Возьмем ваш последний пример. Почему значения в списках представлены в виде имен, а не значений? Зачем явно указывать слово list, разве несколько значений не будут на это прямо указывать?
        • –4
          Можно и в виде значений, только тогда придётся как-то различать типы примитивов. Указание чисел и констант в виде имён позволяет не указывать дополнительно эти типы.

          Если не указывать list то в случае одного единственного элемента списка будет невозможно понять список там должен быть из одного элемента или же просто этот самый элемент.
          • +11
            > тогда придётся как-то различать типы примитивов
            Но здесь же они тоже не различаются. В синтаксисе нет ничего про типы. А где дополнительно нужно описывать тип? Как-то это не вяжется с парсером в 10 паттернов.
            • –2
              Типы — это сущности другого уровня — языкового. Для разных языков нужны разные типы, так что не место им на уровне формата.

              Типы можно указывать по разному. Например, для языка программирования, где набор типов может расширяться, они могут указываться так:

              image

              Но для JSON с фиксированным набором типов и простым способом их различения — это излишне.

              Транслятор json.tree в json вполне может проанализировать узел-имя и понять, что там число или булево значение, или null, а вот узлы-значения воспринимать как строки.
              • +10
                Вам не кажется, что наличие таких уровней ухудшает многие параметры, которые вы оценили на 5. Например, размер в сериализованном виде увеличивается от этих list и map, а человекопонятность и удобство редактирования уменьшается.
                • –3
                  Разумеется, JSON модель данных выраженная в Tree формате будет занимать больше места. Точно также Tree модель данных в формате JSON будет будет ещё больше. Это неизбежная цена отображения одного формата на другой.
                  • +2
                    Только это не JSON модель данных, javascript. И такая модель данных (со строками, числами, логическими константами, массивами и объектами) существует во многих языках. А вот модели данных как в Tree я не встречал.
                    • 0
                      JS модель данных пошире будет. И вы преувеличиваете универсальность этой модели. Во многих языках в массиве могут быть значения только одного типа. Во многих языках числа и строки представляются множеством разных типов. Во многих языках есть такие структуры как множества, ссылки, матрицы (которые ни разу не массивы массивов). И это я не говорю про многообразие кастомных типов.
          • +2
            Типы можно различать, используя BSON. А он еще и бинарный формат, да, и конвертируется из обычного JSON.
            • +3
              BSON — это всё же чисто бинарный формат. Его и руками в редакторе не поредактируешь и в логах не посмотришь. И вообще с ним работать можно лишь через специальные инструменты.
              • 0
                Ну как это не поредактируешь? Hex-редактор и погнал. Ну а специальные инструменты — это слишком натянуто. Он очень строго типизирован, есть куча готовых реализаций, а своя реализация «на коленке», пишется за пару часов, а то и меньше. С tree тоже придется использовать «специальные инструменты».
                UPD: А просматривать можно соответствующий JSON документ.
                • –4
                  HEX редактор — весьма специальный инструмент.

                  Для Tree и простейшего текстового редактора хватит в большинстве случаев.

                  Я не сомневаюсь, что можно извертнуться и хоть на ASM-е писать. Но зачем?
      • +16
        >Если сразу показать
        А если не показать, то человеку придется проматывать вниз, чтобы понять о чем вообще речь.
        Либо уйти, не читая.

        >Тут же я хотел сначала обрисовать недостатки известных форматов, подчеркнув, что в предлагаемом решении их нет. Вы же испортили себе всё впечатление :-)
        Нет, не испортил. Таблицу я прочитал до того, как промотал вниз.
        Почти в самый конец, потому как в пределах 4 (!!!) экранов не увидел ниодного примера этго чуда,
        которое получило оценку 5 почти по всем пунктам

        >Одним универсальным проще, чем двумя ограниченными
        Чем ограничены эти два формата?
        Как уже говорил, «списки», по сути, это вложенные JSON документы.
        Массивы это просто синтаксис для таких документов, без необходимости ввода ключей, чтобы не писать в столбик ключи без значений, как вы это делаете в Tree (из примера ниже)

        >На самом деле менее.
        Вовсе нет. Что там пелена, что здесь.

        >В нём сложно написать что-то нечитаемое или допустить синтаксическую ошибку.
        Видимо из-за проблем с выразительным средствами.
        Как же проблема с = перед значением, которую описала sferrka?
        А хранение ключей, которую описал я выше?
        А пробелы в ключе, про которые говорил PHmaster ниже?

        > форматах экранировать приходится гораздо большее число символов.
        У вас перенос строки, если верить примеру выше, просто заменяется на '=', что не сильно лучше \n
        Поэтому количество символов, которые нужно экранировать у вас вовсе не меньше, если не больше из-за '='

        >Я так понимаю вы намекаете на base64, base62
        Нет, не намекаю. Строка в JSON является строкой условно, ничто не мешает вам хранить в ней blob
        • 0
          > Строка в JSON является строкой условно, ничто не мешает вам хранить в ней blob
          А если в бинарных данных попадется символ, который нужно экранировать?
        • +6
          Строка в JSON является строкой условно, ничто не мешает вам хранить в ней blob
          Ничего условного в ней нет, в спеке вполне однозначно сказано, что это строка из символов Unicode, причём слеши и кавычки нужно ещё и обязательно экранировать. Так что блоб там хранить можно только предварительно конвертировав его в base64 или что-то подобное.
        • 0
          map — неупорядоченный (ограничение по упорядоченности) ассоциативный (ограничение по уникальности ключей) массив.
          list — упорядоченный индексированный (ограничение на индексы — строго последовательно, начиная с 0) массив.

          Напомню, что в JSON нужно экранировать:
          * перевод строки
          * кавычки
          * обратный слеш
          * пачку управляющих символов

          В Tree спецсимвол не допустимый в значениях только один — перевод строки.
          • +4
            Извините, но эти «ограничения» служат для удобства использования и никак не виляют на выразительность.

            В то же время в Tree этого всего нет, а значит придется как-то это где-то специфицировать (как отличить сортированный от несортированного?). Введете TSD и Tree Schema?
            Это не простота, это бедность.

            > пачку управляющих символов
            Эти подлежат «экранированию» по причине того, что просто так с клавиатуры их не ввести.
            Остальные, действительно, ограничения, накладываемые форматом, здесь вы правы.

            Идея использовать практически голые AST как формат хранения данных мне нравится.
            Но то, что вы показываете — сильно недоработанная концепция.
            И, пожалуй, его главная проблема — амбициозность разработчика, которая формирует соответствующее отношение к самому проекту.
            • 0
              Вы зря недооцениваете стек XML технологий. Их разрабатывали куда более умные люди, чем вы или я. И идея разделения языка и формата — очень здравая, потому как позволяет иметь в рамках одного формата тысячи языков, для которых не надо писать отдельные парсеры.
              • 0
                Почему вы решили, что я его недооцениваю? XML хорошо показал себя в свое время, но сейчас ему находят более эффективные альтернативы. В IT все стремится к упрощению. Почему JSON стал так популярен? Потому, что он прост: не нужна никакая схема, не нужно ничего декларировать — просто используй.

                >И идея разделения языка и формата — очень здравая
                Разве я это отрицал? Я говорил лишь о том, что Tree не решает тех проблем, которые вы нашли в JSON. Во всяком случае, без использования схемы. А если использовать схему, то ваш формат усложняется. Причем это усложнение сразу нивелирует все его плюсы:
                1. Он становится слишком неудобным для использования человеком
                2. Он начинает конкурировать с BSON (так же «удобно» редактировать руками) и Protobuffers, где «сливает» вообще по всем параметрам.
                • –2
                  а) А при чём тут схема?
                  б) В чём проблема запилить схему? Я вам удочку даю, а вы от меня рыбы требуете.

                  1. Вы попробуйте сначала, а потом уже говорите о неудобности.
                  2. Вполне себе разумный компромис между компактностью и читаемостью. Всякие BSON и прочие бинарники имеют сильно ограниченное применение. В то время как адекватных применений Tree — гораздо больше.
                  • 0
                    >А при чём тут схема?
                    При том, что ей вы описываете что есть что. Если в языке нет необходимого минимума, то приходится описывать вообще все.

                    >В чём проблема запилить схему?
                    Только в том, что это нужно сделать. И чем раньше это «нужно» появится, тем хуже.

                    >Вы попробуйте сначала, а потом уже говорите о неудобности.
                    Пробовал уже. Как только вы для этого предложили нечто похожее на формы Бэкуса-Наура.
                    В связи с чем, кстати, появился вопрос: как вам идея использовать вместо вашего решения bison? Он несравнимо более гибок, да и реализация уже есть. Получаем сразу и описание формата и парсеры под все популярные языки.

                    >Всякие BSON и прочие бинарники имеют сильно ограниченное применение
                    Тем не менее в бинарной области они работать будут лучше чем та спецификация Tree, что вы показываете. А в «небинарной» есть JSON (который, кстати, без проблем конвертируется в BSON).

                    > В то время как адекватных применений Tree — гораздо больше.
                    Попытка создать инструмент, который делает все, приводит к тому, что он делает все в равной степени плохо. В том смысле, что специализированный инструмент, справился бы с конкретной задачей значительно лучше.
                    • 0
                      Я уже устал повторять, что Tree — это формат, а не язык. Примеры языков в статье есть и для ник вполне чётко определено что есть что.

                      Идея-то хорошая, но боюсь не найду в ближайшее время времени ковыряться с bison.

                      Специализированный инструмент справится лучше, только на изучение каждого инструмента нужно время и таскать их все с собой не очень удобно. Плюс ещё и стыкуются они друг с другом плохо.
                      • +2
                        >Я уже устал повторять, что Tree — это формат, а не язык
                        Извините, что утомил вас. Не волнуйтесь, я помню, и это очевидно из его сравнение с JSON, XML, YAML.

                        Перефразирую: если формат не предоставляет достаточно конструкций сам по себе, то их придется где-то описывать. Пример с массивами: да, в JSON они нумеруются от 0 и только так. Но в Tree такого нет вообще. Нигде не написано, являются ли дочерние узлы упорядоченными или нет и какие им даются индексы. А это значит, что придется вводить новую спецификацию (схему), для того, чтобы это описать.

                        Если вы подразумеваете, что в чистом виде он никогда не будет использоваться, то презентуйте его как мета-формат.

                        >только на изучение каждого инструмента нужно время
                        Даже если описать все эти языки в рамках одного формата, это не делает изучение более простым

                        >таскать их все с собой не очень удобно
                        Как это отличается от все-в-одном?

                        >Плюс ещё и стыкуются они друг с другом плохо
                        Весьма спорное утверждение. Все зависит от конкретного случая.

                        Напомню, что сама идея, мне нравится. Мне не нравится её текущая реализация и тот факт, что эту реализацию пытаются выдать за «убийцу» вышеперечисленных форматов.
                        • 0
                          Вместо того, чтобы хардкодить некоторые типы (например, строки, числа, булевы), а с остальными (например, даты, телефонные номера, гео-координаты) ковыряйтесь как хотите, на мой взгляд лучше дать инструмент объявления своих типов и не навязывать какой-либо набор, «которого хватит всем». Посмотрите на описание языка xml.tree — оно же предельно простое, хоть и не формализованное с помощью каких-либо схем.

                          Изучить новый словарь для знакомого синтаксиса куда проще, чем изучить новый синтаксис.

                          А вы зря недооцениваете универсальные инструменты. Возьмём вот например нож. Есть нож для рыбы, для мяса, для сыра, для хлеба, для разделки, для удаления костей, для раскрытия раковин устриц, тысячи их. Каждый из них предельно хорош в своей области. А тут прихожу я и говорю, что вот этими двумя типами ножей можно делать всё что угодно. Пусть и не будет того повышенного комфорта, как при использовании устричного ножа. Зато ножи будут взаимозаменяемы, требовать мало места, их не придётся менять каждые 5 минут, подбирая нужный для своего типа нарезаемого продукта.
                          • 0
                            >Возьмём вот например нож
                            приятно видеть человека, видящего перспективность метафорического компьютера :)

                            данные: дождевая, вода из-под крана, столовая питьевая, минеральная, газированная сладкая, дистиллированная, вода из лужи, морская, речная, фильтрованная…

                            дистиллированная вода — бинарные данные
                            вода из-под крана — данные, вводимые пользователем

                            формат: мыло, шампунь, ополаскиватель, гель для душа стиральный порошок, средство для мыться посуды, зубная паста, крем для бритья средство для мытья ковров, средство для пола, средство для унитаза, средство для мытья стекол…

                            при смешении воды и средства получаем данные в формате

                            инструмент: щетка для обуви, зубная щетка, половая тряпка, веник, губка для мытья посуды, скребок для мытья посуды, расческа, щетка для пылесоса, пемза, полотенце…

                            То есть, вы утверждаете, что ваш формат — это мыло.
                            А ваши оппоненты, что не хотят употреблять молоко в формате сыворотка, когда есть еще масло, сметана, творог, сливки.

                            почему бы не провести опрос.
                            • 0
                              Ну вы уж совсем мой тезис до абсурда довели :-)
                              Tree тогда уж, это не мыло, а органическая химия, где и преимущественно из одних только атомов углерода собирают самые разнообразные вещества.
                • 0
                  Почему JSON стал так популярен? Потому, что он прост: не нужна никакая схема, не нужно ничего декларировать — просто используй.

                  На самом деле, не поэтому, а потому, что на веб-клиенте (в браузере) его парсинг был существенно проще и дешевле, чем парсинг xml.

                  (xml тоже можно использовать без схемы и деклараций, если что)
                  • 0
                    или всё же потому, что это серилизатор для ЯваСкрипта? )))
                    • 0
                      Это не «сериализатор для яваскрипта», это сериализатор, который в некий момент решили использовать в JavaScript как самый простой (потому что чтобы разобрать этот формат, если без учета безопасности, достаточно сделать eval)
                      • 0
                        ок, делали не столько для ЯваСкрипта.
                        Но получил популярность потому, что очень прост для ЯваСкрипта.
                        Тот же XML надо вначале распарсить в ДОМ, а уж потом из него вытаскивать данные для скриптов, а с использованием жсона — всё уже распакованно.
                        • 0
                          А теперь перечитайте, что написано в комменте, на который вы отвечаете:
                          потому, что на веб-клиенте (в браузере) его парсинг был существенно проще и дешевле, чем парсинг xml.
                        • 0
                          >вначале распарсить в ДОМ
                          SAX
                          • 0
                            SAX на javascript?
                            Но получил популярность потому, что очень прост для ЯваСкрипта.
                            • 0
                              То есть DOM на js вас не смущает?

                              >Но получил популярность потому, что очень прост для ЯваСкрипта.
                              К чему вы это?
                              • 0
                                Разумеется, DOM на js меня не смущает.
                                • 0
                                  Если вы клоните к тому, что страница суть тоже xml, то что это меняет? Это запрещает использовать sax-парсеры?
                        • +1
                          По распакованному JSON всё-равно придётся пройтись и преобразовать во внутреннее представление (объектики там всякие, перекрёстные ссылки и тд). Так что особой разницы нет по какому дереву бегать — json, tree или xml.
                          • 0
                            ko.mapping.fromJS(responceJson, viewModel);
                            
                            Сделайте-ка мне, пожалуйста, аналогично с форматом tree :)
                            • 0
                              А в чём проблема?
                              • 0
                                Проблема в том, что распакованный JSON — это объект, который можно напрямую отображать на модель. На выходе же парсера tree — дерево узлов, которые требуется обрабатывать дополнительно.
                                • 0
                                  Беспредметный спор. Покажите как вы отобразите JSON модель данных на модель предметной области «на прямую». А я сделаю аналогичное отображение из Tree. А потом сравним у кого сколько «дополнительного» кода получилось.
                                  • –1
                                    Я же написал выше. Между прочим, реально используемый код.
                                    • 0
                                      koko.mapping.fromTree(responceTree, viewModel);
                                      • 0
                                        Приведите библиотеку. А то по запросу «koko.mapping.fromTree» гуглится какая-то фигня, а без библиотеки сравнить количество «дополнительного» кода не получается.

                                        • –1
                                          А что толку? Реализация будет точно такая же — через паттерн Visitor.
                                          • +1
                                            Какой Visitor в гомогенном дереве, о чем вы?
                                            • –1
                                              Где вы увидели гомогенное дерево?
                                              • +2
                                                Ваш Tree — это гомогенное дерево (это то его достоинство, которым вы так гордитесь)
                                                • –1
                                                  Не совсем, имена-то узлов разные, а именно они определяют как транслировать абстрактное дерево в объекты приложения.
                                                  • +2
                                                    Угу. Вот Visitor подключится (если надо) к делу после трансляции абстрактного дерева в объекты приложения, а не до — а на этой фазе уже не важно, из чего эти объекты смапили.
                                                    • –2
                                                      Вообще-то мы тут обсуждали именно трансформацию из дерева данных в дерево объектов.
                                                      • +2
                                                        … а на этой фазе visitor не используется, потому что дерево данных в вашем случае полностью гомогенно.
                  • 0
                    >xml тоже можно использовать без схемы и деклараций
                    Не спорю, можно. Но несколько сложнее, взять хотя бы проблему выбора между атрибутом и дочерним элементом.

                    Хотя, пожалуй, вы правы.
                    • 0
                      Если для вас это по каким-то причинам проблема — просто всегда используйте дочерние элементы.
      • +10
        А можно пример того как ваш формат дружит с бинарными данными.
        • 0
          Пример программного кода или пример файла?
          • 0
            Пример файла конечно, зачем мне код.
            • 0
              • 0
                Отлично, а что будет если в бинарных данных один из символов будет \n?
                • 0
                  Очевидно, вместо одного узла-значения будет несколько.
                  • –3
                    Это если за \n последует такое же количество табов (что мало вероятно), а если попутно ещё и = встретится так это сделает всё ещё хуже. Что то не вижу я бинарной безопасности в вашем формате.
                    • –2
                      image
                      • +2
                        То есть мне придётся самому в коде разбивать данные на блоки? А чем это лучше экранирования " и \ в json?
                        • 0
                          Зачем самому, если это можно делать автоматически?
                          • 0
                            json тоже это сам делает
                            • 0
                              Разумеется. Вопрос в стоимости этой операции и человекочитаемости.
                              • 0
                                Стоимость этой операции в обоих случаях O(n). В чем вопрос?
                                • 0
                                  Вопрос в размере константы.
                                  • +1
                                    Во-первых, не в размере константы, а в отношении размеров констант. И вы не сможете адекватно посчитать k для JSON-кодировщика, потому что его реализация очень сильно зависит от возможностей платформы и изворотливости программиста.

                                    Во-вторых, в то время как стоимость кодирования в JSON зависит только от самой строки, в Tree она зависит от строки и ее места в иерархии, что тоже не добавляет счастья.

                                    Нотацию O(f(n)) для того и придумали, чтобы не размениваться на такие мелочи. Для разумной реализации на разумном языке стоимость кодирования и декодирования строкового примитива что в Tree, что в JSON будет сопоставима, и отличия будут вноситься больше на уровне «читаем из буфера vs читаем из потока».
                                    • 0
                                      В JSON многие символы требуют экранирования, что вынуждает работать с байтами индивидуально. Tree же позволяет копировать данные пачками, что гораздо быстрее.
                                      • +1
                                        Каким образом вы гарантируете, что в «пачке» нет переводов строки, если не пройдетесь по каждому байту индивидуально?
                                        • –1
                                          Если пачка прочитана из Tree файла, или через readln(), то переводов строк там точно нет. Если же у нас дикие данные, то при импорте в дерево необходимо пробежаться по ним в поисках переводов строк. Поиск символа в строке — это куда более быстрая операция.
                                          • +3
                                            При импорте в дерево вам нужно не просто «пробежаться… в поисках переводов строк», а точно так же, как и с экранированием, при каждом встреченном символе перевода строки добавить дополнительные символы экранизации: в вашем случае это табы и символ "=", и добавляются они не перед экранируемым символом, а после него. Причем, заметьте, вы не можете экранировать данные и затем вставить их в любое произвольное место в ваше дерево, как это возможно с тем же JSON, так как количество вставляемых в Tree табов после символа перевода строки зависит от контекста, т.е. конкретного места, для которого вы экранируете, а именно — от уровня вложенности объектов в этом месте. А это, в свою очередь, означает, что вы не можете взять некий объект, сериализировать его в Tree-представление и потом использовать сериализованный кусок текста в произвольном месте Tree-файла (или любого другого потока вывода), как это возможно с тем же json и XML. То есть, для сериализации объектов нужно заранее знать всю структуру сериализуемого дерева этих объектов целиком, и при сериализации обходить эту структуру в строгом порядке: от корня к листьям.
                                            • –1
                                              При импорте в дерево не происходит экранирования — только составление списка диапазонов.

                                              static Values
                                              ( T = string )
                                              ( T value , Tree[] childs = [] , string uri = "" ) 
                                              {
                                              	auto chunks = ( cast(string) value ).split( '\n' );
                                              	auto nodes = chunks.map!( chunk =>
                                              		new Tree( "" , chunk , [] , uri )
                                              	);
                                              	return nodes.array;
                                              }
                                              

                                              Функция split не копирует данные, если что, а возвращает так называемые срезы исходного массива.

                                              При сериализации они просто сливаются в выходной поток как есть, без какой-либо обработки.

                                              } else if( this._value.length || prefix.length ) {
                                              	output.write( "=" ~ this._value ~ "\n" );
                                              }
                                              
                                              • +1
                                                В таком слчае так можно сказать про любое другое экранирование, вот например простейшие экранирование " и \
                                                static Values
                                                ( T = string )
                                                ( T value , Tree[] childs = [] , string uri = "" ) 
                                                {
                                                	auto chunks = ( cast(string) value ).split( "\"\\" );
                                                	auto nodes = chunks.map!( chunk =>
                                                		new Tree( "" , chunk , [] , uri )
                                                	);
                                                	return nodes.array;
                                                }
                                                

                                                и потом
                                                } else if( this._value.length || prefix.length ) {
                                                	output.write( "\\" ~ this._value);
                                                }
                                                

                                                Имеются ли тут кардинальные различия? Не думаю.
                                                • 0
                                                  У вас при сплите данные теряются. Если разрезать по более чем одному символу, то потом не понятно по какому символу собирать.
                                                  • +1
                                                    ну так всегда можно свой split написать который не будет обрезать символы, я просто хотел показать, что по сути экранирование ничем не отличается от вашего разделения.
                                                    • 0
                                                      Экранирование в JSON отличается. Да, можно придумать экранирование, где перед кавычкой и бэкслешем ставится бэкслеш и всё, но чем это:
                                                      { "lalala" :
                                                          "foo\"
                                                      bar\\"
                                                      }
                                                      

                                                      Лучше этого:
                                                      lalala
                                                          =foo"
                                                          =bar\
                                                      

                                                      ?
                                                      • 0
                                                        Экранирование в JSON отличается.

                                                        ничем кроме символов, и того факта что спец символы нужно добавлять до а не после
                                                        Лучше хз, а чем хуже? Между экранированием символом \ и экранированием строкой неопределённой длинны, я бы честно говоря выбрал первый вариант.
                                                        • 0
                                                          Тем, что исходный символ удаляется.
                                                          • 0
                                                            Где же он удаляется? Знак переноса у вас всё также остаётся.
                                                            • 0
                                                              В JSON удаляется.
                                                              "\n" — тут нет символа c с кодом 13
                                      • +1
                                        Не позволяет. Вам нужно знать, где в данных \n, поэтому у вас не выйдет «просто прочитать» или «просто записать». Это все те же самые O(n) (а когда вы говорите про «прочитано через readln» — то для вас эта сложность просто спрятана внутрь вызываемой функции; но это не значит, что ее нет).

                                        Так что утверждение про сопоставимые сложности алгоритмов остается неопровергнутым.
                                        • 0
                                          Я с ним и не спорил. Речь о константах. Вы по ссылке ходили? Разница на порядок.
                                          • 0
                                            Ходил, а толку? Эта ссылка не применима в вашем случае, потому что она относится к копированию целых массивов в памяти.

                                            А у вас что? У вас
                                            (а) копировать надо не весь массив, а до определенной позиции
                                            (б) вы, на самом деле, вообще ничего не копируете, а либо читаете, либо пишете с/на I/O-устройство (сеть, диск и так далее), и стоимость самого I/O выше, чем стоимость всех операций сравнения, которые надо сделать (а больше отличий-то и нет)
                                            • 0
                                              Вы не на палец смотрите, а туда, куда я показываю :-)
                                              Это иллюстрация, что обработка сразу пачки данных куда эффективней побайтовой обработки.

                                              • 0
                                                Эффективнее, конечно. Но для бинарно-небезопасных форматов это в чистом виде невозможно в любом случае. Поэтому все равно в том или ином виде будет обработка экранирующих последовательностей — которая, как уже говорилось, сводится к O(n) (с чем вы, впрочем, уже согласились). Дальше вопрос k. Вы утверждаете, что для JSON, в котором экранируемых символов больше, k существенно больше, чем для tree. А я утверждаю, что в типовых применениях эта разница не будет заметна на фоне стоимости основной операции (i/o).

                                                Внимание, вопрос: будем на уровне логики обсуждать, или уже пора тесты писать?
                                                • –1
                                                  i/o между процессами заключается именно что в копировании памяти.
                                                  • +3
                                                    Внезапно, tree предлагается для межпроцессной комуникации? А зачем? Там чистый бинарный формат намного эффективнее.

                                                    Я всегда считал, что основные применения таких форматов — это прочитай/запиши на диск, в БД или в сеть.
                                                    • 0
                                                      Для интероперабельности.
                                                      Например, unix pipes.
                                                      • +1
                                                        Вы правда думаете, что unix pipes работает через копирование памяти? Странно, а я думал, что оно сделано через потоки (точнее, через форвард-онли файл-дескрипторы).
                                                        • 0
                                                          Я правда думаю, что дерево сериализуется в память и парсится из неё же, без обращения к диску.
                                                          • 0
                                                            Во-первых, далеко не факт, что это быстрее. Во-вторых, когда вы сериализуете и парсите дерево в памяти, у вас парсинг отдельно, копирование — отдельно, и поэтому оно в обоих алгоритмах является константой (благодаря чему радостно игнорируется O-нотацией). Ну и в-третьих, при чем тут диск, мы вроде как о unix pipes говорим?

                                                            Ладно, чтобы не заниматься дурацкими вопросами, напишите, пожалуйста, в псевдокоде, как, по-вашему, будет происходить процесс сериализации — передачи через unix pipe — десериализации данных, с указанием того, где именно копируется память.
                                                            • 0
                                                              В случае tree:
                                                              При парсинге копировать ничего не нужно.
                                                              При сериализации просто копируются участки памяти в выходной буфер.

                                                              В случае json:
                                                              При парсинге происходит декодирование, для чего выделяется отдельная память.
                                                              При сериализации происходит кодирование и запись в буфер.
                                                              • +1
                                                                Не так быстро.

                                                                Во-первых, почему ничего не надо копировать при парсинге tree?
                                                                Во-вторых, где пайп-то?
                                                                В-третьих, откуда вы берете участки памяти для копирования в выходной буфер? Вот у вас на входе строка, потенциально содержащая \n — откуда вы знаете, какие ее куски надо копировать?
                                                                • 0
                                                                  1. А зачем?
                                                                  2. За кулисами. Пайплайном управляет операционка.
                                                                  3. Из входного буфера и беру. Как бы tree-парсер строит дерево по входящему потоку — отсюда и знаю, где что.
                                                                  • +1
                                                                    А зачем?

                                                                    Ну то есть данные из входного потока попадают в структуру в памяти чудом?

                                                                    За кулисами. Пайплайном управляет операционка.

                                                                    Ага, то есть фраза «i/o между процессами заключается именно что в копировании памяти.» уже ни на чем не основана?

                                                                    Из входного буфера и беру

                                                                    Какого входного буфера?

                                                                    Как бы tree-парсер строит дерево по входящему потоку — отсюда и знаю, где что.

                                                                    Какой парсер, если речь идет о сериализации? Это, на минуточку, обратная операция. Речь идет о том, что у вас в приложении была прикладная информация, строка, которую кто-то хочет как данные записать в ваш формат. Вот это надо сериализовать. В какой момент (и с какой стоимостью) будут убраны \n?
                                                                    • –1
                                                                      А что они там забыли? Пусть лежат себе в своём потоке.

                                                                      Если у вас есть что возразить — дерзайте. Давайте не будем с умным видом загадывать друг другу загадки.

                                                                      В который пришли данные. Что не понятно?

                                                                      В момент создания соответствующей ветки дерева будет пробежка по данным в поисках переводов строк. Убирать их нет никакого смысла.
                                                                      • 0
                                                                        Да я уже возразил (указав, что unix pipe — это не копирование памяти), только вы это игнорируете.

                                                                        Что ж, вернемся к этому примеру еще раз. Вот у вас есть входной поток (именно поток, то есть либо cin, либо сетевое соединение, то есть читать вы его можете только один раз и только вперед; это как раз ситуация чтения из unix pipe), и вы в нем стоите на позиции «начало значения». Это значение нужно прочитать в память. Пожалуйста, покажите, как именно вы на этой операции получите разницу в скорости парсинга на порядок по сравнению с аналогичной операцией для JSON.

                                                                        (С записью будем разбираться следующим этапом)
                                                                        • 0
                                                                          А что же это? Прямая запись в адресное пространство другого процесса?

                                                                          • 0
                                                                            Для программы это файловый дескриптор, с соответствующим набором операций. Что у него там внутри происходит, вы уже не знаете.
                                                                            • 0
                                                                              Может поделитесь этим сакральным знанием как данные передаются между процессами без копирования?
                                                                              • 0
                                                                                (Ну понятно, показывать, где у вас там разница на порядок, вы не хотите — или не можете, поэтому цепляетесь за удобную фразу.)

                                                                                Так вот, где-то внутри unix pipe память, конечно, копируется. В том же значении, в котором запись элемента в массив — копирование значения этого элемента. Но важно то, что там внутри есть не только копирование, но еще и управление состоянием потока, buffer rotation и другие смешные вещи, поэтому давать ссылку на memcpy, а потом распространять описанную там производительность на unix pipe не надо.
                                                                                • –1
                                                                                  Работа пайплайна не зависит от того что за данные мы передаём. Но чтобы передать данные в пайплайн нужно подготовить их в том виде, в котором они должны в него идти.

                                                                                  В случае JSON мы должны выделить некоторый буфер, сериализовать в него дерево и сказать системе «отправь ка этот буфер в выходной поток».

                                                                                  В случае Tree данные не надо как либо экранировать, так что достаточно сказать «отправь этот кусок памяти в буфер, потом этот, а затем тот».
                                                                                  • 0
                                                                                    В случае JSON мы должны выделить некоторый буфер, сериализовать в него дерево и сказать системе «отправь ка этот буфер в выходной поток».

                                                                                    Это еще зачем? Что мешает взять JSON и сразу сериализовать его в выходной поток?

                                                                                    так что достаточно сказать «отправь этот кусок памяти в буфер, потом этот, а затем тот».

                                                                                    А управляющие последовательности сами собой возьмутся? Ну и да, в случае Tree бремя экранирования лежит на пользовательском коде (потому что Tree не поддерживает значения, содержащие \n).

                                                                                    Вот именно чтобы это все не трогать, я и предлагал обсуждать чтение из входящего потока конкретного значения, причем строкового (поскольку это единственный общий тип между tree и json). Что характерно, в этом треде именно это и обсуждается, поскольку он весь посвящен экранированию.

                                                                                    • –2
                                                                                      Потокобезопасность мешает.

                                                                                      Вы не фантазируйте, а попробуйте разобраться в формате. А лучше сделайте свою реализацию и сразу всё поймёте.
                                                                                      • +1
                                                                                        Потокобезопасность мешает.

                                                                                        Ээээ, это каким образом? (Почему-то реализациям в .net не мешает)

                                                                                        Вы не фантазируйте, а попробуйте разобраться в формате. А лучше сделайте свою реализацию и сразу всё поймёте.

                                                                                        Я не фантазирую, я уже набросал псевдокод реализации, и именно поэтому и говорю, что разница в скорости чтения в обоих случаях пренебрежима (и ни в одном случае нет необходимости в копировании буфера памяти целиком). Так что теперь мне интересен ваш псевдокод, на основании которого вы делаете вывод о разнице в скорости на порядок.
                                                                                        • 0
                                                                                          Так покажите же свой псевдокод.
                                                                                          • +1
                                                                                            (Srsly?)

                                                                                            Мы исходим из положения, что входной поток стоит ровно на начале читаемой строки.

                                                                                            Tree:
                                                                                            buf = "";
                                                                                            while(true)
                                                                                            {
                                                                                              c = ReadNextChar();
                                                                                              switch(c)
                                                                                              {
                                                                                                case EOF:
                                                                                                case '\n':
                                                                                                  return buf;
                                                                                                default:
                                                                                                  buf += c;
                                                                                              }
                                                                                            }
                                                                                            


                                                                                            Стоимость строго 4n (полагая операции перехода, чтения, выбора и присвоения идентичными по стоимости).

                                                                                            JSON:

                                                                                            buf = "";
                                                                                            while(true)
                                                                                            {
                                                                                              c = ReadNextChar();
                                                                                              switch(c)
                                                                                              {
                                                                                                case EOF:
                                                                                                  throw;
                                                                                                case '"':
                                                                                                  return buf;
                                                                                                case '\\':
                                                                                                  switch(ReadNextChar())
                                                                                                  {
                                                                                                    case 'u'
                                                                                                      buf += MakeChar(ReadNextChar(), ReadNextChar(), ReadNextChar(), ReadNextChar())
                                                                                                    case 'n':
                                                                                                      buf += '\n';
                                                                                                    //raise, rinse, repeat
                                                                                                    default:
                                                                                                      throw;
                                                                                                  }
                                                                                                default:
                                                                                                  buf += c;
                                                                                              }
                                                                                            }
                                                                                            


                                                                                            Стоимость в пограничном случае m (ни одной эскейп-последовательности): 4n.
                                                                                            Стоимость в пограничном случае w (все символы — двухсимвольный эскейп): 3n
                                                                                            Стоимость в пограничном случае u (все символы — юникод-эскейп): 2.5n

                                                                                            Обобщенная формула стоимости: 4m + 6w + 15u, при n = m + 2w + 6u

                                                                                            BTW, решив простенькую систему неравенств, можно внезапно выяснить, что чтение JSON по такому алгоритму никогда не медленнее чтения tree.

                                                                                            PS Кстати, у меня тут из грамматики создалось ощущение, что tree не ждет \n в конце каждого значения, а это значит, что можно очень легко сломать файл, просто разорвав передачу, и способов это провалидировать не существует. Это нехорошо.
                                                                                            • 0
                                                                                              Да, кривая грамматика. Там и символ перевода строки не тем кодом был задан.

                                                                                              Какой-то псевдокод у вас совсем оторванный от реальности.

                                                                                              Tree:
                                                                                              1. запоминаем начальное и конечное смещение = 0
                                                                                              2. читаем байты в цикле
                                                                                              2.1. если байт равен 0A, то создаём узел дерева, который хранит начальное и конечное смещение
                                                                                              2.2. иначе увеличиваем конечное смещение
                                                                                              3. формируем префикс
                                                                                              4. пробегаемся по списку узлов
                                                                                              4.1. говорим системе скопировать префикс в системный буфер
                                                                                              4.2. говорим системе скопировать данные по смещениям из узла в системный буфер

                                                                                              JSON:
                                                                                              1. выделить памяти с запасом под временный буфер
                                                                                              2. в цикле читаем байты
                                                                                              2.1. если спецсимвол, то смотрим какой и пишем в буфер соответствующий набор байт
                                                                                              2.2. иначе копируем байт как есть
                                                                                              2.3. если буфер вдруг закончился, то говорим системе скопировать его в системный буфер
                                                                                              2.3.1 начинаем заполнять его заново

                                                                                              Итого, отличие в следующем:
                                                                                              JSON: побайтово фактически копируем все данные во временный буфер, чем больше буфер, тем меньше системных вызовов
                                                                                              Tree: побайтово читаем, но создаём сравнительно небольшую структуру пропорциональную числу переводов строк, число системных вызовов в 2 раза больше числа переводов строк
                                                                                              • +1
                                                                                                Какой-то псевдокод у вас совсем оторванный от реальности.

                                                                                                Он (а) гарантировано работающий и (б) выполняющий поставленную задачу

                                                                                                (Вы, все-таки, пишете, пожалуйста, псевдокод, а не текст, а то слишком вопросов много.)

                                                                                                Tree:
                                                                                                1. запоминаем начальное и конечное смещение = 0
                                                                                                2. читаем байты в цикле

                                                                                                Читаем откуда и куда? Что является верхней границей цикла?

                                                                                                2.1. если байт равен 0A, то создаём узел дерева, который хранит начальное и конечное смещение
                                                                                                2.2. иначе увеличиваем конечное смещение
                                                                                                3. формируем префикс
                                                                                                4. пробегаемся по списку узлов
                                                                                                4.1. говорим системе скопировать префикс в системный буфер
                                                                                                4.2. говорим системе скопировать данные по смещениям из узла в системный буфер

                                                                                                Что такое «системный буфер»? Скопировать данные откуда?

                                                                                                Tree: побайтово читаем, но создаём сравнительно небольшую структуру пропорциональную числу переводов строк, число системных вызовов в 2 раза больше числа переводов строк

                                                                                                У вас с переводом строки значение заканчивается, задача выполнена. Откуда вдруг следующие действия?

                                                                                                Я еще раз повторяю задачу: надо прочитать из входящего потока (т.е., forward-only источника данных) строковое значения, исходя из того, что сейчас положение потока — начало этого значения (в tree — после =, в JSON — после "). Если угодно, рассматривайте эту задачу как функцию, у которой на входе правильно спозиционированный поток, а на выходе — прочитанное значение (положение в потоке, понятное дело, меняется).

                                                                                                Если по каким-то причинам вы считаете эту задачу некорректной и никогда не возникающей, то объясните мне, что делает вот этот код (честное слово, я лучше даже не буду пытаться посчитать его стоимость по памяти, с учетом того, что строки в D неизменны):

                                                                                                if( input[0] == '=' ) {
                                                                                                  auto value = input.takeUntil( "\n" )[ 1 .. $ ];
                                                                                                  auto next = new Tree( "" , value , [] , baseUri , row , col );
                                                                                                  ...
                                                                                                }
                                                                                                
                                                                                                ...
                                                                                                
                                                                                                string takeUntil( ref string input , string symbols ) {
                                                                                                  auto res = "";
                                                                                                  while( input.length ) {
                                                                                                    auto symbol = input[0];
                                                                                                    if( symbols.indexOf( symbol ) == -1 ) {
                                                                                                      res ~= symbol;
                                                                                                      input = input[ 1 .. $ ];
                                                                                                    } else {
                                                                                                      break;
                                                                                                    }
                                                                                                  }
                                                                                                  return res;
                                                                                                }
                                                                                                
                                                                                                • 0
                                                                                                  In most Unix-like systems, all processes of a pipeline are started at the same time, with their streams appropriately connected, and managed by the scheduler together with all other processes running on the machine. An important aspect of this, setting Unix pipes apart from other pipe implementations, is the concept of buffering: for example a sending program may produce 5000 bytes per second, and a receiving program may only be able to accept 100 bytes per second, but no data is lost. Instead, the output of the sending program is held in a queue. When the receiving program is ready to read data, the operating system sends its data from the queue, then removes that data from the queue. If the queue buffer fills up, the sending program is suspended (blocked) until the receiving program has had a chance to read some data and make room in the buffer. In Linux, the size of the buffer is 65536 bytes.
                                                                                                  en.wikipedia.org/wiki/Pipeline_(Unix)#Implementation

                                                                                                  У вас условия меняются как перчатки. Входящий поток в каком формате?

                                                                                                  Именно благодаря тому, что строки в D неизменны и есть GC, срезы этих строк занимают считанные байты.
                                                                                                  • 0
                                                                                                    У вас условия меняются как перчатки. Входящий поток в каком формате?

                                                                                                    Входящий поток в виде последовательности символов (чтобы не размениваться на детали реализации utf-8). И нет, эти условия как не менялись, так и не меняются.

                                                                                                    Именно благодаря тому, что строки в D неизменны и есть GC, срезы этих строк занимают считанные байты.

                                                                                                    Срезы-то да. А суммы?
                                                                                                    • 0
                                                                                                      Тогда при чём тут пассаж про = и "?

                                                                                                      Не знаю, умеет ли D объединять последовательно идущие срезы. Но даже если нет, то не проблема конкатенацию срезов заменить на пересоздание среза.
                                                                                                      • +1
                                                                                                        Тогда при чём тут пассаж про = и "?

                                                                                                        К положению в потоке.

                                                                                                        Я так понимаю, что вы будете придираться к чему угодно, лишь бы не показывать свой псевдокод?
                                                                                                      • 0
                                                                                                        Ага, я так и знал, что вашего псевдокода в итоге не будет. Ок, тезис про более высокую производительность tree на чтении значений считаем опровергнутым.
                                                                                                        • 0
                                                                                                          import std.stdio;
                                                                                                          import std.file;
                                                                                                          import jin.tree;
                                                                                                          import std.json;
                                                                                                          import std.datetime;
                                                                                                          
                                                                                                          void testJSON() {
                                                                                                          	auto JSONString = cast(string) read( "./source/jin/tree/formats/sample.min.json" );
                                                                                                          	std.file.write( "res.json" , parseJSON( JSONString ).toString() );
                                                                                                          }
                                                                                                          void testTree() {
                                                                                                          	auto TreeString = cast(string) read( "./source/jin/tree/formats/sample.tree" );
                                                                                                          	std.file.write( "res.tree" , Tree.parse( TreeString ).toString() );
                                                                                                          }
                                                                                                          
                                                                                                          int main(string[] argv)
                                                                                                          {
                                                                                                          	auto result = comparingBenchmark!( testJSON, testTree, 200 );
                                                                                                          	writeln( result.point );
                                                                                                          	writeln( result );
                                                                                                          
                                                                                                          	result = comparingBenchmark!( testTree, testJSON, 200 );
                                                                                                          	writeln( result.point );
                                                                                                          	writeln( result );
                                                                                                          	return 0;
                                                                                                          }
                                                                                                          

                                                                                                          Результат:

                                                                                                          1.42267
                                                                                                          ComparingBenchmarkResult(TickDuration(5512316), TickDuration(3874621))
                                                                                                          0.750915
                                                                                                          ComparingBenchmarkResult(TickDuration(3867075), TickDuration(5149818))
                                                                                                          

                                                                                                          Tree примерно в полтора раза быстрее JSON. При этом:
                                                                                                          * в тесте есть чтение с диска и запись на него (впрочем, ОС всё-равно это дело кэширует)
                                                                                                          * данные не содержат символов, которые необходимо в JSON экранировать
                                                                                                          * JSON читается и сериализется в минифицированном виде
                                                                                                          • 0
                                                                                                            на чтении значений

                                                                                                            С тем, что ваш формат в общем читается быстрее, я не спорил.
                                                                                                            • 0
                                                                                                              А чем чтение значений отличается от чтения всего остального?
                                                                                                              • 0
                                                                                                                Логикой. Дискуссия была именно про значения и влияние экранирования в них на скорость обработки.
                                                      • +1
                                                        Кстати, безотносительно того, как работают unix pipes — вы правда думаете, что это самый частый сценарий использования вашего формата? А если нет, то что делать с типовыми применениями, где все-таки есть ощутимые потери на I/O?
                                                        • 0
                                                          Очевидно, в этом случае разница в скорости будет не столь значительна. Но уменьшение нагрузки на процессор тем не менее будет.
                                                          • 0
                                                            В каком сценарии использования это уменьшение будет давать ощутимый прирост производительности?
                      • +1
                        Я так понимаю вы намекаете на base64, base62 и другие способы конвертации бинарных данных в текстовые? Вот собственно эта фаза перекодировки и мешает. Она не позволяет просто потоком вгрузить файл в память и далее просто ссылаться на его участки — обязательно надо выделять отдельные куски памяти для отдельных декодированных значений.


                        Как в случае Tree будет выглядить «просто ссылка» на бинарную переменную int-10?
                        • –2
                          return numbers.select('int-10')[0].value!int
                          

                          И да, в этот момент будет копирование. Более другие структуры типа StringBuffer могут обойтись без него.
                          • +1
                            Вы привели код, после обработки парсером.
                            Ни о какой «ссылке на участок файла» речи здесь не идет.
                            С таким же успехом я и в xml запихаю бинарные данные в виде base64, распакую и верну результат.

                            Внимательно прочитайте цитату. Там вы говорите, что недостатком такого подхода является необходимость выделения отдельного буффера под данные перед их использованием.
                            Покажите код, который будет отдавать ссылку на int-10 без выделения дополнительной памяти под сборку этого самого инта.
                            • 0
                              Вот именно, что вам придётся распаковывать, а я просто скопирую. Копирование участков памяти — это гораздо более быстрая операция, чем битовые операции или трансляция через таблицу символов.

                              А зачем вам ссылка, если он может быть сразу загружен в регистр процессора? ;-) Код не приведу, ибо программирую на более высоком уровне абстракции, но компилятору сгенерировать его не проблема.
                              • 0
                                Я вообще не обсуждал скорость.
                                Я обсуждал ваш конкретный довод — «выделять отдельную память».
                                Вывод тут ясен — довод не актуален и, судя по всему, вброшен без серьезного обдумывания.
                                • –2
                                  Очевидно, для массивов байт без выделения отдельной памяти не обойтись. К счастью, если куда более экономные структуры. Связные списки, деревья, кучи и тп. Они вполне могут обходиться ссылками на участки памяти.
                              • 0
                                Если уж мы говорим о «декодировании», то тут тоже особых проблем нет.
                                Ничего не мешает сунуть бинарные данные в тот же json. Единственное что придется сделать — экранировать кавычки и бэкслэш.
                                С точки зрения скорости исполнения и сложности кода нет разницы, экранировать один символ(перенос строки) или два(кавычки и бэк слэш).

                                Реальным плюсом вашего формата стало бы хранение цельного блока бинарных данных, т.к. это позволило бы делать то, о чем вы говорили — ссылаться на документ не работая с бинарными данными отдельно. Но у вас этого плюса нет. как уже было выяснено.
                                • 0
                                  Давайте так, я дам вам JSON с экранированными кавычками и бэкслешами, а вы попробуете его распарсить.
                                  • 0
                                    Я не смогу это распарсить. Ваш JSON не валидный.

                                    А вообще, в чем вы видите сложность? В том, что стандартные парсеры это не поймут? Ну так это естественно. Речь же как раз о том, что нужно изменить, чтобы тот же json смог хранить бинари не хуже чем у вас.
                                    Или вы видите проблему в том, чтобы написать парсер, который будет считать значения валидными, если они содержат любые символы, а не только unicode(как это по стандарту должно быть).
                                    • 0
                                      А, ну с такой позиции-то конечно. Только вот кавычки — это довольно частый в употреблении символ, поэтому их лучше бы не экранировать, чтобы не вредить читаемости.
                                      • –3
                                        Есть вопрос, как вы сохраните бинарный массив интов каждый из которых имеет значение '\n'? Как это будет выглядеть в вашем формате?
                                        • 0
                                          Опять вы про массивы и мапки :-)

                                          image

                                          foreach( Tree n ; numbers.select('int') ) {
                                              writeln( n.value!int );
                                          }
                                          

  • +9
    А как можно представить строковое значение, состоящее из одного/N пробелов? Пустое строковое значение? Если так, как я думаю — то без отображения в редакторе непечатаемых символов можно получить не то, что ожидаешь.
    • –8
      Да, есть такой нюанс. К сожалению, большинство редакторов непечатаемые символы показывают слишком навязчиво и вырвиглазно. Думаю через плагин подсветки синтаксиса можно решить эту проблему.
      • +2
        Думаю можно кроме "=" добавить «хередок» формат:
        имя #= считается_всё
        включая пробелы и переводы строк
        до пустой
        \n\s*#=\n
        или в первой строчке до #=
        #=
        
        • –4
          А чем подсветка концов строк не угодила-то?
        • +1
          Не знаю, как heredoc поможет увидеть, есть пробелы в значении, или там пустая строка. Но зато он может помочь при минимизации: глубоко вложенные данные, имеющие много символов перевода строки, будут минимизироваться за счет избавления от стартовых символов табуляции.

          Я же имел в виду, что можно было бы сделать что-то вроде «присваивания с кавычками», как-то так:

          something "="simple quoted string goes here"
          emptyString "=""
          singleSpace "=" "
          doubleSpace "="  "
          tooManySpaces "="                            "
          


          Это повысило бы читаемость, особенно для тех, кто привык к тому же json и вообще «классическому» подходу объявления строковых литералов. Вместо "= можно использовать что-то вроде q= или |= или что угодно еще, чтобы избежать визуальной путаницы с кавычками. Ну и поддержку вложенных кавычек можно не реализовывать для таких значений: для этого есть обычное «безкавычечное» присваивание.

          Однако, такое нововведение добавит в формат «еще один способ делать то же самое», что не всегда есть хорошо и не всем нравится. Ну и плюс парсинг и валидация документа усложнится.
  • 0
    .
  • +1
    Модель Tree крайне проста — есть только один тип узлов, и каждый узел имеет: имя, значение, список дочерних узлов. Имя и значение являются взаимоисключающими.
    Здесь точно нет ошибки? Вот же простой пример узла с именем и значением:

    ip =8.8.8.8
    
    • –1
      Это два узла, между ними стоит пробел :-)
      • 0
        Тогда что такое узел? Я понимал что это узел дерева, но вы видимо называете узлом токены. Это надо было подробнее расписать и с примерами.
        • +8
          Еще больше запутывает ваше описание «есть только один тип узлов, и каждый имеет имя, значение, список дочерних». Получается не один и не каждый.
          • 0
            Видимо, имеется в виду, как у узлов традиционного дерева, скажем, бинарного: поля есть все у каждого узла, но некоторые могут быть пустыми (null). Если арибут(поле) узла «имя» заполнен, то это lvalue элемент, тот, что слева от присваивания. Иначе(имя=null) могут быть заполнены либо «значение» (окраинный лист), либо «дочерние» (ссылка на другой узел/список узлов, может быть реализовано как вырожденное дерево).
            • 0
              Почти. Любой узел может иметь либо не пустое имя, либо не пустое значение, либо пустое и имя и значение. Любые узлы могут иметь дочерние.
              • 0
                Тогда чем отличаются
                ip =8.8.8.8
                

                и
                ip
                    =8.8.8.8
                

                • 0
                  Ничем. Первая запись — короткая форма, позволяющая не плодить лишних отступов, когда дочерний узел всего один.
              • +2
                1) Почему узел с не пустым именем не может иметь значение? Почему запрещено ip=8.8.8.8?

                2) Каким образом узел с не пустым значением может иметь дочерние? Так?
                =8.8.8.8
                    дочернийузел1
                    дочернийузел2
                
                Для чего это нужно?

                А к какому узлу будут привязаны дочерние в этом случае?
                ip =8.8.8.8
                    дочернийузел1
                    дочернийузел2
                
                Правильно ли я понимаю, что в первом случае дочерние будут привязаны к узлу, который идет непосредственно перед ними, а во втором к предыдущему узлу? Т.е. к какому элементу буду привязаны дочерние узлы, зависит от типа родительского узла? Хотя вы и утверждаете что все узлы одинаковые. Либо все же не зависит, и дочерние узлы будут привязаны к узлу со значением =8.8.8.8? Но тогда ваше утверждение, что две записи равноправны, неверно.

                3) Тот же вопрос, когда в родительской строке два или больше узлов только с названием.

                4) С учетом всего вышесказанного вы по прежнему считаете, что ваш формат очень простой?
                • 0
                  1. Потому что значение может состоять из нескольких узлов, ввиду того, что символа перевода строки в них быть не может.

                  2. Яркий пример — указание для всех узлов, откуда они были взяты:

                  image

                  3. Почитайте внимательнее описание формата. «Наличие табуляции в строке означает, что первый узел в этой строке должен быть вложен в последний узел последней строки имеющей табуляцию на один меньше.»

                  4. Да, очень.
              • +2
                > Любой узел может иметь либо не пустое имя, либо не пустое значение, либо пустое и имя и значение
                И это получается не «один тип узлов», а три:
                1. Узел-имя.
                2. Узел-значение.
                3. Пустой узел (по факту, насколько я понял, используемый для представления пустых строк в данных, пусть тогда будет не 3, а 2а).
                Другое дело, что все они могут иметь дочерние, это их действительно объединяет. (Я даже знаю, где можно использовать узлы, дочерние к узлам-значениям).

                На самом деле, очень интересная идея. Но по результатам обсуждения здесь, думаю, вы уже заметили, что очень многое неочевидно и надо подробно разжёвывать и документировать все нюансы. Инерция восприятия очень сильна (хоть с теми же пробелами перед-после = — это не воспринимается как два узла; похоже по записи на пару ключ-значение и воспринимается так же).
                Ну и, прежде чем говорить об убийстве чего бы то ни было, нужны инструменты трансляции в|из убиенных без потерь. То есть перегнали JSON>Tree>JSON — получили на выходе исходный JSON, чтоб даже diff не отличил родного сына от подкидыша.
                По поводу человекочитаемости: тут я вас поддерживаю. Рад, что кто-то кроме меня считает JSON, XML и YAML менее человекочитаемыми. Просто работаю с этими читающими человеками (переводчиками в моём случае) и каждый раз всё, что приходит на локализацию (а схемы у разных заказчиков ой какие разные, да даже в одном проекте может быть несколько разных форматов или разное использование одного формата), приходится перегонять во что-то более удобное и потом засовывать обратно, иначе будут переведённые ключи, убиение экранирования в значениях и прочая трудноуловимая содомия. Для программиста они все человекочитаемы, а вот для случайного человека, которому надо поправить пару строк в данных — всё пугающая каша (экранирование и кавычки особенно). Единственный минус — отсутствие комментариев. Бох с ним, не надо вольностей типа комментариев после строк, но хочется, чтоб хотя бы строка без отступов, начинающаяся с #, воспринималась как комментарий. Комментарии — плюс к человекочитаемости. (Впрочем, никто не мешает это сделать в конкретной реализации, но тут мы получим несовместимые реализации, что не есть гуд).
                Вообще, когда увидел, сразу два применения под свой рабочий процесс возникли в голове:
                1. Перегонка из других форматов в Tree (кстати, с названием тоже надо что-то думать, вы это гуглить пробовали?), потом открытие этого файла как TSV в табличном редакторе и команда: «девочки, переводим столбец D». Потом так же спокойно загоняем обратно.
                2. Давно думаю над своей простой системой памяти переводов (все современные слишком перегружены) и Tree отлично бы подошёл в качестве легкоразбираемого и человекочитаемого внутреннего формата, тем более что в|из него можно было бы перегонять существующие стандарты вроде TMX (страшненький XML).
                В общем, успехов вам, буду следить за развитием и думать над практическим применением. Хороший формат, только уж больно неочевидный, слишком много стереотипов и привычек ломается.
                • 0
                  Вы даете переводчикам переводить файлы в «сыром» виде, не давая воспринимающих эти форматы инструментов?.. Кажется, я начинаю понимать, почему качественных переводов днем с огнем не найти…
                  • +2
                    Нет, их давали в сыром виде до меня. Я как раз с этим борюсь.
                    А нормальных инструментов, воспринимающих все форматы, нет в природе, приходится колхозить на каждый раз.
                    Характерный пример — крупный проект на рельсах, в котором из всех возможных способов локализации (нормальный gettext в том числе) разработчики выбрали умолчальный simple. Как результат — ТЫСЯЧИ файлов YAML с очень разной схемой, которые не воспринимает ни один инструмент, потому что из них валидных ровно ДВА.

                    > Кажется, я начинаю понимать, почему качественных переводов днем с огнем не найти.
                    Нет, даже близко не начинаете. Это сложная и многофакторная проблема. Качество подготовки кадров, узкие ценовые рамки, треш на клиентской стороне (например, непонимание того, что у других языков в отличие от default могут не работать конструкции вида «%d items»), убогость готовых инструментов, неумение менеджеров договариваться о реальных сроках, экономия на хороших редакторах, тьма причин.
                    • 0
                      Справедливости ради, в defaul language конструкция %d items тоже иногда не работает. Но я понял, о чем речь.
                      • 0
                        Да, я говорил об отличии «один-много» от «одна штука, две штуки, пять штук», и, сами понимаете, это не единственное отличие русского от default, а в природе есть не только русский. И есть ещё отдельный класс: сейчас многие онлайн-игры делают в Китае, и там приходится иметь дело даже не с английским, а с мунспик-пиджином, вот где вообще кровавые слёзы. Там что в самом языке, что в подходах к локализации абсолютно инопланетная неформализуемая логика.
                • 0
                  Просто работаю с этими читающими человеками (переводчиками в моём случае) и каждый раз всё, что приходит на локализацию (а схемы у разных заказчиков ой какие разные, да даже в одном проекте может быть несколько разных форматов или разное использование одного формата), приходится перегонять во что-то более удобное и потом засовывать обратно, иначе будут переведённые ключи, убиение экранирования в значениях и прочая трудноуловимая содомия

                  В контексте вашей задачи tree ситуации не исправит — там тоже будет проблема с недопустимыми символами.
                  • 0
                    Пробел перед значением и странные переводы строк? Это проверяемо. Опять же, я не жду, что в своей нынешней реализации формат спасёт от всех проблем. Но подумать и примериться надо. Потому что может спасти от части существующих и не создать сильно много новых проблем. Ну и для внутреннего представления памяти переводов он однозначно годен, потому что не сложнее .po, гораздо проще .tmx и может редактироваться неспециалистом, которому в голову вдолблена пара простых правил.
                    • 0
                      В первую очередь — странные отношения со строками, которые вынесут мозг любому не-специалисту.

                      Для внутреннего представления, возможно, подойдет, но, я сильно подозреваю, что избыточно.
                      • 0
                        Не знаю. Надо пробовать. Но вообще мне кажется, что «перевод только после знака равно» и «перед новой строкой тот же знак равно с отступом в столько же табуляций» несколько проще объяснить, чем значение всех экранируемых обычно символов и «почему, если в русском тексте добавились кавычки, надо всю строку тоже закавычить, а внутренние кавычки заэкранировать». В основном это сейчас ложится на редакторов и мою и без того не слишком крепкую голову :)
                        И, повторюсь, я не жду волшебной палочки, я примеряюсь к новому инструменту. Не исключаю, что он окажется напрочь негоден на практике, но подход необычный и интересный, поэтому любопытство лично меня всё равно заставит попробовать.
                        • +1
                          Очень сложно объяснить, что если в конце текста нужен перевод строки, то нужно вбить перевод, отбивку табуляцией и еще один перевод; но если он не в конце текста, то начинать писать сразу после отбивки.
                          • 0
                            Довольно странный кейс, но всё вполне логично — строк получится столько же сколько и символов равно.
                            • 0
                              О нет.

                              =abc\n -> одна строка (с точки зрения человека)

                              =abс\n
                              =\n -> все еще одна строка с точки зрения человека

                              =abc\n
                              =def\n -> две строки с точки зрения человека.
                              • 0
                                "abc\n" // одна строка с точки зрения человека
                                "abс\n\n" // всё ещё одна строка с точки зрения человека
                                "abc\ndef\n" // и опять же одна строка с точки зрения человека
                                

                                Вы что доказать-то этой софистикой хотите?
                                • 0
                                  Что у tree есть свои странности, которые неподготовленному человеку вынесут мозг. Конкретный кейс конкретного massimus.
                                  • –1
                                    Вы находитесь в плену собственных привычек.
                                    • 0
                                      «Неподготовленному».
                • 0
                  Важно, что у всех узлов один апи, а так, да, можно ввести 3 отдельных субкласса, но я ограничился одним классом и 3 хелперами для их создания.

                  Тул для транскодинга, конечно будет. Я хочу использовать Tree как AST в которое перегонять остальные форматы (хоть xml, хоть css), трансформировать с помощью lisp/xslt-подобного языка в модель целевого языка и сериализовывать в него.

                  Может присоединишься к разработке? :-)
                  • 0
                    Боюсь, если я присоединюсь, потом за мной придётся разгребать тонны быдлокода :)
                    Не было пока, к моему стыду, ни одного случая, чтоб собствеенноручно написанное хотелось бы выложить в паблик, я хорош только в быстрых временных велосипедах на костылях. Борюсь с собой, но пока так.
                  • 0
                    P. S.: Про «быстрость» велосипедов — это про скорость написания, а не выполнения. Пока просто передо мной никогда не стояла задача, где критична скорость работы программы, одноразовость сказывается. Но я на гитхабе подписался следить за проектом, по крайней мере багрепорты и фичреквесты с меня будут скорее всего.
                  • 0
                    По поводу негуглибельного названия: попробовал treemple, unitree и несколько производных, пока фэйл :)
                    Но подумаю, наверняка можно придумать гуглибельное и более-менее уникальное название. Чтоб хотя бы «*tree format» возвращал что-то информативное.
                    • 0
                      (впрочем, как вариант entree, с ресторанами бороться легче, хотя бы за счёт дополнительных слов в запросе, чем с симметричными форматами)
                    • 0
                      Как насчёт treelobite?
                      • 0
                        TreeLoByte :-)
                        • 0
                          TreeLowByte
      • +2
        vintage, вы зря игнорируете мой вопрос. Ваше описание формата основано на концепции узлов. При этом вы не говорите, что это такое. По прошествии суток я так и не могу понять синтаксиса вашего супер-простого формата.
        • –2
          Прошу прощения, но днём я работу работаю, а не хабру хабрю :-) Ответил чуть выше.
          • 0
            Само собой, нисколько не хотел претендовать на ваше рабочее время. Однако вы ответили на много других вопросов, заданных значительно позже, а тот, что выше, к тому моменту проигнорировали.
            • 0
              Они были банально выше на странице. Я не сравнивал даты комментариев.
      • +3
        До этого момента, я думал, что все понятно…
  • +17
    Мне кажется, что основная проблема – с названием. Если нужно найти что-то на тему XML или JSON, то в Гугле можно набрать это слово, и оно сработает как тег, выдача будет существенно предметной. Если же завтра, допустим, Tree распространится, то придется каждый раз хитрить, чтобы отвязаться от биологов, теорий баз данных, описаний структур и онтологий. Если вводите новый термин, его нужно делать по возможности уникальным.
    • –1
      Действительно. У вас нет идей, как его назвать так, чтобы название отражало его иерархическую суть?
      • 0
        TreeS
        • +4
          Чем множественная форма поможет в этом вопросе?
          • +4
            На самом деле для меня это сокращение от TreeSpace. А множественная форма сильно поможет — www.google.ru/webhp?sourceid=chrome-instant&ion=1&espv=2&ie=UTF-8#newwindow=1&safe=off&q=Trees
            , потому что нет такого названия сущности. И гугл сумеет различить tree и Trees если появится.
            • НЛО прилетело и опубликовало эту надпись здесь
        • 0
          Тогда уж TreeSp ;)
        • 0
          S-Tree, по аналогии с B-Tree.
          • –2
            Кстати, SyntaxTree очень даже подходит идеологически.
      • 0
        .tree
      • 0
        tabbed tree
      • +9
        XTreeML :)
        • 0
          К сожалению, сейчас XML технологии не в почёте. А так, классная игра слов.
    • 0
      Искать если и будут, то скорее не сам формат, а какой-то язык на его основе. Например, json.tree.
  • +16
    Скорость парсинга/сериализации

    Очень интересно как вы это оценили, по моему мнению ваш формат тяжелее того же json, грамматика у вас не проще, она сложнее т.к. она контекстно зависимая.
    Удобство редактирования

    Вот уж нет, ваш формат неудобно редактировать. Почему после = не должно быть пробела? Это ведь неудобно. Да и реализуется это не сложно.

    Просто я тоже не давно писал свой формат, от нечего делать, и как по мне так он удобнее. Интересно сравнить как будет выглядеть в вашем формате подобная структура. Может что полезного для своего формата найду в вашем.
    {
    	// Узел может одновременно иметь атрибуты, значения, подузлы
    	scene (type = Open; lods = on;) = "Start location" 
    	{
    		object = "Tree"
    		{
    			position = 56.42, 23.0, 234.325;
    			rotation = 0, 45.34, 0;
    			
    			mesh (layout = pos, UV, norm, tang, wei, ind; animated = true) 
    				= "./assets/meshes/tree.ms";
    				
    			animations = "idle_1", "idle_2", "idle_3";
    			
    			collider (static = true) = cylinder
    			{
    				offset = 0, 5, 0;
    				radius = 45.351;
    				height = 180.45;
    			}
    			
    			material = cook torrance
    			{
    				albedo = "./assets/textures/tree_a.png";
    				normal(normal = rgb; spec = a) = "./textures/tree_n.png";
    			}
    			
    			object = "leaves"
    			{
    				mesh (tangents = on; animated = true) = "./assets/meshes/leaves.ms";
    				animations = "idle_1", "idle_2", "idle_3";
    				
    				material(translucent = true; alpha = alpha test) = cook torrance
    				{
    					albedo = "./assets/textures/leaves_a.png";
    					normal(normal = rgb; spec = a) = "./textures/leaves_n.png";
    					translucent = "./textures/leaves_t.png";
    				}
    			}
    		}	
    	}
    }
    
    • –4
      А что сложного в тривиальной контекстно зависимой грамматике? Там нужно всего-лишь иметь стек узлов и по числу табов его обрезать.

      Люди вон, к нагромождению скобочек в лиспе привыкли так, что удобным это считают, а вы тут на один пробел жалуетесь :-)

      Насколько я понял код, в Tree он будет выглядеть примерно так:

      image
      • +3
        Тем что у вас она куда менее тривиальна чем у того же JSON. А на пробел жалуемся т.к. не понятен смысл данного ограничения, который разгуливается на раз.
        Насколько я понял код, в Tree он будет выглядеть примерно так:

        Ок тогда поговорим о проблемах которые в данном случае несёт ваш формат.
        1) Он получился длиннее и намного (вы не описали материал и под узел leaves). И он неправильно передал структуру выше, а конкретно: почему у rotation в конце просто 0 а не =0 это опечатка? layout в моём случае это массив констант layout = pos, UV, norm, tang, wei, ind; у вас же это под узлы месша.
        2) Такая же проблема как и у JSON, это отсутствие атрибутов (собственно потому мне и пришлось писать свой формат). Есть ли хоть какая нибудь гарантия что узлы name, type, lods в вашем случае не окажутся где то в середине узла между другими большими под узлами? Напомню что в ваш формат пишет не только человек, но и машина, а ей не укажешь какой узел должен идти раньше, а какой позже (по крайней мере подобного требования в вашем стандарте я не увидел).
        3) Также имеется проблема которая сразу ставит крест на формате (по крайней мере для меня), отсутствие поддержки массивов. Серьёзно кому может понравится писать
        offset
            =0
            =5
            =0
        // вместо
        offset = 0, 5, 0;
        //или
        "offset" : [0, 5, 0],
        


        В результате получаем что ваш формат проигрывает XML в случае когда активно используются атрибуты и массивы, и также проигрывает JSON в случае с использованием массивов и если он будет использоваться для протокола передачи (из-за табов).
        Если так нравятся табы, то можно было бы сделать тот же JSON только без ", {
        Выглядело бы лучше.
        • –4
          При парсинге JSON вам точно также нужен будет стек, только ещё нужно будет разэкранировать строки.

          1. Разумеется, специализированный DSL будет наглядней и короче, чем универсальный формат. 0 — это конечно же опечатка.

          2. А это принципиальный момент, чтобы атрибуты шли первым? Если да, то и вставляйте их первыми :-) Формат лишь гарантирует, что в каком порядке их туда положил, в таком же и получишь.

          3. Это только в тривиальных случаях «и в одну строку норм».

          "offset" : [ 123454.234345, 1234893434.3425342, 324589565.1342352461 ],
          

          image

          JSON плохо своей моделью данных прежде всего. Любое дерево (ast, html и прочее) в JSON выглядит как нагромождение костылей.
          • +3
            1. Разумеется, специализированный DSL будет наглядней и короче, чем универсальный формат.

            Дело в том, что то что я показал не является специализированным форматом, он подходит для любых потребностей.
            Формат лишь гарантирует, что в каком порядке их туда положил, в таком же и получишь.

            Честно говоря не нашёл этого требования в исходном тексте, разве это зависит не от реализации? Если для хранение нодов я буду использовать мапу или хеш, как можно гарантировать порядок записи?
            3. Это только в тривиальных случаях «и в одну строку норм».

            Это в любых случаях норм, и я не говорил что запись должна быть исключительно в 1 строку, вот пример записи матрицы из того же формата.
            transform =
               1, 0, 0, 0,
               0, 1, 0, 0,
               0, 0, 1, 0,
               0, 0, 0, 0,
            

            И да хотел бы я посмотреть в какой ад превратился бы формат коллады, используй они твой формат. По большому счёту именно твой формат является специализированным.
            JSON плохо своей моделью данных прежде всего. Любое дерево (ast, html и прочее) в JSON выглядит как нагромождение костылей.

            Он практически полностью идентичен твоему формату, все различия заключаются лишь в том что для блоков у тебя используются табы, а там {} ну и в требованиях использовать " во всём остальном он лучше твоего формата, опять же если использовать hjson то и от " можно будет избавится. Не вижу ничего в чём твой формат превосходит JSON и из-за чего на него хотелось бы перейти. Где те фичи ради которых хотелось бы на него перейти?
            А ещё я никак не могу понять чем твоя «Произвольная иерархия» отличается от того же json или XML? И если уж говорить о возможностях то YAML на голову выше.
            Вообще с таблицей которую ты предоставил я в корне несогласен. Она крайне субъективна.
            • –3
              Честно говоря не нашёл этого требования в исходном тексте, разве это зависит не от реализации? Если для хранение нодов я буду использовать мапу или хеш, как можно гарантировать порядок записи?
              Это не требование, это определение списка.

              Матрицы могут быть представлены на уровне языка так:

              image

              Хоть это и потребует немного больше телодвижений, чем с простым вертикальным списком. С другой стороны, модель Tree позволяет делать такие выкрутасы:

              image

              • +1
                Это не требование, это определение списка.

                Не вижу в вашем тексте требования что дочерние узлы должны хранится в списке.
                Матрицы могут быть представлены на уровне языка так:

                То есть на уровне приложения мне придётся парсить 1..0..0..0?
                С другой стороны, модель Tree позволяет делать такие выкрутасы:

                В каком смысле? y.determinant нам придётся разбивать в коде?
                • –3
                  Присмотритесь внимательнее.

                  Точки — это подсветка пробелов в IDEA

                  Тут речь о том, что в ячейке может быть формула от другой матрицы.
                  • +5
                    Точки — это подсветка пробелов в IDEA

                    А в ы можете не делать скриншот ide а просто вставить текст с тегом кода? Ато это сбивает с толку, я например в IDEA не работал, откуда мне занть что точки это пробелы.
                    Тут речь о том, что в ячейке может быть формула от другой матрицы.

                    В каком смысле формула? Мне придётся писать код чтобы из 1 0 0 0 получить нужную мне последовательность?
                    • –1
                      Каким тегом? Хабр как бы не поддерживает подсветку Tree синтаксиса.

                      Тому, кто будет разрабатывать реализацию языка, придётся написать этот код.
                      • +4
                        Текст без подсветки лучше его отсутствия.
                        • –2
                          Но лучше всё же с подсветкой.
                          • +5
                            Да, текст лучше с подсветкой. Но сейчас-то текста вообще нет.
                            • –2
                              Если вам нужно его скопировать и поредактировать, то могу выложить и сырым текстом.
                    • –3
                      А в ы можете не делать скриншот ide а просто вставить текст с тегом кода? Ато это сбивает с толку, я например в IDEA не работал, откуда мне занть что точки это пробелы.
                      В Visual Studio работаете? Ctrl+R, Ctrl+W
                      • –1
                        Хабравчане, и за что минусы? Я лишь сделал предположение по профилю Chaos_Optima, что он работает с .NET и соответственно…

                        Хотя… нужно было на весь список подписок посмотреть.
        • –3
          А что собственно мешает так записать? массив только будет рассматриваться как строка, и вся работа с ним ложится на приложение которое ожидает там массив. Или приложение знать не знает что там должен быть массив?
          • 0
            Если так рассуждать так вообще ничего ненужно, сразу брать и перекладывать всё на приложение. Зачем нам вообще форматы какие-то.
            В данном случае всё упирается в удобстве, приложение может не знать что там или менять логику работы в зависимости от того что там, массив или просто значение. В любом случае это одна из фундаментальных структур которую не плохо было бы иметь. В конце концов это нетрудно сделать. Достаточно добавить [ ] и сделать так чтобы внутри этих скобок табуляция и переносы не учитывались, на уровне элементов.
            • –1
              Сделать вроде бы нетрудно, но сразу же поиметь неопределенность в другом и свести на нет остальные достоинства формата.
              Собственно «сделать так чтобы внутри этих скобок табуляция и переносы не учитывались» это и есть перенос обработки массива на приложение.
              • 0
                А в чём заключается неопределённость, и каким образом это сводит на нет остальные достоинства формата?
                Собственно «сделать так чтобы внутри этих скобок табуляция и переносы не учитывались» это и есть перенос обработки массива на приложение.

                эм… вообще то нет. Если мы напишем
                object
                   transform =[ 1, 0, 0,
                                             0, 1, 0,
                                             0, 0, 1]
                

                то это сломает парсер.
    • 0
      Похоже на формат конфига nginx'а.
      Есть в открытом доступе парсеры?
  • –4