У статті представлений повний Swift "ASN.1 компілятор"
на мові Elixir. Автор — Максим Сохацький, 2023-9-1.
Зміст
1) Техніка компіляції та розміточний парсер
2) Послідовності та множини
3) Імплісити, експлісити та опшинали
4) Рекурсивні суми
5) Переліки та цілочисельні переліки
6) Тензори
Техніка компіляції
Оскільки мова ASN.1 має чіткий синтаксис побудова її компілятора може базуватися
на BNF парсер генераторах таких як lexx/yacc або їх аналонах на інших мовах програмування
leex/yexx (Erlang), Citron (Swift), ANTLR (Java). Загалом нас цікавитимуть LL, LR, LALR, SLR.
Але найшвидші парсери це потокові бінарні, Erlang-овий саме такий і займає 2000
рядків разом з токенайзером на 100 рядків.
Технічно, ASN.1 компілятори є AST транформаціями з вихідної мови в цільову,
в даному випадку з Erlang шляхом Elixir в Swift. Основне завдання при цьому
побудувати базовий розміточний парсер у вигляді мікро-бібліотеки, яку буде
використовувати як хелпери згенерований код. В нашому випадку такою бібліотекою
є swift-asn1 від Apple тому це теж нам полегшує завдання. Таким чином наш
згенерований код сумісний з усіма екстеншинами з бібліотек Apple.
Розміточний парсер
В нас в ЗОШ №5 вчився математик Сергій Книш, який закінчив МГУ в 19 і
поїхав викладати в Каліфорнію. Я коли був в Сан-Франціско заходив до нього в гості
і він розказува таку байку, а він на байки багатий бо він писав мережевий протокол
IPX для шкільного комплексу Корветів і БК-0010 шоб можна було в іграшки по мережі
гратися і міг в памʼяті сумувати ряди з точністю до всіх розрядів калькулятора.
Так от він розказував шо коли ше тільки починався С++, то таблицю віртуальних методів
використовували як масив який індексувався байткодом операції, і коли ви читали байт
зі стріма ви простоо викликали функцію з цим індексом в таблиці віртуальнихх методів
для десеріалізації цього типу даних. Хоча це байка але в ній є зерно істини,
розрізати десеріалізатор і серіалізатор на примітивні функції, кожна з яких
займається своїм байт-кодом — техніка, що притаманна багатьом бібліотечним парсерам
на мовах програмування C++, Rust, Swift, Java, тощо. Приблизно така сама архітектура і
у розміточного TLV парсера Apple.
Послідовності та множини
Простий приклад з якого варто почати імплементацію будь якого ASN.1 компілятора.
Рекурсивні суми та їх теги
Перше шо ви повинні зробити перед тим як публікувати ASN.1 компілятор на сайті ITU.INT це
пересвідчитися шо він сприймає класичне визначення рекурсивного списку з книжки Олівʼє Дебюсона [6].
Якщо ви хочете тримати в сумі різні значення однакового типу ви повинні використовувати
врапери теги, які бувають двох видів: 1) імплісіти (129) і 2) експлісіти (160).
Імплісіти, експлісіти та опшинали
Якщо ви хочете позначити поле додатковою інформацією такою як можливіть приймати значення
NULL (OPTIONAL) та вид тегування (129, 160).
Переліки та цілочисельні переліки
Тензори
Висновок
Найкращий спосіб вивчити ASN.1 — це написати ASN.1 компілятор. Був детально проаналізований X.680
стандарт та знайдені конкуретні переваги над іншими компіляторами: 1) більшість не підтримують
рекурсивні типи даних, 2) більшість не підримують повністю всі ASN.1 файли ITU.INT, 3) більшість не підтримують тензори.
Наш компілятор зроблений таким, що усуває ці недоліки. Що стосується швидкості парсерів Apple для мови Swift,
то вони в середньому в два рази повільніші за fast_ber парсерів на C++ і в 2 рази швидші
за парсери asn1c.
Реалізація використовує потоковий бінарний парсер ASN.1 файлів на 2000 рядків коду разом
з токенайзером, що йдуть в дистрибутиві Erlang/OTP в аплікейшині asn1, а сам компілятор виконаний
як script файл для мови Elixir на 600 рядків і не потребує компіляції. Найближчі конкуренти asn1c і
asn1іcc займають 25000 рядків.
Доведення коректності компілятора має комбінаторний вигляд, а головна теорема
стверджує шо за 2 проходи, можна повністю скомпілювати ASN.1 X.680 стандарт
в будь яку мову програмування, кожен прохід містить доведення коректності
11 циклів, разом які використовують 50 розгалужень кожне з яких доводиться
окремо в комбінаторному стилі кейс аналізом. За перший прохід будують часткові
форвард декларації, які повністю стають насиченими на другому проході.
В процесі першого проходу не зберігаються файли, зате між проходами
не очищується контекст, який містить три типи ключів: визначення типів для носіння полів та їх властивостей,
синоніми, та специфікатори тензорів. Також компілятор містить verbose опцію
-v яка показує всі стадії насичення контексту.
На розробку компілятора був витрачений 1 людино-місяць та згенеровано ним 30000 рядків Swift 5.8 коду який працює на Windows/Linux/Mac.
Цей код покриває всі конверти які можна знайти на сайті ITU.INT у вигляді ASN.1 файлів. Сюди входять
PKIX, CMP, CMC, LDAP, CMS, PKCS, X.400, тощо. Компілятор asn1scc написаний на F# який підтримується
Європейським Космічним Агенством не працює на повній множині всіх цих декларацій, а займає теж немало, 32000 рядків на F#.