Как стать автором
Обновить

Как я писал простой язык конфигурации и в итоге перемудрил

Уровень сложностиПростой
Время на прочтение24 мин
Количество просмотров5.7K

Здравствуйте, судя по моему скромному опыту, с таким форматом обменом данными, как JSON, знакомы очень многие. Сейчас он является де‑факто стандартом в веб‑приложениях (при обмене данными между клиентом и сервером), а некоторые производные формы JSON используются и в других местах — например, BSON в MongoDB или MessagePack в Pinterest или Redis (поддерживается в скриптинге). Я тоже хорошо знаком с JSON и довольно длительное время регулярно его использовал, однако...

Однажды мне в голову пришла мысль: «А почему бы не попробовать сократить JSON, написав свой формат и оставив его при этом совместимым со своим родителем?». Сказано — сделано, благо что в тот момент я писал в качестве пет‑проекта маленький движок для текстовых квестов, конфиги и темы к которому писались именно на JSON, то есть пространство для экспериментов было. Так как я уже был знаком с тем, как писать формальные грамматики для ANTLR, я быстро набросал её концепт, взяв за основу грамматику JSON и добавив сахара по вкусу (до зубного скрежета). Так как мой движок уже назывался Lirot (с иврита — «видеть»), я не стал заморачиваться и назвал новый язык для его конфигурационных Av — это пятый месяц еврейского календаря, а ещё он примерно соответствует июлю — началу августа. Это было как раз кстати, ведь Av я начал писать именно в июле.

Что из нового я добавил в Av:

  1. Комментарии — есть только однострочные, начинаются на точку с запятой и идут до конца строки;

  2. Идентификаторы (Id) — можно использовать вместо строк там, где ожидается ключ или значение. Представляют собой просто целые слова;

  3. Шестнадцатеричные целые числа (HexInt) — беззнаковые, иногда удобно писать сразу их, а не строки. Например, я использовал их в конфигах тем для движка;

  4. Байтовые последовательности (bytes), которые можно составлять из HexInt с любым количеством знаков (желательно разумным). Концепт подразумевает, что выравнивание и разбивка последовательностей на целые числа определённой разрядности лежит на плечах реализующего, в моей реализации на Java они парсятся как Byte);

  5. Биндинги на уровне массивов и мап (в Av так называются объекты) — полезная штука для сокращения повторяемых значений;

  6. Ссылки на существующие константы или на их свойства;

  7. Чёткое разделение number на int и float. Int представлен десятичными и шестнадцатеричными литералами;

  8. Отдельный тип bool, к которому принадлежат true и false;

  9. Nil — прямой аналог null, который в некоторых случаях помогает сократить объём текста, а также просто нравится мне больше;

  10. Общая категория atom, куда входят int, float, string, bool, nil и id;

  11. Дополнительные виды ключей в мапах — можно использовать не только строки, но и любые атомы;

  12. Опциональные запятые в конце массивов и мап.

  13. Интерполяция строк;

  14. Опциональные фигурные скобки в топ‑левеле;

  15. Семь видов заимствований (borrow).

Что я убрал (опционально) из JSON:

  • Кавычки возле коротких строковых ключей и значений (в большинстве случаев можно заменить на id);

  • Двоеточия в объектах;

  • Запятые в объектах и массивах.

Да, конечно, я добавил в JSON гораздо больше, чем убрал, однако фактически при замерах Av почти всегда оказывается компактнее JSON. Сначала я расскажу о каждом из изменений, а затем сравню Av с JSON. Примечание: большая часть примеров будет браться с сайта jsoneditoronline.org.

Нововведения

Ниже для каждого нововведения я буду приводить пример текста на JSON, если для него есть прямое соответствие, а под ним — на Av.

1. Комментарии

Av поддерживает однострочные комментарии, которые начинаются на точку с запятой. Спецификация JSON не описывает комментарии, однако некоторые реализации закрывают на это глаза и добавляют однострочные, многострочные или оба вида сразу.

// this is a comment
/*
this is a comment too
*/
; this is a comment

2. Идентификаторы

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

Id: [a-zA-Z_0-9+/%|&^<=>*!?\u0391-\u03A9\u03B1-\u03C9\-]+;
{
  "name":"Chris",
  "age":23,
  "city":"New York"
}
{
  name:Chris,
  age:23,
  city:"New York"
}

3. Шестнадцатеричные числа

Почти во всех местах, где я видел шестнадцатеричные числа, они писались с префиксами 0x или #. Unreal Engine (color picker в виджетах), Blender, Photoshop, CSS и другие инструменты используют именно второй вариант, да и мне он нравится больше, поэтому я выбрал его.

#CAFEBABE ; #cafebabe, #CaFeBaBe

4. Байтовые последовательности

Записываются в виде круглых скобок, внутри которых есть как минимум одно шестнадцатеричное число. Можно группировать числа как угодно, пробелы при этом не требуются. Допустимо группировать числа с количеством нибблов, не равным степени двойки (правда, потом вам придётся самим решать, что с ними делать, но всё же допустимо).

