Logipard
1.0.0
EN
RU
Этот документ оформлен как одиночная статическая страница, но он также оснащён некоторыми инструментами для чтения, которые могут показаться вам непривычными. Поэтому, прежде чем мы начнём, несколько слов о том, что вы видите на странице.
Темы часто вложены на несколько уровней. Если вы сделаете прокрутку, то можете заметить, что заголовки "залипают" каскадом - это позволяет следить за структурой контекста. На заголовке можно кликнуть - это прокрутит документ в начало соответствующей темы. Также, стрелки с левой стороны заголовка позволяют быстро перелистывать темы на одном уровне вложенности.
Фрагменты текста, выделенные вот таким образом: ✖ "Вернуть" и другие действия - встроенные ссылки Logipard на некие другие темы в пределах данной страницы. В отличие от обычных HTML-ссылок, они не перебрасывают вас на новое место сразу при нажатии. Вместо этого, ссылаемый элемент разворачивается внутри строки, позволяя предварительно просмотреть целевую тему без потери визуального контекста (попробуйте кликнуть по ссылке и посмотрите, как это выглядит).
Возможно, в данном конкретном примере, когда разворачиваемый элемент и так находится на виду, это не очень впечатляет, но становится значительно удобнее, когда целевая тема находится далеко.
Элемент разворачивается в кратком режиме, показывая только основную информацию. Можно кликнуть "Ещё", чтобы увидеть больше. Если вы считаете, что увидели достаточно, и хотите вернуть развёрнутый элемент на место и восстановить исходный текст, используйте действие Вернуть.
Обратите внимание, что элемент всегда присутствует на странице в одном экземпляре. Поэтому, когда он разворачивается где-либо, то убирается из места, где был до этого. Дойдя до его "штатного" места, вы можете увидеть подсказку, предлагающую вернуть его обратно.
  • Как альтернатива к Вернуть, возвращающему элемент на его исходное место и оставляющему обзор на том месте, где вы нажимали на ссылку, возможно действие Вернуть и перейти, которое возвращает элемент на место и перемещает обзор к нему - своего рода завершение процесса, который получился бы у вас при нажатии на стандартную HTML-ссылку.
  • Если у вас есть элементы, развёрнутые на нештатных для них местах, можно заметить, что на заголовках тем, к которым эти элементы относились, появилось действие Сбросить - это, по существе, быстрое "убрать" для всех элементов, перемещённых изнутри соответствующей темы.
  • Наконец, можно Поднять развёрнутый элемент, то есть перенести на его место одну из родительских для него тем, чтобы увидеть более крупный участок контекста (может быть недоступно для некоторых элементов, или не всегда практично, из-за структуры документа).
  • Действие #LP? позволяет получить имя элемента в модели документации. (Смысл этих слов станет понятен позже, когда вы ознакомитесь с концепцией и терминологией Logipard.)
В этом разделе мы объясним, что такое Logipard, и с какой стороны за него браться.

Мотивация

Существующие генераторы документации на основе аннотаций часто имеют проблемы с решением основных задач:
  1. Ограниченный охват документации Большинство инструментов генерируют документацию, ориентированную исключительно на объекты кода (например, классы, модули, функции). Однако в них нет удовлетворительной поддержки для документации более высокого уровня, такой как:
    • Отдельные руководства по быстрому началу работы и типовые примеры использования.
    • Документация в терминах объектов и рабочих процессов, специфичных для предметной области, которые могут не иметь простого 1:1 соответствия с объектами кода.
    • Встроенные заметки разработчика, ссылки на задачи, произвольные метаданные, и прочие виды нестандартной информации.
  2. Ограничения, связанные с языками Эти инструменты обычно привязаны к конкретным языкам программирования, в связи с чем возникают трудности с документированием:
    • Объектов более высокого уровня или специфичных для предметной области (например, скриптов, ресурсов, вспомогательных инструментов).
    • Отношения объектов между различными предметными областями (например, между основным языком и скриптовой системы). Кроме того, обычно существуют ограничения на места размещения аннотаций, что ограничивает гибкость в организации и структурировании исходников документации.
  3. Жёсткий формат выходных данных
    Генерируемая документация обычно является законченным документом, ориентированным на чтение конечным пользователем, что затрудняет следующие задачи:
    • Связь или перекрёстные ссылки между разнородными артефактами документации.
    • Эффективное использование машиночитаемых форматов (например, XML, JSON), даже если инструмент способен их генерировать, т. к. они часто содержат ту же самую фиксированную жёсткую структуру, что и человекочитаемый вывод.

Как эти проблемы решаются в Logipard

Logipard предлагает новый подход к созданию документации, основанный на двух ключевых концепциях:
  1. Модель документации свободной формы (Freeform Documentation Object Model, или FDOM) Logipard собирает все фрагменты документации в единую, в пределах проекта, базу данных, называемую Модель документации свободной формы (FDOM). FDOM спроектирована, имея в виду следующие свойства:
    • Гибкость: Не накладывается никаких органичений на структуру документации или семантику элементов документа, позволяя включать в качестве таковых объекты программы, главы руководства пользователя, заметки разработчика, что-либо ещё, или всё вышеперечисленное в любой комбинации, и организовывать их в любую структуру, которую вы видите наиболее подходящей.
    • Независимость от языка: Поддержка документации из разнообразных источников, не привязанных к конкретной предметной области или языку программирования.
    • Возможность перекрёстных ссылок: FDOM предоставляет унифицированное пространство имён, позволяя легко делать перекрёстные ссылки между различными частями документации (например, между полем в SQL для создания таблицы и объектами, соответствующими ему в коде бизнес-логики).
    1. Артефакт, ориентированный на инструменты FDOM - машиночитаемый артефакт, ориентированный на инструменты, а не на конечного пользователя. Он служит первичным источником информации по документации, которую можно запрашивать и фрагментировать различными способами. Конкретная документация для конечного пользователя (например, справка по API, руководства пользователя) создаётся генераторами - вспомогательными инструментами, которые читают из FDOM соответствующие срезы информации, организуют их соответственно структуре целевого документа и выдают окончательный результат.

Основные преимущества Logipard

  • Унифицированное хранение данных документации: FDOM собирает все фрагменты документации в единую гибкую модель.
  • Независимость от языка и предметной области: Документируйте элементы кода, скрипты, рабочие процессы, и прочие разнородные объекты, в одном общем пространстве.
  • Улучшенная перекрёстная ссылаемость: Проще указывать связи между объектами из разных предметных областей или разных видов документации.
  • Настраиваемый вывод: Генерация артефактов документации, заточенных под конкретные различные цели (например, заметки разработчика, документация конечного пользователя).
Теперь давайте ознакомимся с процессом...
Установите Node.js 13 или выше.
Далее, установите Logipard CLI или глобально:
# с gitverse
npm install -g git+https://gitverse.ru/mikle33/logipard

# или: с github
npm install -g git+https://github.com/sbtrn-devil/logipard

# или: с npm
# TODO
или локально в вашу текущую папку:
[UNTRANSLATED-ru]
# с gitverse
npm install git+https://gitverse.ru/mikle33/logipard

# или: с github
npm install git+https://github.com/sbtrn-devil/logipard

# или: с npm
# TODO
После глобальной инсталляции, Logipard CLI можно запустить из любой текущей папки:
lp-cli [cmd line options]
После локальной инсталляции, Logipard CLI можно запустить из текущей папки:
Linux:
node_modules/.bin/lp-cli [cmd line options]
Windows:
node_modules\.bin\lp-cli [cmd line options]
Запустите lp-cli без опций или с опцией -h или --help для справки по опциям командной строки (их достаточно немного, спецификация основной части работы предполагается через конфигурационный файл (или файлы)).
После инсталляции можно пройти ✖ Пример для быстрого старта для получения общего представления, как оно всё делается.
Например, вы собираетесь написать инновационную библиотеку, которая совершит переворот в мире программирования.
Вы создали каталог, в котором будет находиться проект (обозначим её <PROJECT_ROOT>), и, возможно, даже создали в ней package.json (будем считать, что это библиотека для Node.js).
Также вы установили Logipard CLI, как написано в ✖ Установка ; будем считать, что команда CLI - lp-cli [options] (если вы установили локально, то внесите соответствующую поправку).
Создайте основную кодовую базу библиотеки:
Содзать файл: <PROJECT_ROOT>/index.js
module.exports.leftpad = function leftpad(str, len, padding = ' ') {
	str = str + '';
	if (str.length >= len) return str;
	padding = (padding = padding + '').length ? padding : ' ';
	while (padding.length + str.length < len) {
		padding = padding + padding;
	}
	return padding.substring(0, len - str.length) + str;
}
Теперь можно начать документировать наше добро.
Основной источник докумнетации - аннотации кода в исходниках. В нашем случае, мы можем добавить несколько их вот таким образом:
Редактировать файл: <PROJECT_ROOT>/index.js
//#LP functions/leftpad { <#./%title: leftpad(str, len[, padding])#>
// Pad a string from left to the given minimum length with whitespaces or a given padding string.
//#LP ./str %arg: String to be padded
//#LP ./len %arg: Number, minimum required length of the padded string (<#ref leftpad/str#>)
//#LP ./padding %arg: String, optional. If specified, this string or its portion will be used for the padding instead of spaces.
//
// The string is repeated as required if it is less than the length required for the padding, and its leftmost part of the required length is used.
module.exports.leftpad = function leftpad(str, len, padding = ' ') {
	... // almost everything within can be left as is so far

	//#LP ./%return: The padded string with minimum length of (<#ref leftpad/len#>)
	return padding.substring(0, len - str.length) + str;
}

