Содержание
0. Что это такое?
SINX (SINX Is Not XML) – язык разметки полуструктурированных данных,
альтернативный XML, HTML и огромному множеству менее известных альтернатив.
Да, ещё один. В чём смысл, спросите вы? Зачем нам ещё один язык разметки?
Не знаю, как вам, но расскажу, зачем он понадобился мне и почему его
пришлось придумать.
1. Мотивация
Не секрет, что одна из наиболее частых потребностей в практике программирования
– представление, хранение и распознавание данных в неких текстовых
форматах. Типичные случаи:
конфигурационные и управляющие файлы,
таблицы ресурсов, различные манифесты,
сериализация и десериализация внутренних данных,
логи и отчёты, предназначенные для автоматизированной обработки,
...
Почему форматы для подобных задач желательны именно текстовые? Во-первых,
для чтения и редактирования любого текстового формата достаточно
блокнота (бывают, конечно, форматы, для которых блокнота мало, но
зато его именно достаточно), а для бинарного формата требуются
специальные редакторы, как правило – специализированные под каждый
конкретный формат. В ряде случаев это неудобно и непрактично. На
самопальные же форматы для приземлённых гаражных нужд не напасёшься
никаких редакторов. Во-вторых, бинарные форматы по самой своей природе
обладают относительно жёсткой структурой. Если программа эволюционирует,
и в данных, представляемых ими, что-то появляется, исчезает или меняет
место, научить программу понимать формат предыдущей версии (или хотя
бы написать конвертор в следующую) становится накладнее, чем в текстовом
случае.
Бывает так, что подобный формат требуется не один. Мне, например,
в один прекрасный момент потребовалось сразу целых три – для конфигурационного,
для локализационного и для сериализационного файлов. А какая самая
неприятная особенность текстового (хотя, почему только текстового...)
формата? Правильно, написание парсера (в более общем случае – ещё
и генератора).
Поэтому программист рано или поздно приходит к мысли, что вместо зоопарка
форматов хорошо бы иметь один универсальный, на базе которого строились
бы все остальные. Если формат хороший, то парсер и генератор для
него пишутся только один раз, а работа с более сложными структурами
более или менее тривиальным образом ведётся в терминах базового парсера/генератора,
без доведения последнего под конкретные случаи с помощью лобзиков
и напильников.
Итак, мне понадобился формат со следующими характеристиками:
текстовый,
позволяющий представить как структурированные данные, в которых главную
роль играет древовидная структура, так и текст с незначительной разметкой,
где главную роль играет сам текст.
Какая мысль у вас возникает первой при виде таких требований? Конечно
же, XML. Он заточен именно под такие задачи, он – распространённый
индустриальный стандарт, на нём основано множество технологий, для
него существует уйма готовых библиотек на всех языках мира, и прочая,
и прочая...
Казалось бы, бери да пользуйся. Ан нет. XML меня абсолютно не устроил,
и вот чем.
1. Он избыточен. Он просто запредельно избыточен. Чтобы понять, насколько
всё ужасно, посмотрите хотя бы на
примеры XML-RPC и попробуйте осознать,
что в этих примерах речь идёт всего лишь о передаче пары параметров.
XML-документы считаются человекочитаемыми исключительно формально.
На практике разглядеть данные среди многочисленных открывающе-закрывающих
тегов крайне затруднительно, не говоря уже о том, чтобы такой документ
набрать ручками.
2. Он навороченный. Он невообразимо навороченный. Он изобилует ненужными
и дублирующими друг друга излишествами. Чего стоит одна только альтернатива
<tag><value>data</value></tag> vs. <tag value="data"/>
для представления одного и того же по существу куска данных!
Как следствие, все так называемые готовые библиотеки для работы с
оным форматом – жирные непотребства, которые по размеру и количеству
файлов занимают места едва ли не на порядок больше, чем иные программы
(а нам всего-то надо было несчастную конфижку для hello, world...),
нередко требуют нетривиального билда (за которым волочатся ГНУсные
тулчейны и всякие $тудии), а интерфейс, который они дают – итерации
разной степени рукопашности по мешанине из атрибутов и поддеревьев.
Глядя на код, который при этом получается, хочется рыдать кровавыми
слезами. Если вам пока ещё не хочется, найдите в анналах истории,
например, справку по функции
GetPrivateProfileString и группе ей
подобных (там же заодно посмотрите описание формата INI-файла), просветитесь,
а после сравните,
как делается доступ к XML-документам (заметьте,
к уже загруженным и распарсенным) в считающейся одной из наиболее
простых XML-библиотек.
При всём при этом, не всякая чудо-библиотека может похвастаться умением
понимать XML во всей его полноте (
пример, раздел с недвусмысленным
названием 'What it doesn't do?').
Многие люди считают XML чем-то хорошим исключительно потому, что пользуются
производными форматами через соответствующие программы (офисы, инкскейпы,
экспрешенбленды и т. д.) и не видят происходящего под капотом. Но
у нас, простых программистов с простыми приземлёнными нуждами, писателей,
а не читателей, критерии несколько иные. Поэтому не знаю, как для
вас, а для меня было очевидно, что XML – наихудшее из возможных решений
(к тому же, в моём конкретном случае – откровенный overkill).
Я вас умоляю, только не надо стыдить меня неприятием международного
стандарта, склонностью к велосипедостроительству, и всё такое. Вот
сидит простой советский программист, перед ним простой компилятор
с программной строки и девственно чистый блокнот, и стоит задача
– написать небольшую консольную программу, немного конфигурируемую.
Как все эти замечательные международные стандарты помогут ему против
необходимости тащить в дом и приучать к лотку многотонного монстра,
на фоне которого его собственный код затеряется жалким придатком?
Вот именно. А если монстр уже притащен и приучен, причина этому может
быть только одна – программисту было явно больше нечем заняться.
Итак, XML не нужен. Какие более простые и лишённые указанных недостатков
альтернативы предлагает нам коллективный разум?
Они, безусловно, лучше, но слишком узкоспециализированны. Конкретно
говоря, все эти форматы заточены сугубо под данные, а представление
в них текста с незначительной разметкой – занятие неблагодарное.
Кроме того, их синтаксис в чём-то функционально избыточен, в чём-то
слишком негибок. К примеру, JSON предусматривает ровно 5 базовых
типов данных (строка, число, бул, словарь, последовательность). Чтобы
быть в состоянии распарсить данный формат, программа должна понимать
все пять, даже если какие-то из них ей не нужны. С другой стороны,
если программе требуется какой-то другой тип данных (скажем, комплексное
число или текст с разметкой), формат резко перестаёт быть компактным,
и начинаются косяки. Примерно так же дело обстоит и с остальными.
Очень неплохо. Примерно что-то такое мне и было нужно – компактность
и гибкость структуры в одном флаконе. Но и тут не всё идеально. Во-первых,
этот формат тоже ориентирован на данные, и представление текста с
разметкой (напомню, таково одно из требований!) в нём по-прежнему
несколько неуклюже. А во-вторых, синтаксис всё ещё сложноват. Не
знаю, какими представлениями о простоте руководствовался автор, но
направления для упрощения и устранения излишеств просматриваются
с первых же строчек спецификации.
Через все эти мытарства и родилась идея формата SINX.
Не сказать, чтобы формат получился самобытным и революционным. Задачи
нарисовать новый язык разметки, ни на что не похожий, не ставилось
(зачем?) – заимствования из предшественников заметны невооружённым
глазом. (Да и затравка формата, в общем-то, не придумана с нуля,
а позаимствована из одного древнего редактора для первого старкрафта
– мне запомнилось, как оригинально автор решил проблему ввода непечатных
символов). Приоритеты были следующие:
формат, не уступающий XML и HTML по равнопригодности как для текста,
так и для структурированных данных, но избавленный от их тяжеловесности,
максимальная простота и минимальная избыточность. Если у нас текст,
он должен не теряться среди элементов синтаксиса и как можно меньше
искажаться ему в угоду. Если у нас структурированные данные, средств
синтаксиса должно хватать на описание структуры, и ни на грамм больше.
Классифицировать данные по типам, разложить их на информацию и метаинформацию
юзер сможет и сам, если понадобится (а если не понадобится, то в
формате это тем более ни к чему). Наша же задача – дать ему вытащить
эти данные без лишней бюрократии и мутных условностей.
Насколько получилось реализовать задуманное – судите сами.
2. Принцип и синтаксис
Идея формата следующая. Есть SINX-строка – последовательность
символов (конечная), в том числе и пустая. Символы могут быть двух
видов:
терминальный (буква, цифра и т. д.),
специальный, характеризующийся именем и данными. Данные специального
символа также являются SINX-строкой, к которой рекурсивно применимо
всё вышесказанное.
От этого принципа и танцуют дальнейшие построения.
2.1. Кодировка
Для формата SINX годится любая кодировка, в которой представимы символы
'<', '>', '%' и есть символы, считающиеся пробельными (whitespaces).
Желательно, но менее обязательно наличие символов '.', ':', '=',
и ещё менее обязательно – '-', '#', символов для всех шестнадцатеричных
цифр и букв 'x' (латинский икс), 'l' и 'g'. Рекомендуется UTF-8 или
какая-нибудь 8-битная кодировка на основе ASCII. Но, если желаете
извращений, это может быть UTF-32 или какой-нибудь EBCDIC.
Важное замечание: все символы в SINX берутся, как есть, к ним ни при
каких обстоятельствах не применяется никаких трансляций и трансформаций.
То есть, последовательности пробелов не склеиваются и не выбрасываются,
табуляция и переводы строк не транслируются в пробелы, регистр букв
учитывается, и т. д. Возможна трансляция CR/LF в одиночный перевод
строки и прочие платформоспецифичные выкрутасы, но предполагается,
что это происходит на уровне физического чтения потока перед скармливанием
данных в SINX-парсер, и формат этих тонкостей не касается. На уровне
формата все символы, поступившие на вход, учитываются без изменений.
Если речь идёт о некой последовательности без уточнений, подразумевается,
что она бинарно-дословно соответствует её представлению во входном
потоке.
2.2. Синтаксис (SINX уровень 0)
SINX-строка состоит из последовательности элементов. Элементы могут
быть следующие:
1) Простой терминальный символ – любой символ, кроме '<' и '>'.
Пример: aba125 буквы входят в пример 15#tbs.
Простой терминальный символ обозначает просто соответствующий терминальный
символ.
2) Терминальная последовательность:
'<' ограничитель '%' последовательность-символов '%'
ограничитель '>'
где:
ограничитель – последовательность любых символов, кроме '%',
'<', '>', '=' и пробельных (может быть и пустой), причём второй ограничитель
должен быть точно той же (бинарно идентичной) последовательностью,
что и первый,
последовательность-символов – последовательность любых возможных
символов в данной кодировке, не включающая подпоследовательности
'%' ограничитель '>'.
Примеры:
<abc%пример%abc>
<%пример последовательности с пустым ограничителем%>
<ab%пример последовательности < со служебными >< символами > %
внутри%%ab> (последний знак процента перед %ab – часть последовательности)
<$#!@%пример последовательности с небуквенноцифровыми символами
в ограничителе %$#!@>
<abcdx%это %abcdx тоже %abcdx > правильный %abcd> пример%abcdx>
(в последовательность входят все символы между <abcdx% и %abcdx>
– завершающим маркером последовательности является не сам ограничитель,
а именно последовательность из процента, ограничителя и закрывающей
угловой скобки)
Терминальная последовательность интерпретируется, как последовательность
составляющих её терминальных символов, не включая начальный и конечный
маркеры.
Пример: abc<%def%>ghi интерпретируется аналогично abcdefghi
Однако начальный и конечный маркеры считаются частью содержащей их
SINX-строки и включаются в неё, если речь идёт о ней в целом.
Символы внутри последовательности не интерпретируются никаким специальным
образом и считаются частью последовательности, независимо от их вида.
<abc% можно <def% даже %def> и так %abc> (<def% и %def> в данном
случае – просто наборы символов, включаемые в последовательность
наравне с остальными)
Назначение последовательности – выражение куска данных, в котором
могут встречаться служебные символы.
<%Например, арифметических или языковых выражений, типа x = i>11?
100 : 500;%>
3) Специальный символ:
'<' имя разграничитель SINX-строка '>'
где:
имя – последовательность любых символов, кроме '%', '<', '>',
'=' и пробельных (может быть и пустой),
разграничитель – либо последовательность пробельных символов
(если за ней следует '<' или '>', она может быть и пустой), либо
одиночный символ '='.
SINX-строка является данными специального символа и может, в свою
очередь, состоять из всех перечисленных ранее видов элементов (включая
спецсимволы). Она может быть пустой.
Примеры:
<a пример>
<$!#@ пример с нетривиальным именем спецсимвола>
<a пример с составными данными: <xxx%последовательность%xxx>, <b
вложенный спецсимвол>>
<a=пример с ограничителем '='>
<a
пример с ограничителем из перевода строки (в предположении, что в
используемой кодировке он считается пробельным символом)>
< пример спецсимвола с пустым именем>
<=ещё один>
<xxx> (пример спецсимвола с пустыми данными)
<xxx=> (пример спецсимвола с пустыми данными и разграничителем
'=')
<xxx<%yyy%>> (пример спецсимвола с пробельным разграничителем
нулевой длины, что возможно, поскольку его данные которого начинаются
с '<')
<> (пример спецсимвола с пустым именем и данными)
<=> (ещё один)
< > (ещё один)
<a =тут символ '=' является частью данных, т. к. разграничитель
– пробел>
<a= тут разграничителем является '=', а пробел, стоящий после него,
является частью данных>
Символы разграничителя и открывающие-закрывающие '<' и '>' не включаются
ни в имя, ни в данные спецсимвола. Однако они считаются частями содержащей
его SINX-строки и включаются в неё, если речь идёт о ней в целом.
Специальный символ интерпретируется, как спецсимвол с соответствующим
именем и данными. Относительно имени есть следующие нюансы:
если имя начинается с '.' и/или заканчивается на ':', эти символы
не включаются в само имя, но парсер *должен* запомнить тот факт,
что они были, в виде отдельных флагов для данного спецсимвола. '.'
в начале имени называется признаком атрибута, ':' в конце
– признаком составного символа.
Например, все эти символы имеют одно и то же имя:
<a пример символа с именем 'a'>
<.a пример символа с именем 'a' и признаком атрибута>
<a: пример символа с именем 'a' и признаком составного символа>
<.a: пример символа с именем 'a' и признаками атрибута и составного
символа>
кроме того, парсер *должен* запомнить в виде отдельного флага для
данного спецсимвола, какой именно разграничитель в нём был использован
– последовательность пробелов или '='. Разграничитель '=' называется
признаком терминальности.
Признаки сами по себе не несут синтаксической нагрузки (кроме того,
что их нужно исключать из имени и учитывать отдельно). Их смысл –
метасинтаксический. Пользователь конечного формата вправе наделять
их любым значением на своё усмотрение или игнорировать. SINX уровень
1 (см. ниже) содержит рекомендации насчёт применения признаков, но
они не являются требованиями, и на уровне формата к их соблюдению
пользователя ничто не принуждает.
Если символы '.', ':' или '=' в выбранной кодировке не предусмотрены,
все правила, в которых упоминаются соответствующие признаки, допускается
исключить.
Как следует понимать фразу "считаются частью содержащей их SINX-строки
и включаются в неё, если речь идёт о ней в целом" в пп. 2) и 3)?
Очень просто. Если речь идёт о посимвольном чтении из SINX-строки,
то символы не учитываются, а если речь идёт о SINX-строке, как едином
целом (например, о данных спецсимвола), то подразумеваются все составляющие
её символы, включая служебные.
Пример: 1235<xx%abcdef%xx>678<a <%x%>> есть последовательность
символов: '1', '2', '3', '5', 'a', 'b', 'c', 'd', 'e', 'f', '6',
'7', '8', 'спецсимвол с именем a и данными <%x%>'.
Но в то же время она в целом является SINX-строкой 1235<xx%abcdef%xx>678<a
<%x%>>. Аналогично, данные спецсимвола с именем a из этой
последовательности – SINX-строка <%x%>, но с точки зрения
посимвольного чтения они представляют собой последовательность из
одного символа 'x'.
Вот и весь синтаксис SINX. Если желаете грамматики, она такова:
любой-терминальный-символ ::= любой терминальный символ, представимый
в используемой кодировке
терминальный-символ-не-угловая-скобка ::= любой терминальный
символ, представимый в используемой кодировке, кроме '<' и '>'
терминальный-символ-имени ::= любой терминальный символ, представимый
в используемой кодировке, кроме '<', '>', '%', '=' и пробельных символов
пробельный-терминальный-символ ::= любой символ, считающийся
в используемой кодировке пробельным
имя ::= терминальный-символ-имени*
разграничитель ::= '=' | пробельный-терминальный-символ*
(если '=' в выбранной кодировке не существует, то разграничитель
::= пробельный-терминальный-символ*)
SINX-строка ::= SINX-символ*
SINX-символ ::= терминальный-символ-не-угловая-скобка
| терминальная-последовательность | спецсимвол
терминальная-последовательность ::= '<' имя '%' любой-терминальный-символ*
'%' имя '>' (2-е имя должно бинарно идентичным первому)
спецсимвол ::= '<' имя разграничитель SINX-строка
'>'
Но, как вы сами видите, написать парсер можно и без всякой грамматики
– логика очень простая.
Синтаксис организован так, чтобы, по возможности, валидной SINX-строкой
являлась любая последовательность символов. Действительно произвольная
последовательность может быть ошибочной. Но синтаксическая ошибка
– не повод, чтобы отказаться от возвращения результата. В случае
обнаружения перечисленных ниже ошибок парсер *должен* реагировать
одним из предложенных для соответствующей ошибки вариантов, на усмотрение
реализации:
1) Символ '>', не имеющий соответствующего открывающего '<'. Пример:
abc>de
Варианты:
считать его концом потока, не включая в сам поток (abc),
игнорировать (рассматривать поток так, как если бы этого символа
не было) (abcde).
2) Незакрытый спецсимвол. Пример: abc<de <f><g h
Варианты:
считать, что все незакрытые явным образом спецсимволы автоматически
закрываются с концом потока в соответствующем порядке (abc<de
<f><g h>>),
игнорировать открывающие фрагменты незакрытых спецсимволов, включая
разграничители (abc<f>h).
3) Незакрытая терминальная последовательность. Пример: <abc%def
Её всегда следует считать автоматически закрывающейся с концом потока
(<abc%def%abc>).
4) Последовательность '<' имя (конец потока) следует считать
открывающим фрагментом спецсимвола с пробельным разграничителем нулевой
длины и, соответственно, частным случаем ошибки 2).
Парсер *может* сообщать об обнаруженных синтаксических ошибках дополнительно,
но всегда *должен* проводить распарсивание до конца в соответствии
с выбранными вариантами реакции.
Описание синтаксиса и вариантов действия при синтаксических ошибках
назовём "нулевым уровнем формата SINX". Для практического применения,
в принципе, уровня 0 уже достаточно. Но перед языками разметки часто
встречаются некоторые задачи, которые вам, скорее всего, всё равно
придётся так или иначе решить в рамках своего формата. Для наиболее
распространённых из них предусмотрен SINX уровень 1, описанный в
следующей главе.
2.3. Базовые метасинтаксические рекомендации (SINX уровень 1)
SINX уровень 0 задаёт синтаксис формата, а SINX уровень 1 предлагает
некоторые обязательные соглашения о том, как интерпретировать в рамках
этого синтаксиса определённые специальные символы.
1) Терминальные символы, заданные через код.
Спецсимволы с именами вида '#' последовательность-десятичных-цифр
или '#' '0' x|X последовательность-шестнадцатеричных-цифр
следует интерпретировать, как символ с соответствующим численным
кодом в принятой кодировке. Примеры:
<#33> (символ '!' (по ASCII))
<#0x33> (символ '3' (по ASCII))
<#0XDeAdBeEf> (символ с 16-чным кодом DEADBEEF)
Данные спецсимвола при этом не учитываются. Т. е., например, mail<#0x40
gg>to интерпретируется аналогично mail<#0x40>to и mail@to.
Если имя спецсимвола начинается с '#', но не представляет собой правильного
десятичного или 16-чного числа, этот спецсимвол не транслируется
в терминальный и рассматривается, как обычный спецсимвол с данным
именем.
Предусмотрено два особых спецсимвола, которые интепретируются как
'<' и '>':
<lt> – '<',
<gt> – '>'.
Буквы l, g и t могут быть в любом регистре, если это допускается кодировкой.
Пример: <example вот спецсимволы, выраженные прописью: <lt><gt>
<lT>spec1 содержимое1<Gt> <Lt>spec2 содержимое2<gT>> – интерпретируется
аналогично <example <%вот спецсимволы, выраженные прописью: <>
<spec1 содержимое1> <spec2 содержимое2>%>>
Это правило допускается исключить, если в выбранной кодировке не предусмотрено
достаточного количества символов для выражения 16-чных цифр, символов
'#' и букв 'l', 'g' и 'x'.
2) Комментарии.
Спецсимволы с именем '--' следует считать комментариями и игнорировать.
Пример: a<-- b>cd – аналогично acd
2 вышеперечисленных соглашения обязательны для соответствия уровню
1. Кроме них, SINX уровень 1 предлагает и необязательных к соблюдению
рекомендаций относительно использования признаков.
3) Признак терминальности (разграничитель '=' в спецсимволе) *следует*
использовать в случае, если все данные спецсимвола считаются в рамках
конечного формата терминальными – т. е., либо конечным неделимым
куском данных, либо формат этого куска является пользовательским
и распарсивается отдельно, вне рамок SINX-формата. Заметьте, однако,
что эта конвенция не освобождает данные от необходимости быть синтаксически
правильной SINX-строкой.
Спецсимволы в терминальных данных *следует*, на усмотрение конечного
формата в каждом конкретном случае, либо интерпретировать, как терминальные
символы (в соотв. с соглашением 1 данной главы, если они подходят
по формату), либо игнорировать, либо включать в данные в виде бинарно-дословной
последовательности составляющих их символов. Терминальные последовательности
в терминальных данных, также на усмотрение конечного формата, *следует*
либо интерпретировать, как соответствующую последовательность, либо
включать в данные в виде бинарно-дословной последовательности составляющих
её символов.
Примеры:
<url=http://mikle33.narod.ru>
<url=http:<#47><#47>mikle33.<-- комментарий –->narod.<%ru%>>
(данные, на усмотрение конечного формата, могут быть проинтерпретированы,
как: http://mikle33.narod.ru, http:<#47><#47>mikle33.narod.ru,
http://mikle33.narod.<%ru%>, http:<#47><#47>mikle33.narod.<%ru%>,
http:<#47><#47>mikle33.<-- комментарий –->narod.<%ru%>, и
т. п.)
Если данные спецсимвола пустые, то разграничитель '=' *можно* не использовать.
4) Признак атрибута *следует* использовать для спецсимволов, которые
в рамках конечного формата считаются атрибутами объемлющего спецсимвола
(или, если таковых нет, то всего потока), и предполагается, что спецсимволов
с таким именем в объемлющем спецсимволе/потоке может быть в количестве
не более одного. (Т. е., спецсимволы с признаком атрибута рекомендуется
использовать в качестве аналогов атрибутам в XML/HTML.) Если спецсимволов
с таким именем встречается более одного, то, на усмотрение конечного
формата, действительным можно считать либо самый первый, либо самый
последний.
Пример:
<error <.locations <line=11><line=22><line=100500>at these lines>An
error has been encountered>
5) Признак составного символа *следует* использовать для спецсимволов,
которые в рамках конечного формата используются для хранения структурированных
данных, и предполагается, что значащими в их данных являются только
спецсимволы (на данные вложенных спецсимволов эта конвенция не распространяется,
если только они тоже не являются составными спецсимволами). Все прочие
символы (включая спецсимволы-комментарии и спецификаторы терминальных
символов по соглашениям 1 и 2) *следует* считать комментариями/элементами
форматирования и игнорировать.
Пример:
<url: <scheme=http> <domain=mikle33> <domain=narod> <domain=ru>>
<url:
(url, разложенный на компоненты и представленный через составной символ
SINX)
<scheme=http>
<domain=mikle33>
<domain=narod>
<domain=ru>>
оба варианта следует интерпретировать аналогично <url: <scheme=http><domain=mikle33><domain=narod><domain=ru>>
Признаки могут комбинироваться, если это соответствует их роли в рамках
конечного формата. Пример:
<table: <.width=100%><.height=50%>
<tr:
<td <b>Cell11</b>> <td <i>Cell12</i>>
>
<tr:
<td <u>Cell21</u>> <td Cell22>
>
<tr:
<td <.colspan=2>Обратите внимание: <b>, </b>, <i>, </i>, <u> и </u>
с точки зрения SINX – это не открывающие и закрывающие теги, а независимые
спецсимволы.>
>>
Признаки терминальности и составного символа, очевидно, являются логически
несовместимыми, но в рамках синтаксиса они могут присутствовать одновременно.
SINX уровень 1 принципиально не даёт никаких рекомендаций насчёт
такой комбинации и оставляет её использование и смысл на усмотрение
конечного формата.
Если выбранная кодировка не предусматривает символа для какого-либо
признака, и соответствующее правило исключено на уровне синтаксиса,
то исключается и соответствующая рекомендация из пунктов 3-5.
2.4. И это всё?
Да, это всё. Заметьте, не "всё, что вам надо знать для начала, но
позже вы откроете для себя...", а именно всё. Данная глава является
исчерпывающим описанием SINX уровней 0 и 1, как формата.
3. Почему SINX, а не XML?
Для начала есть смысл задать обратный вопрос – почему XML, а не SINX?
Если вы не свободны в выборе формата в силу условий задачи, участвуете
в проекте, где не всё зависит от вас, и знакомство с используемыми
технологиями важно для срабатываемости участников, или вы используете
сложные инструменты, форматы которых являются для вас непреодолимой
данностью, или имеете ещё какие-нибудь аналогичные внешние обстоятельства
– что ж, тогда, конечно, ничего не попишешь; используйте XML и примите
мои соболезнования.
Но, если вы пишете что-то лично для себя и свободны в выборе инструментов
и технологий, большинство ваших аргументов в пользу XML, скорее всего,
проистекают из укоренившихся стереотипов и стадного инстинкта. Это
плохие вещи, нужно тренироваться ломать их в пользу здравого смысла.
Вот вам несколько соображений, почему для кустарных нужд SINX лучше
XML.
+1. SINX проще. Об этом можно было бы и не упоминать лишний раз, но
он действительно намного проще. Он прост почти настолько, насколько
это вообще возможно (мог бы быть ещё чуть проще, но были учтены соображения
эстетики и кое-какие вспомогательные моменты). Парсер нулевого уровня
вы напишете с нуля примерно за час, надстройку над ним для первого
уровня – максимум ещё за час. При всём при этом, SINX даёт вам точно
те же возможности для выражения данных и разметки, что и XML. Вы,
вероятно, обратили внимание на псевдо-HTML-пример в предыдущей главе.
Он как раз иллюстрирует принцип конверсии из XML/HTML в SINX (и,
при необходимости, обратно). Принцип настолько примитивен и очевиден
(и не менее очевидным образом автоматизируем), что я, с вашего позволения,
не буду его разжёвывать.
Более того, предоставляя те же возможности, SINX делает это более
простым и однозначным образом. Пример: в XML, чтобы преобразовать
атрибут во вложенный тег или наоборот, нужно сами понимаете, сколько
всего сделать (и в XML-документе, и в читающем его коде). В SINX
же нет разделения на теги и атрибуты, и проблема сводится к чисто
эстетическому решению, поставить или убрать признак атрибута (1 точку)
перед именем спецсимвола (или вообще забить на этот вопрос).
+2. SINX компактнее. Дело даже не в том, что служебные издержки в
SINX-строке в ~2 раза меньше, чем в идентичном по структуре XML-документе.
Формат в меньшей степени затрагивает сам текст. Задумывались ли вы
над такой возможностью, как вывод сообщения для пользователя в stdout
прямо в XML-формате? Вряд ли – здравый смысл показывает, что текст
затеряется в обрамлении служебных элементов и излишеств. В случае
с SINX же вполне можно сделать так, что человекочитаемость по сравнению
с простым текстом пострадает ненамного. Сравните:
<?xml version='1.0' encoding='UTF-8' ?>
<message file="file.txt" line="14">
Это сообщение, относящееся к строке <14> файла "file.txt".
</message>
и:
<message <.file=file.txt> <.line=14>
<% Это сообщение, относящееся к строке <14> файла "file.txt". %>>
+3. SINX гибче. Рассмотрим, к примеру, XML-кусок <tag attr="some-value"/>
и аналогичную SINX-строку <tag <.attr some-value>>. Что можно
сказать про some-value? Это, видимо, должен быть какой-то текст.
А почему, собственно, только текст? Почему бы ему не быть вложенным
узлом со своей собственной структурой? В SINX такая возможность заложена
по дизайну: <tag <.attr <.attr-of-attr=100%><component1=xxx><component2=yyy>>>
– причём данные спецсимвола, являясь полноценной SINX-строкой, всегда
могут быть выделены и рассмотрены независимо, как самостоятельный
документ. В XML же, даже если мы, поднатужившись, сконструируем аналог
из вложенных тегов:
<tag><attr>
<attr-of-attr>100%</attr-of-attr>
<component1>xxx</component1>
<component2>yyy</component2>
</attr></tag>
то внутреннее содержимое <tag>, в общем случае, даже не будет валидным
XML-документом, который можно рассмотреть вне контекста объемлющего
документа.
Это гораздо более внушительное преимущество, чем может показаться.
Почему? Вот вам пара наглядных примеров из пары современных форматов:
SVG:
...
<path fill="none" stroke="black" d="M 227 239 L 328 90 L 346 250 L
201 124 L 410 150 L 228 238" />
...
CSS:
...
<style type="text/css">
body { color: red; }
h1 { color: white; background-color: orange !important; }
h2 { color: white; background-color: green !important; }
</style>
...
Это хорошие иллюстрации анекдотичного положения дел в так называемых
форматах, основанных на XML. По идее, основанность на XML означает,
что берём первый попавшийся XML-парсер, натравливаем его на документ,
и наши проблемы решены. Однако нередки случаи, когда роль собственно
XML в неком формате ограничивается отметкой какого-то общего куска
с данными, а сами данные следуют в совершенно ином формате, парсящемся
отдельно и совсем по другим правилам. А что такого в этих данных?
У них экзотическая структура, не позволяющая представить их поддеревом
главного документа и распарсить на общих принципах? Случается и такое
(скажем, жабаскриптовые вставки), но явно не здесь. Здесь у нас такая
же кучка узлов со свойствами, как и в остальном документе. И что
мешает представить тот же CSS как-нибудь вот так:
...
<style ...>
<body color="red"/>
<h1 color="red" orange="!important/>
<h2 color="white" green="!important/>
</style>
...
?
Да, так бы заняло чуть больше места, но регулярность формата дала
бы преимущества при... ох, постойте, а ведь элементы CSS бывают вложенными?
А ведь у них бывают поля со сложным составным форматом?? А ведь стиль
должно быть можно задавать не только в спецсекции документа, но и
через атрибут тега, к которому он применяется??? Ох, мама. Да, пожалуй,
лучше придумать отдельный формат.
Что называется, скупой платит дважды.
А если бы вместо X/HTML был SINX, такой проблемы бы не стояло.
<style ...>
body {
background-color: #111111;
background-image: url("caulk_sucker.jpg");
}
p {
}
h1 {
color: #222222;
background-color: #333333;
}
</style>
...
<h1 style="color: #FFFFFF; background-color: #000000;">желаете напрямую?</h1>
===>
<style:
<body:
<.background-color=#111111>
<.background-image: <.url=caulk_sucker.jpg>>
>
<h1:
<.color=#222222>
<.background-color=#333333>
>
>
<h1 <.style: <.color=#FFFFFF> <.background-color=#000000>>можно и
напрямую!>
(Обратите внимание – результат практически тот же, но мы не вышли
за рамки SINX.)
К сожалению, XML/HTML+CSS получили распространение в той форме, в
которой они есть, и лечить их генетические дефекты уже поздно. А
вот вашим самопальным форматам может повезти больше – ещё есть возможность
основать их на SINX и через это значительно уменьшить вероятность,
что придётся придумывать языки в языке.
+4. SINX можно употреблять даже для бинарных данных. Правило о бинарно-дословном
соответствии последовательностей их представлению во входном потоке
сурово и недвусмысленно, а элемент терминальной последовательности
более универсален, чем XML-овское CDATA, и позволяет бинарно-дословным
образом вместить абсолютно любую последовательность.
Кроме того, SINX уровня 0 идеально подходит для inplace-распарсивания.
Непронумерованный бонусный плюс. SINX – крайне щадящий формат в плане
миграции с XML. Как отмечено выше, задача конвертации из XML в SINX
и наоборот решается просто и прозрачно, можно элементарно написать
универсальный конвертатор. При этом SINX использует привычные и знакомые
угловые скобочки. :)
Шутка юмором, но любопытный факт: XML/HTML без жабаскриптов, CDATA
и заковыристых комментариев, как правило, является валидной SINX-строкой,
в которой спецсимволы совпадают с открывающими и закрывающими тегами.
С преимуществами всё понятно, а недостатки? Объективности ради, попробуем
рассмотреть некоторые типовые возражения.
–1. "Для XML разработано множество технологий – XSLT, XPath, и прочее,
у него есть dtd/dsd/schema, а у вашего формата ничего этого нет.
А если всё это придумывать, не получится ничего качественно лучшего.
Так в чём же смысл?"
Ну и часто ли вы пользуетесь схемами не то что в повседневной жизни,
а даже в производстве? Зачем она вам? Проверить валидность документа?
Можно подумать, ваша программа, имея распарсенный в дерево конфигурационный
файл и разбирая его простым поиском по нужным ключам + проверкой
значений на валидность, и так не поймёт, её ли это конфигурационный
файл или нечто левое.
Для каких-то экзотических применений схема, может, и нужна. Но у вас
– не экзотический случай. Я в этом уверен на 99%. Именно такой (как
минимум) процент применений XML обходятся без всякой схемы. Среднестатистическая
библиотека для работы с XML даже не обучена такой премудрости. Так
о чём разговор?
За остальные множество технологий скажу ещё короче: добрая половина
из них – узкоспециализированные костыли, придуманные для борьбы с
корявостями XML в различных специфических применениях. Большинство
этих "технологий", скорее всего, известно вам только по названию
и с вашими повседневными нуждами имеет мало общего.
А то, что вам от базового текстового формата действительно нужно,
SINX даёт, и даёт качественнее, чем XML (см. список преимуществ).
–2. "Для XML есть множество готовых библиотек, а про ваш формат знают
полтора человека, и для него ничего толкового нет."
Ну берите "готовые библиотеки" и радуйтесь рукописными итерациями
по DOM-дереву через красивые, удобные интерфейсы. И созданию-редактированию
вашего формата, особенно когда он будет подвергаться расширениям,
тоже радуйтесь. Когда нарадуетесь, перечитайте ещё раз главу 2. SINX
настолько прост, что написание для него собственного парсера займёт
у вас меньше, чем освоение иных XML-библиотек. И даже этот труд окупится
временем, которое вы впоследствии сэкономите на разных мелочах за
счёт более простой и логичной структуры формата.
За отправную идею о том, как может выглядит библиотека для работы
с SINX, предлагается взять пример из гл. 4.
–3. "Если уж изобретать вместо стандартного формата велосипед, лучше
я изобрету свой собственный, с блэкджеком и шлюхами – он-то точно
идеально подойдёт под мои требования."
Попробуйте. Попытки думать самостоятельно, а не оглядываться на авторитеты
– занятие полезное, и нередко даёт ценные плоды.
Один форумный товарищ, сочтя SINX слишком сложным и вообще идеологически
неправильным, прямо на месте изобрёл свой собственный велосипед.
Изобретение долженствовало показать несовершенство смелого замысла
и его неконкурентоспособность на велосипедном рынке.
И получился у него полный аналог SINX уровня 0, отличающийся только
способом записи открывающих и закрывающих фрагментов спецсимвола
(в сторону большей многословности).
Таким образом, скептически настроенный товарищ своими руками доказал
если и не идеальную минималистичность нашего формата, то, как минимум,
его крайнюю к этому близость.
Поэтому, конечно, можете и попридумывать что-нибудь своё, но стоит
ли, если высока вероятность получить то же самое? :)
4. Библиотека
По правилам хорошего тона, к описанию формата прилагается пример библиотеки
(на C++) для чтения и записи с примером использования:
sinx.zip (27
кб). Библиотека не отличается особой компактностью и сферичностью,
поскольку при её создании не ставилось задач сделать нечто компактное
и сферическое. Кроме вещей, относящихся непосредственно к формату,
она содержит кое-какие удобства и излишества, а также использует
некоторые вещи из STL (конкретно говоря, std::map, std::deque и std::string).
С одной стороны, она предполагалась референсной имплементацией SINX-парсера
и генератора, а с другой – для реального применения автором в своих
прожектах и с робким прицелом на портируемость. (Тем не менее, даже
при всей своей некомпактности, несферичности и излишествах, она меньше
RapidXml (примерно аналогичной по функциям библиотеки для XML, самой
минималистичной из существующих) на можете сами посмотреть, сколько
килобукв, и при этом реализует SINX уровня 1 полностью, безо всяких
ограничений. О чём это говорит?)
Подробной документации не прилагается, ибо: а) лень :), б) всё, чего
достаточно для счастья конечному пользователю, проиллюстрировано
в примере и довольно очевидно в своей простоте, в) всё остальное
снабжено doxygen-комментариями (правда, не по-русски, чему есть производственные
причины), так что поклассовую справку при желании можно сварганить
и доксигеном. Здесь же будет дано общее описание содержимого посылки,
общей идеи и краткие наставления по применению.
Посылка содержит ряд файлов, которые надлежит распаковать в одну папку:
sinx_pltfm.h – платформо-зависимые концепты для парсера и кое-какие
общие определения,
sinx_read_l0.h, sinx_read_l1.h – реализации парсера 0-го и
1-го уровней соответственно,
sinx_write.h – реализация генератора (использует вышеперечисленное),
sinx.h, sinx.cpp – обёртки для вышеперечисленного в товарный
вид,
test.cpp – главная программа примера,
test.sinx, testpr.sinx, example_gen.sinx – примеры файлов в
формате SINX. Из них последний генерируется примером (в рамках демонстрации
генератора), а предпоследний – прямолинейная конверсия из XML-файла
(проект-заглушка от C++ Builder), главным образом для иллюстрации
непронумерованного бонусного плюса из гл. 3.
Никаких файлов проектов, makefile-ов и прочего мусора в посылку не
положено. Предполагается, что программа тривиально собирается из
командной строки. В C++ Builder версии 5 и выше (есть подозрение,
что и ниже) это делается вот такой командой:
bcc32 test.cpp sinx.cpp (собирается test.exe)
В M$ Vi$ual $tudio 2005 и позднее (предполагается, что вы уже сконфигурировали
$тудию для работы с командной строки) –
cl /EHsc test.cpp sinx.cpp
Пример компилируется и на gcc от WxDev++ (gcc test.cpp sinx.cpp),
но с командной строки оно не пожелало видеть стандартной библиотеки
на этапе линковки, как с этим бороться – разбираться было лень и
незачем. Поэтому, если у вас gcc или какой-нибудь маргинальный компилятор
(или другая операционная система, не отрицаю и такой возможности),
попробуйте разобраться сами, благо принцип должен быть понятен из
приведённых выше примеров.
Принцип применения самой библиотеки здесь описывать не будем – приёмы,
достаточные для типового практического случая, можно почерпнуть из
текста примера (test.cpp), где всё довольно очевидно. Но кое-какие
дополнительные пояснения насчёт внутреннего устройства и логики некоторых
вещей дать всё же стоит.
Итак, неcколько развёрнутых слов про каждый файл, в порядке их логической
взаимосвязи.
sinx_pltfm.h. Изначальный замысел был в том, чтобы весь парсер
отвязать от какой бы то ни было платформы и параметризовать неким
классом-концептом, в котором пользователь и определит требуемые платформо-специфические
вещи. Один из таких классов, SINX0_Platform, определяется
(подразумевалось, что в качестве примера) в sinx_pltfm.h. Основные
вещи, которые в нём определены – тип SINX0_Platform::TChar
(символ используемой кодировки), коды синтаксически значимых символов,
функции динамической аллокации/деаллокации памяти и класс SINX0_Platform::Buffer,
определяющий буфер с исходной SINX-строкой для разбора. В данной
реализации он сделан через буфер в памяти, содержащий строку целиком,
но ничто не мешает переписать его внутренности для работы над частично
буферизируемыми данными из внешнего источника.
sinx_l0.h (опирается на sinx_pltfm.h). К сожалению,
достичь задуманного в полной мере не удалось – этот скользкий путь
привёл на территорию навороченных шаблонов, на которой мнения различных
компиляторов разошлись до несовместимости. Но для парсера SINX уровня
0, которому посвящён файл sinx_l0.h, цель всё-таки была достигнута.
В данном файле содержатся следующие классы:
SINX0_String<Platform> – фрагмент SINX-строки в дословно-двоичном
представлении. Проще говоря, кусок буфера (Platform::Buffer), рассматриваемый
как строка. Её можно сравнивать с другими строками (в том числе и
с C-style строками) и брать от неё подстроку.
SINX0_SuxxElement<Platform>, SINX0_SuxxParser<Platform> – обеспечивают
последовательное извлечение из SINX-строки (представленной SINX0_String<Platform>)
элементов в стиле SAX-парсера. (Название классов намекает на моё
отношение к SAX-парсерам, как методологии, но их метод, при всей
своей практической ущербности, удобен в качестве базиса.) Кто из
них реализует парсер, а кто представляет извлекаемый элемент – полагаю,
уточнять не надо. Элементы могут быть следующими:
последовательность данных,
открывающий фрагмент спецсимвола (с именем и признаками),
закрывающий фрагмент спецсимвола,
маркер конца строки.
SINX0_Symbol<Platform>, SINX0_SymbolIterator<Platform> – обеспечивают
более прямо соответствующее уровню 0 обращение к SINX-строке – в
терминах отдельных символов, терминальных и специальных. Первый SINX0_Symbol<Platform>
можно получить присвоением из SINX0_String<Platform> (это будет "виртуальный"
спецсимвол с пустым именем и данными, совпадающими с присваиваемой
строкой) и далее извлекать из него компоненты с помощью SINX0_SymbolIterator<Platform>.
В части обработки ошибок формата (см. конец гл. 2.2) парсер придерживается
следующей стратегии:
символ '>', не имеющий соответствующего открывающего '<', игнорируется,
открывающие фрагменты спецсимволов, не имеющие соответствующих '>',
игнорируются.
Все перечисленные классы суть шаблоны, параметризуемые классом, соответствующим
концепту платформы. Иными словами, реализующими тот же интерфейс,
что и SINX0_Platform из вышеупомянутого sinx_pltfm.h (на практике,
это только он и будет). Кроме того, все они сделаны так, что не используют
динамических аллокаций (кроме ситуации с присвоением SINX0_Symbol
из SINX0_String – одна дополнительная аллокация) и для работы с данными
полагаются на объект буфера, от которого всё и танцует. То есть,
предполагается, что буфер должен оставаться в живых не менее, чем
любой из SINX0_...–классов, его использующих (для этой цели в его
интерфейсе предусмотрен подсчёт ссылок). Учитывайте это, если задумаете
сделать свою собственную реализацию буфера.
При желании, sinx_l0.h и содержащиеся в нём богатства можно использовать
самостоятельно, их достаточно для работы с SINX уровня 0. Но интерфейс
и применение у всего этого дела не самые удобные (примерно на уровне
библиотек для XML), поэтому общаться с SINX0_... напрямую вы, скорее
всего, не будете.
sinx_l1.h (опирается на sinx_l0.h). Парсер SINX уровня
1. По отмеченным выше причинам получился, к сожалению, непараметризуемым
(использует строго SINX0_Platform и SINX0_...–классы, параметризованные
именно им), но зато у него более пристойный интерфейс. Помимо вещей,
связанных с собственно SINX уровня 1, включает несколько вспомогательных
излишеств – для удобства и даже для демонстрации кое-каких возможностей
по использованию формата.
Классы, которые здесь представляют основной интерес, следующие:
SINX1_Symbol – олицетворяет SINX-спецсимвол. Позволяет извлекать
из него данные в форме строки или целого числа (если они не содержат
спецсимволов, не сводимых к терминальным по правилам SINX уровня
1), получать составляющие спецсимволы (индивидуально по имени или
полным комплектом), получать все данные в виде неинтерпретированной
SINX-строки, и т. п. Для возврата данных SINX1_Symbol использует
std::string и std::deque (и, на внутреннем уровне, std::map), но
сделано так, что никаких связанных с динамическими аллокациями операций
не проводится до тех пор, пока вы явно не вызовете функцию запроса
данных. Поэтому копировать SINX1_Symbol и возвращать из функций можете,
не опасаясь исключений.
Обработка ошибок формата следует той же стратегии, что описана выше
для SINX0_Symbol.
Поучительная возможность, которую следует упомянуть особо – извлечение
спецсимвола из нижележащего поддерева по описанию пути (функция OpenPathL).
Что это такое? Проще всего показать на примере. В SINX-строке:
<a:
<b blah>
<c:
<e blah-blah>
<d <.x=1>>
<d <.x=2>>
<d <.x=3>>
>>
<x>
спецсимвол 'b' имеет путь <a <b>> (или, что то же самое, <a
<b><.index=0> (индекс может указываться только целым числом)),
спецсимвол 'c' имеет путь <a <c>>,
первый спецсимвол 'd' имеет путь <a <c <d>>> (или, что то
же самое, <a <c <d><.index=0>>>)
второй спецсимвол 'd' имеет путь <a <c <d><.index=1>>>,
третий спецсимвол 'd' имеет путь <a <c <d><.index=2>>>,
спецсимвол <.x=2> имеет путь <a <c <d <x>><.index=1>>>,
...
В общем, вы поняли принцип.
SINX-строкой для поиска выступают данные SINX1_Symbol, а путь вы даёте
его функции OpenPathL (в виде обычной строки), в ответ вам возвращается
искомый спецсимвол. Если спецсимвол с такими "координатами" не найден,
то возвращается результат, соответствующий пустому спецсимволу и
имеющий специальный флажок IsEof (). Эта возможность полезна, если
у вас есть некий сложный SINX-файл, а конфигурация, интересующая
вашу программу, является поддеревом, затерянным где-то в общей структуре,
и местоположение его корневого узла проще всего указать в качестве
входных данных (например, с командной строки).
Поучительно здесь то, что, как видите, путь до искомого символа также
описывается в SINX-формате (и, как нетрудно догадаться, распарсивается
этим же самым парсером). Вот такой импровизированный велосипедик,
не отходя от кассы, средствами самой кассы. Сравните простоту и минимум
затрат, к которой он нам достался, с XPath – аналогичной по назначению
технологией для XML. Да, я знаю, что в XPath несоизмеримо больше
возможностей, и синтаксис у него более удобный, и т. д., и т. п...
Но – у него другой синтаксис, несовместимый с XML, и он требует отдельного
парсера. Вдумайтесь в этот факт. Даже если бы "технология" имела
ровно такие же примитивные возможности, как у нас, у неё всё равно
был бы другой синтаксис, несовместимый с XML и требующий отдельного
парсера. В нашем импровизированном "SINXPath" не потребовалось изобретать
ничего нового. Мы бы могли, но зачем, если можно легко и сравнительно
красиво обойтись подручными средствами?
Что мешало пойти похожим путём и сочинителям XPath? Чисто теоретически,
ничто не мешало. Чисто практически... Думаю, всё понятно.
sinx_write.h (опирается на sinx_l1.h). Реализует запись
данных в формате SINX уровня 1 (включая запись комментариев, экранирование
символов через спецсимволы <#...> при выводе обычных строк,
посильное соблюдение конвенций о признаках и форматирование внутри
спецсимволов, которые юзер предпишет считать составными).
Принцип использования:
Юзер должен определить свой класс выходного потока – наследника чисто
виртуального класса SINX_WriteDocumentBase, в котором нужно доопределить
пару виртуальных функций для записи байт в поток. Далее предполагается,
что экземпляр этого класса воплощает открытый поток, и с помощью
функций, предоставляемых этим объектом (а точнее, его ещё более ранним
предком SINX_WriteNode), можно записывать в него данные.
Для записи простых данных (строк и спецсимволов с простым строковым
содержимым) достаточно функций из класса SINX_WriteNode. Если требуется
записать спецсимвол со сложной структурой, нужно: а) открыть новый
спецсимвол, создав объект класса SINX_WriteSpecialOpen (при этом
можно указать желаемые признаки открываемого спецсимвола и указания
по его форматированию), б) записать составляющие его данные (это
делается таким же образом, т. к. SINX_WriteSpecialOpen тоже является
наследником SINX_WriteNode), в) закрыть спецсимвол (это происходит
автоматически при уничтожении SINX_WriteSpecialOpen).
sinx.h, sinx.cpp (опирается на sinx_l1.h и sinx_write.h)
– обёртка классов и констант из sinx_l1.h и sinx_write.h в объединённый
неймспейс SINX, определение кое-каких вспомогательных функций и реализация
классов-обёрток для наиболее естественного случая – чтения и записи
в/из файла (классы SINX_ReadFileOpen/SINX::ReadFileOpen и
SINX_WriteFileOpen/SINX::WriteFileOpen).
Принцип использования вспомогательных функций достаточно очевиден
по их именам и параметрам. Не самая очевидная идея только у функций
SINX::ReadSpecialEnumL<Enum> и SINX::WriteSpecialEnumL<Enum>,
предназначенных для чтения/записи спецсимвола, хранящего значение
типа enum (или иного типа с небольшим заранее известным набором допустимых
значений). Это шаблоны, параметром которых является, собственно,
тот enum-тип, который вы хотите прочитать/записать. Этим функциям
нужно предоставить специальный объект класса SINX::EnumHelper<Enum>
(тоже шаблон, параметром которого должен быть тот же enum-тип), описывающий
соответствие допустимых значений требуемого типа и их строковых представлений.
Как конструируется такой объект, можете посмотреть в примере.
test.cpp – собственно пример. Представляет собой программу,
работающую с командной строки. Если запустить её без параметров,
она генерирует файл example_gen.sinx (он также включён в посылку,
но можно со спокойной совестью его стереть, всё равно будет создан
заново), а потом читает из него некоторые данные. Файл сравнительно
большой и сложный по структуре (пример из простого и примитивного
файла был бы менее интересен, не правда ли?), поэтому пусть вас не
смущает объём кода, отвечающего за пример записи – он потому и большой,
что много пишет.
Ещё можно запустить программу с одним параметром – именем SINX-файла
(см. другие файлы с расширением .sinx из посылки), в этом случае
она выведет поэлементный дамп указанного файла.
Вот, в сущности, и всё. Разбирайтесь и пользуйтесь.
5. Соглашение об использовании
В наше суровое копирастическое время об этом тоже нужно сказать пару
слов. Пара слов:
===
Соглашение об использовании.
Код из представленного выше архива sinx.zip разрешается свободно и
безвозмездно использовать и модифицировать любым способом и в любых
целях, за исключением следующего ограничения: запрещено добавлять
в него любые отметки о копирайте и лицензии. Код предоставляется,
как есть, без каких-либо гарантий.
Описанием формата SINX является глава 2 данного документа, за исключением
подраздела 2.4. Описание разрешается свободно и безвозмездно использовать
любым образом, при условии сохранности его текста в неизменном виде.
SINX, как формат, допускается свободно и безвозмездно использовать
в любых целях, при соблюдении следующих требований:
1. Сам по себе формат SINX является общественным достоянием и принципиально
не патентуется.
2. Патентование новой технологии или формата данных, в которых в каком-либо
качестве используется формат SINX, разрешается только в том случае,
если патент касается только именно этой технологии или формата, и
под его действие не попадает ни один объект, кроме перечисленных
явным и конкретным образом в тексте патента,
3. Допускается введение формата SINX в уже существующую и запатентованную
технологию или формат (например, в качестве альтернативного базового
формата), но только в качестве элемента, не покрытого соответствующим
патентом. Если введение его в таком качестве невозможно без изменения
текста патента, разрешается оформить на модифицированный формат или
технологию новый патент, к которому применяются ограничения п. 2.
Используя формат SINX и прочие материалы с данной страницы, вы тем
самым соглашаетесь на требования данного соглашения об использовании.
===
Пара слов не претендует на юридическую строгость, но обрисовывает
основные направления. Данный раздел в будущем может уточняться и
совершенствоваться, если того потребует ситуация.
Может возникнуть вопрос – а зачем на странице самопального и малоизвестного
формата этот раздел, отдающий в свете самопальности и малоизвестности
чем-то цирковым?
Как самопальный и малоизвестный формат, SINX имеет ещё одно неочевидное
преимущество – лицензионную чистоту. Можно употреблять его для любых
целей, не опасаясь ситуаций
типа таких. Необходимость в данном разделе,
конечно, сомнительна – здравый смысл подсказывает, что вряд ли в
ближайшее время SINX обретёт широкую известность, повсеместно вытеснит
б-гомерзкий XML, и кому-то до него вообще будет дело. Но, как человек
параноидальный, я не могу не рассмотреть все варианты. И лучше написать
несколько смешных буковок сейчас, чем потом внезапно обнаружить,
что уже поздно.
6. Пример софта
В доказательство того, что я действительно использовал формат SINX
для собственных нужд, вот пример программы –
игра Operation I.T.C.H.
(5 мб, .zip) (работает на вантузе, теоретически – от 2000, достоверно
– на XP, 7 и, по свидетельствам очевидцев, в Wine). Поиск мест, где
он там используется, предоставляю вашему любопытству.
7. Дополнительные материалы (обновления от 2011 и далее)
В процессе использования формата SINX в моей деятельности к нему появились
(и продолжают появляться) разные полезные разности. Я буду выкладывать
их сюда.
sinxxml.zip (187 кб) – небольшой консольный конвертор SINX<->XML.
Сурцы прилагаются, инструкция печатается при запуске. Для борьбы
с XML использован RapidXML, поэтому есть несколько дефектов. При
конверсии из XML обычно теряется форматирование из табов и отступов,
а обратное даёт не совсем корректный XML: нужно дописать вручную
<?xml version="1.0" encoding="utf-8"?> (а я говорил вам, что
библиотеки для работы с XML неполноценны!), и будет нехорошо, если
исходный SINX состоит из нескольких элементов на корневом уровне
– правильный XML позволяет иметь там только один элемент (я же говорил,
что XML – неполноценный формат!:).
sinx_cs.zip (7 кб) – минималистическая библиотека для SINX в C# 3.0+,
можно использовать на Window$ Phone (7) SDK (собственно, для этого
и создавалась). Используются документационные комментарии C# (не
по-русски), так что разберётесь. Класс Test в конце файла – как бы
пример, удалите его перед тем, как использовать для своих нужд. Если
у вас C# 4+, можно найти фрагмент "public SinxSpecial this[string
pName,int i]" и заменить в нём на "int i=0", тогда вместо xxx["special_name",0]
можно будет писать xxx["special_name"].
sinx_php.zip (4 kb) – библиотека для использования SINX в PHP 4/5.
Примеров нет, но всё документировано (имеется даже краткое описание
формата) (правда, опять-таки не по-русски) – не потеряетесь.
(C) 2011 Mikle[Subterranean Devil]
Использование текста с данной страницы допускается только со ссылкой на источник