; space is not neccessary
(#89#50#4E#47#0D#0A#1A#0A)
; grouped to bytes
(#89 #50 #4E #47 #0D #0A #1A #0A)
; grouped to 2 bytes
(#8950 #4E47 #0D0A #1A0A)
; grouped to 4 bytes
(#89504E47 #0D0A1A0A)
; grouped to 8 bytes
(#89504E470D0A1A0A)

5. Биндинги

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

Есть два вида констант: временные (temporary) и постоянные (persistent). Разница в том, что временные константы не затрагивают структуру массива или мапы, а постоянные напрямую встраиваются в них. В случае с мапой создаётся новая пара ключ‑значение, а в случае с массивом в него добавляется значение, которое в дальнейшем тоже будет доступно по ссылке. Синтаксически разница очень простая:

{
  foo := 42 ; temporary
  bar ::= 4 ; persistent, will also declare "bar": 4
}

Область видимости у биндингов (как и у ссылок) лексическая. При определении константы с именем, которое уже есть в данной области видимости, старое определение затеняется, при этом в каждой области видимости есть доступ к константам, которые были объявлены до её создания. Если биндинг a объявлен перед биндингом b, то нельзя написать a := b, поскольку в момент объявления a ресолвер Av ещё не знает про b. Следовательно, создать рекурсивные биндинги нельзя.

{
  thumbnails := { ; temporary bind
      url := "https://i.ytimg.com/vi/"
      default {
          url 'url
          width 120
          height 90
      }
      medium {
          url 'url
          width 320
          height 180
      }
      high {
          url 'url
          width 480
          height 360
      }
      ; it is not valid because unvisibleForThumbnails
      ; will be declared AFTER thumbnails
      unv 'unvisibleForThumbnails
  }
  unvisibleForThumbnails := nil
  myThumbnails 'thumbnails
}

Замечание: в качестве имён биндингов можно использовать не только идентификаторы, но и строки.

6. Ссылки

Ссылки нужны для использования биндингов и их свойств в качестве значений. Ссылка на биндинг выглядит как одиночная кавычка и имя биндинга: 'myBinding, получение свойства выглядит так:

'myBinding.prop.foo.bar.baz

Так как справа находится atom, а не только id или string, свойства можно получать совершенно разные. Кроме того, свойства можно получать и у обычных выражений:

{
  a ::= {
    b := 0
    c ::= 5
    c1 ?
  }
  d 'a ; {c 5}
  e a.c ; 5
  f a.size ; 1
  ? a.entries ; [[c 5] [c1 ?]]

  arr := [137 80 99] ; Quinacridone magenta
  zero arr.0 ; 137
  arrSize [0 1 2].size
}

Важно отметить, что у некоторых типов изначально есть свои свойства, находящиеся в свойстве props. Если это массив, то это его элементы по индексам, а также size. Если это мапа, то это entries, keys и values. Важно отметить, что при получении ссылки на свойство мапы будет браться именно пользовательский (или встроенный) биндинг, а не значение по ключу. Ещё раз:

{ ; m
  ; m = {key value}
  key value
  
  ; m = {key value}, m props are
  ; {entries [[key value]] keys [key] values [value]}
  prop := value
  
  ; m = {key value key1 value}, m props are
  ; {entries [[key value] [key1 value]] keys [key key1] values [value value]}
  key1 ::= value

  entries := 1
}

Если затенить встроенное свойство, то встроенная логика перестанет работать (на самом деле, конечно, все эти entries, keys и size считаться будут, но вот выколупать их из значения будет уже невозможно, поскольку ссылка протухнет.

7. Int и Float

В JSON числа представлены единственным типом — number, Av же чётко проводит границу между целыми (при этом поддерживая шестнадцатеричные литералы) и дробными.

42.0 //number
42 //number
42.0 ; float
42 ; int
#abcd ; hex int

8. Bool

Спецификация JSON определяет true, null и false как три литерала и не присваивает им какой‑либо тип, Av же настаивает, что true и false имеют тип bool. Выглядят они точно так же, как и в JSON, так что пример я приводить не буду.

9. Nil

Nil в Av отличается от null в JSON только тем, что дополнительно имеет трёхбуквенный вариант написания.

null
null ; JSON-compatible nil
nil ; Av preferred nil

10. Atom

Av определяет общую категорию для неделимых значений и называет её atom. В atom входят:

  • Int;

  • Float;

  • String;

  • Bool;

  • Nil;

  • Id.

11. Атомы на месте ключей

В JSON ключом мапы может быть исключительно строка, Av позволяет использовать в качестве ключа значение любого типа, входящего в категорию atom.

{
    "42": "answer",
    "answer": 42,
    "-0.0": "minusZero",
    "null": "val",
    "true": false,
    "false": true
}
{
	answer 42
	42 answer
	-0.0 minusZero
	nil val
	true false
	false true
}

12. Запятые

JSON не разрешает использовать замыкающие запятые (trailing commas) в конце списка или мапы, в Av это вполне возможно. Лично я не очень люблю их, однако вполне согласен с утверждением, что они очень полезны при активной вставке данных (не нужно постоянно писать запятую, чтобы вставить что‑то в конец).

Пример ниже взят отсюда.

[
  { "name": "Chris", "age": 23, "city": "New York" },
  { "name": "Emily", "age": 19, "city": "Atlanta" },
  { "name": "Joe", "age": 32, "city": "New York" },
  { "name": "Kevin", "age": 19, "city": "Atlanta" },
  { "name": "Michelle", "age": 27, "city": "Los Angeles" },
  { "name": "Robert", "age": 45, "city": "Manhattan" },
  { "name": "Sarah", "age": 31, "city": "New York" }
]
[
  { "name": "Chris", "age": 23, "city": "New York" },
  { "name": "Emily", "age": 19, "city": "Atlanta" },
  { "name": "Joe", "age": 32, "city": "New York" },
  { "name": "Kevin", "age": 19, "city": "Atlanta" },
  { "name": "Michelle", "age": 27, "city": "Los Angeles" },
  { "name": "Robert", "age": 45, "city": "Manhattan" },
  { "name": "Sarah", "age": 31, "city": "New York" }, ; note this trailing comma
]

Пример ниже сгенерирован с помощью этого сайта.

{
  "result": [
    "Oriental Concrete Tuna",
    "Gorgeous Plastic Pizza",
    "Handcrafted Soft Soap",
    "Fantastic Steel Computer",
    "Licensed Frozen Hat",
    "Generic Rubber Towels"
  ]
}
{
  "result": [
    "Oriental Concrete Tuna",
    "Gorgeous Plastic Pizza",
    "Handcrafted Soft Soap",
    "Fantastic Steel Computer",
    "Licensed Frozen Hat",
    "Generic Rubber Towels", ; note this trailing comma
  ]
}

13. Интерполяция строк

Av поддерживает интерполяцию строк, которая запускается после разбора текста на нём. Выглядит так же, как и в Cue:

{
  answer := 42
  "answer is \($answer)"; or just "answer is \(42)"
}

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

14. Опциональные фигурные скобки в топ-левеле

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

// JSON allows only map at top-level
// and only with curly braces
{
  "foo": "bar",
  "baz": "quux",
  "arr": [0, 1, 2, 3]
}
foo bar
baz quux
arr [0 1 2 3]

15. Семь смертных заимствований

В Av есть очень странная для языка конфигурации концепция — заимствования (borrow). Они позволяют брать два значения и определённым образом совмещать одно с другим, заимствуя у нового значения что‑то для старого. Вы можете думать о них как об операторах или инфиксных функциях (по факту так и есть). Для каждой пары типов есть своя перегрузка каждого заимствования.

Ниже приведён список встроенных заимствований:

  • Overlay: ~, overlay

  • Unite: +, unite

  • Default: |, default

  • Intersect: &, intersect

  • Differ: , differ

  • Either: ^, either

  • Push: @, push

Ниже для каждого из заимствований я буду приводить табличку тип‑тип. Условные обозначения в табличке:

  • _ — старое значение остаётся без изменений;

  • l — левое значение;

  • r — правое значение;

  • ? — Будет объяснено отдельно;

  • lq — уникальность в пользу левого значения;

  • rq — уникальность в пользу правого значения;

  • [...arr] или {...obj} — spread, как в JavaScript.

Что ещё важно отметить: при сложении или вычитании чисел оба приводятся к старшему типу (int → float), а bool рассматривается как int (0 или 1). При сложении строки с чем‑либо происходит конкатенация. Единственный тип, почти не участвующий в заимствованиях, — это bytes.

Overlay (~)

Совмещает два значения, заменяя значения в первом объекте/массиве на соответствующие значения из второго, если они существуют. Если слева находится nil, то наложение срабатывает в пользу правого значения. Если слева массив, а справа — мапа, то мапа превращается в массив, получается

[[key1 val1] [key2 val2] ... [keyn valn]].

Если слева мапа, а справа — массив, массив превращается в мапу, получается

{0 el0 1 el1 ... n eln}.

~

int

float

string

bool

nil

bytes

array

map

int

_

_

_

_

_

_

_

_

float

_

_

_

_

_

_

_

_

string

_

_

_

_

_

_

_

_

bool

_

_

_

l xnor r

_

_

_

_

nil

r

r

r

r

r

r

r

r

bytes

_

_

_

_

_

_

_

_

array

_

_

_

_

_

_

rq [...l, ...r]

rq [...l, ...r.arr]

map

_

_

_

_

_

_

rq {...l, ...r.map}

rq {...l, ...r}

object1 ::= {
	id 1
	name "Object One"
	color red
	size large
	features [feature1 feature2]
}

object2 ::= {
	id 2
	name "Object Two"
	color blue
	shape circle
	features [feature3 feature4]
}

; object3_overlay 'object1 ~ 'object2
object3_overlay {
	id 2
	name "Object Two"
	color blue
	size large
	shape circle
	features [feature3 feature4]
}

Unite (+)

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

+

int

float

string

bool

nil

bytes

array

map

int

l + r

l + r

l + r

l + r

_

_

_

_

float

l + r

l + r

l + r

l + r

_

_

_

_

string

l + r

l + r

l + r

l + r

_

_

_

_

bool

l + r

l + r

l + r

l nor r

_

_

_

_

nil

r

r

r

r

r

r

r

r

bytes

_

_

_

_

_

?

_

_

array

_

_

_

_

_

_

[...l, ...r]

[...l, ...r.arr]

map

_

_

_

_

_

_

{...l, ...r.map}

{...l, ...r}

Unite bytes‑bytes склеивает две последовательности байтов.

; object3_unite 'object1 + 'object2
object3_unite {
	id 2
	name "Object One"
	color red
	size large
	shape circle
	features [feature1 feature2 feature3 feature4]
}

Default (|)

Применяет значения из второго объекта или массива к первому, только если они отсутствуют в первом.

|

int

float

string

bool

nil

bytes

array

map

int

_

_

_

_

_

_

_

_

float

_

_

_

_

_

_

_

_

string

_

_

_

_

_

_

_

_

bool

_

_

_

l or r

_

_

_

_

nil

r

r

r

r

r

r

r

r

bytes

_

_

_

_

_

_

_

_

array

_

_

_

_

_

_

lq [...l, ...r]

lq [...l, ...r.arr]

map

_

_

_

_

_

_

lq {...l, ...r.map}

lq {...l, ...r}

; object3_default 'object1 | 'object2
object3_default {
	id 1
	name "Object One"
	color red
	size large
	shape circle
	features [feature1 feature2]
}

Intersect (&)

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

&

int

float

string

bool

nil

bytes

array

map

int

_

_

_

_

_

_

_

_

float

_

_

_

_

_

_

_

_

string

_

_

_

_

_

_

_

_

bool

_

_

_

l and r

_

_

_

_

nil

r

r

r

r

r

r

r

r

bytes

_

_

_

_

_

_

_

_

array

_

_

_

_

_

_

common [...l, ...r]

common [...l, ...r.arr]

map

_

_

_

_

_

_

common {...l, ...r.map}

common {...l, ...r}

; object3_intersect 'object1 & 'object2
object3_intersect {
	id 1
	name "Object One"
	color red
	features [feature1 feature2]
}

Differ (-)

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

-

int

float

string

bool

nil

bytes

array

map

int

l - r

l - r

_

l - r

_

_

_

_

float

l - r

l - r

_

l - r

_

_

_

_

string

_

_

_

_

_

_

_

_

bool

l - r

l - r

_

l nand r

_

_

_

_

nil

r

r

r

r

r

r

r

r

bytes

_

_

_

_

_

_

_

_

array

_

_

_

_

_

_

diff [...l, ...r]

diff [...l, ...r.arr]

map

_

_

_

_

_

_

diff {...l, ...r.map}

diff {...l, ...r}

; object3_differ 'object1 - 'object2
object3_differ {
	size large
}

Either (^)

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

^

int

float

string

bool

nil

bytes

array

map

int

_

_

_

_

_

_

_

_

float

_

_

_

_

_

_

_

_

string

_

_

_

_

_

_

_

_

bool

_

_

_

l xor r

_

_

_

_

nil

r

r

r

r

r

r

r

r

bytes

_

_

_

_

_

_

_

_

array

_

_

_

_

_

_

unique [...l, ...r]

unique [...l, ...r.arr]

map

_

_

_

_

_

_

unique {...l, ...r.map}

unique {...l, ...r}

; object3_either 'object1 ^ 'object2
object3_either {
	size large
	shape circle
}

Push (@)

Добавляет значения из второго объекта или массива к первому независимо от того, существуют ли они уже в первом.

@

int

float

string

bool

nil

bytes

array

map

int

_

_

_

_

_

_

_

_

float

_

_

_

_

_

_

_

_

string

_

_

_

_

_

_

_

_

bool

_

_

_

l imply r

_

_

_

_

nil

r

r

r

r

r

r

r

r

bytes

_

_

_

_

_

_

_

_

array

[...l, r]

[...l, r]

[...l, r]

[...l, r]

[...l, r]

[...l, r]

[...l, r]

[...l, r]

map

{...l, r}

{...l, r}

{...l, r}

{...l, r}

{...l, r}

{...l, r}

{...l, r}

{...l, r}

; object3_push 'object1 @ 'object2
;object3_push {
;	id 1
;	name "Object One"
;	color red
;	size large
;	features [feature1 feature2]
;	{ ; wont't work: there's no key
;		id 2
;		name "Object Two"
;		color blue
;		shape circle
;		features [feature3 feature4]
;	}
;}
 
; object3_push 'object1 ~ {
;   features 'object1.features @ 'object2.features
; }
object3_push {
	id 1
	name "Object One"
	color red
	size large
	features [feature1 feature2 [feature3 feature4]]
}

Сжимаем JSON в идиоматичный Av

Шаг 0. JSON

Ниже приведён ответ YouTube на некий запрос (код взят отсюда). Ответ, конечно же, в формате JSON. Ответ пропущен через форматтер с шириной табуляции в два пробела, фигурные скобки расположены, как обычно (далее все примеры Av будут отформатированы аналогичным образом).

5,987 символов
{
  "kind": "youtube#searchListResponse",
  "etag": "q4ibjmYp1KA3RqMF4jFLl6PBwOg",
  "nextPageToken": "CAUQAA",
  "regionCode": "NL",
  "pageInfo": {
    "totalResults": 1000000,
    "resultsPerPage": 5
  },
  "items": [
    {
      "kind": "youtube#searchResult",
      "etag": "QCsHBifbaernVCbLv8Cu6rAeaDQ",
      "id": {
        "kind": "youtube#video",
        "videoId": "TvWDY4Mm5GM"
      },
      "snippet": {
        "publishedAt": "2023-07-24T14:15:01Z",
        "channelId": "UCwozCpFp9g9x0wAzuFh0hwQ",
        "title": "3 Football Clubs Kylian Mbappe Should Avoid Signing ✍️❌⚽️ #football #mbappe #shorts",
        "description": "",
        "thumbnails": {
          "default": {
            "url": "https://i.ytimg.com/vi/TvWDY4Mm5GM/default.jpg",
            "width": 120,
            "height": 90
          },
          "medium": {
            "url": "https://i.ytimg.com/vi/TvWDY4Mm5GM/mqdefault.jpg",
            "width": 320,
            "height": 180
          },
          "high": {
            "url": "https://i.ytimg.com/vi/TvWDY4Mm5GM/hqdefault.jpg",
            "width": 480,
            "height": 360
          }
        },
        "channelTitle": "FC Motivate",
        "liveBroadcastContent": "none",
        "publishTime": "2023-07-24T14:15:01Z"
      }
    },
    {
      "kind": "youtube#searchResult",
      "etag": "0NG5QHdtIQM_V-DBJDEf-jK_Y9k",
      "id": {
        "kind": "youtube#video",
        "videoId": "aZM_42CcNZ4"
      },
      "snippet": {
        "publishedAt": "2023-07-24T16:09:27Z",
        "channelId": "UCM5gMM_HqfKHYIEJ3lstMUA",
        "title": "Which Football Club Could Cristiano Ronaldo Afford To Buy? 💰",
        "description": "Sign up to Sorare and get a FREE card: https://sorare.pxf.io/NellisShorts Give Soraredata a go for FREE: ...",
        "thumbnails": {
          "default": {
            "url": "https://i.ytimg.com/vi/aZM_42CcNZ4/default.jpg",
            "width": 120,
            "height": 90
          },
          "medium": {
            "url": "https://i.ytimg.com/vi/aZM_42CcNZ4/mqdefault.jpg",
            "width": 320,
            "height": 180
          },
          "high": {
            "url": "https://i.ytimg.com/vi/aZM_42CcNZ4/hqdefault.jpg",
            "width": 480,
            "height": 360
          }
        },
        "channelTitle": "John Nellis",
        "liveBroadcastContent": "none",
        "publishTime": "2023-07-24T16:09:27Z"
      }
    },
    {
      "kind": "youtube#searchResult",
      "etag": "WbBz4oh9I5VaYj91LjeJvffrBVY",
      "id": {
        "kind": "youtube#video",
        "videoId": "wkP3XS3aNAY"
      },
      "snippet": {
        "publishedAt": "2023-07-24T16:00:50Z",
        "channelId": "UC4EP1dxFDPup_aFLt0ElsDw",
        "title": "PAULO DYBALA vs THE WORLD'S LONGEST FREEKICK WALL",
        "description": "Can Paulo Dybala curl a football around the World's longest free kick wall? We met up with the World Cup winner and put him to ...",
        "thumbnails": {
          "default": {
            "url": "https://i.ytimg.com/vi/wkP3XS3aNAY/default.jpg",
            "width": 120,
            "height": 90
          },
          "medium": {
            "url": "https://i.ytimg.com/vi/wkP3XS3aNAY/mqdefault.jpg",
            "width": 320,
            "height": 180
          },
          "high": {
            "url": "https://i.ytimg.com/vi/wkP3XS3aNAY/hqdefault.jpg",
            "width": 480,
            "height": 360
          }
        },
        "channelTitle": "Shoot for Love",
        "liveBroadcastContent": "none",
        "publishTime": "2023-07-24T16:00:50Z"
      }
    },
    {
      "kind": "youtube#searchResult",
      "etag": "juxv_FhT_l4qrR05S1QTrb4CGh8",
      "id": {
        "kind": "youtube#video",
        "videoId": "rJkDZ0WvfT8"
      },
      "snippet": {
        "publishedAt": "2023-07-24T10:00:39Z",
        "channelId": "UCO8qj5u80Ga7N_tP3BZWWhQ",
        "title": "TOP 10 DEFENDERS 2023",
        "description": "SoccerKingz https://soccerkingz.nl Use code: 'ILOVEHOF' to get 10% off. TOP 10 DEFENDERS 2023 Follow us! • Instagram ...",
        "thumbnails": {
          "default": {
            "url": "https://i.ytimg.com/vi/rJkDZ0WvfT8/default.jpg",
            "width": 120,
            "height": 90
          },
          "medium": {
            "url": "https://i.ytimg.com/vi/rJkDZ0WvfT8/mqdefault.jpg",
            "width": 320,
            "height": 180
          },
          "high": {
            "url": "https://i.ytimg.com/vi/rJkDZ0WvfT8/hqdefault.jpg",
            "width": 480,
            "height": 360
          }
        },
        "channelTitle": "Home of Football",
        "liveBroadcastContent": "none",
        "publishTime": "2023-07-24T10:00:39Z"
      }
    },
    {
      "kind": "youtube#searchResult",
      "etag": "wtuknXTmI1txoULeH3aWaOuXOow",
      "id": {
        "kind": "youtube#video",
        "videoId": "XH0rtu4U6SE"
      },
      "snippet": {
        "publishedAt": "2023-07-21T16:30:05Z",
        "channelId": "UCwozCpFp9g9x0wAzuFh0hwQ",
        "title": "3 Things You Didn't Know About Erling Haaland ⚽️🇳🇴 #football #haaland #shorts",
        "description": "",
        "thumbnails": {
          "default": {
            "url": "https://i.ytimg.com/vi/XH0rtu4U6SE/default.jpg",
            "width": 120,
            "height": 90
          },
          "medium": {
            "url": "https://i.ytimg.com/vi/XH0rtu4U6SE/mqdefault.jpg",
            "width": 320,
            "height": 180
          },
          "high": {
            "url": "https://i.ytimg.com/vi/XH0rtu4U6SE/hqdefault.jpg",
            "width": 480,
            "height": 360
          }
        },
        "channelTitle": "FC Motivate",
        "liveBroadcastContent": "none",
        "publishTime": "2023-07-21T16:30:05Z"
      }
    }
  ]
}

Шаг 1. Первичный марафет

На первом шаге я:

  1. Заменяю строки идентификаторами там, где это возможно;

  2. Убираю двоеточия после ключей;

  3. Убираю запятые;

  4. Опускаю фигурные скобки в топ‑левеле.

5,069 символов
kind "youtube#searchListResponse"
etag q4ibjmYp1KA3RqMF4jFLl6PBwOg
nextPageToken: CAUQAA
regionCode NL
pageInfo {
  totalResults 1000000
  resultsPerPage 5
}
items [
  {
    kind "youtube#searchResult"
    etag QCsHBifbaernVCbLv8Cu6rAeaDQ
    id {
      kind "youtube#video"
      videoId TvWDY4Mm5GM
    }
    snippet {
      publishedAt "2023-07-24T14:15:01Z"
      channelId UCwozCpFp9g9x0wAzuFh0hwQ
      title "3 Football Clubs Kylian Mbappe Should Avoid Signing ✍️❌⚽️ #football #mbappe #shorts"
      description ""
      thumbnails {
        "default" {
          url "https://i.ytimg.com/vi/TvWDY4Mm5GM/default.jpg"
          width 120
          height 90
        }
        medium {
          url "https://i.ytimg.com/vi/TvWDY4Mm5GM/mqdefault.jpg"
          width 320
          height 180
        }
        high {
          url "https://i.ytimg.com/vi/TvWDY4Mm5GM/hqdefault.jpg"
          width 480
          height 360
        }
      }
      channelTitle "FC Motivate"
      liveBroadcastContent none
      publishTime "2023-07-24T14:15:01Z"
    }
  }
  {
    kind "youtube#searchResult"
    etag 0NG5QHdtIQM_V-DBJDEf-jK_Y9k
    id {
      kind "youtube#video"
      videoId aZM_42CcNZ4
    }
    snippet {
      publishedAt "2023-07-24T16:09:27Z"
      channelId UCM5gMM_HqfKHYIEJ3lstMUA
      title "Which Football Club Could Cristiano Ronaldo Afford To Buy? 💰"
      description "Sign up to Sorare and get a FREE card: https://sorare.pxf.io/NellisShorts Give Soraredata a go for FREE: ..."
      thumbnails {
        "default" {
          url "https://i.ytimg.com/vi/aZM_42CcNZ4/default.jpg"
          width 120
          height 90
        }
        medium {
          url "https://i.ytimg.com/vi/aZM_42CcNZ4/mqdefault.jpg"
          width 320
          height 180
        }
        high {
          url "https://i.ytimg.com/vi/aZM_42CcNZ4/hqdefault.jpg"
          width 480
          height 360
        }
      }
      channelTitle "John Nellis"
      liveBroadcastContent none
      publishTime "2023-07-24T16:09:27Z"
    }
  }
  {
    kind "youtube#searchResult"
    etag WbBz4oh9I5VaYj91LjeJvffrBVY
    id {
      kind "youtube#video"
      videoId wkP3XS3aNAY
    }
    snippet {
      publishedAt "2023-07-24T16:00:50Z"
      channelId UC4EP1dxFDPup_aFLt0ElsDw
      title "PAULO DYBALA vs THE WORLD'S LONGEST FREEKICK WALL"
      description "Can Paulo Dybala curl a football around the World's longest free kick wall? We met up with the World Cup winner and put him to ..."
      thumbnails {
        "default" {
          url "https://i.ytimg.com/vi/wkP3XS3aNAY/default.jpg"
          width 120
          height 90
        }
        medium {
          url "https://i.ytimg.com/vi/wkP3XS3aNAY/mqdefault.jpg"
          width 320
          height 180
        }
        high {
          url "https://i.ytimg.com/vi/wkP3XS3aNAY/hqdefault.jpg"
          width 480
          height 360
        }
      }
      channelTitle "Shoot for Love"
      liveBroadcastContent none
      publishTime "2023-07-24T16:00:50Z"
    }
  }
  {
    kind "youtube#searchResult"
    etag juxv_FhT_l4qrR05S1QTrb4CGh8
    id {
      kind "youtube#video"
      videoId rJkDZ0WvfT8
    }
    snippet {
      publishedAt "2023-07-24T10:00:39Z"
      channelId UCO8qj5u80Ga7N_tP3BZWWhQ
      title "TOP 10 DEFENDERS 2023"
      description "SoccerKingz https://soccerkingz.nl Use code: 'ILOVEHOF' to get 10% off. TOP 10 DEFENDERS 2023 Follow us! • Instagram ..."
      thumbnails {
        "default" {
          url "https://i.ytimg.com/vi/rJkDZ0WvfT8/default.jpg"
          width 120
          height 90
        }
        medium {
          url "https://i.ytimg.com/vi/rJkDZ0WvfT8/mqdefault.jpg"
          width 320
          height 180
        }
        high {
          url "https://i.ytimg.com/vi/rJkDZ0WvfT8/hqdefault.jpg"
          width 480
          height 360
        }
      }
      channelTitle "Home of Football"
      liveBroadcastContent none
      publishTime "2023-07-24T10:00:39Z"
    }
  }
  {
    kind "youtube#searchResult"
    etag "wtuknXTmI1txoULeH3aWaOuXOow"
    id {
      kind "youtube#video"
      videoId XH0rtu4U6SE
    }
    snippet {
      publishedAt "2023-07-21T16:30:05Z"
      channelId UCwozCpFp9g9x0wAzuFh0hwQ
      title "3 Things You Didn't Know About Erling Haaland ⚽️🇳🇴 #football #haaland #shorts"
      description ""
      thumbnails {
        "default" {
          url "https://i.ytimg.com/vi/XH0rtu4U6SE/default.jpg"
          width 120
          height 90
        }
        medium {
          url "https://i.ytimg.com/vi/XH0rtu4U6SE/mqdefault.jpg"
          width 320
          height 180
        }
        high {
          url "https://i.ytimg.com/vi/XH0rtu4U6SE/hqdefault.jpg"
          width 480
          height 360
        }
      }
      channelTitle "FC Motivate"
      liveBroadcastContent none
      publishTime "2023-07-21T16:30:05Z"
    }
  }
]

Результат первого шага: 84.7% от шага 0.

Шаг 2. Вынос thumbnails в биндинги

5,335 символов
thumbnails := {
  url := "https://i.ytimg.com/vi/"
  "default" ::= {
    url ::= 'url
    width 120
    height 90
  }
  medium ::= {
    url ::= 'url
    width 320
    height 180
  }
  high ::= {
    url ::= 'url
    width 480
    height 360
  }
}
kind "youtube#searchListResponse"
etag q4ibjmYp1KA3RqMF4jFLl6PBwOg
nextPageToken: CAUQAA
regionCode NL
pageInfo {
  totalResults 1000000
  resultsPerPage 5
}
items [
  {
    kind "youtube#searchResult"
    etag QCsHBifbaernVCbLv8Cu6rAeaDQ
    id {
      kind "youtube#video"
      videoId TvWDY4Mm5GM
    }
    snippet {
      publishedAt "2023-07-24T14:15:01Z"
      channelId UCwozCpFp9g9x0wAzuFh0hwQ
      title "3 Football Clubs Kylian Mbappe Should Avoid Signing ✍️❌⚽️ #football #mbappe #shorts"
      description ""
      thumbnails {
        "default" {
          url "https://i.ytimg.com/vi/TvWDY4Mm5GM/default.jpg"
          width 120
          height 90
        }
        medium {
          url "https://i.ytimg.com/vi/TvWDY4Mm5GM/mqdefault.jpg"
          width 320
          height 180
        }
        high {
          url "https://i.ytimg.com/vi/TvWDY4Mm5GM/hqdefault.jpg"
          width 480
          height 360
        }
      }
      channelTitle "FC Motivate"
      liveBroadcastContent none
      publishTime "2023-07-24T14:15:01Z"
    }
  }
  {
    kind "youtube#searchResult"
    etag 0NG5QHdtIQM_V-DBJDEf-jK_Y9k
    id {
      kind "youtube#video"
      videoId aZM_42CcNZ4
    }
    snippet {
      publishedAt "2023-07-24T16:09:27Z"
      channelId UCM5gMM_HqfKHYIEJ3lstMUA
      title "Which Football Club Could Cristiano Ronaldo Afford To Buy? 💰"
      description "Sign up to Sorare and get a FREE card: https://sorare.pxf.io/NellisShorts Give Soraredata a go for FREE: ..."
      thumbnails {
        "default" {
          url "https://i.ytimg.com/vi/aZM_42CcNZ4/default.jpg"
          width 120
          height 90
        }
        medium {
          url "https://i.ytimg.com/vi/aZM_42CcNZ4/mqdefault.jpg"
          width 320
          height 180
        }
        high {
          url "https://i.ytimg.com/vi/aZM_42CcNZ4/hqdefault.jpg"
          width 480
          height 360
        }
      }
      channelTitle "John Nellis"
      liveBroadcastContent none
      publishTime "2023-07-24T16:09:27Z"
    }
  }
  {
    kind "youtube#searchResult"
    etag WbBz4oh9I5VaYj91LjeJvffrBVY
    id {
      kind "youtube#video"
      videoId wkP3XS3aNAY
    }
    snippet {
      publishedAt "2023-07-24T16:00:50Z"
      channelId UC4EP1dxFDPup_aFLt0ElsDw
      title "PAULO DYBALA vs THE WORLD'S LONGEST FREEKICK WALL"
      description "Can Paulo Dybala curl a football around the World's longest free kick wall? We met up with the World Cup winner and put him to ..."
      thumbnails {
        "default" {
          url "https://i.ytimg.com/vi/wkP3XS3aNAY/default.jpg"
          width 120
          height 90
        }
        medium {
          url "https://i.ytimg.com/vi/wkP3XS3aNAY/mqdefault.jpg"
          width 320
          height 180
        }
        high {
          url "https://i.ytimg.com/vi/wkP3XS3aNAY/hqdefault.jpg"
          width 480
          height 360
        }
      }
      channelTitle "Shoot for Love"
      liveBroadcastContent none
      publishTime "2023-07-24T16:00:50Z"
    }
  }
  {
    kind "youtube#searchResult"
    etag juxv_FhT_l4qrR05S1QTrb4CGh8
    id {
      kind "youtube#video"
      videoId rJkDZ0WvfT8
    }
    snippet {
      publishedAt "2023-07-24T10:00:39Z"
      channelId UCO8qj5u80Ga7N_tP3BZWWhQ
      title "TOP 10 DEFENDERS 2023"
      description "SoccerKingz https://soccerkingz.nl Use code: 'ILOVEHOF' to get 10% off. TOP 10 DEFENDERS 2023 Follow us! • Instagram ..."
      thumbnails {
        "default" {
          url "https://i.ytimg.com/vi/rJkDZ0WvfT8/default.jpg"
          width 120
          height 90
        }
        medium {
          url "https://i.ytimg.com/vi/rJkDZ0WvfT8/mqdefault.jpg"
          width 320
          height 180
        }
        high {
          url "https://i.ytimg.com/vi/rJkDZ0WvfT8/hqdefault.jpg"
          width 480
          height 360
        }
      }
      channelTitle "Home of Football"
      liveBroadcastContent none
      publishTime "2023-07-24T10:00:39Z"
    }
  }
  {
    kind "youtube#searchResult"
    etag "wtuknXTmI1txoULeH3aWaOuXOow"
    id {
      kind "youtube#video"
      videoId XH0rtu4U6SE
    }
    snippet {
      publishedAt "2023-07-21T16:30:05Z"
      channelId UCwozCpFp9g9x0wAzuFh0hwQ
      title "3 Things You Didn't Know About Erling Haaland ⚽️🇳🇴 #football #haaland #shorts"
      description ""
      thumbnails {
        "default" {
          url "https://i.ytimg.com/vi/XH0rtu4U6SE/default.jpg"
          width 120
          height 90
        }
        medium {
          url "https://i.ytimg.com/vi/XH0rtu4U6SE/mqdefault.jpg"
          width 320
          height 180
        }
        high {
          url "https://i.ytimg.com/vi/XH0rtu4U6SE/hqdefault.jpg"
          width 480
          height 360
        }
      }
      channelTitle "FC Motivate"
      liveBroadcastContent none
      publishTime "2023-07-21T16:30:05Z"
    }
  }
]

Результат второго шага: 105.3% от шага 1, 89.1% от шага 0.

Шаг 3. Использование borrow и интерполированных строк

4,551 символ
thumbnails := {
  url := "https://i.ytimg.com/vi/"
  "default" ::= {
    url ::= 'url
    width 120
    height 90
  }
  medium ::= {
    url ::= 'url
    width 320
    height 180
  }
  high ::= {
    url ::= 'url
    width 480
    height 360
  }
}
kind "youtube#searchListResponse"
etag q4ibjmYp1KA3RqMF4jFLl6PBwOg
nextPageToken: CAUQAA
regionCode NL
pageInfo {
  totalResults 1000000
  resultsPerPage 5
}
items [
  url := 'thumbnails.url
  {
    kind "youtube#searchResult"
    etag QCsHBifbaernVCbLv8Cu6rAeaDQ
    id {
      kind "youtube#video"
      videoId TvWDY4Mm5GM
    }
    snippet {
      publishedAt "2023-07-24T14:15:01Z"
      channelId UCwozCpFp9g9x0wAzuFh0hwQ
      title "3 Football Clubs Kylian Mbappe Should Avoid Signing ✍️❌⚽️ #football #mbappe #shorts"
      description ""
      thumbnails 'thumbnails ~ {
        "default" {
          url "\('url)TvWDY4Mm5GM/default.jpg"
        }
        medium {
          url "\('url)TvWDY4Mm5GM/mqdefault.jpg"
        }
        high {
          url "\('url)TvWDY4Mm5GM/hqdefault.jpg"
        }
      }
      channelTitle "FC Motivate"
      liveBroadcastContent none
      publishTime "2023-07-24T14:15:01Z"
    }
  }
  {
    kind "youtube#searchResult"
    etag 0NG5QHdtIQM_V-DBJDEf-jK_Y9k
    id {
      kind "youtube#video"
      videoId aZM_42CcNZ4
    }
    snippet {
      publishedAt "2023-07-24T16:09:27Z"
      channelId UCM5gMM_HqfKHYIEJ3lstMUA
      title "Which Football Club Could Cristiano Ronaldo Afford To Buy? 💰"
      description "Sign up to Sorare and get a FREE card: https://sorare.pxf.io/NellisShorts Give Soraredata a go for FREE: ..."
      thumbnails 'thumbnails ~ {
        "default" {
          url "\('url)aZM_42CcNZ4/default.jpg"
        }
        medium {
          url "\('url)aZM_42CcNZ4/mqdefault.jpg"
        }
        high {
          url "\('url)aZM_42CcNZ4/hqdefault.jpg"
        }
      }
      channelTitle "John Nellis"
      liveBroadcastContent none
      publishTime "2023-07-24T16:09:27Z"
    }
  }
  {
    kind "youtube#searchResult"
    etag WbBz4oh9I5VaYj91LjeJvffrBVY
    id {
      kind "youtube#video"
      videoId wkP3XS3aNAY
    }
    snippet {
      publishedAt "2023-07-24T16:00:50Z"
      channelId UC4EP1dxFDPup_aFLt0ElsDw
      title "PAULO DYBALA vs THE WORLD'S LONGEST FREEKICK WALL"
      description "Can Paulo Dybala curl a football around the World's longest free kick wall? We met up with the World Cup winner and put him to ..."
      thumbnails 'thumbnails ~ {
        "default" {
          url "\('url)wkP3XS3aNAY/default.jpg"
        }
        medium {
          url "\('url)wkP3XS3aNAY/mqdefault.jpg"
        }
        high {
          url "\('url)wkP3XS3aNAY/hqdefault.jpg"
        }
      }
      channelTitle "Shoot for Love"
      liveBroadcastContent none
      publishTime "2023-07-24T16:00:50Z"
    }
  }
  {
    kind "youtube#searchResult"
    etag juxv_FhT_l4qrR05S1QTrb4CGh8
    id {
      kind "youtube#video"
      videoId rJkDZ0WvfT8
    }
    snippet {
      publishedAt "2023-07-24T10:00:39Z"
      channelId UCO8qj5u80Ga7N_tP3BZWWhQ
      title "TOP 10 DEFENDERS 2023"
      description "SoccerKingz https://soccerkingz.nl Use code: 'ILOVEHOF' to get 10% off. TOP 10 DEFENDERS 2023 Follow us! • Instagram ..."
      thumbnails 'thumbnails ~ {
        "default" {
          url "\('url)rJkDZ0WvfT8/default.jpg"
        }
        medium {
          url "\('url)rJkDZ0WvfT8/mqdefault.jpg"
        }
        high {
          url "\('url)rJkDZ0WvfT8/hqdefault.jpg"
        }
      }
      channelTitle "Home of Football"
      liveBroadcastContent none
      publishTime "2023-07-24T10:00:39Z"
    }
  }
  {
    kind "youtube#searchResult"
    etag "wtuknXTmI1txoULeH3aWaOuXOow"
    id {
      kind "youtube#video"
      videoId XH0rtu4U6SE
    }
    snippet {
      publishedAt "2023-07-21T16:30:05Z"
      channelId UCwozCpFp9g9x0wAzuFh0hwQ
      title "3 Things You Didn't Know About Erling Haaland ⚽️🇳🇴 #football #haaland #shorts"
      description ""
      thumbnails 'thumbnails ~ {
        "default" {
          url "\('url)XH0rtu4U6SE/default.jpg"
        }
        medium {
          url "\('url)XH0rtu4U6SE/mqdefault.jpg"
        }
        high {
          url "\('url)XH0rtu4U6SE/hqdefault.jpg"
        }
      }
      channelTitle "FC Motivate"
      liveBroadcastContent none
      publishTime "2023-07-21T16:30:05Z"
    }
  }
]