//#LP }
Аннотации в основном выглядят читабельно и, возможно, почти самоописательно (не считая некоторых характерных деталей синтаксиса, на которых мы не будем останавливаться прямо сейчас), но есть кое-что не очевидное на глаз, о чём следует упомянуть.
Во-первых, техническое замечание: Logipard распознаёт в качестве аннотаций последовательности идущих подряд однострочных комментариев (здесь - //), возможно с некоторым кодом перед ними в начале строки, причём первый комментарий начинается с токена #LP, и до ближайшей строки, которая не заканчивается однострочным комментарием. В нашем случае, // после ./padding нужна для того, чтобы комментарий // The string is ... был включён в последовательность. С другой стороны, линия с комментарием // almost everything ... отделена от последовательности линией без комментария, так что это "просто комментарий в коде", который не принимается во внимание.
Следите за этим, чтобы обеспечить включение в аннотации тех комментариев, которые нужны, и пропуск тех, которые не нужны.
Во-вторых, Logipard не имеет совершенно никакого представления об исходном коде и каких-либо сущностях, характерных для данного языка. Всё, что имеет значение - структура модели документации, а определение этой структуры и аккуратное следование ей полностью зависит от вас.
Например, в нашей документации мы задали наличие следующих элементов со следующими именами:
  • functions: элемент автоматически введён в модель в силу того, что мы задали его члены-подэлементы, будем считать его контейнером для списка функций
  • functions/leftpad: основной элемент документации для нашей функции, содержит всё, что к ней относится (т. е. следующие элементы)
  • functions/leftpad/%title (идёт от ./%title): элемент, содержащий человекочитаемый заголовок для главного элемента
  • functions/leftpad/str, functions/leftpad/len, functions/leftpad/padding: элементы, документирующие аргументы функции; заметьте, что каждый из них помечен тегом модели %arg
  • functions/leftpad/%return: элемент, документирующий возвращаемое значение нашей функции
  • %arg: тег модели с вот таким именем, введён в модель в силу того, что мы его использовали
Обратите внимание, что все семантические значения элементов (контейнер списка функций, главный элемент для функции, элементы для читаемого заголовка функции и возвращаемого значения, тот факт, что мы помечаем аргументы именно тегом %arg, тот факт, что документируемая сущность - именно функция) сугубо конвенциональны, как и их имена. С точки зрения модели документации Logipard, они все - просто элементы общего вида, и на данном этапе их интерпретация в тех качествах, которые описаны выше, пока что существует исключительно умозрительно.
Вы можете принимать другие конвенции для ваших собственных задач, но в рамках нашего быстрого старта будем придерживаться вот такой.
Ещё один момент - места расположения аннотаций на самом деле не имеют значения, размещайте их так, как вам удобнее. В данном случае мы разместили их рядом с декларацией функции, аналогично тому, как делается в традиционных генераторах документации типа Javadoc, TSDoc, Doxygen, и т. п. (Но даже и здесь, мы решили разместить фрагмент %return рядом с оператором возврата, а не в одном блоке с описаниями параметров - опять-таки, исключительно для примера, можно было разместить его и более традиционно.) Но в общем случае их можно разместить в любом месте файла с исходным кодом, или в другом файле, или даже разместить различные подэлементы в совершенно разных местах. Это, возможно, не очень полезно для вещей вроде аргументов функции и её возвращаемого значения, но информация, имеющая отношение к функции и желательная к размещению в её информационном узле, может не ограничиваться только этими данными.
Хотя в модели документа нет встроенных ограничений на выбор структуры и имён для элементов, есть несколько моментов, которые желательно иметь в виду для поддержания хорошего стиля и сопровождаемости.
  • краткие имена для элементов, которые предполагаются в качестве вспомогательных полей данных или для использования в качестве тегов, следует начинать с %. По удачному стечению обстоятельств, мы уже так и сделали.
  • элементы документации, специфичные для вашего проекта/модуля/библиотеки, следует размещать под одним общим корневым элементом с достаточно уникальным именем, характерным для данного проекта (домен проекта). Это поможет избежать конфликта имён, когда дело коснётся интеграции нескольких проектов, и облегчит объединение их документации.
В нашем случае, мы выбрали для наших элементов не самые лучшие имена. Лучше было бы что-нибудь наподобие:
  • domain.our-unique.leftpad.for-nodejs/functions
  • domain.our-unique.leftpad.for-nodejs/functions/leftpad
  • domain.our-unique.leftpad.for-nodejs/functions/leftpad/%title
  • domain.our-unique.leftpad.for-nodejs/functions/leftpad/str
  • domain.our-unique.leftpad.for-nodejs/functions/leftpad/len
  • domain.our-unique.leftpad.for-nodejs/functions/leftpad/padding
  • domain.our-unique.leftpad.for-nodejs/functions/leftpad/%return
  • %arg (это, по определённым причинам, можно оставить как есть)
Допечатывать доменный префикс к именам элементов всякий раз, когда потребуется, разумеется, непрактично (даже при том, что требуется это не слишком часто). Для этого лучше определить алиас. Сделаем это:
Редактировать файл: <PROJECT_ROOT>/index.js
//#LP-alias M: domain.our-unique.leftpad.for-nodejs

// теперь вместо domain.our-unique.leftpad.for-nodejs/functions/... можно использовать M/functions

//#LP M/functions/leftpad { <#./%title: leftpad(str, len[, padding])#>
...
// ...остальная часть файла может быть оставлена без изменений, в ней используются относительные имена
Более того, этот //#LP-alias M: domain.our-unique.leftpad.for-nodejs, скорее всего, будет общей частью для всех файлов, содержащих документацию по нашему проекту, и к этой общей части, возможно, со временем добавятся ещё некоторые. В целях дальнейшего уменьшения дубликации, можно вынести данный фрагмент в отдельный файл и просто подключать его везде, где нужно...
Создать файл: <PROJECT_ROOT>/leftpad-module-inc.lp-txt
#LP-alias M: domain.our-unique.leftpad.for-nodejs
(Обратите внимание, что у этого файла немного другая структура. Поскольку Logipard не имеет представления об языке программирования, можно использовать даже отдельные текстовые файлы, полностью состоящие из аннотаций Logipard.)
Редактировать файл: <PROJECT_ROOT>/index.js
//#LP-include leftpad-module-inc.lp-txt

//#LP M/functions/leftpad { <#./%title: leftpad(str, len[, padding])#>
...
// ...остальная часть файла всё ещё остаётся без изменений
В этом быстром старте мы делаем HTML-документацию, и первое, что нам для этого потребуется - подготовить файл шаблона HTML.
Создать файл: <PROJECT_ROOT>/leftpad-doc-html.tpl.html
<html>
<head>
<style>
body {
	font-family: sans-serif;
}
code {
	font-family: monospace;
	background: lightgray;
	margin: 0;
	padding: 0;
}
pre {
	font-family: monospace;
	background: lightgray;
	margin: 0;
	padding: 0;
}
table, th, td { border: 1px solid; border-spacing: 0; border-collapse: collapse; }
table { margin: 0.5em }
CSS_TARGET
</style>
</head>
<body>
<div style="display: flex; flex-direction: column; height: 100%; overflow: hidden">

<div style="border-bottom: double 3px">
<center style="font-size: 200%">leftpad for node.js</center>
<center style="font-size: 75%">1.0.0</center>
</div>

<div style="padding: 0; margin: 0; overflow: clip; height: 0; flex-grow: 1">
HTML_TARGET
</div>

</div>
</body>
</html>
Данный шаблон - исключительно для примера, он включает самый необходимый минимум того, что возможно. Обратите внимание на метки CSS_TARGET и HTML_TARGET.
Теперь, чтобы перейти к чему-то более существенному, нам нужно подготовить конфигурационный файл Logipard. Мы не будем сейчас объяснять всю магию, происходящую здесь, просто имейте в виду, что именно здесь вы реализуете свои конвенции по модели документа и указываете другие технические вещи.
Создать файл: <PROJECT_ROOT>/lp-config.json
//#charset utf-8
// (Этот комментарий выше будет принят во внимание; предполагается, что вы сохраняете данный файл в UTF-8)
// Заметим, что это не очень похоже на корректный JSON (взять хотя бы комментарии), но пока не обращайте на это внимание,
// просто скопируйте и перенесите в файл всё как есть.
{
	"+ config": {
	},

	"lp-extract": {
		"+ config": {
			// обратите внимание, что неабсолютные пути - относительно корня проекта (то есть, места, в котором вы сохраняете данный файл)
			"+ excludeInFiles": ["node_modules/**"]
		},
		"items": [
			{
				// секция для основной кодовой базы, в нашем случае это все JS-файлы
				"inFiles": ["**/*.js"], 
				"excludeInFiles": [],
				"outDir": "lp-extract.gen",
				"reader": "${LP_HOME}/lpxread-basic" $, // символ $ в конце - не опечатка!
				"lpxread-basic": {
					"srcType": "generic-c-like"
				}
			},
			{
				// помните leftpad-module-inc.lp-txt? он попадает под эту секцию
				"inFiles": ["**/*-inc.lp-txt"],
				"excludeInFiles": [],
				"forLPInclude": true,
				"outDir": "lp-extract.gen/lp-includes",
				"reader": "${LP_HOME}/lpxread-basic" $,
				"lpxread-basic": {
					"srcType": "lp-text"
				}
			}
		]
	},

	"lp-compile": {
		"+ config": {
		},
		"items": [
			{
				"inRootDir": "lp-extract.gen",
				"lpIncLookupDirName": "lp-includes",
				"writer": "${LP_HOME}/lpcwrite-basic-json" $,
				"lpcwrite-basic-json": {
					// можете настроить это под имя вашего проекта
					"outFile": "lp-compile.gen/leftpad-doc-fdom.json"
				}
			}
		]
	},

	"lp-generate": {
		"+ config": {
		},
		"items": [
			{
				"inFile": "lp-compile.gen/leftpad-doc-fdom.json", // здесь то же, что в outFile в секции lp-compile
				"writer": "${LP_HOME}/lpgwrite-example" $,
				"lpgwrite-example": {
					// тут очень много магии, просто скопируйте весь этот фрагмент
					"program": file("${LP_HOME}/lpgwrite-example-docprg.lpson" $, {
						"docprgPrologue": [ { "nameAlias": "M", "name": "domain.our-unique.leftpad.for-nodejs" } ],
						"docRootItems": {
							"query": [{ "with": "M/functions" }],
							"sort": { "byMember": "%order", "keyFormat": "ds-natural", "order": "asc" }
						},
						"LS_EXTENDS": "Extends (is a)",
						"LS_MEMBERS": "Members",
						"LS_NAME": "Name",
						"LS_DESCRIPTION": "Description",
						"LS_MEMBERS_FROM_EXTENTS": "Members from extents",
						"LS_ARGUMENTS": "Arguments",
						"LS_RETURNS": "Returns:",
						"LS_ERRORS": "Errors:",
						"LS_MEMBERS_DETAILED": "Members (detailed)",
						"LS_MEMBERS_FROM_EXTENTS_DETAILED": "Members from extents (detailed)",
						"LS_ARGUMENTS_DETAILED": "Arguments (detailed)",
						"LS_NOTES": "Notes",
						"LS_PROPERTIES": "Properties",
						"LS_PROPERTIES_FROM_EXTENTS": "Properties from extents",
						"LS_METHODS": "Methods",
						"LS_METHODS_FROM_EXTENTS": "Methods from extents"
					}),
					"renders": [
						{
							"docModel": "DocMain",
							"renderer": "${LP_HOME}/lpgwrite-example-render-html" $,
							"lpgwrite-example-render-html": {
								"outFile": "lp-generate.gen/leftpad-doc.html",
								"emitToc": true,
								"inTemplateFile": "leftpad-doc-html.tpl.html",
								"htmlPlaceholder": "HTML_TARGET",
								"cssPlaceholder": "CSS_TARGET",
								"localizedKeywords": {
									"SNAPBACK": "Snapback",
									"SNAPBACK_AND_SCROLL": "Snapback & Scroll",
									"ELEVATE": "Elevate",
									"RESET": "Reset",
									"ELEVATE_TO": "Elevate to...",
									"COPY_ITEM_NAME": "Copy this item's LP FDOM full name to clipboard:",
									"ITEM_UNFOLDED_ELSEWHERE": "Item unfolded elsewhere on page, click/tap to unfold here...",
									"MORE": "More... >>",
									"TABLE_OF_CONTENTS": "Table of contents"
								}
							}
						}
					]
				}
			}
		]
	}
}
Теперь мы готовы сгенерировать страницу документации для проекта нашего быстрого старта.
Предполагая, что текущий рабочий каталог - <PROJECT_ROOT>, и вы работаете под пользователем, имеющим право на запись в него, вызовите CLI:
lp-cli lp-config.json
Вы должны увидеть некоторый вывод, который, в случае успеха, выглядит примерно так:
=== Performing stage: EXTRACT ===
EXTRACT: 15.039ms
=== Performing stage: COMPILE ===
COMPILE: 18.592ms
=== Performing stage: GENERATE ===
Start render
lpgwrite-example-render-html: file lp-generate.gen/leftpad-doc.html created
GENERATE: 69.098ms
Должны появиться некоторые новые каталоги, в том числе lp-generate.gen, в котором должен находиться файл leftpad-doc.html. Это и есть наша страница с документацией, можно открыть её в браузере. Неплохо для начала? Заметим ещё кое-что: это полностью самодостаточный HTML-файл, можно перемещать его как угодно, не боясь потерять зависимости, и ещё он статический и удобный для индексации поисковиками при размещении в интернете.
Также загляните в каталог lp-compile.gen и файл leftpad-doc-fdom.json в нём. Это база данных модели вашей документации (в данном случае, в JSON-формате).
Хотя это выглядит не так интересно, и не очень человекочитаемо - это, вообще говоря, промежуточный артефакт, на который в большинстве случаев можно не обращать внимания - на самом деле, это ключевой элемент в парадигме Logipard. Предполагается, что эта БД - единое место для хранения всех фрагментов LP-документации из проекта. В итоге может быть много конечных документов, составленных из разных срезов этой БД, но для всех них она будет общим источником данных.
Продолжим наш быстрый старт и посмотрим, как это может работать в нашем случае.
Теперь, когда код нашей библиотеки завершён, хорошо бы добавить что-нибудь вроде README-файла. И, в идеале, сделать как отдельный README.md, так и добавить такую же информацию новой частью к основной HTML-странице.
Начнём с изготовления файла-исходника...
Создать файл: <PROJECT_ROOT>/readme.lp-txt
#LP-include leftpad-module-inc.lp-txt
#-LP обратите внимание на разный синтаксис, когда мы используем чисто-текстово-образные файлы
# кстати, фрагменты, начинающиеся со строки #-LP, считаются не аннотационными комментариями, и Logipard
# не включает их в БД и документмацию, они длятся до следующего разметочного тега #LP.

# То есть, вот этот параграф - всё ещё комментарий LP (решётки в начале на самом деле не нужны, но лучше соблюдать
# единообразие визуального стиля). Также обратите внимание, что согласованные отступы (т. е. одинаковое количество одного типа пробелов
# в начале каждой промежуточной линии в пределах #LP-фрагмента) учитываются и корректно обрабатываются.
#LP M/readme { <#./%title: leftpad: quickstart#>

String left pad, revised and improved.
#LP ./install { <#./%title: Install#> <#./%order: 1#>
```
$ npm install git+https://<whereismygit.com>/leftpad
# TODO: actual location
```
#LP }
#LP ./usage { <#./%title: Usage#> <#./%order: 2#>
```
const { leftpad } = require('./leftpad');

leftpad('foo', 5);
// '  foo'

leftpad('foo', 2);
// 'foo'

leftpad('foo', 10, '+-=');
// '+-=+-=+foo'
```
#LP }
#-LP Кстати, не помешает добавить ссылку на инструкцию по использованию в раздел документируемой функции...
#LP M/functions/leftpad/usage { <#./%title: Usage#>
See some usage examples under <#ref readme/usage#>.
#LP }
#-LP И ещё добавим в секцию списка функций немного официального содержимого...
#LP M/functions: <#./%title: Functions reference#>
Reference on the library functions.
#-LP Тем не менее, имейте в виду один подводный камень при использовании многострочного #LP...: синтаксиса: рамки такого фрагмента ограничены следующим не-<#...#> #LP тегом или #-LP комментарием
# Поэтому строки после данного комментария снова находятся в M/readme (а сам комментарий, соответственно, заканчивается только на теге #LP)
#LP ./versions { <#./%title: Versions summary#> <#./%order: 3#>
#LP ./1.0.0: Initial release version.
#LP ./0.0.9: Pre-release version.

Was not documented with LP, so it pretty sucked.
#LP }
#LP }
Теперь добавим вот что...
Редактировать файл: <PROJECT_ROOT>/lp-config.json
...
	"lp-extract": {
	... // под "items"...
		"items": [
			// добавьте третий элемент (для учёта нового readme.lp-txt):
			...,
			{
				"inFiles": ["**/*.lp-txt"],
				"excludeInFiles": [],
				"outDir": "lp-extract.gen",
				"reader": "${LP_HOME}/lpxread-basic" $,
				"lpxread-basic": {
					"srcType": "lp-text"
				}
			}
		]
	},
...
	"lp-generate": {
	... // первый (и пока единственный) элемент под "items"...
		"items": [
			{
				"inFile": "lp-compile.gen/leftpad-doc-fdom.json",
				"writer": "${LP_HOME}/lpgwrite-example" $,
				"lpgwrite-example": {
... // в основном оставьте, как есть, кроме....
						"docRootItems": {
							"query": [{ "with": ["M/readme", "M/functions"] }], // <-- замените содержимое члена "query" на вот такое
							],
... // всё остальное остаётся как есть
						},
... // и здесь тоже
			},
			// далее, добавьте вот такой второй элемент в "items":
			{
				"inFile": "lp-compile.gen/leftpad-doc-fdom.json", // обратите внимание - всё ещё то же самое, что outFile в секции lp-compile
				"writer": "${LP_HOME}/lpgwrite-example" $,
				"lpgwrite-example": {
					"program": file("${LP_HOME}/lpgwrite-example-docprg.lpson" $, {
						"docprgPrologue": [ { "nameAlias": "M", "name": "domain.our-unique.leftpad.for-nodejs" } ],
						"docRootItems": {
							"query": [{ "with": ["M/readme"] }],
							],
							"sort": { "byMember": "%order", "keyFormat": "natural", "order": "asc" }
						}
					}),
					"renders": [
						{
							"docModel": "DocMain",
							"renderer": "${LP_HOME}/lpgwrite-example-render-md" $,
							"lpgwrite-example-render-md": {
								"outFile": "lp-generate.gen/leftpad-README.md",
								"emitToc": true,
								"addSourceRef": false,
								"header": "# leftpad #\n\n---",
							}
						}
					]
				}
			}
		// всё остальное остаётся как есть
		}
...

Снова запустите конвейер LP:
lp-cli lp-config.json
Теперь снова посмотрите в lp-generate.gen: должен появиться новый файл leftpad-README.md (посмотрите его каким-нибудь подручным MD-просмотрщиком), а файл leftpad-doc.html обновился - теперь в нём есть та же информация, что и в readme, при этом осталась справка по функциям, и заметны прочие улучшения, о которых можно было догадаться при редактировании исходника readme.
Вот таким образом выполняются базовые документационные задачи Logipard; этого, возможно, более чем достаточно для повседневных нужд. Но в его рамках и с помощью модели документации можно сделать намного больше, причём это могут быть даже задачи, выходящие за пределы обычного документирования. Для более подробного ознакомления с возможностями и более конкретного понимания, что мы сделали в быстром старте, см. основную часть документации.
Также, в качестве примера более сложного проекта, документированного с помощью Logipard, можете ознакомиться с кодом самого Logipard. Все исходники документации намеренно оставлены в его пакете.
Теперь можно описать идеи и понятия, лежащие в основе Logipard, более подробно.
Прежде всего, следует ознакомиться с ✖ Модель документации свободной формы (FDOM) , т. к. именно в терминах этой модели документации вы будете работать, и эта терминология фигурирует в каждом аспекте конвейера Logipard, начиная с аннотаций в исходниках документации до интерфейса средств настройки Logipard.
К слову о конвейере - процесс генерации документации в Logipard организован следующим образом:
image
Как здесь можно увидеть, конвейер преполагает настраиваемость пользователем во всех ключевых точках. Хотя Logipard идёт в комплекте со встроенными средствами, достаточными для базового использования, и вам не нужно с самого начала погружаться в подробности этой настройки, имейте в виду, что возможности значительно шире.
Ознакомьтесь более подробно с каждой стадией конвейера: ✖ Стадии конвейера Logipard
Все подробности конвейера в пределах проекта задаются через конфигурационный файл Logipard - ознакомьтесь с ним более конкретно: ✖ Файл конфигурации Logipard
После того, как вы узнаете достаточно обо всех этих вещах (и, по мере необходимости, о связанных с ними), здесь можно найти справку по всем предметам и интерфейсам, с которыми вы можете иметь дело как пользователь: ✖ Справка
Центральная идея Logipard состоит в следующем: извлечённые фрагменты документации сохраняются в единой промежуточной машиночитаемой базе данных, которая впоследствии доступна генераторам документации, равно как и для других применений, по некой единообразной стратегии обращения к данным.
Logipard не принуждает использовать какую-либо конкретную реализацию или стратегию хранения (хотя некоторые встроенные реализации предоставляются для быстрого старта и как примеры) - как и многое другое, это предмет пользовательской настройки. Мы определяем только структуру данных на самом общем уровне, логику её заполнения и методы доступа, которые должны поддерживаться реализацией базы данных, чтобы вписываться в задуманную схему конвейера Logipard. Эти определения составляют модель документации свободной формы (Freeform Documentation Object Model, или FDOM).
Объяснение структуры данных FDOM лучше начать с иллюстрации...
image
Модель данных состоит из узлов. Каждый узел может рассматриваться как контейнер документации для отдельной сущности в вашей предметной области. Что именно понимать под "сущностью" - исключительно вопрос конвенции, которую вы примете в вашем проекте. Это может быть, например:
  • класс в коде программы,
  • член класса,
  • метод класса,
  • глава, или иной именованный раздел текста,
  • элемент глоссария,
  • ...и т. п.
В общем, примерно что угодно, имеющее смысл как отдельный озаглавленный фрагмент текста. Ещё один вариант - использовать узлы для задания дополнительных (мета)данных (дополнительных информационных полей, маркеров, и т. д.) к других узлам. Любая специфическая для предметной области семантика может быть реализована поверх отношений между узлами, предусмотренных FDOM (см.: ✖ Отношение "родитель-член" и имена , ✖ Отношение "помеченный-тег" ).
В приведённом выше примере, у нас в общей сложности 11 узлов.
У каждого узла есть ассоциированный с ним контент (или содержимое) - собственно текст, или какие-либо иные данные или фрагменты документации. С точки зрения FDOM, контент непрозрачен (хотя его исходный материал, естественно, отталкивается от текстовых аннотаций), и намеренно не задействуется в базовой терминологии запросов FDOM (см. ✖ Запросы FDOM ). Терминология контента (является ли он простым текстом, или Markdown-текстом, или ссылкой на ресурс, или метаданными, требующими специальной интерпретации, и т. д.), а также возможное расширение способов запроса с привлечением контента, является прерогативой пользователей FDOM (читателей или писателей).
Узлы могут иметь отношение "родитель-член". Каждый узел может иметь произвольное число (в том числе ни одного) узлов-членов, и также сам является членом в точности одного родительского узла (кроме корневого узла). Циклы не разрешены, и в модели существует в точности один корневой узел, так что структура связей "родитель-член", образующая скелет модели - односвязное дерево. Структура "родитель-член" также задаёт базис для пространства имён узлов.
Каждый узел имеет краткое имя, последовательность символов, которая должна быть уникальной среди соседних членов под тем же родителем, но может не быть глобально уникальной. (См. ✖ Краткие имена узлов , какие последовательности являются корректными краткими именами.) Тем самым, узел может быть однозначно идентифицирован по полному имени - краткие имена узлов, составляющих путь от корня к данному узлу, соединённые в одну строку через прямые косые черты (/). Так, например, на картинке из нашего примера узел с кратким именем subNodeA-3-1 имеет полное имя nodeA/subNodeA-3/subNodeA-3-1, а узел с кратким именем %tag3 имеет полное имя moreTags/%tag3.
Таким образом, полное имя узла может рассмариваться как аналог пути в каталоге, начиная с корневого узла (поэтому будем иногда называть его имя-путь или полное имя-путь).
Корректное краткое имя - это любая непустая и не состоящая из одних пробелов последовательность символов, не включающая /, с одним дополнением: последовательность между одиночными или двойными кавычками считается одним символом, и может включать символы, обычно не разрешённые. Например, spare/can - не корректное краткое имя (хотя является корректным полным именем), а space time - вообще не корректное имя, но "spare/can" и 'space time' - оба корректные краткие имена. Или ещё, например, A/"B/C"/D - трёхкомпонентное полное имя, состоящее из кратких имён A, "B/C", D. Кавычки должны иметь парные закрывающие, кроме случаев, когда они находятся внутри другого типа кавычек.
Обратите внимание, что кавычки считаются частью имени, и внутри них не делается никакого экранирования или компрессии пробелов (то есть, "shortName" - не то же самое имя, что shortName, и не то же самое, что "shortName ").
Кроме того, некоторые краткие имена считаются зарезервированными и (не) должны использоваться с учётом следующих оговорок:
  • краткие имена, начинающиеся с # (например, #include, ##mp). Это включает и одиночный #. Такие имена зарезервированы в качестве приватных генерируемых. Они могут появляться в скомпилированной модели, но только не в качестве кратких имён, явно заданных пользователем.
  • краткие имена, содержащие :, {, } (например, a::b, {ARG}). :, { и } зарезервированы в качестве ограничителей для входного синтаксиса.
  • краткие имена, состоящие только из точек (., .., ..., и т. д.). Они зарезервированы для обозначения текущего/предыдущего/пред-предыдущего/и т. д. уровней имени-пути и не могут быть собственными именами узлов.
  • краткое имя ~ (одиночная тильда) - каждое употребление этого краткого имени преобразуется в уникальное приватное сгенерированное краткое имя, и не может быть собственным именем узла.
Можно использовать одинарные и двойные кавычки, чтобы в некоторой степени обойти это ограничение ("#name", ".", "~", '{ARG}', 'a::b' - корректные краткие имена).
Некоторые краткие имена могут быть общеупотребительными в вашей предметной области и иметь значение конкретного атрибута метаданных для родительского узла. Например, узел с именем %title может указывать заголовок для родительского узла. Рекомендуется начинать такие краткие имена с символа '%', чтобы подчеркнуть их особую роль и упростить их фильтрацию от "обычных" подузлов.
Кроме того, узлы могут быть связаны отношением "помеченный-тег", у которого нет ограничений на направления и связуемые узлы. (Мы также будем использовать теги модели как синоним для меток, в тех контекстах, когда нужно отличать метки FDOM от тегов разметки текста.) В терминах аналогий с программированием, это отношение можно понимать как "слабые" ссылки, в противоположность "сильным" ссылкам из отношения "родитель-член" (и структуры пространства имён). При дальнейшем описании модели, тот факт, что узел помечен некими тегами, может обозначаться перечислением тегов через решётку: node #tag1 #tag2 ...
Основное применение тегов - пометка узла неким набором флагов из вашей предметной области, для указания пользователям FDOM, что документируемый объект (не) относится к некому определённому типу данной области - например, функция, структура, запрос, и т. п. На каждый такой флаг заводится специальный тег (например, %function, %arg, и т. п.), каждый из которых также является FDOM узлом. Такой подход, помимо уменьшения числа базовых сущностей в моделе, позволяет тегам самим быть помеченными и иметь подузлы метаданных, что позволяет создавать достаточно сложные терминологии.
Ещё одно применение - создание атрибутов метаданных, содержащих список других узлов. Можно создать узел с общеупотребительным кратким именем, помеченный всеми узлами, которые нужно включить в список. Например, в узле, описывающем класс, расширяющий некоторые базовые классы/интерфейсы, можно создать подузел с именем %extends и пометить его всеми узлами, описывающими базовые классы.
Несуществующие узлы считаются взаимно эквивалентными нулевым узлам - таким, которые не имеют непустого контента, не являются тегами к другим узлам, не помечены сами, и не имеют ненулевых узлов-членов. Обычно их нет смысла хранить в представлении по соображениям оптимизации.
Каждый фрагмент контента и каждый добавляемый тег ассоциируются с некоторым исходником, в котором он задан (т. е. файл с исходным кодом), и существует в модели исключительно в силу присутствия в данном исходнике. Единственный правомерный метод создания и обновления FDOM - повторное извлечение и компиляция исходников и замена всех данных, ассоциированных с ними, на более актуальные.
В результате обнавления узел может стать нулевым узлом - это допустимая ситуация.
Структура FDOM спроектирована тек, чтобы порядок обработки каждого индивидуального исходника не привносил эффектов, могущих повлияющих на результаты запроса. Создаёте ли вы FDOM с чистого листа из некоторого набора исходников, или только из части этого набора, а остальное обновляете инкрементально, возможно, повторяя по мере изменения в исходниках - вы в итоге должны получить одну и ту же модель (с точностью до возможной погрешности в порядке, в котором идут узлы при перечислении или следуют фрагменты контента, но эта погрешность не влияет на результаты запроса).
FDOM включает понятие запроса - спецификация, определяющая принципы доступа на чтение к сохранённым данным и общие черты интерфейса, через который следует предоставлять доступ к данным. Конкретный интерфейс API и низкоуровневая реализация интерфейса запроса - на усмотрение конкретных реализаций, они остаются за рамками спецификации.
В сущности, запрос FDOM - это конвейер, принимающий на вход коллекцию узлов и выдающий другую коллекцию узлов на основе определённых критериев. Этот конвейер может состоять из либо базовых запросов, либо быть составной цепочкой из подзапросов (фрагментов запросов). Запросы могут выполняться все сразу, или применяться инкрементально, с хранением промежуточного состояния в контексте запроса.
Основным элементом представления данных FDOM является коллекция - набор из узлов, не содержащий дубликатов и нулевых узлов. Набор считается неупорядоченным, и может быть пустой. Коллекция может быть явно задана пользователем, или быть полученной как результат запроса.
Наличие коллекции предполагает, что пользователь может получить доступ к каждому отдельному узлу в ней, по крайней мере посредством перечисления. Доступ к узлу предполагает возможность получить все его элементы:
  • контент (способом, зависящим от реализации, поэтому конкретные детали доступа к контенту непрозрачны в рамках концепции запроса FDOM),
  • набор узлов-членов (в виде FDOM-коллекции),
  • набор кратких имён узлов-членов, и каждый отдельный член по его краткому имени,
  • набор тегов узла (в виде FDOM-коллекции).
Хотя реализациям API рекомендуется по возможности сохранять при перечислении элементов из одного исходника тот порядок, в котором они были заданы, на такое поведение полагаться не следует. В общем случае, порядок элементов имеет значение только в самую последнюю очередь, когда все требуемые коллекции уже получены, и область ответственности запроса завершена. По этой причине, концепция запроса FDOM намеренно не содержит никаких понятий о сортировке, оставляя это под ответственность других слоёв API.
Контекст запроса представляет состояние конвейера запроса после каждого фрагмента запроса (или, в начале запроса, исходное состояние). Он ассоциирован с текущей коллекцией, которая является результатом последнего выполненного фрагмента запроса (или, в начальном состоянии, входная коллекция, предоставленная пользователем).
Чтобы упростить реализаторам возможную оптимизацию, концепция запроса FDOM предполагает, что текущая коллекция становится доступной пользователю только после завершения контекста - явного указание от пользователя, что запрос закончен, и требуется доступ к окончательному результату. Результат запроса - текущая коллекция из контекста на момент завершения.
Существует некоторое число базовых запросов, из которых может состоять более сложный запрос. FDOM не предлагает конкретного жёсткого синтаксиса для DSL запросов, поэтому будем описывать их в несколько условной нотации. Каким именно способом они будут выражаться в рабочем коде, зависит от конкретного API и реализации. Сложный запрос - это, в своей основе, список базовых запросов, в котором каждый последующий начинается с входными данными, полученными как выходные данные от предыдущего, причём у первого это исходная текущая коллекция контекста, а результат последнего становится новой текущей коллекцией. В нашей условной нотации такие списки-запросы будут обозначаться <запрос1> / <запрос2> / ... / <запросN>.
Напомним некоторые моменты насчёт запросов, которые следует держать в уме:
  • коллекции (включая результаты запросов) не содержат нулевых узлов и дубликатов
  • коллекции не упорядоченные, порядок их элементов при перечислении зависит от реализации. Хотя реализациям рекомендуется сохранять порядок перечисления в явно заданных коллекциях (см. ✖ Задание коллекций ) и порядок, в котором объявляются элементы из одного исходника, это совет "по возможности", а не требование, и ему не всегда возможно следовать. Точное упорядочивание - вопрос, выходящий за рамки FDOM.
Не запрос сам по себе, но бывают случаи, когда нужно задать коллекцию явным образом (начальную, на которой начинается запрос, или вспомогательную коллекцию в дополнение к текущей). FDOM позволяет следующие способы для спецификации коллекции:
  • непосредственный список узлов, обычно по их полным именам: [node-fn-1, node-fn-2, ...] Т. к. коллекция может быть пустой, но не может содержать нулевых узлов, все нулевые узлы, возможно перечисленные в этом списке, по факту исключаются из коллекции (они не попадают в перечисление и не учитываются никаким образом, в том числе при подсчёте размера коллекции).
  • смешанные список узлов и спецификаций коллекций, например: [node-fn-1, node-fn-2, <coll-spec-1>, node-fn-3...] Расширенный вариант списка узлов. Элемненты спецификации коллекции воспринимаются как элементы списка, полученные распаковкой соответствующей коллекции; узлы-дубликаты игнорируются (узел включается в коллекцию не более одного раза).
  • объединение коллекций: <coll-spec-1> + <coll-spec-2> + ... Коллекция, которая включает каждый узел из каждой указанной коллекции.
  • пересечение коллекций: <coll-spec-1> & <coll-spec-2> & ... Коллекция, которая включает только узлы, имеющиеся одновременно во всех указанных коллекциях.
  • вычитание коллекций: <coll-spec-1> - <coll-spec-2> - ... Коллекция, которая включает только узлы, которые находятся в первой коллекции списка, но не во второй или в каких-либо последующих коллекциях из списка.
  • ссылка по алиасу: SomeCollAlias Алиас - "именованная закладка" для коллекции в контексте запроса, чтобы можно было запомнить её и сослаться на неё позже. Алиас, по конвенции, обозначается корректным кратким именем FDOM.
  • API-специфичное представление: любой иной способ, который API предоставляет пользователю для задания готового объекта коллекции, или задаваемого напрямую, или полученного из запроса, или как-либо ещё.
Спецификация может быть составной, например: BasicNodes + [advanced/%node1, advanced/%node3, MoreAdvancedNodes] + ((EvenMoreNodesA & EvenMoreNodesB) - [%node4]).
Замечание насчёт алиасов: они могут быть действительными только в пределах конкретного запроса, или же постоянными (общими для множества независимых запросов). В любом случае, алиас полагается уникальным в своей области действия и, после того, как он задан, не должен переназначаеться на другую коллекцию (в случае переназначения алиаса поведение зависит от реализации).
Некоторые запросы требуют задать условие для проверки применительно к узлу, могущему быть включённым в результат. FDOM предусматривает следующие условия:
  • Булевая константа: true - данное условие всегда соблюдается, false - данное условие всегда не соблюдается.
  • isAnyOf <спецификация-коллекции>: условие соблюдается, если узел - один из заданной коллекции.
  • hasMembersThat <подусловие>: условие соблюдается, если под-условие выполняется по крайней мере для одного из ненулевых членов узла.
  • hasMembersNamed <регулярное выражение>: условие соблюдается, если узел имеет по крайней мере один ненулевой член с краткими именем, подходящим под заданное регулярное выражение. Это сокращённый вариант для hasMembersThat (named <регулярное выражение>) (см. ниже), могущий иметь более оптимальную реализацию.
  • hasAnyOfTags <спецификация-коллекции>: условие соблюдается, если узел имеет по крайней мере один тег из заданной коллекции узлов-тегов.
  • hasAllOfTags <спецификация-коллекции>: условие соблюдается, если узел имеет все теги из заданной коллекции узлов-тегов.
  • hasParentThat <подусловие>: условие соблюдается, если подусловие соблюдается для непосредственного родителя узла.
  • named <регулярное выражение>: условие соблюдается, если краткое имя узла подходит под заданное регулярное выражение.
  • and <список подусловий>: условие соблюдается, если соблюдаются все подусловия из заданного списка.
  • or <список подусловий>: условие соблюдается, если соблюдается по крайней мере одно подусловие из заданного списка.
  • not <подусловие>: условие соблюдается, если подусловие не соблюдается, и не соблюдается, если подусловие соблюдается.
  • ссылка по алиасу SomeConditionAlias: условие может быть помечено алиасом-закладкой (по конвенции обозначаемым корректным кратким именем FDOM), который может быть использован далее в пределах данного запроса или других запросов в данном контексте.
Обратите внимание, что не предусмотрено условий, работающих по данным контента. Запросы FDOM - чисто структурные.
alias ShortName - задать локальный алиас для текущей коллекции (он действует только до завершения текущего запроса). Не изменяет текущую коллекцию, просто задаёт для неё алиас, который может быть использован в последующих частях запроса.
Все краткие имена алиасов, локальные и постоянные, предполагаются уникальными в пределах контекста. Переиспользование идентификатора не рекомендуется (локальный идентификатор может переиспользоваться только в следующем запроса), и поведение в этом случае зависит от реализации.
with <спецификация-коллекции> - заменить текущую коллекцию явно заданной (см. ✖ Задание коллекций ). Полезно для подзапросов, которые основаны на некотором предварительно известном множестве узлов.
membersThat <спецификация-условия> [on <спецификация-коллекции>] [recursive] - собрать узлы-члены из каждого элемента текущей коллекции, такие, что каждый член удовлетворяет заданному условию ( ✖ Задание условий ), и заменить текущую коллекцию этим набором узлов. Вместо текущей коллекции можно задать поиск по явно заданной. Данный запрос может быть рекурсивным ( ✖ Рекурсивные запросы ).
Например, у нас есть узел и его члены:
  • node
  • node/memberA
  • node/memberB
  • node/memberC
Запрос начинается с коллекцией:
  • node
Затем запрос membersThat (not (named /^.*A/)) даёт следующую коллекцию:
  • node/memberB
  • node/memberC
itemsThat <спецификация-условия> [on <спецификация-коллекции>] [recursive] - отобрать из текущей коллекции элементы, удовлетворяющие заданному условию ( ✖ Задание условий ), и заменить текущую коллекцию набором этих узлов. Вместо текущей коллекции можно задать поиск по явно заданной. Данный запрос может быть рекурсивным ( ✖ Рекурсивные запросы ).
Например, запрос начинается с коллекцией узлов:
  • node1/memberA
  • node2/memberB
  • node3/memberC
Тогда запрос itemsThat (not (named /^.*A/)) даёт следующую коллекцию:
  • node2/memberB
  • node3/memberC
tagsThat <спецификация-коллекции> [on <спецификация-коллекции>] [recursive] - собрать узлы-теги с каждого элемента коллекции, такие, что каждый тег удовлетворяет заданному условию ( ✖ Задание условий ), и заменить текущую коллекцию набором этих узлов. Вместо текущей коллекции можно задать поиск по явно заданной. Данный запрос может быть рекурсивным ( ✖ Рекурсивные запросы ). Хотя этот запрос можно использовать на любой коллекции, типовое использование для него - на одиночном элементе (коллекции из одного элемента).
inMembersThat <спецификация-условия> [on <спецификация-коллекции>] [recursive] query <список-базовых-запросов> - собрать узлы-члены от каждого элемента текущей коллекции, такие, что каждый член удовлетворяет заданному условию ( ✖ Задание условий ), и заменить текущую коллекцию результатом заданного списка запросов на коллекции эти узлов (членов исходных). Вместо текущей коллекции можно задать начало поиска по подзапросам на явно заданной. Данный запрос может быть рекурсивным ( ✖ Рекурсивные запросы ).
Например, имеем узлы с членами и подчленами:
  • node
  • node/memberA
  • node/memberB
  • node/memberB/%data
  • node/memberC
  • node/other/%data
Запрос начинается с коллекцией:
  • node
Тогда запрос inMembersThat (named /%^member/) query (hasMembersNamed /^%data$/) выдаёт следующую коллекцию:
  • node/memberB/%data
(node/other имеет член %data, но он не проходит по условию в inMembersThat, поэтому не попадает в коллекцию для поиска по подзапросу.)
Подзапросы могут использовать локальные алиасы коллекций из того же пространства имён, что и основной запрос.
inTagsThat <спецификация-условия> [on <спецификация-коллекции>] [recursive] query <список-базовых-запросов> - собрать узлы-теги с каждого элемента текущей коллекции, такие, что каждый тег удовлетворяет заданному условию ( ✖ Задание условий ), и заменить текущую коллекцию результатом заданного списка запросов на коллекции этих узлов (тегов от исходных). Вместо текущей коллекции можно задать начало поиска по подзапросам на явно заданной. Данный запрос может быть рекурсивным ( ✖ Рекурсивные запросы ).
Например, имеем узлы-теги и их члены:
  • %tag1
  • %tag2
  • %tag2/%isLangObject
  • %tag2/%subInfo
  • %tag3
  • %tag3/%subInfo
и следующий набор узлов со следующими тегами:
  • node1 #%tag1
  • node2 #%tag1 #%tag2
  • node3 #%tag3 #%tag2
  • node4 #%tag4
Запрос - inTagsThat (hasMembersNamed /^%isLangObject$/) query membersThat (named /^%subInfo$/).
Выполненный на одной из следующих коллекций:
  • node1
  • node2
  • node3
он возвращает коллекцию:
  • %tag2/%subInfo
Выполненный на коллекции:
  • node1
  • node4
он возвращает пустую коллекцию.
Подзапросы могут использовать локальные алиасы коллекций из того же пространства имён, что и основной запрос.
inItemsThat <спецификация-условия> [on <спецификация-коллекции>] [recursive] query <список-базовых-запросов> - взять каждый элемент текущей коллекции, удовлетворяющий заданному условию ( ✖ Задание условий ), и заменить текущую коллекцию результатом заданного списка запросов на коллекции этих узлов (элементов исходной коллекции). Вместо текущей коллекции можно задать начало поиска по подзапросам на явно заданной. Данный запрос может быть рекурсивным ( ✖ Рекурсивные запросы ).
Например, имеем следующие узлы и их члены:
  • node1
  • node1/member1
  • node1/member2
  • node1/member2/%flagged
  • node2
  • node2/member2
  • node2/member2/%flagged
  • node3/member1
  • node3/member1/%flagged
Запрос - inItemsThat (hasMembersNamed /^member1$/) query membersThat (haveMembersNamed /^%flagged$/). Выполненный на коллекции:
  • node1
  • node2
  • node3
он возвращает коллекцию:
  • node1/member2
  • node3/member1
Подзапросы могут использовать локальные алиасы коллекций из того же пространства имён, что и основной запрос.
subtractQuery [on <спецификация-коллекции>] <спикок-базовых-запросов> - выполнить заданный список запросов и вычесть результат из текущей коллекции. Коллекция, на которой выполняется подзапрос - по умолчанию, исходная коллекция для самого subtractQuery (т. е. текущая), но можно указать вместо неё явно заданную.
Например, имеем следующие узлы и их члены:
  • node1
  • node2
  • node2/%flag
  • node3
Запрос - subtractQuery itemsThat (haveMembersNamed /^%flag$/). Выполненный на коллекции:
  • node1
  • node2
  • node3
он возвращает коллекцию:
  • node1
  • node3
Подзапросы могут использовать локальные алиасы коллекций из того же пространства имён, что и основной запрос.
unionQuery [on <спецификация-коллекции>] <список-базовых-запросов> - выполнить заданный список запросов и объединить результат с текущей коллекцией. Коллекция, на которой выполняется подзапрос - по умолчанию, исходная коллекция для самого unionQuery (т. е. текущая), но можно указать вместо неё явно заданную.
Например, имеем следующие узлы и их члены:
  • node1
  • node2
  • node2/member
  • node3
Запрос - unionQuery membersThat (true). Выполненный на коллекции:
  • node1
  • node2
  • node3
он возвращает коллекцию:
  • node1
  • node2
  • node2/member
  • node3
Подзапросы могут использовать локальные алиасы коллекций из того же пространства имён, что и основной запрос.
intersectQuery [on <спецификация-коллекции>] <список-базовых-запросов> - выполнить заданный список запросов и взять пересечение результата с текущей коллекцией. Коллекция, на которой выполняется подзапрос - по умолчанию, исходная коллекция для самого intersectQuery (т. е. текущая), но можно указать вместо неё явно заданную.
Например, имеем следующие узлы и их члены:
  • node1
  • node2
  • node2/%flag
  • node3
Запрос - intersectQuery itemsThat (haveMembersNamed /^%flag$/). Выполненный на коллекции:
  • node1
  • node2
  • node3
он возвращает коллекцию:
  • node2
Подзапросы могут использовать локальные алиасы коллекций из того же пространства имён, что и основной запрос.
sideQuery [on <спецификация-коллекции>] <список-базовых-запросов> - выполнить заданный список запросов, но оставить текущую коллекцию без изменений. Этот запрос имеет смысл только в случае, если список заканчивается заданием локального алиаса. Коллекция, на которой выполняется подзапрос - по умолчанию, исходная коллекция для самого sideQuery (т. е. текущая), но можно указать вместо неё явно заданную.
Предполагаемое использование для данного запроса - задать алиас в процессе серии базовых запросов, не нарушеная ход "основного" запроса. Например, имеем узел с тегами:
  • tagsList #tag1 #tag2
и есть коллекция узлов с тегами:
  • node1 #tag1
  • node2 #tag2
  • node3 #tag3
Тогда запрос "выбрать узлы с тегами из списка, содержащегося в tagsList" будет такой: (sideQuery on [tagsList] allTagsThat (true) / alias TAGS) / allItemsThat (hasAnyOfTags TAGS).
Применённый к коллекции:
  • node1
  • node2
  • node3
он вернёт коллекцию:
  • node1
  • node2
Подзапросы могут использовать локальные алиасы коллекций из того же пространства имён, что и основной запрос.
Некоторые запросы можно задать как рекурсивные. Это означает, что, после замены текущей коллекции подходящим набором узлов, тот же самый запрос снова применяется к полученной коллекции, результат добавляется к текущей коллекции (как в операции объединения), потом то же самое повторяется для новодобавленных узлов, и так далее, пока коллекция не перестанет расти. Это полезно для запросов, которые извлекают узлы по некоторому транзитивному отношению.
Например, пусть у нас есть узлы и их члены с тегами, как указано ниже:
  • classA
  • classB
  • classB/%extends #classA
  • classC
  • classD
  • classD/%extends #classB #classC
Начиная запрос со стартовой коллекцией из одиночного узла для некоторого 'класса', например:
  • classD
мы можем использовать следующий запрос, чтобы получить коллекцию узлов для классов, которые расширяются исходным: inMembersThat (named /^%extends$/) allTagsThat (true), но такой запрос вернёт только узлы для "непосредственно" расширяемых классов:
  • classB
  • classC
Чтобы вытащить весь набор расширяемых классов на всю глубину дерева, нужна рекурсивная версия запроса: inMembersThat (named /^%extends$/) recursive allTagsThat (true). Тогда мы получим ожидаемое:
  • classA (рекурсивно запрошенный через classB)
  • classB
  • classC
Генерация документации в Logipard происходит в три стадии:
  • извлечение аннотаций в единообразные входные файлы в формате, понимаемом компилятором FDOM
  • компиляция из входных файлов в собственно FDOM
  • генерация требуемых артефактов документации на основе скомпилированного FDOM
Когда вызывается Logipard CLI, он выполняет все стадии в перечисленном выше порядке. Каждая стадия полагается на артефакты, сгенерированные на предыдущей, но, т. к. эти артефакты обычно сохраняются, может оказаться, что нужно (пере)запустить только некоторые стадии - это делается явным указанием тех, которые вам нужны.
Запуск только стадии извлечения:
lp-cli --extract <файл-конфигурации>
Запуск только стадии компиляции:
lp-cli --compile <файл-конфигурации>
Запуск только стадии генерации:
lp-cli --generate <файл-конфигурации>
Запуск стадий компиляции и генерации:
lp-cli --compile --generate <файл-конфигурации>
...и т. п.
На этой стадии документационные аннотации извлекаются из файлов-исходников (или иных источников) и помещаются во входные файлы Logipard определённого единообразного формата. Это выполняется плагинами, называемыми считыватели стадии извлечения.
Хотя ожидаемый выходной формат на этой стадии (и входной для стадии компиляции) фиксирован, формат самих аннотаций, как и их распознавание, остаётся на усмотрение считывателей стадии извлечения. Вполне возможно, например, сделать считыватель из комментариев javadoc или doxygen, из машиночитаемого выхода какого-либо компилятора, или даже из скомпилированного кода. Также вполне возможно пропустить один и тот же исходник через несколько считывателей-извлекателей, предоставляя каждому извлечь ту часть входных данных, которую он может распознать.
Хотя технически возможно извлечь исходные данные для нескольких независимых FDOM, в целом ожидается, что все экстракторы проекта будут готовить исходные данные для одного и того же FDOM, и что они будут придерживаться некоторой согласованной общей конвенции при организации извлеченных входных файлов. Для работы с независимыми FDOM лучше иметь раздельные файлы конфигурации проекта.
Стадия извлечения управляется разделом ✖ lp-extract конфигурационного файла.
Считыватель стадии извлечения должен быть реализован в соответствии с надлежащим интерфейсом: ✖ Интерфейс считывателя стадии извлечения .
Logipard включает некоторые встроенные считыватели стадии извлечения: ✖ Встроенные считыватели стадии извлечения
Несколько готовых к использованию считывателей стадии извлечения, включены в Logipard в качестве как инструментов для быстрого старта, так и примеров реализации.
Считыватель, извлекающий входные данные для LP из однострочных комментариев или чисто текстовых файлов, с минимальной дополнительной обработкой. Поддерживает однострочные комментарии в типовых C-подобных (//), shell-подобных (#), lua и SQL-подобных (--) языках порграммирования, и, в качестве специального "языка программирования", чистый текст. Также служит примером реализации считывателя стадии извлечения. Этот считыватель задействуется в элементе задания стадии извлечения через задание параметра writer: "${LP_HOME}/lpxread-basic" $ в ( ✖ reader ).
Непрерывная последовательность строк с однострочными комментариями, начинающихся с #LP или -#LP, считается фрагментом входных данных для LP, относящихся к одиночному #LP-тегу разметки (фрагменты -#LP игнорируются), например:
code code // не LP комментарий
code //#LP: LP тег
code code code // продолжение LP тега
code //#LP: другой LP тег
code code code // продолжение другого LP тега
code
code // снова не LP комментарий comment (строка без комментария завершает непрерывность)
code code
code code code // ещё раз не LP comment
code //#LP: третий LP тег <#LP: теги, полностью записанные внутри строки, включая отступления, тоже допускаются#> в том числе
code code code //-#LP закомментированный LP тег
code //#LP: 4й LP тег
выдаёт следующий извлечённый входной поток:
<#LP: LP тег
 продолжение LP тега#>
<#LP: другой LP тег
 продолжение другого LP тега#>
<#LP: третий LP тег <#LP: теги, полностью записанные внутри строки, включая отступления, тоже допускаются#> в том числе#>
<#LP: 4й LP тег#>
Дополнительно разрешается указать используемую кодировку с помощью комментария вида //#charset utf-8 (не более одного раза на файл, слово "charset" должно быть в нижнем регистре). Что именно будет считаться за однострочный комментарий, зависит от типа исходника, заданного для этого элемента задания извлечения (см. ✖ Конфигурация lpxread-basic (элемент задания lp-extract) ).
Для чисто текстового "языка программирования", за однострочный комментарий считается каждая строка файла. Побочный эффект такой конвенции - вам может понадобиться вставить строку с #-LP (или #LP-) комментарем, чтобы отметить окончание LP-тега, начатого по #LP tag: .... Дополнлительно, в достаточно специфическом случае, когда у вас есть фрагмент кода, включающего разметку LP, следует разместить её междё строками-ограничителями #LP~delimiter~, чтобы предотвратить нарушение в формате извлекаемых данных. Например:
#LP~x~
```
#LP ./example: пример кода, содержащего дословную разметку LP <#~~ и неэкранированный дословный фрагмент ~~#>
```
#LP~x~
Всё между строками #LP~...~ будет выдано в извлечённый входной поток в точности дословно, хотя корректность фрагмента в целом как разметки LP по-прежнему требуется. Поэтому используйте данный способ только для выделения фрагментов кода, и с осторожностью.
Добавьте член с именем lpxread-basic с конфигурацией, специфичной для lpxread-basic, к элементу задания стадии извлечения, использующему lpxread-basic. Подчлены данного объекта описаны далее...
Члены
Имя
Описание
✖ srcType
Тип исходника у файлов, перечисленных в inFiles...
Например:
{
	"inFiles": ["**/*.js"],
	"excludeInFiles": [],
	"outDir": "...",
	"reader": "logipard/lpxread-basic",
	"lpxread-basic": {
		"srcType": "generic-c-like"
	}
}
Члены (подробно)
Тип исходника у файлов, перечисленных в inFiles...
Может быть одним из:
  • generic-c-like: C-подобные языки, разрешающие однострочные комментарии, начинающиеся с // (семейство C family, семейство Java, JS, PHP, и т. д.)
  • generic-sh-like: языки, разрешающие однострочные комментарии, начинающиеся с # (семейство sh, python, perl, PHP, и т. д.)
  • lua-sql: языки, разрешающие однострочные комментарии, начинающиеся с -- (наиболее известные - Lua и SQL)
  • lp-text: файл на чисто текстовой основе, однострочным комментарием считается каждая строка
На этой стадии извлечённые входные данные компилируются в конкретное представление FDOM. Logipard не принуждает использовать какое-либо определённое представление FDOM или механизм его хранения. Вместо этого, он передаёт команды по созданию и обновлению модели плагинам, называемым записыватели скомпилированной модели (или записыватели стадии компиляции), и вопрос представления и хранения модель (в локальном файле некоторого формата, или в БД, или некотором онлайновом сервисе), оставляется на их усмотрение. В дополнение к ним, есть также понятие считывателей скомпилированной модели, которые, как ожидается, могут считывать FDOM из места хранения, созданного/обновляемого соответствующими записывателями скомпилированной модели, и предоставлять её в удобной форме для запросов пользователя (которым обычно является генератор на стадии генерации).
Стадия компиляции управляется элементом ✖ lp-compile конфигурационного файла.
Записыватель скомпилированной модели должен быть реализован в соответствии с надлежащим интерфейсом: ✖ Интерфейс записывателя стадии компиляции .
Реализация считывателя скомпилированной модели остаётся на усмотрение реализатора, но рекомендуется делать её с оглядкой на концепцию запросов FDOM: ✖ Запросы FDOM .
Logipard включает несколько встроенных записывателей скомпилированной модели: ✖ Встроенные записыватели скомпилированной модели и соответствующих им считывателей модели: ✖ Встроенные считыватели скомпилированной модели
Несколько готовых к использованию записывателей скомпилированной модели, включены в Logipard в качестве как инструментов для быстрого старта, так и примеров реализации.
Записыватель модели в JSON-файл со структурой, представленной ниже. Записыватель хранит всю модель целиком в памяти и каждый раз полностью перезаписывает файл, поэтому он может не подходить для достаточно больших объёмов документации и/или для слишком частых обновлений.
См. описание выходной структуры JSON-файла, конфигурации записывателя и подробности об использовании: ✖ ${LP_HOME}/lpcwrite-basic-json
Считыватели скомпилированной модели, соответствующие встроенным записывателям скомпилированной модели.
Этот считыватель может читать FDOM из JSON-файла, скомпилированного ✖ ${LP_HOME}/lpcwrite-basic-json: Записыватель FDOM в JSON-файл . Он используется внутри ✖  ${LP_HOME}/lpgwrite-example: Пример-генератор одностраничной документации HTML/MD , но доступен также и для применения сам по себе в ваших собственных генераторах (или для других целей). Он следует рекомендуемой схеме интерфейса для считывателей модели: ✖ Рекомендуемый интерфейс для считывателя скомпилированного FDOM .
См. описание интерфейса и использования: ✖ logipard/lpgread-basic-json.js
На этой стадии предполагается производство конечных артефактов из скомпилированного и сохранённого FDOM. Эта задача поручается плагинам, называемым записыватели стадии генерации (или, в более широком смысле, просто генераторы). Генератор может делать самые разные вещи в меру изобретательности пользователя. В наиболее типичном случае это может быть создание некой документации для конечного пользователя, но это может быть также обновление в вики, или трекере задач, или генерация некого кода или конфигурации для поставки, или промежуточного артефакта для следующей стадии некого более крупного конвейера, продолжающегося за пределы Logipard.
Элементы задания на этой стадии упорядочены, поэтому считается разумным и возможным вариант, когда записыватель стадии генерации подготавливает некие входных данных для другого записывателя, который будет запущен на этой же стадии, но следует позже по списку.
Поскольку работа генератора должна быть основана на FDOM, он почти всегда полагается на извлечение данных с помощью считвателей скомпилированной модели. Как правило, записыватель стадии генерации поддерживает определённый набор считывателей скомпилированной модели, и должен получить данные о месте хранения соответствующего представления FDOM как некоторую часть конфигурации.
Стадия генерации управляется разделом ✖ lp-generate конфигурационного файла.
Записыватель стадии генерации должен быть реализован в соответствии с надлежащим интерфейсом: ✖ Интерфейс записывателя стадии генерации .
Logipard включает несколько встроенных записывателей стадии генерации: ✖ Встроенные записыватели стадии генерации
Несколько готовых к использованию записывателей стадии генерации, включены в Logipard в качестве как инструментов для быстрого старта, так и примеров реализации.
Этот записыватель стадии генерации выдаёт человекочитаемую страницу с документацией, извлечённой и структурированной по заданной программе документа. См. описание конфигурации записывателя и более подробную информацию об использовании: ✖ ${LP_HOME}/lpgwrite-example . Этот генератор задействуется в элементе стадии генерации через задание параметра writer: "${LP_HOME}/lpgwrite-example" $ в ✖ writer .
Этот генератор содействует в переводе на разные языки представлений FDOM в виде JSON-файлов, скомпилированных ✖ ${LP_HOME}/lpcwrite-basic-json: Записыватель FDOM в JSON-файл . См. описание конфигурации записывателя и более подробную информацию об использовании: ✖ ${LP_HOME}/lpgwrite-i18n-assist . Этот генератор задействуется в элементе стадии генерации через задание параметра writer: "${LP_HOME}/lpgwrite-i18n-assist" $ в ✖ writer .
Конфигурация проекта передаётся в Logipard в конфигурационном файле формата JSON (точнее говоря, ✖ LPSON ) следующей структуры:
{
	// необязательно (обратите внимание, после '+' - ровно один пробел)
	"+ config": {
		...
	},
	// обязательно, конфигурация для стадии извлечения
	"lp-extract": {
		// необязательно
		"+ config": {
			...
		},
		// обязательно
		"items": [
			{
				"SKIP": bool, // необязательно, не-false значение означает, что данный элемент закомментирован
				"inRootDir": string-path, // необязательно, по уполчанию - корневой каталог проекта (см. ниже)
				"inFiles": [ ...strings-file-globs ], // обязательно, должно быть относительно каталога, заданного в inRootDir
				"excludeInFiles": [ ...strings-file-globs ], // необязательно, должно быть относительно каталога, заданного в inRootDir
				"forLPInclude": bool, // необязательно, по умолчанию false
				"outDir": string-path, // обязательно
				"reader": string-path, // обязательно
				... // все прочие параметры не распознаются непосредственно в Logipard и являются дополнительными, считывателям здесь могут потребоваться их собственные параметры
			},
			... // ноль, один или более элементов
		]
	},
	// обязательно, конфигурация для стадии компиляции
	"lp-compile": {
		// необязательно
		"+ config": {
			...
		},
		// обязательно
		"items": [
			{
				"SKIP": bool, // необязательно, не-false значение означает, что данный элемент закомментирован
				"inRootDir": string-path, // обязательно 
				"outFile": string-path,
				"writer": string-path,
			},
			... // ноль, один или более элементов
		]
	},
	// обязательно, конфигурация для стадии генерации
	"lp-generate": {
		// необязательно
		"+ config": {
		},
		// обязательно
		"items": [
			{
				"SKIP": bool, // необязательно, не-false значение означает, что данный элемент закомментирован
				"inFile": string-path,
				"writer": string-path,
				...
			},
			... // ноль, один или более элементов
		]
	}
}
Конфигурационный файл также работает как якорь для корневого каталога проекта (корня проекта). Им считается каталог, в котором размещён конфигурационный файл. Все относительные пути к файлам и каталогам предполагаются относительно корня проекта, если для конкретного случая не оговаривается иное.
Указанный набор членов - необходимый минимум, распознаваемый непосредственно самим Logipard. Однако допускаются дополнительные - их могут использовать пользовательские инструменты. Единица конфигурации, передаваемая инструменту, основана на на объекте одиночного элемента из "items", но в него также дополнительно добавляется содержимое объекта "+ config" из соответствующей инструменту стадии, и дополнительно добавляется содержимое "+ config" из корневого уровня (в порядке глобальный "+ config" -> "+ config" стадии -> элемент). Добавление объектов делается путём неглубокого почленного слияния. В случае совпадения имён, член более следующего по порядку объекта заменяет член более предыдущего. Однако, возможно модифицировать это поведение для элемента-массива или объекта: если ожидаемое имя члена - "id", то добавьте член с именем "+ id" в "+ config" - в этом случае, член "id" полученного элемента будет содержать подчлены/элементы от "+ id" из исходного(ых) "+ config", добавленные перед теми, которые укзаны в "id" самого элемента.
Т. е.:
{
	"+ config": {
		...
		"a": [1, 2],
		"+ b": [3, 4],
		"c": [5, 6],
		"+ d": [7, 8],
		...
	},
	"lp-...": {
		"+ config": {
			...
			"a": [9, 10],
			"b": [11, 12],
			"+ c": [13, 14],
			"+ d": [15, 16],
			...
		},
		"items": [
			...
			{
				// "a", "b", "c", "d" не заданы, в результирующем элемене конфигурации они будут:
				// "a": [9, 10]
				// "b": [3, 4, 11, 12]
				// "c": [13, 14]
				// "d": [7, 8, 15, 16]
			},
			{
				...
				"a": ["A", "B"], // в результате будет "a": ["A", "B"]
				"b": ["A", "B"], // в результате будет "b": ["A", "B"]
				"c": ["A", "B"], // в результате будет "c": [13, 14, "A", "B"]
				"d": ["A", "B"], // в результате будет "d": [7, 8, 15, 16, "A", "B"]
				...
			}
		]
	}
}
Члены
Имя
Описание
✖ "+ config"
Параметры конфигурации, общие для всех элементов заданий во всех стадиях. Добавляются к каждому конкретному элементу конфигурации перед содержимым самого элемента и содержимым "+ config" конкретной стадии.
✖ lp-extract
Параметры конфигурации для элементов заданий на стадии извлечения.
✖ lp-compile
Параметры конфигурации для элементов задания на стадии компиляции.
✖ lp-generate
Параметры конфигурации для элементов задания на стадии генерации.
Члены (подробно)
Параметры конфигурации, общие для всех элементов заданий во всех стадиях. Добавляются к каждому конкретному элементу конфигурации перед содержимым самого элемента и содержимым "+ config" конкретной стадии.
Параметры конфигурации для элементов заданий на стадии извлечения.
Члены
Имя
Описание
✖ "+ config"
Параметры конфигурации, общие для всех элементов заданий на стадии извлечения. Добавляется к каждому конкретному элементу конфигурации перед содержимым самого элемента, и после содержимого глобального ✖ "+ config" .
✖ items[]
Массив конфигураций, задающих каждый элемент задания на стадии извлечения.
Члены (подробно)
Параметры конфигурации, общие для всех элементов заданий на стадии извлечения. Добавляется к каждому конкретному элементу конфигурации перед содержимым самого элемента, и после содержимого глобального ✖ "+ config" .
Массив конфигураций, задающих каждый элемент задания на стадии извлечения.
Члены
Имя
Описание
✖ SKIP
Булевой. Не-false значение сообщает Logipard, что этот элемент нужно пропустить. Используйте, чтобы "закомментировать" временно отключаемые элементы. Необязательно, по умолчанию false.
✖ inRootDir
Корневой каталог для поиска входных файлов, по умолчанию - корень проекта.
✖ inFiles
Строка, или массив строк, с glob-масками для имён файлов - задаёт множество файлов-исходников, попадающих под данный элемент задания. Пути в масках относительны ✖ inRootDir .
✖ excludeInFiles
Строка, или массив строк, с glob-масками для имён файлов - задаёт множество файлов для исключения из списка, попавшего под ✖ inFiles . Пути в масках относительны ✖ inRootDir . Необязательный параметр.
✖ outDir
Строка, путь к каталогу, где будет размещены файлы во входном формате Logipard, извлечённые из файлов-исходников. Каталог с результатом извлечения предполагается временным по назначению, и должен быть добавлен в игнор-список системы контроля версий. Обратите внимание, что один и тот же файл-исходник может быть обработан несколькими элементами задания стадии извлечения, но, если в разных элементах задний указан один и тот же outDir для извлечённых файлов, то более поздние задания могут перезаписать свой результат поверх результата более ранних - следует принимать это во внимание и заранее позаботиться, чтобы их выходные пути не конфликтовали.
✖ forLPInclude
Булевой, если true, то результаты извлечения из исходников данным элементом задания будут сохранены как файлы модулей, доступные для включения при помощи LP-inc/LP-include (см. ✖ Подключение файлов модулей ). Необязательно, по умолчанию false.
✖ reader
Строка. Путь к JS-файлу считывателя стадии извлечения, относительно корня проекта (или абсолютный). Считыватель стадии извлечения должен соответствовать ✖ Интерфейс считывателя стадии извлечения . Logipard включает несколько встроенных считывателей стадии извлечения: ✖ Встроенные считыватели стадии извлечения
Члены (подробно)
Булевой. Не-false значение сообщает Logipard, что этот элемент нужно пропустить. Используйте, чтобы "закомментировать" временно отключаемые элементы. Необязательно, по умолчанию false.
Корневой каталог для поиска входных файлов, по умолчанию - корень проекта.
Строка, или массив строк, с glob-масками для имён файлов - задаёт множество файлов-исходников, попадающих под данный элемент задания. Пути в масках относительны ✖ inRootDir .
Строка, или массив строк, с glob-масками для имён файлов - задаёт множество файлов для исключения из списка, попавшего под ✖ inFiles . Пути в масках относительны ✖ inRootDir . Необязательный параметр.
Строка, путь к каталогу, где будет размещены файлы во входном формате Logipard, извлечённые из файлов-исходников. Каталог с результатом извлечения предполагается временным по назначению, и должен быть добавлен в игнор-список системы контроля версий. Обратите внимание, что один и тот же файл-исходник может быть обработан несколькими элементами задания стадии извлечения, но, если в разных элементах задний указан один и тот же outDir для извлечённых файлов, то более поздние задания могут перезаписать свой результат поверх результата более ранних - следует принимать это во внимание и заранее позаботиться, чтобы их выходные пути не конфликтовали.
Булевой, если true, то результаты извлечения из исходников данным элементом задания будут сохранены как файлы модулей, доступные для включения при помощи LP-inc/LP-include (см. ✖ Подключение файлов модулей ). Необязательно, по умолчанию false.
Обычно корневой путь до извлечённых файлов один и тот же для всех элементов одного проекта, но некоторые группы элементов можно размещать в специальных подкаталогах - например, можно задать <root>/src для файлов, извлечённых из исходников с кодом, и <root>/txt для файлов, извлечённых из исходников-текстовых документов.
Строка. Путь к JS-файлу считывателя стадии извлечения, относительно корня проекта (или абсолютный). Считыватель стадии извлечения должен соответствовать ✖ Интерфейс считывателя стадии извлечения . Logipard включает несколько встроенных считывателей стадии извлечения: ✖ Встроенные считыватели стадии извлечения
Параметры конфигурации для элементов задания на стадии компиляции.
Члены
Имя
Описание
✖ "+ config"
Параметры конфигурации, общие для всех элементов заданий на стадии компиляции. Добавляется к каждому конкретному элементу конфигурации перед содержимым самого элемента, и после содержимого глобального ✖ "+ config" .
✖ items[]
Массив конфигураций, задающих каждый элемент задания на стадии компиляции.
Члены (подробно)
Параметры конфигурации, общие для всех элементов заданий на стадии компиляции. Добавляется к каждому конкретному элементу конфигурации перед содержимым самого элемента, и после содержимого глобального ✖ "+ config" .
Массив конфигураций, задающих каждый элемент задания на стадии компиляции.
Члены
Имя
Описание
✖ SKIP
Булевой. Не-false значение сообщает Logipard, что этот элемент нужно пропустить. Используйте, чтобы "закомментировать" временно отключаемые элементы. Необязательно, по умолчанию false.
✖ inRootDir
Путь к корневому каталогу с входными файлами для модели, извлечёнными из исходников на стадии извлечения. В большинстве случаев, этот корень тот же, что был указан в ✖ outDir .
✖ lpIncLookupDirName
Имя каталога для использования в каскадном поиске LP-inc/LP-include.
✖ writer
Строка. Путь к JS записывателя модели стадии компиляции. Записыватель стадии компиляции должен соответствовать ✖ Интерфейс записывателя стадии компиляции . Logipard включает несколько встроенных записывателей стадии компиляции: ✖ Встроенные записыватели скомпилированной модели
Члены (подробно)
Булевой. Не-false значение сообщает Logipard, что этот элемент нужно пропустить. Используйте, чтобы "закомментировать" временно отключаемые элементы. Необязательно, по умолчанию false.
Путь к корневому каталогу с входными файлами для модели, извлечёнными из исходников на стадии извлечения. В большинстве случаев, этот корень тот же, что был указан в ✖ outDir .
Имя каталога для использования в каскадном поиске LP-inc/LP-include.
При использовании <#LP-include filename#> во входном файле LP, когда filename не абсолютно и не явно локальное (т. е. не начинается с . или ..), файл ищется в расположении ./<value-of-lpIncLookupDirName>/filename, если не найден там - то ../<value-of-lpIncLookupDirName>/filename, и так далее во всё более внешних каталогах (но не выше, чем inRootDir).
Строка. Путь к JS записывателя модели стадии компиляции. Записыватель стадии компиляции должен соответствовать ✖ Интерфейс записывателя стадии компиляции . Logipard включает несколько встроенных записывателей стадии компиляции: ✖ Встроенные записыватели скомпилированной модели
Параметры конфигурации для элементов задания на стадии генерации.
Члены
Имя
Описание
✖ "+ config"
Параметры конфигурации, общие для всех элементов заданий на стадии генерации. Добавляется к каждому конкретному элементу конфигурации перед содержимым самого элемента, и после содержимого глобального ✖ "+ config" .
✖ items[]
Массив конфигураций, задающих каждый элемент задания на стадии генерации.
Члены (подробно)
Параметры конфигурации, общие для всех элементов заданий на стадии генерации. Добавляется к каждому конкретному элементу конфигурации перед содержимым самого элемента, и после содержимого глобального ✖ "+ config" .
Массив конфигураций, задающих каждый элемент задания на стадии генерации.
Члены
Имя
Описание
✖ SKIP
Булевой. Не-false значение сообщает Logipard, что этот элемент нужно пропустить. Используйте, чтобы "закомментировать" временно отключаемые элементы. Необязательно, по умолчанию false.
✖ writer
Строка. Путь к JS записывателя стадии генерации (или просто "генератора"). Запиыватель стадии генерации должен соответствовать ✖ Интерфейс записывателя стадии генерации . Logipard включает несколько встроенных записывателей стадии генерации: ✖ Встроенные записыватели стадии генерации
Члены (подробно)
Булевой. Не-false значение сообщает Logipard, что этот элемент нужно пропустить. Используйте, чтобы "закомментировать" временно отключаемые элементы. Необязательно, по умолчанию false.
Строка. Путь к JS записывателя стадии генерации (или просто "генератора"). Запиыватель стадии генерации должен соответствовать ✖ Интерфейс записывателя стадии генерации . Logipard включает несколько встроенных записывателей стадии генерации: ✖ Встроенные записыватели стадии генерации
Справка по всем предметам и интерфейсам, с которыми пользователь Logipard может иметь дело в той или иной ситуации.
API-интерфейсы и дополнительные элементы конфигурации для встроенных плагинов.
Считыватель стадии извлечения вызывается при обработке элемента задания стадии извлечения. Его задача - распарсить данные из исходного файла и вернуть извлечённые аннотации в формате входного файла LP ( ✖ Формат входных файлов Logipard ). Он задаётся полем reader в элементе задания стадии извлечения ( ✖ reader ). Считыватель стадии извлечения должен быть реализован как модуль CommonJS со следующим интерфейсом...
Члены
Имя
Описание
✖ async .parseInput({ buffer, itemConfig, filePath })
Распарсить контент файла, переданный как Buffer-объект Node.JS, и вернуть результат извлечения во входном формате LP, как одну слитную строку.
Интерфейс должен экспоритироваться модулем reader через module.exports, например, так:
exports.parseInput = async function parseInput({ buffer, itemConfig, filePath }) { ... }
Члены (подробно)
Распарсить контент файла, переданный как Buffer-объект Node.JS, и вернуть результат извлечения во входном формате LP, как одну слитную строку.
Параметры
Имя
Описание
✖ buffer
Buffer, входной файл, переданный в непреобразованной двоичной форме. Вопросы кодировки оставляются на усмотрение считывателя.
✖ itemConfig
словарь (как Object), фрагмент объекта конфигурации, относящиегося к данному элементу задания ( ✖ items[] ). Считыватель имеет доступ к любым членам объекта конфигурации для своего элемента задания, но хороший стиль - держать всю специфичную для него конигурацию в выделенном подобъекте, размещённом в словаре под именем, указывающем на считывателя.
✖ filePath
строка, путь к файлу (не зависит от корня проекта, может самостоятельно применяться в fs или path). Может быть полезно, если одного содержимого файла недостаточно для целей считывателя, и требуется некоторая дополнительная информация, связанная с данным файлом.
Возвращает:
строка, должна содержать результат извлечения в входном формате LP ( ✖ Формат входных файлов Logipard ).
Ошибки:
parseInput может выбросить ошибку для сообщения о неудаче извлечения.
Параметры (подробно)
Buffer, входной файл, переданный в непреобразованной двоичной форме. Вопросы кодировки оставляются на усмотрение считывателя.
словарь (как Object), фрагмент объекта конфигурации, относящиегося к данному элементу задания ( ✖ items[] ). Считыватель имеет доступ к любым членам объекта конфигурации для своего элемента задания, но хороший стиль - держать всю специфичную для него конигурацию в выделенном подобъекте, размещённом в словаре под именем, указывающем на считывателя.
строка, путь к файлу (не зависит от корня проекта, может самостоятельно применяться в fs или path). Может быть полезно, если одного содержимого файла недостаточно для целей считывателя, и требуется некоторая дополнительная информация, связанная с данным файлом.
Методы
async .parseInput({ buffer, itemConfig, filePath })
Вспомогательное API, предоставляемое обратному вызову из записывателя стадии компиляции ✖  async .processCustomTag({ modelOutput, targetNodeName, tagName, toolkit, sourceFile }) , помогает обрабатывать контент тега в LP-специфике, если этот тег не трактуется как терминальный текстовый узел и может содержать LP-разметку.
Члены
Имя
Описание
✖ .lpNameRegexp(bounds,flags)
Получить RegExp для поиска или выделения строки, котрая подходит как имя элемента модели LP. В слуаче успешного сопоставления, [0] match-объекта - строка с именем.
✖ .parseName(nameString)
Распарсить строку с LP-именем в массив фрагментов.
✖ .currentScopeNodeName
Получить полное, без алиасов, имя текущего узла (как массив фрагментов имени). Только для чтения.
✖ .resolveParsedName(parsedName)
Получить полное FDOM-имя узла по массиву от распарсенного имени (полученному через ✖ .parseName(nameString) ). Полезно, когда преполагается наличие в пользовательском теге FDOM-имени, которое процессор должен разрешить по таким же правилам, как оно бы разрешилось в <#ref имя#>, использованном в этом же месте.
✖ .items[]
Массив элементов, содержащихся в теге; каждый элемент - либо строка (контент), либо нестроковой объект (вложенный тег, который следует обработать с помощью ✖ async .processTag(tagItem) ).
✖ async .processTag(tagItem)
Обрботать (вложенный) элемент-тег так же, как его обработал бы LP, если бы встретил в строке штатным образом.
✖ .text
Контент тега как единая строка. Предполагается, что в контенте не имеется вложенных тегов, в противном случае это свойство будет null.
Члены (подробно)
Получить RegExp для поиска или выделения строки, котрая подходит как имя элемента модели LP. В слуаче успешного сопоставления, [0] match-объекта - строка с именем.
Параметры
Имя
Описание
✖ bounds
Строка, необязательный параметр, по умолчанию '', может также быть '^', '$', '^$'. Задаёт граничные утверждения для включения в рег. выражение. Если содержит ^, то в начало выражения добавляется ^. Если содержит $, то в конец выражения добавляется $.
✖ flags
Строка, необязательный параметр, по умолчанию 'g'. Список regexp-флагов для добавления на регулярное выражение.
Возвращает:
RegExp-объект, сопоставляющий LP-имя
Чтобы распарсить имя более подробным образом, используйте ✖ .parseName(nameString) .
Параметры (подробно)
Строка, необязательный параметр, по умолчанию '', может также быть '^', '$', '^$'. Задаёт граничные утверждения для включения в рег. выражение. Если содержит ^, то в начало выражения добавляется ^. Если содержит $, то в конец выражения добавляется $.
Строка, необязательный параметр, по умолчанию 'g'. Список regexp-флагов для добавления на регулярное выражение.
Распарсить строку с LP-именем в массив фрагментов.
Параметры
Имя
Описание
✖ nameString
Исходное имя, строка
Возвращает:
Массив фрагментов распарсенного имени.
Параметры (подробно)
Исходное имя, строка
Получить полное, без алиасов, имя текущего узла (как массив фрагментов имени). Только для чтения.
Получить полное FDOM-имя узла по массиву от распарсенного имени (полученному через ✖ .parseName(nameString) ). Полезно, когда преполагается наличие в пользовательском теге FDOM-имени, которое процессор должен разрешить по таким же правилам, как оно бы разрешилось в <#ref имя#>, использованном в этом же месте.
Параметры
Имя
Описание
✖ parsedName
Массив, распарсенное имя, как его возвращает ✖ .parseName(nameString) .
Возвращает:
Строка, полное FDOM-имя узла.
Параметры (подробно)
Массив, распарсенное имя, как его возвращает ✖ .parseName(nameString) .
Массив элементов, содержащихся в теге; каждый элемент - либо строка (контент), либо нестроковой объект (вложенный тег, который следует обработать с помощью ✖ async .processTag(tagItem) ).
Объект-тег предполагается объектом непрозрачной структуры, и годится только для передачи в processTag - записыватель всё равно не имеет достаточно осмысленного контекста, чтобы сделать с ним что-либо другое.
Обрботать (вложенный) элемент-тег так же, как его обработал бы LP, если бы встретил в строке штатным образом.
Параметры
Имя
Описание
✖ tagItem
Объект элемента-тега - значение, полученное из массива ✖ .items[] .
Обратите внимание, что под-обработка тега предполагается непрозрачной для пользователя инструментария (т. е., для выполняемого в данный момент ✖  async .processCustomTag({ modelOutput, targetNodeName, tagName, toolkit, sourceFile }) внешнего тега), и объект-инструментарий, используемый этим пользователем, не должен быть доступен (и, тем более, использоваться) каким-либо вложенным обратным вызовам, могущим произойти во время под-обработки.
Параметры (подробно)
Объект элемента-тега - значение, полученное из массива ✖ .items[] .
Контент тега как единая строка. Предполагается, что в контенте не имеется вложенных тегов, в противном случае это свойство будет null.
Методы
.lpNameRegexp(bounds,flags)
.parseName(nameString)
.resolveParsedName(parsedName)
async .processTag(tagItem)
Записыватель стадии компиляции вызывается при обработке задания компиляции. Его задача - получать команды на построение/обновление FDOM и, на их основе, строить/обновлять скомпилированное представление FDOM, за которое он отвечает. Он задаётся полем writer в элементе, описывающем задание компиляции ( ✖ writer ). Записыватель стадии компиляции должен быть реализован как модуль CommonJS, экспортирующий следующий интерфейс...
Члены
Имя
Описание
✖  async .processCustomTag({ modelOutput, targetNodeName, tagName, toolkit, sourceFile })
Обработать пользовательский внутритекстовый тег в контенте указанного целевого узла. Интерпретация тега - на усмотрение записывателя: это может быть добавление в представление модели некого специфичного для этого представления контента, или некие настройки в процесс вывода контента, и т. д.
✖ async .openModelOutput({ itemConfig, workDir })
Инициализировать хранилище скомпилированной модели, или открыть существующее для обновления.
✖ async .closeModelOutput({ modelOutput })
Завершить вывод модели и инвалидировать дескриптор. Записыватель будет открываться и закрываться ровно один раз за каждый элемент задания компиляции, назовём это сессией обновления модели.
✖ async .invalidateSourceFile({ modelOutput, sourceFile, newDependencies })
Инвалидировать заданный файл-источник. Весь контент и добавления тегов, заданные в этом файле ( ✖ async .appendContent({ modelOutput, targetNodeName, content, sourceFile }) , ✖ async .tagTo({ modelOutput, tagNodeName, targetNodeName, sourceFile }) ), должны быть удалены из хранилища или перемещены в архив, т. к. предстоит их замена более новыми версиями. Обратите внимание, что файл-источник в данном случае, как и в других методах записывателя, означает входной файл в формате LP созданный на стадии ( ✖ Стадия извлечения ), а не тот исходный файл с аннотациями, который редактируется пользователем. ПРэтому он будет иметь расширение .lpinput и размещаться на пути, заданном в соответствующем задании извлечения в ✖ outDir .
✖ async .appendContent({ modelOutput, targetNodeName, content, sourceFile })
Дописать контент к заданному целевому узлу. Через этот метод добавляется только текстовый контент, для прочих компонентов контента предусмотрены другие методы.
✖ async .tagTo({ modelOutput, tagNodeName, targetNodeName, sourceFile })
Добавить узел-тег к целевому узлу в качестве тега. Равносильная формулировка - "пометить заданным узлом-тегом заданный целевой узел" (или "применить узел-тег к целевому узлу"). В любом варианте формулировки, это означает, что узел tagNodeName будет добавлен к списку тегов узла targetNodeName.
✖  async .appendRef({ modelOutput, targetNodeName, refNodeName, refText, sourceFile })
Дописать к контенту целевого узла внутритекстовую ссылку.
Интерфейс должен экспортирорваться модулем writer через module.exports - например, таким образом:
exports.openModelOutput = async function openModelOutput({ itemConfig, workDir }) { ... }
exports.closeModelOutput = async function closeModelOutput({ modelOutput }) { ... }
exports.invalidateSourceFile = async function invalidateSourceFile({ modelOutput, sourceFile, newDependencies }) { ... }
exports.appendContent = async function appendContent({ modelOutput, targetNodeName, content, sourceFile }) { ... }
exports.tagTo = async function tagTo({ modelOutput, tagNodeName, targetNodeName, sourceFile }) { ... }
exports.appendRef = async function appendRef({ modelOutput, targetNodeName, refNodeName, refText, sourceFile }) { ... }
exports.processCustomTag = async function processCustomTag({ modelOutput, targetNodeName, tagName, toolkit, sourceFile }) { ... }
Члены (подробно)
Обработать пользовательский внутритекстовый тег в контенте указанного целевого узла. Интерпретация тега - на усмотрение записывателя: это может быть добавление в представление модели некого специфичного для этого представления контента, или некие настройки в процесс вывода контента, и т. д.
Параметры
Имя
Описание
✖ modelOutput
Дескриптор вывода модели (тот, который был возвращён из ✖ async .openModelOutput({ itemConfig, workDir }) ).
✖ targetNodeName
Строка, полное FDOM-имя целевого узла, в котором встретился пользовательский тег.
✖ tagName
Строка, имя пользовательского тега.
✖ toolkit
Объект, набор вспомогательных функций, предоставляемых для обрабоки пользовательских тегов. См. ( ✖  Инструментарий записывателя стадии компиляции для процессора пользовательских тегов ).
✖ sourceFile
Строка, путь к исходном файлу, из которого исходит команда с данным пользовательским тегом.
Возвращает:
нет
Ошибки:
processCustomTag может выбросить ошибку.
Параметры (подробно)
Дескриптор вывода модели (тот, который был возвращён из ✖ async .openModelOutput({ itemConfig, workDir }) ).
Строка, полное FDOM-имя целевого узла, в котором встретился пользовательский тег.
Строка, имя пользовательского тега.
Объект, набор вспомогательных функций, предоставляемых для обрабоки пользовательских тегов. См. ( ✖  Инструментарий записывателя стадии компиляции для процессора пользовательских тегов ).
Строка, путь к исходном файлу, из которого исходит команда с данным пользовательским тегом.
Инициализировать хранилище скомпилированной модели, или открыть существующее для обновления.
Параметры
Имя
Описание
✖ itemConfig
Словарь (как Object), фрагмент объекта конфигурации, относящийся к данному элементу задания ( ✖ items[] ). Записыватель имеет доступ к любым членам объекта конфигурации для своего элемента задания, но хороший стиль - держать всю специфичную для него конигурацию в выделенном подобъекте, размещённом в словаре под именем, указывающем на записывателя.
✖ workDir
Строка, путь к корневому каталогу проекта, в виде, готовом для непосредственного использования в fs или path. Полезно, когда конфигурация записывателя содержит какие-либо пути до файлов/каталогов, которые должны быть указаны относительно корневого каталога проекта.
Возвращает:
дескриптор вывода модели, объект непрозрачной структуры, который будет использоваться как дескриптор для модели и передаваться другим методам записывателя.
Ошибки:
openModelOutput может выбросить ошибку.
Параметры (подробно)
Словарь (как Object), фрагмент объекта конфигурации, относящийся к данному элементу задания ( ✖ items[] ). Записыватель имеет доступ к любым членам объекта конфигурации для своего элемента задания, но хороший стиль - держать всю специфичную для него конигурацию в выделенном подобъекте, размещённом в словаре под именем, указывающем на записывателя.
Строка, путь к корневому каталогу проекта, в виде, готовом для непосредственного использования в fs или path. Полезно, когда конфигурация записывателя содержит какие-либо пути до файлов/каталогов, которые должны быть указаны относительно корневого каталога проекта.
Завершить вывод модели и инвалидировать дескриптор. Записыватель будет открываться и закрываться ровно один раз за каждый элемент задания компиляции, назовём это сессией обновления модели.
Параметры
Имя
Описание
✖ modelOutput
Дескриптор вывода модели (тот, который был возвращён из ✖ async .openModelOutput({ itemConfig, workDir }) ). После данного вызова полагается более не валидным.
Возвращает:
нет
Ошибки:
closeModelOutput может выбросить ошибку.
Параметры (подробно)
Дескриптор вывода модели (тот, который был возвращён из ✖ async .openModelOutput({ itemConfig, workDir }) ). После данного вызова полагается более не валидным.
Инвалидировать заданный файл-источник. Весь контент и добавления тегов, заданные в этом файле ( ✖ async .appendContent({ modelOutput, targetNodeName, content, sourceFile }) , ✖ async .tagTo({ modelOutput, tagNodeName, targetNodeName, sourceFile }) ), должны быть удалены из хранилища или перемещены в архив, т. к. предстоит их замена более новыми версиями. Обратите внимание, что файл-источник в данном случае, как и в других методах записывателя, означает входной файл в формате LP созданный на стадии ( ✖ Стадия извлечения ), а не тот исходный файл с аннотациями, который редактируется пользователем. ПРэтому он будет иметь расширение .lpinput и размещаться на пути, заданном в соответствующем задании извлечения в ✖ outDir .
Параметры
Имя
Описание
✖ modelOutput
Дескриптор вывода модели (тот, который был возвращён из ✖ async .openModelOutput({ itemConfig, workDir }) ).
✖ sourceFile
строка, путь к инвалидируемому файлу-источнику, относительно пути, указанного для задания компиляции в ✖ inRootDir .
Возвращает:
нет
Ошибки:
invalidateSourceFile может выбросить ошибку.
Обратите внимание, что один и тот же тег может быть добавлен к узлу командами из нескольких источников, поэтому тег должен удаляться только после того, как инвалидированы все источники, из которых он добавлялся, и только в том случае, если его не передобавили обратно (см. ✖ async .tagTo({ modelOutput, tagNodeName, targetNodeName, sourceFile }) ).
Параметры (подробно)
Дескриптор вывода модели (тот, который был возвращён из ✖ async .openModelOutput({ itemConfig, workDir }) ).
строка, путь к инвалидируемому файлу-источнику, относительно пути, указанного для задания компиляции в ✖ inRootDir .
Дописать контент к заданному целевому узлу. Через этот метод добавляется только текстовый контент, для прочих компонентов контента предусмотрены другие методы.
Параметры
Имя
Описание
✖ modelOutput
Дескриптор вывода модели (тот, который был возвращён из ✖ async .openModelOutput({ itemConfig, workDir }) ).
✖ targetNodeName
строка, полное FDOM-имя целевого узла, к которому дописывается контент.
✖ content
строка, контент, добавляемый в целевой узел.
✖ sourceFile
строка, путь к файлу-источнику, из которого происходит данный контент, относительно пути, указанного для задания компиляции в ✖ inRootDir .
Возвращает:
нет
Ошибки:
appendContent может выбросить ошибку.
Параметры (подробно)
Дескриптор вывода модели (тот, который был возвращён из ✖ async .openModelOutput({ itemConfig, workDir }) ).
строка, полное FDOM-имя целевого узла, к которому дописывается контент.
строка, контент, добавляемый в целевой узел.
строка, путь к файлу-источнику, из которого происходит данный контент, относительно пути, указанного для задания компиляции в ✖ inRootDir .
Добавить узел-тег к целевому узлу в качестве тега. Равносильная формулировка - "пометить заданным узлом-тегом заданный целевой узел" (или "применить узел-тег к целевому узлу"). В любом варианте формулировки, это означает, что узел tagNodeName будет добавлен к списку тегов узла targetNodeName.
Параметры
Имя
Описание
✖ modelOutput
Дескриптор вывода модели (тот, который был возвращён из ✖ async .openModelOutput({ itemConfig, workDir }) ).
✖ tagNodeName
строка, полное FDOM-имя узла-тега, который применяется.
✖ targetNodeName
строка, полное FDOM-имя целевого узла, к которому применяется tagNodeName.
✖ sourceFile
строка, путь к входному файлу, из которого исходит команда на добавление тега, относительно пути указанного для задания компиляции ✖ inRootDir . Есть смысл хранить указание на источник, добавивший тег, в контексте последующей инвалидации ( ✖ async .invalidateSourceFile({ modelOutput, sourceFile, newDependencies }) ) - тег остаётся в силе, пока остаётся хотя бы один не инвалидированный источник, добавляющий его. (Заметьте, что tagTo может быть вызван для одной и той же комбинации tagNodeName и targetNodeName несколько раз с различными sourceFile.)
Возвращает:
нет
Ошибки:
tagTo может выбросить ошибку.
Параметры (подробно)
Дескриптор вывода модели (тот, который был возвращён из ✖ async .openModelOutput({ itemConfig, workDir }) ).
строка, полное FDOM-имя узла-тега, который применяется.
строка, полное FDOM-имя целевого узла, к которому применяется tagNodeName.
строка, путь к входному файлу, из которого исходит команда на добавление тега, относительно пути указанного для задания компиляции ✖ inRootDir . Есть смысл хранить указание на источник, добавивший тег, в контексте последующей инвалидации ( ✖ async .invalidateSourceFile({ modelOutput, sourceFile, newDependencies }) ) - тег остаётся в силе, пока остаётся хотя бы один не инвалидированный источник, добавляющий его. (Заметьте, что tagTo может быть вызван для одной и той же комбинации tagNodeName и targetNodeName несколько раз с различными sourceFile.)
Гарантируется, что в рамках одного задания компиляции, и, соответственно, одной и той же сессии обновления модели (см. ✖ async .closeModelOutput({ modelOutput }) ), ✖ async .invalidateSourceFile({ modelOutput, sourceFile, newDependencies }) будет вызван ранее, чем какой-либо tagTo, и ровно один раз для каждого sourceFile для которого будут вызваны какие-либо tagTo (или иные добавляющие контент методы). То есть, если тег остаётся в силе после всех команд на инвалидацию источника, то все вызовы tagTo будут повторно валидировать применение тега из соответствующих sourceFile.
Дописать к контенту целевого узла внутритекстовую ссылку.
Параметры
Имя
Описание
✖ modelOutput
Дескриптор вывода модели (тот, который был возвращён из ✖ async .openModelOutput({ itemConfig, workDir }) ).
✖ targetNodeName
Строка, полное FDOM-имя целевого узла, к которому в контент дописывается ссылка.
✖ refNodeName
Строка, полное FDOM-имя узла, на который указывает дописываемая ссылка.
✖ refText
Строка, альтернативный текст ссылки. Может быть пустым (и именно так и должен храниться в скомпилированной модели, т. к. генераторы могут считать это за указание к использованию для ссылки подходящего текста по умолчанию).
✖ sourceFile
Строка, путь к входному файлу, из которого исходит команда на добавление этой ссылки, относительно пути указанного для задания компиляции ✖ inRootDir .
Возвращает:
нет
Ошибки:
appendRef может выбросить ошибку.
Параметры (подробно)
Дескриптор вывода модели (тот, который был возвращён из ✖ async .openModelOutput({ itemConfig, workDir }) ).
Строка, полное FDOM-имя целевого узла, к которому в контент дописывается ссылка.
Строка, полное FDOM-имя узла, на который указывает дописываемая ссылка.
Строка, альтернативный текст ссылки. Может быть пустым (и именно так и должен храниться в скомпилированной модели, т. к. генераторы могут считать это за указание к использованию для ссылки подходящего текста по умолчанию).
Строка, путь к входному файлу, из которого исходит команда на добавление этой ссылки, относительно пути указанного для задания компиляции ✖ inRootDir .
Методы
async .processCustomTag({ modelOutput, targetNodeName, tagName, toolkit, sourceFile })
async .openModelOutput({ itemConfig, workDir })
async .closeModelOutput({ modelOutput })
async .invalidateSourceFile({ modelOutput, sourceFile, newDependencies })
async .appendContent({ modelOutput, targetNodeName, content, sourceFile })
async .tagTo({ modelOutput, tagNodeName, targetNodeName, sourceFile })
async .appendRef({ modelOutput, targetNodeName, refNodeName, refText, sourceFile })
Записыватель стадии генерации вызывается при обработке элеменита задания стадии генерации. Его задача - прочитать FDOM-представление из поддерживаемого источника (обычно с использованием считвателя компиляции) и сгенерировать документацию или иной артефакт, за который он отвечает. Он задаётся полем writer в элементе задания стадии генерации ( ✖ writer ). Записыватель стадии генерации должен быть реализован как модуль CommonJS со следующим интерфейсом...
Члены
Имя
Описание
✖ async .perform({ workDir, itemConfig, errors })
Провести процесс генерации.
Интерфейс должен экспортирорваться модулем writer через module.exports - например, таким образом:
exports.perform = async function perform({ workDir, itemConfig, errors }) { ... }
Члены (подробно)
Провести процесс генерации.
Параметры
Имя
Описание
✖ workDir
Строка, путь к корневому каталогу проекта, может использоваться как есть в fs или path.
✖ itemConfig
Словарь (как Object), фрагмент объекта конфигурации, относящийся к данному элементу задания ( ✖ items[] ). Записыватель имеет доступ к любым членам объекта конфигурации для своего элемента задания, но хороший стиль - держать всю специфичную для него конигурацию в выделенном подобъекте, размещённом в словаре под именем, указывающем на записывателя.
✖ errors
Массив, коллекция ошибок (как объекты Error JS), возникших в процессе обработки - сюда следует добавлять ошибки по мере их возникновения.
Возвращает:
нет
Ошибки:
perform может выбросить ошибку, но рекомендуется сделать это только в самом конце для индикации неудачи процесса в целом, а промежуточные ошибки, по мере возможности, накапливать в errors.
Параметры (подробно)
Строка, путь к корневому каталогу проекта, может использоваться как есть в fs или path.
Словарь (как Object), фрагмент объекта конфигурации, относящийся к данному элементу задания ( ✖ items[] ). Записыватель имеет доступ к любым членам объекта конфигурации для своего элемента задания, но хороший стиль - держать всю специфичную для него конигурацию в выделенном подобъекте, размещённом в словаре под именем, указывающем на записывателя.
Массив, коллекция ошибок (как объекты Error JS), возникших в процессе обработки - сюда следует добавлять ошибки по мере их возникновения.
Методы
async .perform({ workDir, itemConfig, errors })
Рендерер вызывается генератором ✖  ${LP_HOME}/lpgwrite-example: Пример-генератор одностраничной документации HTML/MD . Его задача - создать окончательный документ поддерживаемого им формата (HTML, MD, и т. д.) соответственно структуре документа, переданной из lpgwrite-example. Он задаётся полем renders[]/renderer внутри элемента задания стадии генерации для lpgwrite-example ( ✖ renderer ). Рендерер должен быть реализован как модуль CommonJS со следующим интерфейсом...
Члены
Имя
Описание
✖ async .render({ workDir, rendererConfig, input, errors })
Выдать выходной документ, используя конфигурацию рендерера и входные данные, предоставленные вызывателем ( ✖  ${LP_HOME}/lpgwrite-example: Пример-генератор одностраничной документации HTML/MD ).
Интерфейс должен экспортироваться модулем renderer через module.exports, например, так:
exports.render = async function render({ workDir, rendererConfig, input, errors }) { ... }
Члены (подробно)
Выдать выходной документ, используя конфигурацию рендерера и входные данные, предоставленные вызывателем ( ✖  ${LP_HOME}/lpgwrite-example: Пример-генератор одностраничной документации HTML/MD ).
Параметры
Имя
Описание
✖ workDir
Строка, путь к корневому каталогу проекта, может использоваться как есть в fs или path.
✖ rendererConfig
Словарь (как Object), фрагмент конфигурации объекта, относящегося к элементу для данного рендерера ( ✖ renders[] ). Рендерер имеет доступ к любым членам объекта конфигурации для своего элемента, но хороший стиль - держать всю специфичную для него конигурацию в выделенном подобъекте, размещённом в словаре под именем, указывающем на рендерер.
✖ input
Входные данные для создания документа. Объект в следующем формате: ✖ Входной формат для рендерера lpgwrite-example .
✖ errors
Массив, коллекция ошибок (как объекты Error JS), возникших в процессе создания документа - сюда следует добавлять ошибки по мере их возникновения.
Возвращает:
нет
Ошибки:
render может выбросить ошибку.
Параметры (подробно)
Строка, путь к корневому каталогу проекта, может использоваться как есть в fs или path.
Словарь (как Object), фрагмент конфигурации объекта, относящегося к элементу для данного рендерера ( ✖ renders[] ). Рендерер имеет доступ к любым членам объекта конфигурации для своего элемента, но хороший стиль - держать всю специфичную для него конигурацию в выделенном подобъекте, размещённом в словаре под именем, указывающем на рендерер.
Входные данные для создания документа. Объект в следующем формате: ✖ Входной формат для рендерера lpgwrite-example .
Массив, коллекция ошибок (как объекты Error JS), возникших в процессе создания документа - сюда следует добавлять ошибки по мере их возникновения.
Методы
async .render({ workDir, rendererConfig, input, errors })
Содержит данные документа для вывода. Словарь (как Object) со следующими членами...
Члены
Имя
Описание
✖ .toc[]
Массив элементов для оглавления. Каждый элемент - словарь (как Object) со следующими членами...
✖ .itemsByUid[uid]
Словарь по UID (строке). Те же элементы, что в ✖ .items[] , развёрнутые в один плоский список, но с ассоциативным ключом по UID ( ✖ .uid ).
✖ .items[]
Массив элементов для показа, упорядоченные в предполагаемом порядке показа при нахождении на общей странице. Каждый элемент - словарь (как Object) со следующими членами...
Члены (подробно)
Массив элементов для оглавления. Каждый элемент - словарь (как Object) со следующими членами...
Члены
Имя
Описание
✖ .title
Строка, человекочитаемый заголовок элемента.
✖ .uid
Строка, UID элемента (ключ в ✖ .itemsByUid[uid] ).
✖ .subEntries[]
Массив (не null, всегда по крайней мере пустой), вложенные элементы данного элемента оглавления. Каждый элемент имеет такую же структуру, как корневой элемент .toc[], включая следующий уровень .subEntries (и т. д.)
Элементы упорядочены в соответствии с предполагаемым порядком показа.
Члены (подробно)
Строка, человекочитаемый заголовок элемента.
Строка, UID элемента (ключ в ✖ .itemsByUid[uid] ).
Массив (не null, всегда по крайней мере пустой), вложенные элементы данного элемента оглавления. Каждый элемент имеет такую же структуру, как корневой элемент .toc[], включая следующий уровень .subEntries (и т. д.)
Элементы упорядочены в соответствии с предполагаемым порядком показа.
Словарь по UID (строке). Те же элементы, что в ✖ .items[] , развёрнутые в один плоский список, но с ассоциативным ключом по UID ( ✖ .uid ).
Массив элементов для показа, упорядоченные в предполагаемом порядке показа при нахождении на общей странице. Каждый элемент - словарь (как Object) со следующими членами...
Члены
Имя
Описание
✖ .uid
Строка, UID элемента, может использоваться для доступа к данному элементу через ✖ .itemsByUid[uid] .
✖ .modelBasic[]
Базовая часть модели элемента, видимая в кратком режиме показа, должна показываться всегда. Массив (не null, всегда по крайней мере пустой) элементов в порядке показа, каждый элемент может содержать некоторые из следующих членов...
✖ .modelMore[]
Дополнительная часть модели элемента для показа в полном режиме, в дополнение к базовой части. Массив (не null, всегда по крайней мере пустой), который может содержать все те же элементы, что и ✖ .modelBasic[] .
Члены (подробно)
Строка, UID элемента, может использоваться для доступа к данному элементу через ✖ .itemsByUid[uid] .
Базовая часть модели элемента, видимая в кратком режиме показа, должна показываться всегда. Массив (не null, всегда по крайней мере пустой) элементов в порядке показа, каждый элемент может содержать некоторые из следующих членов...
Члены
Имя
Описание
✖ .itemTitle
Строка. Если этот член присутствует, это означает, что данный элемент списка - заголовок элемента, и содержит человекочитаемый текст заголовка элемента.
✖ .uid
Строка, определён, только если присутствует ✖ .itemTitle . UID целевого элемента (точнее, того, от которого заголовок), тот же самый, что в ✖ .uid .
✖ .item
Строка. Если этот член присутствует, это означает, что данный элемент - место, в которое следует вывести вложенный FDOM-элемент, и содержит UID выводимого элемента, тот же, что ✖ .uid . Обратите внимание, что один и тот же элемент (с тем же UID) может появляться в документе несколько раз, и одно из таких мест появления будет указано как домашнее (основное) место размещения данного элемента - проверяйте флаг ✖ .isHomeLocation , если это понятие имеет значение в рамках выводимого формата документа.
✖ .isHomeLocation
Булевой, определён, только если присутствует ✖ .item . Если true, то это место, предлагаемое как домашнее место размещения элемента. У каждого элемента есть только одно домашнее место размещения.
✖ .printType
Строка, определён, только если присутствует ✖ .item . Определяет предлагаемый режим показа для FDOM-элемента, вставляемого в данное место. Может быть один из:
  • "brief": показывать только краткую часть данных элемента
  • "full": показывать данные элемента полностью
✖ .text
Строка. Если этот член присутствует, то это фрагмент текста в Markdown. Некоторые HTML-подобные теги, регистро-зависимые, должны интерпретироваться как встроенные LP-ссылки (текстовые свойства закодированы по HTML):
  • <lp-src file="filename"></lp-src> (сам тег внутри не содержит текста, file закодирован по HTML): встроенная ссылка на исходный входной файл LP-формата, только без .lpinput-расширения. Присутствует всегда, как его интерпретировать или игнорировать - на усмотрение рендерера.
  • <lp-ref uid="UID" text="показываемый текст"></lp-ref> (сам тег внутри не содержит текста, file закодирован по HTML): встроенная LP-ссылка на элемент (такая, как задаётся по <#ref ...#>). UID тот же, что в ✖ .uid . Показываемый текст может быть пустым, в этом случае рекомендуется использовать заголовок элемента ( ✖ title ).
✖ .openSection
Строка. Если этот член присутствует, это означает, что данный элемент списка открывает озаглавленную секцию, и член содержит идентификатор секции для соответствия идущему позже ✖ .closeSection
✖ .closeSection
Строка. Если этот член присутствует, то данный элемент списка закрывает озаглавленную секцию, и член содержит идентификатор секции для закрытия, соответствующий шедшему ранее ✖ .openSection .
✖ .title
Строка, определён, только если присутствует ✖ .openSection . Заголовок открываемой секции.
✖ .table
Если этот член присутствует, это означает блок с таблицей. Объект со следующими членами-свойствами...
✖ .list[][]
Массив массивов строк. Если этот член присутствует, это означает, что данный элемент списка - это список (одноуровневый, ненумерованный). Каждый элемент массива - элемент списка, каждый под-элемент - Markdown-текст (аналогично ✖ .text ), предполагается, что под-элементы собраны в одну строку, следуя друг за другом в порядке следования в массиве.
Члены (подробно)
Строка. Если этот член присутствует, это означает, что данный элемент списка - заголовок элемента, и содержит человекочитаемый текст заголовка элемента.
Строка, определён, только если присутствует ✖ .itemTitle . UID целевого элемента (точнее, того, от которого заголовок), тот же самый, что в ✖ .uid .
Строка. Если этот член присутствует, это означает, что данный элемент - место, в которое следует вывести вложенный FDOM-элемент, и содержит UID выводимого элемента, тот же, что ✖ .uid . Обратите внимание, что один и тот же элемент (с тем же UID) может появляться в документе несколько раз, и одно из таких мест появления будет указано как домашнее (основное) место размещения данного элемента - проверяйте флаг ✖ .isHomeLocation , если это понятие имеет значение в рамках выводимого формата документа.
Булевой, определён, только если присутствует ✖ .item . Если true, то это место, предлагаемое как домашнее место размещения элемента. У каждого элемента есть только одно домашнее место размещения.
Строка, определён, только если присутствует ✖ .item . Определяет предлагаемый режим показа для FDOM-элемента, вставляемого в данное место. Может быть один из:
  • "brief": показывать только краткую часть данных элемента
  • "full": показывать данные элемента полностью
Строка. Если этот член присутствует, то это фрагмент текста в Markdown. Некоторые HTML-подобные теги, регистро-зависимые, должны интерпретироваться как встроенные LP-ссылки (текстовые свойства закодированы по HTML):
  • <lp-src file="filename"></lp-src> (сам тег внутри не содержит текста, file закодирован по HTML): встроенная ссылка на исходный входной файл LP-формата, только без .lpinput-расширения. Присутствует всегда, как его интерпретировать или игнорировать - на усмотрение рендерера.
  • <lp-ref uid="UID" text="показываемый текст"></lp-ref> (сам тег внутри не содержит текста, file закодирован по HTML): встроенная LP-ссылка на элемент (такая, как задаётся по <#ref ...#>). UID тот же, что в ✖ .uid . Показываемый текст может быть пустым, в этом случае рекомендуется использовать заголовок элемента ( ✖ title ).
Строка. Если этот член присутствует, это означает, что данный элемент списка открывает озаглавленную секцию, и член содержит идентификатор секции для соответствия идущему позже ✖ .closeSection
Строка. Если этот член присутствует, то данный элемент списка закрывает озаглавленную секцию, и член содержит идентификатор секции для закрытия, соответствующий шедшему ранее ✖ .openSection .
Строка, определён, только если присутствует ✖ .openSection . Заголовок открываемой секции.
Если этот член присутствует, это означает блок с таблицей. Объект со следующими членами-свойствами...
Члены
Имя
Описание
✖ .headers[]
Массив заголовков колонок, в порядке показа колонок. Каждый элемент - строка с заголовком колонки как Markdown-текстом (аналогично ✖ .text ).
✖ .rows[]
Массив строк таблицы, в порядке показа. Каждый элемент списка - массив колонок, в порядке показа колонок, в котором каждый под-элемент - строка с текстом колонки как Markdown-текстом (аналогично ✖ .text ).
Члены (подробно)
Массив заголовков колонок, в порядке показа колонок. Каждый элемент - строка с заголовком колонки как Markdown-текстом (аналогично ✖ .text ).
Массив строк таблицы, в порядке показа. Каждый элемент списка - массив колонок, в порядке показа колонок, в котором каждый под-элемент - строка с текстом колонки как Markdown-текстом (аналогично ✖ .text ).
Массив массивов строк. Если этот член присутствует, это означает, что данный элемент списка - это список (одноуровневый, ненумерованный). Каждый элемент массива - элемент списка, каждый под-элемент - Markdown-текст (аналогично ✖ .text ), предполагается, что под-элементы собраны в одну строку, следуя друг за другом в порядке следования в массиве.
Дополнительная часть модели элемента для показа в полном режиме, в дополнение к базовой части. Массив (не null, всегда по крайней мере пустой), который может содержать все те же элементы, что и ✖ .modelBasic[] .
Переводчик вызывается из генератора ✖  ${LP_HOME}/lpgwrite-i18n-assist: Вспомогательный генератор для помощи в локализации . Его задача - вернуть начальный вариант перевода для заданной строки, соответственно переданной информации о локали. Задаётся полем translator в элементе конфигурации задания для lpgwrite-example (lpgwrite-example/translator). Транслятор должен быть реализован как модуль CommonJS со следующим интерфейсом...
Члены
Имя
Описание
✖ async .translate(str, translatorArgs)
Выполнить перевод и вернуть результат.
Интерфейс должен экспортироваться модулем translator через module.exports, например, так:
exports.translate = async function translate(str, translatorArgs) { ... }
Члены (подробно)
Выполнить перевод и вернуть результат.
Параметры
Имя
Описание
✖ str
Строка, собственно строка для перевода. Предполагается Markdown-текстов, с возможным содержанием следующих HTML-подобных тегов:
  • <lp-ref item="FDOM имя">альт. текст ссылки</lp-ref>: встроенная LP-ссылка. альт. текст ссылки может быть переведён (обратите внимание, что он закодирован по HTML), остальную часть следует оставить как есть.
  • <lp-tag>...текст...</lp-tag>: пользовательский тег разметки (из числа ✖ extraTags ). ...текст... - закодированный по HTML JSON-код объекта, и в таком же формате должен отстаться после перевода. Какие бывают пользовательские теги и что в них следует переводить - на усмотрение переводчика. Фрагменты объекта, для которых это не ясно, следует оставить в исходном виде.
✖ translatorArgs
Значение аргумента, заданное для переводчика в ✖ translatorArgs . Передаётся как соответствующий объект конфигурации, как он задан в конфигурационном файле.
Возвращает:
Переведённый ✖ str , с учётом отмеченных там моментов. Рекомендуется также добавить к переведённой строке отметку о необходимости вычитки.
Ошибки:
translate может выбросить ошибку.
Параметры (подробно)
Строка, собственно строка для перевода. Предполагается Markdown-текстов, с возможным содержанием следующих HTML-подобных тегов:
  • <lp-ref item="FDOM имя">альт. текст ссылки</lp-ref>: встроенная LP-ссылка. альт. текст ссылки может быть переведён (обратите внимание, что он закодирован по HTML), остальную часть следует оставить как есть.
  • <lp-tag>...текст...</lp-tag>: пользовательский тег разметки (из числа ✖ extraTags ). ...текст... - закодированный по HTML JSON-код объекта, и в таком же формате должен отстаться после перевода. Какие бывают пользовательские теги и что в них следует переводить - на усмотрение переводчика. Фрагменты объекта, для которых это не ясно, следует оставить в исходном виде.
Значение аргумента, заданное для переводчика в ✖ translatorArgs . Передаётся как соответствующий объект конфигурации, как он задан в конфигурационном файле.
Методы
async .translate(str, translatorArgs)
Этот записыватель сохраняет некоторое представление FDOM в виде JSON-файла. Соответствующий считыватель - ✖ logipard/lpgread-basic-json.js . Использование этого считывателя в элементе стадии компиляции включается через writer: "${LP_HOME}/lpcwrite-basic-json" $ в ✖ writer .
Этот записыватель использует дополнительный член lpcwrite-basic-json в элементе конфигурации стадии компиляции:
{
	...
	writer: "${LP_HOME}/lpcwrite-basic-json" $, // копировать дословно!
	lpcwrite-basic-json: {
		outFile: ...,
		extraTags: { // optional
			...
		}
	}
}
Дополнительный член в элементе ✖ items[] , содеращий конфигурацию для lpcwrite-basic-json.
Члены
Имя
Описание
✖ extraTags
Объект, представляющий словарь формата tagName: tagContentType. Описывает дополнительные теги, которые записыватель будет распознавать во входном потоке. Они будут добавлены в скомпилированное JSON-представление модели как объекты customTag (см. ✖ .content [lpgread-basic-json] ). Пользовательские теги, не описанные здесь, будут пропускаться с выдачей предупреждения. Обратите внимание, что имена тегов не чувствительны к регистру (приводятся к нижнему).
✖ outFile
Строка. Путь к выходному JSON-файлу (если путь не абсолютный, то относительно корневого каталога проекта). Файл каждый раз перезаписывается полностью, но элементы представления модели, уже существовавшие в нём, по возможности обновляются, а не перестраиваются с нуля, стараясь сохранить фрагменты данных, для которых источники по факту не изменялись.
Члены (подробно)
Объект, представляющий словарь формата tagName: tagContentType. Описывает дополнительные теги, которые записыватель будет распознавать во входном потоке. Они будут добавлены в скомпилированное JSON-представление модели как объекты customTag (см. ✖ .content [lpgread-basic-json] ). Пользовательские теги, не описанные здесь, будут пропускаться с выдачей предупреждения. Обратите внимание, что имена тегов не чувствительны к регистру (приводятся к нижнему).
Формат словаря следующий:
extraTags: {
	"tagName1": <tag1 content type>, // строка
	"tagName2": <tag2 content type>, // строка
	...
}
Свойства
Встроенный файл. Объект customTag будет { name: "<tag-name>", file: "data:;base64,...двоичные данные файла, закодированные в base64 data URL-схеме..." }
Текст. Объект customTag будет { name: "<tag-name>", text: "...содержимое тега, трактуемое как обычный текст..." }
Строка. Путь к выходному JSON-файлу (если путь не абсолютный, то относительно корневого каталога проекта). Файл каждый раз перезаписывается полностью, но элементы представления модели, уже существовавшие в нём, по возможности обновляются, а не перестраиваются с нуля, стараясь сохранить фрагменты данных, для которых источники по факту не изменялись.
Данный записыватель стадии генерации выдаёт человекочитаемую документацию, извлекаемую и структурируемую в соответствии с программой документа (см. ✖ Программа документа для ${LP_HOME}/lpgwrite-example ). lpgwirte-example сам по себе определяет только общую, формато-независимую структуру документа, а вывод документа в конретном целевом формате доверяется под-плагину, называемому рендерер. Как можно понять из заголовка, имеются встроенные рендереры для одностраничных HTML и MD документов, но на самом деле пользователь может создавать и задействовать и свои собственные рендереры.
lpgwrite-example добавляет поверх FDOM-терминологии несколько дополнительных конвенций:
  • член с именем %title содержит человекочитаемый заголовок элемента. Если члена %title нет, то заголовок предполагается таким же, как краткое имя элемента. Обычно элемент следует за открывающим фрагментом элемента, в виде <#./%title: Your title#> (обратите внимание, что поначалу может часто напрашиваться пропуск ./ или : - это ошибка; привыкнуть к правильному написанию - вопрос опыта и некоторой практики).
  • текстовый контент; за исключением особых значений и LP-тегов, он считается текстов в Markdown-формате (см. шпаргалку по MD, например, здесь)
  • первый параграф в текстовом контенте элемента, если это не элемент списка или невстроенный блок кода, считается краткой информацией. Вместе с остальной частью текстового контента элемента он составляет полную информацию.
Данный записыватель использует дополнительный член lpgwrite-example в элементе конфигурации стадии генерации ( ✖  ${LP_HOME}/lpgwrite-example: Пример-генератор одностраничной документации HTML/MD ):
{
	...
	writer: "${LP_HOME}/lpgwrite-example" $, // копировать дословно!
	lpgwrite-example: {
		trace: ...,
		program: [...],
		renders: [
			{
				docModel: ...,
				renderer: ...,
				... // дополнительная рендереро-специфичная конфигуркция
			},
			...
		]
	}
}
Члены объекта lpgwrite-example, как перечислено далее:
Члены
Имя
Описание
✖ renders[]
Список под-заданий по собственно выводу документа. В дополнение к нижеперечисленным членам, может содержать также дополнительные члены с рендереро-специфичными фрагментами конфигурации.
✖ trace
Булевой, необязательно. Если true, то обработка программы документа будет проходить с выводом дополнительных логов, позволяя более подробную трассировку того, что делается, а что не делается.
✖ program[]
Массив инструкций программы документа (см. ✖ Программа документа для ${LP_HOME}/lpgwrite-example )
Члены (подробно)
Список под-заданий по собственно выводу документа. В дополнение к нижеперечисленным членам, может содержать также дополнительные члены с рендереро-специфичными фрагментами конфигурации.
Члены
Имя
Описание
✖ renderer
Строка, путь к модулю рендерера. Рендерер должен следовать ✖ Интерфейс для рендерера lpgwrite-example . В составе Logipard имеются следующие встроенные рендереры...
  • HTML ( ✖  ${LP_HOME}/lpgwrite-example-render-html: рендерер HTML для генератора lpgwrite-example )
  • Markdown ( ✖  ${LP_HOME}/lpgwrite-example-render-md: рендерер Markdown для генератора lpgwrite-example )
✖ docModel
Строка, используемая модель документа. Ссылается на docModel в программе документа, конкретно по значению name в ✖ Определение модели документа .
Члены (подробно)
Строка, путь к модулю рендерера. Рендерер должен следовать ✖ Интерфейс для рендерера lpgwrite-example . В составе Logipard имеются следующие встроенные рендереры...
  • HTML ( ✖  ${LP_HOME}/lpgwrite-example-render-html: рендерер HTML для генератора lpgwrite-example )
  • Markdown ( ✖  ${LP_HOME}/lpgwrite-example-render-md: рендерер Markdown для генератора lpgwrite-example )
Строка, используемая модель документа. Ссылается на docModel в программе документа, конкретно по значению name в ✖ Определение модели документа .
Булевой, необязательно. Если true, то обработка программы документа будет проходить с выводом дополнительных логов, позволяя более подробную трассировку того, что делается, а что не делается.
Массив инструкций программы документа (см. ✖ Программа документа для ${LP_HOME}/lpgwrite-example )
Данный генератор предназначен для содействия в переводе на разные языки в рамках представления FDOM в виде JSON-файлов, скомпилированных ✖ ${LP_HOME}/lpcwrite-basic-json: Записыватель FDOM в JSON-файл . Идея в следующем: текст для перевода извлекается в человекочитаемые и человекоредактируемые промежуточные файлы переводов, и далее поддерживает переведённые файлы-зеркала исходного JSON-представления FDOM на основе этого перевода в актуальном относительно него состоянии. Промежуточные файлы, в свою очередь, поддерживаются в состоянии, актуальном относительно самогО исходного JSON-представления, и их можно хранить в системе контроля версий вместе с кодом проекта. Начальный (автоматический) перевод доверяется под-плагину, называемому переводчик. Предоставляется встроенный заглушечный переводчик lpgwrite-i18n-assist-trn-none, но пользователь может создавать и задействовать свои собственные переводчики.
lpgwrite-i18n-assist образует своего рода "субконвейер": его вывод является промежуточным, и предполагается, что его используют уже непосредственно генераторы документов, которые следуют за ним в списке элементов lp-generate.
lpgwrite-i18n-assist добавляет поверх FDOM следующую конвенцию:
  • член %title, если есть, содержит человекочитаемый заголовок для его родительского элемента (аналогично ✖ ${LP_HOME}/lpgwrite-example )
  • если к члену %title добавлен тег %noloc, этот заголовок полагаеся непереводимым - он не будет включён в промежуточный файл, и будет фигурировать в переведённом FDOM-файле без изменений
Например: <#./%title: Этот заголовок будет переведён#>, и <#./%title %noloc: Этот заголовок не будет переведён#>. Эта конвенция не противоречит аналогичной в ✖ ${LP_HOME}/lpgwrite-example и органично её дополняет.
Этот рендерер использует дополнительный член lpgwrite-i18n-assist в конфигурации для элемента генерации:
...
{
	inFile: ...,
	writer: "${LP_HOME}/lpgwrite-i18n-assist" $, // копировать дословно!
	lpgwrite-i18n-assist: {
		translator: ...,
		items: [
			{
				outFile: ...,
				interimFile: ...,
				interimFileCharset: ...,
				translatorArgs: ...
			}
		]
	}
},
...
Члены объекта lpgwrite-i18n-assist, в следующем составе:
Члены
Имя
Описание
✖ items[]
Массив. Элемнты для обработки в этом задании lpgwrite-i18n-assist, используя один и тот же переводчик, заданный в ✖ translator . Каждый элемент в items[] - объект следующего вида:
✖ translator
Строка, путь к модулю переводчика, абсолютный или относительно корня проекта. Переводчик должен следовать ✖ Интерфейс для переводчика lpgwrite-i18n-assist . Logipard включает встроенный переводчик-заглушку ✖  ${LP_HOME}/lpgwrite-i18n-assist-trn-none: Переводчик-заглушка для генератора lpgwrite-i18n-assist .
Члены (подробно)
Массив. Элемнты для обработки в этом задании lpgwrite-i18n-assist, используя один и тот же переводчик, заданный в ✖ translator . Каждый элемент в items[] - объект следующего вида:
Члены
Имя
Описание
✖ translatorArgs
Произвольное значение JSON/LPSON, необязательно (по умолчанию null). Объект или значение, которое будет передано переводчику в метод ✖ async .translate(str, translatorArgs) .
✖ outFile
Строка. Путь к выходному файлу JSON FDOM с переведённым текстом, абсолютный или относительно корня проекта. Предполагается наличие расширения .json.
✖ interimFile
Строка. Путь к промежуточному файлу перевода, абсолютный или относительно корня проекта. В сущности, это почти обычный текстовый файл, поэтому предполагается наличие расширения .txt.
✖ interimFileCharset
Строка, необязательно (по умолчанию "utf-8"). Кодировка для использования в промежуточном файле.
Члены (подробно)
Произвольное значение JSON/LPSON, необязательно (по умолчанию null). Объект или значение, которое будет передано переводчику в метод ✖ async .translate(str, translatorArgs) .
Строка. Путь к выходному файлу JSON FDOM с переведённым текстом, абсолютный или относительно корня проекта. Предполагается наличие расширения .json.
Строка. Путь к промежуточному файлу перевода, абсолютный или относительно корня проекта. В сущности, это почти обычный текстовый файл, поэтому предполагается наличие расширения .txt.
Строка, необязательно (по умолчанию "utf-8"). Кодировка для использования в промежуточном файле.
Строка, путь к модулю переводчика, абсолютный или относительно корня проекта. Переводчик должен следовать ✖ Интерфейс для переводчика lpgwrite-i18n-assist . Logipard включает встроенный переводчик-заглушку ✖  ${LP_HOME}/lpgwrite-i18n-assist-trn-none: Переводчик-заглушка для генератора lpgwrite-i18n-assist .
Промежуточный файл выглядит, следующим образом (например)...
...
## Item: /domain.logipard/interfaces/compile/%title
# lp-stage-plugin-ifs.lp-txt
/ "Para:Ev+yL9F/vTiMmuKTf0MCOtkPdxbajKJGYcTegdUiEhKX4g0C7A+PMVsfHPOVu90ZRrksqgrsekUutwoGUA72zw=="
Интерфейсы, относящиеся к стадии компиляции
\ "Para:Ev+yL9F/vTiMmuKTf0MCOtkPdxbajKJGYcTegdUiEhKX4g0C7A+PMVsfHPOVu90ZRrksqgrsekUutwoGUA72zw=="

## Item: /domain.logipard/interfaces/compile
## Item: /domain.logipard/interfaces/compile/writer-toolkit/%title
# internal/lp-compile-tools.js
/ "Para:vGDelX4EnoLn07hY9QgDuASeK7cUvLxrere0vuqNEu/pOGNVoVfpoUEsEtI0IW/gLrN3w2BHhUdktg51eEeEKg=="
Инструментарий записывателя стадии компиляции для процессора пользовательских тегов
\ "Para:vGDelX4EnoLn07hY9QgDuASeK7cUvLxrere0vuqNEu/pOGNVoVfpoUEsEtI0IW/gLrN3w2BHhUdktg51eEeEKg=="
...
Строки / "Para:..."...\ "Para:..." - ограничители, между которыми располагается фрагмент переведённого контента, который можно редактировать вручную. lpgwrite-i18n-assist сохраняет эти фрагменты в текущем виде, пока не изменятся соответствующие фрагменты контента в оригинале (но может менять их положение в файле при изменении FDOM-структуры).
Для удобства сопровождения и уменьшения затрат на повторный перевод при изменении контента lpgwrite-i18n-assist соблюдает разбиение на уровне одного параграфа или элемента списка на фрагмент, группирует их по элементам модели и с сохранением взаимного порядка.
Не редактируйте сами строки Para или коды в них - это теги для сопоставления фрагментам соответствующего исходного контента. Строки вне этих фрагментов следует считать комментариями для удобства навигации, они могут меняться без каких-либо гарантий.
Этот считыватель читает FDOM-представление из JSON-файла, скомпилированного через ✖ ${LP_HOME}/lpcwrite-basic-json: Записыватель FDOM в JSON-файл . Он следует рекомендуемой схеме для считывателя модели: ✖ Рекомендуемый интерфейс для считывателя скомпилированного FDOM .
Члены
Имя
Описание
✖ async loadFromFile(filePath [, extractSrcFile])
Загрузить модель в память и открыть для чтения в терминах FDOM ( ✖ Запросы FDOM ). Функция на уровне модуля.
Пример использования (предполагая, что Logipard установлен глобально или как node-модуль):
const { loadFromFile } = require('logipard/lpgread-basic-json');

async main() { // API загрузчика асинхронное

	var reader = await loadFromFile("your-fdom.json");

	// полагая, что ваша модель содержит элементы с такими именами, как ниже...
	reader.nameAlias("domain.your.program", "M"); // задать алиас имени
	var classesSection = reader.item("M/classes"); // <Item>, domain.your.program/classes
	var classA = reader.item(classesSection, "classA"); // <Item>, domain.your.program/classes/classA

	// найдём элементы для всех классов, которые расширяет A, и напечатаем их заголовки
	var extends = reader.item("%extends"); // %extends
	var queryCtxItemsExtByA = reader.newQueryContext(); // <QueryContext>
	var itemsExtByA = queryCtxItemsExtByA.with(classA) // или .with(queryCtxItemsExtByA.collection(classA))
		.query({ inMembersThat: { named: "^%extends$" }, recursive: true, query: { tagsThat: true }})
		.teardownCollection(); // itemsExtByA = <Collection>

	for (var itemExtByA of itemsExtByA) { // itemExtByA = <Item>
		console.log(reader.item(itemExtByA, "%title").content[0]); // полагая, что все элементы имеют члены %title с чисто текстовым контентом
	}
}
Члены (подробно)
Загрузить модель в память и открыть для чтения в терминах FDOM ( ✖ Запросы FDOM ). Функция на уровне модуля.
Параметры
Имя
Описание
✖ filePath
Строка. Путь (такой же, как для методов fs Node.JS) к JSON-файлу с представлением FDOM.
✖ extractSrcFile
Булевой, необязательный параметр (по умолчанию false). Если true, то в текст будут вставлены имена файлов-источников LP. Полезно при чтении с диагностическими целями, или имея таковые в виду.
Возвращает:
Дескриптор считывателя, ✖ <Reader> [lpgread-basic-json]
Ошибки:
loadFromFile может выбросить ошибку.
Использование:
const { loadFromFile } = require('logipard/lpgread-basic-json.js');
var reader = await loadFromFile("my-fdom-file.json");
Параметры (подробно)
Строка. Путь (такой же, как для методов fs Node.JS) к JSON-файлу с представлением FDOM.
Булевой, необязательный параметр (по умолчанию false). Если true, то в текст будут вставлены имена файлов-источников LP. Полезно при чтении с диагностическими целями, или имея таковые в виду.
Методы
async loadFromFile(filePath [, extractSrcFile])
Объект-считыватель, основной дескриптор для доступа к загруженному FDOM.
Члены
Имя
Описание
✖ .item([itemRelTo,] name)
Получить элемент по его полному или относительному FDOM-имени. Аналогично ✖ .item([baseItem ,] name) , но не поддерживает алиасы, т. к. используется без контекста.
✖ .itemByUid(uid)
Вернуть элемент по UID (см. ✖ .uid ). Поскольку это специфичный для данного считывателя метод, не предусмотренный терминологией FDOM, он может вернуть null для несуществующего элемента.
✖ .newQueryContext()
Создать новый объект контекста запроса.
Члены (подробно)
Получить элемент по его полному или относительному FDOM-имени. Аналогично ✖ .item([baseItem ,] name) , но не поддерживает алиасы, т. к. используется без контекста.
Параметры
Имя
Описание
✖ itemRelTo
Необязательный параметр. Если задан, то он обозначает элемент, путь к котрому принимается за базовый для ✖ name , который в этом случае считается относительным путём. Может быть одним из следующего:
  • строка: полное имя базового элемента, заданное строкой
  • массив: полное имя базового элемента, разбитое на массив коротких имён
  • ✖ <Item> [lpgread-basic-json] : элемент, заданный непосредственно представляющим его объектом
✖ name
Имя элемента, полное, если itemRelTo не задан, или относительное него, если задан.
Возвращает:
элемент, как ✖ <Item> [lpgread-basic-json] . Обратите внимание, что, согласно парадигме запросов в FDOM, это значение никогда не бывает null: если элемент по факту не существует, то возвращается объект нулевого элемента.
Параметры (подробно)
Необязательный параметр. Если задан, то он обозначает элемент, путь к котрому принимается за базовый для ✖ name , который в этом случае считается относительным путём. Может быть одним из следующего:
  • строка: полное имя базового элемента, заданное строкой
  • массив: полное имя базового элемента, разбитое на массив коротких имён
  • ✖ <Item> [lpgread-basic-json] : элемент, заданный непосредственно представляющим его объектом
Имя элемента, полное, если itemRelTo не задан, или относительное него, если задан.
Вернуть элемент по UID (см. ✖ .uid ). Поскольку это специфичный для данного считывателя метод, не предусмотренный терминологией FDOM, он может вернуть null для несуществующего элемента.
Параметры
Имя
Описание
✖ uid
Строка. UID элемента, как возвращается его свойством .uid.
Возвращает:
✖ <Item> [lpgread-basic-json] , или null.
Параметры (подробно)
Строка. UID элемента, как возвращается его свойством .uid.
Создать новый объект контекста запроса.
Возвращает:
✖ <Context> [lpgread-basic-json]
Методы
.item([itemRelTo,] name)
.itemByUid(uid)
.newQueryContext()
Элемент FDOM, в реализации ✖ logipard/lpgread-basic-json.js .
Расширяет (является)
  • ✖ <Item>
Члены
Имя
Описание
✖ .content [lpgread-basic-json]
Свойство только для чтения, массив элементов контента. Реализует ✖ .content в варианте, специфичном для ✖ logipard/lpgread-basic-json.js .
✖ .uid
Свойство только для чтения, строка. UID элемента в JSON-представлении модели, предоставляемый как ключ для быстрого доступа.
✖ .toString()
Стандартное преобразование в строку JS
Члены из расширеяемых
Имя
Описание
✖ .content
Свойство только для чтения. Контент элемента (текст, внутритекстовые ссылки, и всё прочее, что поддерживает модель, обслуживаемая данным считывателем.)
✖ .name
Свойство только для чтения, строка. Полный путь элемента (без именных алиасов).
✖ .shortName
Свойство только для чтения, строка. Краткое имя элемента (последний сегмент полного имени-пути).
✖ .tags
Свойство только для чтения. Коллекция тегов элемента.
✖ .members
Свойство только для чтения. Коллекция членов элемента.
✖ .isNull
Свойство только для чтения, булевое. Проверка, что элемент пустой (true) или нет (false).
✖ .parent
Свойство только для чтения, ✖ <Item> . Возвращает родительский элемент (тот, членом которого является данный). Для корневого элемента возвращает null (именно null-значение, а не нулевой элемент).
✖ .isConditionTrue(lpqCtx, condSpec)
Проверить, что элемент удовлетворяет некоторому условию; проверка должна делаться в рамках заданного контекста запроса (чтобы иметь возможность использовать алиасы условий и коллекций).
Члены (подробно)
Свойство только для чтения, массив элементов контента. Реализует ✖ .content в варианте, специфичном для ✖ logipard/lpgread-basic-json.js .
Каждый элемент - одно из следующего:
  • строка: чисто текстовый фрагмент содержимого, интерпретация на усмотрение пользователя соответственно конкретному контексту пользовательского уровня модели.
  • объект { ref: <Item>, text: string } (ref - это объект ✖ <Item> [lpgread-basic-json] ): внутритекстовая ссылка на FDOM-элемент
  • объект { customTag: object }: пользовательский тег, как его записал ✖ ${LP_HOME}/lpcwrite-basic-json: Записыватель FDOM в JSON-файл
Свойство только для чтения, строка. UID элемента в JSON-представлении модели, предоставляемый как ключ для быстрого доступа.
Стандартное преобразование в строку JS
Члены из расширяемых (подробно)
.content
.name
.shortName
.tags
.members
.isNull
.parent
.isConditionTrue(lpqCtx, condSpec)
Методы из расширяемых
.isConditionTrue(lpqCtx, condSpec)
FDOM-коллекция, в реализации ✖ logipard/lpgread-basic-json.js .
Расширяет (является)
  • ✖ <Item>
Члены
Имя
Описание
✖ .toString()
Стандартное преобразование в строку JS
Члены из расширеяемых
Имя
Описание
✖ .content
Свойство только для чтения. Контент элемента (текст, внутритекстовые ссылки, и всё прочее, что поддерживает модель, обслуживаемая данным считывателем.)
✖ .name
Свойство только для чтения, строка. Полный путь элемента (без именных алиасов).
✖ .shortName
Свойство только для чтения, строка. Краткое имя элемента (последний сегмент полного имени-пути).
✖ .tags
Свойство только для чтения. Коллекция тегов элемента.
✖ .members
Свойство только для чтения. Коллекция членов элемента.
✖ .isNull
Свойство только для чтения, булевое. Проверка, что элемент пустой (true) или нет (false).
✖ .parent
Свойство только для чтения, ✖ <Item> . Возвращает родительский элемент (тот, членом которого является данный). Для корневого элемента возвращает null (именно null-значение, а не нулевой элемент).
✖ .isConditionTrue(lpqCtx, condSpec)
Проверить, что элемент удовлетворяет некоторому условию; проверка должна делаться в рамках заданного контекста запроса (чтобы иметь возможность использовать алиасы условий и коллекций).
Члены (подробно)
Стандартное преобразование в строку JS
Члены из расширяемых (подробно)
.content
.name
.shortName
.tags
.members
.isNull
.parent
.isConditionTrue(lpqCtx, condSpec)
Методы из расширяемых
.isConditionTrue(lpqCtx, condSpec)
Объект скомпилированного FDOM-запроса, в реализации ✖ logipard/lpgread-basic-json.js .
Расширяет (является)
  • ✖ <Query>
Контекст FDOM-запроса, в реализации ✖ logipard/lpgread-basic-json.js .
Расширяет (является)
  • ✖ <QueryContext>
Члены
Имя
Описание
✖ .itemByUid(uid)
Вернуть элемент по UID (см. ✖ .uid ). Поскольку это специфичный для данного считывателя метод, не предусмотренный терминологией FDOM, он может вернуть null для несуществующего элемента.
✖ .clearNameAlias(aliasName)
Очистить алиас элемента, заданный через ✖ .nameAlias(aliasName, item) . Алиас более не является валидным, пока не будет задан заново.
✖ .clearCollectionAlias(collectionAliasName)
Очистить алиас коллекции, заданный через ✖ .collectionAlias(aliasName, ...collectionSpecs) . Алиас более не является валидным, пока не будет задан заново.
✖ .clearQueryAlias(queryAliasName)
Очистить алиас запроса, заданный ✖ .queryAlias(aliasName, ...querySpecs) . Алиас более не является валидным, пока не будет задан заново.
✖ .clearConditionAlias(conditionAliasName)
Очистить алиас условия, заданный ✖ .conditionAlias(aliasName, condSpec) . Алиас более не является валидным, пока не будет задан заново.
Члены из расширеяемых
Имя
Описание
✖ .nameAlias(aliasName, item)
Задать алиас имени элемента (который должен быть корректным кратким именем), могущий впоследствии использоваться как самостоятельное имя элемента, или как начальный сегмент для другого имени элемента в пределах данного ✖ <QueryContext> . Поведение в случае уже существующего алиаса с таким же именем зависит от реализации.
✖ .collectionAlias(aliasName, ...collectionSpecs)
Задать именованный алиас коллекции, который может быть использован позднее для ссылки на эту коллекцию в пределах данного контекста ( ✖ <CollectionSpec> ). Коллекция строится из коллекций, соответствующих каждому элементу списка спецификаций. Алиас - постоянный в пределах данного контекста, в отличие от локального алиаса ( ✖ Задать локальный алиас коллекции ["alias ..."] ).
✖ .queryAlias(aliasName, ...querySpecs)
Задать именованный алиас запроса, который может быть использован позднее для ссылки на этот запрос в пределах данного контекста ( ✖ <QuerySpec> ). Список трактуется как составной запрос.
✖ .conditionAlias(aliasName, condSpec)
Задать именованный алиас условия, который может быть использован позднее для ссылки на это условие в пределах данного контекста ( ✖ <Condition> ).
✖ .item([baseItem ,] name)
Вернуть элемент по заданному имени-пути, полному или относительно заданного базового элемнета. Первый краткоимённый сегмент полного имени может быть алиасом имени, определённым в данном ✖ <QueryContext> .
✖ .collection(...collectionSpecs)
Возвращает коллекцию, заданную списком спецификаций элементов коллекции. Каждый элемент списка - ✖ <CollectionSpec> .
✖ .with(...collectionSpecs)
Задать текущую коллекцию для последующего запроса (вызова ✖ .query(...querySpecs) ). Коллекция строится из коллекций, соответствующих каждому элементу списка. .with, по сути, инициирует цепочку запроса, но может быть использован и в её середине, чтобы заменить текущую коллекцию на некотором шаге.
✖ .query(...querySpecs)
Выполнить запрос, или список запросов, трактуемый как составной запрос, с учётом того, что текущая коллекция задана предыдущим ✖ .with(...collectionSpecs) или получена от предыдущих вызовов .query. Обратите внимание, что полученная коллекция не возвращается немедленно, а становится новой текущей коллекцией.
✖ .teardownCollection()
Завершить запрос и вернуть результат (текущую коллекцию на момент вызова). Сама текущая коллекция сбрасывается, так что следующий запрос должен быть инициализирован повторно, начиная с ✖ .with(...collectionSpecs) .
✖ .currentCollectionAlias(aliasName)
Задать именованный алиас коллекции для текущей коллекции, который может быть использован позже для ссылки на эту коллекцию в пределах данного контекста ( ✖ <CollectionSpec> ). Может быть использован только в процессе запроса (пока имеет значение текущая коллекция), в противном вызов данного метода - ошибка. Это локальный алиас запроса, в отличие от постоянного ( ✖ Задать локальный алиас коллекции ["alias ..."] ).
✖ .compileQuery(...querySpecs)
Скомпилировать запрос в объект-дескриптор, который может быть использован позже для ссылки на этот запрос в пределах данного контекста ( ✖ <QuerySpec> ). Список трактуется как составной запрос.
Члены (подробно)
Вернуть элемент по UID (см. ✖ .uid ). Поскольку это специфичный для данного считывателя метод, не предусмотренный терминологией FDOM, он может вернуть null для несуществующего элемента.
Параметры
Имя
Описание
✖ uid
Строка. UID элемента, как возвращается его свойством .uid.
Возвращает:
✖ <Item> [lpgread-basic-json] , или null.
Параметры (подробно)
Строка. UID элемента, как возвращается его свойством .uid.
Очистить алиас элемента, заданный через ✖ .nameAlias(aliasName, item) . Алиас более не является валидным, пока не будет задан заново.
Параметры
Имя
Описание
✖ aliasName
Строка. Имя алиаса.
Возвращает:
Сам же объект ( ✖ <Context> [lpgread-basic-json] ), что позволяет создавать цепочку вызовов.
Параметры (подробно)
Строка. Имя алиаса.
Очистить алиас коллекции, заданный через ✖ .collectionAlias(aliasName, ...collectionSpecs) . Алиас более не является валидным, пока не будет задан заново.
Параметры
Имя
Описание
✖ collectionAliasName
Строка. Имя алиаса.
Возвращает:
Сам же объект ( ✖ <Context> [lpgread-basic-json] ), что позволяет создавать цепочку вызовов.
Параметры (подробно)
Строка. Имя алиаса.
Очистить алиас запроса, заданный ✖ .queryAlias(aliasName, ...querySpecs) . Алиас более не является валидным, пока не будет задан заново.
Параметры
Имя
Описание
✖ queryAliasName
Строка. Имя алиаса.
Возвращает:
Сам же объект ( ✖ <Context> [lpgread-basic-json] ), что позволяет создавать цепочку вызовов.
Параметры (подробно)
Строка. Имя алиаса.
Очистить алиас условия, заданный ✖ .conditionAlias(aliasName, condSpec) . Алиас более не является валидным, пока не будет задан заново.
Параметры
Имя
Описание
✖ conditionAliasName
Строка. Имя алиаса.
Возвращает:
Сам же объект ( ✖ <Context> [lpgread-basic-json] ), что позволяет создавать цепочку вызовов.
Параметры (подробно)
Строка. Имя алиаса.
Члены из расширяемых (подробно)
.nameAlias(aliasName, item)
.collectionAlias(aliasName, ...collectionSpecs)
.queryAlias(aliasName, ...querySpecs)
.conditionAlias(aliasName, condSpec)
.item([baseItem ,] name)
.collection(...collectionSpecs)
.with(...collectionSpecs)
.query(...querySpecs)
.teardownCollection()
.currentCollectionAlias(aliasName)
.compileQuery(...querySpecs)
Методы
.itemByUid(uid)
.clearNameAlias(aliasName)
.clearCollectionAlias(collectionAliasName)
.clearQueryAlias(queryAliasName)
.clearConditionAlias(conditionAliasName)
Методы из расширяемых
.nameAlias(aliasName, item)
.collectionAlias(aliasName, ...collectionSpecs)
.queryAlias(aliasName, ...querySpecs)
.conditionAlias(aliasName, condSpec)
.item([baseItem ,] name)
.collection(...collectionSpecs)
.with(...collectionSpecs)
.query(...querySpecs)
.teardownCollection()
.currentCollectionAlias(aliasName)
.compileQuery(...querySpecs)
Этот рендерер для ✖ ${LP_HOME}/lpgwrite-example выдаёт документацию в формате одностраничного HTML-документа с инструментами навигации, подобного тому, который вы сейчас читаете.
Этот рендерер использует дополнительный член lpgwrite-example-render-html в конфигурации для элемента генерации ✖ renders[] :
lpgwrite-example: {
	...
	renders: [
		{
			docModel: ...,
			renderer: "${LP_HOME}/lpgwrite-example-render-html" $, // копировать дословно!
			lpgwrite-example-render-html: {
				outFile: ...,
				emitToc: ...,
				inTemplateFile: "logipard-doc.tpl.html",
				cssClasses: {
					// все члены этого объекта не обязательны, и cssClasses может быть опущен вообще целиком
					itemTitle: ...,
					rawTitle: ...,
					paragraph: ...,
					verbatimSpan: ...,
					linkSpan: ...,
					moreSpan: ...,
					elsewhereSpan: ...,
					actionSpan: ...,
					offSiteBlock: ...
				},
				htmlPlaceholder: ...,
				cssPlaceholder: ...,
				extraTokens: {
					TOKEN_ID: "token value",
					ANOTHER_TOKEN_ID: "token value 2",
					...
				},
				localizedKeywords: {
					// настройте эти члены соответственно целевому языку
					SNAPBACK: "Вернуть",
					SNAPBACK_AND_SCROLL: "Вернуть и перейти",
					ELEVATE: "Поднять",
					RESET: "Сбросить",
					ELEVATE_TO: "Поднять до...",
					COPY_ITEM_NAME: "Копировать полное имя модели LP FDOM этого элемента в буфер:",
					ITEM_UNFOLDED_ELSEWHERE: "Элемент развёрнут в другом месте страницы - нажмите, чтобы развернуть здесь...",
					MORE: "Ещё... >>",
					TABLE_OF_CONTENTS: "Содержание"
				},
				addSourceRef: ...
			}
		},
		...
	]
}
Объект lpgwrite-example-render-html внутри соответствующего элемента renders[], включает следующие члены...
Члены
Имя
Описание
✖ outFile
Строка. Путь к выходному файлу документа (.html) для записи, абсолютный или относительно корня проекта.
✖ emitToc
Булевой, необязательно (по умолчанию true). Если true, то рендерер добавит в документ секцию с оглавлением ("Содержание").
✖ inTemplateFile
Строка. Путь к файлу шаблона для выходного HTML, абсолютный или относительно корня проекта. Шаблон - это HTML-код генерируемого файла с добавленными в него маркерами для вставки сгенерированных CSS, HTML, и, при необходимости, дополнительными маркерами.
✖ cssClasses
Словарь строк, необязательно. Классы CSS для применения к определённым элементам выходного документа. Обратите внимание, что эти классы рассчитаны на каскадирование со сгенерированными классами lpgwrite-example-render-html, управляющими макетом вёрстки, поэтому должны содержать только данные, влияющие на внешний вид (font, color, background, padding, etc.), а не расположение (display, grid или связанные с ним, flex или связанные с ним, position, z-order, и т. д.).
✖ htmlPlaceholder
Строка. Текст маркера в шаблоне, в точности совпадающий с тем, который будет заменён на сгенерированный HTML-код, должен располагаться внутри тега <body>. Вставленный код будет обёрнут в одиночный <div>-элемент без явно указанных непосредственно на нём классов и стилей.
✖ cssPlaceholder
Строка. Текст марекра в шаблоне, в точности совпадающий с тем, который будет заменён на сгенерированный CSS-код, должен располагаться внутри тега <style> и не внутри какого-либо блока.
✖ extraTokens
Словарь строк, необязательно. Любые дополнительные токены для замены в шаблоне. Ключи словаря - тексты маркеров, в точности совпадающие с теми, которые будут заменяться (они не должны совпадать с теми, которые задействованы для htmlPlaceholder, cssPlaceholder, или других таких же токенов), значения - дословный HTML-код для вставки на их место.
✖ localizedKeywords
Словарь строк, необязательно. Список строк для применения в определённых местах UI сгенерированного документа, предполагаются соответствующими языку перевода целевого документа. Эти строки - обычный текст.
✖ addSourceRef
Булевой, необязательно (по умолчанию = false). Если true, генератор добавит имена исходных файлов к текстовым фрагментам, это поможет напомнить о происхождении конкретного куска текста. Этот режим полезен при вычитке и отладке черновика документа, особенно когда ваш проект и информация в нём разрастётся на достаточно большое число файлов.
Члены (подробно)
Строка. Путь к выходному файлу документа (.html) для записи, абсолютный или относительно корня проекта.
Булевой, необязательно (по умолчанию true). Если true, то рендерер добавит в документ секцию с оглавлением ("Содержание").
Строка. Путь к файлу шаблона для выходного HTML, абсолютный или относительно корня проекта. Шаблон - это HTML-код генерируемого файла с добавленными в него маркерами для вставки сгенерированных CSS, HTML, и, при необходимости, дополнительными маркерами.
Словарь строк, необязательно. Классы CSS для применения к определённым элементам выходного документа. Обратите внимание, что эти классы рассчитаны на каскадирование со сгенерированными классами lpgwrite-example-render-html, управляющими макетом вёрстки, поэтому должны содержать только данные, влияющие на внешний вид (font, color, background, padding, etc.), а не расположение (display, grid или связанные с ним, flex или связанные с ним, position, z-order, и т. д.).
Объект может содержать следующие члены, все они строки и необязательны (генератор подставит значения по умолчанию при необходимости):
  • itemTitle: класс для заголовка элемента (большой кликабельный заголовок с элементами навигации)
  • rawTitle: класс для неэлементного заголовка (второстепенный заголовок, такой как 'Примечания' или 'Члены')
  • paragraph: класс для основного параграфа текста
  • verbatimSpan: класс для внутристрокового фрагмента кода (вот такого). Он не касается блоков кода - они выполняются как теги <code> и стилизируются через них.
  • linkSpan: класс для встроенной ссылки Logipard
  • moreSpan: класс для кликабкельного текста "Ещё...", показываемого в кратком режиме показа элемента
  • elsewhereSpan: класс для текста "Элемент развёрнут в другом месте страницы...", видимого на месте для элемента, развёрнутого в другом месте
  • actionSpan: класс для действий на заголовке элемента ("Вернуть" и т. д.), обратите внимание, что элементы, попадающие под него, дочерние по отношению к заголовку, который попадает также под itemTitle
  • offsiteBlock: класс для применения к элементу, который развёрнут на не основном для него месте (исполнение по умолчанию - обвод границы синим сверху, снизу и слева)
Строка. Текст маркера в шаблоне, в точности совпадающий с тем, который будет заменён на сгенерированный HTML-код, должен располагаться внутри тега <body>. Вставленный код будет обёрнут в одиночный <div>-элемент без явно указанных непосредственно на нём классов и стилей.
Строка. Текст марекра в шаблоне, в точности совпадающий с тем, который будет заменён на сгенерированный CSS-код, должен располагаться внутри тега <style> и не внутри какого-либо блока.
Словарь строк, необязательно. Любые дополнительные токены для замены в шаблоне. Ключи словаря - тексты маркеров, в точности совпадающие с теми, которые будут заменяться (они не должны совпадать с теми, которые задействованы для htmlPlaceholder, cssPlaceholder, или других таких же токенов), значения - дословный HTML-код для вставки на их место.
Словарь строк, необязательно. Список строк для применения в определённых местах UI сгенерированного документа, предполагаются соответствующими языку перевода целевого документа. Эти строки - обычный текст.
Объект может содержать следующие члены, все они строки и необязательны (генератор подставит значения по умолчанию при необходимости):
  • SNAPBACK: строка для действия "Вернуть" (заголовок элемента)
  • SNAPBACK_AND_SCROLL: строка для действия "Вернуть и перейти" (заголовок элемента)
  • ELEVATE: строка для действия "Поднять" (заголовок элемента)
  • RESET: строка для действия "Сброс" (заголовок элемента)
  • ELEVATE_TO: строка для заголовка "Поднять до..." (диалог для действия "Поднять")
  • COPY_ITEM_NAME: строка для заголовка "Копировать полное имя модели LP FDOM этого элемента в буфер:" (диалог для действия "#LP?")
  • MORE: строка для текста "Ещё... >>" (краткий вид элемента)
  • TABLE_OF_CONTENTS: строка для текста "Содержание" (секция оглавления)
Булевой, необязательно (по умолчанию = false). Если true, генератор добавит имена исходных файлов к текстовым фрагментам, это поможет напомнить о происхождении конкретного куска текста. Этот режим полезен при вычитке и отладке черновика документа, особенно когда ваш проект и информация в нём разрастётся на достаточно большое число файлов.
Этот рендерер для ✖ ${LP_HOME}/lpgwrite-example выдаёт документацию в формате одностраничного Markdown-документа.
Этот рендерер использует дополнительный член lpgwrite-example-render-md в конфигурации для элемента генерации ✖ renders[] :
lpgwrite-example: {
	...
	renders: [
		{
			docModel: ...,
			renderer: "${LP_HOME}/lpgwrite-example-render-md" $, // копировать дословно!
			lpgwrite-example-render-md: {
				outFile: ...,
				emitToc: ...,
				header: ...,
				footer: ...,
				addSourceRef: ...
			}
		},
		...
	]
}
Члены
Имя
Описание
✖ outFile
Строка. Путь к записываемому файлу выходного документа (.md), абсолютный или относительно корня проекта.
✖ emitToc
Булевой, необязательно (по умолчанию = true). Если true, то рендерер добавит секцию с оглавлением в начале документа.
✖ header
Строка, необязательно. Если задано, то рендерер допишет эту строку в самое начало документа, перед оглавлением (если есть), что полезно для создания заголовка и аннотаций. Эта строка - в разметке Markdown.
✖ footer
Строка, необязательно. Если задано, то рендерер допишет эту строку в самый конец документа. Эта строка - в разметке Markdown.
✖ addSourceRef
Булевой, необязательно (по умолчанию = false). Если true, генератор добавит имена исходных файлов к текстовым фрагментам, это поможет напомнить о происхождении конкретного куска текста. Этот режим полезен при вычитке и отладке черновика документа, особенно когда ваш проект и информация в нём разрастётся на достаточно большое число файлов.
Члены (подробно)
Строка. Путь к записываемому файлу выходного документа (.md), абсолютный или относительно корня проекта.
Булевой, необязательно (по умолчанию = true). Если true, то рендерер добавит секцию с оглавлением в начале документа.
Строка, необязательно. Если задано, то рендерер допишет эту строку в самое начало документа, перед оглавлением (если есть), что полезно для создания заголовка и аннотаций. Эта строка - в разметке Markdown.
Строка, необязательно. Если задано, то рендерер допишет эту строку в самый конец документа. Эта строка - в разметке Markdown.
Булевой, необязательно (по умолчанию = false). Если true, генератор добавит имена исходных файлов к текстовым фрагментам, это поможет напомнить о происхождении конкретного куска текста. Этот режим полезен при вычитке и отладке черновика документа, особенно когда ваш проект и информация в нём разрастётся на достаточно большое число файлов.
Это переводчик для ✖ ${LP_HOME}/lpgwrite-i18n-assist , выполняющий фиктивный перевод. Результат представляет собой исходный текст без изменений, с добавленным в начало фрагментом [UNTRANSLATED-<язык>], по которому можно искать в промежуточном файле обновлённые и/или непереведённые строки.
Этот переводчик использует следующий формат translatorArgs внутри члена lpgwrite-i18n-assist элемента конфигурации ✖ renders[] для задания генерации:
lpgwrite-i18n-assist: {
	...
	renders: [
		{
			docModel: ...,
			renderer: "${LP_HOME}/lpgwrite-i18n-assist" $, // копировать дословно!
			lpgwrite-i18n-assist: {
				translator: "${LP_HOME}/lpgwrite-i18n-assist-trn-none" $, // копировать дословно!
				items: [
				{
						...
						translatorArgs: { lang: ... }
					},
					...
				]
			},
		...
	]
}
Специфичный для lpgwrite-i18n-assist-trn-none объект translatorArgs, со следующими членами:
Члены
Имя
Описание
✖ lang
Строка. Код языка. Будет подставлен в начальный фрагмент [UNTRANSLATED-lang] в строках с фиктивным переводом.
Члены (подробно)
Строка. Код языка. Будет подставлен в начальный фрагмент [UNTRANSLATED-lang] в строках с фиктивным переводом.
Определённый формат файла, в который считыватели стадии извлечения должны предварительно скомпилировать извлечённые входные данные. Вместе с концепцией FDOM, этот формат - одна из немногих вещей, которые Logipard требует в чётко оговоренном виде.
Стоит заметить, что требование относится только к результату, выдаваемому считывателями стадии извлечения, а не к читаемым ими файлам-исходникам, с которыми работает пользователь. Встроенный в Logipard ✖ ${LP_HOME}/lpxread-basic: Базовый языконезависимый считыватель стадии извлечения - достаточно тонкая обёртка над форматом входного файла, но считыватель стадии извлечения не обязан принимать на вход настолько же простой исходный формат. Например, вполне приемлемая постановка вопроса - создать считыватель стадии извлечения, которые переводит javadoc-комментарии в Logipard-совместимую форму.
Входная информация разбивается на множество файлов, соответствующих входным файлам-исходникам, сохраняя структуру их папок и имён, насколько возможно (к исходным именам добавляется расширение .lpinput), в каталог, указанный в ✖ outDir в элементе задания стадии генерации.
Входной файл LP - текстовый файл в кодировке UTF-8, размеченный тегами разметки в формате <#имя-тега ... #> (имя тега - буквенно-цифровое, разрешаются -). Теги могут быть вложенными. Помимо составляющих частей тегов, остальной формат текста (чисто текст, Markdown, HTML, или что-либо ещё) непрозрачен с точки зрения входных данных LP, его интерпретация - задача для стадий компиляции и генерации.
Типичный фрагмент входных данных может выглядеть так:
<#LP ./dataStruct { <#./%title: FDOM data structure#> <#./%order: 1#>
The FDOM data structure explanation starts best with a visual example...

<#img ./lp-model.png #>

The model data consists of *nodes*. 
- blah
- blah blah

blah blah blah (see: <#ref dataStruct/parent-member#>, <#ref dataStruct/tagged-tag#> ).

blah blah total of 11 nodes.#>
Возможно экранировать фрагменты текста, используя ограничители <#~delimiter~ ... ~delimiter~#>. Ограничитель - последовательность любых не-~ символов, в том числе пустая, она должна совпадать у начинающего и заканчивающего ограничителей экранируемого фрагмента. Всё между ограничителями берётся как дословный простой текст:
<#this-is-tag
	это данные контента
	<#this-is-tag-too и это тоже данные контента#>
	<#~~
	Это всё обычный текст, <#even-this#>
	~~#>

	Это снова данные контента <#and-a-tag#> (буквы в тегах разрешаются только латинские)

	<#~a~
	Это снова обычный текст, <#~~ и даже это ~~#>
	~a~#>
#>
Имена тегов разметки могут начинаться с - (также возможно написание <-#tag-name ... #>) - такие теги считаются закомментированными и не влияют на контент и FDOM, хотя внутри они всё равно должны иметь корректный формат (правильно согласованные открытия-закрытия тегов и экранировок).
это данные <#with-markup-tag inside#>
а это <-#dropped-tag#> данные без тегов разметки <#-this-is-dropped-tag-either and this <#is-not#>, но игнорируется, т. к. внутри исключённого тега#> однозначно
то же самое, что:
это данные <#with-markup-tag inside#>
а это  данные без тегов разметки  однозначно
Имена тегов разметки, начинающиеся с LP, в том числе собственно <#LP ...#>, зарезервированы для потока контента и директив Logipard (эти имена не чувствительны к регистру). Кроме того, <# ... #> считается краткой формой записи для <#LP ...#>. Также зарезервирован тег <#ref ...#> (для ссылок на элементы документации Logipard, см. далее), это имя тоже не чувствительно к регистру. Все прочие теги называются пользовательскими тегами, и их обработка предоставляется записывателю скомпилированной модели на стадии ✖ Стадия компиляции . Их имена МОГУТ быть чувствительными к регистру, на усмотрение реализации записывателя модели.
Поток контента Logipard реализуется как тег <#LP itemName: ...контент... #> или <# itemName: ...content...#>. За именем элемента могут следовать имена FDOM-тегов, опционально начинающиеся с #: <#LP itemName tagItemName1 tagItemName2 ...: ...#>, <#LP itemName #tagItemName1 tagItemName2 ...: ...#>, <#LP itemName #tagItemName1 #tagItemname2 ...: ...#>.
Поток контента, собственно говоря, представляет собой инструкции по добавлению контента и указанных FDOM-тегов к элементу с указанным именем. Входной файл как таковой - это последовательность потоков контента в некотором количестве.
Потоки контента могут следовать друг за другом:
<#LP A: это попадает в элемент A#>
<#LP B: это попадает в элемент B#>
<#LP C: это попадает в элемент C#>
<#LP D %tagDWithThis#> <-# если нет контента, только добавляются FDOM-теги, то можно не добавлять `:`#>
<#LP A: это попадает в элемент A
Может быть несколько потоков в один и тот же элемент - это разрешено, контент из каждого следующего поочерёдно дописывается к предыдущему.
Разрешено даже иметь такие потоки в разных входных файлах, но в этом случае следует иметь в виду, что порядок, в котором входные файлы будут
обработаны, не гаранитирован.
#>
или быть вложенными:
<#LP Outer: это попадает в элемент Outer
	<#LP Inner: это попадает в элемент Inner#>
	это опять попадает в элемент Outer
#>
Вложенный поток контента называется отступлением [уровня] (как временное отступление от уровня 'текущего' элемента).
Отступление уровня может быть сделано открытым, используя { для ограничителя вместо :. В этом случае, даже после окончания отступления, текущий уровень остаётся открытым до того момента, когда встретится тег закрытия открытого отступления:
<#LP A:	это попадает в A
	<#LP B { это попадает в B#>
	это всё ещё попадает в B
	<#LP }
	это попадает в A (обратите внимание - если тег закрытия содержит некоторый контент, то важно разместить его на новой строке (или после тега разметки, по крайней мере <-# comment#>) после `}`) #>
	это попадает в A

	<#LP C/D { это попадает в C/D#>
	<#LP E { открытые отступления могут быть вложенными#>
	это попадает в E
	<#LP } #>
	это попадает в C/D
	<#LP } #>
	это попадает в A
#>
Имя элемента/тега, указанное в потоке контента, в конечном счёте разрешается в полное имя FDOM (см. ✖ Отношение "родитель-член" и имена ). Но использовать везде дословные полные имена было бы крайне непрактично, поэтому имена, с которыми вы имеете дело внутри тегов разметки LP, считаются частичными (сокращёнными), и при их разрешении действует ряд правил.
Текущий элемент
Текущий элемент может обозначаться одиночной точкой (как "текущий каталог") в качестве начального сегмента имени. Можно использовать его и как законченное имя.
<#LP A: это попадает в A
	<#LP.: это тоже попадает в A#>
#>
<#LP B/C: это попадает в B/C
	<#LP.: это тоже попадает в B/C#>
	Добавлять таким способом контент нет особого смысла, но это подходящий способ для добавления тега:
	<#LP . %tag"B/C"WithThis #>
#>
<#LP D/E/F: это попадает в D/E/F
	<#LP D/E/F/G: это попадает в D/E/F/G#>
	это попадает в D/E/F
	<#LP ./G: это опять попадает в D/E/F/G#>
#>
Одиночную точку можно пропустить совсем:
<#LP A
	<#LP: это в A (как и <#LP#>) #>
#>
Если вы собираетесь совместить этот синтаксис с добавление тегов, нужно будет использовать префикс #:
<#LP #%tagToCurrentItem#>
Сокращения-надкаталоги
Применение .., ... и т. д. (как "надкаталогов") в качестве начальных сегментов означает имя на один, два, и т. д. уровня выше, чем текущее имя:
<#LP D/E/F/G: это попадает в D/E/F/G
	<#LP ..: это попадает в D/E/F#>
	<#LP ...: это попадает в D/E#>
	<#LP ....: это попадает в D#>
	<#LP .....: это попадает в корневой элемент#>
#>
К слову, все потоки контента во входном файле - по сути, отступления на уровне корневого элемента.
Технически, можно добавлять контент и на уровень корневого элемента, но это имеет мало смысла и является плохим стилем.
Если сегмент-надкаталог ведёт выше, чем возможно с текущего уровня, но существует внешний уровень вложения, то продолжается подъём по нему, потом по следующему внешнему уровню, и т. д.
<#LP A/B
	<#LP C/D {#>
		<#LP ..: это попадает в C#>
		<#LP ...: здесь мог быть уровень корня, но у нас есть внешний уровень, с которого можно продолжить, так что это попадает в A/B #>
		<#LP ....: это попадает в A #>
		<#LP .....: и только это попадает в корень #>
	Это снова попадает в C/D
	<#LP } #>
	Это снова попадает в A/B
#>
"Текущий каталог" и "надкаталог" можно использовать и в качестве сегментов в середине имён, хотя это достаточно странный способ:
<#LP A/B
	<#LP ./C/D/..: . - это A/B, ./C/D - это A/B/C/D, ./C/D/.. - это A/B/C, так что, в результате, это A/B/C #>
#>
Сокращения-имена внешних уровней
Если начальный сегмент имени - не "текущий каталог" и не "надкаталог", он не обязательно означает сегмент имени первого уровня. Сначала просматриваются открытые уровни, начиная с текущего и к более внешним, и, если нашлось совпадение, в качестве отправного уровня берётся именно оно.
<#LP A:
	<#LP B/C: this is B/C (not A/B/C !)
		<#LP D: это действительно D#>
		<#LP C: это B/C#>
		<#LP B: это B#>
		<#LP A: уровень B закончился, но над ним есть A, так что это A#>
	#>
#>
Правила разрешения имён действуют на именах элементов, открывающих поток контента (отступление), именах тегов и именах элементов в ссылках:
<#LP A/B/C
	<#LP ./%tag: это A/B/C/%tag #>
	<#LP A C/%tag: мы в A и добавляем ему тег A/B/C/%tag
		<#ref ./B#> - ссылка на A/B
	#>
#>
Обратите внимание, что для имён элемента и следующих за ним тегов в начале отступления текущий уровень - всё ещё внешний, а для ссылок и отступлений внутри - уже сам текущий элемент.
Закрытие открытых отступлений по именам
Тег закрытия открытого отступления обычно закрывает всё отступление, которое было оставлено открытым:
<#LP A:
	<#LP B/C { #>
	Это попадает в B/C
	<#LP } #> <-#закрывает B/C#>
	Это попадает в A
#>
Но возможно задать краткое имя для указания, до какого уровня нужно закрыть - это будет первый уровень, совпадающий с данным именем:
<#LP A:
	<#LP B/C/D { #>
	Это попадает в B/C/D
	<#LP } C #> <-#закрывает C, но оставляет B (обратите внимание - краткое имя на той же строке, что `}`) #>
	Это попадает в B
	<#LP } #> <-# закрывает "оставшуюся" часть отступления, то есть B (можно было бы также использовать <#LP } B#> с тем же результатом)#>
	Это попадает в A
#>
Вместо закрытия уровня по имени включительно, можно указать, что нужно остаться на уровне с этим именем, добавив сегмент .:
<#LP A:
	<#LP B/C/D { #>
	Это попадает в B/C/D
	<#LP } C/. #>
	Это попадает в B/C (попало бы в B, если бы мы использовали "} C")
	<#LP } #>
	Снова в A
#>
Правило "подниматься наружу по уровням при исчерпании внутренних уровней" при этом также действует:
<#LP A:
	<#LP B/C { #>
	<#LP D/E { #>
	это попадает в D/E
	<#LP } E #>
	это попадает в D
	<#LP } B #>
	закрыли остающиеся D и B/C, это попадает в A
#>
но с одной важной оговоркой: можно проводить закрытия только "сквозь" открытые отступления. Текущие обычные (не-открытые) отступления образуют барьер отступления, которое препятствует закрытию уровней выше него.
<#LP A:
	<#LP B/C { #>
	<#LP D/E:
	это попадает в D/E, но обратите внимание, что "D/E" открыто как не-открытое отступление, и мы всё ещё находимся на его уровне
	СЛЕДУЮЩЕЕ - НЕКОРРЕКТНО: <#LP } E #>
	Использование "} D", или "} B", или "} C", или "} A" здесь также не разрешается, т. к. они проходят сквозь всё ещё действующий D/E.
	#>
	<#LP } B #> 

	<#LP F/G {
	То же самое относится в уровню внутри действующего тега, оставляющего открытое отступление,
	до того, как он завершится.
	То есть, следующее - некорректно: <#LP } F #>
	#>
	но здесь, поскольку тег, оставивший открытое отступление, закончился, следующее уже приемлемо:
	<#LP } F #>
#>
Эта мера предусмотрена для повышения устойчивости синтаксиса к ненамеренному нарушению согласованности уровней.
Трассировка уровня
Правила разрешения имён спроектированы, чтобы, по возможности, вести себя по принципу "наименьшего удивления", и результат их применения должен в основном соответствовать интуитивно ожидаемому при чтении и написании аннотаций. Тем не менее, в сомнительных случаях можно вставить разметочный тег <#LP-TRACE-WHERE [опциональная метка]#>, который распечатает имя текущего уровеня в том месте, в которое он вставлен, включая опциональную метку (если она задана) и расшифровку разрешения имени.
Правила разрешения имён действуют также в именах и тегах заголовков потоков контента, спецификации макросов и алиасов ( ✖ Макросы , ✖ Алиасы имён ), встроенных ссылок ( ✖ Встроенные ссылки ) и обратного добавления тега ( ✖ Прямое и обратное добавление тегов ).
Встроенные ссылки на элементы FDOM задаются тегом разметки <#REF item/name#>. При указании имени действуют правила разрешения имён ( ✖ Разрешение имён элементов и тегов FDOM ). Ссылки поддерживаются на стадии компиляции FDOM как встроенная возможность - пользователю FDOM (напримр, генератору) не требуется изобретать для этого отдельный пользовательский тег.
Возможно явное указание альтернативного текста для ссылки:
Это <#ref item/name: ссылка на item/name с альт. текстом#>
Незаданный альт. текст считается пустым. Вообще говоря, интерпретация альт. текста (или отсутствия такового) предоставляется пользователю FDOM, такому, как генератор.
Добавление тегов FDOM делается при открытии отступления (<# name tag1 tag2 ...: ...контент...#>), или, если они добавляются к элементу текущего уровня, позднее во вспомогательном под-отступлении (<#. tag1 tag2 ...#>). Но возможно сделать и наоборот - добавить элемент текущего уровня как FDOM-тег какому-либо другому элементу: <#LP-TAG-TO other-item-name#>.
Можно подключить входной файл, с таким же результатом, как если бы его содержимое было вставлено непосредственно. Этот файл называется [входным] файлом-модулем.
<#LP-INCLUDE lp-module-inc.lp-txt#>
или короче...
<#LP-INC lp-module-inc.lp-txt#>
обратите внимание, что здесь даётся только исходное расширение файла, без суффикса .lpinput
Извлечения из входных файлов-модулей немного отличаются от "основных" входных файлов: их имена имеют суффикс .lpinput-inc вместо .lpinput, и они не обрабатываются автоматически на стадии компиляции, поскольку считаются только фрагментами "основных" файлов, которые подключаются вручную. Нельзя подключать другие "основные" файлы (но можно подключить файл-модуль из другого файла-модуля).
Подготовка файлов-модулей обычно делается отдельными элементами задания стадии извлечения, которые имеют флаг ✖ forLPInclude установленным в true. Рекомендуется также держать файлы-модуль в специально выделенном подкаталоге (например, если ✖ outDir для основных файлов - "lp-extract.gen", то для файлов-модулей это может быть что-нибудь вроде "lp-extract.gen/lp-includes". Тому есть причина. Позже, на стадии компиляции, при обработке директив <#LP-inc[lude] имя-файла#>, имя-файла будет интерпретироваться следующим образом:
  • если оно начинается с . или .., то это путь отнсительно каталога, где находится обрабатываемый входной файл (т. е. содержащий данный <#LP-INCLUDE#>) - но это достаточно редкий случай,
  • в противном случае, ищется файл <includes-dir>/file-name[.lpinput-inc] методом каскадного поиска, то есть, начиная с каталога, где находится обрабатываемый файл, если там нет - то в ../<includes-dir>/file-name[.lpinput-inc] относительно него, потом ../../<includes-dir>/file-name[.lpinput-inc], и т. д., до тех пор, пока подключаемый файл не будет найден, или пока не будет достигнут каталог <extracted-input-root>. То есть, по стратегии, аналогичной той, по которой Node.JS ищет файл по require(filename). Для организации и использования файлов-модулей рекомендуется именно этот способ. Например, можно разместить файл-модуль как <extracted-input-root>/<includes-dir>/common.lp-txt[.lpinput-inc], а потом подключать его через <#LP-INCLUDE common.lp-txt#> из любого входного файла под <extracted-input-root>/**.
Обратите внимание, что на стадии извлечения можно задавать outDir практически как угодно, но следует позаботиться, чтобы они совпадали с каталогами, которые будут <extracted-input-root> и <includes-dir> на стадии компиляции (т. е. будут указаны в ✖ inRootDir и ✖ lpIncLookupDirName , соответственно). Если несколько заданий стадии извлечения рассчитаны на одно и то же целевое задание стадии компиляции, то их outDir должны быть согласованы с inRootDir и lpIncLookupDirName этого задания стадии компиляции.
Файлы-модули обычно содержат определения макросов и алиасов, а не непосредственно добавляемый контент.
Для имён можно определять алиасы. Это может быть удобно для более удобного именования длинных элементов (например, M для следующего за корневым уровнем имени элемента-домена), или для быстрого перемещения фрагментов FDOM под другие фактические имена без изменения исходников. Синтаксис для определения алиаса: <#LP-ALIAS new-name: old-name#> После этого new-name становится алиасом для old-name (само по себе old-name тоже можно продолжать использовать).
Несколько правил, которые следует помнить насчёт алиасов:
  1. Алиас действует только во время компиляции и только в пределах конкретного входного файла (в том числе на фрагменты, подключённые через <#LP-INCLUDE ...#>, но не на каждый файл-модуль сам по себе, а по месту конкретного подключения). В FDOM концепция алиасов отсутствует, и в окончательном скомпилированном результате все имена попадают уже разрешёнными.
  2. Разрешение алиасов действует для начальных частей имён, но делается после применения правил разрешения имён. Т. е.:
<#LP-ALIAS A/B: C#>
<#LP A/B: это попадает в C: A/B #>
<#LP A/B/D: это попадает в C/D, потому что начальная часть A/B - алиас для C#>
но:
<#LP A/B/../D: это попадает в A/D, потому что A/B/../D разрешается в A/D, а A - не алиас #>

также:
<#LP A: это попадает в A
	<#LP ./B: это попадает в C, потому что непосредственно разрешённое имя - A/B, и оно является алиасом#>
#>
Разрешённое имя до применения алиаса также называется буквальным именем, и именно так упоминается в выводе <#LP-TRACE-WHERE#>. 3. Алиас можно переопределить - переопределение вступает в силу в порядке входного потока:
<#LP-ALIAS A/B: C#>
<#LP A/B: это попадает в C#>
<#LP-ALIAS A/B: D#>
<#LP A/B: это попадает в D#>
Однако обратите внимание, что спецификация переопределяемого алиаса распространяется только на последний сегмент имени, и имя в целом может зависеть от алиасов на предшествующих сегментах:
<#LP-ALIAS A: B#>
<#LP-ALIAS A/C: D#> A/C и B/C означают D (здесь A - буквальное имя и алиас, B - настоящее имя без алиаса)
<#LP-ALIAS A/C/E: F#> A/C/E и B/C/E означают D/E
<#LP-ALIAS A: G#>
<#LP-ALIAS A/C: H#> A/C и G/C теперь означают H, A/C/E означает H/E, B/C всё ещё означает D!
Настоящие имена элементов к этому моменту - B, D, D/E, F, G
Можно рассматривать определение алиаса как создание символической ссылки на элементы в "каталоге" пространства имён, работающее по похожей логике. 4. Если настоящее имя (напрямую или через алиас) было использовано в текущем входном файле одним из следующим способов:
  • как тег FDOM или цель для такого тега,
  • имело какой-либо добавленный под это имя контент,
  • использовалось как фактическое имя для макроса ( ✖ Макросы ) или имя алиаса,
  • было целью в <#ref ...#>,
  • использовалось как промежуточный фрагмент пути для любого из вышеперечисленного,
то его больше нельзя использовать в данном входном файле для алиаса:
<#LP A/B: контент#>
<#LP-ALIAS A/B: C#> это будет пропущено с выдачей предупреждения
<#LP-ALIAS A: D#> и это тоже
ограничение, однако, не распространяется на неиспользованные имена под A/B:
<#LP-ALIAS A/B/C: C#>
и само A/B всё ещё может быть целью алиаса:
<#LP-ALIAS E: A/B#>
<#LP E: это попадает в A/B#>
<#LP-ALIAS E: C#> E может быть переопределено, т. к. оно с самого начала было алиасом
Обратите внимание, что в вышеприведённом примере, после определения E как алиаса, больше нельзя использовать или ссылаться на элемент с настоящим именем E, или какие-либо его подэлементы, в данном входном файле - E будет всегда обозначать элемент, на который в данный момент настроен алиас E. Таким образом, данное ограничение вводится, чтобы предотвратить перемешивание алиасов и настоящих имён неочевидно трактуемым образом.
Некоторые имена узлов можно определить как макросы, добавляющие заданный набор тегов и контент в произвольные узлы, методом добавления псевдо-узлов макросов в качестве тегов или отступлений.
<#LP-MACRO Mac %tag1 %tag2: контент макроса#>
Добавление макроса как тега к узлу - то же самое, что непосредственное добавление соответствующего набора тегов и контента в начало "помечаемого" узла:
<#LP test-node Mac: тестовый контент#>
то же самое, что:
<#LP test-node %tag1 %tag2: контент макроса тестовый контент#>
или:
<#LP test-node: тестовый контент <#. Mac#> ещё тестовый контент #>
то же самое, что:
<#LP test-node: тестовый контент <#. %tag1 %tag2: контент макроса#> ещё тестовый контент #>
или используйте макрос непосредственно внутри, что равносильно его использованию как тегу (т. е. раскрытие контента макроса в начало содержащего узла):
<#LP test-node: тестовый контент <#Mac#>#>
то же самое, что:
<#LP test-node %tag1 %tag2: контент макроса тестовый контент#>
Если макросов несколько, они раскрываются в порядке применения (это имеет значение, если они добавляют контент).
Макросы могут содержать произвольные разметочные теги LP:
<#LP-MACRO %is-a-class %class: <#LP-TAG-ON classes#> <#LP ./%title: Заголовок класса: #> #>
...
<#LP ClassA %class: это класс A, мы добавили его в <#ref classes#>, используя макрос
	<#./%title: A#> <-#получим %title = "Class title: A"#>
#>
Технически возможно использовать даже LP-ALIAS или другой LP-MACRO, хотя обычно в этом нет большого смысла, и следует быть осторожными с этими вариантами, если всё-таки решитесь их использовать. Имейте в виду, что уровень имени внутри макроса (т. е., место, которое обозначает <# . #>, или откуда начинается поиск при разрешении имени) - тот узел, в который идёт вставка, а не сам макрос.
<#LP-MACRO where-am-I: <#LP-TRACE-WHERE#>#>
<#LP A where-am-I: внутри A#>
<#LP B where-am-I: внутри B#>
Как и в случае LP-ALIAS ( ✖ Алиасы имён ), нельзя определить макрос под тем фактическим полным именем, которое уже использовалось в текущем входном файле одним из следующих способов:
  • как тег FDOM или цель для такого тега,
  • имело какой-либо добавленный под это имя контент,
  • как фактическое имя для макроса или алиаса,
  • было целью в <#ref ...#>,
  • использовалось как промежуточный фрагмент пути для любого из вышеперечисленного,
Кроме того, не является корректным использовать полное имя, под которым определён макрос, в качестве начала пути для каких-либо подузлов:
<#LP-MACRO Mac: контент макроса#>
<#LP Mac/I: может сработать не так, как вы ожидаете#>
Аналогично алиасам, макросы действуют только во время компиляции и в пределах конкретного входного файла - в конечном выводе FDOM они все будут раскрыты.
Программа документа для ✖ ${LP_HOME}/lpgwrite-example - список инструкций, описывающих, какие элементы выбираются для заполнения страницы документа, как организуется информация из них, и построение контекста для данной операции. Организационно, программа задаёт одну или более моделей документа (определения включаемого набора элементов и подробностей их представления), на которые позже ссылаются спецификации рендереров ( ✖ renders[] ). В конфигурационном файле программа документа структурируется в виде массива JSON/LPSON из элементов-команд - они описаны ниже более подробно.
Программа документа может выглядеть так:
[
	{ nameAlias: "M", name: "domain.logipard" },
	{
		docModel: {
			{
				name: "DocMain",
				query: [
					...
				],
				sort: { ... }
			}
		},
		forEachItem: [
			...
		]
	},
	{
		docModel: {
			{
				name: "DocReadme",
				query: [
					...
				],
				sort: { ... }
			}
		},
		forEachItem: [
			...
		]
	},
	...
]
Многие команды задействуют спецификацию условий, запросов, коллекций или их алиасов, в терминах ✖ Запросы FDOM . В программе документации они представляются как JSON/LPSON объекты, совместимым с ✖ Рекомендуемый интерфейс для считывателя скомпилированного FDOM способом, как описано ниже.
Любой из объектов, перечисленных в ✖ <Condition> .
В дальнейших описаниях будет идти под обозначением {...условие}.
Любой из объектов, перечисленных в ✖ <CollectionSpec> , кроме вариантов ✖ <Item> и ✖ <Collection> , т. к. они не имеют аналогов для выражения в JSON/LPSON окружении.
В дальнейших описаниях будет идти под обозначением {...коллекция}.
Любой из объектов (или массив таких объектов) из перечисленных в ✖ <QuerySpec> , кроме варианта ✖ <Query> , т. к. он не имеет аналога для выражения в JSON/LPSON окружении. Массив трактуется как составной запрос, с применением компонентов в соответствующем порядке. Начальная коллекция для запроса зависит от контекста, насчёт этого будут дополнительные пояснения по мере необходимости.
В дальнейших описаниях будет идти под обозначением {...запрос}.
В ряде контекстов, которые требуют спецификации коллекции, программа документации также позволяет задать сортировку, чтобы определить порядок, в котором коллекция будет выдаваться/показываться. Формат спецификации сортировки следующий:
{
	byMember: "member-name",
	keyFormat: "lexical|natural",
	order: "asc|desc"
}
В дальнейших описаниях будет идти под обозначением {...сортировка}.
Члены
Имя
Описание
✖ byMember
Строка. Задаёт краткое имя члена, который будет использоваться как ключ сортировки. Ключ состоит из контента члена, интерпретируемого как простой текст, с удалением пробелов в начале и конце. Предполагается, что в контенте члена не будет содержаться вложенной разметки LP, в противном случае фактическое значение ключа не гарантируется. Сравнение ключа чувствительно к регистру.
✖ keyFormat
Строка, необязательно (по умолчанию lexical). Может быть lexical или natural:
  • lexical: ключи сравниваются как строки (через лексикографическое сравнение строк).
  • natural: ключи разбиваются (и сравниваются пофрагментно) на последовательности из числовых и нечисловых фрагментов, где пары сегментов числовой/числовой сравниваются как числа, а нечисловой/числовой и нечисловой/нечисловой сравниваются как строки. Т. е., 1.2.3-a и 1.10-z сравниваются как [1, ".", 2, ".", 3, "-a"] и [1, ".", 10, "-z"], первые различающиеся сегменты - 2 и 3, которые являются случаем числовой/числовой, 2 меньше, поэтому 1.2.3-a меньше 1.10-z. Если ключ начинается с + или -, за которым следует цифра, этот +/- считается частью числа в первом сегменте, который считается числовым.
✖ order
Строка, необязательно (по умолчанию asc). Может быть asc (для сортировки по возрастанию) или desc (для сортировки по убыванию).
Члены (подробно)
Строка. Задаёт краткое имя члена, который будет использоваться как ключ сортировки. Ключ состоит из контента члена, интерпретируемого как простой текст, с удалением пробелов в начале и конце. Предполагается, что в контенте члена не будет содержаться вложенной разметки LP, в противном случае фактическое значение ключа не гарантируется. Сравнение ключа чувствительно к регистру.
Если член-ключ отсутствует, то у элемента нет ключа. Элементы без ключа размещаются в неопределённом порядке после отсортированных элементов с ключами.
Строка, необязательно (по умолчанию lexical). Может быть lexical или natural:
  • lexical: ключи сравниваются как строки (через лексикографическое сравнение строк).
  • natural: ключи разбиваются (и сравниваются пофрагментно) на последовательности из числовых и нечисловых фрагментов, где пары сегментов числовой/числовой сравниваются как числа, а нечисловой/числовой и нечисловой/нечисловой сравниваются как строки. Т. е., 1.2.3-a и 1.10-z сравниваются как [1, ".", 2, ".", 3, "-a"] и [1, ".", 10, "-z"], первые различающиеся сегменты - 2 и 3, которые являются случаем числовой/числовой, 2 меньше, поэтому 1.2.3-a меньше 1.10-z. Если ключ начинается с + или -, за которым следует цифра, этот +/- считается частью числа в первом сегменте, который считается числовым.
В большинстве случаев лучше всего подходит метод сравнения natural - он корректно обрабатывает такие ключи, как:
  • строки, имеющие фиксированную структуру с включением чисел, например, item1, item2, ...item10, ...,
  • целые числа и числа с десятичной точкой (без экспоненты), например, 1, -2, 3.14,
  • номера версий, например, 1.0.3
Строка, необязательно (по умолчанию asc). Может быть asc (для сортировки по возрастанию) или desc (для сортировки по убыванию).
Это команды, создающие алиасы для условий, запросов и коллекций, в терминах ✖ Запросы FDOM . Их можно использовать на корневом уровне программы для определения контекста, общего для всех моделей (например, общеиспользуемый алиас для имени домена проекта), или использовать внутри спецификации модели документа (см. далее).
Задаёт именованный алиас для элемента. Синтаксис инструкции: { nameAlias: "ItemAliasName", name: "строка-имени"}, где строка-имени - полное имя FDOM (возможно начинающееся с определённого ранее алиаса). Имя алиаса элемента должно быть корректным кратким именем FDOM.
Соответствует ✖ .nameAlias(aliasName, item) .
Задаёт именованный алиас для запроса. Синтаксис инструкции: { queryAlias: "QueryAliasName", query: {...запрос}}.
Соответствует ✖ .queryAlias(aliasName, ...querySpecs) .
Рекомендуется не заводить одинаковых имён для алиасов запросов на корневом уровне программы и внутри моделей документов во избежание неожиданного поведения.
Задаёт именованный алиас для условия. Синтаксис инструкции: { conditionAlias: "?CondAliasName", condition: {...условие}} (использование префикса ? - необязательная конвенция).
Соответствует ✖ .conditionAlias(aliasName, condSpec) .
Рекомендуется не заводить одинаковых имён для алиасов условий на корневом уровне программы и внутри моделей документов во избежание неожиданного поведения.
Задаёт именованный алиас для коллекции. Синтаксис инструкции: { collectionAlias: "CollAliasName", collection: {...коллекция}}. Это постоянный алиас, который будет общим для последующих запросов, в отличие от локального алиаса в пределах запроса, который действует только до окончания текущего запроса ( ✖ Задать локальный алиас коллекции ["alias ..."] ).
Соответствует ✖ .collectionAlias(aliasName, ...collectionSpecs) .
Рекомендуется не заводить одинаковых имён для алиасов коллекций на корневом уровне программы и внутри моделей документов во избежание неожиданного поведения.
Программа документации может задавать одну или более моделей документа. Инструкция спецификации следующая:
{
	docModel: {
		name: "DocumentModelName",
		rootItems: {
			query: {...запрос},
			sort: {...сортировка}
		},
		excludeUnder: {...коллекция}, // необязательно
		whitelistUnder: {...коллекция} // необязательно
	},
	forEachItem: [
		... // список инструкций спецификации человекочитаемого контента элемента
	]
}
Члены
Имя
Описание
✖ forEachItem
one of listed in ✖ Команды определения контекста (be sure you don't assign aliases with conflicting names), or one of the instructions listed in this section.
✖ docModel
Задать имя модели документа и набор FDOM-элементов для включения в эту модель.
Члены (подробно)
one of listed in ✖ Команды определения контекста (be sure you don't assign aliases with conflicting names), or one of the instructions listed in this section.
Инструкция, которая состоит из интегральной строковой константы JSON/LPSON. Может иметь множество значений, в зависимости от формата строки:
  • "member-field-name": любое краткое имя FDOM (обратите внимание, что строки, начинающиеся с %% и #, зарезервированы для других инструкций и не попадают в этот вариант). Предписывает выдать непосредственный контент заданного поля-члена текущего элемента, без (под)членов, или не выдавать ничего, если члена с таким именем нет.
  • "#text:...arbitrary string...": выдать обычный текст, следующий за префиксом #text:, со стилем обычного текста.
  • "%%title": выдать человекочитаемый заголовок текущего элемента (контент его члена %title, или краткое имя элемента, если %title отсутствует), со стилем выделенного заголовка (или как интерактивный элемент-заголовок, если это применимо в рендерере). В общем случае, не требуется давать этой инструкции явно - заголовок выдаётся автоматически, если у элемента не приватное имя (краткое имя, начинающееся с #).
  • "%%brief": выдать краткую часть непосредственного контента элемента (его первый параграф, если он не блок кода или элемент списка), в стиле общего текста.
  • "%%debrief": выдать часть непосредственного контента элемента, оставшуюся после %%brief, в стиле общего текста. Инструкция "%%brief" и следующая за ней инструкция "%%debrief" в сумме выдают полный непосредственный контент элемента.
  • "%%refTitle": выдать заголовок текущего элемента, в стиле общего текста, завёрнутый во внутристраничную ссылку (Logipard-ссылку, если это применимо в рендерере). Данная инструкция сама по себе не имеет особого смысла, т. к. ссылки из непосредственного контента элемента на сам этот элемент нерабочие по построению - обычно она используется в связке с #item (см. далее).
  • "%%more-start": эта инструкция отмечает место, на котором заканчивается контент элемента, показываемый в кратком режиме просмотра. Всё ниже будет должно быть видимо только в режиме полного просмотра (или после переключения в него). Эта инструкция может быть использована только один раз за всю секцию forEachTeam.
  • "%%mark-for-toc": эта инструкция показывает, что данный элемент следует включить в дерево оглавления (если такое применимо в рендерере). По умолчанию, элемент не отмечается для включения в оглавление, и следует позаботиться о том, чтобы туда включались только достаточно значимые элементы, иначе оглавление получится перегруженным. Не требуется отмечать каждый уровень в ветке дерева - оно сокращается до только отмеченных элементов (т. е., если отмечены только элемент и его прародительский элемент, то в оглавлении элемент будет представлен как непосредственный член своего прародителя).
  • "#item:...spec...": любой вышеперечисленный вариант, кроме %%more-start и %%mark-for-toc, после префикса #item: - например, #item:%%title, #item:%%refTitle, #item:fieldName, и т. п. Эту инструкцию не допускается размещать отдельно - только внутри инструкции emitAsItems... (см. ниже), и она относится к текущему элементу итерируемой подколлекции.
Блок инструкций, вывод которых нужно поместить внутри озаглавленной секции, которая будет выдана с заголовком в стиле выделенного подзаголовка (менее выделенного, чем заголовок элемента). Инструкция представляет собой объект следующего вида:
{
	section: "Заголовок секции",
	content: [
		...
	]
}
Члены
Имя
Описание
✖ section
Строка. Обычный текст, заголовок секции.
✖ content[]
Массив. Блок инструкций (из числа тех, которые применимы под ✖ forEachItem ), которые будут выдавать содержимое секции.
Члены (подробно)
Строка. Обычный текст, заголовок секции.
Массив. Блок инструкций (из числа тех, которые применимы под ✖ forEachItem ), которые будут выдавать содержимое секции.
Выполнить запрос над заданной коллекцией и установить для полученной в результате коллекции постоянный алиас (он заменит алиас, определённый под этим именем ранее, если такой был). Инструкция представляет собой объект следующего вида:
{
	on: {...коллекция},
	query: {...запрос},
	as: "ResultAlias"
}
В данной команде понятие текущего элемента может не задействоваться, поэтому её допустимо использовать вне docModel.
Члены
Имя
Описание
✖ on
Коллекция, с которой начинается запрос (как описано в ✖ Коллекции ). Кроме того, если команда используется внутри docModel, определён алиас имени "%%self", который можно использовать в качестве поля on или внутри запроса - он означает текущий элемент.
✖ query
Запрос для выполнения, над on в качестве исходной коллекции.
✖ as
Строка. Алиас, задаваемый для полученной коллекции (заменяет одноимённый, определённый ранее, и остаётся в силе на выполняемых далее инструкциях, включая итерации forEachItem для последующих элементов, учитывайте это во избежание эффектов, зависимых от порядка выполнения).
Члены (подробно)
Коллекция, с которой начинается запрос (как описано в ✖ Коллекции ). Кроме того, если команда используется внутри docModel, определён алиас имени "%%self", который можно использовать в качестве поля on или внутри запроса - он означает текущий элемент.
Запрос для выполнения, над on в качестве исходной коллекции.
Строка. Алиас, задаваемый для полученной коллекции (заменяет одноимённый, определённый ранее, и остаётся в силе на выполняемых далее инструкциях, включая итерации forEachItem для последующих элементов, учитывайте это во избежание эффектов, зависимых от порядка выполнения).
Выполнить блок инструкций только в том случае, если заданная коллекция не пуста. Инструкция представляет собой объект следующего вида:
{
	ifNotEmpty: {...коллекция},
	then: [
		...
	]
}
В данной команде понятие текущего элемента может не задействоваться, поэтому её допустимо использовать вне docModel.
Члены
Имя
Описание
✖ ifNotEmpty
Коллекция для проверки (как описано в ✖ Коллекции ).
✖ then[]
Массив. Блок инструкций (из числа тех, которые применимы под ✖ forEachItem ), которые будут выполнены, если коллекция ifNotEmpty не пустая.
Члены (подробно)
Коллекция для проверки (как описано в ✖ Коллекции ).
Массив. Блок инструкций (из числа тех, которые применимы под ✖ forEachItem ), которые будут выполнены, если коллекция ifNotEmpty не пустая.
Выполнить блок инструкций только в том случае, если заданное условие выполняется для текущего элемента. Инструкция представляет собой объект следующего вида:
{
	ifCondition: {...условие},
	then: [
		...
	]
}
Члены
Имя
Описание
✖ ifCondition
Условие для проверки (как описано в ✖ Условия ).
✖ then[]
Массив. Блок инструкций (из числа тех, которые применимы под ✖ forEachItem ), которые будут выполнены, если соблюдается ifCondition.
Члены (подробно)
Условие для проверки (как описано в ✖ Условия ).
Массив. Блок инструкций (из числа тех, которые применимы под ✖ forEachItem ), которые будут выполнены, если соблюдается ifCondition.
Выдать элементы заданной коллекции в формате таблицы, по строке на элемент коллекции, с колонками и заголовками колонок, как указано. Инструкция представляет собой объект следующего вида:
{
	with: {...коллекция},
	sort: {...сортировка},
	emitAsItemsTable: [
		[ "column-header-spec", "column-content-spec" ],
		...
	]
}
Члены
Имя
Описание
✖ with
Коллекция (как описано в ✖ Коллекции ).
✖ sort
Необязательно. Спецификация сортировки (как указано в ✖ Спецификация сортировки ) для применения на коллекции with при её выводе в данную таблицу.
✖ emitItemsAsTable[]
Массив. Спецификация колонок таблицы. Каждый элемент задаёт колонку, в порядке слева направо, как двухэлементный подмассив:
Члены (подробно)
Коллекция (как описано в ✖ Коллекции ).
Необязательно. Спецификация сортировки (как указано в ✖ Спецификация сортировки ) для применения на коллекции with при её выводе в данную таблицу.
Массив. Спецификация колонок таблицы. Каждый элемент задаёт колонку, в порядке слева направо, как двухэлементный подмассив:
Члены
Имя
Описание
✖ [0]
Строка. Заголовок колонки. Интерпретируется как ✖ Строка (текст, ссылки на поля, и т. д.) .
✖ [1]
Строка. Содержимое колонки. Интерпретируется как ✖ Строка (текст, ссылки на поля, и т. д.) , причём элемент для префикса #item: - это элемент коллекции, предназначенный для данной строки таблицы.
Члены (подробно)
Строка. Заголовок колонки. Интерпретируется как ✖ Строка (текст, ссылки на поля, и т. д.) .
Строка. Содержимое колонки. Интерпретируется как ✖ Строка (текст, ссылки на поля, и т. д.) , причём элемент для префикса #item: - это элемент коллекции, предназначенный для данной строки таблицы.
Выдать элементы заданной коллекции в формате списка, по одной строке списка на элемент, полученной методом слияния заданных фрагментов в одну строку. Инструкция представляет собой объект следующего вида:
{
	with: {...коллекция},
	sort: {...сортировка},
	emitAsItemsList: [ "fragment-1-spec" [, "fragment-2-spec", ...] ]
}
Члены
Имя
Описание
✖ with
Коллекция (как описано в ✖ Коллекции ).
✖ sort
Необязательно. Спецификация сортировки (как описано в ✖ Спецификация сортировки ) для применения на коллекции with при её выводе в данный список.
✖ emitItemsAsList[]
Массив. Спецификация фрагментов, составляющих строку списка, в перечисленном порядке. Каждый фрагмент - строка, интерпретируемая как ✖ Строка (текст, ссылки на поля, и т. д.) , причём элемент для префикса #item: - это элемент коллекции, предназначенный для данной строки списка.
Члены (подробно)
Коллекция (как описано в ✖ Коллекции ).
Необязательно. Спецификация сортировки (как описано в ✖ Спецификация сортировки ) для применения на коллекции with при её выводе в данный список.
Массив. Спецификация фрагментов, составляющих строку списка, в перечисленном порядке. Каждый фрагмент - строка, интерпретируемая как ✖ Строка (текст, ссылки на поля, и т. д.) , причём элемент для префикса #item: - это элемент коллекции, предназначенный для данной строки списка.
Выдать элементы заданной коллекции как последовательность вложенных подэлементов (каждый независимо формируется сам по себе в соответствии с ✖ forEachItem ), предполагая места вывода данных элементов как их основные места расположения. Инструкция представляет собой объект следующего вида:
{
	with: {...коллекция},
	sort: {...сортировка},
	emitAsOwnItems: "basic|full"
}
По конвенции lpgwrite-example, элемент может быть выдан несколько раз в разных местах документа, но только одно из этих мест считается "домашним" местом расположения. Формат документа может полагать его, например, настоящим местом для размещения информации об элементе, а в других местах просто оставлять ссылку на это место (но может также и игнорировать данную подсказку).
Если для элемента оказывается несколько мест размещения соответственно emitAsOwnItems/emitAsExtItems, lpgwrite-example выбирает одно из них как домашнее место размещения, при этом места из emitAsOwnItems имеют больший приоритет быть выбранными.
Члены
Имя
Описание
✖ with
Коллекция (как описано в ✖ Коллекции ).
✖ sort
Необязательно. Спецификация сортировки (как описано в ✖ Спецификация сортировки ) для применения на коллекции with при её выводе в данный список.
✖ emitAsOwnItems
Строка. Указывает рекомендуемый информационный режим для элементов, выдаваемых по данной инструкции. Может быть одним из следующих...
Члены (подробно)
Коллекция (как описано в ✖ Коллекции ).
Необязательно. Спецификация сортировки (как описано в ✖ Спецификация сортировки ) для применения на коллекции with при её выводе в данный список.
Строка. Указывает рекомендуемый информационный режим для элементов, выдаваемых по данной инструкции. Может быть одним из следующих...
Члены
Имя
Описание
✖ basic
Нужно показывать только краткую часть информации по элементу.
✖ full
Нужно показывать полную информацию.
Члены (подробно)
Нужно показывать только краткую часть информации по элементу.
Нужно показывать полную информацию.
Выдать элементы заданной коллекции как последовательность вложенных подэлементов (каждый независимо формируется сам по себе в соответствии с ✖ forEachItem ) предполагая места вывода данных элементов как их второстепенные места расположения. Инструкция представляет собой объект следующего вида:
{
	with: {...коллекция},
	sort: {...сортировка},
	emitAsExtItems: "basic|full"
}
По конвенции lpgwrite-example, элемент может быть выдан несколько раз в разных местах документа, но только одно из этих мест считается "домашним" местом расположения. Формат документа может полагать его, например, настоящим местом для размещения информации об элементе, а в других местах просто оставлять ссылку на это место (но может также и игнорировать данную подсказку).
Если для элемента оказывается несколько мест размещения соответственно emitAsOwnItems/emitAsExtItems, lpgwrite-example выбирает одно из них как домашнее место размещения, при этом места из emitAsOwnItems имеют больший приоритет быть выбранными.
Члены
Имя
Описание
✖ with
Коллекция (как описано в ✖ Коллекции ).
✖ sort
Необязательно. Спецификация сортировки (как описано в ✖ Спецификация сортировки ) для применения на коллекции with при её выводе в данный список.
✖ emitAsOwnItems
Строка. Указывает рекомендуемый информационный режим для элементов, выдаваемых по данной инструкции. Может быть одним из следующих...
Члены (подробно)
Коллекция (как описано в ✖ Коллекции ).
Необязательно. Спецификация сортировки (как описано в ✖ Спецификация сортировки ) для применения на коллекции with при её выводе в данный список.
Строка. Указывает рекомендуемый информационный режим для элементов, выдаваемых по данной инструкции. Может быть одним из следующих...
Члены
Имя
Описание
✖ basic
Нужно показывать только краткую часть информации по элементу.
✖ full
Нужно показывать полную информацию.
Члены (подробно)
Нужно показывать только краткую часть информации по элементу.
Нужно показывать полную информацию.
Вывести в лог заданную коллекцию с опционально заданной меткой. Предназначено для отладочных целей. Инструкция представляет собой объект следующего вида:
{
	collDump: {...коллекция},
	label: "labelSpec"
}
Члены
Имя
Описание
✖ collDump
Коллекция (как описано в ✖ Коллекции ).
✖ label
Необязательно. Строка, задающая метку. Интерпретируется как в ✖ Строка (текст, ссылки на поля, и т. д.) .
Члены (подробно)
Коллекция (как описано в ✖ Коллекции ).
Необязательно. Строка, задающая метку. Интерпретируется как в ✖ Строка (текст, ссылки на поля, и т. д.) .
Задать имя модели документа и набор FDOM-элементов для включения в эту модель.
Члены
Имя
Описание
✖ name
Строка. Имя модели, которое будет использоваться для ссылки на эту модель в конфигурации рендерера (см. ✖ docModel ).
✖ rootItems
Начальный срез, с которого начинается включаемый набор. Набор корневых элементов получается через запрос и добавляется в список элементов. Далее набор расширяется, чтобы включать все элементы, на которые есть ссылка ( ✖ Встроенные ссылки ) из (или будут выдаваться в качестве подэлементов) элементов, уже имеющихся в списке - и так далее, последовательным расширением дерева из охваченных элементов. Далее этот набор можно сократить (см. ✖ excludeUnder , ✖ whitelistUnder ).
✖ excludeUnder
Коллекция корневых элементов для рекурсивного исключения из начального набора после ✖ rootItems . Если задана коллекция excludeUnder, то все элементы, находящиеся по FDOM-членству в поддереве любого из этих элементов (включая их самих), исключаются из документа и любых списков/таблиц, основанных на коллекциях, а встроенные ссылки на такие элементы являются нерабочими.
✖ whitelistUnder
Коллекция корневых элементов для фильтрации по "белому списку" в начальном наборе после ✖ rootItems . Если задана коллекция whitelistUnder, то все элементы, которые не находятся по FDOM-членству в поддереве какого-либо из этих элементов (включая их самих), исключаются из документа и любых списков/таблиц, основанных на коллекциях, а встроенные ссылки на такие элементы являются нерабочими.
Обратите внимание, что в модели документа порядок, в котором элементы набора выдаются в результирующий документ, определён, и указывается в необходимых местах (см. далее).
Члены (подробно)
Строка. Имя модели, которое будет использоваться для ссылки на эту модель в конфигурации рендерера (см. ✖ docModel ).
Начальный срез, с которого начинается включаемый набор. Набор корневых элементов получается через запрос и добавляется в список элементов. Далее набор расширяется, чтобы включать все элементы, на которые есть ссылка ( ✖ Встроенные ссылки ) из (или будут выдаваться в качестве подэлементов) элементов, уже имеющихся в списке - и так далее, последовательным расширением дерева из охваченных элементов. Далее этот набор можно сократить (см. ✖ excludeUnder , ✖ whitelistUnder ).
Члены
Имя
Описание
✖ query
Запрос, выдающий корневые элементы. Исходная текущая коллекция для этого запроса - пустая, поэтому, чтобы он имел смысл, его следует начинать с базового запроса { with: ... } (см. ✖ <QuerySpec> ).
✖ sort
Спецификация сортировки, чтобы определить относительный порядок корневых элементов в результирующем документе. Обратите внимание, что это порядок только для верхнего уровня: все подэлементы будут выдаваться после содержащего элемента и перед следующим элементом верхнего уровня, а упорядочивание в подэлементах задаётся уже в соответствующимх инструкциями выдачи.
Члены (подробно)
Запрос, выдающий корневые элементы. Исходная текущая коллекция для этого запроса - пустая, поэтому, чтобы он имел смысл, его следует начинать с базового запроса { with: ... } (см. ✖ <QuerySpec> ).
Спецификация сортировки, чтобы определить относительный порядок корневых элементов в результирующем документе. Обратите внимание, что это порядок только для верхнего уровня: все подэлементы будут выдаваться после содержащего элемента и перед следующим элементом верхнего уровня, а упорядочивание в подэлементах задаётся уже в соответствующимх инструкциями выдачи.
Коллекция корневых элементов для рекурсивного исключения из начального набора после ✖ rootItems . Если задана коллекция excludeUnder, то все элементы, находящиеся по FDOM-членству в поддереве любого из этих элементов (включая их самих), исключаются из документа и любых списков/таблиц, основанных на коллекциях, а встроенные ссылки на такие элементы являются нерабочими.
Эта опция полезна, если вам нужно жёстко исключить деревья определённых элементов из документа, и достижение этого через настройку ✖ rootItems оказывается недостаточно практичным.
excludeUnder является противоположным ✖ whitelistUnder по смыслу, и обычно их не следует использовать вместе. Однако, если всё же использовать, то excludeUnder применяется первым.
Коллекция корневых элементов для фильтрации по "белому списку" в начальном наборе после ✖ rootItems . Если задана коллекция whitelistUnder, то все элементы, которые не находятся по FDOM-членству в поддереве какого-либо из этих элементов (включая их самих), исключаются из документа и любых списков/таблиц, основанных на коллекциях, а встроенные ссылки на такие элементы являются нерабочими.
Эта опция полезна, если вы генерируете документ из ограниченного поддерева FDOM, и нужно предохраниться от "утечки" информации из ненужных поддеревьев из-за случайной ссылки на них.
whitelistUnder является противоположным ✖ excludeUnder по смыслу, и обычно их не следует использовать вместе. Однако, если всё же использовать, то excludeUnder применяется первым.
"Встроенная" программа для создания страницы документации общего вида, используемая для генерации документации самого Logipard, и подходящая для быстрого старта. Её следует использовать через возможность LPSON file (см. ✖ file(...): вставка значения из файла JSON/LPSON ) с некоторыми добавленными параметрами, как показано ниже; она определяет модель документа с именем DocMain:
	...
	lpgwrite-example: {
		...,
		program: file("${LP_HOME}/lpgwrite-example-docprg.lpson" $, {
			docprgPrologue: [ ... ], // инструкции для вставки в начало
			docRootItems: {...запрос},
			LS_EXTENDS: "Расширяет (является)",
			LS_MEMBERS: "Члены",
			LS_NAME: "Имя",
			LS_DESCRIPTION: "Описание",
			LS_MEMBERS_FROM_EXTENTS: "Члены из расширеяемых",
			LS_ARGUMENTS: "Параметры",
			LS_RETURNS: "Возвращает:",
			LS_ERRORS: "Ошибки:",
			LS_MEMBERS_DETAILED: "Члены (подробно)",
			LS_MEMBERS_FROM_EXTENTS_DETAILED: "Члены из расширяемых (подробно)",
			LS_ARGUMENTS_DETAILED: "Параметры (подробно)",
			LS_NOTES: "Примечания",
			LS_PROPERTIES: "Свойства",
			LS_PROPERTIES_FROM_EXTENTS: "Свойства из расширяемых",
			LS_METHODS: "Методы",
			LS_METHODS_FROM_EXTENTS: "Методы из расширяемых"
		}),
		renders: [
			{
				docModel: "DocMain",
				renderer: "${LP_HOME}/lpgwrite-example-render-html" $,
				...
			},
			{
				docModel: "DocMain",
				renderer: "${LP_HOME}/lpgwrite-example-render-md" $,
				...
			},
			...
		]
	}
Она также добавляет несколько трактовок поверх модели FDOM, интерпретируя некоторые члены и теги как указания по предметной области некого обобщённого языка программирования. Подробности описаны ниже.
Первый параграф контента элемента считается его кратким описанием. Это часть, видимая в кратком режиме показа элемента, в дополнение к наиболее существенным даным элемента (а именно, ✖ %extends: список расширяемых объектов , ✖ %member: элементы-члены , ✖ %arg: элементы-аргументы , ✖ %return: описание возвращаемого значения , ✖ %errors: описание ошибок ).
Список объектов (в смысле имён элементов с документацией по ним), которые текущий документируемый объект 'расширяет' в некотором смысле, вкладваемом в это понятие в целевом языке - например, базовые классы или записи даных. Члены ( ✖ %member: элементы-члены ), методы ( ✖ %method: элементы-методы ) и свойства ( ✖ %property: элементы-свойства ) расширяемых объектов будут добавлены в документацию объекта в специальных второстепенных секциях. Список задаётся добавлением 'расширяемых' элементов как тегов к члену %extends или %.extends (сам этот член не должен содержать никакого контента, только эти добавляемые теги).
Список объектов (в смысле имён элементов с документацией по ним), которые являются 'членами' текущего документируемого объекта в некотором смысле, вкладываемом в это понятие в целевом языке. Например, члены структуры. Объекты-члены (в значении целевого языка) - это элементы-члены (в значении FDOM) с добавленным тегом %member или %.member. Возможно совмещать его с тегами %property/%.property или %method/%.method.
Список объектов (в смысле имён элементов с документацией по ним), которые являются 'аргументами' текущего документируемого объекта в некотором смысле, вкладываемом в это понятие в целевом языке. Например, аргументы функции или конструктора. Объекты-аргументы - это элементы-члены (в значении FDOM) с добавленным тегом %arg или %.arg.
Описание того, что является 'возвращаемым значением' текущего документируемого объекта в смысле, вкладываемом в это понятие в целевом языке. Например, если объект - это функция, которая возвращает значение. Содержимое члена %return предполагается описанием этого значения и добавляется в отведённую для этого секцию документации. Член %return не должен иметь заголовка.
Описание того, какие 'ошибки' возможны в пределах текущего документируемого объекта в смысле, вкладываемом в это понятие в целевом языке. Например, список возможных ошибок, выбрасываемых из объекта. Содержимое члена %errors предполагается описанием этих ошибок и добавляется в отведённую для этого секцию документации. Член %errors не должен иметь заголовка.
Под все 'ошибки' отведён только один член FDOM, но тут можно задействовать гибкость FDOM: член %errors может иметь подчлены, даже помеченные тегами %member.
lpgwrite-example-docprg предполагает упорядочивание вложенных элементов в соответствии с содержимым члена %order, используя натуральную строковую сортировку.
По умолчанию, lpgwrite-example-docprg включает в оглавление только элементы, которые не помечены %arg, %member, %method или %property, и имеют "публичные" краткие имена (не начинающиеся с % или #). Если у вас есть такой элемент, но вы хотите всё равно включить его в оглавление, добавьте ему тег %for-toc.
Список объектов (в смысле имён элементов с документацией по ним), которые являются 'методами' текущего документируемого объекта в некотором смысле, вкладываемом в это понятие в целевом языке. Например, методы объекта языка. Объекты-методы - это элементы-члены (в значении FDOM) с добавленным тегом %method или %.method. Возможно совмещать его с тегами %member/%.member или %property/%.property.
Обратите внимание, что для языков, предполагающих перегрузку методов, такими как Java, C++, частично JS, может быть непрактично использовать в качестве FDOM-имён ни дословные краткие имена, ни полные имена с сигнатурой - чаще всего лучше использовать некое сокращённо-модифицированное имя для FDOM и %title для указания полного человекочитаемого имени.
Список объектов (в смысле имён элементов с документацией по ним), которые являются 'свойствами' текущего документируемого объекта в некотором смысле, вкладываемом в это понятие в целевом языке. Например, свойства объекта языка. Объекты-свойства - это элементы-члены (в значении FDOM) с добавленным тегом %property или %.property. Возможно совмещать его с тегами %member/%.member или %method/%.method.
Любые дополнительные фрагменты информации, которые следует добавить в контент перед тем, как переходить к непосредственным членам; возможно, содержащие подэлементы; под них отведена подсекция 'Примечания'. Предполагаемое использование - через добавление анонимных членов к элемент/%note следующим образом:
#LP main-item {
Контент основного элемента
#LP ./%note/~ {
#LP контент примечания 1 (внутри элемента), с членом
#LP ./note-1-member: член примечания 1
#LP }
<#LP ./%note/~: контент примечания 2 (внутри элемента), простого#>
Ещё контент основного элемента
#LP }
#LP main-item/%note/~: контент примечания 3 (снаружи элемента)
Контент примечаний добавляется после контента основного элемента, но перед описанием членов элемента, развёрнутым образом, в подсекции 'Примечания'.
Примечания могут быть особенно полезны, когда добавляются из других мест, чем основной контент элемента, возможно даже из других файлов-исходников. Таким образом можно добавлять любые полезные комментарии, относящиеся к конкретному элементу, непосредственно из участка кода, контекстуально с ними связанного; потом они будут сгруппированы в одно место как секция примечаний к элементу.
Ещё одно преимущество разбиения примечаний на несколько подобъектов под членом %notes - появляется больше вариантов для контроля за порядком их следования в документе. Добавление непосредственно к контенту элемента из разных исходников не гарантирует, в каком порядке в итоге будут следовать фрагменты, и могут даже нарушиться ваши предположения о том, что будет считаться краткой частью информации для данного элемента. С другой стороны, место расположения секции 'Примечания' чётко определено, и на члены %notes под ней действуют подсказки %order ( ✖ %order: контроль порядка следования ).
Любой элемент-примечание может содержать подэлементы, которые будут показаны в пределах данного элемента обычным образом (с заголовками и т. д.), но пользуйтесь этой возможностью с осторожностью, т. к. анонимные элементы и его подэлементы не имеют гарантированного FDOM-имени, по которому на них можно делать ссылки. Можно определить алиас, но он будет действовать только в пределах данного входного файла.
Непосредственный контент самого %note и его неанонимные члены не используются. В целях разделения обязанностей, для такого есть отдельная возможность с несколько другим предполагаемым применением - см. ✖ extra .
В члене %extra можно указать контент, который будет показан после основного контента элемента внутри текста, так же, как если бы он был написан в конце самого контента элемента. Конкретнее говоря, он ведёт себя как дополнительный подэлемент без заголовка, следующий перед секцией с подробным описанием членов элемента (и перед секцией 'Примечания', если таковая имеется), что выглядит как продолжение контента основного элемента. Предполагаемое использование - через добавление контента и членов к item/%extra следующим образом:
#LP main-item {
Контент основного элемента
#LP ./%extra: непосредственный контент extra для основного элемента
#LP ./%extra/A %member: член A в extra основного элемента (внутри элемента)
Ещё контент основного элемента
#LP ./ownMember %member: собственный член основного элемента
#LP }
#LP main-item/%extra/B %member {
член B extra основного элемента (снаружи элемента)
#LP ./extra-B-member: дополнительный член элемента B
#LP }
Данный пример приводит к следующей видимой структуре секции main-item:
# main-item
Контент основного элемента
Члены:
 ownMember | собственный член основного элемента
// конец краткого описания main-item
Ещё контент основного элемента
 // здесь начинаются данные из %extra
 непосредственный контент extra для основного элемента
 Члены: // от %extra
 A | член A в extra основного элемента (внутри элемента)
 B | член B extra основного элемента (снаружи элемента)
 Члены (подробно): // of %extra
 # A
 член A в extra основного элемента (внутри элемента)
 # B
 член B extra основного элемента (снаружи элемента)
  # extra-B-member
  дополнительный член элемента B
 // здесь заканчиваются данные из %extra
Члены (подробно): // от main-item
 # ownMember
 собственный член основного элемента
Основное назначение %extra - контроль порядка показа членов в пограничных случаях.
lpgwrite-example-docprg размещает непосредственные члены элемента, помеченные тегами %member, %arg, %return, %errors, и аналогичные из расширяемых элементов, в конце краткого описания элемента, но перед остальной частью контента. Иногда это может нарушить связность информации (например, фрагмент кода с ограничителями, представляющий общий вид элемента, которые лучше разместить перед списком членов). Чтобы обойти это неудобство, можно переместить список членов, аргументов и т. д. из самого элемента в члены его элемента ./%extra. Они всё ещё будут выглядеть "встроенными" в общий текст, но логически уже будут частью отдельного элемента, и не будут участвовать в общем порядке показа частей основного элемента.
Параметры для lpgwrite-example-docprg задаются через добавленные контекстные переменные в LPSON-операторе file:
Члены
Имя
Описание
✖ docprgPrologue
Массив инструкций для добавления в самом начале документационной программы. Обычно - определения алиасов для применения в ✖ docRootItems .
✖ docRootItems
Секция rootItems (see ✖ rootItems ) генерируемой модели (DocMain). Этот объект будет задан как rootItems полностью, как написан, без дополнений и обёрток, так что пользователь не должен полагаться здесь на какие-либо умолчания.
✖ localization
Группа предопределённых заголовков, используемых в сгенерированной странице. Вынесены в параметры, чтобы сделать их локализуемыми в этой точке. Этот объект - словарь строк, с именами членов, означающих смысл каждой строки:
  • LS_EXTENDS: заголовок для секции со списком элементов из списка %extends list (т. е. добавленных тегами к члену %extends)
  • LS_MEMBERS: заголовок для секции с таблицей элементов с тегами %member
  • LS_MEMBERS_FROM_EXTENTS: заголовок для секции со списком элементов с тегами %member внутри элементов из списка %extends, на всю глубину дерева "расширяемых"
  • LS_PROPERTIES: заголовок для секции с таблицей элементов с тегами %property
  • LS_PROPERTIES_FROM_EXTENTS: заголовок для секции со списком элементов с тегами %property внутри элементов из списка %extends, на всю глубину дерева "расширяемых"
  • LS_METHODS: заголовок для секции с таблицей элементов с тегами %method
  • LS_METHODS_FROM_EXTENTS: заголовок для секции со списком элементов с тегами %method внутри элементов из списка %extends list, на всю глубину дерева "расширяемых"
  • LS_ARGUMENTS: заголовок для секции с таблицей элементов с тегами %arg
  • LS_NAME: заголовок для колонки таблицы с именем элемента (1-я)
  • LS_DESCRIPTION: заголовок для колонки таблицы с описанием элемента (2-я)
  • LS_RETURNS: заголовок для секции с контентом члена %return
  • LS_ERRORS: заголовок для секции с контентом члена %error
  • LS_MEMBERS_DETAILED: заголовок для секции с полными документациями элементов с тегами %member
  • LS_MEMBERS_FROM_EXTENTS_DETAILED: заголовок для секции с полными документациями элементов с тегами %member, определёнными в элементах из списка %extends, на всю глубину дерева "расширяемых"
  • LS_ARGUMENTS_DETAILED: заголовок для секции с полными документациями элементов с тегами %arg
  • LS_NOTES: заголовок для секции с полными документациями подчленов элемента %notes
Члены (подробно)
Массив инструкций для добавления в самом начале документационной программы. Обычно - определения алиасов для применения в ✖ docRootItems .
Секция rootItems (see ✖ rootItems ) генерируемой модели (DocMain). Этот объект будет задан как rootItems полностью, как написан, без дополнений и обёрток, так что пользователь не должен полагаться здесь на какие-либо умолчания.
Группа предопределённых заголовков, используемых в сгенерированной странице. Вынесены в параметры, чтобы сделать их локализуемыми в этой точке. Этот объект - словарь строк, с именами членов, означающих смысл каждой строки:
  • LS_EXTENDS: заголовок для секции со списком элементов из списка %extends list (т. е. добавленных тегами к члену %extends)
  • LS_MEMBERS: заголовок для секции с таблицей элементов с тегами %member
  • LS_MEMBERS_FROM_EXTENTS: заголовок для секции со списком элементов с тегами %member внутри элементов из списка %extends, на всю глубину дерева "расширяемых"
  • LS_PROPERTIES: заголовок для секции с таблицей элементов с тегами %property
  • LS_PROPERTIES_FROM_EXTENTS: заголовок для секции со списком элементов с тегами %property внутри элементов из списка %extends, на всю глубину дерева "расширяемых"
  • LS_METHODS: заголовок для секции с таблицей элементов с тегами %method
  • LS_METHODS_FROM_EXTENTS: заголовок для секции со списком элементов с тегами %method внутри элементов из списка %extends list, на всю глубину дерева "расширяемых"
  • LS_ARGUMENTS: заголовок для секции с таблицей элементов с тегами %arg
  • LS_NAME: заголовок для колонки таблицы с именем элемента (1-я)
  • LS_DESCRIPTION: заголовок для колонки таблицы с описанием элемента (2-я)
  • LS_RETURNS: заголовок для секции с контентом члена %return
  • LS_ERRORS: заголовок для секции с контентом члена %error
  • LS_MEMBERS_DETAILED: заголовок для секции с полными документациями элементов с тегами %member
  • LS_MEMBERS_FROM_EXTENTS_DETAILED: заголовок для секции с полными документациями элементов с тегами %member, определёнными в элементах из списка %extends, на всю глубину дерева "расширяемых"
  • LS_ARGUMENTS_DETAILED: заголовок для секции с полными документациями элементов с тегами %arg
  • LS_NOTES: заголовок для секции с полными документациями подчленов элемента %notes
Все эти строки, на самом деле, опциональны, но рекомендуется задать их все. Значения по умолчанию будут иметь префикс [D] для указания, что это заменители по умолчанию, которые лучше исправить.
Интерфейс, который рекомендуется реализовать считывателям моделей FDOM, сгенерированных Logipard на стадии компиляции. В частности, он реализуется в ✖ logipard/lpgread-basic-json.js
Тип, инкапсулирующий узел элемента FDOM в движке данного считывателя. Может быть "нулевым элементом" (не путать с null-значением). Если элемент в движке данного считывателя - это ресурс, который требуется явным образом освободить после использования, документация по реализации должна подчеркнуть это, указать время жизни для объекта, и должен быть предоставлен способ освобождения.
Члены
Имя
Описание
✖ .content
Свойство только для чтения. Контент элемента (текст, внутритекстовые ссылки, и всё прочее, что поддерживает модель, обслуживаемая данным считывателем.)
✖ .name
Свойство только для чтения, строка. Полный путь элемента (без именных алиасов).
✖ .shortName
Свойство только для чтения, строка. Краткое имя элемента (последний сегмент полного имени-пути).
✖ .tags
Свойство только для чтения. Коллекция тегов элемента.
✖ .members
Свойство только для чтения. Коллекция членов элемента.
✖ .isNull
Свойство только для чтения, булевое. Проверка, что элемент пустой (true) или нет (false).
✖ .parent
Свойство только для чтения, ✖ <Item> . Возвращает родительский элемент (тот, членом которого является данный). Для корневого элемента возвращает null (именно null-значение, а не нулевой элемент).
✖ .isConditionTrue(lpqCtx, condSpec)
Проверить, что элемент удовлетворяет некоторому условию; проверка должна делаться в рамках заданного контекста запроса (чтобы иметь возможность использовать алиасы условий и коллекций).
Нулевой элемент обозначает элемент с некорректным путём или пустой. Они считаются эквивалентными: если имя указывает на элемент, не присутствующий в модели явным образом, он считается пустым элементом, и наоборот - пустой элемент считается отсутствием элемента при добавлении в коллекцию (то есть, не добавляется, даже если его добавление явно указывается).
Члены (подробно)
Свойство только для чтения. Контент элемента (текст, внутритекстовые ссылки, и всё прочее, что поддерживает модель, обслуживаемая данным считывателем.)
Возвращает:
Массив элементов, каждый из которых может быть одним из следующего:
  • строка, для текстового контента
  • объект вида { ref: <Item>, text: string } (ref - это значение типа ✖ <Item> ), для внутритекстовой ссылки на другой элемент. Текст - это альтернативный видимый текст для данной ссылки, если он не пустой, то рекомендуется использовать его, а не заголовок ссылаемого элемента по умолчанию.
  • по необходимости, любой другой тип фрагмента, специфичный для данного FDOM-считывателя и обслуживаемой им модели
Свойство только для чтения, строка. Полный путь элемента (без именных алиасов).
Свойство только для чтения, строка. Краткое имя элемента (последний сегмент полного имени-пути).
Свойство только для чтения. Коллекция тегов элемента.
Возвращает:
✖ <Collection> , коллекция тегов.
Свойство только для чтения. Коллекция членов элемента.
Возвращает:
✖ <Collection> , коллекция членов элемента.
Свойство только для чтения, булевое. Проверка, что элемент пустой (true) или нет (false).
Свойство только для чтения, ✖ <Item> . Возвращает родительский элемент (тот, членом которого является данный). Для корневого элемента возвращает null (именно null-значение, а не нулевой элемент).
Проверить, что элемент удовлетворяет некоторому условию; проверка должна делаться в рамках заданного контекста запроса (чтобы иметь возможность использовать алиасы условий и коллекций).
Параметры
Имя
Описание
✖ lpqCtx
✖ <QueryContext> , контекст запроса.
✖ condSpec
✖ <Condition> , условие.
Возвращает:
true, если элемент удовлетворяет условию, false, если нет.
Параметры (подробно)
✖ <QueryContext> , контекст запроса.
✖ <Condition> , условие.
Методы
.isConditionTrue(lpqCtx, condSpec)
Объект, содержащий предкомпилированный запрос, который можно хранить в переменной или ином слоте для значения. Сам по себе не предоставляет полезных свойств или методов, предполагаемое использование - в контекстах, где требуется указать запрос. Если в движке данного считывателя этот объект - ресурс, который требуется явным образом освободить после использования, документация по реализации должна подчеркнуть это, указать время жизни для объекта, и должен быть предоставлен способ освобождения.
Дескриптор для выполнения запросов, который также содержит вспомогательное состоение (алиасы пространства имён, именованные ссылки на коллекцию, и предмет текущего запроса). Если в движке данного считывателя этот объект - ресурс, который требуется явным образом освободить после использования, документация по реализации должна подчеркнуть это, указать время жизни для объекта, и должен быть предоставлен способ освобождения.
Члены
Имя
Описание
✖ .nameAlias(aliasName, item)
Задать алиас имени элемента (который должен быть корректным кратким именем), могущий впоследствии использоваться как самостоятельное имя элемента, или как начальный сегмент для другого имени элемента в пределах данного ✖ <QueryContext> . Поведение в случае уже существующего алиаса с таким же именем зависит от реализации.
✖ .collectionAlias(aliasName, ...collectionSpecs)
Задать именованный алиас коллекции, который может быть использован позднее для ссылки на эту коллекцию в пределах данного контекста ( ✖ <CollectionSpec> ). Коллекция строится из коллекций, соответствующих каждому элементу списка спецификаций. Алиас - постоянный в пределах данного контекста, в отличие от локального алиаса ( ✖ Задать локальный алиас коллекции ["alias ..."] ).
✖ .queryAlias(aliasName, ...querySpecs)
Задать именованный алиас запроса, который может быть использован позднее для ссылки на этот запрос в пределах данного контекста ( ✖ <QuerySpec> ). Список трактуется как составной запрос.
✖ .conditionAlias(aliasName, condSpec)
Задать именованный алиас условия, который может быть использован позднее для ссылки на это условие в пределах данного контекста ( ✖ <Condition> ).
✖ .item([baseItem ,] name)
Вернуть элемент по заданному имени-пути, полному или относительно заданного базового элемнета. Первый краткоимённый сегмент полного имени может быть алиасом имени, определённым в данном ✖ <QueryContext> .
✖ .collection(...collectionSpecs)
Возвращает коллекцию, заданную списком спецификаций элементов коллекции. Каждый элемент списка - ✖ <CollectionSpec> .
✖ .with(...collectionSpecs)
Задать текущую коллекцию для последующего запроса (вызова ✖ .query(...querySpecs) ). Коллекция строится из коллекций, соответствующих каждому элементу списка. .with, по сути, инициирует цепочку запроса, но может быть использован и в её середине, чтобы заменить текущую коллекцию на некотором шаге.
✖ .query(...querySpecs)
Выполнить запрос, или список запросов, трактуемый как составной запрос, с учётом того, что текущая коллекция задана предыдущим ✖ .with(...collectionSpecs) или получена от предыдущих вызовов .query. Обратите внимание, что полученная коллекция не возвращается немедленно, а становится новой текущей коллекцией.
✖ .teardownCollection()
Завершить запрос и вернуть результат (текущую коллекцию на момент вызова). Сама текущая коллекция сбрасывается, так что следующий запрос должен быть инициализирован повторно, начиная с ✖ .with(...collectionSpecs) .
✖ .currentCollectionAlias(aliasName)
Задать именованный алиас коллекции для текущей коллекции, который может быть использован позже для ссылки на эту коллекцию в пределах данного контекста ( ✖ <CollectionSpec> ). Может быть использован только в процессе запроса (пока имеет значение текущая коллекция), в противном вызов данного метода - ошибка. Это локальный алиас запроса, в отличие от постоянного ( ✖ Задать локальный алиас коллекции ["alias ..."] ).
✖ .compileQuery(...querySpecs)
Скомпилировать запрос в объект-дескриптор, который может быть использован позже для ссылки на этот запрос в пределах данного контекста ( ✖ <QuerySpec> ). Список трактуется как составной запрос.
Члены (подробно)
Задать алиас имени элемента (который должен быть корректным кратким именем), могущий впоследствии использоваться как самостоятельное имя элемента, или как начальный сегмент для другого имени элемента в пределах данного ✖ <QueryContext> . Поведение в случае уже существующего алиаса с таким же именем зависит от реализации.
Параметры
Имя
Описание
✖ aliasName
Имя алиаса, строка.
✖ item
Алиасируемый элемент. Строка (полное имя-путь, возможно с участием другого алиаса) или ✖ <Item> .
Возвращает:
Сам исходный объект ( ✖ <QueryContext> ), что позволяет строить цепочку из вызовов.
Параметры (подробно)
Имя алиаса, строка.
Алиасируемый элемент. Строка (полное имя-путь, возможно с участием другого алиаса) или ✖ <Item> .
Задать именованный алиас коллекции, который может быть использован позднее для ссылки на эту коллекцию в пределах данного контекста ( ✖ <CollectionSpec> ). Коллекция строится из коллекций, соответствующих каждому элементу списка спецификаций. Алиас - постоянный в пределах данного контекста, в отличие от локального алиаса ( ✖ Задать локальный алиас коллекции ["alias ..."] ).
Параметры
Имя
Описание
✖ collectionSpecs
Каждый элемент списка - ✖ <CollectionSpec> .
Возвращает:
Сам исходный объект ( ✖ <QueryContext> ), что позволяет строить цепочку из вызовов.
В зависимости от реализации, может быть предоставлен метод для удаления алиаса.
Параметры (подробно)
Каждый элемент списка - ✖ <CollectionSpec> .
Задать именованный алиас запроса, который может быть использован позднее для ссылки на этот запрос в пределах данного контекста ( ✖ <QuerySpec> ). Список трактуется как составной запрос.
Параметры
Имя
Описание
✖ aliasName
Строка, имя алиаса.
✖ querySpecs
Каждый элемент списка - ✖ <QuerySpec> .
Возвращает:
Сам исходный объект ( ✖ <QueryContext> ), что позволяет строить цепочку из вызовов.
В зависимости от реализации, может быть предоставлен метод для удаления алиаса.
Параметры (подробно)
Строка, имя алиаса.
Каждый элемент списка - ✖ <QuerySpec> .
Задать именованный алиас условия, который может быть использован позднее для ссылки на это условие в пределах данного контекста ( ✖ <Condition> ).
Параметры
Имя
Описание
✖ aliasName
Строка, имя алиаса.
✖ condSpec
Спецификация условия, одиночное ✖ <Condition> .
Возвращает:
Сам исходный объект ( ✖ <QueryContext> ), что позволяет строить цепочку из вызовов.
В зависимости от реализации, может быть предоставлен метод для удаления алиаса.
Параметры (подробно)
Строка, имя алиаса.
Спецификация условия, одиночное ✖ <Condition> .
Вернуть элемент по заданному имени-пути, полному или относительно заданного базового элемнета. Первый краткоимённый сегмент полного имени может быть алиасом имени, определённым в данном ✖ <QueryContext> .
Параметры
Имя
Описание
✖ baseItem
Необязательный параметр. Базовый элемент, от которого строится путь ✖ name . ✖ <Item> , строка или массив строк.
✖ name
Путь к элементу. Строка или массив строк. Может начинаться с алиаса имени, определённого в данном ✖ <QueryContext> .
Возвращает:
Искомый элемент, в виде ✖ <Item> . Всегда должен быть не null-объект - несуществующий в модели элемент неявно создаётся в виде нулевого элемента.
Параметры (подробно)
Необязательный параметр. Базовый элемент, от которого строится путь ✖ name . ✖ <Item> , строка или массив строк.
Строка трактуется как полное имя-путь, массив строк трактуется как полный путь, заданный списком из кратких имён-компонентов. Путь, заданный строками или массивом компонент, может начинаться с алиаса имени, определённого в данном ✖ <QueryContext> .
Если относительное имя задано, скажем, some/path, а заданный baseItem имеет путь base/item, то полученный элемент ищется по пути base/item/some/path.
Путь к элементу. Строка или массив строк. Может начинаться с алиаса имени, определённого в данном ✖ <QueryContext> .
Возвращает коллекцию, заданную списком спецификаций элементов коллекции. Каждый элемент списка - ✖ <CollectionSpec> .
Параметры
Имя
Описание
✖ collectionSpecs
Каждый элемент списка - ✖ <CollectionSpec> .
Возвращает:
Коллекция, в виде ✖ <Collection>
Каждая спецификация элемента коллекции обрабатывается и добавляется к результату в индивидуальном порядке, независимо от логики спецификаций других элементов, но в любом случае каждый ✖ <Item> содержится в результирующей коллекции не более одного раза.
Параметры (подробно)
Каждый элемент списка - ✖ <CollectionSpec> .
Задать текущую коллекцию для последующего запроса (вызова ✖ .query(...querySpecs) ). Коллекция строится из коллекций, соответствующих каждому элементу списка. .with, по сути, инициирует цепочку запроса, но может быть использован и в её середине, чтобы заменить текущую коллекцию на некотором шаге.
Параметры
Имя
Описание
✖ collectionSpecs
Каждый элемент списка - ✖ <CollectionSpec> .
Возвращает:
Сам исходный объект ( ✖ <QueryContext> ), что позволяет строить цепочку из вызовов.
Параметры (подробно)
Каждый элемент списка - ✖ <CollectionSpec> .
Выполнить запрос, или список запросов, трактуемый как составной запрос, с учётом того, что текущая коллекция задана предыдущим ✖ .with(...collectionSpecs) или получена от предыдущих вызовов .query. Обратите внимание, что полученная коллекция не возвращается немедленно, а становится новой текущей коллекцией.
Параметры
Имя
Описание
✖ querySpecs
Каждый элемент списка - ✖ <QuerySpec> .
Возвращает:
Сам исходный объект ( ✖ <QueryContext> ), что позволяет строить цепочку из вызовов.
Параметры (подробно)
Каждый элемент списка - ✖ <QuerySpec> .
Завершить запрос и вернуть результат (текущую коллекцию на момент вызова). Сама текущая коллекция сбрасывается, так что следующий запрос должен быть инициализирован повторно, начиная с ✖ .with(...collectionSpecs) .
Возвращает:
Результат, в виде ✖ <Collection>
Задать именованный алиас коллекции для текущей коллекции, который может быть использован позже для ссылки на эту коллекцию в пределах данного контекста ( ✖ <CollectionSpec> ). Может быть использован только в процессе запроса (пока имеет значение текущая коллекция), в противном вызов данного метода - ошибка. Это локальный алиас запроса, в отличие от постоянного ( ✖ Задать локальный алиас коллекции ["alias ..."] ).
Возвращает:
Сам исходный объект ( ✖ <QueryContext> ), что позволяет строить цепочку из вызовов.
Ошибки:
Выбрасывает ошибку, если в контексте не задана текущая коллекция.
В зависимости от реализации, может быть предоставлен метод для удаления алиаса.
Скомпилировать запрос в объект-дескриптор, который может быть использован позже для ссылки на этот запрос в пределах данного контекста ( ✖ <QuerySpec> ). Список трактуется как составной запрос.
Параметры
Имя
Описание
✖ querySpecs
Каждый элемент списка - ✖ <QuerySpec> .
Возвращает:
Объект скомпилированного запроса ( ✖ <Query> ).
Этот метод можно понимать как "анонимную" версию ✖ .queryAlias(aliasName, ...querySpecs) для большего удобства применения на стороне кода и возможной оптимизации, т. к. различные запросы обычно достаточно многочисленны и разнообразны, и может быть непрактично заводить для каждого именной алиас.
В зависимости от реализации, может быть предоставлен метод для удаления скомпилированного запроса.
Параметры (подробно)
Каждый элемент списка - ✖ <QuerySpec> .
Методы
.nameAlias(aliasName, item)
.collectionAlias(aliasName, ...collectionSpecs)
.queryAlias(aliasName, ...querySpecs)
.conditionAlias(aliasName, condSpec)
.item([baseItem ,] name)
.collection(...collectionSpecs)
.with(...collectionSpecs)
.query(...querySpecs)
.teardownCollection()
.currentCollectionAlias(aliasName)
.compileQuery(...querySpecs)
Тип, который инкапсулирует завершённую и читабельную коллекцию из ✖ <Item> . Должно быть итерабельным в JS (for...of). Коллекция не должна содержать нулевых (с ✖ .isNull = true) элементов, или одних и тех же элементов несколько раз. Если в движке данного считывателя этот объект - ресурс, который требуется явным образом освободить после использования, документация по реализации должна подчеркнуть это, указать время жизни для объекта, и должен быть предоставлен способ освобождения.
Члены
Имя
Описание
✖ .size
Свойство только для чтения, число. Размер коллекции (сколько в ней элементов).
✖ .contains(item)
Проверить, содержит ли коллекция данный элемент. Должен быть false для любого нулевого (с ✖ .isNull = true) элемента.
✖ [Symbol.iterator]
Коллекция должна быть JS-перечисляемым объектом, выдавая содержащиеся ✖ <Item> в некотором порядке (for (var item of collection)). Рекомендуется, чтобы реализация сохраняла элементы, объявленные в одном и том же исходнике, в том же порядке, в каком они объявлены, но пользователю не рекомендуется полагаться на это допущение.
Члены (подробно)
Свойство только для чтения, число. Размер коллекции (сколько в ней элементов).
Проверить, содержит ли коллекция данный элемент. Должен быть false для любого нулевого (с ✖ .isNull = true) элемента.
Параметры
Имя
Описание
✖ item
✖ <Item> , элемент для проверки на присутствие в коллекции.
Возвращает:
true, если элемент содержится в коллекции, false, если нет
Параметры (подробно)
✖ <Item> , элемент для проверки на присутствие в коллекции.
Коллекция должна быть JS-перечисляемым объектом, выдавая содержащиеся ✖ <Item> в некотором порядке (for (var item of collection)). Рекомендуется, чтобы реализация сохраняла элементы, объявленные в одном и том же исходнике, в том же порядке, в каком они объявлены, но пользователю не рекомендуется полагаться на это допущение.
Методы
.contains(item)
Элемент списка спецификаций условия. Соответствует концепции ✖ Задание условий в FDOM. Может быть одним из следующих объектов...
  • строка: ссылка на условие по алиасу
  • булевой: тип условия "булевая константа"
  • { isAnyOf: <CollectionSpec> }: условие типа isAnyOf
  • { hasMembersNamed: <string | RegExp> }: условие типа hasMembersNamed, регулярное выражение может быть задано как RegExp-объект JS (не следует использовать флагов, кроме i) или как исходная строка с регулярным выражением (с предполагаемым отсутствием флагов)
  • { hasMembersThat: <Condition> }: условие типа hasMembersThat
  • { hasAnyOfTags: <CollectionSpec> }: условие типа hasAnyOfTags
  • { hasAllOfTags: <CollectionSpec> }: условие типа hasAllOfTags
  • { hasParentThat: <Condition> }: условие типа hasParentThat
  • { named: <string | RegExp> }: условие типа named, регулярное выражение может быть задано как RegExp-объект JS (не следует использовать флагов, кроме i) или как исходная строка с регулярным выражением (с предполагаемым отсутствием флагов)
  • { and: [ ...<Condition> ] }: условие типа and, аргумент - массив объектов <Condition>
  • { or: [ ...<Condition> ] }: условие типа or, аргумент - массив объектов <Condition>
  • { not: <Condition> }: условие типа not
Элемент списка спецификаций коллекции. Соответствует концепции ✖ Задание коллекций FDOM. Может быть одним из следующих объектов...
  • ✖ <Item> : непосредственно заданный одиночный элемент
  • ✖ <Collection> : непосредственно заданная коллекция, разворачивается и добавляется поэлементно
  • строка: имя одиночного элемента, или алиаса коллекции, если такой именованный алиас задан в контексте (алиас коллекции приоритетнее имени элемента при поиске)
  • массив: элементов спецификаций коллекции - обрабатывается так же, как если бы его элементы непосредственно входили в список collectionSpecs (возможна вложенность произвольного уровня)
  • { union: [nestedCollectionSpecs] }: объединение коллекций, заданное массивом элементов, каждый из которых - тоже элемент спецификации коллекции (возможна вложенность произвольного уровня, но обратите внимание, что элементы на первом уровне списка union трактуются как операнды для операции объединения, а не составные элементы списка)
  • { intersect: [nestedCollectionSpecs] }: пересечение коллекций, заданное массивом элементов, каждый из которых - тоже элемент спецификации коллекции (возможна вложенность произвольного уровня, но обратите внимание, что элементы на первом уровне списка intersect трактуются как операнды для операции пересечения, а не составные элементы списка)
  • { subtract: [nestedCollectionSpecs] }: разность коллекций, заданная массивом элементов (вычитание 2-го и последующиъ элементов из 1-го элемента), каждый из которых - тоже элемент спецификации коллекции (возможна вложенность произвольного уровня, но что элементы на первом уровне списка subtract трактуются как операнды для операции вычитания, а не составные элементы списка)
Элемент спецификации списка базовых запросов. Соответствует концепции ✖ Базовые запросы FDOM. Может быть одним из следующих объектов...
  • строка: ссылка на алиас запроса по имени алиаса, заданному строкой
  • ✖ <Query> : предкомпилированный объект запроса
  • [ ...<QuerySpec> ]: массив спецификаций запроса, составной запрос, где компоненты применяются в порядке перечисления
  • { alias: string }: ✖ Задать локальный алиас коллекции ["alias ..."] , имя задаваемого алиаса задаётся строкой
  • { with: <CollectionSpec> }: ✖ Заменить текущую коллекцию ["with ..."]
  • { membersThat: <Condition>, on?: <CollectionSpec>, recursive?: boolean }: ✖ Выбрать члены, удовлетворяющие условию ["membersThat ..."]
  • { tagsThat: <Condition>, on?: <CollectionSpec>, recursive?: boolean }: ✖  Выбрать теги с элементов коллекций, удовлетворяющие условию ["tagsThat ..."]
  • { inMembersThat: <Condition>, query: [ ...<QuerySpec> ], on?: <CollectionSpec>, recursive?: boolean }: ✖  Выполнить подзапрос на тех членах элементов коллекции, которые удовлетворяют условию ["inMembersThat ..."]
  • { inTagsThat: <Condition>, query: [ ...<QuerySpec> ], on?: <CollectionSpec>, recursive?: boolean }: ✖  Выполнить подзапрос на тех тегах от элементов коллекции, которые удовлетворяют условию ["inTagsThat ..."]
  • { inItemsThat: <Condition>, query: [ ...<QuerySpec> ], on?: <CollectionSpec>, recursive?: boolean }: ✖  Выполнить подзапрос на элементах коллекции, удовлетворяющих условию ["inItemsThat ..."]
  • { subtractQuery: [ ...<QuerySpec> ], on?: <CollectionSpec> }: ✖  Вычитание результата подзапроса из текущей коллекции ["subtractQuery ..."]
  • { unionQuery: [ ...<QuerySpec> ], on?: <CollectionSpec> }: ✖  Объединение результата подзапроса с текущей коллекцией ["unionQuery ..."]
  • { intersectQuery: [ ...<QuerySpec> ], on?: <CollectionSpec> }: ✖  Пересечение результата подзапроса с текущей коллекцией ["intersectQuery ..."]
  • { sideQuery: [ ...<QuerySpec> ], on?: <CollectionSpec> }: ✖ Выполнить подзапрос, не меняя текущую коллекцию ["sideQuery ..."]
Для файлов конфигурации Logipard характерны достаточно большие размеры, поэтому обычный JSON становится неудобным и плохо сопровождаемым. Для решения этого затруднения Logipard использует своё собственное расширение JSON, которое мы, по естественным соображениям, назвали LPSON. Он спроектирован для решения ряда проблем масштабируемости и сопровождаемости:
  • возможность комментариев в стиле C++ (это позволяет использовать документационные аннотации LP в самих конфигурационных файлах LP!)
  • более устойчивый синтаксис с бОльшим числом визуальных зацепок для улучшения человекочитаемости и редактируемости
  • модульность (возможность разбивать любой объект на несколько файлов)
  • возможность явного указания кодировки файла
  • использование значений на базе конфигурируемых контекстных переменных
  • улучшенная диагностика ошибок разбора (парсер пытается найти как можно больше ошибок, вместо того, чтобы, подобно традиционным парсерам JSON, остановиться на первой - это становится полезным для больших и модульных объектов)
  • улучшенные опции для отладки (назовём это так)
  • обратная совместимость с JSON
LPSON парсер предназначен для внутреннего использования в Logipard, однако также доступен и для пользователя: ✖ Пользовательское применение
В смысле применения на стороне кода, LPSON преобразуется в обычное JSON-совместимое значение, так что коду не требуется знать ничего лишнего по сравнению с обычным JSON-объектом.
Введение в грамматику LPSON.
Выражение - строка символов, из которой вычисляется JSON-совместимое значение. В JSON, такие строки ограничены словарями, списками, и атомарными константами: числами, строками в двойных кавычках, true, false, и null. Всё это возможно также и в LPSON (плюс некоторые расширенные возможности для списков и строк), плюс ещё некоторые варианты:
  • vars: результат - словарь переменных
  • file(...): результат - значение, распарсенное из заданного файла JSON/LPSON
  • оператор доступа к полю: значение, за которым следует .fieldName, или ."fieldName", или .(field-name-expr), результат - значение заданного поля из предшествующего значения
  • оператор интерполяции строки: значение, за которым следует $, результат - предыдущее значение-строка, в котором фрагменты вида ${var-name} заменяются значениями соответствующих контекстных переменных
  • имплантация типа в словарь: значение, за которым следует словарь ( { ... }), результат - этот словарь с добавленным ключом "@type" и предыдущим значением в качестве значения для этого ключа
  • цепочка из нескольких операторов (например, vars.objectField1."objectField2".("objectField${THREE}" $).stringField { value: 123 }). Они вычисляются слева направо с одним и тем же приоритетом.
Выражение может стоять во всех контекстах, где требуется значение, и также в качестве ключа словаря (при условии, что его результат - строка)
Полное выражение таково: file(name-value) или file(name-value, extra-vars-dictionary-value). Оно распарсивает и вычисляет LPSON-значение из заданного файла, добавляя/заменяя в словаре контекста заданный набор допонительных переменных. Модифицированный словарь будет действовать только для выражений ( ✖ 'vars': словарь контекстных переменных ) в дочернем контексте внутри вставляемого файла; контекстные переменные самого текущего файла не меняются.
Параметры
Имя
Описание
✖ name-value
значение, результат которого - имя файла. Относительные имена задаются относительно каталога с текущим файлом (т. е., file("xxx.lpson") изнутри yyy/zzz.lpson означает файл yyy/xxx.lpson).
✖ extra-vars-dictionary-value
значение, результат которого - словарь. Ключи - имена контекстных переменных для добавления/замены в дочернем контексте, значения - соответствующие значения для этих переменных.
Пример: { member: file("value-for-member.lpson", { VERSION: "1.0.0" }) }
Файлы с одним и тем же фактическим набором контекстных переменных кэшируются при распарсивании, поэтому не беспокойтесь насчёт производительности, когда используете file("один-и-тот-же-файл") много раз.
Параметры (подробно)
значение, результат которого - имя файла. Относительные имена задаются относительно каталога с текущим файлом (т. е., file("xxx.lpson") изнутри yyy/zzz.lpson означает файл yyy/xxx.lpson).
значение, результат которого - словарь. Ключи - имена контекстных переменных для добавления/замены в дочернем контексте, значения - соответствующие значения для этих переменных.
Строковой литерал. Может быть в двойных кавычках ("abc", JSON-совместимая форма), в одинарных кавычках ('abc'), или с ограничителями из обратных кавычек (
`...`abc`...`).
В литералах с кавычками допускаются такие же экранированные обратной косой чертой последовательности, как в JSON (\n, \", \\, \n, \r, \t, \uXXXX и т. п.). Кавычки неподходящего типа (' в "..." и " в '...') можно оставить неэкранированными. Прямые переводы строки (непосредственные или экранированные) в строках с кавычками не допускаются.
В литералах с ограничителями, закрывающий ограничитель - в точности такое же число идущих подряд обратных кавычек, как и открывающий. Все символы внутри берутся дословно, включая переводы строки (не проводится экранирования или обрезания пробелов), с парой исключений для удобства:
  • открывающие обратные кавычки могут быть последними не-пробелами на строке - в этом случае, следующие за ними пробелы и перевод строки обрезаются. Эти кавычки не обязаны быть первыми символами на всей строке.
  • закрывающие обратные кавычки могут быть первыми не-пробелами на строке - в этом случае, предшествующий перевод строки и пробелы обрезаются. Эти обратные кавычки не обязаны быть последними на всей строке.
То есть:
перед, `a`, после
// то же самое, что
перед, ``
a
        ``, после
Числовой литерал. Может быть десятичным числом, допускается знак минус и экспонента, как в JSON, например: 1, 2, -1.0e10... В LPSON, допускается также знак плюс (+1 etc.), и, кроме того, целые шестнадцатеричные числа (нечувствительно к регистру), например: 0x1F, 0X100, -0x123
Булевая константа true (токен чувствителен к регистру). То же, что в JSON.
Булевая константа false (токен чувствителен к регистру). То же, что в JSON.
Константа со значением null (токен чувствителен к регистру). То же, что в JSON.
Значение, результат которого - словарь контекстных переменных, с именами переменных как ключами, и значениями (JSON-совместимыми), назначенными соответствующим переменным.
На уровне корневого начального файла, словарь содержит некоторый набор переменных по умолчанию, в частности для конфигурации LP это следующие:
  • LP_HOME: каталог с инсталляцией работающего в данный момент исполнителя конвейера Logipard, с помощью этой переменной можно ссылаться на встроенные модули LP
  • LP_PROJECT_ROOT: корневой каталог проекта, используйте для конструкции строк, которые должны означать имена файлов относительно корня проекта (не считая имён файлов в операторе file(...), там это делается автоматически)
  • THISFILE: путь к текущему LPSON-файлу, может быть полезно для построения имён разных элементов относительно размещения файла (например, "${vars.THISFILE}/../item_in_the_same_dir.png" $)
(expression), или (expression1, expression2, ...) - выражение, которое нужно выичслить с более высоким приоритетом, чем прочие операторы в цепочке. Его результат - значение выражения в скобках, или последнего выражения в списке внутри скобок.
Аналог JSON-словаря (хэша): { "key1": value1, "key2": value2, ... }, но имеет некоторые дополнительные возможности:
Упрощённые ключи
Двойные кавычки в именах ключей могут быть пропущены, если ключи являются корректными идентификаторами. В LPSON, символами для корректных идентификаторов считаются не только A-Z, a-z, 0-9 (кроме первого символа) и _, как в JS, но также +, -, *, / (кроме // и /*, которые считаются началом комментариев), $ и =. Пример:
{
 "jsonStyleKey": 0,
 LP-style-key: 1,
 /this-is+allowed=too*$: 2,
 jsonStyleKey: 3 // то же самое, что "jsonStyleKey"
}
В случае дублирующихся ключей, идущие позже автоматически заменяют идущие раньше.
Spread-выражения
Добавление ключей и значений из значения, заданного предоставленным выражением - его результатом должен быть словарь. Аналогично spread-оператору в JS внутри объекта. Пример:
{
 a: 1,
 ...({ b: 2, c: 3 }),
 d: 4
}
Поведение при дублирующихся ключах такое же, как если бы содержимое объекта было напрямую вставлено в это место.
Ключи-выражения
Ключи могут быть выражениями, результатами которых должны быть строки. Пример:
{
 (vars.KEY_NAME): "value"
}
@type-префиксация
Если перед литералом словаря идёт выражение, это то же самое, как если бы это выражение было задано как значение внутри словаря под ключом "@type". Пример:
"string" { value: "123" }
```<lp-src file="lp-input.lp-txt"></lp-src>то же самое, что:<lp-src file="lpson.js"></lp-src>

{ "@type": "string", value: "123" }

Значение выражения может быть не только атомарным значением:



{ class: "ещё один словарь" } { value: "типизованное значение" }

{ "@type": { class: "ещё один словарь" }, value: "типизованное значение" }

(выражение) { value: "типизованное значение" }

{ "@type": (выражение), value: "типизованное значение" }

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

**Запятая после завершающего элемента**

Аналогично JS и большинству прочих C-подобных синтаксисов, LPSON позволяет ставить запятую после заключительного элемента словаря:



{ is: "legit", legit: "too", }
Аналогично JSON-списку: [ value1, value2, ... ], но с некоторыми дополнительными возможностями:
Spread-выражения
Вставка значений из результата заданного выражения, который должен быть списком. Аналогично spread-оператору JS внутри массива. Пример:
[
 1,
 ...([2, 3]),
 4
}
Запятая после завершающего элемента
Аналогично JS и большинству прочих C-подобных синтаксисов, LPSON позволяет ставить запятую после заключительного элемента списка:
[
 "is",
 "legit",
]
Результат левой части должен быть словарём. Результат оператора - значение его поля (члена) по заданному ключу. Отсутствие такого поля считается ошибкой, но разрешается указывать значения по умолчанию на случай такой ситуации.
Ключ может быть строкой в двойных кавычках, или корректным идентификатором без кавычек, или выражением в круглых скобках, результат которого - строка:
{ a: "value" }.a
{ a: "value" }."a"
{ a: "value" }.("a")
// результат всех этих выражений - "value"
Можно задать значения по умолчанию, добавив суффикс (= выражение) после ключа или ключа-выражения:
{ a: "value" }.a (= "default value") // результат - "value"
{ b: "value" }.a (= "default value") // результат - "default value", т. к. .a не определено
Ключ считается не определённым только в случае его явного отсутствия. Если значение под ключом - null или любое false-значение, то поле считается установленным на это значение.
Результат левой части должен быть строкой. Результат оператора - строка, полученная интерпретацией и вычислением исходой строки как шаблона, могущего содержать следующие фрагменты:
  • ${varName} - подставить значение контекстной переменной varName, при условии, что она имеет значением строку, число или булевое.
  • \$ (\\$ в строке с двойными кавычками) - буквальный символ $
Пример:
"Program version ${version}, and this is not a \\${placeholder}" $
// если vars.version "1.0.0", то результат идентичен следующей константе: "Program version 1.0.0, and this is not a ${placeholder}"
Если переменная не определена, или не относится к трём перечисленным разрешённым типам, это считается ошибкой. Ожнако, возможно предоставить набор значений по умолчанию для данного конкретного выражения дополнительным префиксом (= выражение-словарь)
// vars.a - "value-of-a", vars.b не определено
"a is ${a}, b is ${b}" $(= { a: 1, b: 2, c: 3 })
// то же, что "a is value-of-a, b is 2"
Каждое значение или spread-выражение может иметь префикс из одной или более аннотаций, которые изменяют контекст его вычисления или добавляют некоторое дополнительное поведение. Формат аннотации - <валидный-LP-идентификатор выражение-параметр>.
Результат параметра-выражения должен быть словарём. Аннотация добавляет (или заменяет существующие) контекстные переменные с именами, соответствующими ключам словаря, равные соответствующим значениям из словаря. Замена проводится в дочернем контексте, который действителен только во время вычисления аннотированного значения или spread-выражения.
Обратите внимание, что переменные вычисляются только один раз перед обработкой основного значения и содержат окончательные JSON-совместимые значения, а не выражения, которые перевычисляются на каждом использовании этих переменных.
Пример:
<+vars { a: 10 }>
{
 innerA: "${a}",
 innerA1: <+vars { a: 11 }> "${a}",
 <+vars { a: 12 }>
 ...{ innerA3: vars.a, innerA4: "${a}" }
}
Если перед значением идут несколько аннотаций +vars подряд, то они применяются в порядке следования, и более поздние применяются поверх более ранних:
<+vars { a: 1 }>
<+vars { a: 2, b: 3 }>
"${a} ${b}" $ // "2 3"
Выводит в стандартный вывод значение - результат параметра-выражения, используя контекстные переменные, которые действуют на момент обработки trace-аннотации. Полезно для проверки или отладки значений, вызывающих вопросы в сомнительных местах.
Пример:
<trace vars.a>
{
 innerA: "${a}"
}
Если аннотация идёт в одном ряду с аннотациями +vars, то используемые переменные - те, что действуют после всех +vars, предшествующих trace:
<+vars { a: 10 }>
<trace vars.a> // 10
<+vars { a: 20 }>
vars.a // 20
Токенизация проводится в два прохода. Первый проход делается над файлом, декодированным в кодировке latin1, с целью найти CHARSET_COMMENT. Второй проход, который даёт собственно строку токенов, выполняется над файлом, декодированным в кодировке, определённой после первого прохода - указанной в CHARSET_COMMENT, или UTF-8 при отсутствии такового.
Грамматика на уровне токенов приведена в нотации регулярных выражений:
WHITESPACE ::= \s+
CHARSET_COMMENT ::= //[^\S\r\n]*#charset[^\S\r\n]+([-A-Za-z0-9_]*).*
COMMENT ::= //.*
MULTILINE_COMMENT ::= /\*[\S\s].*?\*/
// пробелы и комментарии исключаются из строки токенов
// токен '#charset' чувствителен к регистру, но имя самой кодировки - нет, и дефисы в ней игнорируются,
// например, можно написать: #charset utf8, #charset UTF-8, or #charset Utf8-, и т. п.

SINGLE_QUOTE_STRING ::= '(?:\\.|[^'])*'
DOUBLE_QUOTE_STRING ::= "(?:\\.|[^"])*"
// аналогично обычному JSON, строки в кавычках не могут занимать несколько строк

FENCED_STRING ::= (?<fence>`+)(?:\s*?\n)?([\S\s]*?)(?:(?<=\n)[^\S\r\n]*)?\k<fence>
// строка с ограничителем ограничена последовательностями обратных апострофов одной и той же длины
// (начиная с одного), и могут занимать несколько строк
// все символы между ограничителями берутся как есть, включая пробелы и переводы строк,
// кроме замыкающих пробелов на строке с открывающим ограничителем и/или начальных пробелов
// на строке с закрывающим ограничителем, в случае, если ограничители - соответственно, последние/первые символы-не пробелы
// на своих строках; такие пробелы, включая переводы строк, не включаются в строку.

NUMBER ::= [-+]?(?:\d+(?:\.\d+)?(?:[eE]\d+)?|0[Xx][0-9A-Fa-f]+)
// в отличие от обычного JSON, в LPSON разрешается указывать знак '+' и использовать целочисленные шестнадцатеричные числа

PUNCTUATION ::= \.\.\.|[\(\)\[\]\{\}<>.,:]
// распознаваемые символы пунктуации LPSON: '[' ']' '{' '}' '(' ')' '<' '>' ',' ':' '.' '...'

IDENTIFIER ::= (?![0-9])(?:[-+A-Za-z_$*=]|\/(?!\/))+
// помимо цифр, букв и подчёркивания, LPSON позволяет содержание в идентификаторе также $, -, +, *, / (кроме двух последовательно идущих
// '/', которые считаются началом комментария), и =
// идентификатор не должен начинаться с цифры, или с плюса или минуса, за которым идёт цифра
"Стандартный" парсер, основанный на грамматике, в LPSON используется только до уровня базовой структуры, поэтому он называется "уровень 1" (L1). Более тонкие детали ( ✖ "level 2" ) распарсиваются внеграмматическим способом.
L1-грамматика - следующая:
ANNOTATION ::= '<' NOISE '>'
SPREAD ::= '...' NOISE
SUBEXPR ::= '(' (NOISE ',')* NOISE? ')'
LIST.ITEM ::= ANNOTATION* (SPREAD | NOISE)
LIST ::= '[' (LIST.ITEM ',')* LIST.ITEM? ']'
KEY_VALUE ::= NOISE ':' NOISE
DICTIONARY.ITEM ::= ANNOTATION* (SPREAD | KEY_VALUE)
DICTIONARY ::= '{' (DICTIONARY.ITEM ',')* DICTIONARY.ITEM? '}'
ATOMIC_VALUE ::= NUMBER | STRING | IDENTIFIER
NOISE.ITEM ::= SUBEXPR | LIST | DICTIONARY | ATOMIC_VALUE | '.'
NOISE ::= NOISE.ITEM+
// NOISE - упрощённо говоря, некоторое выражение, из которого вычисляется JSON-значение, но его дальнейшая структура не является предметом L1

LPSON_FILE ::= ANNOTATION* NOISE
// файл LPSON содержит ровно один, возможно аннотированный, символ NOISE
Парсер LPSON-файлов можно применять для ваших собственных нужд:
const { loadLpsonFile } = require('logipard/lpson');
...
var [parsedObject, errors] = await loadLpsonFile('path-to-file.lpson', { varA: "A" }); // 2й параметр - словарь переменных
if (errors.length > 0) {
	console.log("Ошибки парсинга:");
	console.dir(errors);
} else {
	console.log("Объект распарсен успешно, обратная сериализация в JSON:", JSON.stringify(parsedObject));
}
Обратите внимание, что парсер всегда переопределяет переменную THISFILE.
The page generated by Logipard 1.0.0 using lpgwrite-example + lpgwrite-example-render-html generator