Результат третьего шага: 82.2% от шага 2, 76.0% от шага 0.

Шаг 4. Дополнительные сокращения

4,440 символов
thumbnails := {
  url := "https://i.ytimg.com/vi/"
  "default" ::= {
    width 120
    height 90
  }
  medium ::= {
    width 320
    height 180
  }
  high ::= {
    width 480
    height 360
  }
}
kind "youtube#searchListResponse"
etag q4ibjmYp1KA3RqMF4jFLl6PBwOg
nextPageToken: CAUQAA
regionCode NL
pageInfo {
  totalResults 1000000
  resultsPerPage 5
}
items [
  url := 'thumbnails.url
  searchResult := {
    kind "youtube#searchResult"
    id ::= {
      kind "youtube#video"
    }
    snippet ::= {
      description ""
      liveBroadcastContent none
    }
  }
  'searchResult ~ {
    etag QCsHBifbaernVCbLv8Cu6rAeaDQ
    id 'searchResult.id + {
      videoId TvWDY4Mm5GM
    }
    snippet 'searchResult.snippet ~ {
      p := "2023-07-24T14:15:01Z"
      publishedAt 'p
      channelId UCwozCpFp9g9x0wAzuFh0hwQ
      title "3 Football Clubs Kylian Mbappe Should Avoid Signing ✍️❌⚽️ #football #mbappe #shorts"
      thumbnails 'thumbnails ~ {
        "default" {
          url "\('url)TvWDY4Mm5GM/default.jpg"
        }
        medium {
          url "\('url)TvWDY4Mm5GM/mqdefault.jpg"
        }
        high {
          url "\('url)TvWDY4Mm5GM/hqdefault.jpg"
        }
      }
      channelTitle "FC Motivate"
      publishTime 'p
    }
  }
  'searchResult ~ {
    etag 0NG5QHdtIQM_V-DBJDEf-jK_Y9k
    id 'searchResult.id + {
      videoId aZM_42CcNZ4
    }
    snippet 'searchResult.snippet ~ {
      p := "2023-07-24T16:09:27Z"
      publishedAt 'p
      channelId UCM5gMM_HqfKHYIEJ3lstMUA
      title "Which Football Club Could Cristiano Ronaldo Afford To Buy? 💰"
      description "Sign up to Sorare and get a FREE card: https://sorare.pxf.io/NellisShorts Give Soraredata a go for FREE: ..."
      thumbnails 'thumbnails ~ {
        "default" {
          url "\('url)aZM_42CcNZ4/default.jpg"
        }
        medium {
          url "\('url)aZM_42CcNZ4/mqdefault.jpg"
        }
        high {
          url "\('url)aZM_42CcNZ4/hqdefault.jpg"
        }
      }
      channelTitle "John Nellis"
      publishTime 'p
    }
  }
  'searchResult ~ {
    etag WbBz4oh9I5VaYj91LjeJvffrBVY
    id 'searchResult.id + {
      videoId wkP3XS3aNAY
    }
    snippet 'searchResult.snippet ~ {
      p := "2023-07-24T16:00:50Z"
      publishedAt 'p
      channelId UC4EP1dxFDPup_aFLt0ElsDw
      title "PAULO DYBALA vs THE WORLD'S LONGEST FREEKICK WALL"
      description "Can Paulo Dybala curl a football around the World's longest free kick wall? We met up with the World Cup winner and put him to ..."
      thumbnails 'thumbnails ~ {
        "default" {
          url "\('url)wkP3XS3aNAY/default.jpg"
        }
        medium {
          url "\('url)wkP3XS3aNAY/mqdefault.jpg"
        }
        high {
          url "\('url)wkP3XS3aNAY/hqdefault.jpg"
        }
      }
      channelTitle "Shoot for Love"
      publishTime 'p
    }
  }
  'searchResult ~ {
    etag juxv_FhT_l4qrR05S1QTrb4CGh8
    id 'searchResult.id + {
      videoId rJkDZ0WvfT8
    }
    snippet 'searchResult.snippet ~ {
      p := "2023-07-24T10:00:39Z"
      publishedAt 'p
      channelId UCO8qj5u80Ga7N_tP3BZWWhQ
      title "TOP 10 DEFENDERS 2023"
      description "SoccerKingz https://soccerkingz.nl Use code: 'ILOVEHOF' to get 10% off. TOP 10 DEFENDERS 2023 Follow us! • Instagram ..."
      thumbnails 'thumbnails ~ {
        "default" {
          url "\('url)rJkDZ0WvfT8/default.jpg"
        }
        medium {
          url "\('url)rJkDZ0WvfT8/mqdefault.jpg"
        }
        high {
          url "\('url)rJkDZ0WvfT8/hqdefault.jpg"
        }
      }
      channelTitle "Home of Football"
      publishTime 'p
    }
  }
  'searchResult ~ {
    etag wtuknXTmI1txoULeH3aWaOuXOow
    id 'searchResult.id + {
      videoId XH0rtu4U6SE
    }
    snippet 'searchResult.snippet ~ {
      p := "2023-07-21T16:30:05Z"
      publishedAt 'p
      channelId UCwozCpFp9g9x0wAzuFh0hwQ
      title "3 Things You Didn't Know About Erling Haaland ⚽️🇳🇴 #football #haaland #shorts"
      thumbnails 'thumbnails ~ {
        "default" {
          url "\('url)XH0rtu4U6SE/default.jpg"
        }
        medium {
          url "\('url)XH0rtu4U6SE/mqdefault.jpg"
        }
        high {
          url "\('url)XH0rtu4U6SE/hqdefault.jpg"
        }
      }
      channelTitle "FC Motivate"
      publishTime 'p
    }
  }
]

Результат третьего шага: 97.6% от шага 2, 74.2% от шага 0.

Сжатие минифицированного JSON

Сравним компактные версии JSON и Av:

{"name":"Chris","age":23,"address":{"city":"New York","country":"America"},"friends":[{"name":"Emily","hobbies":["biking","music","gaming"]},{"name":"John","hobbies":["soccer","gaming"]}]}
{name Chris age 23 address{city"New York"country America}friends[{name Emily hobbies[biking music gaming]}{name John hobbies[soccer gaming]}]}

JSON — 188 символов (100.0%), Av — 142 символа (75.5%).

Итог

Вот такой странный язык конфигурации я написал для своего движка. Мог бы я обойтись без него? Да, конечно, ведь есть стабильные альтернативы вроде Cue, Jsonnet, Dhall. В крайнем случае можно было бы продолжать использовать JSON. Зачем я это сделал? Ну, я люблю придумывать языки с нескучным синтаксисом, а тут был слишком удобный предлог, чтобы я мог его проигнорировать. Нужен ли Av вам? Я думаю, нет. Это локальный язычок, который удовлетворяет всем требованиям своего создателя и только него. Тулинга для него нет, статей и туториалов по нему нет (ну, кроме этой). Официальной спецификации у него нет, семантика довольно странная. Я бы даже сказал, что он больше похож на новый эзотерический язык программирования, чем на реальную замену JSON. Ну и, конечно же, самый главный отпугивающий фактор - я очень сильно перемудрил с языком. Если честно, изначально в Av были только три пункта из списка опциональных нововведений и новые типы (int, float, bool, bytes). Всё остальное я добавлял в процессе разработки.

Однако если вам Av всё же показался интересным, в следующей части могу разобрать, как устроены кастомные заимствования, лямбды и сопоставление с образцом в Av. Они позволяют, пусть и ограниченно, программировать на Av.

Теги:
Хабы:
Всего голосов 19: ↑16 и ↓3+23
Комментарии26

Публикации

Истории

Ближайшие события

11 – 13 февраля
Epic Telegram Conference
Онлайн
27 марта
Deckhouse Conf 2025
Москва
25 – 26 апреля
IT-конференция Merge Tatarstan 2025
Казань